diff --git a/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin.html b/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin.html index 5f02a69..b503f41 100644 --- a/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin.html +++ b/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin.html @@ -1,797 +1,1018 @@ + - - - -네이버 :: Smart Editor 2 ™ - - - - - - - - - + + + + 네이버 :: Smart Editor 2 ™ + + + + + + + + + + - + - -
-
글쓰기영역으로 바로가기 -
- -
-
    -
  • - -
    -
    -
      -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    -
    -
    - -
  • + +
    +
    글쓰기영역으로 바로가기 +
    -
  • - -
    -
    -
      -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    -
    -
    - -
  • -
    -
  • +
    +
      +
    • + +
      +
      +
        +
      • +
      • +
      • +
      • +
      • +
      • +
      • +
      • +
      +
      +
      + +
    • -
    • +
    • + +
      +
      +
        +
      • +
      • +
      • +
      • +
      • +
      • +
      • +
      • +
      • +
      • +
      +
      +
      + +
    • +
    +
      +
    • -
    • +
    • -
    • +
    • -
    • - -
    • + +
    • + + + +
    • + +
    • + +
    • +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
      +
      +
        +
      • +
      • +
      • +
      • +
      • +
      • +
      • +
      +
      +

      직접 입력

      + + + + + +
      + +
      +
      +
      +
      + +
    • +
    +
      +
    • +
      +
        +
      • +
      • +
      • +
      • +
      +
      +
    • +
    +
      +
    • + + + +
    • +
    +
      + + +
    • + +
      +
      +
      +
        +
      • +
        +
          +
        • + + +
        -
      - -
    • - -
    • - -
    • +
    • +
      +
        +
      • + + +
      -
    - - + +
  • +
    +
      +
    • + + +
    +
    +
  • +
  • +
    +
      +
    • + + +
    +
    +
  • +
  • +
    +
      +
    • + + +
    +
    +
  • +
  • +
    +
      +
    • + + +
    +
    +
  • +
+

+ +

+
+
+
+ + -
  • +
  • + + +
    +
    +
    +
    + 칸수 지정 +
    +
    +
    + + +
    +
    +
    + + +
    +
    + + + + + + + + + + + + + + + + + + + +
        
        
        
    +
    +
    + 속성직접입력 +
    +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    + 표스타일 +
    +
    +
    +
    + + + +
    +
    +
    +

    + +

    + +
    + +
    +
    +
    + + +
  • -
  • - + +
    + + -
  • + + + -
  • +
    + +
    -
  • -
  • - -
    -
    -
      -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    -
    -

    직접 입력

    - - - - - -
    - -
    -
    -
    -
    - -
  • - - -
    - - - - - - +
    +
    + + + -
    - -
    - - - - - - - - - -
    - - -
    - -
    + + + + + + +
    +
    +
    +
    +
    + + + +
    +
    +
    + +
    +
    +
    +
    + + + + + + + + +
    + + - - - - -
    - - -
    - - -
    - - - -
    - - -
    - -
    - - - - - - +
    +
    테두리 색
    +
    + + + +
    +
    + +
    +
    정렬
    +
    +
    +
    + +
    + + + + + + + + + + +
    + + +
    + +
    + + + + + + - \ No newline at end of file + + diff --git a/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin_en_US.html b/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin_en_US.html index 6b6b3f2..79ab320 100644 --- a/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin_en_US.html +++ b/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin_en_US.html @@ -1,790 +1,1024 @@ + - - - -네이버 :: Smart Editor 2 ™ - - - - - - - + + + + 네이버 :: Smart Editor 2 ™ + + + + + + + + -Version: 2.9.0.4a256db + Version: 2.9.0.4a256db - -
    -
    글쓰기영역으로 바로가기 -
    - -
    -
      -
    • - -
      -
      -
        -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      -
      -
      - -
    • + +
      +
      글쓰기영역으로 바로가기 +
      -
    • - -
      -
      -
        -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      -
      -
      - -
    • -
      -
    • +
      +
        +
      • + +
        +
        +
          +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        +
        +
        + +
      • -
      • +
      • + +
        +
        +
          +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        +
        +
        + +
      • +
      +
        +
      • -
      • +
      • -
      • +
      • -
      • - -
      • + +
      • + + + +
      • + +
      • + +
      • +
      +
        +
      • + +
      • + +
      • + +
      • + +
      • + +
      • + +
      • + +
      • + +
      • + +
        +
        +
          +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        +
        +

        Enter

        + + + + + +
        + +
        +
        +
        +
        + +
      • +
      +
        +
      • + + + +
      • +
      +
        + + +
      • + +
        +
        +
        +
          +
        • +
          +
            +
          • + + +
          -
        - -
      • - -
      • - -
      • +
      • +
        +
          +
        • + + +
        -
      - - + +
    • +
      +
        +
      • + + +
      +
      +
    • +
    • +
      +
        +
      • + + +
      +
      +
    • +
    • +
      +
        +
      • + + +
      +
      +
    • +
    • +
      +
        +
      • + + +
      +
      +
    • +
    +

    + +

    +
    +
    +
    + + -
  • +
  • + + +
    +
    +
    +
    + Designate Number of Blanks +
    +
    +
    + + +
    +
    +
    + + +
    +
    + + + + + + + + + + + + + + + + + + + +
        
        
        
    +
    +
    + Enter Attributes +
    +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    + Table Style +
    +
    +
    +
    +
    + + + +
    +
    +
    +

    + +

    + +
    + +
    +
    +
    + + +
  • -
  • - +
    + + -
  • + + + -
  • +
    + +
    -
  • -
  • + + + -
  • + + + +
    + + +
    + + +
    + + + + +
    + + - +
    +
    Border Color
    +
    + + + +
    +
    +
    +
    +
    Alignment
    +
    +
    +
    + +
    +
    +
    + + + + + + + + +
    + + +
    + +
    + + + + + + - \ No newline at end of file + + diff --git a/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin_ja_JP.html b/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin_ja_JP.html index caf4785..2e33ffb 100644 --- a/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin_ja_JP.html +++ b/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin_ja_JP.html @@ -1,798 +1,1023 @@ - - - - -네이버 :: Smart Editor 2 ™ - - - - - - - - - + + + + + 네이버 :: Smart Editor 2 ™ + + + + + + + + + + + - + - -
    -
    글쓰기영역으로 바로가기 -
    - -
    -
      -
    • - -
      -
      -
        -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      -
      -
      - -
    • + +
      +
      글쓰기영역으로 바로가기 +
      -
    • - -
      -
      -
        -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      -
      -
      - -
    • -
      -
    • +
      +
        +
      • + +
        +
        +
          +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        +
        +
        + +
      • -
      • +
      • + +
        +
        +
          +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        +
        +
        + +
      • +
      +
        +
      • -
      • +
      • -
      • +
      • -
      • - -
      • + +
      • + + + +
      • + +
      • + +
      • +
      +
        +
      • + +
      • + +
      • + +
      • + +
      • + +
        +
        +
          +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        +
        +

        直接入力

        + + + + + +
        + +
        +
        +
        +
        + +
      • +
      +
        +
      • +
        +
          +
        • +
        • +
        • +
        • +
        +
        +
      • +
      +
        +
      • + + + +
      • +
      +
        + + +
      • + +
        +
        +
        +
          +
        • +
          +
            +
          • + + +
          -
        - -
      • - -
      • - -
      • +
      • +
        +
          +
        • + + +
        -
      - - + +
    • +
      +
        +
      • + + +
      +
      +
    • +
    • +
      +
        +
      • + + +
      +
      +
    • +
    • +
      +
        +
      • + + +
      +
      +
    • +
    • +
      +
        +
      • + + +
      +
      +
    • +
    +

    + +

    +
    +
    +
    + + -
  • +
  • + + +
    +
    +
    +
    + セル数指定 +
    +
    +
    + + +
    +
    +
    + + +
    +
    + + + + + + + + + + + + + + + + + + + +
        
        
        
    +
    +
    + プロパティ直接入力 +
    +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    + 表スタイル +
    +
    +
    +
    + + + +
    +
    +
    +

    + +

    + +
    + +
    +
    +
    + + +
  • -
  • - + +
    + + -
  • + + + -
  • +
    + +
    -
  • -
  • - -
    -
    -
      -
    • -
    • -
    • -
    • -
    • -
    • -
    • -
    -
    -

    直接入力

    - - - - - -
    - -
    -
    -
    -
    - -
  • - - -
    - - - - - - +
    +
    + + + -
    - -
    - - - - - - - - - -
    - - -
    - -
    + + + + + + +
    +
    +
    +
    +
    + + + +
    +
    +
    + +
    +
    +
    +
    + + + + + + + + +
    + + - - - - -
    - - -
    - - -
    - - - -
    - - -
    - -
    - - - - - - +
    +
    枠線の色
    +
    + + + +
    +
    + +
    +
    整列
    +
    +
    +
    + +
    + + + + + + + + + + +
    + + +
    + +
    + + + + + + - \ No newline at end of file + + diff --git a/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin_ko_KR.html b/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin_ko_KR.html index 3087cad..b4e85c2 100644 --- a/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin_ko_KR.html +++ b/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin_ko_KR.html @@ -1,790 +1,1008 @@ + - - - -네이버 :: Smart Editor 2 ™ - - - - - - - + + + + 네이버 :: Smart Editor 2 ™ + + + + + + + + -Version: 2.9.0.4a256db + Version: 2.9.0.4a256db - -
    -
    글쓰기영역으로 바로가기 -
    - -
    -
      -
    • - -
      -
      -
        -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      -
      -
      - -
    • + +
      +
      글쓰기영역으로 바로가기 +
      -
    • - -
      -
      -
        -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      -
      -
      - -
    • -
      -
    • +
      +
        +
      • + +
        +
        +
          +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        +
        +
        + +
      • -
      • +
      • + +
        +
        +
          +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        +
        +
        + +
      • +
      +
        +
      • -
      • +
      • -
      • +
      • -
      • - -
      • + +
      • + + + +
      • + +
      • + +
      • +
      +
        +
      • + +
      • + +
      • + +
      • + +
      • + +
      • + +
      • + +
      • + +
      • + +
        +
        +
          +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        +
        +

        직접 입력

        + + + + + +
        + +
        +
        +
        +
        + +
      • +
      +
        +
      • + + + +
      • +
      +
        + + +
      • + +
        +
        +
        +
          +
        • +
          +
            +
          • + + +
          -
        - -
      • - -
      • - -
      • +
      • +
        +
          +
        • + + +
        -
      - - + +
    • +
      +
        +
      • + + +
      +
      +
    • +
    • +
      +
        +
      • + + +
      +
      +
    • +
    • +
      +
        +
      • + + +
      +
      +
    • +
    • +
      +
        +
      • + + +
      +
      +
    • +
    +

    + +

    +
    +
    +
    + + -
  • +
  • + + +
    +
    +
    +
    + 칸수 지정 +
    +
    +
    + + +
    +
    +
    + + +
    +
    + + + + + + + + + + + + + + + + + + + +
        
        
        
    +
    +
    + 속성직접입력 +
    +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    + 표스타일 +
    +
    +
    +
    + + + +
    +
    +
    +

    + +

    + +
    + +
    +
    +
    + + +
  • -
  • - +
    + + -
  • + + + -
  • +
    + +
    -
  • -
  • + + + -
  • + + + +
    + + +
    + + +
    + + + + +
    + + - +
    +
    테두리 색
    +
    + + + +
    +
    +
    +
    +
    정렬
    +
    +
    +
    + +
    +
    +
    + + + + + + + + +
    + + +
    + +
    + + + + + + - \ No newline at end of file + + diff --git a/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin_zh_CN.html b/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin_zh_CN.html index a0f53b9..0744371 100644 --- a/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin_zh_CN.html +++ b/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin_zh_CN.html @@ -1,790 +1,1011 @@ + - - - -네이버 :: Smart Editor 2 ™ - - - - - - - + + + + 네이버 :: Smart Editor 2 ™ + + + + + + + + -Version: 2.9.0.4a256db + Version: 2.9.0.4a256db - -
    -
    글쓰기영역으로 바로가기 -
    - -
    -
      -
    • - -
      -
      -
        -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      -
      -
      - -
    • + +
      +
      글쓰기영역으로 바로가기 +
      -
    • - -
      -
      -
        -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      -
      -
      - -
    • -
      -
    • +
      +
        +
      • + +
        +
        +
          +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        +
        +
        + +
      • -
      • +
      • + +
        +
        +
          +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        +
        +
        + +
      • +
      +
        +
      • -
      • +
      • -
      • +
      • -
      • - -
      • + +
      • + + + +
      • + +
      • + +
      • +
      +
        +
      • + +
      • + +
      • + +
      • + +
      • + +
      • + +
      • + +
      • + +
      • + +
        +
        +
          +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        +
        +

        直接输入

        + + + + + +
        + +
        +
        +
        +
        + +
      • +
      +
        +
      • + + + +
      • +
      +
        + + +
      • + +
        +
        +
        +
          +
        • +
          +
            +
          • + + +
          -
        - -
      • - -
      • - -
      • +
      • +
        +
          +
        • + + +
        -
      - - + +
    • +
      +
        +
      • + + +
      +
      +
    • +
    • +
      +
        +
      • + + +
      +
      +
    • +
    • +
      +
        +
      • + + +
      +
      +
    • +
    • +
      +
        +
      • + + +
      +
      +
    • +
    +

    + +

    +
    +
    +
    + + -
  • +
  • + + +
    +
    +
    +
    + 指定格数 +
    +
    +
    + + +
    +
    +
    + + +
    +
    + + + + + + + + + + + + + + + + + + + +
        
        
        
    +
    +
    + 直接输入属性 +
    +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    + 表格 +
    +
    +
    +
    + + + +
    +
    +
    +

    + +

    + +
    + +
    +
    +
    + + +
  • -
  • - +
    + + -
  • + + + -
  • +
    + +
    -
  • -
  • + + + -
  • + + + +
    + + +
    + + +
    + + + + +
    + + - +
    +
    外缘颜色
    +
    + + + +
    +
    +
    +
    +
    排列
    +
    +
    +
    + +
    +
    +
    + + + + + + + + +
    + + +
    + +
    + + + + + + - \ No newline at end of file + + diff --git a/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin_zh_TW.html b/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin_zh_TW.html index 90a3429..8aee91b 100644 --- a/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin_zh_TW.html +++ b/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2Skin_zh_TW.html @@ -1,790 +1,1011 @@ + - - - -네이버 :: Smart Editor 2 ™ - - - - - - - + + + + 네이버 :: Smart Editor 2 ™ + + + + + + + + -Version: 2.9.0.4a256db + Version: 2.9.0.4a256db - -
    -
    글쓰기영역으로 바로가기 -
    - -
    -
      -
    • - -
      -
      -
        -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      -
      -
      - -
    • + +
      +
      글쓰기영역으로 바로가기 +
      -
    • - -
      -
      -
        -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      -
      -
      - -
    • -
      -
    • +
      +
        +
      • + +
        +
        +
          +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        +
        +
        + +
      • -
      • +
      • + +
        +
        +
          +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        +
        +
        + +
      • +
      +
        +
      • -
      • +
      • -
      • +
      • -
      • - -
      • + +
      • + + + +
      • + +
      • + +
      • +
      +
        +
      • + +
      • + +
      • + +
      • + +
      • + +
      • + +
      • + +
      • + +
      • + +
        +
        +
          +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        +
        +

        直接輸入

        + + + + + +
        + +
        +
        +
        +
        + +
      • +
      +
        +
      • + + + +
      • +
      +
        + + +
      • + +
        +
        +
        +
          +
        • +
          +
            +
          • + + +
          -
        - -
      • - -
      • - -
      • +
      • +
        +
          +
        • + + +
        -
      - - + +
    • +
      +
        +
      • + + +
      +
      +
    • +
    • +
      +
        +
      • + + +
      +
      +
    • +
    • +
      +
        +
      • + + +
      +
      +
    • +
    • +
      +
        +
      • + + +
      +
      +
    • +
    +

    + +

    +
    +
    +
    + + -
  • +
  • + + +
    +
    +
    +
    + 指定間數 +
    +
    +
    + + +
    +
    +
    + + +
    +
    + + + + + + + + + + + + + + + + + + + +
        
        
        
    +
    +
    + 直接輸入屬性 +
    +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    + 表格形式 +
    +
    +
    +
    + + + +
    +
    +
    +

    + +

    + +
    + +
    +
    +
    + + +
  • -
  • - +
    + + -
  • + + + -
  • +
    + +
    -
  • -
  • + + + -
  • + + + +
    + + +
    + + +
    + + + + +
    + + - +
    +
    外緣顔色
    +
    + + + +
    +
    +
    +
    +
    排列
    +
    +
    +
    + +
    +
    +
    + + + + + + + + +
    + + +
    + +
    + + + + + + - \ No newline at end of file + + diff --git a/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2_noframe.html b/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2_noframe.html index 8e440b6..0c55580 100644 --- a/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2_noframe.html +++ b/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2_noframe.html @@ -1,841 +1,1058 @@ - - - - - - -네이버 :: Smart Editor 2 ™ - - - - - - - - - - - - - - - -Version: 2.8.2.12056 - - -
    -
    글쓰기영역으로 바로가기 -
    - -
    -
      -
    • - -
      -
      -
        -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      -
      -
      - -
    • - -
    • - -
      -
      -
        -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      -
      -
      - -
    • -
      -
    • - -
    • - -
    • - -
    • - -
    • - - - -
    • - -
    • - - - -
    • - -
    • - -
    • -
      -
    • - -
    • - -
    • - -
    • - -
    • - -
    • - -
    • - -
    • - -
    • - -
      -
      -
        -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      -
      -

      직접 입력

      - - - - - -
      - -
      -
      -
      -
      - -
    • -
      -
    • - - - -
    • -
      - - -
    • - -
      -
      -
      -
        -
      • -
        -
          -
        • - - -
        -
        -
      • -
      • -
        -
          -
        • - - -
        -
        -
      • -
      • -
        -
          -
        • - - -
        -
        -
      • -
      • -
        -
          -
        • - - -
        -
        -
      • -
      • -
        -
          -
        • - - -
        -
        -
      • -
      • -
        -
          -
        • - - -
        -
        -
      • -
      -

      - -

      -
      -
      -
      - -
    • - -
    • - - -
      -
      -
      -
      - 칸수 지정 -
      -
      -
      - - -
      -
      -
      - - -
      -
      - - - - - - - - - - - - - - - - - - - -
          
          
          
      -
      -
      - 속성직접입력 -
      -
      -
      -
      -
      -
      - - - -
      -
      -
      -
      -
      - - -
      -
      -
      -
      -
      - - - -
      -
      -
      -
      -
      -
      - - - -
      -
      -
      -
      -
      -
      - 표스타일 -
      -
      -
      - - - -
      -
      -
      -

      - -

      - -
      - -
      -
      -
      - - -
    • - -
    • - - -
      -
      -
      - -

      찾기/바꾸기

      -
        -
      • -
      • -
      -
      -
      -
      -
      -

      - -

      -
      - - -
      -
      -
      - - -
    • -
    -
      -
    • -
    -
    - -
    - - - - - -
    - -
    - - - - - - - - - -
    - - -
    - - -
    - - - - -
    - - -
    - - -
    -
    - - -
    - -
      -
    • -
    • -
    • -
    -
    - -
    - -
    -
    - - - -
    - - - - -
    - -
    -
    - - - - - \ No newline at end of file + + + + + + + + 네이버 :: Smart Editor 2 ™ + + + + + + + + + + + + + + + + + Version: 2.8.2.12056 + + +
    +
    글쓰기영역으로 바로가기 +
    + +
    +
      +
    • + +
      +
      +
        +
      • +
      • +
      • +
      • +
      • +
      • +
      • +
      +
      +
      + +
    • + +
    • + +
      +
      +
        +
      • +
      • +
      • +
      • +
      • +
      • +
      • +
      • +
      • +
      • +
      +
      +
      + +
    • +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + + + +
    • + +
    • + + + +
    • + +
    • + +
    • +
    +
      +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
    • + +
      +
      +
        +
      • +
      • +
      • +
      • +
      • +
      • +
      • +
      +
      +

      직접 입력

      + + + + + +
      + +
      +
      +
      +
      + +
    • +
    +
      +
    • + + + +
    • +
    +
      + + +
    • + +
      +
      +
      +
        +
      • +
        +
          +
        • + + +
        +
        +
      • +
      • +
        +
          +
        • + + +
        +
        +
      • +
      • +
        +
          +
        • + + +
        +
        +
      • +
      • +
        +
          +
        • + + +
        +
        +
      • +
      • +
        +
          +
        • + + +
        +
        +
      • +
      • +
        +
          +
        • + + +
        +
        +
      • +
      +

      + +

      +
      +
      +
      + +
    • + +
    • + + +
      +
      +
      +
      + 칸수 지정 +
      +
      +
      + + +
      +
      +
      + + +
      +
      + + + + + + + + + + + + + + + + + + + +
          
          
          
      +
      +
      + 속성직접입력 +
      +
      +
      +
      +
      +
      +
      + + + +
      +
      +
      +
      +
      + + +
      +
      +
      +
      +
      + + + +
      +
      +
      +
      +
      +
      + + + +
      +
      +
      +
      +
      +
      + 표스타일 +
      +
      +
      +
      + + + +
      +
      +
      +

      + +

      + +
      + +
      +
      +
      + + +
    • + +
    • + + +
      +
      +
      + +

      찾기/바꾸기

      +
        +
      • +
      • +
      +
      +
      +
      +
      +
      +

      + +

      +
      + + +
      +
      +
      + + +
    • +
    +
      +
    • +
    +
    + +
    + + + + + +
    + +
    + + + + + + + + + +
    + + +
    + + +
    + + + + +
    + + +
    + + +
    +
    + + +
    + +
      +
    • +
    • +
    • +
    +
    + +
    + +
    +
    + + + +
    + + + + +
    + +
    +
    + + + + + + diff --git a/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2noframe.html b/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2noframe.html index 5edc725..d9a7bb6 100644 --- a/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2noframe.html +++ b/AvocadoEdition_Light/plugin/editor/smarteditor2/SmartEditor2noframe.html @@ -1,850 +1,1068 @@ + - - - -네이버 :: Smart Editor 2 ™ - - - - - - - + + + + 네이버 :: Smart Editor 2 ™ + + + + + + + + -Version: 2.9.0.4a256db + Version: 2.9.0.4a256db - -
    -
    글쓰기영역으로 바로가기 -
    - -
    -
      -
    • - -
      -
      -
        -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      -
      -
      - -
    • + +
      +
      글쓰기영역으로 바로가기 +
      -
    • - -
      -
      -
        -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      • -
      -
      -
      - -
    • -
      -
    • +
      +
        +
      • + +
        +
        +
          +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        +
        +
        + +
      • -
      • +
      • + +
        +
        +
          +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        +
        +
        + +
      • +
      +
        +
      • -
      • +
      • -
      • +
      • -
      • - -
      • + +
      • + + + +
      • + +
      • + +
      • +
      +
        +
      • + +
      • + +
      • + +
      • + +
      • + +
      • + +
      • + +
      • + +
      • + +
        +
        +
          +
        • +
        • +
        • +
        • +
        • +
        • +
        • +
        +
        +

        직접 입력

        + + + + + +
        + +
        +
        +
        +
        + +
      • +
      +
        +
      • + + + +
      • +
      +
        + + +
      • + +
        +
        +
        +
          +
        • +
          +
            +
          • + + +
          -
        - -
      • - -
      • - -
      • +
      • +
        +
          +
        • + + +
        -
      - - + +
    • +
      +
        +
      • + + +
      +
      +
    • +
    • +
      +
        +
      • + + +
      +
      +
    • +
    • +
      +
        +
      • + + +
      +
      +
    • +
    • +
      +
        +
      • + + +
      +
      +
    • +
    +

    + +

    +
    +
    +
    + + -
  • +
  • + + +
    +
    +
    +
    + 칸수 지정 +
    +
    +
    + + +
    +
    +
    + + +
    +
    + + + + + + + + + + + + + + + + + + + +
        
        
        
    +
    +
    + 속성직접입력 +
    +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    + + +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    + + + +
    +
    +
    +
    +
    +
    + 표스타일 +
    +
    +
    +
    + + + +
    +
    +
    +

    + +

    + +
    + +
    +
    +
    + + +
  • -
  • - +
    + + -
  • + + + -
  • +
    + +
    -
  • -
  • + + + -
  • + + + +
    + + +
    + + +
    + + + + +
    + + +
    + + +
    +
    + + +
    + + +
    + +
    + + + + + + -
  • - - -
    -
    -
    -
    - 칸수 지정 -
    -
    -
    - - -
    -
    -
    - - -
    -
    - - - - - - - - - - - - - - - - - - - -
        
        
        
    -
    -
    - 속성직접입력 -
    -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    -
    - - -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    -
    -
    - - - -
    -
    -
    -
    -
    -
    - 표스타일 -
    -
    -
    - - - -
    -
    -
    -

    - -

    - -
    - -
    -
    -
    - - -
  • - -
  • - - -
    -
    -
    - -

    찾기/바꾸기

    -
      -
    • -
    • -
    -
    -
    -
    -
    -

    - -

    -
    - - -
    -
    -
    - - -
  • - - - - - - - - - -
    - -
    - - - - - - - - - -
    - - -
    - - -
    - - - - -
    - - -
    - - -
    -
    - - -
    - - -
    - -
    - - - - - - - - -
    - - - - -
    - -
    -
    + + - - + + - \ No newline at end of file + + diff --git a/AvocadoEdition_Light/plugin/editor/smarteditor2/config.js b/AvocadoEdition_Light/plugin/editor/smarteditor2/config.js index 5ebbdea..c2d49ba 100644 --- a/AvocadoEdition_Light/plugin/editor/smarteditor2/config.js +++ b/AvocadoEdition_Light/plugin/editor/smarteditor2/config.js @@ -1,30 +1,30 @@ -(function($){ - $(document).ready(function() { - $(".smarteditor2").each( function(index){ - var get_id = $(this).attr("id"); - - if( !get_id || $(this).prop("nodeName") != 'TEXTAREA' ) return true; - - nhn.husky.EZCreator.createInIFrame({ - oAppRef: oEditors, - elPlaceHolder: get_id, - sSkinURI: g5_editor_url+"/SmartEditor2Skin.html", - htParams : { - bUseToolbar : true, // 툴바 사용 여부 (true:사용/ false:사용하지 않음) - bUseVerticalResizer : true, // 입력창 크기 조절바 사용 여부 (true:사용/ false:사용하지 않음) - bUseModeChanger : true, // 모드 탭(Editor | HTML | TEXT) 사용 여부 (true:사용/ false:사용하지 않음) - bSkipXssFilter : true, // client-side xss filter 무시 여부 (true:사용하지 않음 / 그외:사용) - //aAdditionalFontList : aAdditionalFontSet, // 추가 글꼴 목록 - fOnBeforeUnload : function(){ - //alert("완료!"); - } - }, //boolean - fOnAppLoad : function(){ - //예제 코드 - //oEditors.getById["ir1"].exec("PASTE_HTML", ["로딩이 완료된 후에 본문에 삽입되는 text입니다."]); - }, - fCreator: "createSEditor2" - }); - }); - }); -})(jQuery); \ No newline at end of file +(function ($) { + $(document).ready(function () { + $(".smarteditor2").each(function (index) { + var get_id = $(this).attr("id"); + + if (!get_id || $(this).prop("nodeName") != 'TEXTAREA') return true; + + nhn.husky.EZCreator.createInIFrame({ + oAppRef: oEditors, + elPlaceHolder: get_id, + sSkinURI: g5_editor_url + "/SmartEditor2Skin.html", + htParams: { + bUseToolbar: true, // 툴바 사용 여부 (true:사용/ false:사용하지 않음) + bUseVerticalResizer: true, // 입력창 크기 조절바 사용 여부 (true:사용/ false:사용하지 않음) + bUseModeChanger: true, // 모드 탭(Editor | HTML | TEXT) 사용 여부 (true:사용/ false:사용하지 않음) + bSkipXssFilter: true, // client-side xss filter 무시 여부 (true:사용하지 않음 / 그외:사용) + //aAdditionalFontList : aAdditionalFontSet, // 추가 글꼴 목록 + fOnBeforeUnload: function () { + //alert("완료!"); + } + }, //boolean + fOnAppLoad: function () { + //예제 코드 + //oEditors.getById["ir1"].exec("PASTE_HTML", ["로딩이 완료된 후에 본문에 삽입되는 text입니다."]); + }, + fCreator: "createSEditor2" + }); + }); + }); +})(jQuery); diff --git a/AvocadoEdition_Light/plugin/editor/smarteditor2/css/ko_KR/smart_editor2_in.css b/AvocadoEdition_Light/plugin/editor/smarteditor2/css/ko_KR/smart_editor2_in.css index fdecc09..5ee315d 100644 --- a/AvocadoEdition_Light/plugin/editor/smarteditor2/css/ko_KR/smart_editor2_in.css +++ b/AvocadoEdition_Light/plugin/editor/smarteditor2/css/ko_KR/smart_editor2_in.css @@ -1,24 +1,123 @@ @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} -@media screen and (-webkit-min-device-pixel-ratio: 0) { - .se2_inputarea td:empty:after,.se2_inputarea td > p:empty:after{content:"\00A0";line-height:1} +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 +} + +@media screen and (-webkit-min-device-pixel-ratio: 0) { + + .se2_inputarea td:empty:after, + .se2_inputarea td>p:empty:after { + content: "\00A0"; + line-height: 1 + } +} + /* 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} +.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 +} diff --git a/AvocadoEdition_Light/plugin/editor/smarteditor2/editor.lib.php b/AvocadoEdition_Light/plugin/editor/smarteditor2/editor.lib.php index 63071be..0a527e8 100644 --- a/AvocadoEdition_Light/plugin/editor/smarteditor2/editor.lib.php +++ b/AvocadoEdition_Light/plugin/editor/smarteditor2/editor.lib.php @@ -1,171 +1,177 @@ -') ) { //textarea로 작성되고, html 내용이 없다면 - $content = nl2br($content); - } - } - - $editor_url = G5_EDITOR_URL.'/'.$config['cf_editor']; - - $html = ""; - $html .= "웹에디터 시작"; - if ($is_dhtml_editor) - $html .= ''; - - if ($is_dhtml_editor && $js) { - $html .= "\n".''; - $html .= "\n".''; - $html .= "\n".''; - $html .= "\n"; - $js = false; - } - - $smarteditor_class = $is_dhtml_editor ? "smarteditor2" : ""; - $html .= "\n"; - $html .= "\n웹 에디터 끝"; - return $html; -} - - -// textarea 로 값을 넘긴다. javascript 반드시 필요 -function get_editor_js($id, $is_dhtml_editor=true) -{ - if ($is_dhtml_editor) { - return "var {$id}_editor_data = oEditors.getById['{$id}'].getIR();\noEditors.getById['{$id}'].exec('UPDATE_CONTENTS_FIELD', []);\nif(jQuery.inArray(document.getElementById('{$id}').value.toLowerCase().replace(/^\s*|\s*$/g, ''), [' ','

     

    ','


    ','

    ','

    ','
    ','']) != -1){document.getElementById('{$id}').value='';}\n"; - } else { - return "var {$id}_editor = document.getElementById('{$id}');\n"; - } -} - - -// textarea 의 값이 비어 있는지 검사 -function chk_editor_js($id, $is_dhtml_editor=true) -{ - if ($is_dhtml_editor) { - return "if (!{$id}_editor_data || jQuery.inArray({$id}_editor_data.toLowerCase(), [' ','

     

    ','


    ','

    ','
    ']) != -1) { alert(\"내용을 입력해 주십시오.\"); oEditors.getById['{$id}'].exec('FOCUS'); return false; }\n"; - } else { - return "if (!{$id}_editor.value) { alert(\"내용을 입력해 주십시오.\"); {$id}_editor.focus(); return false; }\n"; - } -} - -/* -https://github.com/timostamm/NonceUtil-PHP -*/ - -if (!defined('FT_NONCE_UNIQUE_KEY')) - define( 'FT_NONCE_UNIQUE_KEY' , sha1($_SERVER['SERVER_SOFTWARE'].G5_MYSQL_USER.session_id().G5_TABLE_PREFIX) ); - -if (!defined('FT_NONCE_SESSION_KEY')) - define( 'FT_NONCE_SESSION_KEY' , substr(md5(FT_NONCE_UNIQUE_KEY), 5) ); - -if (!defined('FT_NONCE_DURATION')) - define( 'FT_NONCE_DURATION' , 60 * 60 ); // 300 makes link or form good for 5 minutes from time of generation, 300은 5분간 유효, 60 * 60 은 1시간 - -if (!defined('FT_NONCE_KEY')) - define( 'FT_NONCE_KEY' , '_nonce' ); - -// This method creates a key / value pair for a url string -if(!function_exists('ft_nonce_create_query_string')){ - function ft_nonce_create_query_string( $action = '' , $user = '' ){ - return FT_NONCE_KEY."=".ft_nonce_create( $action , $user ); - } -} - -if(!function_exists('ft_get_secret_key')){ - function ft_get_secret_key($secret){ - return md5(FT_NONCE_UNIQUE_KEY.$secret); - } -} - -// This method creates an nonce. It should be called by one of the previous two functions. -if(!function_exists('ft_nonce_create')){ - function ft_nonce_create( $action = '',$user='', $timeoutSeconds=FT_NONCE_DURATION ){ - - $secret = ft_get_secret_key($action.$user); - - set_session('token_'.FT_NONCE_SESSION_KEY, $secret); - - $salt = ft_nonce_generate_hash(); - $time = time(); - $maxTime = $time + $timeoutSeconds; - $nonce = $salt . "|" . $maxTime . "|" . sha1( $salt . $secret . $maxTime ); - return $nonce; - - } -} - -// This method validates an nonce -if(!function_exists('ft_nonce_is_valid')){ - function ft_nonce_is_valid( $nonce, $action = '', $user='' ){ - - $secret = ft_get_secret_key($action.$user); - - $token = get_session('token_'.FT_NONCE_SESSION_KEY); - - if ($secret != $token){ - return false; - } - - if (is_string($nonce) == false) { - return false; - } - $a = explode('|', $nonce); - if (count($a) != 3) { - return false; - } - $salt = $a[0]; - $maxTime = intval($a[1]); - $hash = $a[2]; - $back = sha1( $salt . $secret . $maxTime ); - if ($back != $hash) { - return false; - } - if (time() > $maxTime) { - return false; - } - return true; - } -} - -// This method generates the nonce timestamp -if(!function_exists('ft_nonce_generate_hash')){ - function ft_nonce_generate_hash(){ - $length = 10; - $chars='1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'; - $ll = strlen($chars)-1; - $o = ''; - while (strlen($o) < $length) { - $o .= $chars[ rand(0, $ll) ]; - } - return $o; - } -} \ No newline at end of file +')) { //textarea로 작성되고, html 내용이 없다면 + $content = nl2br($content); + } + } + + $editor_url = G5_EDITOR_URL . '/' . $config['cf_editor']; + + $html = ""; + $html .= "웹에디터 시작"; + if ($is_dhtml_editor) + $html .= ''; + + if ($is_dhtml_editor && $js) { + $html .= "\n" . ''; + $html .= "\n" . ''; + $html .= "\n" . ''; + $html .= "\n"; + $js = false; + } + + $smarteditor_class = $is_dhtml_editor ? "smarteditor2" : ""; + $html .= "\n"; + $html .= "\n웹 에디터 끝"; + return $html; +} + + +// textarea 로 값을 넘긴다. javascript 반드시 필요 +function get_editor_js($id, $is_dhtml_editor = true) +{ + if ($is_dhtml_editor) { + return "var {$id}_editor_data = oEditors.getById['{$id}'].getIR();\noEditors.getById['{$id}'].exec('UPDATE_CONTENTS_FIELD', []);\nif(jQuery.inArray(document.getElementById('{$id}').value.toLowerCase().replace(/^\s*|\s*$/g, ''), [' ','

     

    ','


    ','

    ','

    ','
    ','']) != -1){document.getElementById('{$id}').value='';}\n"; + } else { + return "var {$id}_editor = document.getElementById('{$id}');\n"; + } +} + + +// textarea 의 값이 비어 있는지 검사 +function chk_editor_js($id, $is_dhtml_editor = true) +{ + if ($is_dhtml_editor) { + return "if (!{$id}_editor_data || jQuery.inArray({$id}_editor_data.toLowerCase(), [' ','

     

    ','


    ','

    ','
    ']) != -1) { alert(\"내용을 입력해 주십시오.\"); oEditors.getById['{$id}'].exec('FOCUS'); return false; }\n"; + } else { + return "if (!{$id}_editor.value) { alert(\"내용을 입력해 주십시오.\"); {$id}_editor.focus(); return false; }\n"; + } +} + +/* +https://github.com/timostamm/NonceUtil-PHP +*/ + +if (!defined('FT_NONCE_UNIQUE_KEY')) + define('FT_NONCE_UNIQUE_KEY', sha1($_SERVER['SERVER_SOFTWARE'] . G5_MYSQL_USER . session_id() . G5_TABLE_PREFIX)); + +if (!defined('FT_NONCE_SESSION_KEY')) + define('FT_NONCE_SESSION_KEY', substr(md5(FT_NONCE_UNIQUE_KEY), 5)); + +if (!defined('FT_NONCE_DURATION')) + define('FT_NONCE_DURATION', 60 * 60); // 300 makes link or form good for 5 minutes from time of generation, 300은 5분간 유효, 60 * 60 은 1시간 + +if (!defined('FT_NONCE_KEY')) + define('FT_NONCE_KEY', '_nonce'); + +// This method creates a key / value pair for a url string +if (!function_exists('ft_nonce_create_query_string')) { + function ft_nonce_create_query_string($action = '', $user = '') + { + return FT_NONCE_KEY . "=" . ft_nonce_create($action, $user); + } +} + +if (!function_exists('ft_get_secret_key')) { + function ft_get_secret_key($secret) + { + return md5(FT_NONCE_UNIQUE_KEY . $secret); + } +} + +// This method creates an nonce. It should be called by one of the previous two functions. +if (!function_exists('ft_nonce_create')) { + function ft_nonce_create($action = '', $user = '', $timeoutSeconds = FT_NONCE_DURATION) + { + + $secret = ft_get_secret_key($action . $user); + + set_session('token_' . FT_NONCE_SESSION_KEY, $secret); + + $salt = ft_nonce_generate_hash(); + $time = time(); + $maxTime = $time + $timeoutSeconds; + $nonce = $salt . "|" . $maxTime . "|" . sha1($salt . $secret . $maxTime); + return $nonce; + + } +} + +// This method validates an nonce +if (!function_exists('ft_nonce_is_valid')) { + function ft_nonce_is_valid($nonce, $action = '', $user = '') + { + + $secret = ft_get_secret_key($action . $user); + + $token = get_session('token_' . FT_NONCE_SESSION_KEY); + + if ($secret != $token) { + return false; + } + + if (is_string($nonce) == false) { + return false; + } + $a = explode('|', $nonce); + if (count($a) != 3) { + return false; + } + $salt = $a[0]; + $maxTime = intval($a[1]); + $hash = $a[2]; + $back = sha1($salt . $secret . $maxTime); + if ($back != $hash) { + return false; + } + if (time() > $maxTime) { + return false; + } + return true; + } +} + +// This method generates the nonce timestamp +if (!function_exists('ft_nonce_generate_hash')) { + function ft_nonce_generate_hash() + { + $length = 10; + $chars = '1234567890qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM'; + $ll = strlen($chars) - 1; + $o = ''; + while (strlen($o) < $length) { + $o .= $chars[rand(0, $ll)]; + } + return $o; + } +} diff --git a/AvocadoEdition_Light/plugin/editor/smarteditor2/js/smarteditor2.js b/AvocadoEdition_Light/plugin/editor/smarteditor2/js/smarteditor2.js index a31914e..91b2955 100644 --- a/AvocadoEdition_Light/plugin/editor/smarteditor2/js/smarteditor2.js +++ b/AvocadoEdition_Light/plugin/editor/smarteditor2/js/smarteditor2.js @@ -15,498 +15,498 @@ You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -if(typeof window.nhn=='undefined'){window.nhn = {};} -if (!nhn.husky){nhn.husky = {};} +if (typeof window.nhn == 'undefined') { window.nhn = {}; } +if (!nhn.husky) { nhn.husky = {}; } /** * @fileOverview This file contains Husky framework core * @name HuskyCore.js */ -(function(){ - var _rxMsgHandler = /^\$(LOCAL|BEFORE|ON|AFTER)_/, - _rxMsgAppReady = /^\$(BEFORE|ON|AFTER)_MSG_APP_READY$/, - _aHuskyCores = [], // HuskyCore instance list - _htLoadedFile = {}; // lazy-loaded file list +(function () { + var _rxMsgHandler = /^\$(LOCAL|BEFORE|ON|AFTER)_/, + _rxMsgAppReady = /^\$(BEFORE|ON|AFTER)_MSG_APP_READY$/, + _aHuskyCores = [], // HuskyCore instance list + _htLoadedFile = {}; // lazy-loaded file list - nhn.husky.HuskyCore = jindo.$Class({ - name : "HuskyCore", - aCallerStack : null, - bMobile : jindo.$Agent().navigator().mobile || jindo.$Agent().navigator().msafari, + nhn.husky.HuskyCore = jindo.$Class({ + name: "HuskyCore", + aCallerStack: null, + bMobile: jindo.$Agent().navigator().mobile || jindo.$Agent().navigator().msafari, - $init : function(htOptions){ - this.htOptions = htOptions||{}; - - _aHuskyCores.push(this); - if( this.htOptions.oDebugger ){ - nhn.husky.HuskyCore.getCore = function() { - return _aHuskyCores; - }; - this.htOptions.oDebugger.setApp(this); - } - - // To prevent processing a Husky message before all the plugins are registered and ready, - // Queue up all the messages here until the application's status is changed to READY - this.messageQueue = []; - - this.oMessageMap = {}; - this.oDisabledMessage = {}; - this.oLazyMessage = {}; - this.aPlugins = []; - - this.appStatus = nhn.husky.APP_STATUS.NOT_READY; - - this.aCallerStack = []; - - this._fnWaitForPluginReady = jindo.$Fn(this._waitForPluginReady, this).bind(); - - // Register the core as a plugin so it can receive messages - this.registerPlugin(this); - }, - - setDebugger: function(oDebugger) { - this.htOptions.oDebugger = oDebugger; - oDebugger.setApp(this); - }, - - exec : function(msg, args, oEvent){ - // If the application is not yet ready just queue the message - if(this.appStatus == nhn.husky.APP_STATUS.NOT_READY){ - this.messageQueue[this.messageQueue.length] = {'msg':msg, 'args':args, 'event':oEvent}; - return true; - } - - this.exec = this._exec; - this.exec(msg, args, oEvent); - }, - - delayedExec : function(msg, args, nDelay, oEvent){ - var fExec = jindo.$Fn(this.exec, this).bind(msg, args, oEvent); - setTimeout(fExec, nDelay); - }, - - _exec : function(msg, args, oEvent){ - return (this._exec = this.htOptions.oDebugger?this._execWithDebugger:this._execWithoutDebugger).call(this, msg, args, oEvent); - }, - _execWithDebugger : function(msg, args, oEvent){ - this.htOptions.oDebugger.log_MessageStart(msg, args); - var bResult = this._doExec(msg, args, oEvent); - this.htOptions.oDebugger.log_MessageEnd(msg, args); - return bResult; - }, - _execWithoutDebugger : function(msg, args, oEvent){ - return this._doExec(msg, args, oEvent); - }, - _doExec : function(msg, args, oEvent){ - var bContinue = false; - - // Lazy메시지가 있으면 파일을 로딩한다. - if(this.oLazyMessage[msg]){ - var htLazyInfo = this.oLazyMessage[msg]; - this._loadLazyFiles(msg, args, oEvent, htLazyInfo.aFilenames, 0); - return false; - } - - if(!this.oDisabledMessage[msg]){ - var allArgs = []; - if(args && args.length){ - var iLen = args.length; - for(var i=0; i= 0 && nhn.DOMFix.parentNode(aAllNodes[iChildIdx]) == aAllNodes[iCurIdx]){ - iChildIdx = this._recurConstructClonedTree(aAllNodes, iChildIdx, oCurNodeCloneWithChildren); - } - - // this may trigger an error message in IE when an erroneous script is inserted - oClonedParentNode.insertBefore(oCurNodeCloneWithChildren, oClonedParentNode.firstChild); - - return iChildIdx; - }; - this._recurConstructClonedTree = _recurConstructClonedTree; - aNodes[aNodes.length] = nhn.DOMFix.parentNode(aNodes[aNodes.length-1]); - this._recurConstructClonedTree(aNodes, aNodes.length-1, oClonedParentNode); - - return {oStartContainer: oClonedStartContainer, oEndContainer: oClonedEndContainer}; - }, - - cloneRange : function(){ - return this._copyRange(new nhn.W3CDOMRange(this._window)); - }, - - _copyRange : function(oClonedRange){ - oClonedRange.collapsed = this.collapsed; - oClonedRange.commonAncestorContainer = this.commonAncestorContainer; - oClonedRange.endContainer = this.endContainer; - oClonedRange.endOffset = this.endOffset; - oClonedRange.startContainer = this.startContainer; - oClonedRange.startOffset = this.startOffset; - oClonedRange._document = this._document; - - return oClonedRange; - }, - - collapse : function(toStart){ - if(toStart){ - this.endContainer = this.startContainer; - this.endOffset = this.startOffset; - }else{ - this.startContainer = this.endContainer; - this.startOffset = this.endOffset; - } - - this._updateRangeInfo(); - }, - - compareBoundaryPoints : function(how, sourceRange){ - switch(how){ - case nhn.W3CDOMRange.START_TO_START: - return this._compareEndPoint(this.startContainer, this.startOffset, sourceRange.startContainer, sourceRange.startOffset); - case nhn.W3CDOMRange.START_TO_END: - return this._compareEndPoint(this.endContainer, this.endOffset, sourceRange.startContainer, sourceRange.startOffset); - case nhn.W3CDOMRange.END_TO_END: - return this._compareEndPoint(this.endContainer, this.endOffset, sourceRange.endContainer, sourceRange.endOffset); - case nhn.W3CDOMRange.END_TO_START: - return this._compareEndPoint(this.startContainer, this.startOffset, sourceRange.endContainer, sourceRange.endOffset); - } - }, - - _findBody : function(oNode){ - if(!oNode){return null;} - while(oNode){ - if(oNode.tagName == "BODY"){return oNode;} - oNode = nhn.DOMFix.parentNode(oNode); - } - return null; - }, - - _compareEndPoint : function(oContainerA, iOffsetA, oContainerB, iOffsetB){ - return this.oBrowserSelection.compareEndPoints(oContainerA, iOffsetA, oContainerB, iOffsetB); - - var iIdxA, iIdxB; - - if(!oContainerA || this._findBody(oContainerA) != this._document.body){ - oContainerA = this._document.body; - iOffsetA = 0; - } - - if(!oContainerB || this._findBody(oContainerB) != this._document.body){ - oContainerB = this._document.body; - iOffsetB = 0; - } - - var compareIdx = function(iIdxA, iIdxB){ - // iIdxX == -1 when the node is the commonAncestorNode - // if iIdxA == -1 - // -> [[...]]... - // if iIdxB == -1 - // -> ...[[...]] - if(iIdxB == -1){iIdxB = iIdxA+1;} - if(iIdxA < iIdxB){return -1;} - if(iIdxA == iIdxB){return 0;} - return 1; - }; - - var oCommonAncestor = this._getCommonAncestorContainer(oContainerA, oContainerB); - - // ================================================================================================================================================ - // Move up both containers so that both containers are direct child nodes of the common ancestor node. From there, just compare the offset - // Add 0.5 for each contaienrs that has "moved up" since the actual node is wrapped by 1 or more parent nodes and therefore its position is somewhere between idx & idx+1 - // NODE1

    NODE2

    NODE3
    - // The position of NODE2 in COMMON_ANCESTOR is somewhere between after NODE1(idx1) and before NODE3(idx2), so we let that be 1.5 - - // container node A in common ancestor container - var oNodeA = oContainerA; - var oTmpNode = null; - if(oNodeA != oCommonAncestor){ - while((oTmpNode = nhn.DOMFix.parentNode(oNodeA)) != oCommonAncestor){oNodeA = oTmpNode;} - - iIdxA = this._getPosIdx(oNodeA)+0.5; - }else{ - iIdxA = iOffsetA; - } - - // container node B in common ancestor container - var oNodeB = oContainerB; - if(oNodeB != oCommonAncestor){ - while((oTmpNode = nhn.DOMFix.parentNode(oNodeB)) != oCommonAncestor){oNodeB = oTmpNode;} - - iIdxB = this._getPosIdx(oNodeB)+0.5; - }else{ - iIdxB = iOffsetB; - } - - return compareIdx(iIdxA, iIdxB); - }, - - _getCommonAncestorContainer : function(oNode1, oNode2){ - oNode1 = oNode1 || this.startContainer; - oNode2 = oNode2 || this.endContainer; - - var oComparingNode = oNode2; - - while(oNode1){ - while(oComparingNode){ - if(oNode1 == oComparingNode){return oNode1;} - oComparingNode = nhn.DOMFix.parentNode(oComparingNode); - } - oComparingNode = oNode2; - oNode1 = nhn.DOMFix.parentNode(oNode1); - } - - return this._document.body; - }, - - deleteContents : function(){ - if(this.collapsed){return;} - - this._splitTextEndNodesOfTheRange(); - - var aNodes = this._getNodesInRange(); - - if(aNodes.length < 1){return;} - var oPrevNode = aNodes[0].previousSibling; - - while(oPrevNode && this._isBlankTextNode(oPrevNode)){oPrevNode = oPrevNode.previousSibling;} - - var oNewStartContainer, iNewOffset = -1; - if(!oPrevNode){ - oNewStartContainer = nhn.DOMFix.parentNode(aNodes[0]); - iNewOffset = 0; - } - - for(var i=0; iA]B - // -> []B - // without these lines, the result would yeild to - // -> []B - if(oNewStartContainer == oNode && iNewOffset === 0){ - iNewOffset = this._getPosIdx(oNewStartContainer); - oNewStartContainer = nhn.DOMFix.parentNode(oNode); - } - } - } - - if(!oPrevNode){ - this.setStart(oNewStartContainer, iNewOffset, true, true); - }else{ - if(oPrevNode.tagName == "BODY"){ - this.setStartBefore(oPrevNode, true); - }else{ - this.setStartAfter(oPrevNode, true); - } - } - - this.collapse(true); - }, - - extractContents : function(){ - var oClonedContents = this.cloneContents(); - this.deleteContents(); - return oClonedContents; - }, - - getInsertBeforeNodes : function(){ - var oFirstNode = null; - - var oParentContainer; - - if(this.startContainer.nodeType == "3"){ - oParentContainer = nhn.DOMFix.parentNode(this.startContainer); - if(this.startContainer.nodeValue.length <= this.startOffset){ - oFirstNode = this.startContainer.nextSibling; - }else{ - oFirstNode = this.startContainer.splitText(this.startOffset); - } - }else{ - oParentContainer = this.startContainer; - oFirstNode = nhn.DOMFix.childNodes(this.startContainer)[this.startOffset]; - } - - if(!oFirstNode || !nhn.DOMFix.parentNode(oFirstNode)){oFirstNode = null;} - - return {elParent: oParentContainer, elBefore: oFirstNode}; - }, - - insertNode : function(newNode){ - var oInsertBefore = this.getInsertBeforeNodes(); - - oInsertBefore.elParent.insertBefore(newNode, oInsertBefore.elBefore); - - this.setStartBefore(newNode); - }, - - selectNode : function(refNode){ - this.reset(this._window); - - this.setStartBefore(refNode); - this.setEndAfter(refNode); - }, - - selectNodeContents : function(refNode){ - this.reset(this._window); - - this.setStart(refNode, 0, true); - this.setEnd(refNode, nhn.DOMFix.childNodes(refNode).length); - }, - - _endsNodeValidation : function(oNode, iOffset){ - if(!oNode || this._findBody(oNode) != this._document.body){throw new Error("INVALID_NODE_TYPE_ERR oNode is not part of current document");} - - if(oNode.nodeType == 3){ - if(iOffset > oNode.nodeValue.length){iOffset = oNode.nodeValue.length;} - }else{ - if(iOffset > nhn.DOMFix.childNodes(oNode).length){iOffset = nhn.DOMFix.childNodes(oNode).length;} - } - - return iOffset; - }, - - - setEnd : function(refNode, offset, bSafe, bNoUpdate){ - if(!bSafe){offset = this._endsNodeValidation(refNode, offset);} - - this.endContainer = refNode; - this.endOffset = offset; - - if(!bNoUpdate){ - if(!this.startContainer || this._compareEndPoint(this.startContainer, this.startOffset, this.endContainer, this.endOffset) != -1){ - this.collapse(false); - }else{ - this._updateRangeInfo(); - } - } - }, - - setEndAfter : function(refNode, bNoUpdate){ - if(!refNode){throw new Error("INVALID_NODE_TYPE_ERR in setEndAfter");} - - if(refNode.tagName == "BODY"){ - this.setEnd(refNode, nhn.DOMFix.childNodes(refNode).length, true, bNoUpdate); - return; - } - this.setEnd(nhn.DOMFix.parentNode(refNode), this._getPosIdx(refNode)+1, true, bNoUpdate); - }, - - setEndBefore : function(refNode, bNoUpdate){ - if(!refNode){throw new Error("INVALID_NODE_TYPE_ERR in setEndBefore");} - - if(refNode.tagName == "BODY"){ - this.setEnd(refNode, 0, true, bNoUpdate); - return; - } - - this.setEnd(nhn.DOMFix.parentNode(refNode), this._getPosIdx(refNode), true, bNoUpdate); - }, - - setStart : function(refNode, offset, bSafe, bNoUpdate){ - if(!bSafe){offset = this._endsNodeValidation(refNode, offset);} - - this.startContainer = refNode; - this.startOffset = offset; - - if(!bNoUpdate){ - if(!this.endContainer || this._compareEndPoint(this.startContainer, this.startOffset, this.endContainer, this.endOffset) != -1){ - this.collapse(true); - }else{ - this._updateRangeInfo(); - } - } - }, - - setStartAfter : function(refNode, bNoUpdate){ - if(!refNode){throw new Error("INVALID_NODE_TYPE_ERR in setStartAfter");} - - if(refNode.tagName == "BODY"){ - this.setStart(refNode, nhn.DOMFix.childNodes(refNode).length, true, bNoUpdate); - return; - } - - this.setStart(nhn.DOMFix.parentNode(refNode), this._getPosIdx(refNode)+1, true, bNoUpdate); - }, - - setStartBefore : function(refNode, bNoUpdate){ - if(!refNode){throw new Error("INVALID_NODE_TYPE_ERR in setStartBefore");} - - if(refNode.tagName == "BODY"){ - this.setStart(refNode, 0, true, bNoUpdate); - return; - } - this.setStart(nhn.DOMFix.parentNode(refNode), this._getPosIdx(refNode), true, bNoUpdate); - }, - - surroundContents : function(newParent){ - newParent.appendChild(this.extractContents()); - this.insertNode(newParent); - this.selectNode(newParent); - }, - - toString : function(){ - var oTmpContainer = this._document.createElement("DIV"); - oTmpContainer.appendChild(this.cloneContents()); - - return oTmpContainer.textContent || oTmpContainer.innerText || ""; - }, - - // this.oBrowserSelection.getCommonAncestorContainer which uses browser's built-in API runs faster but may return an incorrect value. - // Call this function to fix the problem. - // - // In IE, the built-in API would return an incorrect value when, - // 1. commonAncestorContainer is not selectable - // AND - // 2. The selected area will look the same when its child node is selected - // eg) - // when

    TEST

    is selected, TEST will be returned as commonAncestorContainer - fixCommonAncestorContainer : function(){ - if(!jindo.$Agent().navigator().ie){ - return; - } - - this.commonAncestorContainer = this._getCommonAncestorContainer(); - }, - - _isBlankTextNode : function(oNode){ - if(oNode.nodeType == 3 && oNode.nodeValue == ""){return true;} - return false; - }, - - _isAllChildBlankText : function(elNode){ - for(var i=0, nLen=elNode.childNodes.length; i - // - // - // - // - // - // - // - // - // - // - // - // , it's easier to compare the position of B and D rather than C and F because they are siblings - // - // If the range were collapsed, oActualEndNode will precede oActualStartNode by doing this - oActualStartNode = this._getNextNode(this._getPrevNode(oActualStartNode)); - oActualEndNode = this._getPrevNode(this._getNextNode(oActualEndNode)); - - if(oActualStartNode && oActualEndNode && oActualEndNode.tagName != "BODY" && - (this._getNextNode(oActualEndNode) == oActualStartNode || (oActualEndNode == oActualStartNode && this._isBlankTextNode(oActualEndNode))) - ){ - bCollapsed = true; - } - } - - return bCollapsed; - }, - - _splitTextEndNodesOfTheRange : function(){ - var oEndPoints = this._splitTextEndNodes({oStartContainer: this.startContainer, iStartOffset: this.startOffset, - oEndContainer: this.endContainer, iEndOffset: this.endOffset}); - - this.startContainer = oEndPoints.oStartContainer; - this.startOffset = oEndPoints.iStartOffset; - - this.endContainer = oEndPoints.oEndContainer; - this.endOffset = oEndPoints.iEndOffset; - }, - - _splitTextEndNodes : function(oEndPoints){ - oEndPoints = this._splitStartTextNode(oEndPoints); - oEndPoints = this._splitEndTextNode(oEndPoints); - - return oEndPoints; - }, - - _splitStartTextNode : function(oEndPoints){ - var oStartContainer = oEndPoints.oStartContainer; - var iStartOffset = oEndPoints.iStartOffset; - - var oEndContainer = oEndPoints.oEndContainer; - var iEndOffset = oEndPoints.iEndOffset; - - if(!oStartContainer){return oEndPoints;} - if(oStartContainer.nodeType != 3){return oEndPoints;} - if(iStartOffset === 0){return oEndPoints;} - - if(oStartContainer.nodeValue.length <= iStartOffset){return oEndPoints;} - - var oLastPart = oStartContainer.splitText(iStartOffset); - - if(oStartContainer == oEndContainer){ - iEndOffset -= iStartOffset; - oEndContainer = oLastPart; - } - oStartContainer = oLastPart; - iStartOffset = 0; - - return {oStartContainer: oStartContainer, iStartOffset: iStartOffset, oEndContainer: oEndContainer, iEndOffset: iEndOffset}; - }, - - _splitEndTextNode : function(oEndPoints){ - var oStartContainer = oEndPoints.oStartContainer; - var iStartOffset = oEndPoints.iStartOffset; - - var oEndContainer = oEndPoints.oEndContainer; - var iEndOffset = oEndPoints.iEndOffset; - - if(!oEndContainer){return oEndPoints;} - if(oEndContainer.nodeType != 3){return oEndPoints;} - - if(iEndOffset >= oEndContainer.nodeValue.length){return oEndPoints;} - if(iEndOffset === 0){return oEndPoints;} - - oEndContainer.splitText(iEndOffset); - - return {oStartContainer: oStartContainer, iStartOffset: iStartOffset, oEndContainer: oEndContainer, iEndOffset: iEndOffset}; - }, - - _getNodesInRange : function(){ - if(this.collapsed){return [];} - - var oStartNode = this._getActualStartNode(this.startContainer, this.startOffset); - var oEndNode = this._getActualEndNode(this.endContainer, this.endOffset); - - return this._getNodesBetween(oStartNode, oEndNode); - }, - - _getActualStartNode : function(oStartContainer, iStartOffset){ - var oStartNode = oStartContainer; - - if(oStartContainer.nodeType == 3){ - if(iStartOffset >= oStartContainer.nodeValue.length){ - oStartNode = this._getNextNode(oStartContainer); - if(oStartNode.tagName == "BODY"){oStartNode = null;} - }else{ - oStartNode = oStartContainer; - } - }else{ - if(iStartOffset < nhn.DOMFix.childNodes(oStartContainer).length){ - oStartNode = nhn.DOMFix.childNodes(oStartContainer)[iStartOffset]; - }else{ - oStartNode = this._getNextNode(oStartContainer); - if(oStartNode.tagName == "BODY"){oStartNode = null;} - } - } - - return oStartNode; - }, - - _getActualEndNode : function(oEndContainer, iEndOffset){ - var oEndNode = oEndContainer; - - if(iEndOffset === 0){ - oEndNode = this._getPrevNode(oEndContainer); - if(oEndNode.tagName == "BODY"){oEndNode = null;} - }else if(oEndContainer.nodeType == 3){ - oEndNode = oEndContainer; - }else{ - oEndNode = nhn.DOMFix.childNodes(oEndContainer)[iEndOffset-1]; - } - - return oEndNode; - }, - - _getNextNode : function(oNode){ - if(!oNode || oNode.tagName == "BODY"){return this._document.body;} - - if(oNode.nextSibling){return oNode.nextSibling;} - - return this._getNextNode(nhn.DOMFix.parentNode(oNode)); - }, - - _getPrevNode : function(oNode){ - if(!oNode || oNode.tagName == "BODY"){return this._document.body;} - - if(oNode.previousSibling){return oNode.previousSibling;} - - return this._getPrevNode(nhn.DOMFix.parentNode(oNode)); - }, - - // includes partially selected - // for
    , _getNodesBetween(b, c) will yield to b, "a" and c - _getNodesBetween : function(oStartNode, oEndNode){ - var aNodesBetween = []; - this._nNodesBetweenLen = 0; - - if(!oStartNode || !oEndNode){return aNodesBetween;} - - // IE may throw an exception on "oCurNode = oCurNode.nextSibling;" when oCurNode is 'invalid', not null or undefined but somehow 'invalid'. - // It happened during browser's build-in UNDO with control range selected(table). - try{ - this._recurGetNextNodesUntil(oStartNode, oEndNode, aNodesBetween); - }catch(e){ - return []; - } - - return aNodesBetween; - }, - - _recurGetNextNodesUntil : function(oNode, oEndNode, aNodesBetween){ - if(!oNode){return false;} - - if(!this._recurGetChildNodesUntil(oNode, oEndNode, aNodesBetween)){return false;} - - var oNextToChk = oNode.nextSibling; - - while(!oNextToChk){ - if(!(oNode = nhn.DOMFix.parentNode(oNode))){return false;} - - aNodesBetween[this._nNodesBetweenLen++] = oNode; - - if(oNode == oEndNode){return false;} - - oNextToChk = oNode.nextSibling; - } - - return this._recurGetNextNodesUntil(oNextToChk, oEndNode, aNodesBetween); - }, - - _recurGetChildNodesUntil : function(oNode, oEndNode, aNodesBetween){ - if(!oNode){return false;} - - var bEndFound = false; - var oCurNode = oNode; - if(oCurNode.firstChild){ - oCurNode = oCurNode.firstChild; - while(oCurNode){ - if(!this._recurGetChildNodesUntil(oCurNode, oEndNode, aNodesBetween)){ - bEndFound = true; - break; - } - oCurNode = oCurNode.nextSibling; - } - } - aNodesBetween[this._nNodesBetweenLen++] = oNode; - - if(bEndFound){return false;} - if(oNode == oEndNode){return false;} - - return true; - } + this.collapsed = true; + this.commonAncestorContainer = this._document.body; + this.endContainer = this._document.body; + this.endOffset = 0; + this.startContainer = this._document.body; + this.startOffset = 0; + + this.oBrowserSelection = new nhn.BrowserSelection(this._window); + this.selectionLoaded = this.oBrowserSelection.selectionLoaded; + }, + + cloneContents: function () { + var oClonedContents = this._document.createDocumentFragment(); + var oTmpContainer = this._document.createDocumentFragment(); + + var aNodes = this._getNodesInRange(); + + if (aNodes.length < 1) { return oClonedContents; } + + var oClonedContainers = this._constructClonedTree(aNodes, oTmpContainer); + + // oTopContainer = aNodes[aNodes.length-1].parentNode and this is not part of the initial array and only those child nodes should be cloned + var oTopContainer = oTmpContainer.firstChild; + + if (oTopContainer) { + var elCurNode = oTopContainer.firstChild; + var elNextNode; + + while (elCurNode) { + elNextNode = elCurNode.nextSibling; + oClonedContents.appendChild(elCurNode); + elCurNode = elNextNode; + } + } + + oClonedContainers = this._splitTextEndNodes({ + oStartContainer: oClonedContainers.oStartContainer, iStartOffset: this.startOffset, + oEndContainer: oClonedContainers.oEndContainer, iEndOffset: this.endOffset + }); + + if (oClonedContainers.oStartContainer && oClonedContainers.oStartContainer.previousSibling) { + nhn.DOMFix.parentNode(oClonedContainers.oStartContainer).removeChild(oClonedContainers.oStartContainer.previousSibling); + } + + if (oClonedContainers.oEndContainer && oClonedContainers.oEndContainer.nextSibling) { + nhn.DOMFix.parentNode(oClonedContainers.oEndContainer).removeChild(oClonedContainers.oEndContainer.nextSibling); + } + + return oClonedContents; + }, + + _constructClonedTree: function (aNodes, oClonedParentNode) { + var oClonedStartContainer = null; + var oClonedEndContainer = null; + + var oStartContainer = this.startContainer; + var oEndContainer = this.endContainer; + + var _recurConstructClonedTree = function (aAllNodes, iCurIdx, oClonedParentNode) { + + if (iCurIdx < 0) { return iCurIdx; } + + var iChildIdx = iCurIdx - 1; + + var oCurNodeCloneWithChildren = aAllNodes[iCurIdx].cloneNode(false); + + if (aAllNodes[iCurIdx] == oStartContainer) { oClonedStartContainer = oCurNodeCloneWithChildren; } + if (aAllNodes[iCurIdx] == oEndContainer) { oClonedEndContainer = oCurNodeCloneWithChildren; } + + while (iChildIdx >= 0 && nhn.DOMFix.parentNode(aAllNodes[iChildIdx]) == aAllNodes[iCurIdx]) { + iChildIdx = this._recurConstructClonedTree(aAllNodes, iChildIdx, oCurNodeCloneWithChildren); + } + + // this may trigger an error message in IE when an erroneous script is inserted + oClonedParentNode.insertBefore(oCurNodeCloneWithChildren, oClonedParentNode.firstChild); + + return iChildIdx; + }; + this._recurConstructClonedTree = _recurConstructClonedTree; + aNodes[aNodes.length] = nhn.DOMFix.parentNode(aNodes[aNodes.length - 1]); + this._recurConstructClonedTree(aNodes, aNodes.length - 1, oClonedParentNode); + + return { oStartContainer: oClonedStartContainer, oEndContainer: oClonedEndContainer }; + }, + + cloneRange: function () { + return this._copyRange(new nhn.W3CDOMRange(this._window)); + }, + + _copyRange: function (oClonedRange) { + oClonedRange.collapsed = this.collapsed; + oClonedRange.commonAncestorContainer = this.commonAncestorContainer; + oClonedRange.endContainer = this.endContainer; + oClonedRange.endOffset = this.endOffset; + oClonedRange.startContainer = this.startContainer; + oClonedRange.startOffset = this.startOffset; + oClonedRange._document = this._document; + + return oClonedRange; + }, + + collapse: function (toStart) { + if (toStart) { + this.endContainer = this.startContainer; + this.endOffset = this.startOffset; + } else { + this.startContainer = this.endContainer; + this.startOffset = this.endOffset; + } + + this._updateRangeInfo(); + }, + + compareBoundaryPoints: function (how, sourceRange) { + switch (how) { + case nhn.W3CDOMRange.START_TO_START: + return this._compareEndPoint(this.startContainer, this.startOffset, sourceRange.startContainer, sourceRange.startOffset); + case nhn.W3CDOMRange.START_TO_END: + return this._compareEndPoint(this.endContainer, this.endOffset, sourceRange.startContainer, sourceRange.startOffset); + case nhn.W3CDOMRange.END_TO_END: + return this._compareEndPoint(this.endContainer, this.endOffset, sourceRange.endContainer, sourceRange.endOffset); + case nhn.W3CDOMRange.END_TO_START: + return this._compareEndPoint(this.startContainer, this.startOffset, sourceRange.endContainer, sourceRange.endOffset); + } + }, + + _findBody: function (oNode) { + if (!oNode) { return null; } + while (oNode) { + if (oNode.tagName == "BODY") { return oNode; } + oNode = nhn.DOMFix.parentNode(oNode); + } + return null; + }, + + _compareEndPoint: function (oContainerA, iOffsetA, oContainerB, iOffsetB) { + return this.oBrowserSelection.compareEndPoints(oContainerA, iOffsetA, oContainerB, iOffsetB); + + var iIdxA, iIdxB; + + if (!oContainerA || this._findBody(oContainerA) != this._document.body) { + oContainerA = this._document.body; + iOffsetA = 0; + } + + if (!oContainerB || this._findBody(oContainerB) != this._document.body) { + oContainerB = this._document.body; + iOffsetB = 0; + } + + var compareIdx = function (iIdxA, iIdxB) { + // iIdxX == -1 when the node is the commonAncestorNode + // if iIdxA == -1 + // -> [[...]]... + // if iIdxB == -1 + // -> ...[[...]] + if (iIdxB == -1) { iIdxB = iIdxA + 1; } + if (iIdxA < iIdxB) { return -1; } + if (iIdxA == iIdxB) { return 0; } + return 1; + }; + + var oCommonAncestor = this._getCommonAncestorContainer(oContainerA, oContainerB); + + // ================================================================================================================================================ + // Move up both containers so that both containers are direct child nodes of the common ancestor node. From there, just compare the offset + // Add 0.5 for each contaienrs that has "moved up" since the actual node is wrapped by 1 or more parent nodes and therefore its position is somewhere between idx & idx+1 + // NODE1

    NODE2

    NODE3
    + // The position of NODE2 in COMMON_ANCESTOR is somewhere between after NODE1(idx1) and before NODE3(idx2), so we let that be 1.5 + + // container node A in common ancestor container + var oNodeA = oContainerA; + var oTmpNode = null; + if (oNodeA != oCommonAncestor) { + while ((oTmpNode = nhn.DOMFix.parentNode(oNodeA)) != oCommonAncestor) { oNodeA = oTmpNode; } + + iIdxA = this._getPosIdx(oNodeA) + 0.5; + } else { + iIdxA = iOffsetA; + } + + // container node B in common ancestor container + var oNodeB = oContainerB; + if (oNodeB != oCommonAncestor) { + while ((oTmpNode = nhn.DOMFix.parentNode(oNodeB)) != oCommonAncestor) { oNodeB = oTmpNode; } + + iIdxB = this._getPosIdx(oNodeB) + 0.5; + } else { + iIdxB = iOffsetB; + } + + return compareIdx(iIdxA, iIdxB); + }, + + _getCommonAncestorContainer: function (oNode1, oNode2) { + oNode1 = oNode1 || this.startContainer; + oNode2 = oNode2 || this.endContainer; + + var oComparingNode = oNode2; + + while (oNode1) { + while (oComparingNode) { + if (oNode1 == oComparingNode) { return oNode1; } + oComparingNode = nhn.DOMFix.parentNode(oComparingNode); + } + oComparingNode = oNode2; + oNode1 = nhn.DOMFix.parentNode(oNode1); + } + + return this._document.body; + }, + + deleteContents: function () { + if (this.collapsed) { return; } + + this._splitTextEndNodesOfTheRange(); + + var aNodes = this._getNodesInRange(); + + if (aNodes.length < 1) { return; } + var oPrevNode = aNodes[0].previousSibling; + + while (oPrevNode && this._isBlankTextNode(oPrevNode)) { oPrevNode = oPrevNode.previousSibling; } + + var oNewStartContainer, iNewOffset = -1; + if (!oPrevNode) { + oNewStartContainer = nhn.DOMFix.parentNode(aNodes[0]); + iNewOffset = 0; + } + + for (var i = 0; i < aNodes.length; i++) { + var oNode = aNodes[i]; + + if (!oNode.firstChild || this._isAllChildBlankText(oNode)) { + if (oNewStartContainer == oNode) { + iNewOffset = this._getPosIdx(oNewStartContainer); + oNewStartContainer = nhn.DOMFix.parentNode(oNode); + } + oNode.parentNode.removeChild(oNode); + } else { + // move the starting point to out of the parent container if the starting point of parent container is meant to be removed + // [A]B + // -> []B + // without these lines, the result would yeild to + // -> []B + if (oNewStartContainer == oNode && iNewOffset === 0) { + iNewOffset = this._getPosIdx(oNewStartContainer); + oNewStartContainer = nhn.DOMFix.parentNode(oNode); + } + } + } + + if (!oPrevNode) { + this.setStart(oNewStartContainer, iNewOffset, true, true); + } else { + if (oPrevNode.tagName == "BODY") { + this.setStartBefore(oPrevNode, true); + } else { + this.setStartAfter(oPrevNode, true); + } + } + + this.collapse(true); + }, + + extractContents: function () { + var oClonedContents = this.cloneContents(); + this.deleteContents(); + return oClonedContents; + }, + + getInsertBeforeNodes: function () { + var oFirstNode = null; + + var oParentContainer; + + if (this.startContainer.nodeType == "3") { + oParentContainer = nhn.DOMFix.parentNode(this.startContainer); + if (this.startContainer.nodeValue.length <= this.startOffset) { + oFirstNode = this.startContainer.nextSibling; + } else { + oFirstNode = this.startContainer.splitText(this.startOffset); + } + } else { + oParentContainer = this.startContainer; + oFirstNode = nhn.DOMFix.childNodes(this.startContainer)[this.startOffset]; + } + + if (!oFirstNode || !nhn.DOMFix.parentNode(oFirstNode)) { oFirstNode = null; } + + return { elParent: oParentContainer, elBefore: oFirstNode }; + }, + + insertNode: function (newNode) { + var oInsertBefore = this.getInsertBeforeNodes(); + + oInsertBefore.elParent.insertBefore(newNode, oInsertBefore.elBefore); + + this.setStartBefore(newNode); + }, + + selectNode: function (refNode) { + this.reset(this._window); + + this.setStartBefore(refNode); + this.setEndAfter(refNode); + }, + + selectNodeContents: function (refNode) { + this.reset(this._window); + + this.setStart(refNode, 0, true); + this.setEnd(refNode, nhn.DOMFix.childNodes(refNode).length); + }, + + _endsNodeValidation: function (oNode, iOffset) { + if (!oNode || this._findBody(oNode) != this._document.body) { throw new Error("INVALID_NODE_TYPE_ERR oNode is not part of current document"); } + + if (oNode.nodeType == 3) { + if (iOffset > oNode.nodeValue.length) { iOffset = oNode.nodeValue.length; } + } else { + if (iOffset > nhn.DOMFix.childNodes(oNode).length) { iOffset = nhn.DOMFix.childNodes(oNode).length; } + } + + return iOffset; + }, + + + setEnd: function (refNode, offset, bSafe, bNoUpdate) { + if (!bSafe) { offset = this._endsNodeValidation(refNode, offset); } + + this.endContainer = refNode; + this.endOffset = offset; + + if (!bNoUpdate) { + if (!this.startContainer || this._compareEndPoint(this.startContainer, this.startOffset, this.endContainer, this.endOffset) != -1) { + this.collapse(false); + } else { + this._updateRangeInfo(); + } + } + }, + + setEndAfter: function (refNode, bNoUpdate) { + if (!refNode) { throw new Error("INVALID_NODE_TYPE_ERR in setEndAfter"); } + + if (refNode.tagName == "BODY") { + this.setEnd(refNode, nhn.DOMFix.childNodes(refNode).length, true, bNoUpdate); + return; + } + this.setEnd(nhn.DOMFix.parentNode(refNode), this._getPosIdx(refNode) + 1, true, bNoUpdate); + }, + + setEndBefore: function (refNode, bNoUpdate) { + if (!refNode) { throw new Error("INVALID_NODE_TYPE_ERR in setEndBefore"); } + + if (refNode.tagName == "BODY") { + this.setEnd(refNode, 0, true, bNoUpdate); + return; + } + + this.setEnd(nhn.DOMFix.parentNode(refNode), this._getPosIdx(refNode), true, bNoUpdate); + }, + + setStart: function (refNode, offset, bSafe, bNoUpdate) { + if (!bSafe) { offset = this._endsNodeValidation(refNode, offset); } + + this.startContainer = refNode; + this.startOffset = offset; + + if (!bNoUpdate) { + if (!this.endContainer || this._compareEndPoint(this.startContainer, this.startOffset, this.endContainer, this.endOffset) != -1) { + this.collapse(true); + } else { + this._updateRangeInfo(); + } + } + }, + + setStartAfter: function (refNode, bNoUpdate) { + if (!refNode) { throw new Error("INVALID_NODE_TYPE_ERR in setStartAfter"); } + + if (refNode.tagName == "BODY") { + this.setStart(refNode, nhn.DOMFix.childNodes(refNode).length, true, bNoUpdate); + return; + } + + this.setStart(nhn.DOMFix.parentNode(refNode), this._getPosIdx(refNode) + 1, true, bNoUpdate); + }, + + setStartBefore: function (refNode, bNoUpdate) { + if (!refNode) { throw new Error("INVALID_NODE_TYPE_ERR in setStartBefore"); } + + if (refNode.tagName == "BODY") { + this.setStart(refNode, 0, true, bNoUpdate); + return; + } + this.setStart(nhn.DOMFix.parentNode(refNode), this._getPosIdx(refNode), true, bNoUpdate); + }, + + surroundContents: function (newParent) { + newParent.appendChild(this.extractContents()); + this.insertNode(newParent); + this.selectNode(newParent); + }, + + toString: function () { + var oTmpContainer = this._document.createElement("DIV"); + oTmpContainer.appendChild(this.cloneContents()); + + return oTmpContainer.textContent || oTmpContainer.innerText || ""; + }, + + // this.oBrowserSelection.getCommonAncestorContainer which uses browser's built-in API runs faster but may return an incorrect value. + // Call this function to fix the problem. + // + // In IE, the built-in API would return an incorrect value when, + // 1. commonAncestorContainer is not selectable + // AND + // 2. The selected area will look the same when its child node is selected + // eg) + // when

    TEST

    is selected, TEST will be returned as commonAncestorContainer + fixCommonAncestorContainer: function () { + if (!jindo.$Agent().navigator().ie) { + return; + } + + this.commonAncestorContainer = this._getCommonAncestorContainer(); + }, + + _isBlankTextNode: function (oNode) { + if (oNode.nodeType == 3 && oNode.nodeValue == "") { return true; } + return false; + }, + + _isAllChildBlankText: function (elNode) { + for (var i = 0, nLen = elNode.childNodes.length; i < nLen; i++) { + if (!this._isBlankTextNode(elNode.childNodes[i])) { return false; } + } + return true; + }, + + _getPosIdx: function (refNode) { + var idx = 0; + for (var node = refNode.previousSibling; node; node = node.previousSibling) { idx++; } + + return idx; + }, + + _updateRangeInfo: function () { + if (!this.startContainer) { + this.reset(this._window); + return; + } + + // isCollapsed may not function correctly when the cursor is located, + // (below a table) AND (at the end of the document where there's no P tag or anything else to actually hold the cursor) + this.collapsed = this.oBrowserSelection.isCollapsed(this) || (this.startContainer === this.endContainer && this.startOffset === this.endOffset); + // this.collapsed = this._isCollapsed(this.startContainer, this.startOffset, this.endContainer, this.endOffset); + this.commonAncestorContainer = this.oBrowserSelection.getCommonAncestorContainer(this); + // this.commonAncestorContainer = this._getCommonAncestorContainer(this.startContainer, this.endContainer); + }, + + _isCollapsed: function (oStartContainer, iStartOffset, oEndContainer, iEndOffset) { + var bCollapsed = false; + + if (oStartContainer == oEndContainer && iStartOffset == iEndOffset) { + bCollapsed = true; + } else { + var oActualStartNode = this._getActualStartNode(oStartContainer, iStartOffset); + var oActualEndNode = this._getActualEndNode(oEndContainer, iEndOffset); + + // Take the parent nodes on the same level for easier comparison when they're next to each other + // eg) From + // + // + // + // + // + // + // + // + // + // + // + // + // , it's easier to compare the position of B and D rather than C and F because they are siblings + // + // If the range were collapsed, oActualEndNode will precede oActualStartNode by doing this + oActualStartNode = this._getNextNode(this._getPrevNode(oActualStartNode)); + oActualEndNode = this._getPrevNode(this._getNextNode(oActualEndNode)); + + if (oActualStartNode && oActualEndNode && oActualEndNode.tagName != "BODY" && + (this._getNextNode(oActualEndNode) == oActualStartNode || (oActualEndNode == oActualStartNode && this._isBlankTextNode(oActualEndNode))) + ) { + bCollapsed = true; + } + } + + return bCollapsed; + }, + + _splitTextEndNodesOfTheRange: function () { + var oEndPoints = this._splitTextEndNodes({ + oStartContainer: this.startContainer, iStartOffset: this.startOffset, + oEndContainer: this.endContainer, iEndOffset: this.endOffset + }); + + this.startContainer = oEndPoints.oStartContainer; + this.startOffset = oEndPoints.iStartOffset; + + this.endContainer = oEndPoints.oEndContainer; + this.endOffset = oEndPoints.iEndOffset; + }, + + _splitTextEndNodes: function (oEndPoints) { + oEndPoints = this._splitStartTextNode(oEndPoints); + oEndPoints = this._splitEndTextNode(oEndPoints); + + return oEndPoints; + }, + + _splitStartTextNode: function (oEndPoints) { + var oStartContainer = oEndPoints.oStartContainer; + var iStartOffset = oEndPoints.iStartOffset; + + var oEndContainer = oEndPoints.oEndContainer; + var iEndOffset = oEndPoints.iEndOffset; + + if (!oStartContainer) { return oEndPoints; } + if (oStartContainer.nodeType != 3) { return oEndPoints; } + if (iStartOffset === 0) { return oEndPoints; } + + if (oStartContainer.nodeValue.length <= iStartOffset) { return oEndPoints; } + + var oLastPart = oStartContainer.splitText(iStartOffset); + + if (oStartContainer == oEndContainer) { + iEndOffset -= iStartOffset; + oEndContainer = oLastPart; + } + oStartContainer = oLastPart; + iStartOffset = 0; + + return { oStartContainer: oStartContainer, iStartOffset: iStartOffset, oEndContainer: oEndContainer, iEndOffset: iEndOffset }; + }, + + _splitEndTextNode: function (oEndPoints) { + var oStartContainer = oEndPoints.oStartContainer; + var iStartOffset = oEndPoints.iStartOffset; + + var oEndContainer = oEndPoints.oEndContainer; + var iEndOffset = oEndPoints.iEndOffset; + + if (!oEndContainer) { return oEndPoints; } + if (oEndContainer.nodeType != 3) { return oEndPoints; } + + if (iEndOffset >= oEndContainer.nodeValue.length) { return oEndPoints; } + if (iEndOffset === 0) { return oEndPoints; } + + oEndContainer.splitText(iEndOffset); + + return { oStartContainer: oStartContainer, iStartOffset: iStartOffset, oEndContainer: oEndContainer, iEndOffset: iEndOffset }; + }, + + _getNodesInRange: function () { + if (this.collapsed) { return []; } + + var oStartNode = this._getActualStartNode(this.startContainer, this.startOffset); + var oEndNode = this._getActualEndNode(this.endContainer, this.endOffset); + + return this._getNodesBetween(oStartNode, oEndNode); + }, + + _getActualStartNode: function (oStartContainer, iStartOffset) { + var oStartNode = oStartContainer; + + if (oStartContainer.nodeType == 3) { + if (iStartOffset >= oStartContainer.nodeValue.length) { + oStartNode = this._getNextNode(oStartContainer); + if (oStartNode.tagName == "BODY") { oStartNode = null; } + } else { + oStartNode = oStartContainer; + } + } else { + if (iStartOffset < nhn.DOMFix.childNodes(oStartContainer).length) { + oStartNode = nhn.DOMFix.childNodes(oStartContainer)[iStartOffset]; + } else { + oStartNode = this._getNextNode(oStartContainer); + if (oStartNode.tagName == "BODY") { oStartNode = null; } + } + } + + return oStartNode; + }, + + _getActualEndNode: function (oEndContainer, iEndOffset) { + var oEndNode = oEndContainer; + + if (iEndOffset === 0) { + oEndNode = this._getPrevNode(oEndContainer); + if (oEndNode.tagName == "BODY") { oEndNode = null; } + } else if (oEndContainer.nodeType == 3) { + oEndNode = oEndContainer; + } else { + oEndNode = nhn.DOMFix.childNodes(oEndContainer)[iEndOffset - 1]; + } + + return oEndNode; + }, + + _getNextNode: function (oNode) { + if (!oNode || oNode.tagName == "BODY") { return this._document.body; } + + if (oNode.nextSibling) { return oNode.nextSibling; } + + return this._getNextNode(nhn.DOMFix.parentNode(oNode)); + }, + + _getPrevNode: function (oNode) { + if (!oNode || oNode.tagName == "BODY") { return this._document.body; } + + if (oNode.previousSibling) { return oNode.previousSibling; } + + return this._getPrevNode(nhn.DOMFix.parentNode(oNode)); + }, + + // includes partially selected + // for
    , _getNodesBetween(b, c) will yield to b, "a" and c + _getNodesBetween: function (oStartNode, oEndNode) { + var aNodesBetween = []; + this._nNodesBetweenLen = 0; + + if (!oStartNode || !oEndNode) { return aNodesBetween; } + + // IE may throw an exception on "oCurNode = oCurNode.nextSibling;" when oCurNode is 'invalid', not null or undefined but somehow 'invalid'. + // It happened during browser's build-in UNDO with control range selected(table). + try { + this._recurGetNextNodesUntil(oStartNode, oEndNode, aNodesBetween); + } catch (e) { + return []; + } + + return aNodesBetween; + }, + + _recurGetNextNodesUntil: function (oNode, oEndNode, aNodesBetween) { + if (!oNode) { return false; } + + if (!this._recurGetChildNodesUntil(oNode, oEndNode, aNodesBetween)) { return false; } + + var oNextToChk = oNode.nextSibling; + + while (!oNextToChk) { + if (!(oNode = nhn.DOMFix.parentNode(oNode))) { return false; } + + aNodesBetween[this._nNodesBetweenLen++] = oNode; + + if (oNode == oEndNode) { return false; } + + oNextToChk = oNode.nextSibling; + } + + return this._recurGetNextNodesUntil(oNextToChk, oEndNode, aNodesBetween); + }, + + _recurGetChildNodesUntil: function (oNode, oEndNode, aNodesBetween) { + if (!oNode) { return false; } + + var bEndFound = false; + var oCurNode = oNode; + if (oCurNode.firstChild) { + oCurNode = oCurNode.firstChild; + while (oCurNode) { + if (!this._recurGetChildNodesUntil(oCurNode, oEndNode, aNodesBetween)) { + bEndFound = true; + break; + } + oCurNode = oCurNode.nextSibling; + } + } + aNodesBetween[this._nNodesBetweenLen++] = oNode; + + if (bEndFound) { return false; } + if (oNode == oEndNode) { return false; } + + return true; + } }); nhn.W3CDOMRange.START_TO_START = 0; @@ -1219,1682 +1223,1682 @@ nhn.W3CDOMRange.END_TO_START = 3; * @name HuskyRange.js */ nhn.HuskyRange = jindo.$Class({ - _rxCursorHolder : /^(?:\uFEFF|\u00A0|\u200B|
    )$/i, - _rxTextAlign : /text-align:[^"';]*;?/i, - - setWindow : function(win){ - this.reset(win || window); - }, - - $init : function(win){ - this.HUSKY_BOOMARK_START_ID_PREFIX = "husky_bookmark_start_"; - this.HUSKY_BOOMARK_END_ID_PREFIX = "husky_bookmark_end_"; - - this.sBlockElement = "P|DIV|LI|H[1-6]|PRE"; - this.sBlockContainer = "BODY|TABLE|TH|TR|TD|UL|OL|BLOCKQUOTE|FORM"; - - this.rxBlockElement = new RegExp("^("+this.sBlockElement+")$"); - this.rxBlockContainer = new RegExp("^("+this.sBlockContainer+")$"); - this.rxLineBreaker = new RegExp("^("+this.sBlockElement+"|"+this.sBlockContainer+")$"); - this.rxHasBlock = new RegExp("(?:<(?:"+this.sBlockElement+"|"+this.sBlockContainer+").*?>|style=[\"']?[^>]*?(?:display\\s?:\\s?block)[^>]*?[\"']?)", "i"); - - this.setWindow(win); - }, - - select : function(){ - try{ - this.oBrowserSelection.selectRange(this); - }catch(e){} - }, - - setFromSelection : function(iNum){ - this.setRange(this.oBrowserSelection.getRangeAt(iNum), true); - }, - - setRange : function(oW3CRange, bSafe){ - this.reset(this._window); - - this.setStart(oW3CRange.startContainer, oW3CRange.startOffset, bSafe, true); - this.setEnd(oW3CRange.endContainer, oW3CRange.endOffset, bSafe); - }, - - setEndNodes : function(oSNode, oENode){ - this.reset(this._window); - - this.setEndAfter(oENode, true); - this.setStartBefore(oSNode); - }, - - splitTextAtBothEnds : function(){ - this._splitTextEndNodesOfTheRange(); - }, - - getStartNode : function(){ - if(this.collapsed){ - if(this.startContainer.nodeType == 3){ - if(this.startOffset === 0){return null;} - if(this.startContainer.nodeValue.length <= this.startOffset){return null;} - return this.startContainer; - } - return null; - } - - if(this.startContainer.nodeType == 3){ - if(this.startOffset >= this.startContainer.nodeValue.length){return this._getNextNode(this.startContainer);} - return this.startContainer; - }else{ - if(this.startOffset >= nhn.DOMFix.childNodes(this.startContainer).length){return this._getNextNode(this.startContainer);} - return nhn.DOMFix.childNodes(this.startContainer)[this.startOffset]; - } - }, - - getEndNode : function(){ - if(this.collapsed){return this.getStartNode();} - - if(this.endContainer.nodeType == 3){ - if(this.endOffset === 0){return this._getPrevNode(this.endContainer);} - return this.endContainer; - }else{ - if(this.endOffset === 0){return this._getPrevNode(this.endContainer);} - return nhn.DOMFix.childNodes(this.endContainer)[this.endOffset-1]; - } - }, - - getNodeAroundRange : function(bBefore, bStrict){ - if(!this.collapsed){return this.getStartNode();} - - if(this.startContainer && this.startContainer.nodeType == 3){return this.startContainer;} - //if(this.collapsed && this.startContainer && this.startContainer.nodeType == 3) return this.startContainer; - //if(!this.collapsed || (this.startContainer && this.startContainer.nodeType == 3)) return this.getStartNode(); - - var oBeforeRange, oAfterRange, oResult; - - if(this.startOffset >= nhn.DOMFix.childNodes(this.startContainer).length){ - oAfterRange = this._getNextNode(this.startContainer); - }else{ - oAfterRange = nhn.DOMFix.childNodes(this.startContainer)[this.startOffset]; - } - - if(this.endOffset === 0){ - oBeforeRange = this._getPrevNode(this.endContainer); - }else{ - oBeforeRange = nhn.DOMFix.childNodes(this.endContainer)[this.endOffset-1]; - } - - if(bBefore){ - oResult = oBeforeRange; - if(!oResult && !bStrict){oResult = oAfterRange;} - }else{ - oResult = oAfterRange; - if(!oResult && !bStrict){oResult = oBeforeRange;} - } - - return oResult; - }, - - _getXPath : function(elNode){ - var sXPath = ""; - - while(elNode && elNode.nodeType == 1){ - sXPath = "/" + elNode.tagName+"["+this._getPosIdx4XPath(elNode)+"]" + sXPath; - elNode = nhn.DOMFix.parentNode(elNode); - } - - return sXPath; - }, - - _getPosIdx4XPath : function(refNode){ - var idx = 0; - for(var node = refNode.previousSibling; node; node = node.previousSibling){ - if(node.tagName == refNode.tagName){idx++;} - } - - return idx; - }, - - // this was written specifically for XPath Bookmark and it may not perform correctly for general purposes - _evaluateXPath : function(sXPath, oDoc){ - sXPath = sXPath.substring(1, sXPath.length-1); - var aXPath = sXPath.split(/\//); - var elNode = oDoc.body; - - for(var i=2; i -1 && elContainer){ - var aChildNodes = nhn.DOMFix.childNodes(elContainer); - var elNode = null; - - var nIdx = nTextNodeIdx; - var nOffsetLeft = nOffset; - - while((elNode = aChildNodes[nIdx]) && elNode.nodeType == 3 && elNode.nodeValue.length < nOffsetLeft){ - nOffsetLeft -= elNode.nodeValue.length; - nIdx++; - } - - elContainer = nhn.DOMFix.childNodes(elContainer)[nIdx]; - nOffset = nOffsetLeft; - } - - if(!elContainer){ - elContainer = this._document.body; - nOffset = 0; - } - return {elContainer: elContainer, nOffset: nOffset}; - }, - - // this was written specifically for XPath Bookmark and it may not perform correctly for general purposes - getXPathBookmark : function(){ - var nTextNodeIdx1 = -1; - var htEndPt1 = {elContainer: this.startContainer, nOffset: this.startOffset}; - var elNode1 = this.startContainer; - if(elNode1.nodeType == 3){ - htEndPt1 = this._getFixedStartTextNode(); - nTextNodeIdx1 = this._getPosIdx(htEndPt1.elContainer); - elNode1 = nhn.DOMFix.parentNode(elNode1); - } - var sXPathNode1 = this._getXPath(elNode1); - var oBookmark1 = {sXPath:sXPathNode1, nTextNodeIdx:nTextNodeIdx1, nOffset: htEndPt1.nOffset}; - - if(this.collapsed){ - var oBookmark2 = {sXPath:sXPathNode1, nTextNodeIdx:nTextNodeIdx1, nOffset: htEndPt1.nOffset}; - }else{ - var nTextNodeIdx2 = -1; - var htEndPt2 = {elContainer: this.endContainer, nOffset: this.endOffset}; - var elNode2 = this.endContainer; - if(elNode2.nodeType == 3){ - htEndPt2 = this._getFixedEndTextNode(); - nTextNodeIdx2 = this._getPosIdx(htEndPt2.elContainer); - elNode2 = nhn.DOMFix.parentNode(elNode2); - } - var sXPathNode2 = this._getXPath(elNode2); - var oBookmark2 = {sXPath:sXPathNode2, nTextNodeIdx:nTextNodeIdx2, nOffset: htEndPt2.nOffset}; - } - return [oBookmark1, oBookmark2]; - }, - - moveToXPathBookmark : function(aBookmark){ - if(!aBookmark){return false;} - - var oBookmarkInfo1 = this._evaluateXPathBookmark(aBookmark[0]); - var oBookmarkInfo2 = this._evaluateXPathBookmark(aBookmark[1]); - - if(!oBookmarkInfo1["elContainer"] || !oBookmarkInfo2["elContainer"]){return;} - - this.startContainer = oBookmarkInfo1["elContainer"]; - this.startOffset = oBookmarkInfo1["nOffset"]; - - this.endContainer = oBookmarkInfo2["elContainer"]; - this.endOffset = oBookmarkInfo2["nOffset"]; - - return true; - }, - - _getFixedTextContainer : function(elNode, nOffset){ - while(elNode && elNode.nodeType == 3 && elNode.previousSibling && elNode.previousSibling.nodeType == 3){ - nOffset += elNode.previousSibling.nodeValue.length; - elNode = elNode.previousSibling; - } - - return {elContainer:elNode, nOffset:nOffset}; - }, - - _getFixedStartTextNode : function(){ - return this._getFixedTextContainer(this.startContainer, this.startOffset); - }, - - _getFixedEndTextNode : function(){ - return this._getFixedTextContainer(this.endContainer, this.endOffset); - }, - - placeStringBookmark : function(){ - if(this.collapsed || jindo.$Agent().navigator().ie || jindo.$Agent().navigator().firefox){ - return this.placeStringBookmark_NonWebkit(); - }else{ - return this.placeStringBookmark_Webkit(); - } - }, - - placeStringBookmark_NonWebkit : function(){ - var sTmpId = (new Date()).getTime(); - - var oInsertionPoint = this.cloneRange(); - oInsertionPoint.collapseToEnd(); - var oEndMarker = this._document.createElement("SPAN"); - oEndMarker.id = this.HUSKY_BOOMARK_END_ID_PREFIX+sTmpId; - oInsertionPoint.insertNode(oEndMarker); - - var oInsertionPoint = this.cloneRange(); - oInsertionPoint.collapseToStart(); - var oStartMarker = this._document.createElement("SPAN"); - oStartMarker.id = this.HUSKY_BOOMARK_START_ID_PREFIX+sTmpId; - oInsertionPoint.insertNode(oStartMarker); - - // IE에서 빈 SPAN의 앞뒤로 커서가 이동하지 않아 문제가 발생 할 수 있어, 보이지 않는 특수 문자를 임시로 넣어 줌. - if(jindo.$Agent().navigator().ie){ - // SPAN의 위치가 TD와 TD 사이에 있을 경우, 텍스트 삽입 시 알수 없는 오류가 발생한다. - // TD와 TD사이에서는 텍스트 삽입이 필요 없음으로 그냥 try/catch로 처리 - try{ - oStartMarker.innerHTML = unescape("%uFEFF"); - }catch(e){} - - try{ - oEndMarker.innerHTML = unescape("%uFEFF"); - }catch(e){} - } - this.moveToBookmark(sTmpId); - - return sTmpId; - }, - - placeStringBookmark_Webkit : function(){ - var sTmpId = (new Date()).getTime(); - - var elInsertBefore, elInsertParent; - - // Do not insert the bookmarks between TDs as it will break the rendering in Chrome/Safari - // -> modify the insertion position from [abc]abc to [abc]abc - var oInsertionPoint = this.cloneRange(); - oInsertionPoint.collapseToEnd(); - elInsertBefore = this._document.createTextNode(""); - oInsertionPoint.insertNode(elInsertBefore); - elInsertParent = elInsertBefore.parentNode; - if(elInsertBefore.previousSibling && elInsertBefore.previousSibling.tagName == "TD"){ - elInsertParent = elInsertBefore.previousSibling; - elInsertBefore = null; - } - var oEndMarker = this._document.createElement("SPAN"); - oEndMarker.id = this.HUSKY_BOOMARK_END_ID_PREFIX+sTmpId; - elInsertParent.insertBefore(oEndMarker, elInsertBefore); - - var oInsertionPoint = this.cloneRange(); - oInsertionPoint.collapseToStart(); - elInsertBefore = this._document.createTextNode(""); - oInsertionPoint.insertNode(elInsertBefore); - elInsertParent = elInsertBefore.parentNode; - if(elInsertBefore.nextSibling && elInsertBefore.nextSibling.tagName == "TD"){ - elInsertParent = elInsertBefore.nextSibling; - elInsertBefore = elInsertParent.firstChild; - } - var oStartMarker = this._document.createElement("SPAN"); - oStartMarker.id = this.HUSKY_BOOMARK_START_ID_PREFIX+sTmpId; - elInsertParent.insertBefore(oStartMarker, elInsertBefore); - - //elInsertBefore.parentNode.removeChild(elInsertBefore); - - this.moveToBookmark(sTmpId); - - return sTmpId; - }, - - cloneRange : function(){ - return this._copyRange(new nhn.HuskyRange(this._window)); - }, - - moveToBookmark : function(vBookmark){ - if(typeof(vBookmark) != "object"){ - return this.moveToStringBookmark(vBookmark); - }else{ - return this.moveToXPathBookmark(vBookmark); - } - }, - - getStringBookmark : function(sBookmarkID, bEndBookmark){ - if(bEndBookmark){ - return this._document.getElementById(this.HUSKY_BOOMARK_END_ID_PREFIX+sBookmarkID); - }else{ - return this._document.getElementById(this.HUSKY_BOOMARK_START_ID_PREFIX+sBookmarkID); - } - }, - - moveToStringBookmark : function(sBookmarkID, bIncludeBookmark){ - var oStartMarker = this.getStringBookmark(sBookmarkID); - var oEndMarker = this.getStringBookmark(sBookmarkID, true); - - if(!oStartMarker || !oEndMarker){return false;} - - this.reset(this._window); - - if(bIncludeBookmark){ - this.setEndAfter(oEndMarker); - this.setStartBefore(oStartMarker); - }else{ - this.setEndBefore(oEndMarker); - this.setStartAfter(oStartMarker); - } - return true; - }, - - removeStringBookmark : function(sBookmarkID){ - /* - var oStartMarker = this._document.getElementById(this.HUSKY_BOOMARK_START_ID_PREFIX+sBookmarkID); - var oEndMarker = this._document.getElementById(this.HUSKY_BOOMARK_END_ID_PREFIX+sBookmarkID); - - if(oStartMarker) nhn.DOMFix.parentNode(oStartMarker).removeChild(oStartMarker); - if(oEndMarker) nhn.DOMFix.parentNode(oEndMarker).removeChild(oEndMarker); - */ - this._removeAll(this.HUSKY_BOOMARK_START_ID_PREFIX+sBookmarkID); - this._removeAll(this.HUSKY_BOOMARK_END_ID_PREFIX+sBookmarkID); - }, - - _removeAll : function(sID){ - var elNode; - while((elNode = this._document.getElementById(sID))){ - elNode.parentNode.removeChild(elNode); - } - }, - - collapseToStart : function(){ - this.collapse(true); - }, - - collapseToEnd : function(){ - this.collapse(false); - }, - - createAndInsertNode : function(sTagName){ - var tmpNode = this._document.createElement(sTagName); - this.insertNode(tmpNode); - return tmpNode; - }, - - getNodes : function(bSplitTextEndNodes, fnFilter){ - if(bSplitTextEndNodes){this._splitTextEndNodesOfTheRange();} - - var aAllNodes = this._getNodesInRange(); - var aFilteredNodes = []; - - if(!fnFilter){return aAllNodes;} - - for(var i=0; i= 0){return true;} - - if(bIncludePartlySelected){ - if(startToEnd == 1){return false;} - if(endToStart == -1){return false;} - return true; - } - - return false; - }, - - isNodeInRange : function(oNode, bIncludePartlySelected, bContentOnly){ - var oTmpRange = new nhn.HuskyRange(this._window); - - if(bContentOnly && oNode.firstChild){ - oTmpRange.setStartBefore(oNode.firstChild); - oTmpRange.setEndAfter(oNode.lastChild); - }else{ - oTmpRange.selectNode(oNode); - } - - return this.isRangeInRange(oTmpRange, bIncludePartlySelected); - }, - - pasteText : function(sText){ - this.pasteHTML(sText.replace(/&/g, "&").replace(//g, ">").replace(/ /g, " ").replace(/"/g, """)); - }, - - /** - * TODO: 왜 clone 으로 조작할까? - * @param {String} sHTML 삽입할 HTML - * @param {Boolean} bBlock HTML 삽입시 강제로 block 요소 처리할지 여부(true 이면 P태그 안에 삽입될 경우, P태그를 무조건 쪼개고 그 사이에 DIV태그로 감싸서 삽입한다.) - */ - pasteHTML : function(sHTML, bBlock){ - var oTmpDiv = this._document.createElement("DIV"); - oTmpDiv.innerHTML = sHTML; - - if(!oTmpDiv.firstChild){ - this.deleteContents(); - return; - } - - // getLineInfo 전에 북마크를 삽입하지 않으면 IE에서 oLineBreaker가 P태그 바깥으로 잡히는 경우가 있음(P태그에 아무것도 없을때) - var clone = this.cloneRange(); - var sBM = clone.placeStringBookmark(); - - // [SMARTEDITORSUS-1960] PrivateTag, 템플릿삽입등 P태그안에 block 요소 삽입과 관련된 처리 - // P태그인 경우, block요소가 들어오면 안된다. - // 때문에 현재 위치의 컨테이너가 P태그이고 컨텐츠 내용이 block 요소인 경우 P태그를 쪼개고 그 사이에 컨텐츠를 DIV로 감싸서 넣도록 처리한다. - // [SMARTEDITORSUS-2026][SMARTEDITORSUS-2061] bBlock = true 이면 삽입되는 HTML 이 block 요소가 아니더라도 무조건 P태그를 쪼개서 DIV로 감싸도록 한다. - var oLineInfo = clone.getLineInfo(), - oStart = oLineInfo.oStart, - oEnd = oLineInfo.oEnd; - if(oStart.oLineBreaker && oStart.oLineBreaker.nodeName === "P" && (bBlock || clone.rxHasBlock.test(sHTML))){ - // [SMARTEDITORSUS-2169] 선택영역 삭제시 oStart.oLineBreaker도 DOM 트리에서 제거될 수 있기 때문에 필요한 노드를 미리 참조해둔다. - var oParentNode = oStart.oLineBreaker.parentNode, - oNextSibling = oStart.oLineBreaker.nextSibling; - - // 선택영역을 조작해야 하므로 현재 선택된 요소들을 제거한다. - clone.deleteContents(); - - // 동일한 라인에 있으면 뒷부분은 쪼개서 다음 라인으로 삽입한다. - if(oStart.oLineBreaker === oEnd.oLineBreaker){ - var elBM = clone.getStringBookmark(sBM); - clone.setEndNodes(elBM, oEnd.oLineBreaker); - var oNextContents = clone.extractContents(), - oNextFirst = oNextContents.firstChild; // oNextSibling 을 교체하기 위해 쪼개진 요소 첫번째 노드를 미리 참조해둔다. - - // 쪼갠 부분을 삽입하고 - if(oNextSibling){ - oParentNode.insertBefore(oNextContents, oNextSibling); - }else{ - oParentNode.appendChild(oNextContents); - } - // [SMARTEDITORSUS-2145] oNextSibling 을 쪼갠 부분의 첫번째 노드로 교체한다. - oNextSibling = oNextFirst; - } - - // 선택영역 앞쪽이 속한 P태그에서 style과 align 정보를 복사한다. - // 크롬의 경우 div의 style 에 text-align 이 있으면 align 속성은 무시되는데 - // div 안의 block 요소는 text-align 의 대상이 아니라 정렬되지 않는 문제가 있기 때문에 - // style 복사할 때 text-align 속성은 제외한다. - oTmpDiv.style.cssText = oStart.oLineBreaker.style.cssText.replace(this._rxTextAlign, ''); // text-align 제외 - oTmpDiv.align = oStart.oLineBreaker.align; // align 복사 - - // 컨텐츠 삽입 - if(oNextSibling){ - oParentNode.insertBefore(oTmpDiv, oNextSibling); - }else{ - oParentNode.appendChild(oTmpDiv); - } - - // 컨텐츠 삽입 후에 북마크를 지운다. - // 컨텐츠 삽입 전에 지우면 컨텐츠 삽입시 oNextSibling 가 북마크로 잡히는 경우 에러가 발생할 수 있음 - clone.removeStringBookmark(sBM); - - // 컨텐츠 삽입 후 윗라인 P태그에 아무런 내용이 없으면 제거한다. - this._removeEmptyP(this._getPrevElement(oTmpDiv)); - // 아래 라인 P태그에 아무런 내용이 없는 경우는 그 다음 아래 라인이 있을때만 제거한다. - // 아래 라인이 아예없으면 IE에서 커서가 들어가지 않기 때문에 라인을 추가해준다. - var elNextLine = this._getNextElement(oTmpDiv); - if(elNextLine){ - var elAfterNext = this._getNextElement(elNextLine); - if(elAfterNext && this._removeEmptyP(elNextLine)){ - elNextLine = elAfterNext; // 제거되었을 경우만 elNextLine 재할당 - } - }else{ - // 아래 라인이 없으면 윗 라인 스타일을 복사하여 추가해준다. - elNextLine = this._document.createElement("P"); - elNextLine.style.cssText = oStart.oLineBreaker.style.cssText; - elNextLine.align = oStart.oLineBreaker.align; - oParentNode.appendChild(elNextLine); - } - - // 커서를 다음라인으로 앞쪽으로 위치시킨다. - if(elNextLine.innerHTML === ""){ - // 크롬에서 빈

    를 선택해서 collapseToStart 하면 윗라인으로 이동하기 때문에 비어있으면 \uFEFF 를 넣어준다. - elNextLine.innerHTML = (jindo.$Agent().navigator().ie && jindo.$Agent().navigator().version > 8) ? "\u200B" : "\uFEFF"; - } - this.selectNodeContents(elNextLine); - this.collapseToStart(); - - // IE7에서 커서가 다음라인 p태그 앞쪽이 아닌 div태그 끝쪽으로 자동으로 옮겨가는 경우가 있어서 - // 커서가 멋대로 이동하지 않도록 임시북마크를 넣었다가 바로 빼준다. - // (주의) 북마크를 넣었다 빼면 IE10은 다음라인 p태그 끝쪽으로 이동되기 때문에 IE7인 경우만 넣어줌 - // [SMARTEDITORSUS-2043] SE_EditingArea_WYSIWYG.$ON_PASTE_HTML 에서 IE8의 경우만 삽입시 뒤에 \uFEFF가 추가로 붙어서 들어오는데 - // 이로 인해 템플릿과 커서사이가 한줄 벌어지는 문제가 있어서 \uFEFF 추가하는 부분을 삭제하니 커서가 IE7과 동일하게 동작하여 IE8도 임시북마크처리함 - if(jindo.$Agent().navigator().ie && jindo.$Agent().navigator().version < 9){ - sBM = this.placeStringBookmark(); - this.removeStringBookmark(sBM); - } - }else{ - var oFirstNode = oTmpDiv.firstChild; - var oLastNode = oTmpDiv.lastChild; - - this.collapseToStart(); - - while(oTmpDiv.lastChild){this.insertNode(oTmpDiv.lastChild);} - - this.setEndNodes(oFirstNode, oLastNode); - - // delete the content later as deleting it first may mass up the insertion point - // eg)

    [A]BCD

    ---paste O---> O

    BCD

    - clone.moveToBookmark(sBM); - clone.deleteContents(); - clone.removeStringBookmark(sBM); - } - }, - - /** - * 비어있는 P태그이면 제거한다. - * @param {Element} el 검사할 Element - * @returns {Boolean} 제거되었다면 true 를 반환한다. - */ - _removeEmptyP : function(el){ - if(el && el.nodeName === "P"){ - var sInner = el.innerHTML; - if(sInner === "" || this._rxCursorHolder.test(sInner)){ - el.parentNode.removeChild(el); - return true; - } - } - }, - - /** - * 인접한 Element 노드를 찾는다. - * @param {Node} oNode 기준 노드 - * @param {Boolean} bPrev 앞뒤여부(true면 앞, false면 뒤) - * @return {Element} 인접한 Element, 없으면 null 반환 - */ - _getSiblingElement : function(oNode, bPrev){ - if(!oNode){ - return null; - } - - var oSibling = oNode[bPrev ? "previousSibling" : "nextSibling"]; - if(oSibling && oSibling.nodeType === 1){ - return oSibling; - }else{ - return arguments.callee(oSibling, bPrev); - } - }, - - /** - * 앞쪽 인접한 Element 노드를 찾는다. - * @param {Node} oNode 기준 노드 - * @return {Element} 인접한 Element, 없으면 null 반환 - */ - _getPrevElement : function(oNode){ - return this._getSiblingElement(oNode, true); - }, - - /** - * 뒤쪽 인접한 Element 노드를 찾는다. - * @param {Node} oNode 기준 노드 - * @return {Element} 인접한 Element, 없으면 null 반환 - */ - _getNextElement : function(oNode){ - return this._getSiblingElement(oNode, false); - }, - - toString : function(){ - this.toString = nhn.W3CDOMRange.prototype.toString; - return this.toString(); - }, - - toHTMLString : function(){ - var oTmpContainer = this._document.createElement("DIV"); - oTmpContainer.appendChild(this.cloneContents()); - - return oTmpContainer.innerHTML; - }, - - findAncestorByTagName : function(sTagName){ - var oNode = this.commonAncestorContainer; - while(oNode && oNode.tagName != sTagName){oNode = nhn.DOMFix.parentNode(oNode);} - - return oNode; - }, - - selectNodeContents : function(oNode){ - if(!oNode){return;} - - var oFirstNode = oNode.firstChild?oNode.firstChild:oNode; - var oLastNode = oNode.lastChild?oNode.lastChild:oNode; - - this.reset(this._window); - if(oFirstNode.nodeType == 3){ - this.setStart(oFirstNode, 0, true); - }else{ - this.setStartBefore(oFirstNode); - } - - if(oLastNode.nodeType == 3){ - this.setEnd(oLastNode, oLastNode.nodeValue.length, true); - }else{ - this.setEndAfter(oLastNode); - } - }, - - /** - * 노드의 취소선/밑줄 정보를 확인한다 - * 관련 BTS [SMARTEDITORSUS-26] - * @param {Node} oNode 취소선/밑줄을 확인할 노드 - * @param {String} sValue textDecoration 정보 - * @see nhn.HuskyRange#_checkTextDecoration - */ - _hasTextDecoration : function(oNode, sValue){ - if(!oNode || !oNode.style){ - return false; - } - - if(oNode.style.textDecoration.indexOf(sValue) > -1){ - return true; - } - - if(sValue === "underline" && oNode.tagName === "U"){ - return true; - } - - if(sValue === "line-through" && (oNode.tagName === "S" || oNode.tagName === "STRIKE")){ - return true; - } - - return false; - }, - - /** - * 노드에 취소선/밑줄을 적용한다 - * 관련 BTS [SMARTEDITORSUS-26] - * [FF] 노드의 Style 에 textDecoration 을 추가한다 - * [FF 외] U/STRIKE 태그를 추가한다 - * @param {Node} oNode 취소선/밑줄을 적용할 노드 - * @param {String} sValue textDecoration 정보 - * @see nhn.HuskyRange#_checkTextDecoration - */ - _setTextDecoration : function(oNode, sValue){ - if (jindo.$Agent().navigator().firefox) { // FF - oNode.style.textDecoration = (oNode.style.textDecoration) ? oNode.style.textDecoration + " " + sValue : sValue; - } - else{ - if(sValue === "underline"){ - oNode.innerHTML = "" + oNode.innerHTML + "" - }else if(sValue === "line-through"){ - oNode.innerHTML = "" + oNode.innerHTML + "" - } - } - }, - - /** - * 인자로 전달받은 노드 상위의 취소선/밑줄 정보를 확인하여 노드에 적용한다 - * 관련 BTS [SMARTEDITORSUS-26] - * @param {Node} oNode 취소선/밑줄을 적용할 노드 - */ - _checkTextDecoration : function(oNode){ - if(oNode.tagName !== "SPAN"){ - return; - } - - var bUnderline = false, - bLineThrough = false, - sTextDecoration = "", - oParentNode = null; - oChildNode = oNode.firstChild; - - /* check child */ - while(oChildNode){ - if(oChildNode.nodeType === 1){ - bUnderline = (bUnderline || oChildNode.tagName === "U"); - bLineThrough = (bLineThrough || oChildNode.tagName === "S" || oChildNode.tagName === "STRIKE"); - } - - if(bUnderline && bLineThrough){ - return; - } - - oChildNode = oChildNode.nextSibling; - } - - oParentNode = nhn.DOMFix.parentNode(oNode); - - /* check parent */ - while(oParentNode && oParentNode.tagName !== "BODY"){ - if(oParentNode.nodeType !== 1){ - oParentNode = nhn.DOMFix.parentNode(oParentNode); - continue; - } - - if(!bUnderline && this._hasTextDecoration(oParentNode, "underline")){ - bUnderline = true; - this._setTextDecoration(oNode, "underline"); // set underline - } - - if(!bLineThrough && this._hasTextDecoration(oParentNode, "line-through")){ - bLineThrough = true; - this._setTextDecoration(oNode, "line-through"); // set line-through - } - - if(bUnderline && bLineThrough){ - return; - } - - oParentNode = nhn.DOMFix.parentNode(oParentNode); - } - }, - - /** - * Range에 속한 노드들에 스타일을 적용한다 - * @param {Object} oStyle 적용할 스타일을 가지는 Object (예) 글꼴 색 적용의 경우 { color : "#0075c8" } - * @param {Object} [oAttribute] 적용할 속성을 가지는 Object (예) 맞춤범 검사의 경우 { _sm2_spchk: "강남콩", class: "se2_check_spell" } - * @param {String} [sNewSpanMarker] 새로 추가된 SPAN 노드를 나중에 따로 처리해야하는 경우 마킹을 위해 사용하는 문자열 - * @param {Boolean} [bIncludeLI] LI 도 스타일 적용에 포함할 것인지의 여부 [COM-1051] _getStyleParentNodes 메서드 참고하기 - * @param {Boolean} [bCheckTextDecoration] 취소선/밑줄 처리를 적용할 것인지 여부 [SMARTEDITORSUS-26] _setTextDecoration 메서드 참고하기 - */ - styleRange : function(oStyle, oAttribute, sNewSpanMarker, bIncludeLI, bCheckTextDecoration){ - var aStyleParents = this.aStyleParents = this._getStyleParentNodes(sNewSpanMarker, bIncludeLI); - if(aStyleParents.length < 1){return;} - - var sName, sValue; - - for(var i=0; i 1){ // 싱글노드가 아니면 더이상 찾지 않고 null 반환 - return null; - } - } - if(oNode.nodeName === "SPAN"){ - return oNode; - }else{ - return this._findParentSingleSpan(oNode.parentNode); - } - }, - - /** - * 컨테이너 엘리먼트(elContainer)의 모든 자식노드가 노드 배열(waAllNodes)에 속하는지 확인한다 - * 첫 번째 자식 노드와 마지막 자식 노드가 노드 배열에 속하는지를 확인한다 - * @param {Element} elContainer 컨테이너 엘리먼트 - * @param {jindo.$A} waAllNodes Node 의 $A 배열 - * @param {Node} [oNode] 성능을 위한 옵션 노드로 컨테이너의 첫 번째 혹은 마지막 자식 노드와 같으면 indexOf 함수 사용을 줄일 수 있음 - * @return {Array} Style 을 적용할 노드 배열 - */ - // check if all the child nodes of elContainer are in waAllNodes - _isFullyContained : function(elContainer, waAllNodes, oNode){ - var nSIdx, nEIdx; - var oTmpNode = this._getVeryFirstRealChild(elContainer); - // do quick checks before trying indexOf() because indexOf() function is very slow - // oNode is optional - if(oNode && oTmpNode == oNode){ - nSIdx = 1; - }else{ - nSIdx = waAllNodes.indexOf(oTmpNode); - } - - if(nSIdx != -1){ - oTmpNode = this._getVeryLastRealChild(elContainer); - if(oNode && oTmpNode == oNode){ - nEIdx = 1; - }else{ - nEIdx = waAllNodes.indexOf(oTmpNode); - } - } - - return (nSIdx != -1 && nEIdx != -1); - }, - - _getVeryFirstChild : function(oNode){ - if(oNode.firstChild){return this._getVeryFirstChild(oNode.firstChild);} - return oNode; - }, - - _getVeryLastChild : function(oNode){ - if(oNode.lastChild){return this._getVeryLastChild(oNode.lastChild);} - return oNode; - }, - - _getFirstRealChild : function(oNode){ - var oFirstNode = oNode.firstChild; - while(oFirstNode && oFirstNode.nodeType == 3 && oFirstNode.nodeValue == ""){oFirstNode = oFirstNode.nextSibling;} - - return oFirstNode; - }, - - _getLastRealChild : function(oNode){ - var oLastNode = oNode.lastChild; - while(oLastNode && oLastNode.nodeType == 3 && oLastNode.nodeValue == ""){oLastNode = oLastNode.previousSibling;} - - return oLastNode; - }, - - _getVeryFirstRealChild : function(oNode){ - var oFirstNode = this._getFirstRealChild(oNode); - if(oFirstNode){return this._getVeryFirstRealChild(oFirstNode);} - return oNode; - }, - _getVeryLastRealChild : function(oNode){ - var oLastNode = this._getLastRealChild(oNode); - if(oLastNode){return this._getVeryLastChild(oLastNode);} - return oNode; - }, - - _getLineStartInfo : function(node){ - var frontEndFinal = null; - var frontEnd = node; - var lineBreaker = node; - var bParentBreak = false; - - var rxLineBreaker = this.rxLineBreaker; - - // vertical(parent) search - function getLineStart(node){ - if(!node){return;} - if(frontEndFinal){return;} - - if(rxLineBreaker.test(node.tagName)){ - lineBreaker = node; - frontEndFinal = frontEnd; - - bParentBreak = true; - - return; - }else{ - frontEnd = node; - } - - getFrontEnd(node.previousSibling); - - if(frontEndFinal){return;} - getLineStart(nhn.DOMFix.parentNode(node)); - } - - // horizontal(sibling) search - function getFrontEnd(node){ - if(!node){return;} - if(frontEndFinal){return;} - - if(rxLineBreaker.test(node.tagName)){ - lineBreaker = node; - frontEndFinal = frontEnd; - - bParentBreak = false; - return; - } - - if(node.firstChild && node.tagName != "TABLE"){ - var curNode = node.lastChild; - while(curNode && !frontEndFinal){ - getFrontEnd(curNode); - - curNode = curNode.previousSibling; - } - }else{ - frontEnd = node; - } - - if(!frontEndFinal){ - getFrontEnd(node.previousSibling); - } - } - - if(rxLineBreaker.test(node.tagName)){ - frontEndFinal = node; - }else{ - getLineStart(node); - } - - return {oNode: frontEndFinal, oLineBreaker: lineBreaker, bParentBreak: bParentBreak}; - }, - - _getLineEndInfo : function(node){ - var backEndFinal = null; - var backEnd = node; - var lineBreaker = node; - var bParentBreak = false; - - var rxLineBreaker = this.rxLineBreaker; - - // vertical(parent) search - function getLineEnd(node){ - if(!node){return;} - if(backEndFinal){return;} - - if(rxLineBreaker.test(node.tagName)){ - lineBreaker = node; - backEndFinal = backEnd; - - bParentBreak = true; - - return; - }else{ - backEnd = node; - } - - getBackEnd(node.nextSibling); - if(backEndFinal){return;} - - getLineEnd(nhn.DOMFix.parentNode(node)); - } - - // horizontal(sibling) search - function getBackEnd(node){ - if(!node){return;} - if(backEndFinal){return;} - - if(rxLineBreaker.test(node.tagName)){ - lineBreaker = node; - backEndFinal = backEnd; - - bParentBreak = false; - - return; - } - - if(node.firstChild && node.tagName != "TABLE"){ - var curNode = node.firstChild; - while(curNode && !backEndFinal){ - getBackEnd(curNode); - - curNode = curNode.nextSibling; - } - }else{ - backEnd = node; - } - - if(!backEndFinal){ - getBackEnd(node.nextSibling); - } - } - - if(rxLineBreaker.test(node.tagName)){ - backEndFinal = node; - }else{ - getLineEnd(node); - } - - return {oNode: backEndFinal, oLineBreaker: lineBreaker, bParentBreak: bParentBreak}; - }, - - getLineInfo : function(bAfter){ - var bAfter = bAfter || false; - - var oSNode = this.getStartNode(); - var oENode = this.getEndNode(); - - // oSNode && oENode will be null if the range is currently collapsed and the cursor is not located in the middle of a text node. - if(!oSNode){oSNode = this.getNodeAroundRange(!bAfter, true);} - if(!oENode){oENode = this.getNodeAroundRange(!bAfter, true);} - - var oStart = this._getLineStartInfo(oSNode); - var oStartNode = oStart.oNode; - var oEnd = this._getLineEndInfo(oENode); - var oEndNode = oEnd.oNode; - - if(oSNode != oStartNode || oENode != oEndNode){ - // check if the start node is positioned after the range's ending point - // or - // if the end node is positioned before the range's starting point - var iRelativeStartPos = this._compareEndPoint(nhn.DOMFix.parentNode(oStartNode), this._getPosIdx(oStartNode), this.endContainer, this.endOffset); - var iRelativeEndPos = this._compareEndPoint(nhn.DOMFix.parentNode(oEndNode), this._getPosIdx(oEndNode)+1, this.startContainer, this.startOffset); - - if(!(iRelativeStartPos <= 0 && iRelativeEndPos >= 0)){ - oSNode = this.getNodeAroundRange(false, true); - oENode = this.getNodeAroundRange(false, true); - oStart = this._getLineStartInfo(oSNode); - oEnd = this._getLineEndInfo(oENode); - } - } - - return {oStart: oStart, oEnd: oEnd}; - }, - - /** - * 커서홀더나 공백을 제외한 child 노드가 하나만 있는 경우만 node 를 반환한다. - * @param {Node} oNode 확인할 노드 - * @return {Node} single child node를 반환한다. 없거나 두개 이상이면 null 을 반환 - */ - _findSingleChild : function(oNode){ - if(!oNode){ - return null; - } - var oSingleChild = null; - // ZWNBSP 문자가 같이 있는 경우도 있기 때문에 실제 노드를 카운팅해야 함 - for(var i = 0, nCnt = 0, sValue, oChild, aChildNodes = oNode.childNodes; (oChild = aChildNodes[i]); i++){ - sValue = oChild.nodeValue; - if(this._rxCursorHolder.test(sValue)){ - continue; - }else{ - oSingleChild = oChild; - nCnt++; - } - if(nCnt > 1){ // 싱글노드가 아니면 더이상 찾지 않고 null 반환 - return null; - } - } - return oSingleChild; - }, - - /** - * 해당요소의 최하위까지 검색해 커서홀더만 감싸고 있는지 여부를 반환 - * @param {Node} oNode 확인할 노드 - * @return {Boolean} 커서홀더만 있는 경우 true 반환 - */ - _hasCursorHolderOnly : function(oNode){ - if(!oNode || oNode.nodeType !== 1){ - return false; - } - if(this._rxCursorHolder.test(oNode.innerHTML)){ - return true; - }else{ - return this._hasCursorHolderOnly(this._findSingleChild(oNode)); - } - } + _rxCursorHolder: /^(?:\uFEFF|\u00A0|\u200B|
    )$/i, + _rxTextAlign: /text-align:[^"';]*;?/i, + + setWindow: function (win) { + this.reset(win || window); + }, + + $init: function (win) { + this.HUSKY_BOOMARK_START_ID_PREFIX = "husky_bookmark_start_"; + this.HUSKY_BOOMARK_END_ID_PREFIX = "husky_bookmark_end_"; + + this.sBlockElement = "P|DIV|LI|H[1-6]|PRE"; + this.sBlockContainer = "BODY|TABLE|TH|TR|TD|UL|OL|BLOCKQUOTE|FORM"; + + this.rxBlockElement = new RegExp("^(" + this.sBlockElement + ")$"); + this.rxBlockContainer = new RegExp("^(" + this.sBlockContainer + ")$"); + this.rxLineBreaker = new RegExp("^(" + this.sBlockElement + "|" + this.sBlockContainer + ")$"); + this.rxHasBlock = new RegExp("(?:<(?:" + this.sBlockElement + "|" + this.sBlockContainer + ").*?>|style=[\"']?[^>]*?(?:display\\s?:\\s?block)[^>]*?[\"']?)", "i"); + + this.setWindow(win); + }, + + select: function () { + try { + this.oBrowserSelection.selectRange(this); + } catch (e) { } + }, + + setFromSelection: function (iNum) { + this.setRange(this.oBrowserSelection.getRangeAt(iNum), true); + }, + + setRange: function (oW3CRange, bSafe) { + this.reset(this._window); + + this.setStart(oW3CRange.startContainer, oW3CRange.startOffset, bSafe, true); + this.setEnd(oW3CRange.endContainer, oW3CRange.endOffset, bSafe); + }, + + setEndNodes: function (oSNode, oENode) { + this.reset(this._window); + + this.setEndAfter(oENode, true); + this.setStartBefore(oSNode); + }, + + splitTextAtBothEnds: function () { + this._splitTextEndNodesOfTheRange(); + }, + + getStartNode: function () { + if (this.collapsed) { + if (this.startContainer.nodeType == 3) { + if (this.startOffset === 0) { return null; } + if (this.startContainer.nodeValue.length <= this.startOffset) { return null; } + return this.startContainer; + } + return null; + } + + if (this.startContainer.nodeType == 3) { + if (this.startOffset >= this.startContainer.nodeValue.length) { return this._getNextNode(this.startContainer); } + return this.startContainer; + } else { + if (this.startOffset >= nhn.DOMFix.childNodes(this.startContainer).length) { return this._getNextNode(this.startContainer); } + return nhn.DOMFix.childNodes(this.startContainer)[this.startOffset]; + } + }, + + getEndNode: function () { + if (this.collapsed) { return this.getStartNode(); } + + if (this.endContainer.nodeType == 3) { + if (this.endOffset === 0) { return this._getPrevNode(this.endContainer); } + return this.endContainer; + } else { + if (this.endOffset === 0) { return this._getPrevNode(this.endContainer); } + return nhn.DOMFix.childNodes(this.endContainer)[this.endOffset - 1]; + } + }, + + getNodeAroundRange: function (bBefore, bStrict) { + if (!this.collapsed) { return this.getStartNode(); } + + if (this.startContainer && this.startContainer.nodeType == 3) { return this.startContainer; } + //if(this.collapsed && this.startContainer && this.startContainer.nodeType == 3) return this.startContainer; + //if(!this.collapsed || (this.startContainer && this.startContainer.nodeType == 3)) return this.getStartNode(); + + var oBeforeRange, oAfterRange, oResult; + + if (this.startOffset >= nhn.DOMFix.childNodes(this.startContainer).length) { + oAfterRange = this._getNextNode(this.startContainer); + } else { + oAfterRange = nhn.DOMFix.childNodes(this.startContainer)[this.startOffset]; + } + + if (this.endOffset === 0) { + oBeforeRange = this._getPrevNode(this.endContainer); + } else { + oBeforeRange = nhn.DOMFix.childNodes(this.endContainer)[this.endOffset - 1]; + } + + if (bBefore) { + oResult = oBeforeRange; + if (!oResult && !bStrict) { oResult = oAfterRange; } + } else { + oResult = oAfterRange; + if (!oResult && !bStrict) { oResult = oBeforeRange; } + } + + return oResult; + }, + + _getXPath: function (elNode) { + var sXPath = ""; + + while (elNode && elNode.nodeType == 1) { + sXPath = "/" + elNode.tagName + "[" + this._getPosIdx4XPath(elNode) + "]" + sXPath; + elNode = nhn.DOMFix.parentNode(elNode); + } + + return sXPath; + }, + + _getPosIdx4XPath: function (refNode) { + var idx = 0; + for (var node = refNode.previousSibling; node; node = node.previousSibling) { + if (node.tagName == refNode.tagName) { idx++; } + } + + return idx; + }, + + // this was written specifically for XPath Bookmark and it may not perform correctly for general purposes + _evaluateXPath: function (sXPath, oDoc) { + sXPath = sXPath.substring(1, sXPath.length - 1); + var aXPath = sXPath.split(/\//); + var elNode = oDoc.body; + + for (var i = 2; i < aXPath.length && elNode; i++) { + aXPath[i].match(/([^\[]+)\[(\d+)/i); + var sTagName = RegExp.$1; + var nIdx = RegExp.$2; + + var aAllNodes = nhn.DOMFix.childNodes(elNode); + var aNodes = []; + var nLength = aAllNodes.length; + var nCount = 0; + for (var ii = 0; ii < nLength; ii++) { + if (aAllNodes[ii].tagName == sTagName) { aNodes[nCount++] = aAllNodes[ii]; } + } + + if (aNodes.length < nIdx) { + elNode = null; + } else { + elNode = aNodes[nIdx]; + } + } + + return elNode; + }, + + _evaluateXPathBookmark: function (oBookmark) { + var sXPath = oBookmark["sXPath"]; + var nTextNodeIdx = oBookmark["nTextNodeIdx"]; + var nOffset = oBookmark["nOffset"]; + + var elContainer = this._evaluateXPath(sXPath, this._document); + + if (nTextNodeIdx > -1 && elContainer) { + var aChildNodes = nhn.DOMFix.childNodes(elContainer); + var elNode = null; + + var nIdx = nTextNodeIdx; + var nOffsetLeft = nOffset; + + while ((elNode = aChildNodes[nIdx]) && elNode.nodeType == 3 && elNode.nodeValue.length < nOffsetLeft) { + nOffsetLeft -= elNode.nodeValue.length; + nIdx++; + } + + elContainer = nhn.DOMFix.childNodes(elContainer)[nIdx]; + nOffset = nOffsetLeft; + } + + if (!elContainer) { + elContainer = this._document.body; + nOffset = 0; + } + return { elContainer: elContainer, nOffset: nOffset }; + }, + + // this was written specifically for XPath Bookmark and it may not perform correctly for general purposes + getXPathBookmark: function () { + var nTextNodeIdx1 = -1; + var htEndPt1 = { elContainer: this.startContainer, nOffset: this.startOffset }; + var elNode1 = this.startContainer; + if (elNode1.nodeType == 3) { + htEndPt1 = this._getFixedStartTextNode(); + nTextNodeIdx1 = this._getPosIdx(htEndPt1.elContainer); + elNode1 = nhn.DOMFix.parentNode(elNode1); + } + var sXPathNode1 = this._getXPath(elNode1); + var oBookmark1 = { sXPath: sXPathNode1, nTextNodeIdx: nTextNodeIdx1, nOffset: htEndPt1.nOffset }; + + if (this.collapsed) { + var oBookmark2 = { sXPath: sXPathNode1, nTextNodeIdx: nTextNodeIdx1, nOffset: htEndPt1.nOffset }; + } else { + var nTextNodeIdx2 = -1; + var htEndPt2 = { elContainer: this.endContainer, nOffset: this.endOffset }; + var elNode2 = this.endContainer; + if (elNode2.nodeType == 3) { + htEndPt2 = this._getFixedEndTextNode(); + nTextNodeIdx2 = this._getPosIdx(htEndPt2.elContainer); + elNode2 = nhn.DOMFix.parentNode(elNode2); + } + var sXPathNode2 = this._getXPath(elNode2); + var oBookmark2 = { sXPath: sXPathNode2, nTextNodeIdx: nTextNodeIdx2, nOffset: htEndPt2.nOffset }; + } + return [oBookmark1, oBookmark2]; + }, + + moveToXPathBookmark: function (aBookmark) { + if (!aBookmark) { return false; } + + var oBookmarkInfo1 = this._evaluateXPathBookmark(aBookmark[0]); + var oBookmarkInfo2 = this._evaluateXPathBookmark(aBookmark[1]); + + if (!oBookmarkInfo1["elContainer"] || !oBookmarkInfo2["elContainer"]) { return; } + + this.startContainer = oBookmarkInfo1["elContainer"]; + this.startOffset = oBookmarkInfo1["nOffset"]; + + this.endContainer = oBookmarkInfo2["elContainer"]; + this.endOffset = oBookmarkInfo2["nOffset"]; + + return true; + }, + + _getFixedTextContainer: function (elNode, nOffset) { + while (elNode && elNode.nodeType == 3 && elNode.previousSibling && elNode.previousSibling.nodeType == 3) { + nOffset += elNode.previousSibling.nodeValue.length; + elNode = elNode.previousSibling; + } + + return { elContainer: elNode, nOffset: nOffset }; + }, + + _getFixedStartTextNode: function () { + return this._getFixedTextContainer(this.startContainer, this.startOffset); + }, + + _getFixedEndTextNode: function () { + return this._getFixedTextContainer(this.endContainer, this.endOffset); + }, + + placeStringBookmark: function () { + if (this.collapsed || jindo.$Agent().navigator().ie || jindo.$Agent().navigator().firefox) { + return this.placeStringBookmark_NonWebkit(); + } else { + return this.placeStringBookmark_Webkit(); + } + }, + + placeStringBookmark_NonWebkit: function () { + var sTmpId = (new Date()).getTime(); + + var oInsertionPoint = this.cloneRange(); + oInsertionPoint.collapseToEnd(); + var oEndMarker = this._document.createElement("SPAN"); + oEndMarker.id = this.HUSKY_BOOMARK_END_ID_PREFIX + sTmpId; + oInsertionPoint.insertNode(oEndMarker); + + var oInsertionPoint = this.cloneRange(); + oInsertionPoint.collapseToStart(); + var oStartMarker = this._document.createElement("SPAN"); + oStartMarker.id = this.HUSKY_BOOMARK_START_ID_PREFIX + sTmpId; + oInsertionPoint.insertNode(oStartMarker); + + // IE에서 빈 SPAN의 앞뒤로 커서가 이동하지 않아 문제가 발생 할 수 있어, 보이지 않는 특수 문자를 임시로 넣어 줌. + if (jindo.$Agent().navigator().ie) { + // SPAN의 위치가 TD와 TD 사이에 있을 경우, 텍스트 삽입 시 알수 없는 오류가 발생한다. + // TD와 TD사이에서는 텍스트 삽입이 필요 없음으로 그냥 try/catch로 처리 + try { + oStartMarker.innerHTML = unescape("%uFEFF"); + } catch (e) { } + + try { + oEndMarker.innerHTML = unescape("%uFEFF"); + } catch (e) { } + } + this.moveToBookmark(sTmpId); + + return sTmpId; + }, + + placeStringBookmark_Webkit: function () { + var sTmpId = (new Date()).getTime(); + + var elInsertBefore, elInsertParent; + + // Do not insert the bookmarks between TDs as it will break the rendering in Chrome/Safari + // -> modify the insertion position from [abc]abc to [abc]abc + var oInsertionPoint = this.cloneRange(); + oInsertionPoint.collapseToEnd(); + elInsertBefore = this._document.createTextNode(""); + oInsertionPoint.insertNode(elInsertBefore); + elInsertParent = elInsertBefore.parentNode; + if (elInsertBefore.previousSibling && elInsertBefore.previousSibling.tagName == "TD") { + elInsertParent = elInsertBefore.previousSibling; + elInsertBefore = null; + } + var oEndMarker = this._document.createElement("SPAN"); + oEndMarker.id = this.HUSKY_BOOMARK_END_ID_PREFIX + sTmpId; + elInsertParent.insertBefore(oEndMarker, elInsertBefore); + + var oInsertionPoint = this.cloneRange(); + oInsertionPoint.collapseToStart(); + elInsertBefore = this._document.createTextNode(""); + oInsertionPoint.insertNode(elInsertBefore); + elInsertParent = elInsertBefore.parentNode; + if (elInsertBefore.nextSibling && elInsertBefore.nextSibling.tagName == "TD") { + elInsertParent = elInsertBefore.nextSibling; + elInsertBefore = elInsertParent.firstChild; + } + var oStartMarker = this._document.createElement("SPAN"); + oStartMarker.id = this.HUSKY_BOOMARK_START_ID_PREFIX + sTmpId; + elInsertParent.insertBefore(oStartMarker, elInsertBefore); + + //elInsertBefore.parentNode.removeChild(elInsertBefore); + + this.moveToBookmark(sTmpId); + + return sTmpId; + }, + + cloneRange: function () { + return this._copyRange(new nhn.HuskyRange(this._window)); + }, + + moveToBookmark: function (vBookmark) { + if (typeof (vBookmark) != "object") { + return this.moveToStringBookmark(vBookmark); + } else { + return this.moveToXPathBookmark(vBookmark); + } + }, + + getStringBookmark: function (sBookmarkID, bEndBookmark) { + if (bEndBookmark) { + return this._document.getElementById(this.HUSKY_BOOMARK_END_ID_PREFIX + sBookmarkID); + } else { + return this._document.getElementById(this.HUSKY_BOOMARK_START_ID_PREFIX + sBookmarkID); + } + }, + + moveToStringBookmark: function (sBookmarkID, bIncludeBookmark) { + var oStartMarker = this.getStringBookmark(sBookmarkID); + var oEndMarker = this.getStringBookmark(sBookmarkID, true); + + if (!oStartMarker || !oEndMarker) { return false; } + + this.reset(this._window); + + if (bIncludeBookmark) { + this.setEndAfter(oEndMarker); + this.setStartBefore(oStartMarker); + } else { + this.setEndBefore(oEndMarker); + this.setStartAfter(oStartMarker); + } + return true; + }, + + removeStringBookmark: function (sBookmarkID) { + /* + var oStartMarker = this._document.getElementById(this.HUSKY_BOOMARK_START_ID_PREFIX+sBookmarkID); + var oEndMarker = this._document.getElementById(this.HUSKY_BOOMARK_END_ID_PREFIX+sBookmarkID); + + if(oStartMarker) nhn.DOMFix.parentNode(oStartMarker).removeChild(oStartMarker); + if(oEndMarker) nhn.DOMFix.parentNode(oEndMarker).removeChild(oEndMarker); + */ + this._removeAll(this.HUSKY_BOOMARK_START_ID_PREFIX + sBookmarkID); + this._removeAll(this.HUSKY_BOOMARK_END_ID_PREFIX + sBookmarkID); + }, + + _removeAll: function (sID) { + var elNode; + while ((elNode = this._document.getElementById(sID))) { + elNode.parentNode.removeChild(elNode); + } + }, + + collapseToStart: function () { + this.collapse(true); + }, + + collapseToEnd: function () { + this.collapse(false); + }, + + createAndInsertNode: function (sTagName) { + var tmpNode = this._document.createElement(sTagName); + this.insertNode(tmpNode); + return tmpNode; + }, + + getNodes: function (bSplitTextEndNodes, fnFilter) { + if (bSplitTextEndNodes) { this._splitTextEndNodesOfTheRange(); } + + var aAllNodes = this._getNodesInRange(); + var aFilteredNodes = []; + + if (!fnFilter) { return aAllNodes; } + + for (var i = 0; i < aAllNodes.length; i++) { + if (fnFilter(aAllNodes[i])) { aFilteredNodes[aFilteredNodes.length] = aAllNodes[i]; } + } + + return aFilteredNodes; + }, + + getTextNodes: function (bSplitTextEndNodes) { + var txtFilter = function (oNode) { + if (oNode.nodeType == 3 && oNode.nodeValue != "\n" && oNode.nodeValue != "") { + return true; + } else { + return false; + } + }; + + return this.getNodes(bSplitTextEndNodes, txtFilter); + }, + + surroundContentsWithNewNode: function (sTagName) { + var oNewParent = this._document.createElement(sTagName); + this.surroundContents(oNewParent); + return oNewParent; + }, + + isRangeinRange: function (oAnoterRange, bIncludePartlySelected) { + var startToStart = this.compareBoundaryPoints(this.W3CDOMRange.START_TO_START, oAnoterRange); + var startToEnd = this.compareBoundaryPoints(this.W3CDOMRange.START_TO_END, oAnoterRange); + var endToStart = this.compareBoundaryPoints(this.W3CDOMRange.ND_TO_START, oAnoterRange); + var endToEnd = this.compareBoundaryPoints(this.W3CDOMRange.END_TO_END, oAnoterRange); + + if (startToStart <= 0 && endToEnd >= 0) { return true; } + + if (bIncludePartlySelected) { + if (startToEnd == 1) { return false; } + if (endToStart == -1) { return false; } + return true; + } + + return false; + }, + + isNodeInRange: function (oNode, bIncludePartlySelected, bContentOnly) { + var oTmpRange = new nhn.HuskyRange(this._window); + + if (bContentOnly && oNode.firstChild) { + oTmpRange.setStartBefore(oNode.firstChild); + oTmpRange.setEndAfter(oNode.lastChild); + } else { + oTmpRange.selectNode(oNode); + } + + return this.isRangeInRange(oTmpRange, bIncludePartlySelected); + }, + + pasteText: function (sText) { + this.pasteHTML(sText.replace(/&/g, "&").replace(//g, ">").replace(/ /g, " ").replace(/"/g, """)); + }, + + /** + * TODO: 왜 clone 으로 조작할까? + * @param {String} sHTML 삽입할 HTML + * @param {Boolean} bBlock HTML 삽입시 강제로 block 요소 처리할지 여부(true 이면 P태그 안에 삽입될 경우, P태그를 무조건 쪼개고 그 사이에 DIV태그로 감싸서 삽입한다.) + */ + pasteHTML: function (sHTML, bBlock) { + var oTmpDiv = this._document.createElement("DIV"); + oTmpDiv.innerHTML = sHTML; + + if (!oTmpDiv.firstChild) { + this.deleteContents(); + return; + } + + // getLineInfo 전에 북마크를 삽입하지 않으면 IE에서 oLineBreaker가 P태그 바깥으로 잡히는 경우가 있음(P태그에 아무것도 없을때) + var clone = this.cloneRange(); + var sBM = clone.placeStringBookmark(); + + // [SMARTEDITORSUS-1960] PrivateTag, 템플릿삽입등 P태그안에 block 요소 삽입과 관련된 처리 + // P태그인 경우, block요소가 들어오면 안된다. + // 때문에 현재 위치의 컨테이너가 P태그이고 컨텐츠 내용이 block 요소인 경우 P태그를 쪼개고 그 사이에 컨텐츠를 DIV로 감싸서 넣도록 처리한다. + // [SMARTEDITORSUS-2026][SMARTEDITORSUS-2061] bBlock = true 이면 삽입되는 HTML 이 block 요소가 아니더라도 무조건 P태그를 쪼개서 DIV로 감싸도록 한다. + var oLineInfo = clone.getLineInfo(), + oStart = oLineInfo.oStart, + oEnd = oLineInfo.oEnd; + if (oStart.oLineBreaker && oStart.oLineBreaker.nodeName === "P" && (bBlock || clone.rxHasBlock.test(sHTML))) { + // [SMARTEDITORSUS-2169] 선택영역 삭제시 oStart.oLineBreaker도 DOM 트리에서 제거될 수 있기 때문에 필요한 노드를 미리 참조해둔다. + var oParentNode = oStart.oLineBreaker.parentNode, + oNextSibling = oStart.oLineBreaker.nextSibling; + + // 선택영역을 조작해야 하므로 현재 선택된 요소들을 제거한다. + clone.deleteContents(); + + // 동일한 라인에 있으면 뒷부분은 쪼개서 다음 라인으로 삽입한다. + if (oStart.oLineBreaker === oEnd.oLineBreaker) { + var elBM = clone.getStringBookmark(sBM); + clone.setEndNodes(elBM, oEnd.oLineBreaker); + var oNextContents = clone.extractContents(), + oNextFirst = oNextContents.firstChild; // oNextSibling 을 교체하기 위해 쪼개진 요소 첫번째 노드를 미리 참조해둔다. + + // 쪼갠 부분을 삽입하고 + if (oNextSibling) { + oParentNode.insertBefore(oNextContents, oNextSibling); + } else { + oParentNode.appendChild(oNextContents); + } + // [SMARTEDITORSUS-2145] oNextSibling 을 쪼갠 부분의 첫번째 노드로 교체한다. + oNextSibling = oNextFirst; + } + + // 선택영역 앞쪽이 속한 P태그에서 style과 align 정보를 복사한다. + // 크롬의 경우 div의 style 에 text-align 이 있으면 align 속성은 무시되는데 + // div 안의 block 요소는 text-align 의 대상이 아니라 정렬되지 않는 문제가 있기 때문에 + // style 복사할 때 text-align 속성은 제외한다. + oTmpDiv.style.cssText = oStart.oLineBreaker.style.cssText.replace(this._rxTextAlign, ''); // text-align 제외 + oTmpDiv.align = oStart.oLineBreaker.align; // align 복사 + + // 컨텐츠 삽입 + if (oNextSibling) { + oParentNode.insertBefore(oTmpDiv, oNextSibling); + } else { + oParentNode.appendChild(oTmpDiv); + } + + // 컨텐츠 삽입 후에 북마크를 지운다. + // 컨텐츠 삽입 전에 지우면 컨텐츠 삽입시 oNextSibling 가 북마크로 잡히는 경우 에러가 발생할 수 있음 + clone.removeStringBookmark(sBM); + + // 컨텐츠 삽입 후 윗라인 P태그에 아무런 내용이 없으면 제거한다. + this._removeEmptyP(this._getPrevElement(oTmpDiv)); + // 아래 라인 P태그에 아무런 내용이 없는 경우는 그 다음 아래 라인이 있을때만 제거한다. + // 아래 라인이 아예없으면 IE에서 커서가 들어가지 않기 때문에 라인을 추가해준다. + var elNextLine = this._getNextElement(oTmpDiv); + if (elNextLine) { + var elAfterNext = this._getNextElement(elNextLine); + if (elAfterNext && this._removeEmptyP(elNextLine)) { + elNextLine = elAfterNext; // 제거되었을 경우만 elNextLine 재할당 + } + } else { + // 아래 라인이 없으면 윗 라인 스타일을 복사하여 추가해준다. + elNextLine = this._document.createElement("P"); + elNextLine.style.cssText = oStart.oLineBreaker.style.cssText; + elNextLine.align = oStart.oLineBreaker.align; + oParentNode.appendChild(elNextLine); + } + + // 커서를 다음라인으로 앞쪽으로 위치시킨다. + if (elNextLine.innerHTML === "") { + // 크롬에서 빈

    를 선택해서 collapseToStart 하면 윗라인으로 이동하기 때문에 비어있으면 \uFEFF 를 넣어준다. + elNextLine.innerHTML = (jindo.$Agent().navigator().ie && jindo.$Agent().navigator().version > 8) ? "\u200B" : "\uFEFF"; + } + this.selectNodeContents(elNextLine); + this.collapseToStart(); + + // IE7에서 커서가 다음라인 p태그 앞쪽이 아닌 div태그 끝쪽으로 자동으로 옮겨가는 경우가 있어서 + // 커서가 멋대로 이동하지 않도록 임시북마크를 넣었다가 바로 빼준다. + // (주의) 북마크를 넣었다 빼면 IE10은 다음라인 p태그 끝쪽으로 이동되기 때문에 IE7인 경우만 넣어줌 + // [SMARTEDITORSUS-2043] SE_EditingArea_WYSIWYG.$ON_PASTE_HTML 에서 IE8의 경우만 삽입시 뒤에 \uFEFF가 추가로 붙어서 들어오는데 + // 이로 인해 템플릿과 커서사이가 한줄 벌어지는 문제가 있어서 \uFEFF 추가하는 부분을 삭제하니 커서가 IE7과 동일하게 동작하여 IE8도 임시북마크처리함 + if (jindo.$Agent().navigator().ie && jindo.$Agent().navigator().version < 9) { + sBM = this.placeStringBookmark(); + this.removeStringBookmark(sBM); + } + } else { + var oFirstNode = oTmpDiv.firstChild; + var oLastNode = oTmpDiv.lastChild; + + this.collapseToStart(); + + while (oTmpDiv.lastChild) { this.insertNode(oTmpDiv.lastChild); } + + this.setEndNodes(oFirstNode, oLastNode); + + // delete the content later as deleting it first may mass up the insertion point + // eg)

    [A]BCD

    ---paste O---> O

    BCD

    + clone.moveToBookmark(sBM); + clone.deleteContents(); + clone.removeStringBookmark(sBM); + } + }, + + /** + * 비어있는 P태그이면 제거한다. + * @param {Element} el 검사할 Element + * @returns {Boolean} 제거되었다면 true 를 반환한다. + */ + _removeEmptyP: function (el) { + if (el && el.nodeName === "P") { + var sInner = el.innerHTML; + if (sInner === "" || this._rxCursorHolder.test(sInner)) { + el.parentNode.removeChild(el); + return true; + } + } + }, + + /** + * 인접한 Element 노드를 찾는다. + * @param {Node} oNode 기준 노드 + * @param {Boolean} bPrev 앞뒤여부(true면 앞, false면 뒤) + * @return {Element} 인접한 Element, 없으면 null 반환 + */ + _getSiblingElement: function (oNode, bPrev) { + if (!oNode) { + return null; + } + + var oSibling = oNode[bPrev ? "previousSibling" : "nextSibling"]; + if (oSibling && oSibling.nodeType === 1) { + return oSibling; + } else { + return arguments.callee(oSibling, bPrev); + } + }, + + /** + * 앞쪽 인접한 Element 노드를 찾는다. + * @param {Node} oNode 기준 노드 + * @return {Element} 인접한 Element, 없으면 null 반환 + */ + _getPrevElement: function (oNode) { + return this._getSiblingElement(oNode, true); + }, + + /** + * 뒤쪽 인접한 Element 노드를 찾는다. + * @param {Node} oNode 기준 노드 + * @return {Element} 인접한 Element, 없으면 null 반환 + */ + _getNextElement: function (oNode) { + return this._getSiblingElement(oNode, false); + }, + + toString: function () { + this.toString = nhn.W3CDOMRange.prototype.toString; + return this.toString(); + }, + + toHTMLString: function () { + var oTmpContainer = this._document.createElement("DIV"); + oTmpContainer.appendChild(this.cloneContents()); + + return oTmpContainer.innerHTML; + }, + + findAncestorByTagName: function (sTagName) { + var oNode = this.commonAncestorContainer; + while (oNode && oNode.tagName != sTagName) { oNode = nhn.DOMFix.parentNode(oNode); } + + return oNode; + }, + + selectNodeContents: function (oNode) { + if (!oNode) { return; } + + var oFirstNode = oNode.firstChild ? oNode.firstChild : oNode; + var oLastNode = oNode.lastChild ? oNode.lastChild : oNode; + + this.reset(this._window); + if (oFirstNode.nodeType == 3) { + this.setStart(oFirstNode, 0, true); + } else { + this.setStartBefore(oFirstNode); + } + + if (oLastNode.nodeType == 3) { + this.setEnd(oLastNode, oLastNode.nodeValue.length, true); + } else { + this.setEndAfter(oLastNode); + } + }, + + /** + * 노드의 취소선/밑줄 정보를 확인한다 + * 관련 BTS [SMARTEDITORSUS-26] + * @param {Node} oNode 취소선/밑줄을 확인할 노드 + * @param {String} sValue textDecoration 정보 + * @see nhn.HuskyRange#_checkTextDecoration + */ + _hasTextDecoration: function (oNode, sValue) { + if (!oNode || !oNode.style) { + return false; + } + + if (oNode.style.textDecoration.indexOf(sValue) > -1) { + return true; + } + + if (sValue === "underline" && oNode.tagName === "U") { + return true; + } + + if (sValue === "line-through" && (oNode.tagName === "S" || oNode.tagName === "STRIKE")) { + return true; + } + + return false; + }, + + /** + * 노드에 취소선/밑줄을 적용한다 + * 관련 BTS [SMARTEDITORSUS-26] + * [FF] 노드의 Style 에 textDecoration 을 추가한다 + * [FF 외] U/STRIKE 태그를 추가한다 + * @param {Node} oNode 취소선/밑줄을 적용할 노드 + * @param {String} sValue textDecoration 정보 + * @see nhn.HuskyRange#_checkTextDecoration + */ + _setTextDecoration: function (oNode, sValue) { + if (jindo.$Agent().navigator().firefox) { // FF + oNode.style.textDecoration = (oNode.style.textDecoration) ? oNode.style.textDecoration + " " + sValue : sValue; + } + else { + if (sValue === "underline") { + oNode.innerHTML = "" + oNode.innerHTML + "" + } else if (sValue === "line-through") { + oNode.innerHTML = "" + oNode.innerHTML + "" + } + } + }, + + /** + * 인자로 전달받은 노드 상위의 취소선/밑줄 정보를 확인하여 노드에 적용한다 + * 관련 BTS [SMARTEDITORSUS-26] + * @param {Node} oNode 취소선/밑줄을 적용할 노드 + */ + _checkTextDecoration: function (oNode) { + if (oNode.tagName !== "SPAN") { + return; + } + + var bUnderline = false, + bLineThrough = false, + sTextDecoration = "", + oParentNode = null; + oChildNode = oNode.firstChild; + + /* check child */ + while (oChildNode) { + if (oChildNode.nodeType === 1) { + bUnderline = (bUnderline || oChildNode.tagName === "U"); + bLineThrough = (bLineThrough || oChildNode.tagName === "S" || oChildNode.tagName === "STRIKE"); + } + + if (bUnderline && bLineThrough) { + return; + } + + oChildNode = oChildNode.nextSibling; + } + + oParentNode = nhn.DOMFix.parentNode(oNode); + + /* check parent */ + while (oParentNode && oParentNode.tagName !== "BODY") { + if (oParentNode.nodeType !== 1) { + oParentNode = nhn.DOMFix.parentNode(oParentNode); + continue; + } + + if (!bUnderline && this._hasTextDecoration(oParentNode, "underline")) { + bUnderline = true; + this._setTextDecoration(oNode, "underline"); // set underline + } + + if (!bLineThrough && this._hasTextDecoration(oParentNode, "line-through")) { + bLineThrough = true; + this._setTextDecoration(oNode, "line-through"); // set line-through + } + + if (bUnderline && bLineThrough) { + return; + } + + oParentNode = nhn.DOMFix.parentNode(oParentNode); + } + }, + + /** + * Range에 속한 노드들에 스타일을 적용한다 + * @param {Object} oStyle 적용할 스타일을 가지는 Object (예) 글꼴 색 적용의 경우 { color : "#0075c8" } + * @param {Object} [oAttribute] 적용할 속성을 가지는 Object (예) 맞춤범 검사의 경우 { _sm2_spchk: "강남콩", class: "se2_check_spell" } + * @param {String} [sNewSpanMarker] 새로 추가된 SPAN 노드를 나중에 따로 처리해야하는 경우 마킹을 위해 사용하는 문자열 + * @param {Boolean} [bIncludeLI] LI 도 스타일 적용에 포함할 것인지의 여부 [COM-1051] _getStyleParentNodes 메서드 참고하기 + * @param {Boolean} [bCheckTextDecoration] 취소선/밑줄 처리를 적용할 것인지 여부 [SMARTEDITORSUS-26] _setTextDecoration 메서드 참고하기 + */ + styleRange: function (oStyle, oAttribute, sNewSpanMarker, bIncludeLI, bCheckTextDecoration) { + var aStyleParents = this.aStyleParents = this._getStyleParentNodes(sNewSpanMarker, bIncludeLI); + if (aStyleParents.length < 1) { return; } + + var sName, sValue; + + for (var i = 0; i < aStyleParents.length; i++) { + for (var x in oStyle) { + sName = x; + sValue = oStyle[sName]; + + if (typeof sValue != "string") { continue; } + + // [SMARTEDITORSUS-26] 글꼴 색을 적용할 때 취소선/밑줄의 색상도 처리되도록 추가 + if (bCheckTextDecoration && oStyle.color) { + this._checkTextDecoration(aStyleParents[i]); + } + + aStyleParents[i].style[sName] = sValue; + } + + if (!oAttribute) { continue; } + + for (var x in oAttribute) { + sName = x; + sValue = oAttribute[sName]; + + if (typeof sValue != "string") { continue; } + + if (sName == "class") { + jindo.$Element(aStyleParents[i]).addClass(sValue); + } else { + aStyleParents[i].setAttribute(sName, sValue); + } + } + } + + this.reset(this._window); + this.setStartBefore(aStyleParents[0]); + this.setEndAfter(aStyleParents[aStyleParents.length - 1]); + }, + + expandBothEnds: function () { + this.expandStart(); + this.expandEnd(); + }, + + expandStart: function () { + if (this.startContainer.nodeType == 3 && this.startOffset !== 0) { return; } + + var elActualStartNode = this._getActualStartNode(this.startContainer, this.startOffset); + elActualStartNode = this._getPrevNode(elActualStartNode); + + if (elActualStartNode.tagName == "BODY") { + this.setStartBefore(elActualStartNode); + } else { + this.setStartAfter(elActualStartNode); + } + }, + + expandEnd: function () { + if (this.endContainer.nodeType == 3 && this.endOffset < this.endContainer.nodeValue.length) { return; } + + var elActualEndNode = this._getActualEndNode(this.endContainer, this.endOffset); + elActualEndNode = this._getNextNode(elActualEndNode); + + if (elActualEndNode.tagName == "BODY") { + this.setEndAfter(elActualEndNode); + } else { + this.setEndBefore(elActualEndNode); + } + }, + + /** + * Style 을 적용할 노드를 가져온다 + * @param {String} [sNewSpanMarker] 새로 추가하는 SPAN 노드를 마킹을 위해 사용하는 문자열 + * @param {Boolean} [bIncludeLI] LI 도 스타일 적용에 포함할 것인지의 여부 + * @return {Array} Style 을 적용할 노드 배열 + */ + _getStyleParentNodes: function (sNewSpanMarker, bIncludeLI) { + this._splitTextEndNodesOfTheRange(); + + var oSNode = this.getStartNode(); + var oENode = this.getEndNode(); + + var aAllNodes = this._getNodesInRange(); + var aResult = []; + var nResult = 0; + + var oNode, oTmpNode, iStartRelPos, iEndRelPos, oSpan; + var nInitialLength = aAllNodes.length; + var arAllBottomNodes = jindo.$A(aAllNodes).filter(function (v) { return (!v.firstChild || (bIncludeLI && v.tagName == "LI")); }); + + // [COM-1051] 본문내용을 한 줄만 입력하고 번호 매긴 상태에서 글자크기를 변경하면 번호크기는 변하지 않는 문제 + // 부모 노드 중 LI 가 있고, 해당 LI 의 모든 자식 노드가 선택된 상태라면 LI에도 스타일을 적용하도록 처리함 + // --- Range 에 LI 가 포함되지 않은 경우, LI 를 포함하도록 처리 + var elTmpNode = this.commonAncestorContainer; + if (bIncludeLI) { + while (elTmpNode) { + if (elTmpNode.tagName == "LI") { + if (this._isFullyContained(elTmpNode, arAllBottomNodes)) { + aResult[nResult++] = elTmpNode; + } + break; + } + + elTmpNode = elTmpNode.parentNode; + } + } + + for (var i = 0; i < nInitialLength; i++) { + oNode = aAllNodes[i]; + + if (!oNode) { continue; } + + // --- Range 에 LI 가 포함된 경우에 대한 LI 확인 + if (bIncludeLI && oNode.tagName == "LI" && this._isFullyContained(oNode, arAllBottomNodes)) { + aResult[nResult++] = oNode; + continue; + } + + if (oNode.nodeType != 3) { continue; } + if (oNode.nodeValue == "" || oNode.nodeValue.match(/^(\r|\n)+$/)) { continue; } + + var oParentNode = nhn.DOMFix.parentNode(oNode); + + // 부모 노드가 SPAN 인 경우에는 새로운 SPAN 을 생성하지 않고 SPAN 을 리턴 배열에 추가함 + if (oParentNode.tagName == "SPAN") { + if (this._isFullyContained(oParentNode, arAllBottomNodes, oNode)) { + aResult[nResult++] = oParentNode; + continue; + } + } else { + // [SMARTEDITORSUS-1513] 선택된 영역을 single node로 감싸는 상위 span 노드가 있으면 리턴 배열에 추가 + var oParentSingleSpan = this._findParentSingleSpan(oParentNode); + if (oParentSingleSpan) { + aResult[nResult++] = oParentSingleSpan; + continue; + } + } + + oSpan = this._document.createElement("SPAN"); + oParentNode.insertBefore(oSpan, oNode); + oSpan.appendChild(oNode); + aResult[nResult++] = oSpan; + + if (sNewSpanMarker) { oSpan.setAttribute(sNewSpanMarker, "true"); } + } + + this.setStartBefore(oSNode); + this.setEndAfter(oENode); + + return aResult; + }, + + /** + * [SMARTEDITORSUS-1513][SMARTEDITORSUS-1648] 해당노드가 single child로 묶이는 상위 span 노드가 있는지 찾는다. + * @param {Node} oNode 검사할 노드 + * @return {Element} 상위 span 노드, 없으면 null + */ + _findParentSingleSpan: function (oNode) { + if (!oNode) { + return null; + } + // ZWNBSP 문자가 같이 있는 경우도 있기 때문에 실제 노드를 카운팅해야 함 + for (var i = 0, nCnt = 0, sValue, oChild, aChildNodes = oNode.childNodes; (oChild = aChildNodes[i]); i++) { + sValue = oChild.nodeValue; + if (this._rxCursorHolder.test(sValue)) { + continue; + } else { + nCnt++; + } + if (nCnt > 1) { // 싱글노드가 아니면 더이상 찾지 않고 null 반환 + return null; + } + } + if (oNode.nodeName === "SPAN") { + return oNode; + } else { + return this._findParentSingleSpan(oNode.parentNode); + } + }, + + /** + * 컨테이너 엘리먼트(elContainer)의 모든 자식노드가 노드 배열(waAllNodes)에 속하는지 확인한다 + * 첫 번째 자식 노드와 마지막 자식 노드가 노드 배열에 속하는지를 확인한다 + * @param {Element} elContainer 컨테이너 엘리먼트 + * @param {jindo.$A} waAllNodes Node 의 $A 배열 + * @param {Node} [oNode] 성능을 위한 옵션 노드로 컨테이너의 첫 번째 혹은 마지막 자식 노드와 같으면 indexOf 함수 사용을 줄일 수 있음 + * @return {Array} Style 을 적용할 노드 배열 + */ + // check if all the child nodes of elContainer are in waAllNodes + _isFullyContained: function (elContainer, waAllNodes, oNode) { + var nSIdx, nEIdx; + var oTmpNode = this._getVeryFirstRealChild(elContainer); + // do quick checks before trying indexOf() because indexOf() function is very slow + // oNode is optional + if (oNode && oTmpNode == oNode) { + nSIdx = 1; + } else { + nSIdx = waAllNodes.indexOf(oTmpNode); + } + + if (nSIdx != -1) { + oTmpNode = this._getVeryLastRealChild(elContainer); + if (oNode && oTmpNode == oNode) { + nEIdx = 1; + } else { + nEIdx = waAllNodes.indexOf(oTmpNode); + } + } + + return (nSIdx != -1 && nEIdx != -1); + }, + + _getVeryFirstChild: function (oNode) { + if (oNode.firstChild) { return this._getVeryFirstChild(oNode.firstChild); } + return oNode; + }, + + _getVeryLastChild: function (oNode) { + if (oNode.lastChild) { return this._getVeryLastChild(oNode.lastChild); } + return oNode; + }, + + _getFirstRealChild: function (oNode) { + var oFirstNode = oNode.firstChild; + while (oFirstNode && oFirstNode.nodeType == 3 && oFirstNode.nodeValue == "") { oFirstNode = oFirstNode.nextSibling; } + + return oFirstNode; + }, + + _getLastRealChild: function (oNode) { + var oLastNode = oNode.lastChild; + while (oLastNode && oLastNode.nodeType == 3 && oLastNode.nodeValue == "") { oLastNode = oLastNode.previousSibling; } + + return oLastNode; + }, + + _getVeryFirstRealChild: function (oNode) { + var oFirstNode = this._getFirstRealChild(oNode); + if (oFirstNode) { return this._getVeryFirstRealChild(oFirstNode); } + return oNode; + }, + _getVeryLastRealChild: function (oNode) { + var oLastNode = this._getLastRealChild(oNode); + if (oLastNode) { return this._getVeryLastChild(oLastNode); } + return oNode; + }, + + _getLineStartInfo: function (node) { + var frontEndFinal = null; + var frontEnd = node; + var lineBreaker = node; + var bParentBreak = false; + + var rxLineBreaker = this.rxLineBreaker; + + // vertical(parent) search + function getLineStart(node) { + if (!node) { return; } + if (frontEndFinal) { return; } + + if (rxLineBreaker.test(node.tagName)) { + lineBreaker = node; + frontEndFinal = frontEnd; + + bParentBreak = true; + + return; + } else { + frontEnd = node; + } + + getFrontEnd(node.previousSibling); + + if (frontEndFinal) { return; } + getLineStart(nhn.DOMFix.parentNode(node)); + } + + // horizontal(sibling) search + function getFrontEnd(node) { + if (!node) { return; } + if (frontEndFinal) { return; } + + if (rxLineBreaker.test(node.tagName)) { + lineBreaker = node; + frontEndFinal = frontEnd; + + bParentBreak = false; + return; + } + + if (node.firstChild && node.tagName != "TABLE") { + var curNode = node.lastChild; + while (curNode && !frontEndFinal) { + getFrontEnd(curNode); + + curNode = curNode.previousSibling; + } + } else { + frontEnd = node; + } + + if (!frontEndFinal) { + getFrontEnd(node.previousSibling); + } + } + + if (rxLineBreaker.test(node.tagName)) { + frontEndFinal = node; + } else { + getLineStart(node); + } + + return { oNode: frontEndFinal, oLineBreaker: lineBreaker, bParentBreak: bParentBreak }; + }, + + _getLineEndInfo: function (node) { + var backEndFinal = null; + var backEnd = node; + var lineBreaker = node; + var bParentBreak = false; + + var rxLineBreaker = this.rxLineBreaker; + + // vertical(parent) search + function getLineEnd(node) { + if (!node) { return; } + if (backEndFinal) { return; } + + if (rxLineBreaker.test(node.tagName)) { + lineBreaker = node; + backEndFinal = backEnd; + + bParentBreak = true; + + return; + } else { + backEnd = node; + } + + getBackEnd(node.nextSibling); + if (backEndFinal) { return; } + + getLineEnd(nhn.DOMFix.parentNode(node)); + } + + // horizontal(sibling) search + function getBackEnd(node) { + if (!node) { return; } + if (backEndFinal) { return; } + + if (rxLineBreaker.test(node.tagName)) { + lineBreaker = node; + backEndFinal = backEnd; + + bParentBreak = false; + + return; + } + + if (node.firstChild && node.tagName != "TABLE") { + var curNode = node.firstChild; + while (curNode && !backEndFinal) { + getBackEnd(curNode); + + curNode = curNode.nextSibling; + } + } else { + backEnd = node; + } + + if (!backEndFinal) { + getBackEnd(node.nextSibling); + } + } + + if (rxLineBreaker.test(node.tagName)) { + backEndFinal = node; + } else { + getLineEnd(node); + } + + return { oNode: backEndFinal, oLineBreaker: lineBreaker, bParentBreak: bParentBreak }; + }, + + getLineInfo: function (bAfter) { + var bAfter = bAfter || false; + + var oSNode = this.getStartNode(); + var oENode = this.getEndNode(); + + // oSNode && oENode will be null if the range is currently collapsed and the cursor is not located in the middle of a text node. + if (!oSNode) { oSNode = this.getNodeAroundRange(!bAfter, true); } + if (!oENode) { oENode = this.getNodeAroundRange(!bAfter, true); } + + var oStart = this._getLineStartInfo(oSNode); + var oStartNode = oStart.oNode; + var oEnd = this._getLineEndInfo(oENode); + var oEndNode = oEnd.oNode; + + if (oSNode != oStartNode || oENode != oEndNode) { + // check if the start node is positioned after the range's ending point + // or + // if the end node is positioned before the range's starting point + var iRelativeStartPos = this._compareEndPoint(nhn.DOMFix.parentNode(oStartNode), this._getPosIdx(oStartNode), this.endContainer, this.endOffset); + var iRelativeEndPos = this._compareEndPoint(nhn.DOMFix.parentNode(oEndNode), this._getPosIdx(oEndNode) + 1, this.startContainer, this.startOffset); + + if (!(iRelativeStartPos <= 0 && iRelativeEndPos >= 0)) { + oSNode = this.getNodeAroundRange(false, true); + oENode = this.getNodeAroundRange(false, true); + oStart = this._getLineStartInfo(oSNode); + oEnd = this._getLineEndInfo(oENode); + } + } + + return { oStart: oStart, oEnd: oEnd }; + }, + + /** + * 커서홀더나 공백을 제외한 child 노드가 하나만 있는 경우만 node 를 반환한다. + * @param {Node} oNode 확인할 노드 + * @return {Node} single child node를 반환한다. 없거나 두개 이상이면 null 을 반환 + */ + _findSingleChild: function (oNode) { + if (!oNode) { + return null; + } + var oSingleChild = null; + // ZWNBSP 문자가 같이 있는 경우도 있기 때문에 실제 노드를 카운팅해야 함 + for (var i = 0, nCnt = 0, sValue, oChild, aChildNodes = oNode.childNodes; (oChild = aChildNodes[i]); i++) { + sValue = oChild.nodeValue; + if (this._rxCursorHolder.test(sValue)) { + continue; + } else { + oSingleChild = oChild; + nCnt++; + } + if (nCnt > 1) { // 싱글노드가 아니면 더이상 찾지 않고 null 반환 + return null; + } + } + return oSingleChild; + }, + + /** + * 해당요소의 최하위까지 검색해 커서홀더만 감싸고 있는지 여부를 반환 + * @param {Node} oNode 확인할 노드 + * @return {Boolean} 커서홀더만 있는 경우 true 반환 + */ + _hasCursorHolderOnly: function (oNode) { + if (!oNode || oNode.nodeType !== 1) { + return false; + } + if (this._rxCursorHolder.test(oNode.innerHTML)) { + return true; + } else { + return this._hasCursorHolderOnly(this._findSingleChild(oNode)); + } + } }).extend(nhn.W3CDOMRange); /** * @fileOverview This file contains cross-browser selection function * @name BrowserSelection.js */ -nhn.BrowserSelection = function(win){ - this.init = function(win){ - this._window = win || window; - this._document = this._window.document; - }; +nhn.BrowserSelection = function (win) { + this.init = function (win) { + this._window = win || window; + this._document = this._window.document; + }; - this.init(win); + this.init(win); - // [SMARTEDITORSUS-888] IE9 이후로 document.createRange 를 지원 -/* var oAgentInfo = jindo.$Agent().navigator(); - if(oAgentInfo.ie){ - nhn.BrowserSelectionImpl_IE.apply(this); - }else{ - nhn.BrowserSelectionImpl_FF.apply(this); - }*/ + // [SMARTEDITORSUS-888] IE9 이후로 document.createRange 를 지원 + /* var oAgentInfo = jindo.$Agent().navigator(); + if(oAgentInfo.ie){ + nhn.BrowserSelectionImpl_IE.apply(this); + }else{ + nhn.BrowserSelectionImpl_FF.apply(this); + }*/ - if(!!this._document.createRange){ - nhn.BrowserSelectionImpl_FF.apply(this); - }else{ - nhn.BrowserSelectionImpl_IE.apply(this); - } - - this.selectRange = function(oRng){ - this.selectNone(); - this.addRange(oRng); - }; + if (!!this._document.createRange) { + nhn.BrowserSelectionImpl_FF.apply(this); + } else { + nhn.BrowserSelectionImpl_IE.apply(this); + } - this.selectionLoaded = true; - if(!this._oSelection){this.selectionLoaded = false;} + this.selectRange = function (oRng) { + this.selectNone(); + this.addRange(oRng); + }; + + this.selectionLoaded = true; + if (!this._oSelection) { this.selectionLoaded = false; } }; -nhn.BrowserSelectionImpl_FF = function(){ - this._oSelection = this._window.getSelection(); +nhn.BrowserSelectionImpl_FF = function () { + this._oSelection = this._window.getSelection(); - this.getRangeAt = function(iNum){ - iNum = iNum || 0; + this.getRangeAt = function (iNum) { + iNum = iNum || 0; - try{ - var oFFRange = this._oSelection.getRangeAt(iNum); - }catch(e){return new nhn.W3CDOMRange(this._window);} + try { + var oFFRange = this._oSelection.getRangeAt(iNum); + } catch (e) { return new nhn.W3CDOMRange(this._window); } - return this._FFRange2W3CRange(oFFRange); - }; - - this.addRange = function(oW3CRange){ - var oFFRange = this._W3CRange2FFRange(oW3CRange); - this._oSelection.addRange(oFFRange); - }; + return this._FFRange2W3CRange(oFFRange); + }; - this.selectNone = function(){ - this._oSelection.removeAllRanges(); - }; - - this.getCommonAncestorContainer = function(oW3CRange){ - var oFFRange = this._W3CRange2FFRange(oW3CRange); - return oFFRange.commonAncestorContainer; - }; - - this.isCollapsed = function(oW3CRange){ - var oFFRange = this._W3CRange2FFRange(oW3CRange); - return oFFRange.collapsed; - }; - - this.compareEndPoints = function(elContainerA, nOffsetA, elContainerB, nOffsetB){ - var oFFRangeA = this._document.createRange(); - var oFFRangeB = this._document.createRange(); - oFFRangeA.setStart(elContainerA, nOffsetA); - oFFRangeB.setStart(elContainerB, nOffsetB); - oFFRangeA.collapse(true); - oFFRangeB.collapse(true); + this.addRange = function (oW3CRange) { + var oFFRange = this._W3CRange2FFRange(oW3CRange); + this._oSelection.addRange(oFFRange); + }; - try{ - return oFFRangeA.compareBoundaryPoints(1, oFFRangeB); - }catch(e){ - return 1; - } - }; + this.selectNone = function () { + this._oSelection.removeAllRanges(); + }; - this._FFRange2W3CRange = function(oFFRange){ - var oW3CRange = new nhn.W3CDOMRange(this._window); + this.getCommonAncestorContainer = function (oW3CRange) { + var oFFRange = this._W3CRange2FFRange(oW3CRange); + return oFFRange.commonAncestorContainer; + }; - oW3CRange.setStart(oFFRange.startContainer, oFFRange.startOffset, true); - oW3CRange.setEnd(oFFRange.endContainer, oFFRange.endOffset, true); - - return oW3CRange; - }; + this.isCollapsed = function (oW3CRange) { + var oFFRange = this._W3CRange2FFRange(oW3CRange); + return oFFRange.collapsed; + }; - this._W3CRange2FFRange = function(oW3CRange){ - var oFFRange = this._document.createRange(); - oFFRange.setStart(oW3CRange.startContainer, oW3CRange.startOffset); - oFFRange.setEnd(oW3CRange.endContainer, oW3CRange.endOffset); + this.compareEndPoints = function (elContainerA, nOffsetA, elContainerB, nOffsetB) { + var oFFRangeA = this._document.createRange(); + var oFFRangeB = this._document.createRange(); + oFFRangeA.setStart(elContainerA, nOffsetA); + oFFRangeB.setStart(elContainerB, nOffsetB); + oFFRangeA.collapse(true); + oFFRangeB.collapse(true); - return oFFRange; - }; + try { + return oFFRangeA.compareBoundaryPoints(1, oFFRangeB); + } catch (e) { + return 1; + } + }; + + this._FFRange2W3CRange = function (oFFRange) { + var oW3CRange = new nhn.W3CDOMRange(this._window); + + oW3CRange.setStart(oFFRange.startContainer, oFFRange.startOffset, true); + oW3CRange.setEnd(oFFRange.endContainer, oFFRange.endOffset, true); + + return oW3CRange; + }; + + this._W3CRange2FFRange = function (oW3CRange) { + var oFFRange = this._document.createRange(); + oFFRange.setStart(oW3CRange.startContainer, oW3CRange.startOffset); + oFFRange.setEnd(oW3CRange.endContainer, oW3CRange.endOffset); + + return oFFRange; + }; }; -nhn.BrowserSelectionImpl_IE = function(){ - this._oSelection = this._document.selection; - this.oLastRange = { - oBrowserRange : null, - elStartContainer : null, - nStartOffset : -1, - elEndContainer : null, - nEndOffset : -1 - }; +nhn.BrowserSelectionImpl_IE = function () { + this._oSelection = this._document.selection; + this.oLastRange = { + oBrowserRange: null, + elStartContainer: null, + nStartOffset: -1, + elEndContainer: null, + nEndOffset: -1 + }; - this._updateLastRange = function(oBrowserRange, oW3CRange){ - this.oLastRange.oBrowserRange = oBrowserRange; - this.oLastRange.elStartContainer = oW3CRange.startContainer; - this.oLastRange.nStartOffset = oW3CRange.startOffset; - this.oLastRange.elEndContainer = oW3CRange.endContainer; - this.oLastRange.nEndOffset = oW3CRange.endOffset; - }; - - this.getRangeAt = function(iNum){ - iNum = iNum || 0; + this._updateLastRange = function (oBrowserRange, oW3CRange) { + this.oLastRange.oBrowserRange = oBrowserRange; + this.oLastRange.elStartContainer = oW3CRange.startContainer; + this.oLastRange.nStartOffset = oW3CRange.startOffset; + this.oLastRange.elEndContainer = oW3CRange.endContainer; + this.oLastRange.nEndOffset = oW3CRange.endOffset; + }; - var oW3CRange, oBrowserRange; - if(this._oSelection.type == "Control"){ - oW3CRange = new nhn.W3CDOMRange(this._window); + this.getRangeAt = function (iNum) { + iNum = iNum || 0; - var oSelectedNode = this._oSelection.createRange().item(iNum); + var oW3CRange, oBrowserRange; + if (this._oSelection.type == "Control") { + oW3CRange = new nhn.W3CDOMRange(this._window); - // if the selction occurs in a different document, ignore - if(!oSelectedNode || oSelectedNode.ownerDocument != this._document){return oW3CRange;} + var oSelectedNode = this._oSelection.createRange().item(iNum); - oW3CRange.selectNode(oSelectedNode); - - return oW3CRange; - }else{ - //oBrowserRange = this._oSelection.createRangeCollection().item(iNum); - oBrowserRange = this._oSelection.createRange(); + // if the selction occurs in a different document, ignore + if (!oSelectedNode || oSelectedNode.ownerDocument != this._document) { return oW3CRange; } - var oSelectedNode = oBrowserRange.parentElement(); + oW3CRange.selectNode(oSelectedNode); - // if the selction occurs in a different document, ignore - if(!oSelectedNode || oSelectedNode.ownerDocument != this._document){ - oW3CRange = new nhn.W3CDOMRange(this._window); - return oW3CRange; - } - oW3CRange = this._IERange2W3CRange(oBrowserRange); - - return oW3CRange; - } - }; + return oW3CRange; + } else { + //oBrowserRange = this._oSelection.createRangeCollection().item(iNum); + oBrowserRange = this._oSelection.createRange(); - this.addRange = function(oW3CRange){ - var oIERange = this._W3CRange2IERange(oW3CRange); - oIERange.select(); - }; + var oSelectedNode = oBrowserRange.parentElement(); - this.selectNone = function(){ - this._oSelection.empty(); - }; + // if the selction occurs in a different document, ignore + if (!oSelectedNode || oSelectedNode.ownerDocument != this._document) { + oW3CRange = new nhn.W3CDOMRange(this._window); + return oW3CRange; + } + oW3CRange = this._IERange2W3CRange(oBrowserRange); - this.getCommonAncestorContainer = function(oW3CRange){ - return this._W3CRange2IERange(oW3CRange).parentElement(); - }; - - this.isCollapsed = function(oW3CRange){ - var oRange = this._W3CRange2IERange(oW3CRange); - var oRange2 = oRange.duplicate(); + return oW3CRange; + } + }; - oRange2.collapse(); + this.addRange = function (oW3CRange) { + var oIERange = this._W3CRange2IERange(oW3CRange); + oIERange.select(); + }; - return oRange.isEqual(oRange2); - }; - - this.compareEndPoints = function(elContainerA, nOffsetA, elContainerB, nOffsetB){ - var oIERangeA, oIERangeB; + this.selectNone = function () { + this._oSelection.empty(); + }; - if(elContainerA === this.oLastRange.elStartContainer && nOffsetA === this.oLastRange.nStartOffset){ - oIERangeA = this.oLastRange.oBrowserRange.duplicate(); - oIERangeA.collapse(true); - }else{ - if(elContainerA === this.oLastRange.elEndContainer && nOffsetA === this.oLastRange.nEndOffset){ - oIERangeA = this.oLastRange.oBrowserRange.duplicate(); - oIERangeA.collapse(false); - }else{ - oIERangeA = this._getIERangeAt(elContainerA, nOffsetA); - } - } + this.getCommonAncestorContainer = function (oW3CRange) { + return this._W3CRange2IERange(oW3CRange).parentElement(); + }; - if(elContainerB === this.oLastRange.elStartContainer && nOffsetB === this.oLastRange.nStartOffset){ - oIERangeB = this.oLastRange.oBrowserRange.duplicate(); - oIERangeB.collapse(true); - }else{ - if(elContainerB === this.oLastRange.elEndContainer && nOffsetB === this.oLastRange.nEndOffset){ - oIERangeB = this.oLastRange.oBrowserRange.duplicate(); - oIERangeB.collapse(false); - }else{ - oIERangeB = this._getIERangeAt(elContainerB, nOffsetB); - } - } + this.isCollapsed = function (oW3CRange) { + var oRange = this._W3CRange2IERange(oW3CRange); + var oRange2 = oRange.duplicate(); - return oIERangeA.compareEndPoints("StartToStart", oIERangeB); - }; - - this._W3CRange2IERange = function(oW3CRange){ - if(this.oLastRange.elStartContainer === oW3CRange.startContainer && - this.oLastRange.nStartOffset === oW3CRange.startOffset && - this.oLastRange.elEndContainer === oW3CRange.endContainer && - this.oLastRange.nEndOffset === oW3CRange.endOffset){ - return this.oLastRange.oBrowserRange; - } + oRange2.collapse(); - var oStartIERange = this._getIERangeAt(oW3CRange.startContainer, oW3CRange.startOffset); - var oEndIERange = this._getIERangeAt(oW3CRange.endContainer, oW3CRange.endOffset); - oStartIERange.setEndPoint("EndToEnd", oEndIERange); + return oRange.isEqual(oRange2); + }; - this._updateLastRange(oStartIERange, oW3CRange); + this.compareEndPoints = function (elContainerA, nOffsetA, elContainerB, nOffsetB) { + var oIERangeA, oIERangeB; - return oStartIERange; - }; + if (elContainerA === this.oLastRange.elStartContainer && nOffsetA === this.oLastRange.nStartOffset) { + oIERangeA = this.oLastRange.oBrowserRange.duplicate(); + oIERangeA.collapse(true); + } else { + if (elContainerA === this.oLastRange.elEndContainer && nOffsetA === this.oLastRange.nEndOffset) { + oIERangeA = this.oLastRange.oBrowserRange.duplicate(); + oIERangeA.collapse(false); + } else { + oIERangeA = this._getIERangeAt(elContainerA, nOffsetA); + } + } - this._getIERangeAt = function(oW3CContainer, iW3COffset){ - var oIERange = this._document.body.createTextRange(); + if (elContainerB === this.oLastRange.elStartContainer && nOffsetB === this.oLastRange.nStartOffset) { + oIERangeB = this.oLastRange.oBrowserRange.duplicate(); + oIERangeB.collapse(true); + } else { + if (elContainerB === this.oLastRange.elEndContainer && nOffsetB === this.oLastRange.nEndOffset) { + oIERangeB = this.oLastRange.oBrowserRange.duplicate(); + oIERangeB.collapse(false); + } else { + oIERangeB = this._getIERangeAt(elContainerB, nOffsetB); + } + } - var oEndPointInfoForIERange = this._getSelectableNodeAndOffsetForIE(oW3CContainer, iW3COffset); + return oIERangeA.compareEndPoints("StartToStart", oIERangeB); + }; - var oSelectableNode = oEndPointInfoForIERange.oSelectableNodeForIE; - var iIEOffset = oEndPointInfoForIERange.iOffsetForIE; + this._W3CRange2IERange = function (oW3CRange) { + if (this.oLastRange.elStartContainer === oW3CRange.startContainer && + this.oLastRange.nStartOffset === oW3CRange.startOffset && + this.oLastRange.elEndContainer === oW3CRange.endContainer && + this.oLastRange.nEndOffset === oW3CRange.endOffset) { + return this.oLastRange.oBrowserRange; + } - oIERange.moveToElementText(oSelectableNode); + var oStartIERange = this._getIERangeAt(oW3CRange.startContainer, oW3CRange.startOffset); + var oEndIERange = this._getIERangeAt(oW3CRange.endContainer, oW3CRange.endOffset); + oStartIERange.setEndPoint("EndToEnd", oEndIERange); - oIERange.collapse(oEndPointInfoForIERange.bCollapseToStart); - oIERange.moveStart("character", iIEOffset); + this._updateLastRange(oStartIERange, oW3CRange); - return oIERange; - }; + return oStartIERange; + }; - this._getSelectableNodeAndOffsetForIE = function(oW3CContainer, iW3COffset){ -// var oIERange = this._document.body.createTextRange(); + this._getIERangeAt = function (oW3CContainer, iW3COffset) { + var oIERange = this._document.body.createTextRange(); - var oNonTextNode = null; - var aChildNodes = null; - var iNumOfLeftNodesToCount = 0; + var oEndPointInfoForIERange = this._getSelectableNodeAndOffsetForIE(oW3CContainer, iW3COffset); - if(oW3CContainer.nodeType == 3){ - oNonTextNode = nhn.DOMFix.parentNode(oW3CContainer); - aChildNodes = nhn.DOMFix.childNodes(oNonTextNode); - iNumOfLeftNodesToCount = aChildNodes.length; - }else{ - oNonTextNode = oW3CContainer; - aChildNodes = nhn.DOMFix.childNodes(oNonTextNode); - //iNumOfLeftNodesToCount = iW3COffset; - iNumOfLeftNodesToCount = (iW3COffset=0){break;} + return oW3CRange; + }; - oPrevNonTextNode = aChildNodes[i]; - } + this._getW3CContainerAndOffset = function (oIEPointRange, bStartPt) { + var oRgOrigPoint = oIEPointRange; - var pointRangeIdx = i; + var oContainer = oRgOrigPoint.parentElement(); + var offset = -1; - if(pointRangeIdx !== 0 && aChildNodes[pointRangeIdx-1].nodeType == 3){ - var oRgTextStart = this._document.body.createTextRange(); - var oCurTextNode = null; - if(oPrevNonTextNode){ - oRgTextStart.moveToElementText(oPrevNonTextNode); - oRgTextStart.collapse(false); - oCurTextNode = oPrevNonTextNode.nextSibling; - }else{ - oRgTextStart.moveToElementText(oContainer); - oRgTextStart.collapse(true); - oCurTextNode = oContainer.firstChild; - } + var oRgTester = this._document.body.createTextRange(); + var aChildNodes = nhn.DOMFix.childNodes(oContainer); + var oPrevNonTextNode = null; + var pointRangeIdx = 0; - var oRgTextsUpToThePoint = oRgOrigPoint.duplicate(); - oRgTextsUpToThePoint.setEndPoint("StartToStart", oRgTextStart); + for (var i = 0; i < aChildNodes.length; i++) { + if (aChildNodes[i].nodeType == 3) { continue; } - var textCount = oRgTextsUpToThePoint.text.replace(/[\r\n]/g,"").length; + oRgTester.moveToElementText(aChildNodes[i]); - while(textCount > oCurTextNode.nodeValue.length && oCurTextNode.nextSibling){ - textCount -= oCurTextNode.nodeValue.length; - oCurTextNode = oCurTextNode.nextSibling; - } + if (oRgTester.compareEndPoints("StartToStart", oIEPointRange) >= 0) { break; } - // this will enforce IE to re-reference oCurTextNode - var oTmp = oCurTextNode.nodeValue; - - if(bStartPt && oCurTextNode.nextSibling && oCurTextNode.nextSibling.nodeType == 3 && textCount == oCurTextNode.nodeValue.length){ - textCount -= oCurTextNode.nodeValue.length; - oCurTextNode = oCurTextNode.nextSibling; - } + oPrevNonTextNode = aChildNodes[i]; + } - oContainer = oCurTextNode; - offset = textCount; - }else{ - oContainer = oRgOrigPoint.parentElement(); - offset = pointRangeIdx; - } - return {"oContainer" : oContainer, "iOffset" : offset}; - }; + var pointRangeIdx = i; + + if (pointRangeIdx !== 0 && aChildNodes[pointRangeIdx - 1].nodeType == 3) { + var oRgTextStart = this._document.body.createTextRange(); + var oCurTextNode = null; + if (oPrevNonTextNode) { + oRgTextStart.moveToElementText(oPrevNonTextNode); + oRgTextStart.collapse(false); + oCurTextNode = oPrevNonTextNode.nextSibling; + } else { + oRgTextStart.moveToElementText(oContainer); + oRgTextStart.collapse(true); + oCurTextNode = oContainer.firstChild; + } + + var oRgTextsUpToThePoint = oRgOrigPoint.duplicate(); + oRgTextsUpToThePoint.setEndPoint("StartToStart", oRgTextStart); + + var textCount = oRgTextsUpToThePoint.text.replace(/[\r\n]/g, "").length; + + while (textCount > oCurTextNode.nodeValue.length && oCurTextNode.nextSibling) { + textCount -= oCurTextNode.nodeValue.length; + oCurTextNode = oCurTextNode.nextSibling; + } + + // this will enforce IE to re-reference oCurTextNode + var oTmp = oCurTextNode.nodeValue; + + if (bStartPt && oCurTextNode.nextSibling && oCurTextNode.nextSibling.nodeType == 3 && textCount == oCurTextNode.nodeValue.length) { + textCount -= oCurTextNode.nodeValue.length; + oCurTextNode = oCurTextNode.nextSibling; + } + + oContainer = oCurTextNode; + offset = textCount; + } else { + oContainer = oRgOrigPoint.parentElement(); + offset = pointRangeIdx; + } + return { "oContainer": oContainer, "iOffset": offset }; + }; }; nhn.DOMFix = new (jindo.$Class({ - $init : function(){ - if(jindo.$Agent().navigator().ie || jindo.$Agent().navigator().opera){ - this.childNodes = this._childNodes_Fix; - this.parentNode = this._parentNode_Fix; - }else{ - this.childNodes = this._childNodes_Native; - this.parentNode = this._parentNode_Native; - } - }, + $init: function () { + if (jindo.$Agent().navigator().ie || jindo.$Agent().navigator().opera) { + this.childNodes = this._childNodes_Fix; + this.parentNode = this._parentNode_Fix; + } else { + this.childNodes = this._childNodes_Native; + this.parentNode = this._parentNode_Native; + } + }, - _parentNode_Native : function(elNode){ - return elNode.parentNode; - }, - - _parentNode_Fix : function(elNode){ - if(!elNode){return elNode;} + _parentNode_Native: function (elNode) { + return elNode.parentNode; + }, - while(elNode.previousSibling){elNode = elNode.previousSibling;} + _parentNode_Fix: function (elNode) { + if (!elNode) { return elNode; } - return elNode.parentNode; - }, - - _childNodes_Native : function(elNode){ - return elNode.childNodes; - }, - - _childNodes_Fix : function(elNode){ - var aResult = null; - var nCount = 0; + while (elNode.previousSibling) { elNode = elNode.previousSibling; } - if(elNode){ - var aResult = []; - elNode = elNode.firstChild; - while(elNode){ - aResult[nCount++] = elNode; - elNode=elNode.nextSibling; - } - } - - return aResult; - } + return elNode.parentNode; + }, + + _childNodes_Native: function (elNode) { + return elNode.childNodes; + }, + + _childNodes_Fix: function (elNode) { + var aResult = null; + var nCount = 0; + + if (elNode) { + var aResult = []; + elNode = elNode.firstChild; + while (elNode) { + aResult[nCount++] = elNode; + elNode = elNode.nextSibling; + } + } + + return aResult; + } }))(); /*[ * ADD_APP_PROPERTY @@ -2946,146 +2950,146 @@ nhn.DOMFix = new (jindo.$Class({ * @pluginDesc Husky Framework에서 자주 사용되는 메시지를 처리하는 플러그인 */ nhn.husky.CorePlugin = jindo.$Class({ - name : "CorePlugin", + name: "CorePlugin", - // nStatus = 0(request not sent), 1(request sent), 2(response received) - // sContents = response - htLazyLoadRequest_plugins : {}, - htLazyLoadRequest_allFiles : {}, - - htHTMLLoaded : {}, - - $AFTER_MSG_APP_READY : function(){ - this.oApp.exec("EXEC_ON_READY_FUNCTION", []); - }, + // nStatus = 0(request not sent), 1(request sent), 2(response received) + // sContents = response + htLazyLoadRequest_plugins: {}, + htLazyLoadRequest_allFiles: {}, - $ON_ADD_APP_PROPERTY : function(sPropertyName, oProperty){ - this.oApp[sPropertyName] = oProperty; - }, + htHTMLLoaded: {}, - $ON_REGISTER_BROWSER_EVENT : function(obj, sEvent, sMsg, aParams, nDelay){ - this.oApp.registerBrowserEvent(obj, sEvent, sMsg, aParams, nDelay); - }, - - $ON_DISABLE_MESSAGE : function(sMsg){ - this.oApp.disableMessage(sMsg, true); - }, + $AFTER_MSG_APP_READY: function () { + this.oApp.exec("EXEC_ON_READY_FUNCTION", []); + }, - $ON_ENABLE_MESSAGE : function(sMsg){ - this.oApp.disableMessage(sMsg, false); - }, - - $ON_LOAD_FULL_PLUGIN : function(aFilenames, sClassName, sMsgName, oThisRef, oArguments){ - var oPluginRef = oThisRef.$this || oThisRef; -// var nIdx = _nIdx||0; - - var sFilename = aFilenames[0]; - - if(!this.htLazyLoadRequest_plugins[sFilename]){ - this.htLazyLoadRequest_plugins[sFilename] = {nStatus:1, sContents:""}; - } - - if(this.htLazyLoadRequest_plugins[sFilename].nStatus === 2){ - //this.oApp.delayedExec("MSG_FULL_PLUGIN_LOADED", [sFilename, sClassName, sMsgName, oThisRef, oArguments, false], 0); - this.oApp.exec("MSG_FULL_PLUGIN_LOADED", [sFilename, sClassName, sMsgName, oThisRef, oArguments, false]); - }else{ - this._loadFullPlugin(aFilenames, sClassName, sMsgName, oThisRef, oArguments, 0); - } - }, - - _loadFullPlugin : function(aFilenames, sClassName, sMsgName, oThisRef, oArguments, nIdx){ - jindo.LazyLoading.load(nhn.husky.SE2M_Configuration.LazyLoad.sJsBaseURI+"/"+aFilenames[nIdx], - jindo.$Fn(function(aFilenames, sClassName, sMsgName, oThisRef, oArguments, nIdx){ - var sCurFilename = aFilenames[nIdx]; + $ON_ADD_APP_PROPERTY: function (sPropertyName, oProperty) { + this.oApp[sPropertyName] = oProperty; + }, - // plugin filename - var sFilename = aFilenames[0]; - if(nIdx == aFilenames.length-1){ - this.htLazyLoadRequest_plugins[sFilename].nStatus=2; - this.oApp.exec("MSG_FULL_PLUGIN_LOADED", [aFilenames, sClassName, sMsgName, oThisRef, oArguments]); - return; - } - //this.oApp.exec("LOAD_FULL_PLUGIN", [aFilenames, sClassName, sMsgName, oThisRef, oArguments, nIdx+1]); - this._loadFullPlugin(aFilenames, sClassName, sMsgName, oThisRef, oArguments, nIdx+1); - }, this).bind(aFilenames, sClassName, sMsgName, oThisRef, oArguments, nIdx), - - "utf-8" - ); - }, - - $ON_MSG_FULL_PLUGIN_LOADED : function(aFilenames, sClassName, sMsgName, oThisRef, oArguments, oRes){ - // oThisRef.$this는 현재 로드되는 플러그인이 parent 인스턴스일 경우 존재 함. oThisRef.$this는 현재 플러그인(oThisRef)를 parent로 삼고 있는 인스턴스 - // oThisRef에 $this 속성이 없다면 parent가 아닌 일반 인스턴스 - // oPluginRef는 결과적으로 상속 관계가 있다면 자식 인스턴스를 아니라면 일반적인 인스턴스를 가짐 - var oPluginRef = oThisRef.$this || oThisRef; - - var sFilename = aFilenames; + $ON_REGISTER_BROWSER_EVENT: function (obj, sEvent, sMsg, aParams, nDelay) { + this.oApp.registerBrowserEvent(obj, sEvent, sMsg, aParams, nDelay); + }, - // now the source code is loaded, remove the loader handlers - for(var i=0, nLen=oThisRef._huskyFLT.length; ili>button"); - } - }, + this.toolbarArea = jindo.$$.getSingle(".se2_tool", oAppContainer); + this.aAllUI = jindo.$$("[class*=" + this.sUIClassPrefix + "]", this.toolbarArea); + this.elTextTool = jindo.$$.getSingle("div.husky_seditor_text_tool", this.toolbarArea); // [SMARTEDITORSUS-1124] 텍스트 툴바 버튼의 라운드 처리 - $LOCAL_BEFORE_FIRST : function(sMsg) { - var aToolItems = jindo.$$(">ul>li[class*=" + this.sUIClassPrefix + "]>button", this.elTextTool); - var nItemLength = aToolItems.length; - - this.elFirstToolbarItem = this.elFirstToolbarItem || aToolItems[0]; - this.elLastToolbarItem = aToolItems[nItemLength-1]; + // alert 레이어 할당 + this._elAlertLayer = jindo.$$.getSingle(".se2_alert_wrap", oAppContainer); + if (this._elAlertLayer) { + this._elAlertTxts = jindo.$$.getSingle(".se2_alert_txts", this._elAlertLayer); + this._elAlertOk = jindo.$$.getSingle(".se2_confirm", this._elAlertLayer); + this._elAlertCancel = jindo.$$.getSingle(".se2_cancel", this._elAlertLayer); + this._elAlertClose = jindo.$$.getSingle(".btn_close", this._elAlertLayer); + } - this.oApp.registerBrowserEvent(this.toolbarArea, "keydown", "NAVIGATE_TOOLBAR", []); - }, - - /** - * @param {Element} oAppContainer - * @param {Object} htOptions - * @param {Array} htOptions.aDisabled 비활성화할 버튼명 배열 - */ - $init : function(oAppContainer, htOptions){ - this._htOptions = htOptions || {}; - this.htUIList = {}; - this.htWrappedUIList = {}; + this.welToolbarArea = jindo.$Element(this.toolbarArea); + for (var i = 0, nCount = this.aAllUI.length; i < nCount; i++) { + if (this.rxUI.test(this.aAllUI[i].className)) { + var sUIName = RegExp.$1; + if (this.htUIList[sUIName] !== undefined) { + continue; + } - this.aUICmdMap = {}; - this._assignHTMLElements(oAppContainer); - }, + this.htUIList[sUIName] = this.aAllUI[i]; + this.htWrappedUIList[sUIName] = jindo.$Element(this.htUIList[sUIName]); + } + } - $ON_MSG_APP_READY : function(){ - if(this.oApp.bMobile){ - this.oApp.registerBrowserEvent(this.toolbarArea, "touchstart", "EVENT_TOOLBAR_TOUCHSTART"); - }else{ - this.oApp.registerBrowserEvent(this.toolbarArea, "mouseover", "EVENT_TOOLBAR_MOUSEOVER"); - this.oApp.registerBrowserEvent(this.toolbarArea, "mouseout", "EVENT_TOOLBAR_MOUSEOUT"); - } - this.oApp.registerBrowserEvent(this.toolbarArea, "mousedown", "EVENT_TOOLBAR_MOUSEDOWN"); - - this.oApp.exec("ADD_APP_PROPERTY", ["getToolbarButtonByUIName", jindo.$Fn(this.getToolbarButtonByUIName, this).bind()]); - - // [SMARTEDITORSUS-1679] 초기 disabled 처리가 필요한 버튼은 비활성화 - if(this._htOptions.aDisabled){ - this._htOptions._sDisabled = "," + this._htOptions.aDisabled.toString() + ","; // 버튼을 활성화할때 비교하기 위한 문자열구성 - this.oApp.exec("DISABLE_UI", [this._htOptions.aDisabled]); - } - }, - + if (jindo.$$.getSingle("DIV.se2_icon_tool") != null) { + this.elFirstToolbarItem = jindo.$$.getSingle("DIV.se2_icon_tool UL.se2_itool1>li>button"); + } + }, - $ON_NAVIGATE_TOOLBAR : function(weEvent) { + $LOCAL_BEFORE_FIRST: function (sMsg) { + var aToolItems = jindo.$$(">ul>li[class*=" + this.sUIClassPrefix + "]>button", this.elTextTool); + var nItemLength = aToolItems.length; - var TAB_KEY_CODE = 9; - //이벤트가 발생한 엘리먼트가 마지막 아이템이고 TAB 키가 눌려졌다면 - if ((weEvent.element == this.elLastToolbarItem) && (weEvent.key().keyCode == TAB_KEY_CODE) ) { - + this.elFirstToolbarItem = this.elFirstToolbarItem || aToolItems[0]; + this.elLastToolbarItem = aToolItems[nItemLength - 1]; - if (weEvent.key().shift) { - //do nothing - } else { - this.elFirstToolbarItem.focus(); - weEvent.stopDefault(); - } - } + this.oApp.registerBrowserEvent(this.toolbarArea, "keydown", "NAVIGATE_TOOLBAR", []); + }, + + /** + * @param {Element} oAppContainer + * @param {Object} htOptions + * @param {Array} htOptions.aDisabled 비활성화할 버튼명 배열 + */ + $init: function (oAppContainer, htOptions) { + this._htOptions = htOptions || {}; + this.htUIList = {}; + this.htWrappedUIList = {}; + + this.aUICmdMap = {}; + this._assignHTMLElements(oAppContainer); + }, + + $ON_MSG_APP_READY: function () { + if (this.oApp.bMobile) { + this.oApp.registerBrowserEvent(this.toolbarArea, "touchstart", "EVENT_TOOLBAR_TOUCHSTART"); + } else { + this.oApp.registerBrowserEvent(this.toolbarArea, "mouseover", "EVENT_TOOLBAR_MOUSEOVER"); + this.oApp.registerBrowserEvent(this.toolbarArea, "mouseout", "EVENT_TOOLBAR_MOUSEOUT"); + } + this.oApp.registerBrowserEvent(this.toolbarArea, "mousedown", "EVENT_TOOLBAR_MOUSEDOWN"); + + this.oApp.exec("ADD_APP_PROPERTY", ["getToolbarButtonByUIName", jindo.$Fn(this.getToolbarButtonByUIName, this).bind()]); + + // [SMARTEDITORSUS-1679] 초기 disabled 처리가 필요한 버튼은 비활성화 + if (this._htOptions.aDisabled) { + this._htOptions._sDisabled = "," + this._htOptions.aDisabled.toString() + ","; // 버튼을 활성화할때 비교하기 위한 문자열구성 + this.oApp.exec("DISABLE_UI", [this._htOptions.aDisabled]); + } + }, - //이벤트가 발생한 엘리먼트가 첫번째 아이템이고 TAB 키가 눌려졌다면 - if (weEvent.element == this.elFirstToolbarItem && (weEvent.key().keyCode == TAB_KEY_CODE)) { - if (weEvent.key().shift) { - weEvent.stopDefault(); - this.elLastToolbarItem.focus(); - } - } - }, + $ON_NAVIGATE_TOOLBAR: function (weEvent) { - $ON_TOGGLE_TOOLBAR_ACTIVE_LAYER : function(elLayer, elBtn, sOpenCmd, aOpenArgs, sCloseCmd, aCloseArgs){ - this.oApp.exec("TOGGLE_ACTIVE_LAYER", [elLayer, "MSG_TOOLBAR_LAYER_SHOWN", [elLayer, elBtn, sOpenCmd, aOpenArgs], sCloseCmd, aCloseArgs]); - }, + var TAB_KEY_CODE = 9; + //이벤트가 발생한 엘리먼트가 마지막 아이템이고 TAB 키가 눌려졌다면 + if ((weEvent.element == this.elLastToolbarItem) && (weEvent.key().keyCode == TAB_KEY_CODE)) { - $ON_MSG_TOOLBAR_LAYER_SHOWN : function(elLayer, elBtn, aOpenCmd, aOpenArgs){ - this.oApp.exec("POSITION_TOOLBAR_LAYER", [elLayer, elBtn]); - if(aOpenCmd){ - this.oApp.exec(aOpenCmd, aOpenArgs); - } - }, - - $ON_SHOW_TOOLBAR_ACTIVE_LAYER : function(elLayer, sCmd, aArgs, elBtn){ - this.oApp.exec("SHOW_ACTIVE_LAYER", [elLayer, sCmd, aArgs]); - this.oApp.exec("POSITION_TOOLBAR_LAYER", [elLayer, elBtn]); - }, - $ON_ENABLE_UI : function(sUIName){ - this._enableUI(sUIName); - }, + if (weEvent.key().shift) { + //do nothing + } else { + this.elFirstToolbarItem.focus(); + weEvent.stopDefault(); + } + } - /** - * [SMARTEDITORSUS-1679] 여러개의 버튼을 동시에 비활성화 할 수 있도록 수정 - * @param {String|Array} vUIName 비활성화할 버튼명, 배열일 경우 여러개 동시 적용 - */ - $ON_DISABLE_UI : function(sUIName){ - if(sUIName instanceof Array){ - for(var i = 0, sName; (sName = sUIName[i]); i++){ - this._disableUI(sName); - } - }else{ - this._disableUI(sUIName); - } - }, - $ON_SELECT_UI : function(sUIName){ - var welUI = this.htWrappedUIList[sUIName]; - if(!welUI){ - return; - } - welUI.removeClass("hover"); - welUI.addClass("active"); - }, + //이벤트가 발생한 엘리먼트가 첫번째 아이템이고 TAB 키가 눌려졌다면 + if (weEvent.element == this.elFirstToolbarItem && (weEvent.key().keyCode == TAB_KEY_CODE)) { + if (weEvent.key().shift) { + weEvent.stopDefault(); + this.elLastToolbarItem.focus(); + } + } + }, - $ON_DESELECT_UI : function(sUIName){ - var welUI = this.htWrappedUIList[sUIName]; - if(!welUI){ - return; - } - welUI.removeClass("active"); - }, + $ON_TOGGLE_TOOLBAR_ACTIVE_LAYER: function (elLayer, elBtn, sOpenCmd, aOpenArgs, sCloseCmd, aCloseArgs) { + this.oApp.exec("TOGGLE_ACTIVE_LAYER", [elLayer, "MSG_TOOLBAR_LAYER_SHOWN", [elLayer, elBtn, sOpenCmd, aOpenArgs], sCloseCmd, aCloseArgs]); + }, - /** - * [SMARTEDITORSUS-1646] 툴바버튼 선택상태를 토글링한다. - * @param {String} sUIName 토글링할 툴바버튼 이름 - */ - $ON_TOGGLE_UI_SELECTED : function(sUIName){ - var welUI = this.htWrappedUIList[sUIName]; - if(!welUI){ - return; - } - if(welUI.hasClass("active")){ - welUI.removeClass("active"); - }else{ - welUI.removeClass("hover"); - welUI.addClass("active"); - } - }, + $ON_MSG_TOOLBAR_LAYER_SHOWN: function (elLayer, elBtn, aOpenCmd, aOpenArgs) { + this.oApp.exec("POSITION_TOOLBAR_LAYER", [elLayer, elBtn]); + if (aOpenCmd) { + this.oApp.exec(aOpenCmd, aOpenArgs); + } + }, - $ON_ENABLE_ALL_UI : function(htOptions){ - if(this.nUIStatus === 1){ - return; - } - - var sUIName, className; - htOptions = htOptions || {}; - var waExceptions = jindo.$A(htOptions.aExceptions || []); + $ON_SHOW_TOOLBAR_ACTIVE_LAYER: function (elLayer, sCmd, aArgs, elBtn) { + this.oApp.exec("SHOW_ACTIVE_LAYER", [elLayer, sCmd, aArgs]); + this.oApp.exec("POSITION_TOOLBAR_LAYER", [elLayer, elBtn]); + }, - for(sUIName in this.htUIList){ - if(sUIName && !waExceptions.has(sUIName)){ - this._enableUI(sUIName); - } -// if(sUIName) this.oApp.exec("ENABLE_UI", [sUIName]); - } -// jindo.$Element(this.toolbarArea).removeClass("off"); + $ON_ENABLE_UI: function (sUIName) { + this._enableUI(sUIName); + }, - this.nUIStatus = 1; - }, + /** + * [SMARTEDITORSUS-1679] 여러개의 버튼을 동시에 비활성화 할 수 있도록 수정 + * @param {String|Array} vUIName 비활성화할 버튼명, 배열일 경우 여러개 동시 적용 + */ + $ON_DISABLE_UI: function (sUIName) { + if (sUIName instanceof Array) { + for (var i = 0, sName; (sName = sUIName[i]); i++) { + this._disableUI(sName); + } + } else { + this._disableUI(sUIName); + } + }, - $ON_DISABLE_ALL_UI : function(htOptions){ - if(this.nUIStatus === 2){ - return; - } - - var sUIName; - htOptions = htOptions || {}; - var waExceptions = jindo.$A(htOptions.aExceptions || []); - var bLeavlActiveLayer = htOptions.bLeaveActiveLayer || false; + $ON_SELECT_UI: function (sUIName) { + var welUI = this.htWrappedUIList[sUIName]; + if (!welUI) { + return; + } + welUI.removeClass("hover"); + welUI.addClass("active"); + }, - if(!bLeavlActiveLayer){ - this.oApp.exec("HIDE_ACTIVE_LAYER"); - } + $ON_DESELECT_UI: function (sUIName) { + var welUI = this.htWrappedUIList[sUIName]; + if (!welUI) { + return; + } + welUI.removeClass("active"); + }, - for(sUIName in this.htUIList){ - if(sUIName && !waExceptions.has(sUIName)){ - this._disableUI(sUIName); - } -// if(sUIName) this.oApp.exec("DISABLE_UI", [sUIName]); - } -// jindo.$Element(this.toolbarArea).addClass("off"); + /** + * [SMARTEDITORSUS-1646] 툴바버튼 선택상태를 토글링한다. + * @param {String} sUIName 토글링할 툴바버튼 이름 + */ + $ON_TOGGLE_UI_SELECTED: function (sUIName) { + var welUI = this.htWrappedUIList[sUIName]; + if (!welUI) { + return; + } + if (welUI.hasClass("active")) { + welUI.removeClass("active"); + } else { + welUI.removeClass("hover"); + welUI.addClass("active"); + } + }, - this.nUIStatus = 2; - }, - - $ON_MSG_STYLE_CHANGED : function(sAttributeName, attributeValue){ - if(attributeValue === "@^"){ - this.oApp.exec("SELECT_UI", [sAttributeName]); - }else{ - this.oApp.exec("DESELECT_UI", [sAttributeName]); - } - }, + $ON_ENABLE_ALL_UI: function (htOptions) { + if (this.nUIStatus === 1) { + return; + } - $ON_POSITION_TOOLBAR_LAYER : function(elLayer, htOption){ - var nLayerLeft, nLayerRight, nToolbarLeft, nToolbarRight; - - elLayer = jindo.$(elLayer); - htOption = htOption || {}; - var elBtn = jindo.$(htOption.elBtn); - var sAlign = htOption.sAlign; + var sUIName, className; + htOptions = htOptions || {}; + var waExceptions = jindo.$A(htOptions.aExceptions || []); - var nMargin = -1; - if(!elLayer){ - return; - } - if(elBtn && elBtn.tagName && elBtn.tagName == "BUTTON"){ - elBtn.parentNode.appendChild(elLayer); - } + for (sUIName in this.htUIList) { + if (sUIName && !waExceptions.has(sUIName)) { + this._enableUI(sUIName); + } + // if(sUIName) this.oApp.exec("ENABLE_UI", [sUIName]); + } + // jindo.$Element(this.toolbarArea).removeClass("off"); - var welLayer = jindo.$Element(elLayer); + this.nUIStatus = 1; + }, - if(sAlign != "right"){ - elLayer.style.left = "0"; + $ON_DISABLE_ALL_UI: function (htOptions) { + if (this.nUIStatus === 2) { + return; + } - nLayerLeft = welLayer.offset().left; - nLayerRight = nLayerLeft + elLayer.offsetWidth; - - nToolbarLeft = this.welToolbarArea.offset().left; - nToolbarRight = nToolbarLeft + this.toolbarArea.offsetWidth; + var sUIName; + htOptions = htOptions || {}; + var waExceptions = jindo.$A(htOptions.aExceptions || []); + var bLeavlActiveLayer = htOptions.bLeaveActiveLayer || false; - if(nLayerRight > nToolbarRight){ - welLayer.css("left", (nToolbarRight-nLayerRight-nMargin)+"px"); - } - - if(nLayerLeft < nToolbarLeft){ - welLayer.css("left", (nToolbarLeft-nLayerLeft+nMargin)+"px"); - } - }else{ - elLayer.style.right = "0"; + if (!bLeavlActiveLayer) { + this.oApp.exec("HIDE_ACTIVE_LAYER"); + } - nLayerLeft = welLayer.offset().left; - nLayerRight = nLayerLeft + elLayer.offsetWidth; - - nToolbarLeft = this.welToolbarArea.offset().left; - nToolbarRight = nToolbarLeft + this.toolbarArea.offsetWidth; + for (sUIName in this.htUIList) { + if (sUIName && !waExceptions.has(sUIName)) { + this._disableUI(sUIName); + } + // if(sUIName) this.oApp.exec("DISABLE_UI", [sUIName]); + } + // jindo.$Element(this.toolbarArea).addClass("off"); - if(nLayerRight > nToolbarRight){ - welLayer.css("right", -1*(nToolbarRight-nLayerRight-nMargin)+"px"); - } - - if(nLayerLeft < nToolbarLeft){ - welLayer.css("right", -1*(nToolbarLeft-nLayerLeft+nMargin)+"px"); - } - } - }, - - $ON_EVENT_TOOLBAR_MOUSEOVER : function(weEvent){ - if(this.nUIStatus === 2){ - return; - } + this.nUIStatus = 2; + }, - var aAffectedElements = this._getAffectedElements(weEvent.element); - for(var i=0; i childNodes.length<=2) - // -> In this case, do not close here(mousedown). The layer will be closed on "click". If we close the layer here, the click event will open it again because it toggles the visibility. - while(elTmp){ - if(elTmp.className && elTmp.className.match(/active/) && (elTmp.childNodes.length>2 || elTmp.parentNode.className.match(/se2_pair/))){ - return; - } - elTmp = elTmp.parentNode; - } - this.oApp.exec("HIDE_ACTIVE_LAYER_IF_NOT_CHILD", [weEvent.element]); - }, + $ON_POSITION_TOOLBAR_LAYER: function (elLayer, htOption) { + var nLayerLeft, nLayerRight, nToolbarLeft, nToolbarRight; - _enableUI : function(sUIName){ - // [SMARTEDITORSUS-1679] 초기 disabled 설정된 버튼은 skip - if(this._htOptions._sDisabled && this._htOptions._sDisabled.indexOf(","+sUIName+",") > -1){ - return; - } - var i, nLen; - - this.nUIStatus = 0; + elLayer = jindo.$(elLayer); + htOption = htOption || {}; + var elBtn = jindo.$(htOption.elBtn); + var sAlign = htOption.sAlign; - var welUI = this.htWrappedUIList[sUIName]; - var elUI = this.htUIList[sUIName]; - if(!welUI){ - return; - } - welUI.removeClass("off"); - - var aAllBtns = elUI.getElementsByTagName("BUTTON"); - for(i=0, nLen=aAllBtns.length; i - // ", - - $init : function(elAppContainer){ - this.elAppContainer = elAppContainer; - }, - - $ON_MSG_APP_READY : function(){}, - - _assignHTMLElements : function(oAppContainer){ - var htConfiguration = nhn.husky.SE2M_Configuration.SE2M_ColorPalette; - if(htConfiguration){ - this.bUseRecentColor = htConfiguration.bUseRecentColor || false; - this.URL_COLOR_ADD = htConfiguration.addColorURL || "http://api.se2.naver.com/1/colortable/TextAdd.nhn"; - this.URL_COLOR_UPDATE = htConfiguration.updateColorURL || "http://api.se2.naver.com/1/colortable/TextUpdate.nhn"; - this.URL_COLOR_LIST = htConfiguration.colorListURL || "http://api.se2.naver.com/1/colortable/TextList.nhn"; - } - - this.elColorPaletteLayer = jindo.$$.getSingle("DIV.husky_se2m_color_palette", oAppContainer); +nhn.husky.SE2M_ColorPalette = jindo.$Class({ + name: "SE2M_ColorPalette", + elAppContainer: null, + bUseRecentColor: false, + nLimitRecentColor: 17, + rxRGBColorPattern: /rgb\((\d+), ?(\d+), ?(\d+)\)/i, + rxColorPattern: /^#?[0-9a-fA-F]{6}$|^rgb\(\d+, ?\d+, ?\d+\)$/i, + aRecentColor: [], // 최근 사용한 색 목록, 가장 최근에 등록한 색의 index가 가장 작음 + URL_COLOR_LIST: "", + URL_COLOR_ADD: "", + URL_COLOR_UPDATE: "", + sRecentColorTemp: "
  • ", - this.elColorPaletteLayerColorPicker = jindo.$$.getSingle("DIV.husky_se2m_color_palette_colorpicker", this.elColorPaletteLayer); - this.elRecentColorForm = jindo.$$.getSingle("form", this.elColorPaletteLayerColorPicker); - - this.elBackgroundColor = jindo.$$.getSingle("ul.husky_se2m_bgcolor_list", oAppContainer); - this.elInputColorCode = jindo.$$.getSingle("INPUT.husky_se2m_cp_colorcode", this.elColorPaletteLayerColorPicker); - - this.elPreview = jindo.$$.getSingle("SPAN.husky_se2m_cp_preview", this.elColorPaletteLayerColorPicker); - this.elCP_ColPanel = jindo.$$.getSingle("DIV.husky_se2m_cp_colpanel", this.elColorPaletteLayerColorPicker); - this.elCP_HuePanel = jindo.$$.getSingle("DIV.husky_se2m_cp_huepanel", this.elColorPaletteLayerColorPicker); + $init: function (elAppContainer) { + this.elAppContainer = elAppContainer; + }, - this.elCP_ColPanel.style.position = "relative"; - this.elCP_HuePanel.style.position = "relative"; + $ON_MSG_APP_READY: function () { }, - this.elColorPaletteLayerColorPicker.style.display = "none"; - - this.elMoreBtn = jindo.$$.getSingle("BUTTON.husky_se2m_color_palette_more_btn", this.elColorPaletteLayer); - this.welMoreBtn = jindo.$Element(this.elMoreBtn); - - this.elOkBtn = jindo.$$.getSingle("BUTTON.husky_se2m_color_palette_ok_btn", this.elColorPaletteLayer); - - if(this.bUseRecentColor){ - this.elColorPaletteLayerRecent = jindo.$$.getSingle("DIV.husky_se2m_color_palette_recent", this.elColorPaletteLayer); - this.elRecentColor = jindo.$$.getSingle("ul.se2_pick_color", this.elColorPaletteLayerRecent); - this.elDummyNode = jindo.$$.getSingle("ul.se2_pick_color > li", this.elColorPaletteLayerRecent) || null; - - this.elColorPaletteLayerRecent.style.display = "none"; - } - }, - - $LOCAL_BEFORE_FIRST : function(){ - this._assignHTMLElements(this.elAppContainer); - - if(this.elDummyNode){ - jindo.$Element(jindo.$$.getSingle("ul.se2_pick_color > li", this.elColorPaletteLayerRecent)).leave(); - } + _assignHTMLElements: function (oAppContainer) { + var htConfiguration = nhn.husky.SE2M_Configuration.SE2M_ColorPalette; + if (htConfiguration) { + this.bUseRecentColor = htConfiguration.bUseRecentColor || false; + this.URL_COLOR_ADD = htConfiguration.addColorURL || "http://api.se2.naver.com/1/colortable/TextAdd.nhn"; + this.URL_COLOR_UPDATE = htConfiguration.updateColorURL || "http://api.se2.naver.com/1/colortable/TextUpdate.nhn"; + this.URL_COLOR_LIST = htConfiguration.colorListURL || "http://api.se2.naver.com/1/colortable/TextList.nhn"; + } - if( this.bUseRecentColor ){ - this._ajaxRecentColor(this._ajaxRecentColorCallback); - } - - this.oApp.registerBrowserEvent(this.elColorPaletteLayer, "click", "EVENT_CLICK_COLOR_PALETTE"); - // [SMARTEDITORSUS-1833] 아이패드에서 mouseover 이벤트리스너를 등록하면 후속 click 이벤트가 바로 동작하지 않음 - // 모바일환경에서 hover 처리는 의미가 없으므로 PC 환경에서만 hover 처리하도록 함 - if(!this.oApp.bMobile){ - this.oApp.registerBrowserEvent(this.elBackgroundColor, "mouseover", "EVENT_MOUSEOVER_COLOR_PALETTE"); - this.oApp.registerBrowserEvent(this.elColorPaletteLayer, "mouseover", "EVENT_MOUSEOVER_COLOR_PALETTE"); - this.oApp.registerBrowserEvent(this.elBackgroundColor, "mouseout", "EVENT_MOUSEOUT_COLOR_PALETTE"); - this.oApp.registerBrowserEvent(this.elColorPaletteLayer, "mouseout", "EVENT_MOUSEOUT_COLOR_PALETTE"); - } - }, - - $ON_EVENT_MOUSEOVER_COLOR_PALETTE : function(oEvent){ - var elHovered = oEvent.element; - while(elHovered && elHovered.tagName && elHovered.tagName.toLowerCase() != "li"){ - elHovered = elHovered.parentNode; - } - //조건 추가-by cielo 2010.04.20 - if(!elHovered || !elHovered.nodeType || elHovered.nodeType == 9){return;} - if(elHovered.className == "" || (!elHovered.className) || typeof(elHovered.className) == 'undefined'){jindo.$Element(elHovered).addClass("hover");} - }, - - $ON_EVENT_MOUSEOUT_COLOR_PALETTE : function(oEvent){ - var elHovered = oEvent.element; - - while(elHovered && elHovered.tagName && elHovered.tagName.toLowerCase() != "li"){ - elHovered = elHovered.parentNode; - } - if(!elHovered){return;} - if(elHovered.className == "hover"){jindo.$Element(elHovered).removeClass("hover");} - }, - - $ON_EVENT_CLICK_COLOR_PALETTE : function(oEvent){ - var elClicked = oEvent.element; - - while(elClicked.tagName == "SPAN"){elClicked = elClicked.parentNode;} - - if(elClicked.tagName && elClicked.tagName == "BUTTON"){ - if(elClicked == this.elMoreBtn){ - this.oApp.exec("TOGGLE_COLOR_PICKER"); - return; - } - - this.oApp.exec("APPLY_COLOR", [elClicked]); - } - }, - - $ON_APPLY_COLOR : function(elButton){ - var sColorCode = this.elInputColorCode.value, - welColorParent = null; - - if(sColorCode.indexOf("#") == -1){ - sColorCode = "#" + sColorCode; - this.elInputColorCode.value = sColorCode; - } - - // 입력 버튼인 경우 - if(elButton == this.elOkBtn){ - if(!this._verifyColorCode(sColorCode)){ - this.elInputColorCode.value = ""; - alert(this.oApp.$MSG("SE_Color.invalidColorCode")); - this.elInputColorCode.focus(); - - return; - } - - this.oApp.exec("COLOR_PALETTE_APPLY_COLOR", [sColorCode,true]); - - return; - } - - // 색상 버튼인 경우 - welColorParent = jindo.$Element(elButton.parentNode.parentNode.parentNode); - sColorCode = elButton.title; - - if(welColorParent.hasClass("husky_se2m_color_palette")){ // 템플릿 색상 적용 - /* - * [SMARTEDITORSUS-1884][SMARTEDITORSUS-2117] - * 설정값 제거(r12236) 전에도 - * 모든 타입에서 - * nhn.husky.SE2M_Configuration.SE2M_ColorPalette.bAddRecentColorFromDefault 값이 - * undefined인 상태로 동작하고 있었기 때문에 - * false로 처리 - */ - this.oApp.exec("COLOR_PALETTE_APPLY_COLOR", [sColorCode, false]); - }else if(welColorParent.hasClass("husky_se2m_color_palette_recent")){ // 최근 색상 적용 - this.oApp.exec("COLOR_PALETTE_APPLY_COLOR", [sColorCode,true]); - } - }, - - $ON_RESET_COLOR_PALETTE : function(){ - this._initColor(); - }, - - $ON_TOGGLE_COLOR_PICKER : function(){ - if(this.elColorPaletteLayerColorPicker.style.display == "none"){ - this.oApp.exec("SHOW_COLOR_PICKER"); - }else{ - this.oApp.exec("HIDE_COLOR_PICKER"); - } - }, - - $ON_SHOW_COLOR_PICKER : function(){ - this.elColorPaletteLayerColorPicker.style.display = ""; + this.elColorPaletteLayer = jindo.$$.getSingle("DIV.husky_se2m_color_palette", oAppContainer); - this.cpp = new nhn.ColorPicker(this.elCP_ColPanel, {huePanel:this.elCP_HuePanel}); - var fn = jindo.$Fn(function(oEvent) { - this.elPreview.style.backgroundColor = oEvent.hexColor; - this.elInputColorCode.value = oEvent.hexColor; - }, this).bind(); - this.cpp.attach("colorchange", fn); + this.elColorPaletteLayerColorPicker = jindo.$$.getSingle("DIV.husky_se2m_color_palette_colorpicker", this.elColorPaletteLayer); + this.elRecentColorForm = jindo.$$.getSingle("form", this.elColorPaletteLayerColorPicker); - this.$ON_SHOW_COLOR_PICKER = this._showColorPickerMain; - this.$ON_SHOW_COLOR_PICKER(); - }, - - $ON_HIDE_COLOR_PICKER : function(){ - this.elColorPaletteLayerColorPicker.style.display = "none"; - this.welMoreBtn.addClass("se2_view_more"); - this.welMoreBtn.removeClass("se2_view_more2"); - }, - - $ON_SHOW_COLOR_PALETTE : function(sCallbackCmd, oLayerContainer){ - this.sCallbackCmd = sCallbackCmd; - this.oLayerContainer = oLayerContainer; + this.elBackgroundColor = jindo.$$.getSingle("ul.husky_se2m_bgcolor_list", oAppContainer); + this.elInputColorCode = jindo.$$.getSingle("INPUT.husky_se2m_cp_colorcode", this.elColorPaletteLayerColorPicker); - this.oLayerContainer.insertBefore(this.elColorPaletteLayer, null); + this.elPreview = jindo.$$.getSingle("SPAN.husky_se2m_cp_preview", this.elColorPaletteLayerColorPicker); + this.elCP_ColPanel = jindo.$$.getSingle("DIV.husky_se2m_cp_colpanel", this.elColorPaletteLayerColorPicker); + this.elCP_HuePanel = jindo.$$.getSingle("DIV.husky_se2m_cp_huepanel", this.elColorPaletteLayerColorPicker); - this.elColorPaletteLayer.style.display = "block"; - - this.oApp.delayedExec("POSITION_TOOLBAR_LAYER", [this.elColorPaletteLayer.parentNode.parentNode], 0); - }, + this.elCP_ColPanel.style.position = "relative"; + this.elCP_HuePanel.style.position = "relative"; - $ON_HIDE_COLOR_PALETTE : function(){ - this.elColorPaletteLayer.style.display = "none"; - }, - - $ON_COLOR_PALETTE_APPLY_COLOR : function(sColorCode , bAddRecentColor){ - bAddRecentColor = (!bAddRecentColor)? false : bAddRecentColor; - sColorCode = this._getHexColorCode(sColorCode); - - //더보기 레이어에서 적용한 색상만 최근 사용한 색에 추가한다. - if( this.bUseRecentColor && !!bAddRecentColor ){ - this.oApp.exec("ADD_RECENT_COLOR", [sColorCode]); - } - this.oApp.exec(this.sCallbackCmd, [sColorCode]); - }, + this.elColorPaletteLayerColorPicker.style.display = "none"; - $ON_EVENT_MOUSEUP_COLOR_PALETTE : function(oEvent){ - var elButton = oEvent.element; - if(! elButton.style.backgroundColor){return;} - - this.oApp.exec("COLOR_PALETTE_APPLY_COLOR", [elButton.style.backgroundColor,false]); - }, - - $ON_ADD_RECENT_COLOR : function(sRGBCode){ - var bAdd = (this.aRecentColor.length === 0); - - this._addRecentColor(sRGBCode); - - if(bAdd){ - this._ajaxAddColor(); - }else{ - this._ajaxUpdateColor(); - } - - this._redrawRecentColorElement(); - }, - - _verifyColorCode : function(sColorCode){ - return this.rxColorPattern.test(sColorCode); - }, - - _getHexColorCode : function(sColorCode){ - if(this.rxRGBColorPattern.test(sColorCode)){ - var dec2Hex = function(sDec){ - var sTmp = parseInt(sDec, 10).toString(16); - if(sTmp.length<2){sTmp = "0"+sTmp;} - return sTmp.toUpperCase(); - }; - - var sR = dec2Hex(RegExp.$1); - var sG = dec2Hex(RegExp.$2); - var sB = dec2Hex(RegExp.$3); - sColorCode = "#"+sR+sG+sB; - } - - return sColorCode; - }, - - _addRecentColor : function(sRGBCode){ - var waRecentColor = jindo.$A(this.aRecentColor); - - waRecentColor = waRecentColor.refuse(sRGBCode); - waRecentColor.unshift(sRGBCode); - - if(waRecentColor.length() > this.nLimitRecentColor){ - waRecentColor.length(this.nLimitRecentColor); - } - - this.aRecentColor = waRecentColor.$value(); - }, - - _redrawRecentColorElement : function(){ - var aRecentColorHtml = [], - nRecentColor = this.aRecentColor.length, - i; - - if(nRecentColor === 0){ - return; - } - - for(i=0; i this.nLimitRecentColor){ - waColorList.length(this.nLimitRecentColor); - } - - aColorList = waColorList.reverse().$value(); + if (this.bUseRecentColor) { + this.elColorPaletteLayerRecent = jindo.$$.getSingle("DIV.husky_se2m_color_palette_recent", this.elColorPaletteLayer); + this.elRecentColor = jindo.$$.getSingle("ul.se2_pick_color", this.elColorPaletteLayerRecent); + this.elDummyNode = jindo.$$.getSingle("ul.se2_pick_color > li", this.elColorPaletteLayerRecent) || null; - for(i = 0, nLen = aColorList.length; i < nLen; i++){ - this._addRecentColor(this._getHexColorCode(aColorList[i])); - } - - this._redrawRecentColorElement(); - } + this.elColorPaletteLayerRecent.style.display = "none"; + } + }, + + $LOCAL_BEFORE_FIRST: function () { + this._assignHTMLElements(this.elAppContainer); + + if (this.elDummyNode) { + jindo.$Element(jindo.$$.getSingle("ul.se2_pick_color > li", this.elColorPaletteLayerRecent)).leave(); + } + + if (this.bUseRecentColor) { + this._ajaxRecentColor(this._ajaxRecentColorCallback); + } + + this.oApp.registerBrowserEvent(this.elColorPaletteLayer, "click", "EVENT_CLICK_COLOR_PALETTE"); + // [SMARTEDITORSUS-1833] 아이패드에서 mouseover 이벤트리스너를 등록하면 후속 click 이벤트가 바로 동작하지 않음 + // 모바일환경에서 hover 처리는 의미가 없으므로 PC 환경에서만 hover 처리하도록 함 + if (!this.oApp.bMobile) { + this.oApp.registerBrowserEvent(this.elBackgroundColor, "mouseover", "EVENT_MOUSEOVER_COLOR_PALETTE"); + this.oApp.registerBrowserEvent(this.elColorPaletteLayer, "mouseover", "EVENT_MOUSEOVER_COLOR_PALETTE"); + this.oApp.registerBrowserEvent(this.elBackgroundColor, "mouseout", "EVENT_MOUSEOUT_COLOR_PALETTE"); + this.oApp.registerBrowserEvent(this.elColorPaletteLayer, "mouseout", "EVENT_MOUSEOUT_COLOR_PALETTE"); + } + }, + + $ON_EVENT_MOUSEOVER_COLOR_PALETTE: function (oEvent) { + var elHovered = oEvent.element; + while (elHovered && elHovered.tagName && elHovered.tagName.toLowerCase() != "li") { + elHovered = elHovered.parentNode; + } + //조건 추가-by cielo 2010.04.20 + if (!elHovered || !elHovered.nodeType || elHovered.nodeType == 9) { return; } + if (elHovered.className == "" || (!elHovered.className) || typeof (elHovered.className) == 'undefined') { jindo.$Element(elHovered).addClass("hover"); } + }, + + $ON_EVENT_MOUSEOUT_COLOR_PALETTE: function (oEvent) { + var elHovered = oEvent.element; + + while (elHovered && elHovered.tagName && elHovered.tagName.toLowerCase() != "li") { + elHovered = elHovered.parentNode; + } + if (!elHovered) { return; } + if (elHovered.className == "hover") { jindo.$Element(elHovered).removeClass("hover"); } + }, + + $ON_EVENT_CLICK_COLOR_PALETTE: function (oEvent) { + var elClicked = oEvent.element; + + while (elClicked.tagName == "SPAN") { elClicked = elClicked.parentNode; } + + if (elClicked.tagName && elClicked.tagName == "BUTTON") { + if (elClicked == this.elMoreBtn) { + this.oApp.exec("TOGGLE_COLOR_PICKER"); + return; + } + + this.oApp.exec("APPLY_COLOR", [elClicked]); + } + }, + + $ON_APPLY_COLOR: function (elButton) { + var sColorCode = this.elInputColorCode.value, + welColorParent = null; + + if (sColorCode.indexOf("#") == -1) { + sColorCode = "#" + sColorCode; + this.elInputColorCode.value = sColorCode; + } + + // 입력 버튼인 경우 + if (elButton == this.elOkBtn) { + if (!this._verifyColorCode(sColorCode)) { + this.elInputColorCode.value = ""; + alert(this.oApp.$MSG("SE_Color.invalidColorCode")); + this.elInputColorCode.focus(); + + return; + } + + this.oApp.exec("COLOR_PALETTE_APPLY_COLOR", [sColorCode, true]); + + return; + } + + // 색상 버튼인 경우 + welColorParent = jindo.$Element(elButton.parentNode.parentNode.parentNode); + sColorCode = elButton.title; + + if (welColorParent.hasClass("husky_se2m_color_palette")) { // 템플릿 색상 적용 + /* + * [SMARTEDITORSUS-1884][SMARTEDITORSUS-2117] + * 설정값 제거(r12236) 전에도 + * 모든 타입에서 + * nhn.husky.SE2M_Configuration.SE2M_ColorPalette.bAddRecentColorFromDefault 값이 + * undefined인 상태로 동작하고 있었기 때문에 + * false로 처리 + */ + this.oApp.exec("COLOR_PALETTE_APPLY_COLOR", [sColorCode, false]); + } else if (welColorParent.hasClass("husky_se2m_color_palette_recent")) { // 최근 색상 적용 + this.oApp.exec("COLOR_PALETTE_APPLY_COLOR", [sColorCode, true]); + } + }, + + $ON_RESET_COLOR_PALETTE: function () { + this._initColor(); + }, + + $ON_TOGGLE_COLOR_PICKER: function () { + if (this.elColorPaletteLayerColorPicker.style.display == "none") { + this.oApp.exec("SHOW_COLOR_PICKER"); + } else { + this.oApp.exec("HIDE_COLOR_PICKER"); + } + }, + + $ON_SHOW_COLOR_PICKER: function () { + this.elColorPaletteLayerColorPicker.style.display = ""; + + this.cpp = new nhn.ColorPicker(this.elCP_ColPanel, { huePanel: this.elCP_HuePanel }); + var fn = jindo.$Fn(function (oEvent) { + this.elPreview.style.backgroundColor = oEvent.hexColor; + this.elInputColorCode.value = oEvent.hexColor; + }, this).bind(); + this.cpp.attach("colorchange", fn); + + this.$ON_SHOW_COLOR_PICKER = this._showColorPickerMain; + this.$ON_SHOW_COLOR_PICKER(); + }, + + $ON_HIDE_COLOR_PICKER: function () { + this.elColorPaletteLayerColorPicker.style.display = "none"; + this.welMoreBtn.addClass("se2_view_more"); + this.welMoreBtn.removeClass("se2_view_more2"); + }, + + $ON_SHOW_COLOR_PALETTE: function (sCallbackCmd, oLayerContainer) { + this.sCallbackCmd = sCallbackCmd; + this.oLayerContainer = oLayerContainer; + + this.oLayerContainer.insertBefore(this.elColorPaletteLayer, null); + + this.elColorPaletteLayer.style.display = "block"; + + this.oApp.delayedExec("POSITION_TOOLBAR_LAYER", [this.elColorPaletteLayer.parentNode.parentNode], 0); + }, + + $ON_HIDE_COLOR_PALETTE: function () { + this.elColorPaletteLayer.style.display = "none"; + }, + + $ON_COLOR_PALETTE_APPLY_COLOR: function (sColorCode, bAddRecentColor) { + bAddRecentColor = (!bAddRecentColor) ? false : bAddRecentColor; + sColorCode = this._getHexColorCode(sColorCode); + + //더보기 레이어에서 적용한 색상만 최근 사용한 색에 추가한다. + if (this.bUseRecentColor && !!bAddRecentColor) { + this.oApp.exec("ADD_RECENT_COLOR", [sColorCode]); + } + this.oApp.exec(this.sCallbackCmd, [sColorCode]); + }, + + $ON_EVENT_MOUSEUP_COLOR_PALETTE: function (oEvent) { + var elButton = oEvent.element; + if (!elButton.style.backgroundColor) { return; } + + this.oApp.exec("COLOR_PALETTE_APPLY_COLOR", [elButton.style.backgroundColor, false]); + }, + + $ON_ADD_RECENT_COLOR: function (sRGBCode) { + var bAdd = (this.aRecentColor.length === 0); + + this._addRecentColor(sRGBCode); + + if (bAdd) { + this._ajaxAddColor(); + } else { + this._ajaxUpdateColor(); + } + + this._redrawRecentColorElement(); + }, + + _verifyColorCode: function (sColorCode) { + return this.rxColorPattern.test(sColorCode); + }, + + _getHexColorCode: function (sColorCode) { + if (this.rxRGBColorPattern.test(sColorCode)) { + var dec2Hex = function (sDec) { + var sTmp = parseInt(sDec, 10).toString(16); + if (sTmp.length < 2) { sTmp = "0" + sTmp; } + return sTmp.toUpperCase(); + }; + + var sR = dec2Hex(RegExp.$1); + var sG = dec2Hex(RegExp.$2); + var sB = dec2Hex(RegExp.$3); + sColorCode = "#" + sR + sG + sB; + } + + return sColorCode; + }, + + _addRecentColor: function (sRGBCode) { + var waRecentColor = jindo.$A(this.aRecentColor); + + waRecentColor = waRecentColor.refuse(sRGBCode); + waRecentColor.unshift(sRGBCode); + + if (waRecentColor.length() > this.nLimitRecentColor) { + waRecentColor.length(this.nLimitRecentColor); + } + + this.aRecentColor = waRecentColor.$value(); + }, + + _redrawRecentColorElement: function () { + var aRecentColorHtml = [], + nRecentColor = this.aRecentColor.length, + i; + + if (nRecentColor === 0) { + return; + } + + for (i = 0; i < nRecentColor; i++) { + aRecentColorHtml.push(this.sRecentColorTemp.replace(/\{RGB_CODE\}/gi, this.aRecentColor[i])); + } + + this.elRecentColor.innerHTML = aRecentColorHtml.join(""); + + this.elColorPaletteLayerRecent.style.display = "block"; + }, + + _ajaxAddColor: function () { + jindo.$Ajax(this.URL_COLOR_ADD, { + type: "jsonp", + onload: function () { } + }).request({ + text_key: "colortable", + text_data: this.aRecentColor.join(",") + }); + }, + + _ajaxUpdateColor: function () { + jindo.$Ajax(this.URL_COLOR_UPDATE, { + type: "jsonp", + onload: function () { } + }).request({ + text_key: "colortable", + text_data: this.aRecentColor.join(",") + }); + }, + + _showColorPickerMain: function () { + this._initColor(); + this.elColorPaletteLayerColorPicker.style.display = ""; + this.welMoreBtn.removeClass("se2_view_more"); + this.welMoreBtn.addClass("se2_view_more2"); + }, + + _initColor: function () { + if (this.cpp) { this.cpp.rgb({ r: 0, g: 0, b: 0 }); } + this.elPreview.style.backgroundColor = '#' + '000000'; + this.elInputColorCode.value = '#' + '000000'; + this.oApp.exec("HIDE_COLOR_PICKER"); + }, + + _ajaxRecentColor: function (fCallback) { + jindo.$Ajax(this.URL_COLOR_LIST, { + type: "jsonp", + onload: jindo.$Fn(fCallback, this).bind() + }).request(); + }, + + _ajaxRecentColorCallback: function (htResponse) { + var aColorList = htResponse.json()["result"], + waColorList, + i, nLen; + + if (!aColorList || !!aColorList.error) { + return; + } + + waColorList = jindo.$A(aColorList).filter(this._verifyColorCode, this); + + if (waColorList.length() > this.nLimitRecentColor) { + waColorList.length(this.nLimitRecentColor); + } + + aColorList = waColorList.reverse().$value(); + + for (i = 0, nLen = aColorList.length; i < nLen; i++) { + this._addRecentColor(this._getHexColorCode(aColorList[i])); + } + + this._redrawRecentColorElement(); + } }).extend(jindo.Component); //} /** @@ -9898,42 +9902,42 @@ nhn.husky.SE2M_LineHeightWithLayerUI = jindo.$Class({ * @name hp_SE_FontColor.js */ nhn.husky.SE2M_FontColor = jindo.$Class({ - name : "SE2M_FontColor", - rxColorPattern : /^#?[0-9a-fA-F]{6}$|^rgb\(\d+, ?\d+, ?\d+\)$/i, + name: "SE2M_FontColor", + rxColorPattern: /^#?[0-9a-fA-F]{6}$|^rgb\(\d+, ?\d+, ?\d+\)$/i, - $init : function(elAppContainer){ - this._assignHTMLElements(elAppContainer); - }, - - _assignHTMLElements : function(elAppContainer){ - //@ec[ - this.elLastUsed = jindo.$$.getSingle("SPAN.husky_se2m_fontColor_lastUsed", elAppContainer); + $init: function (elAppContainer) { + this._assignHTMLElements(elAppContainer); + }, - this.elDropdownLayer = jindo.$$.getSingle("DIV.husky_se2m_fontcolor_layer", elAppContainer); - this.elPaletteHolder = jindo.$$.getSingle("DIV.husky_se2m_fontcolor_paletteHolder", this.elDropdownLayer); - //@ec] + _assignHTMLElements: function (elAppContainer) { + //@ec[ + this.elLastUsed = jindo.$$.getSingle("SPAN.husky_se2m_fontColor_lastUsed", elAppContainer); - this._setLastUsedFontColor("#000000"); - }, + this.elDropdownLayer = jindo.$$.getSingle("DIV.husky_se2m_fontcolor_layer", elAppContainer); + this.elPaletteHolder = jindo.$$.getSingle("DIV.husky_se2m_fontcolor_paletteHolder", this.elDropdownLayer); + //@ec] - $BEFORE_MSG_APP_READY : function() { - this.oApp.exec("ADD_APP_PROPERTY", ["getLastUsedFontColor", jindo.$Fn(this.getLastUsedFontColor, this).bind()]); - }, - - $ON_MSG_APP_READY : function(){ - this.oApp.exec("REGISTER_UI_EVENT", ["fontColorA", "click", "APPLY_LAST_USED_FONTCOLOR"]); - this.oApp.exec("REGISTER_UI_EVENT", ["fontColorB", "click", "TOGGLE_FONTCOLOR_LAYER"]); - this.oApp.registerLazyMessage(["APPLY_LAST_USED_FONTCOLOR", "TOGGLE_FONTCOLOR_LAYER"], ["hp_SE2M_FontColor$Lazy.js"]); - }, + this._setLastUsedFontColor("#000000"); + }, - _setLastUsedFontColor : function(sFontColor){ - this.sLastUsedColor = sFontColor; - this.elLastUsed.style.backgroundColor = this.sLastUsedColor; - }, - - getLastUsedFontColor : function(){ - return (!!this.sLastUsedColor) ? this.sLastUsedColor : '#000000'; - } + $BEFORE_MSG_APP_READY: function () { + this.oApp.exec("ADD_APP_PROPERTY", ["getLastUsedFontColor", jindo.$Fn(this.getLastUsedFontColor, this).bind()]); + }, + + $ON_MSG_APP_READY: function () { + this.oApp.exec("REGISTER_UI_EVENT", ["fontColorA", "click", "APPLY_LAST_USED_FONTCOLOR"]); + this.oApp.exec("REGISTER_UI_EVENT", ["fontColorB", "click", "TOGGLE_FONTCOLOR_LAYER"]); + this.oApp.registerLazyMessage(["APPLY_LAST_USED_FONTCOLOR", "TOGGLE_FONTCOLOR_LAYER"], ["hp_SE2M_FontColor$Lazy.js"]); + }, + + _setLastUsedFontColor: function (sFontColor) { + this.sLastUsedColor = sFontColor; + this.elLastUsed.style.backgroundColor = this.sLastUsedColor; + }, + + getLastUsedFontColor: function () { + return (!!this.sLastUsedColor) ? this.sLastUsedColor : '#000000'; + } }); //{ /** @@ -9941,45 +9945,45 @@ nhn.husky.SE2M_FontColor = jindo.$Class({ * @name hp_SE2M_BGColor.js */ nhn.husky.SE2M_BGColor = jindo.$Class({ - name : "SE2M_BGColor", - rxColorPattern : /^#?[0-9a-fA-F]{6}$|^rgb\(\d+, ?\d+, ?\d+\)$/i, - - $init : function(elAppContainer){ - this._assignHTMLElements(elAppContainer); - }, - - _assignHTMLElements : function(elAppContainer){ - //@ec[ - this.elLastUsed = jindo.$$.getSingle("SPAN.husky_se2m_BGColor_lastUsed", elAppContainer); - - this.elDropdownLayer = jindo.$$.getSingle("DIV.husky_se2m_BGColor_layer", elAppContainer); - this.elBGColorList = jindo.$$.getSingle("UL.husky_se2m_bgcolor_list", elAppContainer); - this.elPaletteHolder = jindo.$$.getSingle("DIV.husky_se2m_BGColor_paletteHolder", this.elDropdownLayer); - //@ec] + name: "SE2M_BGColor", + rxColorPattern: /^#?[0-9a-fA-F]{6}$|^rgb\(\d+, ?\d+, ?\d+\)$/i, - this._setLastUsedBGColor("#777777"); - }, - - $BEFORE_MSG_APP_READY : function() { - this.oApp.exec("ADD_APP_PROPERTY", ["getLastUsedBackgroundColor", jindo.$Fn(this.getLastUsedBGColor, this).bind()]); - }, - - $ON_MSG_APP_READY : function(){ - this.oApp.exec("REGISTER_UI_EVENT", ["BGColorA", "click", "APPLY_LAST_USED_BGCOLOR"]); - this.oApp.exec("REGISTER_UI_EVENT", ["BGColorB", "click", "TOGGLE_BGCOLOR_LAYER"]); + $init: function (elAppContainer) { + this._assignHTMLElements(elAppContainer); + }, - this.oApp.registerBrowserEvent(this.elBGColorList, "click", "EVENT_APPLY_BGCOLOR", []); - this.oApp.registerLazyMessage(["APPLY_LAST_USED_BGCOLOR", "TOGGLE_BGCOLOR_LAYER"], ["hp_SE2M_BGColor$Lazy.js"]); - }, + _assignHTMLElements: function (elAppContainer) { + //@ec[ + this.elLastUsed = jindo.$$.getSingle("SPAN.husky_se2m_BGColor_lastUsed", elAppContainer); - _setLastUsedBGColor : function(sBGColor){ - this.sLastUsedColor = sBGColor; - this.elLastUsed.style.backgroundColor = this.sLastUsedColor; - }, - - getLastUsedBGColor : function(){ - return (!!this.sLastUsedColor) ? this.sLastUsedColor : '#777777'; - } + this.elDropdownLayer = jindo.$$.getSingle("DIV.husky_se2m_BGColor_layer", elAppContainer); + this.elBGColorList = jindo.$$.getSingle("UL.husky_se2m_bgcolor_list", elAppContainer); + this.elPaletteHolder = jindo.$$.getSingle("DIV.husky_se2m_BGColor_paletteHolder", this.elDropdownLayer); + //@ec] + + this._setLastUsedBGColor("#777777"); + }, + + $BEFORE_MSG_APP_READY: function () { + this.oApp.exec("ADD_APP_PROPERTY", ["getLastUsedBackgroundColor", jindo.$Fn(this.getLastUsedBGColor, this).bind()]); + }, + + $ON_MSG_APP_READY: function () { + this.oApp.exec("REGISTER_UI_EVENT", ["BGColorA", "click", "APPLY_LAST_USED_BGCOLOR"]); + this.oApp.exec("REGISTER_UI_EVENT", ["BGColorB", "click", "TOGGLE_BGCOLOR_LAYER"]); + + this.oApp.registerBrowserEvent(this.elBGColorList, "click", "EVENT_APPLY_BGCOLOR", []); + this.oApp.registerLazyMessage(["APPLY_LAST_USED_BGCOLOR", "TOGGLE_BGCOLOR_LAYER"], ["hp_SE2M_BGColor$Lazy.js"]); + }, + + _setLastUsedBGColor: function (sBGColor) { + this.sLastUsedColor = sBGColor; + this.elLastUsed.style.backgroundColor = this.sLastUsedColor; + }, + + getLastUsedBGColor: function () { + return (!!this.sLastUsedColor) ? this.sLastUsedColor : '#777777'; + } }); //} /** @@ -9987,200 +9991,200 @@ nhn.husky.SE2M_BGColor = jindo.$Class({ * @name hp_SE_Hyperlink.js */ nhn.husky.SE2M_Hyperlink = jindo.$Class({ - name : "SE2M_Hyperlink", - sATagMarker : "HTTP://HUSKY_TMP.MARKER/", - - _assignHTMLElements : function(elAppContainer){ - this.oHyperlinkButton = jindo.$$.getSingle("li.husky_seditor_ui_hyperlink", elAppContainer); - this.oHyperlinkLayer = jindo.$$.getSingle("div.se2_layer", this.oHyperlinkButton); - this.oLinkInput = jindo.$$.getSingle("INPUT[type=text]", this.oHyperlinkLayer); - - this.oBtnConfirm = jindo.$$.getSingle("button.se2_apply", this.oHyperlinkLayer); - this.oBtnCancel = jindo.$$.getSingle("button.se2_cancel", this.oHyperlinkLayer); - - //this.oCbNewWin = jindo.$$.getSingle("INPUT[type=checkbox]", this.oHyperlinkLayer) || null; - }, + name: "SE2M_Hyperlink", + sATagMarker: "HTTP://HUSKY_TMP.MARKER/", - _generateAutoLink : function(sAll, sBreaker, sURL, sWWWURL, sHTTPURL) { - sBreaker = sBreaker || ""; + _assignHTMLElements: function (elAppContainer) { + this.oHyperlinkButton = jindo.$$.getSingle("li.husky_seditor_ui_hyperlink", elAppContainer); + this.oHyperlinkLayer = jindo.$$.getSingle("div.se2_layer", this.oHyperlinkButton); + this.oLinkInput = jindo.$$.getSingle("INPUT[type=text]", this.oHyperlinkLayer); - var sResult; - if (sWWWURL){ - sResult = ''+sURL+''; - } else { - sResult = ''+sURL+''; - } - - return sBreaker+sResult; - }, + this.oBtnConfirm = jindo.$$.getSingle("button.se2_apply", this.oHyperlinkLayer); + this.oBtnCancel = jindo.$$.getSingle("button.se2_cancel", this.oHyperlinkLayer); - /** - * [SMARTEDITORSUS-1405] 자동링크 비활성화 옵션을 체크해서 처리한다. - * $ON_REGISTER_CONVERTERS 메시지가 SE_EditingAreaManager.$ON_MSG_APP_READY 에서 수행되므로 먼저 처리한다. - */ - $BEFORE_MSG_APP_READY : function(){ - var htOptions = nhn.husky.SE2M_Configuration.SE2M_Hyperlink; - if(htOptions && htOptions.bAutolink === false){ - // 자동링크 컨버터 비활성화 - this.$ON_REGISTER_CONVERTERS = null; - // UI enable/disable 처리 제외 - this.$ON_DISABLE_MESSAGE = null; - this.$ON_ENABLE_MESSAGE = null; - // 브라우저의 자동링크기능 비활성화 - try{ this.oApp.getWYSIWYGDocument().execCommand("AutoUrlDetect", false, false); } catch(e){} - } - }, + //this.oCbNewWin = jindo.$$.getSingle("INPUT[type=checkbox]", this.oHyperlinkLayer) || null; + }, - $ON_MSG_APP_READY : function(){ - this.bLayerShown = false; + _generateAutoLink: function (sAll, sBreaker, sURL, sWWWURL, sHTTPURL) { + sBreaker = sBreaker || ""; - // [SMARTEDITORSUS-2260] 메일 > Mac에서 ctrl 조합 단축키 모두 meta 조합으로 변경 - if (jindo.$Agent().os().mac) { - this.oApp.exec("REGISTER_HOTKEY", ["meta+k", "TOGGLE_HYPERLINK_LAYER", []]); - } else { - this.oApp.exec("REGISTER_HOTKEY", ["ctrl+k", "TOGGLE_HYPERLINK_LAYER", []]); - } + var sResult; + if (sWWWURL) { + sResult = '' + sURL + ''; + } else { + sResult = '' + sURL + ''; + } - this.oApp.exec("REGISTER_UI_EVENT", ["hyperlink", "click", "TOGGLE_HYPERLINK_LAYER"]); - this.oApp.registerLazyMessage(["TOGGLE_HYPERLINK_LAYER", "APPLY_HYPERLINK"], ["hp_SE2M_Hyperlink$Lazy.js"]); - }, - - $ON_REGISTER_CONVERTERS : function(){ - this.oApp.exec("ADD_CONVERTER_DOM", ["IR_TO_DB", jindo.$Fn(this.irToDb, this).bind()]); - }, - - $LOCAL_BEFORE_FIRST : function(sMsg){ - if(!!sMsg.match(/(REGISTER_CONVERTERS)/)){ - this.oApp.acceptLocalBeforeFirstAgain(this, true); - return true; - } + return sBreaker + sResult; + }, - this._assignHTMLElements(this.oApp.htOptions.elAppContainer); - this.sRXATagMarker = this.sATagMarker.replace(/\//g, "\\/").replace(/\./g, "\\."); - this.oApp.registerBrowserEvent(this.oBtnConfirm, "click", "APPLY_HYPERLINK"); - this.oApp.registerBrowserEvent(this.oBtnCancel, "click", "HIDE_ACTIVE_LAYER"); - this.oApp.registerBrowserEvent(this.oLinkInput, "keydown", "EVENT_HYPERLINK_KEYDOWN"); - }, - - $ON_EVENT_HYPERLINK_KEYDOWN : function(oEvent){ - if (oEvent.key().enter){ - this.oApp.exec("APPLY_HYPERLINK"); - oEvent.stop(); - } - }, + /** + * [SMARTEDITORSUS-1405] 자동링크 비활성화 옵션을 체크해서 처리한다. + * $ON_REGISTER_CONVERTERS 메시지가 SE_EditingAreaManager.$ON_MSG_APP_READY 에서 수행되므로 먼저 처리한다. + */ + $BEFORE_MSG_APP_READY: function () { + var htOptions = nhn.husky.SE2M_Configuration.SE2M_Hyperlink; + if (htOptions && htOptions.bAutolink === false) { + // 자동링크 컨버터 비활성화 + this.$ON_REGISTER_CONVERTERS = null; + // UI enable/disable 처리 제외 + this.$ON_DISABLE_MESSAGE = null; + this.$ON_ENABLE_MESSAGE = null; + // 브라우저의 자동링크기능 비활성화 + try { this.oApp.getWYSIWYGDocument().execCommand("AutoUrlDetect", false, false); } catch (e) { } + } + }, - /** - * [MUG-1265] 버튼이 사용불가 상태이면 자동변환기능을 막는다. - * @see http://stackoverflow.com/questions/7556007/avoid-transformation-text-to-link-ie-contenteditable-mode - * IE9 이전 버전은 AutoURlDetect을 사용할 수 없어 오류 발생되기 때문에, try catch로 블럭 처리(http://msdn.microsoft.com/en-us/library/aa769893%28VS.85%29.aspx) - */ - $ON_DISABLE_MESSAGE : function(sCmd) { - if(sCmd !== "TOGGLE_HYPERLINK_LAYER"){ - return; - } - try{ this.oApp.getWYSIWYGDocument().execCommand("AutoUrlDetect", false, false); } catch(e){} - this._bDisabled = true; - }, + $ON_MSG_APP_READY: function () { + this.bLayerShown = false; - /** - * [MUG-1265] 버튼이 사용가능 상태이면 자동변환기능을 복원해준다. - */ - $ON_ENABLE_MESSAGE : function(sCmd) { - if(sCmd !== "TOGGLE_HYPERLINK_LAYER"){ - return; - } - try{ this.oApp.getWYSIWYGDocument().execCommand("AutoUrlDetect", false, true); } catch(e){} - this._bDisabled = false; - }, + // [SMARTEDITORSUS-2260] 메일 > Mac에서 ctrl 조합 단축키 모두 meta 조합으로 변경 + if (jindo.$Agent().os().mac) { + this.oApp.exec("REGISTER_HOTKEY", ["meta+k", "TOGGLE_HYPERLINK_LAYER", []]); + } else { + this.oApp.exec("REGISTER_HOTKEY", ["ctrl+k", "TOGGLE_HYPERLINK_LAYER", []]); + } - irToDb : function(oTmpNode){ - if(this._bDisabled){ // [MUG-1265] 버튼이 사용불가 상태이면 자동변환하지 않는다. - return; - } - //저장 시점에 자동 링크를 위한 함수. - //[SMARTEDITORSUS-1207][IE][메일] object 삽입 후 글을 저장하면 IE 브라우저가 죽어버리는 현상 - //원인 : 확인 불가. IE 저작권 관련 이슈로 추정 - //해결 : contents를 가지고 있는 div 태그를 이 함수 내부에서 복사하여 수정 후 call by reference로 넘어온 변수의 innerHTML을 변경 - var oCopyNode = oTmpNode.cloneNode(true); - try{ - oCopyNode.innerHTML; - }catch(e) { - oCopyNode = jindo.$(oTmpNode.outerHTML); - } - - var oTmpRange = this.oApp.getEmptySelection(); - var elFirstNode = oTmpRange._getFirstRealChild(oCopyNode); - var elLastNode = oTmpRange._getLastRealChild(oCopyNode); - var waAllNodes = jindo.$A(oTmpRange._getNodesBetween(elFirstNode, elLastNode)); - var aAllTextNodes = waAllNodes.filter(function(elNode){return (elNode && elNode.nodeType === 3);}).$value(); - var a = aAllTextNodes; - - /* - // 텍스트 검색이 용이 하도록 끊어진 텍스트 노드가 있으면 합쳐줌. (화면상으로 ABC라고 보이나 상황에 따라 실제 2개의 텍스트 A, BC로 이루어져 있을 수 있음. 이를 ABC 하나의 노드로 만들어 줌.) - // 문제 발생 가능성에 비해서 퍼포먼스나 사이드 이펙트 가능성 높아 일단 주석 - var aCleanTextNodes = []; - for(var i=0, nLen=aAllTextNodes.length; i display name 매핑 (웹폰트는 두개가 다름) - this.htFamilyName2DisplayName = {}; - this.htAllFonts = {}; - - this.aBaseFontList = []; - this.aDefaultFontList = []; - this.aTempSavedFontList = []; - - this.htOptions = this.oApp.htOptions.SE2M_FontName; - - if(this.htOptions){ - aDefaultFontList = this.htOptions.aDefaultFontList || []; - aFontList = this.htOptions.aFontList; - htMainFont = this.htOptions.htMainFont; - aFontInUse = this.htOptions.aFontInUse; - - //add Font - if(this.htBrowser.ie && aFontList){ - for(i=0; i 0){ - for(i = 0, nLen = this.aAdditionalFontList.length; i < nLen; i++){ - this.addFont(this.aAdditionalFontList[i][0], this.aAdditionalFontList[i][1], 0, "", "", 1); - } - } - }, - - $ON_MSG_APP_READY : function(){ - this.bDoNotRecordUndo = false; + addAllFonts: function () { + var aDefaultFontList, aFontList, htMainFont, aFontInUse, i; - this.oApp.exec("ADD_APP_PROPERTY", ["addFont", jindo.$Fn(this.addFont, this).bind()]); - this.oApp.exec("ADD_APP_PROPERTY", ["addFontInUse", jindo.$Fn(this.addFontInUse, this).bind()]); - // 블로그등 팩토리 폰트 포함 용 - this.oApp.exec("ADD_APP_PROPERTY", ["setMainFont", jindo.$Fn(this.setMainFont, this).bind()]); - // 메일등 단순 폰트 지정 용 - this.oApp.exec("ADD_APP_PROPERTY", ["setDefaultFont", jindo.$Fn(this.setDefaultFont, this).bind()]); - - this.oApp.exec("REGISTER_UI_EVENT", ["fontName", "click", "SE2M_TOGGLE_FONTNAME_LAYER"]); + // family name -> display name 매핑 (웹폰트는 두개가 다름) + this.htFamilyName2DisplayName = {}; + this.htAllFonts = {}; - this._initFontName(); // [SMARTEDITORSUS-2111] 메일쪽 요청으로 글꼴목록 초기화시점 변경 - }, - - $AFTER_MSG_APP_READY : function(){ - this._attachIEEvent(); - }, - - _assignHTMLElements : function(elAppContainer){ - //@ec[ - this.oDropdownLayer = jindo.$$.getSingle("DIV.husky_se_fontName_layer", elAppContainer); + this.aBaseFontList = []; + this.aDefaultFontList = []; + this.aTempSavedFontList = []; - this.elFontNameLabel = jindo.$$.getSingle("SPAN.husky_se2m_current_fontName", elAppContainer); + this.htOptions = this.oApp.htOptions.SE2M_FontName; - this.elFontNameList = jindo.$$.getSingle("UL", this.oDropdownLayer); - this.elInnerLayer = this.elFontNameList.parentNode; - this.aelFontInMarkup = jindo.$$("LI", this.oDropdownLayer); // 마크업에 있는 LI - this.elFontItemTemplate = this.aelFontInMarkup.shift(); // 맨앞에 있는 LI 는 템플릿 - this.aLIFontNames = jindo.$A(jindo.$$("LI", this.oDropdownLayer)).filter(function(v,i,a){return (v.firstChild !== null);})._array; - //@ec] - - this.sDefaultText = this.elFontNameLabel.innerHTML; - }, - - //$LOCAL_BEFORE_FIRST : function(){ - _initFontName : function(){ - this._addFontInMarkup(); - - this.addAllFonts(); + if (this.htOptions) { + aDefaultFontList = this.htOptions.aDefaultFontList || []; + aFontList = this.htOptions.aFontList; + htMainFont = this.htOptions.htMainFont; + aFontInUse = this.htOptions.aFontInUse; - // [SMARTEDITORSUS-1853] 폰트가 초기화되면 현재 스타일정보를 가져와서 툴바에 반영해준다. - var oStyle; - if(this.oApp.getCurrentStyle && (oStyle = this.oApp.getCurrentStyle())){ - this.$ON_MSG_STYLE_CHANGED("fontFamily", oStyle.fontFamily); - } + //add Font + if (this.htBrowser.ie && aFontList) { + for (i = 0; i < aFontList.length; i++) { + this.addFont(aFontList[i].id, aFontList[i].name, aFontList[i].size, aFontList[i].url, aFontList[i].cssUrl); + } + } - this.oApp.registerBrowserEvent(this.oDropdownLayer, "mouseover", "EVENT_FONTNAME_LAYER_MOUSEOVER", []); - this.oApp.registerBrowserEvent(this.oDropdownLayer, "click", "EVENT_FONTNAME_LAYER_CLICKED", []); - }, + for (i = 0; i < aDefaultFontList.length; i++) { + this.addFont(aDefaultFontList[i][0], aDefaultFontList[i][1], 0, "", "", 1); + } - /** - * 해당 글꼴이 존재하면 LI 요소를 보여주고 true 를 반환한다. - * @param {Element} el 글꼴리스트의 LI 요소 - * @param {String} sFontName 확인할 글꼴이름 - * @return {Boolean} LI 요소가 있고 글꼴이 OS에 존재하면 true 반환 - */ - _checkFontLI : function(el, sFontName){ - if(!el){ - return false; - } + //set Main Font + //if(mainFontSelected=='true') { + if (htMainFont && htMainFont.id) { + //this.setMainFont(mainFontId, mainFontName, mainFontSize, mainFontUrl, mainFontCssUrl); + this.setMainFont(htMainFont.id, htMainFont.name, htMainFont.size, htMainFont.url, htMainFont.cssUrl); + } + // add font in use + if (this.htBrowser.ie && aFontInUse) { + for (i = 0; i < aFontInUse.length; i++) { + this.addFontInUse(aFontInUse[i].id, aFontInUse[i].name, aFontInUse[i].size, aFontInUse[i].url, aFontInUse[i].cssUrl); + } + } + } - var bInstalled = IsInstalledFont(sFontName); - el.style.display = bInstalled ? "block" : "none"; - return bInstalled; - }, + // [SMARTEDITORSUS-245] 서비스 적용 시 글꼴정보를 넘기지 않으면 기본 글꼴 목록이 보이지 않는 오류 + if (!this.htOptions || !this.htOptions.aDefaultFontList || this.htOptions.aDefaultFontList.length === 0) { + this.addFont("돋움,Dotum", "돋움", 0, "", "", 1, null, true); + this.addFont("돋움체,DotumChe,AppleGothic", "돋움체", 0, "", "", 1, null, true); + this.addFont("굴림,Gulim", "굴림", 0, "", "", 1, null, true); + this.addFont("굴림체,GulimChe", "굴림체", 0, "", "", 1, null, true); + this.addFont("바탕,Batang,AppleMyungjo", "바탕", 0, "", "", 1, null, true); + this.addFont("바탕체,BatangChe", "바탕체", 0, "", "", 1, null, true); + this.addFont("궁서,Gungsuh,GungSeo", "궁서", 0, "", "", 1, null, true); + this.addFont('Arial', 'Arial', 0, "", "", 1, "abcd", true); + this.addFont('Tahoma', 'Tahoma', 0, "", "", 1, "abcd", true); + this.addFont('Times New Roman', 'Times New Roman', 0, "", "", 1, "abcd", true); + this.addFont('Verdana', 'Verdana', 0, "", "", 1, "abcd", true); + this.addFont('Courier New', 'Courier New', 0, "", "", 1, "abcd", true); + } - /** - * 마크업에 있는 글꼴 목록을 추가해준다. - */ - _addFontInMarkup : function(){ - for(var i = 0, elLi, sFontFamily, elSeparator, bUseSeparator; (elLi = this.aelFontInMarkup[i]); i++){ - if(elLi.firstChild){ - sFontFamily = this._getFontFamilyFromLI(elLi).replace(this._rxQuote, "").replace(this._rxComma, ","); - // 폰트패밀리값으로 OS에 폰트가 설치되어있는지 확인하여 노출하고 노출되면 구분선노출플래그를 true 로 세팅한다. - bUseSeparator |= this._checkFontLI(elLi, sFontFamily); - }else if(elLi.className.indexOf(this.FONT_SEPARATOR) > -1){ - if(elSeparator){ // 이전에 구분선이 있었으면 구분선 노출여부 판단 - elSeparator.style.display = bUseSeparator ? "block" : "none"; - } + // [SMARTEDITORSUS-1436] 글꼴 리스트에 글꼴 종류 추가하기 기능 + if (!!this.aAdditionalFontList && this.aAdditionalFontList.length > 0) { + for (i = 0, nLen = this.aAdditionalFontList.length; i < nLen; i++) { + this.addFont(this.aAdditionalFontList[i][0], this.aAdditionalFontList[i][1], 0, "", "", 1); + } + } + }, - elSeparator = elLi; // 새로운 구분선 저장 - bUseSeparator = false; // 구분선노출플래그 리셋 - }else{ - elLi.style.display = "none"; - } - } - // 마지막 구분선 노출여부를 확인한다. - if(elSeparator){ - elSeparator.style.display = bUseSeparator ? "block" : "none"; - } - }, + $ON_MSG_APP_READY: function () { + this.bDoNotRecordUndo = false; - _attachIEEvent : function(){ - if(!this.htBrowser.ie){ - return; - } - - if(this.htBrowser.nativeVersion < 9){ // [SMARTEDITORSUS-187] [< IE9] 최초 paste 시점에 웹폰트 파일을 로드 - this._wfOnPasteWYSIWYGBody = jindo.$Fn(this._onPasteWYSIWYGBody, this); - this._wfOnPasteWYSIWYGBody.attach(this.oApp.getWYSIWYGDocument().body, "paste"); - - return; - } - - if(document.documentMode < 9){ // [SMARTEDITORSUS-169] [>= IE9] 최초 포커스 시점에 웹폰트 로드 - this._wfOnFocusWYSIWYGBody = jindo.$Fn(this._onFocusWYSIWYGBody, this); - this._wfOnFocusWYSIWYGBody.attach(this.oApp.getWYSIWYGDocument().body, "focus"); - - return; - } + this.oApp.exec("ADD_APP_PROPERTY", ["addFont", jindo.$Fn(this.addFont, this).bind()]); + this.oApp.exec("ADD_APP_PROPERTY", ["addFontInUse", jindo.$Fn(this.addFontInUse, this).bind()]); + // 블로그등 팩토리 폰트 포함 용 + this.oApp.exec("ADD_APP_PROPERTY", ["setMainFont", jindo.$Fn(this.setMainFont, this).bind()]); + // 메일등 단순 폰트 지정 용 + this.oApp.exec("ADD_APP_PROPERTY", ["setDefaultFont", jindo.$Fn(this.setDefaultFont, this).bind()]); - // documentMode === 9 - // http://blogs.msdn.com/b/ie/archive/2010/08/17/ie9-opacity-and-alpha.aspx // opacity:0.0; - this.welEditingAreaCover = jindo.$Element('
    '); + this.oApp.exec("REGISTER_UI_EVENT", ["fontName", "click", "SE2M_TOGGLE_FONTNAME_LAYER"]); - this.oApp.welEditingAreaContainer.prepend(this.welEditingAreaCover); - jindo.$Fn(this._onMouseupCover, this).attach(this.welEditingAreaCover.$value(), "mouseup"); - }, - - _onFocusWYSIWYGBody : function(e){ - this._wfOnFocusWYSIWYGBody.detach(this.oApp.getWYSIWYGDocument().body, "focus"); - this._loadAllBaseFont(); - }, - - _onPasteWYSIWYGBody : function(e){ - this._wfOnPasteWYSIWYGBody.detach(this.oApp.getWYSIWYGDocument().body, "paste"); - this._loadAllBaseFont(); - }, - - _onMouseupCover : function(e){ - e.stop(); + this._initFontName(); // [SMARTEDITORSUS-2111] 메일쪽 요청으로 글꼴목록 초기화시점 변경 + }, - // [SMARTEDITORSUS-1632] 문서 모드가 9 이상일 때, 경우에 따라 this.welEditingAreaContainer가 없을 때 스크립트 오류 발생 - if(this.welEditingAreaCover){ - this.welEditingAreaCover.leave(); - } - //this.welEditingAreaCover.leave(); - // --[SMARTEDITORSUS-1632] - - var oMouse = e.mouse(), - elBody = this.oApp.getWYSIWYGDocument().body, - welBody = jindo.$Element(elBody), - oSelection = this.oApp.getEmptySelection(); - - // [SMARTEDITORSUS-363] 강제로 Selection 을 주도록 처리함 - oSelection.selectNode(elBody); - oSelection.collapseToStart(); - oSelection.select(); + $AFTER_MSG_APP_READY: function () { + this._attachIEEvent(); + }, - welBody.fireEvent("mousedown", {left : oMouse.left, middle : oMouse.middle, right : oMouse.right}); - welBody.fireEvent("mouseup", {left : oMouse.left, middle : oMouse.middle, right : oMouse.right}); - - /** - * [SMARTEDITORSUS-1691] - * [IE 10-] 에디터가 초기화되고 나서

    로만 innerHTML을 설정하는데, - * 이 경우 실제 커서는

    내부에 있는 것이 아니라 그 앞에 위치한다. - * 따라서 임시 북마크를 사용해서

    내부로 커서를 이동시켜 준다. - * - * [SMARTEDITORSUS-1781] - * [IE 11] 문서 모드가 Edge인 경우에 한하여 - *


    로 innerHTML을 설정하는데, - * 실제 커서는


    앞에 위치한다. - * 이 경우에는 임시 북마크를 삽입할 필요 없이
    앞에 커서를 위치시켜 준다. - * */ - if(this.oApp.oNavigator.ie && document.documentMode < 11 && this.oApp.getEditingMode() === "WYSIWYG"){ - if(this.oApp.getWYSIWYGDocument().body.innerHTML == "

    "){ - this.oApp.getWYSIWYGDocument().body.innerHTML = '

    '; - var oSelection = this.oApp.getSelection(); - oSelection.moveToStringBookmark("INIT"); - oSelection.select(); - oSelection.removeStringBookmark("INIT"); - } - }else if(this.oApp.oNavigator.ie && this.oApp.oNavigator.nativeVersion == 11 && document.documentMode == 11 && this.oApp.getEditingMode() === "WYSIWYG"){ - if(this.oApp.getWYSIWYGDocument().body.innerHTML == "


    "){ - var elCursorHolder_br = jindo.$$.getSingle("br", elBody); - oSelection.setStartBefore(elCursorHolder_br); - oSelection.setEndBefore(elCursorHolder_br); - oSelection.select(); - } - } - // --[SMARTEDITORSUS-1781][SMARTEDITORSUS-1691] - }, + _assignHTMLElements: function (elAppContainer) { + //@ec[ + this.oDropdownLayer = jindo.$$.getSingle("DIV.husky_se_fontName_layer", elAppContainer); - $ON_EVENT_TOOLBAR_MOUSEDOWN : function(){ - if(this.htBrowser.nativeVersion < 9 || document.documentMode < 9){ - return; - } - - // [SMARTEDITORSUS-1632] 문서 모드가 9 이상일 때, 경우에 따라 this.welEditingAreaContainer가 없을 때 스크립트 오류 발생 - if(this.welEditingAreaCover){ - this.welEditingAreaCover.leave(); - } - //this.welEditingAreaCover.leave(); - // --[SMARTEDITORSUS-1632] - }, - - _loadAllBaseFont : function(){ - var i, nFontLen; - - if(!this.htBrowser.ie){ - return; - } - - if(this.htBrowser.nativeVersion < 9){ - for(i=0, nFontLen=this.aBaseFontList.length; i 20){ - this.oDropdownLayer.style.overflowX = 'hidden'; - this.oDropdownLayer.style.overflowY = 'auto'; - this.oDropdownLayer.style.height = '400px'; - this.oDropdownLayer.style.width = '204px'; // [SMARTEDITORSUS-155] 스크롤을 포함하여 206px 이 되도록 처리 - } - }, + this.elFontNameList = jindo.$$.getSingle("UL", this.oDropdownLayer); + this.elInnerLayer = this.elFontNameList.parentNode; + this.aelFontInMarkup = jindo.$$("LI", this.oDropdownLayer); // 마크업에 있는 LI + this.elFontItemTemplate = this.aelFontInMarkup.shift(); // 맨앞에 있는 LI 는 템플릿 + this.aLIFontNames = jindo.$A(jindo.$$("LI", this.oDropdownLayer)).filter(function (v, i, a) { return (v.firstChild !== null); })._array; + //@ec] - $ON_EVENT_FONTNAME_LAYER_MOUSEOVER : function(wev){ - var elTmp = this._findLI(wev.element); - if(!elTmp){ - return; - } + this.sDefaultText = this.elFontNameLabel.innerHTML; + }, - this._clearLastHover(); - - elTmp.className = "hover"; - this.elLastHover = elTmp; - }, - - $ON_EVENT_FONTNAME_LAYER_CLICKED : function(wev){ - var elTmp = this._findLI(wev.element); - if(!elTmp){ - return; - } - - var sFontFamily = this._getFontFamilyFromLI(elTmp); - // [SMARTEDITORSUS-169] 웹폰트의 경우 fontFamily 에 ' 을 붙여주는 처리를 함 - var htFontInfo = this.htAllFonts[sFontFamily.replace(/\"/g, nhn.husky.SE2M_FontNameWithLayerUI.CUSTOM_FONT_MARKS)]; - var nDefaultFontSize; - if(htFontInfo){ - nDefaultFontSize = htFontInfo.defaultSize+"pt"; - }else{ - nDefaultFontSize = 0; - } - this.oApp.exec("SET_FONTFAMILY", [sFontFamily, nDefaultFontSize]); - }, - - _findLI : function(elTmp){ - while(elTmp.tagName != "LI"){ - if(!elTmp || elTmp === this.oDropdownLayer){ - return null; - } - elTmp = elTmp.parentNode; - } + //$LOCAL_BEFORE_FIRST : function(){ + _initFontName: function () { + this._addFontInMarkup(); - if(elTmp.className.indexOf(this.FONT_SEPARATOR) > -1){ - return null; - } - return elTmp; - }, - - _clearLastHover : function(){ - if(this.elLastHover){ - this.elLastHover.className = ""; - } - }, - - $ON_SE2M_TOGGLE_FONTNAME_LAYER : function(){ - this.oApp.exec("TOGGLE_TOOLBAR_ACTIVE_LAYER", [this.oDropdownLayer, null, "MSG_FONTNAME_LAYER_OPENED", [], "MSG_FONTNAME_LAYER_CLOSED", []]); - this.oApp.exec('MSG_NOTIFY_CLICKCR', ['font']); - }, - - $ON_MSG_FONTNAME_LAYER_OPENED : function(){ - this.oApp.exec("SELECT_UI", ["fontName"]); - }, - - $ON_MSG_FONTNAME_LAYER_CLOSED : function(){ - this._clearLastHover(); - this.oApp.exec("DESELECT_UI", ["fontName"]); - }, - - $ON_MSG_STYLE_CHANGED : function(sAttributeName, sAttributeValue){ - if(sAttributeName == "fontFamily"){ - sAttributeValue = sAttributeValue.replace(/["']/g, ""); - var elLi = this._getMatchingLI(sAttributeValue); - this._clearFontNameSelection(); - if(elLi){ - this.elFontNameLabel.innerHTML = this._getFontNameLabelFromLI(elLi); - jindo.$Element(elLi).addClass("active"); - }else{ - //var sDisplayName = this.htFamilyName2DisplayName[sAttributeValue] || sAttributeValue; - var sDisplayName = this.sDefaultText; - this.elFontNameLabel.innerHTML = sDisplayName; - } - } - }, + this.addAllFonts(); - $BEFORE_RECORD_UNDO_BEFORE_ACTION : function(){ - return !this.bDoNotRecordUndo; - }, - $BEFORE_RECORD_UNDO_AFTER_ACTION : function(){ - return !this.bDoNotRecordUndo; - }, - $BEFORE_RECORD_UNDO_ACTION : function(){ - return !this.bDoNotRecordUndo; - }, + // [SMARTEDITORSUS-1853] 폰트가 초기화되면 현재 스타일정보를 가져와서 툴바에 반영해준다. + var oStyle; + if (this.oApp.getCurrentStyle && (oStyle = this.oApp.getCurrentStyle())) { + this.$ON_MSG_STYLE_CHANGED("fontFamily", oStyle.fontFamily); + } - $ON_SET_FONTFAMILY : function(sFontFamily, sDefaultSize){ - if(!sFontFamily){return;} - - // [SMARTEDITORSUS-169] 웹폰트의 경우 fontFamily 에 ' 을 붙여주는 처리를 함 - var oFontInfo = this.htAllFonts[sFontFamily.replace(/\"/g, nhn.husky.SE2M_FontNameWithLayerUI.CUSTOM_FONT_MARKS)]; - if(!!oFontInfo){ - oFontInfo.loadCSS(this.oApp.getWYSIWYGDocument()); - } - - // fontFamily와 fontSize 두개의 액션을 하나로 묶어서 undo history 저장 - this.oApp.exec("RECORD_UNDO_BEFORE_ACTION", ["SET FONTFAMILY", {bMustBlockElement:true}]); - this.bDoNotRecordUndo = true; - - if(parseInt(sDefaultSize, 10) > 0){ - this.oApp.exec("SET_WYSIWYG_STYLE", [{"fontSize":sDefaultSize}]); - } - this.oApp.exec("SET_WYSIWYG_STYLE", [{"fontFamily":sFontFamily}]); - - this.bDoNotRecordUndo = false; - this.oApp.exec("RECORD_UNDO_AFTER_ACTION", ["SET FONTFAMILY", {bMustBlockElement:true}]); - - this.oApp.exec("HIDE_ACTIVE_LAYER", []); + this.oApp.registerBrowserEvent(this.oDropdownLayer, "mouseover", "EVENT_FONTNAME_LAYER_MOUSEOVER", []); + this.oApp.registerBrowserEvent(this.oDropdownLayer, "click", "EVENT_FONTNAME_LAYER_CLICKED", []); + }, - this.oApp.exec("CHECK_STYLE_CHANGE", []); - }, - - _getMatchingLI : function(sFontName){ - sFontName = sFontName.toLowerCase(); - var elLi, aFontFamily; - for(var i=0; i - return (elLi.getElementsByTagName("EM")[0]).style.fontFamily; - }, - - _getFontNameLabelFromLI : function(elLi){ - return elLi.firstChild.firstChild.firstChild.nodeValue; - }, - - _clearFontNameSelection : function(elLi){ - for(var i=0; i -1) { + if (elSeparator) { // 이전에 구분선이 있었으면 구분선 노출여부 판단 + elSeparator.style.display = bUseSeparator ? "block" : "none"; + } - // OS에 해당 폰트가 존재하는지 여부를 확인한다. - if(bCheck && !IsInstalledFont(fontId)){ - return null; - } + elSeparator = elLi; // 새로운 구분선 저장 + bUseSeparator = false; // 구분선노출플래그 리셋 + } else { + elLi.style.display = "none"; + } + } + // 마지막 구분선 노출여부를 확인한다. + if (elSeparator) { + elSeparator.style.display = bUseSeparator ? "block" : "none"; + } + }, - // [SMARTEDITORSUS-2235] 폰트패밀리가 대소문자 구분되어 삽입될 수 있도록 주석처리 - //fontId = fontId.toLowerCase(); - - var newFont = new fontProperty(fontId, fontName, defaultSize, fontURL, fontCSSURL); - - var sFontFamily; - var sDisplayName; - if(defaultSize>0){ - sFontFamily = fontId+"_"+defaultSize; - sDisplayName = fontName+"_"+defaultSize; - }else{ - sFontFamily = fontId; - sDisplayName = fontName; - } - - if(!fontType){ - sFontFamily = nhn.husky.SE2M_FontNameWithLayerUI.CUSTOM_FONT_MARKS + sFontFamily + nhn.husky.SE2M_FontNameWithLayerUI.CUSTOM_FONT_MARKS; - } - - if(this.htAllFonts[sFontFamily]){ - return this.htAllFonts[sFontFamily]; - } - this.htAllFonts[sFontFamily] = newFont; -/* - // do not add again, if the font is already in the list - for(var i=0; i웹폰트B 선택>웹폰트 A를 다시 선택하면 웹폰트 A가 적용되지 않는 문제가 발생 - // - // [원인] - // - IE9의 웹폰트 로드/언로드 시점 - // 웹폰트 로드 시점: StyleSheet 의 @font-face 구문이 해석된 이후, DOM Tree 상에서 해당 웹폰트가 최초로 사용된 시점 - // 웹폰트 언로드 시점: StyleSheet 의 @font-face 구문이 해석된 이후, DOM Tree 상에서 해당 웬폰트가 더이상 사용되지 않는 시점 - // - 메뉴 리스트에 적용되는 스타일은 @font-face 이전에 처리되는 것이어서 언로드에 영향을 미치지 않음 - // - // 스마트에디터의 경우, 웹폰트를 선택할 때마다 SPAN 이 새로 추가되는 것이 아닌 선택된 SPAN 의 fontFamily 를 변경하여 처리하므로 - // fontFamily 변경 후 DOM Tree 상에서 더이상 사용되지 않는 것으로 브라우저 판단하여 언로드 해버림. - // [해결] - // 언로드가 발생하지 않도록 메뉴 리스트에 스타일을 적용하는 것을 @font-face 이후로 하도록 처리하여 DOM Tree 상에 항상 적용될 수 있도록 함 - // - // [SMARTEDITORSUS-969] [IE10] 웹폰트를 사용하여 글을 등록하고, 수정모드로 들어갔을 때 웹폰트가 적용되지 않는 문제 - // - IE10에서도 웹폰트 언로드가 발생하지 않도록 조건을 수정함 - // -> 기존 : nativeVersion === 9 && documentMode === 9 - // -> 수정 : nativeVersion >= 9 && documentMode >= 9 - if(this.htBrowser.ie && this.htBrowser.nativeVersion >= 9 && document.documentMode >= 9) { - newFont.loadCSSToMenu(); - } - - this.htFamilyName2DisplayName[sFontFamily] = fontName; + if (this.htBrowser.nativeVersion < 9) { // [SMARTEDITORSUS-187] [< IE9] 최초 paste 시점에 웹폰트 파일을 로드 + this._wfOnPasteWYSIWYGBody = jindo.$Fn(this._onPasteWYSIWYGBody, this); + this._wfOnPasteWYSIWYGBody.attach(this.oApp.getWYSIWYGDocument().body, "paste"); - sSampleText = sSampleText || this.oApp.$MSG('SE2M_FontNameWithLayerUI.sSampleText'); - this._addFontToMenu(sDisplayName, sFontFamily, sSampleText); - - if(!fontType){ - this.aBaseFontList[this.aBaseFontList.length] = newFont; - }else{ - if(fontType == 1){ - this.aDefaultFontList[this.aDefaultFontList.length] = newFont; - }else{ - this.aTempSavedFontList[this.aTempSavedFontList.length] = newFont; - } - } + return; + } - return newFont; - }, - // Add the font AND load it right away - addFontInUse : function (fontId, fontName, defaultSize, fontURL, fontCSSURL, fontType) { - var newFont = this.addFont(fontId, fontName, defaultSize, fontURL, fontCSSURL, fontType); - if(!newFont){ - return null; - } + if (document.documentMode < 9) { // [SMARTEDITORSUS-169] [>= IE9] 최초 포커스 시점에 웹폰트 로드 + this._wfOnFocusWYSIWYGBody = jindo.$Fn(this._onFocusWYSIWYGBody, this); + this._wfOnFocusWYSIWYGBody.attach(this.oApp.getWYSIWYGDocument().body, "focus"); - newFont.loadCSS(this.oApp.getWYSIWYGDocument()); - - return newFont; - }, - // Add the font AND load it right away AND THEN set it as the default font - setMainFont : function (fontId, fontName, defaultSize, fontURL, fontCSSURL, fontType) { - var newFont = this.addFontInUse(fontId, fontName, defaultSize, fontURL, fontCSSURL, fontType); - if(!newFont){ - return null; - } - - this.setDefaultFont(newFont.fontFamily, defaultSize); - - return newFont; - }, - - setDefaultFont : function(sFontFamily, nFontSize){ - var elBody = this.oApp.getWYSIWYGDocument().body; - elBody.style.fontFamily = sFontFamily; - if(nFontSize>0){elBody.style.fontSize = nFontSize + 'pt';} - } + return; + } + + // documentMode === 9 + // http://blogs.msdn.com/b/ie/archive/2010/08/17/ie9-opacity-and-alpha.aspx // opacity:0.0; + this.welEditingAreaCover = jindo.$Element('
    '); + + this.oApp.welEditingAreaContainer.prepend(this.welEditingAreaCover); + jindo.$Fn(this._onMouseupCover, this).attach(this.welEditingAreaCover.$value(), "mouseup"); + }, + + _onFocusWYSIWYGBody: function (e) { + this._wfOnFocusWYSIWYGBody.detach(this.oApp.getWYSIWYGDocument().body, "focus"); + this._loadAllBaseFont(); + }, + + _onPasteWYSIWYGBody: function (e) { + this._wfOnPasteWYSIWYGBody.detach(this.oApp.getWYSIWYGDocument().body, "paste"); + this._loadAllBaseFont(); + }, + + _onMouseupCover: function (e) { + e.stop(); + + // [SMARTEDITORSUS-1632] 문서 모드가 9 이상일 때, 경우에 따라 this.welEditingAreaContainer가 없을 때 스크립트 오류 발생 + if (this.welEditingAreaCover) { + this.welEditingAreaCover.leave(); + } + //this.welEditingAreaCover.leave(); + // --[SMARTEDITORSUS-1632] + + var oMouse = e.mouse(), + elBody = this.oApp.getWYSIWYGDocument().body, + welBody = jindo.$Element(elBody), + oSelection = this.oApp.getEmptySelection(); + + // [SMARTEDITORSUS-363] 강제로 Selection 을 주도록 처리함 + oSelection.selectNode(elBody); + oSelection.collapseToStart(); + oSelection.select(); + + welBody.fireEvent("mousedown", { left: oMouse.left, middle: oMouse.middle, right: oMouse.right }); + welBody.fireEvent("mouseup", { left: oMouse.left, middle: oMouse.middle, right: oMouse.right }); + + /** + * [SMARTEDITORSUS-1691] + * [IE 10-] 에디터가 초기화되고 나서

    로만 innerHTML을 설정하는데, + * 이 경우 실제 커서는

    내부에 있는 것이 아니라 그 앞에 위치한다. + * 따라서 임시 북마크를 사용해서

    내부로 커서를 이동시켜 준다. + * + * [SMARTEDITORSUS-1781] + * [IE 11] 문서 모드가 Edge인 경우에 한하여 + *


    로 innerHTML을 설정하는데, + * 실제 커서는


    앞에 위치한다. + * 이 경우에는 임시 북마크를 삽입할 필요 없이
    앞에 커서를 위치시켜 준다. + * */ + if (this.oApp.oNavigator.ie && document.documentMode < 11 && this.oApp.getEditingMode() === "WYSIWYG") { + if (this.oApp.getWYSIWYGDocument().body.innerHTML == "

    ") { + this.oApp.getWYSIWYGDocument().body.innerHTML = '

    '; + var oSelection = this.oApp.getSelection(); + oSelection.moveToStringBookmark("INIT"); + oSelection.select(); + oSelection.removeStringBookmark("INIT"); + } + } else if (this.oApp.oNavigator.ie && this.oApp.oNavigator.nativeVersion == 11 && document.documentMode == 11 && this.oApp.getEditingMode() === "WYSIWYG") { + if (this.oApp.getWYSIWYGDocument().body.innerHTML == "


    ") { + var elCursorHolder_br = jindo.$$.getSingle("br", elBody); + oSelection.setStartBefore(elCursorHolder_br); + oSelection.setEndBefore(elCursorHolder_br); + oSelection.select(); + } + } + // --[SMARTEDITORSUS-1781][SMARTEDITORSUS-1691] + }, + + $ON_EVENT_TOOLBAR_MOUSEDOWN: function () { + if (this.htBrowser.nativeVersion < 9 || document.documentMode < 9) { + return; + } + + // [SMARTEDITORSUS-1632] 문서 모드가 9 이상일 때, 경우에 따라 this.welEditingAreaContainer가 없을 때 스크립트 오류 발생 + if (this.welEditingAreaCover) { + this.welEditingAreaCover.leave(); + } + //this.welEditingAreaCover.leave(); + // --[SMARTEDITORSUS-1632] + }, + + _loadAllBaseFont: function () { + var i, nFontLen; + + if (!this.htBrowser.ie) { + return; + } + + if (this.htBrowser.nativeVersion < 9) { + for (i = 0, nFontLen = this.aBaseFontList.length; i < nFontLen; i++) { + this.aBaseFontList[i].loadCSS(this.oApp.getWYSIWYGDocument()); + } + } else if (document.documentMode < 9) { + for (i = 0, nFontLen = this.aBaseFontList.length; i < nFontLen; i++) { + this.aBaseFontList[i].loadCSSToMenu(); + } + } + + this._loadAllBaseFont = function () { }; + }, + + _addFontToMenu: function (sDisplayName, sFontFamily, sSampleText) { + var elItem = document.createElement("LI"); + elItem.innerHTML = this.elFontItemTemplate.innerHTML.replace("@DisplayName@", sDisplayName).replace("FontFamily", sFontFamily).replace("@SampleText@", sSampleText); + this.elFontNameList.insertBefore(elItem, this.elFontItemTemplate); + + this.aLIFontNames[this.aLIFontNames.length] = elItem; + + if (this.aLIFontNames.length > 20) { + this.oDropdownLayer.style.overflowX = 'hidden'; + this.oDropdownLayer.style.overflowY = 'auto'; + this.oDropdownLayer.style.height = '400px'; + this.oDropdownLayer.style.width = '204px'; // [SMARTEDITORSUS-155] 스크롤을 포함하여 206px 이 되도록 처리 + } + }, + + $ON_EVENT_FONTNAME_LAYER_MOUSEOVER: function (wev) { + var elTmp = this._findLI(wev.element); + if (!elTmp) { + return; + } + + this._clearLastHover(); + + elTmp.className = "hover"; + this.elLastHover = elTmp; + }, + + $ON_EVENT_FONTNAME_LAYER_CLICKED: function (wev) { + var elTmp = this._findLI(wev.element); + if (!elTmp) { + return; + } + + var sFontFamily = this._getFontFamilyFromLI(elTmp); + // [SMARTEDITORSUS-169] 웹폰트의 경우 fontFamily 에 ' 을 붙여주는 처리를 함 + var htFontInfo = this.htAllFonts[sFontFamily.replace(/\"/g, nhn.husky.SE2M_FontNameWithLayerUI.CUSTOM_FONT_MARKS)]; + var nDefaultFontSize; + if (htFontInfo) { + nDefaultFontSize = htFontInfo.defaultSize + "pt"; + } else { + nDefaultFontSize = 0; + } + this.oApp.exec("SET_FONTFAMILY", [sFontFamily, nDefaultFontSize]); + }, + + _findLI: function (elTmp) { + while (elTmp.tagName != "LI") { + if (!elTmp || elTmp === this.oDropdownLayer) { + return null; + } + elTmp = elTmp.parentNode; + } + + if (elTmp.className.indexOf(this.FONT_SEPARATOR) > -1) { + return null; + } + return elTmp; + }, + + _clearLastHover: function () { + if (this.elLastHover) { + this.elLastHover.className = ""; + } + }, + + $ON_SE2M_TOGGLE_FONTNAME_LAYER: function () { + this.oApp.exec("TOGGLE_TOOLBAR_ACTIVE_LAYER", [this.oDropdownLayer, null, "MSG_FONTNAME_LAYER_OPENED", [], "MSG_FONTNAME_LAYER_CLOSED", []]); + this.oApp.exec('MSG_NOTIFY_CLICKCR', ['font']); + }, + + $ON_MSG_FONTNAME_LAYER_OPENED: function () { + this.oApp.exec("SELECT_UI", ["fontName"]); + }, + + $ON_MSG_FONTNAME_LAYER_CLOSED: function () { + this._clearLastHover(); + this.oApp.exec("DESELECT_UI", ["fontName"]); + }, + + $ON_MSG_STYLE_CHANGED: function (sAttributeName, sAttributeValue) { + if (sAttributeName == "fontFamily") { + sAttributeValue = sAttributeValue.replace(/["']/g, ""); + var elLi = this._getMatchingLI(sAttributeValue); + this._clearFontNameSelection(); + if (elLi) { + this.elFontNameLabel.innerHTML = this._getFontNameLabelFromLI(elLi); + jindo.$Element(elLi).addClass("active"); + } else { + //var sDisplayName = this.htFamilyName2DisplayName[sAttributeValue] || sAttributeValue; + var sDisplayName = this.sDefaultText; + this.elFontNameLabel.innerHTML = sDisplayName; + } + } + }, + + $BEFORE_RECORD_UNDO_BEFORE_ACTION: function () { + return !this.bDoNotRecordUndo; + }, + $BEFORE_RECORD_UNDO_AFTER_ACTION: function () { + return !this.bDoNotRecordUndo; + }, + $BEFORE_RECORD_UNDO_ACTION: function () { + return !this.bDoNotRecordUndo; + }, + + $ON_SET_FONTFAMILY: function (sFontFamily, sDefaultSize) { + if (!sFontFamily) { return; } + + // [SMARTEDITORSUS-169] 웹폰트의 경우 fontFamily 에 ' 을 붙여주는 처리를 함 + var oFontInfo = this.htAllFonts[sFontFamily.replace(/\"/g, nhn.husky.SE2M_FontNameWithLayerUI.CUSTOM_FONT_MARKS)]; + if (!!oFontInfo) { + oFontInfo.loadCSS(this.oApp.getWYSIWYGDocument()); + } + + // fontFamily와 fontSize 두개의 액션을 하나로 묶어서 undo history 저장 + this.oApp.exec("RECORD_UNDO_BEFORE_ACTION", ["SET FONTFAMILY", { bMustBlockElement: true }]); + this.bDoNotRecordUndo = true; + + if (parseInt(sDefaultSize, 10) > 0) { + this.oApp.exec("SET_WYSIWYG_STYLE", [{ "fontSize": sDefaultSize }]); + } + this.oApp.exec("SET_WYSIWYG_STYLE", [{ "fontFamily": sFontFamily }]); + + this.bDoNotRecordUndo = false; + this.oApp.exec("RECORD_UNDO_AFTER_ACTION", ["SET FONTFAMILY", { bMustBlockElement: true }]); + + this.oApp.exec("HIDE_ACTIVE_LAYER", []); + + this.oApp.exec("CHECK_STYLE_CHANGE", []); + }, + + _getMatchingLI: function (sFontName) { + sFontName = sFontName.toLowerCase(); + var elLi, aFontFamily; + for (var i = 0; i < this.aLIFontNames.length; i++) { + elLi = this.aLIFontNames[i]; + aFontFamily = this._getFontFamilyFromLI(elLi).toLowerCase().split(","); + for (var h = 0; h < aFontFamily.length; h++) { + if (!!aFontFamily[h] && jindo.$S(aFontFamily[h].replace(/['"]/ig, "")).trim().$value() == sFontName) { + return elLi; + } + } + } + return null; + }, + + _getFontFamilyFromLI: function (elLi) { + //return elLi.childNodes[1].innerHTML.toLowerCase(); + //
  • + return (elLi.getElementsByTagName("EM")[0]).style.fontFamily; + }, + + _getFontNameLabelFromLI: function (elLi) { + return elLi.firstChild.firstChild.firstChild.nodeValue; + }, + + _clearFontNameSelection: function (elLi) { + for (var i = 0; i < this.aLIFontNames.length; i++) { + jindo.$Element(this.aLIFontNames[i]).removeClass("active"); + } + }, + + /** + * Add the font to the list + * @param fontId {String} value of font-family in style + * @param fontName {String} name of font list in editor + * @param defaultSize + * @param fontURL + * @param fontCSSURL + * @param fontType fontType == null, custom font (sent from the server) + * fontType == 1, default font + * fontType == 2, tempSavedFont + * @param sSampleText {String} sample text of font list in editor + * @param bCheck {Boolean} + */ + addFont: function (fontId, fontName, defaultSize, fontURL, fontCSSURL, fontType, sSampleText, bCheck) { + // custom font feature only available in IE + if (!this.htBrowser.ie && fontCSSURL) { + return null; + } + + // OS에 해당 폰트가 존재하는지 여부를 확인한다. + if (bCheck && !IsInstalledFont(fontId)) { + return null; + } + + // [SMARTEDITORSUS-2235] 폰트패밀리가 대소문자 구분되어 삽입될 수 있도록 주석처리 + //fontId = fontId.toLowerCase(); + + var newFont = new fontProperty(fontId, fontName, defaultSize, fontURL, fontCSSURL); + + var sFontFamily; + var sDisplayName; + if (defaultSize > 0) { + sFontFamily = fontId + "_" + defaultSize; + sDisplayName = fontName + "_" + defaultSize; + } else { + sFontFamily = fontId; + sDisplayName = fontName; + } + + if (!fontType) { + sFontFamily = nhn.husky.SE2M_FontNameWithLayerUI.CUSTOM_FONT_MARKS + sFontFamily + nhn.husky.SE2M_FontNameWithLayerUI.CUSTOM_FONT_MARKS; + } + + if (this.htAllFonts[sFontFamily]) { + return this.htAllFonts[sFontFamily]; + } + this.htAllFonts[sFontFamily] = newFont; + /* + // do not add again, if the font is already in the list + for(var i=0; i웹폰트B 선택>웹폰트 A를 다시 선택하면 웹폰트 A가 적용되지 않는 문제가 발생 + // + // [원인] + // - IE9의 웹폰트 로드/언로드 시점 + // 웹폰트 로드 시점: StyleSheet 의 @font-face 구문이 해석된 이후, DOM Tree 상에서 해당 웹폰트가 최초로 사용된 시점 + // 웹폰트 언로드 시점: StyleSheet 의 @font-face 구문이 해석된 이후, DOM Tree 상에서 해당 웬폰트가 더이상 사용되지 않는 시점 + // - 메뉴 리스트에 적용되는 스타일은 @font-face 이전에 처리되는 것이어서 언로드에 영향을 미치지 않음 + // + // 스마트에디터의 경우, 웹폰트를 선택할 때마다 SPAN 이 새로 추가되는 것이 아닌 선택된 SPAN 의 fontFamily 를 변경하여 처리하므로 + // fontFamily 변경 후 DOM Tree 상에서 더이상 사용되지 않는 것으로 브라우저 판단하여 언로드 해버림. + // [해결] + // 언로드가 발생하지 않도록 메뉴 리스트에 스타일을 적용하는 것을 @font-face 이후로 하도록 처리하여 DOM Tree 상에 항상 적용될 수 있도록 함 + // + // [SMARTEDITORSUS-969] [IE10] 웹폰트를 사용하여 글을 등록하고, 수정모드로 들어갔을 때 웹폰트가 적용되지 않는 문제 + // - IE10에서도 웹폰트 언로드가 발생하지 않도록 조건을 수정함 + // -> 기존 : nativeVersion === 9 && documentMode === 9 + // -> 수정 : nativeVersion >= 9 && documentMode >= 9 + if (this.htBrowser.ie && this.htBrowser.nativeVersion >= 9 && document.documentMode >= 9) { + newFont.loadCSSToMenu(); + } + + this.htFamilyName2DisplayName[sFontFamily] = fontName; + + sSampleText = sSampleText || this.oApp.$MSG('SE2M_FontNameWithLayerUI.sSampleText'); + this._addFontToMenu(sDisplayName, sFontFamily, sSampleText); + + if (!fontType) { + this.aBaseFontList[this.aBaseFontList.length] = newFont; + } else { + if (fontType == 1) { + this.aDefaultFontList[this.aDefaultFontList.length] = newFont; + } else { + this.aTempSavedFontList[this.aTempSavedFontList.length] = newFont; + } + } + + return newFont; + }, + // Add the font AND load it right away + addFontInUse: function (fontId, fontName, defaultSize, fontURL, fontCSSURL, fontType) { + var newFont = this.addFont(fontId, fontName, defaultSize, fontURL, fontCSSURL, fontType); + if (!newFont) { + return null; + } + + newFont.loadCSS(this.oApp.getWYSIWYGDocument()); + + return newFont; + }, + // Add the font AND load it right away AND THEN set it as the default font + setMainFont: function (fontId, fontName, defaultSize, fontURL, fontCSSURL, fontType) { + var newFont = this.addFontInUse(fontId, fontName, defaultSize, fontURL, fontCSSURL, fontType); + if (!newFont) { + return null; + } + + this.setDefaultFont(newFont.fontFamily, defaultSize); + + return newFont; + }, + + setDefaultFont: function (sFontFamily, nFontSize) { + var elBody = this.oApp.getWYSIWYGDocument().body; + elBody.style.fontFamily = sFontFamily; + if (nFontSize > 0) { elBody.style.fontSize = nFontSize + 'pt'; } + } }); nhn.husky.SE2M_FontNameWithLayerUI.CUSTOM_FONT_MARKS = "'"; // [SMARTEDITORSUS-169] 웹폰트의 경우 fontFamily 에 ' 을 붙여주는 처리를 함 // property function for all fonts - including the default fonts and the custom fonts // non-custom fonts will have the defaultSize of 0 and empty string for fontURL/fontCSSURL -function fontProperty(fontId, fontName, defaultSize, fontURL, fontCSSURL){ - this.fontId = fontId; - this.fontName = fontName; - this.defaultSize = defaultSize; - this.fontURL = fontURL; - this.fontCSSURL = fontCSSURL; - - this.displayName = fontName; - this.isLoaded = true; - this.fontFamily = this.fontId; - - // it is custom font - if(this.fontCSSURL != ""){ - this.displayName += '' + defaultSize; - this.fontFamily += '_' + defaultSize; - // custom fonts requires css loading - this.isLoaded = false; - - // load the css that loads the custom font - this.loadCSS = function(doc){ - // if the font is loaded already, return - if(this.isLoaded){ - return; - } - - this._importCSS(doc); - this.isLoaded = true; - }; - - // [SMARTEDITORSUS-169] [IE9] - // addImport 후에 처음 적용된 DOM-Tree 가 iframe 내부인 경우 (setMainFont || addFontInUse 에서 호출된 경우) - // 해당 폰트에 대한 언로드 문제가 계속 발생하여 IE9에서 addFont 에서 호출하는 loadCSS 의 경우에는 isLoaded를 true 로 변경하지 않음. - this.loadCSSToMenu = function(){ - this._importCSS(document); - }; - - this._importCSS = function(doc){ - var nStyleSheet = doc.styleSheets.length; - var oStyleSheet = doc.styleSheets[nStyleSheet - 1]; - - if(nStyleSheet === 0 || oStyleSheet.imports.length > 30){ // imports limit - // [SMARTEDITORSUS-1828] IE11에서 document.createStyleSheet API가 제거되었음 - // [SMARTEDITORSUS-2181] createStyleSheet 를 사용하면 순서보장이 안되어 createElement("style") 로 삽입하는 방식만 사용 - // 참고1 : http://msdn.microsoft.com/en-us/library/ie/bg182625(v=vs.85).aspx#legacyapis - // 참고2 : http://msdn.microsoft.com/en-us/library/ie/ms531194(v=vs.85).aspx - oStyleSheet = doc.createElement("style"); - doc.documentElement.firstChild.appendChild(oStyleSheet); - // [SMARTEDITORSUS-2189] sheet은 모던브라우저, IE9이상부터 지원 (IE8이하는 styleSheet 으로 가져와야함) - // 참고 : http://help.dottoro.com/ljpatulu.php - oStyleSheet = oStyleSheet.sheet || oStyleSheet.styleSheet; - } - - oStyleSheet.addImport(this.fontCSSURL); - }; - }else{ - this.loadCSS = function(){}; - this.loadCSSToMenu = function(){}; - } - - this.toStruct = function(){ - return {fontId:this.fontId, fontName:this.fontName, defaultSize:this.defaultSize, fontURL:this.fontURL, fontCSSURL:this.fontCSSURL}; - }; +function fontProperty(fontId, fontName, defaultSize, fontURL, fontCSSURL) { + this.fontId = fontId; + this.fontName = fontName; + this.defaultSize = defaultSize; + this.fontURL = fontURL; + this.fontCSSURL = fontCSSURL; + + this.displayName = fontName; + this.isLoaded = true; + this.fontFamily = this.fontId; + + // it is custom font + if (this.fontCSSURL != "") { + this.displayName += '' + defaultSize; + this.fontFamily += '_' + defaultSize; + // custom fonts requires css loading + this.isLoaded = false; + + // load the css that loads the custom font + this.loadCSS = function (doc) { + // if the font is loaded already, return + if (this.isLoaded) { + return; + } + + this._importCSS(doc); + this.isLoaded = true; + }; + + // [SMARTEDITORSUS-169] [IE9] + // addImport 후에 처음 적용된 DOM-Tree 가 iframe 내부인 경우 (setMainFont || addFontInUse 에서 호출된 경우) + // 해당 폰트에 대한 언로드 문제가 계속 발생하여 IE9에서 addFont 에서 호출하는 loadCSS 의 경우에는 isLoaded를 true 로 변경하지 않음. + this.loadCSSToMenu = function () { + this._importCSS(document); + }; + + this._importCSS = function (doc) { + var nStyleSheet = doc.styleSheets.length; + var oStyleSheet = doc.styleSheets[nStyleSheet - 1]; + + if (nStyleSheet === 0 || oStyleSheet.imports.length > 30) { // imports limit + // [SMARTEDITORSUS-1828] IE11에서 document.createStyleSheet API가 제거되었음 + // [SMARTEDITORSUS-2181] createStyleSheet 를 사용하면 순서보장이 안되어 createElement("style") 로 삽입하는 방식만 사용 + // 참고1 : http://msdn.microsoft.com/en-us/library/ie/bg182625(v=vs.85).aspx#legacyapis + // 참고2 : http://msdn.microsoft.com/en-us/library/ie/ms531194(v=vs.85).aspx + oStyleSheet = doc.createElement("style"); + doc.documentElement.firstChild.appendChild(oStyleSheet); + // [SMARTEDITORSUS-2189] sheet은 모던브라우저, IE9이상부터 지원 (IE8이하는 styleSheet 으로 가져와야함) + // 참고 : http://help.dottoro.com/ljpatulu.php + oStyleSheet = oStyleSheet.sheet || oStyleSheet.styleSheet; + } + + oStyleSheet.addImport(this.fontCSSURL); + }; + } else { + this.loadCSS = function () { }; + this.loadCSSToMenu = function () { }; + } + + this.toStruct = function () { + return { fontId: this.fontId, fontName: this.fontName, defaultSize: this.defaultSize, fontURL: this.fontURL, fontCSSURL: this.fontCSSURL }; + }; } /** * ColorPicker Component * @author gony */ - nhn.ColorPicker = jindo.$Class({ - elem : null, - huePanel : null, - canvasType : "Canvas", - _hsvColor : null, - $init : function(oElement, oOptions) { - this.elem = jindo.$Element(oElement).empty(); - this.huePanel = null; - this.cursor = jindo.$Element("
    ").css("overflow", "hidden"); - this.canvasType = jindo.$(oElement).filters?"Filter":jindo.$("").getContext?"Canvas":null; +nhn.ColorPicker = jindo.$Class({ + elem: null, + huePanel: null, + canvasType: "Canvas", + _hsvColor: null, + $init: function (oElement, oOptions) { + this.elem = jindo.$Element(oElement).empty(); + this.huePanel = null; + this.cursor = jindo.$Element("
    ").css("overflow", "hidden"); + this.canvasType = jindo.$(oElement).filters ? "Filter" : jindo.$("").getContext ? "Canvas" : null; - if(!this.canvasType) { - return false; - } - - this.option({ - huePanel : null, - huePanelType : "horizontal" - }); - - this.option(oOptions); - if (this.option("huePanel")) { - this.huePanel = jindo.$Element(this.option("huePanel")).empty(); - } + if (!this.canvasType) { + return false; + } - // rgb - this._hsvColor = this._hsv(0,100,100); // #FF0000 + this.option({ + huePanel: null, + huePanelType: "horizontal" + }); - // event binding - for(var name in this) { - if (/^_on[A-Z][a-z]+[A-Z][a-z]+$/.test(name)) { - this[name+"Fn"] = jindo.$Fn(this[name], this); - } - } + this.option(oOptions); + if (this.option("huePanel")) { + this.huePanel = jindo.$Element(this.option("huePanel")).empty(); + } - this._onDownColorFn.attach(this.elem, "mousedown"); - if (this.huePanel) { - this._onDownHueFn.attach(this.huePanel, "mousedown"); - } + // rgb + this._hsvColor = this._hsv(0, 100, 100); // #FF0000 - // paint - this.paint(); - }, - rgb : function(rgb) { - this.hsv(this._rgb2hsv(rgb.r, rgb.g, rgb.b)); - }, - hsv : function(hsv) { - if (typeof hsv == "undefined") { - return this._hsvColor; - } + // event binding + for (var name in this) { + if (/^_on[A-Z][a-z]+[A-Z][a-z]+$/.test(name)) { + this[name + "Fn"] = jindo.$Fn(this[name], this); + } + } - var rgb = null; - var w = this.elem.width(); - var h = this.elem.height(); - var cw = this.cursor.width(); - var ch = this.cursor.height(); - var x = 0, y = 0; + this._onDownColorFn.attach(this.elem, "mousedown"); + if (this.huePanel) { + this._onDownHueFn.attach(this.huePanel, "mousedown"); + } - if (this.huePanel) { - rgb = this._hsv2rgb(hsv.h, 100, 100); - this.elem.css("background", "#"+this._rgb2hex(rgb.r, rgb.g, rgb.b)); + // paint + this.paint(); + }, + rgb: function (rgb) { + this.hsv(this._rgb2hsv(rgb.r, rgb.g, rgb.b)); + }, + hsv: function (hsv) { + if (typeof hsv == "undefined") { + return this._hsvColor; + } - x = hsv.s/100 * w; - y = (100-hsv.v)/100 * h; - } else { - var hw = w / 2; - if (hsv.v > hsv.s) { - hsv.v = 100; - x = hsv.s/100 * hw; - } else { - hsv.s = 100; - x = (100-hsv.v)/100 * hw + hw; - } - y = hsv.h/360 * h; - } + var rgb = null; + var w = this.elem.width(); + var h = this.elem.height(); + var cw = this.cursor.width(); + var ch = this.cursor.height(); + var x = 0, y = 0; - x = Math.max(Math.min(x-1,w-cw), 1); - y = Math.max(Math.min(y-1,h-ch), 1); + if (this.huePanel) { + rgb = this._hsv2rgb(hsv.h, 100, 100); + this.elem.css("background", "#" + this._rgb2hex(rgb.r, rgb.g, rgb.b)); - this.cursor.css({left:x+"px",top:y+"px"}); + x = hsv.s / 100 * w; + y = (100 - hsv.v) / 100 * h; + } else { + var hw = w / 2; + if (hsv.v > hsv.s) { + hsv.v = 100; + x = hsv.s / 100 * hw; + } else { + hsv.s = 100; + x = (100 - hsv.v) / 100 * hw + hw; + } + y = hsv.h / 360 * h; + } - this._hsvColor = hsv; - rgb = this._hsv2rgb(hsv.h, hsv.s, hsv.v); + x = Math.max(Math.min(x - 1, w - cw), 1); + y = Math.max(Math.min(y - 1, h - ch), 1); - this.fireEvent("colorchange", {type:"colorchange", element:this, currentElement:this, rgbColor:rgb, hexColor:"#"+this._rgb2hex(rgb.r, rgb.g, rgb.b), hsvColor:hsv} ); - }, - paint : function() { - if (this.huePanel) { - // paint color panel - this["_paintColWith"+this.canvasType](); + this.cursor.css({ left: x + "px", top: y + "px" }); - // paint hue panel - this["_paintHueWith"+this.canvasType](); - } else { - // paint color panel - this["_paintOneWith"+this.canvasType](); - } + this._hsvColor = hsv; + rgb = this._hsv2rgb(hsv.h, hsv.s, hsv.v); - // draw cursor - this.cursor.appendTo(this.elem); - this.cursor.css({position:"absolute",top:"1px",left:"1px",background:"white",border:"1px solid black"}).width(3).height(3); + this.fireEvent("colorchange", { type: "colorchange", element: this, currentElement: this, rgbColor: rgb, hexColor: "#" + this._rgb2hex(rgb.r, rgb.g, rgb.b), hsvColor: hsv }); + }, + paint: function () { + if (this.huePanel) { + // paint color panel + this["_paintColWith" + this.canvasType](); - this.hsv(this._hsvColor); - }, - _paintColWithFilter : function() { - // white : left to right - jindo.$Element("
    ").css({ - position : "absolute", - top : 0, - left : 0, - width : "100%", - height : "100%", - filter : "progid:DXImageTransform.Microsoft.Gradient(GradientType=1,StartColorStr='#FFFFFFFF',EndColorStr='#00FFFFFF')" - }).appendTo(this.elem); + // paint hue panel + this["_paintHueWith" + this.canvasType](); + } else { + // paint color panel + this["_paintOneWith" + this.canvasType](); + } - // black : down to up - jindo.$Element("
    ").css({ - position : "absolute", - top : 0, - left : 0, - width : "100%", - height : "100%", - filter : "progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr='#00000000',EndColorStr='#FF000000')" - }).appendTo(this.elem); - }, - _paintColWithCanvas : function() { - var cvs = jindo.$Element("").css({width:"100%",height:"100%"}); - cvs.appendTo(this.elem.empty()); - - var ctx = cvs.attr("width", cvs.width()).attr("height", cvs.height()).$value().getContext("2d"); - var lin = null; - var w = cvs.width(); - var h = cvs.height(); + // draw cursor + this.cursor.appendTo(this.elem); + this.cursor.css({ position: "absolute", top: "1px", left: "1px", background: "white", border: "1px solid black" }).width(3).height(3); - // white : left to right - lin = ctx.createLinearGradient(0,0,w,0); - lin.addColorStop(0, "rgba(255,255,255,1)"); - lin.addColorStop(1, "rgba(255,255,255,0)"); - ctx.fillStyle = lin; - ctx.fillRect(0,0,w,h); + this.hsv(this._hsvColor); + }, + _paintColWithFilter: function () { + // white : left to right + jindo.$Element("
    ").css({ + position: "absolute", + top: 0, + left: 0, + width: "100%", + height: "100%", + filter: "progid:DXImageTransform.Microsoft.Gradient(GradientType=1,StartColorStr='#FFFFFFFF',EndColorStr='#00FFFFFF')" + }).appendTo(this.elem); - // black : down to top - lin = ctx.createLinearGradient(0,0,0,h); - lin.addColorStop(0, "rgba(0,0,0,0)"); - lin.addColorStop(1, "rgba(0,0,0,1)"); - ctx.fillStyle = lin; - ctx.fillRect(0,0,w,h); - }, - _paintOneWithFilter : function() { - var sp, ep, s_rgb, e_rgb, s_hex, e_hex; - var h = this.elem.height(); + // black : down to up + jindo.$Element("
    ").css({ + position: "absolute", + top: 0, + left: 0, + width: "100%", + height: "100%", + filter: "progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr='#00000000',EndColorStr='#FF000000')" + }).appendTo(this.elem); + }, + _paintColWithCanvas: function () { + var cvs = jindo.$Element("").css({ width: "100%", height: "100%" }); + cvs.appendTo(this.elem.empty()); - for(var i=1; i < 7; i++) { - sp = Math.floor((i-1)/6 * h); - ep = Math.floor(i/6 * h); + var ctx = cvs.attr("width", cvs.width()).attr("height", cvs.height()).$value().getContext("2d"); + var lin = null; + var w = cvs.width(); + var h = cvs.height(); - s_rgb = this._hsv2rgb((i-1)/6*360, 100, 100); - e_rgb = this._hsv2rgb(i/6*360, 100, 100); - s_hex = "#FF"+this._rgb2hex(s_rgb.r, s_rgb.g, s_rgb.b); - e_hex = "#FF"+this._rgb2hex(e_rgb.r, e_rgb.g, e_rgb.b); + // white : left to right + lin = ctx.createLinearGradient(0, 0, w, 0); + lin.addColorStop(0, "rgba(255,255,255,1)"); + lin.addColorStop(1, "rgba(255,255,255,0)"); + ctx.fillStyle = lin; + ctx.fillRect(0, 0, w, h); - jindo.$Element("
    ").css({ - position : "absolute", - left : 0, - width : "100%", - top : sp + "px", - height : (ep-sp) + "px", - filter : "progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr='"+s_hex+"',EndColorStr='"+e_hex+"')" - }).appendTo(this.elem); - } + // black : down to top + lin = ctx.createLinearGradient(0, 0, 0, h); + lin.addColorStop(0, "rgba(0,0,0,0)"); + lin.addColorStop(1, "rgba(0,0,0,1)"); + ctx.fillStyle = lin; + ctx.fillRect(0, 0, w, h); + }, + _paintOneWithFilter: function () { + var sp, ep, s_rgb, e_rgb, s_hex, e_hex; + var h = this.elem.height(); - // white : left to right - jindo.$Element("
    ").css({ - position : "absolute", - top : 0, - left : 0, - width : "50%", - height : "100%", - filter : "progid:DXImageTransform.Microsoft.Gradient(GradientType=1,StartColorStr='#FFFFFFFF',EndColorStr='#00FFFFFF')" - }).appendTo(this.elem); + for (var i = 1; i < 7; i++) { + sp = Math.floor((i - 1) / 6 * h); + ep = Math.floor(i / 6 * h); - // black : down to up - jindo.$Element("
    ").css({ - position : "absolute", - top : 0, - right : 0, - width : "50%", - height : "100%", - filter : "progid:DXImageTransform.Microsoft.Gradient(GradientType=1,StartColorStr='#00000000',EndColorStr='#FF000000')" - }).appendTo(this.elem); - }, - _paintOneWithCanvas : function() { - var rgb = {r:0, g:0, b:0}; - var cvs = jindo.$Element("").css({width:"100%",height:"100%"}); - cvs.appendTo(this.elem.empty()); - - var ctx = cvs.attr("width", cvs.width()).attr("height", cvs.height()).$value().getContext("2d"); - - var w = cvs.width(); - var h = cvs.height(); - var lin = ctx.createLinearGradient(0,0,0,h); + s_rgb = this._hsv2rgb((i - 1) / 6 * 360, 100, 100); + e_rgb = this._hsv2rgb(i / 6 * 360, 100, 100); + s_hex = "#FF" + this._rgb2hex(s_rgb.r, s_rgb.g, s_rgb.b); + e_hex = "#FF" + this._rgb2hex(e_rgb.r, e_rgb.g, e_rgb.b); - for(var i=0; i < 7; i++) { - rgb = this._hsv2rgb(i/6*360, 100, 100); - lin.addColorStop(i/6, "rgb("+rgb.join(",")+")"); - } - ctx.fillStyle = lin; - ctx.fillRect(0,0,w,h); + jindo.$Element("
    ").css({ + position: "absolute", + left: 0, + width: "100%", + top: sp + "px", + height: (ep - sp) + "px", + filter: "progid:DXImageTransform.Microsoft.Gradient(GradientType=0,StartColorStr='" + s_hex + "',EndColorStr='" + e_hex + "')" + }).appendTo(this.elem); + } - lin = ctx.createLinearGradient(0,0,w,0); - lin.addColorStop(0, "rgba(255,255,255,1)"); - lin.addColorStop(0.5, "rgba(255,255,255,0)"); - lin.addColorStop(0.5, "rgba(0,0,0,0)"); - lin.addColorStop(1, "rgba(0,0,0,1)"); - ctx.fillStyle = lin; - ctx.fillRect(0,0,w,h); - }, - _paintHueWithFilter : function() { - var sp, ep, s_rgb, e_rgb, s_hex, e_hex; - var vert = (this.option().huePanelType == "vertical"); - var w = this.huePanel.width(); - var h = this.huePanel.height(); - var elDiv = null; + // white : left to right + jindo.$Element("
    ").css({ + position: "absolute", + top: 0, + left: 0, + width: "50%", + height: "100%", + filter: "progid:DXImageTransform.Microsoft.Gradient(GradientType=1,StartColorStr='#FFFFFFFF',EndColorStr='#00FFFFFF')" + }).appendTo(this.elem); - var nPanelBorderWidth = parseInt(this.huePanel.css('borderWidth'), 10); - if (!!isNaN(nPanelBorderWidth)) { nPanelBorderWidth = 0; } - w -= nPanelBorderWidth * 2; // borderWidth를 제외한 내측 폭을 구함 - - for(var i=1; i < 7; i++) { - sp = Math.floor((i-1)/6 * (vert?h:w)); - ep = Math.floor(i/6 * (vert?h:w)); + // black : down to up + jindo.$Element("
    ").css({ + position: "absolute", + top: 0, + right: 0, + width: "50%", + height: "100%", + filter: "progid:DXImageTransform.Microsoft.Gradient(GradientType=1,StartColorStr='#00000000',EndColorStr='#FF000000')" + }).appendTo(this.elem); + }, + _paintOneWithCanvas: function () { + var rgb = { r: 0, g: 0, b: 0 }; + var cvs = jindo.$Element("").css({ width: "100%", height: "100%" }); + cvs.appendTo(this.elem.empty()); - s_rgb = this._hsv2rgb((i-1)/6*360, 100, 100); - e_rgb = this._hsv2rgb(i/6*360, 100, 100); - s_hex = "#FF"+this._rgb2hex(s_rgb.r, s_rgb.g, s_rgb.b); - e_hex = "#FF"+this._rgb2hex(e_rgb.r, e_rgb.g, e_rgb.b); + var ctx = cvs.attr("width", cvs.width()).attr("height", cvs.height()).$value().getContext("2d"); - elDiv = jindo.$Element("
    ").css({ - position : "absolute", - filter : "progid:DXImageTransform.Microsoft.Gradient(GradientType="+(vert?0:1)+",StartColorStr='"+s_hex+"',EndColorStr='"+e_hex+"')" - }); - - var width = (ep - sp) + 1; // IE에서 폭을 넓혀주지 않으면 확대 시 벌어짐, 그래서 1px 보정 - elDiv.appendTo(this.huePanel); - elDiv.css(vert?"left":"top", 0).css(vert?"width":"height", '100%'); - elDiv.css(vert?"top":"left", sp + "px").css(vert?"height":"width", width + "px"); - } - }, - _paintHueWithCanvas : function() { - var opt = this.option(), rgb; - var vtc = (opt.huePanelType == "vertical"); - - var cvs = jindo.$Element("").css({width:"100%",height:"100%"}); - cvs.appendTo(this.huePanel.empty()); - - var ctx = cvs.attr("width", cvs.width()).attr("height", cvs.height()).$value().getContext("2d"); - var lin = ctx.createLinearGradient(0,0,vtc?0:cvs.width(),vtc?cvs.height():0); + var w = cvs.width(); + var h = cvs.height(); + var lin = ctx.createLinearGradient(0, 0, 0, h); - for(var i=0; i < 7; i++) { - rgb = this._hsv2rgb(i/6*360, 100, 100); - lin.addColorStop(i/6, "rgb("+rgb.join(",")+")"); - } - ctx.fillStyle = lin; - ctx.fillRect(0,0,cvs.width(),cvs.height()); - }, - _rgb2hsv : function(r,g,b) { - var h = 0, s = 0, v = Math.max(r,g,b), min = Math.min(r,g,b), delta = v - min; - s = (v ? delta/v : 0); - - if (s) { - if (r == v) { - h = 60 * (g - b) / delta; - } else if (g == v) { - h = 120 + 60 * (b - r) / delta; - } else if (b == v) { - h = 240 + 60 * (r - g) / delta; - } + for (var i = 0; i < 7; i++) { + rgb = this._hsv2rgb(i / 6 * 360, 100, 100); + lin.addColorStop(i / 6, "rgb(" + rgb.join(",") + ")"); + } + ctx.fillStyle = lin; + ctx.fillRect(0, 0, w, h); - if (h < 0) { - h += 360; - } - } - - h = Math.floor(h); - s = Math.floor(s * 100); - v = Math.floor(v / 255 * 100); + lin = ctx.createLinearGradient(0, 0, w, 0); + lin.addColorStop(0, "rgba(255,255,255,1)"); + lin.addColorStop(0.5, "rgba(255,255,255,0)"); + lin.addColorStop(0.5, "rgba(0,0,0,0)"); + lin.addColorStop(1, "rgba(0,0,0,1)"); + ctx.fillStyle = lin; + ctx.fillRect(0, 0, w, h); + }, + _paintHueWithFilter: function () { + var sp, ep, s_rgb, e_rgb, s_hex, e_hex; + var vert = (this.option().huePanelType == "vertical"); + var w = this.huePanel.width(); + var h = this.huePanel.height(); + var elDiv = null; - return this._hsv(h,s,v); - }, - _hsv2rgb : function(h,s,v) { - h = (h % 360) / 60; s /= 100; v /= 100; + var nPanelBorderWidth = parseInt(this.huePanel.css('borderWidth'), 10); + if (!!isNaN(nPanelBorderWidth)) { nPanelBorderWidth = 0; } + w -= nPanelBorderWidth * 2; // borderWidth를 제외한 내측 폭을 구함 - var r=0, g=0, b=0; - var i = Math.floor(h); - var f = h-i; - var p = v*(1-s); - var q = v*(1-s*f); - var t = v*(1-s*(1-f)); + for (var i = 1; i < 7; i++) { + sp = Math.floor((i - 1) / 6 * (vert ? h : w)); + ep = Math.floor(i / 6 * (vert ? h : w)); - switch (i) { - case 0: r=v; g=t; b=p; break; - case 1: r=q; g=v; b=p; break; - case 2: r=p; g=v; b=t; break; - case 3: r=p; g=q; b=v; break; - case 4: r=t; g=p; b=v; break; - case 5: r=v; g=p; b=q;break; - case 6: break; - } + s_rgb = this._hsv2rgb((i - 1) / 6 * 360, 100, 100); + e_rgb = this._hsv2rgb(i / 6 * 360, 100, 100); + s_hex = "#FF" + this._rgb2hex(s_rgb.r, s_rgb.g, s_rgb.b); + e_hex = "#FF" + this._rgb2hex(e_rgb.r, e_rgb.g, e_rgb.b); - r = Math.floor(r*255); - g = Math.floor(g*255); - b = Math.floor(b*255); + elDiv = jindo.$Element("
    ").css({ + position: "absolute", + filter: "progid:DXImageTransform.Microsoft.Gradient(GradientType=" + (vert ? 0 : 1) + ",StartColorStr='" + s_hex + "',EndColorStr='" + e_hex + "')" + }); - return this._rgb(r,g,b); - }, - _rgb2hex : function(r,g,b) { - r = r.toString(16); - if (r.length == 1) { - r = '0'+r; - } - - g = g.toString(16); - if (g.length==1) { - g = '0'+g; - } - - b = b.toString(16); - if (b.length==1) { - b = '0'+b; - } + var width = (ep - sp) + 1; // IE에서 폭을 넓혀주지 않으면 확대 시 벌어짐, 그래서 1px 보정 + elDiv.appendTo(this.huePanel); + elDiv.css(vert ? "left" : "top", 0).css(vert ? "width" : "height", '100%'); + elDiv.css(vert ? "top" : "left", sp + "px").css(vert ? "height" : "width", width + "px"); + } + }, + _paintHueWithCanvas: function () { + var opt = this.option(), rgb; + var vtc = (opt.huePanelType == "vertical"); - return r+g+b; - }, - _hex2rgb : function(hex) { - var m = hex.match(/#?([0-9a-f]{6}|[0-9a-f]{3})/i); - if (m[1].length == 3) { - m = m[1].match(/./g).filter(function(c) { - return c+c; - }); - } else { - m = m[1].match(/../g); - } - return { - r : Number("0x" + m[0]), - g : Number("0x" + m[1]), - b : Number("0x" + m[2]) - }; - }, - _rgb : function(r,g,b) { - var ret = [r,g,b]; + var cvs = jindo.$Element("").css({ width: "100%", height: "100%" }); + cvs.appendTo(this.huePanel.empty()); - ret.r = r; - ret.g = g; - ret.b = b; + var ctx = cvs.attr("width", cvs.width()).attr("height", cvs.height()).$value().getContext("2d"); + var lin = ctx.createLinearGradient(0, 0, vtc ? 0 : cvs.width(), vtc ? cvs.height() : 0); - return ret; - }, - _hsv : function(h,s,v) { - var ret = [h,s,v]; + for (var i = 0; i < 7; i++) { + rgb = this._hsv2rgb(i / 6 * 360, 100, 100); + lin.addColorStop(i / 6, "rgb(" + rgb.join(",") + ")"); + } + ctx.fillStyle = lin; + ctx.fillRect(0, 0, cvs.width(), cvs.height()); + }, + _rgb2hsv: function (r, g, b) { + var h = 0, s = 0, v = Math.max(r, g, b), min = Math.min(r, g, b), delta = v - min; + s = (v ? delta / v : 0); - ret.h = h; - ret.s = s; - ret.v = v; + if (s) { + if (r == v) { + h = 60 * (g - b) / delta; + } else if (g == v) { + h = 120 + 60 * (b - r) / delta; + } else if (b == v) { + h = 240 + 60 * (r - g) / delta; + } - return ret; - }, - _onDownColor : function(e) { - if (!e.mouse().left) { - return false; - } + if (h < 0) { + h += 360; + } + } - var pos = e.pos(); + h = Math.floor(h); + s = Math.floor(s * 100); + v = Math.floor(v / 255 * 100); - this._colPagePos = [pos.pageX, pos.pageY]; - this._colLayerPos = [pos.layerX, pos.layerY]; + return this._hsv(h, s, v); + }, + _hsv2rgb: function (h, s, v) { + h = (h % 360) / 60; s /= 100; v /= 100; - this._onUpColorFn.attach(document, "mouseup"); - this._onMoveColorFn.attach(document, "mousemove"); + var r = 0, g = 0, b = 0; + var i = Math.floor(h); + var f = h - i; + var p = v * (1 - s); + var q = v * (1 - s * f); + var t = v * (1 - s * (1 - f)); - this._onMoveColor(e); - }, - _onUpColor : function(e) { - this._onUpColorFn.detach(document, "mouseup"); - this._onMoveColorFn.detach(document, "mousemove"); - }, - _onMoveColor : function(e) { - var hsv = this._hsvColor; - var pos = e.pos(); - var x = this._colLayerPos[0] + (pos.pageX - this._colPagePos[0]); - var y = this._colLayerPos[1] + (pos.pageY - this._colPagePos[1]); - var w = this.elem.width(); - var h = this.elem.height(); + switch (i) { + case 0: r = v; g = t; b = p; break; + case 1: r = q; g = v; b = p; break; + case 2: r = p; g = v; b = t; break; + case 3: r = p; g = q; b = v; break; + case 4: r = t; g = p; b = v; break; + case 5: r = v; g = p; b = q; break; + case 6: break; + } - x = Math.max(Math.min(x, w), 0); - y = Math.max(Math.min(y, h), 0); + r = Math.floor(r * 255); + g = Math.floor(g * 255); + b = Math.floor(b * 255); - if (this.huePanel) { - hsv.s = hsv[1] = x / w * 100; - hsv.v = hsv[2] = (h - y) / h * 100; - } else { - hsv.h = y/h*360; + return this._rgb(r, g, b); + }, + _rgb2hex: function (r, g, b) { + r = r.toString(16); + if (r.length == 1) { + r = '0' + r; + } - var hw = w/2; + g = g.toString(16); + if (g.length == 1) { + g = '0' + g; + } - if (x < hw) { - hsv.s = x/hw * 100; - hsv.v = 100; - } else { - hsv.s = 100; - hsv.v = (w-x)/hw * 100; - } - } + b = b.toString(16); + if (b.length == 1) { + b = '0' + b; + } - this.hsv(hsv); + return r + g + b; + }, + _hex2rgb: function (hex) { + var m = hex.match(/#?([0-9a-f]{6}|[0-9a-f]{3})/i); + if (m[1].length == 3) { + m = m[1].match(/./g).filter(function (c) { + return c + c; + }); + } else { + m = m[1].match(/../g); + } + return { + r: Number("0x" + m[0]), + g: Number("0x" + m[1]), + b: Number("0x" + m[2]) + }; + }, + _rgb: function (r, g, b) { + var ret = [r, g, b]; - e.stop(); - }, - _onDownHue : function(e) { - if (!e.mouse().left) { - return false; - } + ret.r = r; + ret.g = g; + ret.b = b; - var pos = e.pos(); + return ret; + }, + _hsv: function (h, s, v) { + var ret = [h, s, v]; - this._huePagePos = [pos.pageX, pos.pageY]; - this._hueLayerPos = [pos.layerX, pos.layerY]; + ret.h = h; + ret.s = s; + ret.v = v; - this._onUpHueFn.attach(document, "mouseup"); - this._onMoveHueFn.attach(document, "mousemove"); + return ret; + }, + _onDownColor: function (e) { + if (!e.mouse().left) { + return false; + } - this._onMoveHue(e); - }, - _onUpHue : function(e) { - this._onUpHueFn.detach(document, "mouseup"); - this._onMoveHueFn.detach(document, "mousemove"); - }, - _onMoveHue : function(e) { - var hsv = this._hsvColor; - var pos = e.pos(); - var cur = 0, len = 0; - var x = this._hueLayerPos[0] + (pos.pageX - this._huePagePos[0]); - var y = this._hueLayerPos[1] + (pos.pageY - this._huePagePos[1]); + var pos = e.pos(); - if (this.option().huePanelType == "vertical") { - cur = y; - len = this.huePanel.height(); - } else { - cur = x; - len = this.huePanel.width(); - } + this._colPagePos = [pos.pageX, pos.pageY]; + this._colLayerPos = [pos.layerX, pos.layerY]; - hsv.h = hsv[0] = (Math.min(Math.max(cur, 0), len)/len * 360)%360; + this._onUpColorFn.attach(document, "mouseup"); + this._onMoveColorFn.attach(document, "mousemove"); - this.hsv(hsv); + this._onMoveColor(e); + }, + _onUpColor: function (e) { + this._onUpColorFn.detach(document, "mouseup"); + this._onMoveColorFn.detach(document, "mousemove"); + }, + _onMoveColor: function (e) { + var hsv = this._hsvColor; + var pos = e.pos(); + var x = this._colLayerPos[0] + (pos.pageX - this._colPagePos[0]); + var y = this._colLayerPos[1] + (pos.pageY - this._colPagePos[1]); + var w = this.elem.width(); + var h = this.elem.height(); - e.stop(); - } - }).extend(jindo.Component); + x = Math.max(Math.min(x, w), 0); + y = Math.max(Math.min(y, h), 0); + + if (this.huePanel) { + hsv.s = hsv[1] = x / w * 100; + hsv.v = hsv[2] = (h - y) / h * 100; + } else { + hsv.h = y / h * 360; + + var hw = w / 2; + + if (x < hw) { + hsv.s = x / hw * 100; + hsv.v = 100; + } else { + hsv.s = 100; + hsv.v = (w - x) / hw * 100; + } + } + + this.hsv(hsv); + + e.stop(); + }, + _onDownHue: function (e) { + if (!e.mouse().left) { + return false; + } + + var pos = e.pos(); + + this._huePagePos = [pos.pageX, pos.pageY]; + this._hueLayerPos = [pos.layerX, pos.layerY]; + + this._onUpHueFn.attach(document, "mouseup"); + this._onMoveHueFn.attach(document, "mousemove"); + + this._onMoveHue(e); + }, + _onUpHue: function (e) { + this._onUpHueFn.detach(document, "mouseup"); + this._onMoveHueFn.detach(document, "mousemove"); + }, + _onMoveHue: function (e) { + var hsv = this._hsvColor; + var pos = e.pos(); + var cur = 0, len = 0; + var x = this._hueLayerPos[0] + (pos.pageX - this._huePagePos[0]); + var y = this._hueLayerPos[1] + (pos.pageY - this._huePagePos[1]); + + if (this.option().huePanelType == "vertical") { + cur = y; + len = this.huePanel.height(); + } else { + cur = x; + len = this.huePanel.width(); + } + + hsv.h = hsv[0] = (Math.min(Math.max(cur, 0), len) / len * 360) % 360; + + this.hsv(hsv); + + e.stop(); + } +}).extend(jindo.Component); //{ /** @@ -11309,404 +11313,404 @@ function fontProperty(fontId, fontName, defaultSize, fontURL, fontCSSURL){ * @name hp_SE2M_Accessibility.js */ nhn.husky.SE2M_Accessibility = jindo.$Class({ - name : "SE2M_Accessibility", - - /* - * elAppContainer : mandatory - * sLocale, sEditorType : optional - */ - $init: function(elAppContainer, sLocale, sEditorType) { - this._assignHTMLElements(elAppContainer); - - if(!!sLocale){ - this.sLang = sLocale; - } - - if(!!sEditorType){ - this.sType = sEditorType; - } - }, + name: "SE2M_Accessibility", - _assignHTMLElements : function(elAppContainer){ - this.elHelpPopupLayer = jindo.$$.getSingle("DIV.se2_accessibility", elAppContainer); - this.welHelpPopupLayer = jindo.$Element(this.elHelpPopupLayer); + /* + * elAppContainer : mandatory + * sLocale, sEditorType : optional + */ + $init: function (elAppContainer, sLocale, sEditorType) { + this._assignHTMLElements(elAppContainer); - //close buttons - this.oCloseButton = jindo.$$.getSingle("BUTTON.se2_close", this.elHelpPopupLayer); - this.oCloseButton2 = jindo.$$.getSingle("BUTTON.se2_close2", this.elHelpPopupLayer); - - this.nDefaultTop = 150; - - // [SMARTEDITORSUS-1594] 포커스 탐색에 사용하기 위해 할당 - this.elAppContainer = elAppContainer; - // --[SMARTEDITORSUS-1594] - }, - - $ON_MSG_APP_READY : function(){ - this.htAccessOption = nhn.husky.SE2M_Configuration.SE2M_Accessibility || {}; - this.oApp.exec("REGISTER_HOTKEY", ["alt+F10", "FOCUS_TOOLBAR_AREA", []]); - this.oApp.exec("REGISTER_HOTKEY", ["alt+COMMA", "FOCUS_BEFORE_ELEMENT", []]); - this.oApp.exec("REGISTER_HOTKEY", ["alt+PERIOD", "FOCUS_NEXT_ELEMENT", []]); + if (!!sLocale) { + this.sLang = sLocale; + } - if (this.sLang && this.sLang !== 'ko_KR') { - //do nothing - return; + if (!!sEditorType) { + this.sType = sEditorType; + } + }, + + _assignHTMLElements: function (elAppContainer) { + this.elHelpPopupLayer = jindo.$$.getSingle("DIV.se2_accessibility", elAppContainer); + this.welHelpPopupLayer = jindo.$Element(this.elHelpPopupLayer); + + //close buttons + this.oCloseButton = jindo.$$.getSingle("BUTTON.se2_close", this.elHelpPopupLayer); + this.oCloseButton2 = jindo.$$.getSingle("BUTTON.se2_close2", this.elHelpPopupLayer); + + this.nDefaultTop = 150; + + // [SMARTEDITORSUS-1594] 포커스 탐색에 사용하기 위해 할당 + this.elAppContainer = elAppContainer; + // --[SMARTEDITORSUS-1594] + }, + + $ON_MSG_APP_READY: function () { + this.htAccessOption = nhn.husky.SE2M_Configuration.SE2M_Accessibility || {}; + this.oApp.exec("REGISTER_HOTKEY", ["alt+F10", "FOCUS_TOOLBAR_AREA", []]); + this.oApp.exec("REGISTER_HOTKEY", ["alt+COMMA", "FOCUS_BEFORE_ELEMENT", []]); + this.oApp.exec("REGISTER_HOTKEY", ["alt+PERIOD", "FOCUS_NEXT_ELEMENT", []]); + + if (this.sLang && this.sLang !== 'ko_KR') { + //do nothing + return; + } else { + this.oApp.exec("REGISTER_HOTKEY", ["alt+0", "OPEN_HELP_POPUP", []]); + + //[SMARTEDITORSUS-1327] IE 7/8에서 ALT+0으로 팝업 띄우고 esc클릭시 팝업창 닫히게 하려면 아래 부분 꼭 필요함. (target은 document가 되어야 함!) + this.oApp.exec("REGISTER_HOTKEY", ["esc", "CLOSE_HELP_POPUP", [], document]); + } + + //[SMARTEDITORSUS-1353] + if (this.htAccessOption.sTitleElementId) { + this.oApp.registerBrowserEvent(document.getElementById(this.htAccessOption.sTitleElementId), "keydown", "MOVE_TO_EDITAREA", []); + } + }, + + $ON_MOVE_TO_EDITAREA: function (weEvent) { + var TAB_KEY_CODE = 9; + if (weEvent.key().keyCode == TAB_KEY_CODE) { + if (weEvent.key().shift) { return; } + this.oApp.delayedExec("FOCUS", [], 0); + } + }, + + $LOCAL_BEFORE_FIRST: function (sMsg) { + jindo.$Fn(jindo.$Fn(this.oApp.exec, this.oApp).bind("CLOSE_HELP_POPUP", [this.oCloseButton]), this).attach(this.oCloseButton, "click"); + jindo.$Fn(jindo.$Fn(this.oApp.exec, this.oApp).bind("CLOSE_HELP_POPUP", [this.oCloseButton2]), this).attach(this.oCloseButton2, "click"); + + //레이어의 이동 범위 설정. + var elIframe = this.oApp.getWYSIWYGWindow().frameElement; + this.htOffsetPos = jindo.$Element(elIframe).offset(); + this.nEditorWidth = elIframe.offsetWidth; + + this.htInitialPos = this.welHelpPopupLayer.offset(); + var htScrollXY = this.oApp.oUtils.getScrollXY(); + + this.nLayerWidth = 590; + this.nLayerHeight = 480; + + this.htTopLeftCorner = { x: parseInt(this.htOffsetPos.left, 10), y: parseInt(this.htOffsetPos.top, 10) }; + //[css markup] left:11 top:74로 되어 있음 + }, + + /** + * [SMARTEDITORSUS-1594] + * SE2M_Configuration_General에서 포커스를 이동할 에디터 영역 이후의 엘레먼트를 지정해 두었다면, 설정값을 따른다. + * 지정하지 않았거나 빈 String이라면, elAppContainer를 기준으로 자동 탐색한다. + * */ + $ON_FOCUS_NEXT_ELEMENT: function () { + // 포커스 캐싱 + this._currentNextFocusElement = null; // 새로운 포커스 이동이 발생할 때마다 캐싱 초기화 + + if (this.htAccessOption.sNextElementId) { + this._currentNextFocusElement = document.getElementById(this.htAccessOption.sNextElementId); + } else { + this._currentNextFocusElement = this._findNextFocusElement(this.elAppContainer); + } + + if (this._currentNextFocusElement) { + window.focus(); // [SMARTEDITORSUS-1360] IE7에서는 element에 대한 focus를 주기 위해 선행되어야 한다. + this._currentNextFocusElement.focus(); + } else if (parent && parent.nhn && parent.nhn.husky && parent.nhn.husky.EZCreator && parent.nhn.husky.EZCreator.elIFrame) { + parent.focus(); + if (this._currentNextFocusElement = this._findNextFocusElement(parent.nhn.husky.EZCreator.elIFrame)) { + this._currentNextFocusElement.focus(); + } + } + }, + + /** + * [SMARTEDITORSUS-1594] DIV#smart_editor2 다음 요소에서 가장 가까운 포커스용 태그를 탐색 + * */ + _findNextFocusElement: function (targetElement) { + var target = null; + + var el = targetElement.nextSibling; + + while (el) { + if (el.nodeType !== 1) { // Element Node만을 대상으로 한다. + // 대상 노드 대신 nextSibling을 찾되, 부모를 거슬러 올라갈 수도 있다. + // document.body까지 거슬러 올라가게 되면 탐색 종료 + el = this._switchToSiblingOrNothing(el); + if (!el) { + break; } else { - this.oApp.exec("REGISTER_HOTKEY", ["alt+0", "OPEN_HELP_POPUP", []]); - - //[SMARTEDITORSUS-1327] IE 7/8에서 ALT+0으로 팝업 띄우고 esc클릭시 팝업창 닫히게 하려면 아래 부분 꼭 필요함. (target은 document가 되어야 함!) - this.oApp.exec("REGISTER_HOTKEY", ["esc", "CLOSE_HELP_POPUP", [], document]); - } - - //[SMARTEDITORSUS-1353] - if (this.htAccessOption.sTitleElementId) { - this.oApp.registerBrowserEvent(document.getElementById(this.htAccessOption.sTitleElementId), "keydown", "MOVE_TO_EDITAREA", []); - } - }, - - $ON_MOVE_TO_EDITAREA : function(weEvent) { - var TAB_KEY_CODE = 9; - if (weEvent.key().keyCode == TAB_KEY_CODE) { - if(weEvent.key().shift) {return;} - this.oApp.delayedExec("FOCUS", [], 0); - } - }, - - $LOCAL_BEFORE_FIRST : function(sMsg){ - jindo.$Fn(jindo.$Fn(this.oApp.exec, this.oApp).bind("CLOSE_HELP_POPUP", [this.oCloseButton]), this).attach(this.oCloseButton, "click"); - jindo.$Fn(jindo.$Fn(this.oApp.exec, this.oApp).bind("CLOSE_HELP_POPUP", [this.oCloseButton2]), this).attach(this.oCloseButton2, "click"); - - //레이어의 이동 범위 설정. - var elIframe = this.oApp.getWYSIWYGWindow().frameElement; - this.htOffsetPos = jindo.$Element(elIframe).offset(); - this.nEditorWidth = elIframe.offsetWidth; - - this.htInitialPos = this.welHelpPopupLayer.offset(); - var htScrollXY = this.oApp.oUtils.getScrollXY(); - - this.nLayerWidth = 590; - this.nLayerHeight = 480; - - this.htTopLeftCorner = {x:parseInt(this.htOffsetPos.left, 10), y:parseInt(this.htOffsetPos.top, 10)}; - //[css markup] left:11 top:74로 되어 있음 - }, - - /** - * [SMARTEDITORSUS-1594] - * SE2M_Configuration_General에서 포커스를 이동할 에디터 영역 이후의 엘레먼트를 지정해 두었다면, 설정값을 따른다. - * 지정하지 않았거나 빈 String이라면, elAppContainer를 기준으로 자동 탐색한다. - * */ - $ON_FOCUS_NEXT_ELEMENT : function() { - // 포커스 캐싱 - this._currentNextFocusElement = null; // 새로운 포커스 이동이 발생할 때마다 캐싱 초기화 - - if(this.htAccessOption.sNextElementId){ - this._currentNextFocusElement = document.getElementById(this.htAccessOption.sNextElementId); - }else{ - this._currentNextFocusElement = this._findNextFocusElement(this.elAppContainer); - } - - if(this._currentNextFocusElement){ - window.focus(); // [SMARTEDITORSUS-1360] IE7에서는 element에 대한 focus를 주기 위해 선행되어야 한다. - this._currentNextFocusElement.focus(); - }else if(parent && parent.nhn && parent.nhn.husky && parent.nhn.husky.EZCreator && parent.nhn.husky.EZCreator.elIFrame){ - parent.focus(); - if(this._currentNextFocusElement = this._findNextFocusElement(parent.nhn.husky.EZCreator.elIFrame)){ - this._currentNextFocusElement.focus(); - } - } - }, - - /** - * [SMARTEDITORSUS-1594] DIV#smart_editor2 다음 요소에서 가장 가까운 포커스용 태그를 탐색 - * */ - _findNextFocusElement : function(targetElement){ - var target = null; - - var el = targetElement.nextSibling; - - while(el){ - if(el.nodeType !== 1){ // Element Node만을 대상으로 한다. - // 대상 노드 대신 nextSibling을 찾되, 부모를 거슬러 올라갈 수도 있다. - // document.body까지 거슬러 올라가게 되면 탐색 종료 - el = this._switchToSiblingOrNothing(el); - if(!el){ - break; - }else{ - continue; - } - } - - // 대상 노드를 기준으로, 전위순회로 조건에 부합하는 노드 탐색 - this._recursivePreorderTraversalFilter(el, this._isFocusTag); - - if(this._nextFocusElement){ - target = this._nextFocusElement; - - // 탐색에 사용했던 변수 초기화 - this._bStopFindingNextElement = false; - this._nextFocusElement = null; - - break; - }else{ - // 대상 노드 대신 nextSibling을 찾되, 부모를 거슬러 올라갈 수도 있다. - // document.body까지 거슬러 올라가게 되면 탐색 종료 - el = this._switchToSiblingOrNothing(el); - if(!el){ - break; - } - } - } - - // target이 존재하지 않으면 null 반환 - return target; - }, - - /** - * [SMARTEDITORSUS-1594] 대상 노드를 기준으로 하여, nextSibling 또는 previousSibling을 찾는다. - * nextSibling 또는 previousSibling이 없다면, - * 부모를 거슬러 올라가면서 첫 nextSibling 또는 previousSibling을 찾는다. - * document의 body까지 올라가도 nextSibling 또는 previousSibling이 나타나지 않는다면 - * 탐색 대상으로 null을 반환한다. - * @param {NodeElement} 대상 노드 (주의:NodeElement에 대한 null 체크 안함) - * @param {Boolean} 생략하거나 false이면 nextSibling을 찾고, true이면 previousSibling을 찾는다. - * */ - _switchToSiblingOrNothing : function(targetElement, isPreviousOrdered){ - var el = targetElement; - - if(isPreviousOrdered){ - if(el.previousSibling){ - el = el.previousSibling; - }else{ - // 형제가 없다면 부모를 거슬러 올라가면서 탐색 - - // 이 루프의 종료 조건 - // 1. 부모를 거슬러 올라가다가 el이 document.body가 되는 시점 - // - 더 이상 previousSibling을 탐색할 수 없음 - // 2. el이 부모로 대체된 뒤 previousSibling이 존재하는 경우 - while(el.nodeName.toUpperCase() != "BODY" && !el.previousSibling){ - el = el.parentNode; - } - - if(el.nodeName.toUpperCase() == "BODY"){ - el = null; - }else{ - el = el.previousSibling; - } - } - }else{ - if(el.nextSibling){ - el = el.nextSibling; - }else{ - // 형제가 없다면 부모를 거슬러 올라가면서 탐색 - - // 이 루프의 종료 조건 - // 1. 부모를 거슬러 올라가다가 el이 document.body가 되는 시점 - // - 더 이상 nextSibling을 탐색할 수 없음 - // 2. el이 부모로 대체된 뒤 nextSibling이 존재하는 경우 - while(el.nodeName.toUpperCase() != "BODY" && !el.nextSibling){ - el = el.parentNode; - } - - if(el.nodeName.toUpperCase() == "BODY"){ - el = null; - }else{ - el = el.nextSibling; - } - } - } - - return el; - }, - - /** - * [SMARTEDITORSUS-1594] 대상 노드를 기준으로 하는 트리를 전위순회를 거쳐, 필터 조건에 부합하는 첫 노드를 찾는다. - * @param {NodeElement} 탐색하려는 트리의 루트 노드 - * @param {Function} 필터 조건으로 사용할 함수 - * @param {Boolean} 생략하거나 false이면 순수 전위순회(루트 - 좌측 - 우측 순)로 탐색하고, true이면 반대 방향의 전위순회(루트 - 우측 - 좌측)로 탐색한다. - * */ - _recursivePreorderTraversalFilter : function(node, filterFunction, isReversed){ - var self = this; - - // 현재 노드를 기준으로 필터링 - var _bStopFindingNextElement = filterFunction.apply(node); - - if(_bStopFindingNextElement){ - // 최초로 포커스 태그를 찾는다면 탐색 중단용 flag 변경 - self._bStopFindingNextElement = true; - - if(isReversed){ - self._previousFocusElement = node; - }else{ - self._nextFocusElement = node; - } - - return; - }else{ - // 필터링 조건에 부합하지 않는다면, 자식들을 기준으로 반복하게 된다. - if(isReversed){ - for(var len = node.childNodes.length, i = len - 1; i >= 0; i--){ - self._recursivePreorderTraversalFilter(node.childNodes[i], filterFunction, true); - if(!!this._bStopFindingNextElement){ - break; - } - } - }else{ - for(var i=0, len = node.childNodes.length; i < len; i++){ - self._recursivePreorderTraversalFilter(node.childNodes[i], filterFunction); - if(!!this._bStopFindingNextElement){ - break; - } - } - } - } - }, - - /** - * [SMARTEDITORSUS-1594] 필터 함수로, 이 노드가 tab 키로 포커스를 이동하는 태그에 해당하는지 확인한다. - * */ - _isFocusTag : function(){ - var self = this; - - // tab 키로 포커스를 잡아주는 태그 목록 - var aFocusTagViaTabKey = ["A", "BUTTON", "INPUT", "TEXTAREA"]; - - // 포커스 태그가 현재 노드에 존재하는지 확인하기 위한 flag - var bFocusTagExists = false; - - for(var i = 0, len = aFocusTagViaTabKey.length; i < len; i++){ - if(self.nodeType === 1 && self.nodeName && self.nodeName.toUpperCase() == aFocusTagViaTabKey[i] && !self.disabled && jindo.$Element(self).visible()){ - bFocusTagExists = true; - break; - } - } - - return bFocusTagExists; - }, - - /** - * [SMARTEDITORSUS-1594] - * SE2M_Configuration_General에서 포커스를 이동할 에디터 영역 이전의 엘레먼트를 지정해 두었다면, 설정값을 따른다. - * 지정하지 않았거나 빈 String이라면, elAppContainer를 기준으로 자동 탐색한다. - * */ - $ON_FOCUS_BEFORE_ELEMENT : function() { - // 포커스 캐싱 - this._currentPreviousFocusElement = null; // 새로운 포커스 이동이 발생할 때마다 캐싱 초기화 - - if(this.htAccessOption.sBeforeElementId){ - this._currentPreviousFocusElement = document.getElementById(this.htAccessOption.sBeforeElementId); - }else{ - this._currentPreviousFocusElement = this._findPreviousFocusElement(this.elAppContainer); // 삽입될 대상 - } - - if(this._currentPreviousFocusElement){ - window.focus(); // [SMARTEDITORSUS-1360] IE7에서는 element에 대한 focus를 주기 위해 선행되어야 한다. - this._currentPreviousFocusElement.focus(); - }else if(parent && parent.nhn && parent.nhn.husky && parent.nhn.husky.EZCreator && parent.nhn.husky.EZCreator.elIFrame){ - parent.focus(); - if(this._currentPreviousFocusElement = this._findPreviousFocusElement(parent.nhn.husky.EZCreator.elIFrame)){ - this._currentPreviousFocusElement.focus(); - } - } - }, - - /** - * [SMARTEDITORSUS-1594] DIV#smart_editor2 이전 요소에서 가장 가까운 포커스용 태그를 탐색 - * */ - _findPreviousFocusElement : function(targetElement){ - var target = null; - - var el = targetElement.previousSibling; - - while(el){ - if(el.nodeType !== 1){ // Element Node만을 대상으로 한다. - // 대상 노드 대신 previousSibling을 찾되, 부모를 거슬러 올라갈 수도 있다. - // document.body까지 거슬러 올라가게 되면 탐색 종료 - el = this._switchToSiblingOrNothing(el, /*isReversed*/true); - if(!el){ - break; - }else{ - continue; - } - } - - // 대상 노드를 기준으로, 역 전위순회로 조건에 부합하는 노드 탐색 - this._recursivePreorderTraversalFilter(el, this._isFocusTag, true); - - if(this._previousFocusElement){ - target = this._previousFocusElement; - - // 탐색에 사용했던 변수 초기화 - this._bStopFindingNextElement = false; - this._previousFocusElement = null; - - break; - }else{ - // 대상 노드 대신 previousSibling을 찾되, 부모를 거슬러 올라갈 수도 있다. - // document.body까지 거슬러 올라가게 되면 탐색 종료 - el = this._switchToSiblingOrNothing(el, /*isReversed*/true); - if(!el){ - break; - } - } - } - - // target이 존재하지 않으면 null 반환 - return target; - }, - - $ON_FOCUS_TOOLBAR_AREA : function(){ - this.oButton = jindo.$$.getSingle("BUTTON.se2_font_family", this.elAppContainer); - if(this.oButton && !this.oButton.disabled){ // [SMARTEDITORSUS-1369] IE9이하에서 disabled 요소에 포커스를 주면 오류 발생 - window.focus(); - this.oButton.focus(); - } - }, - - $ON_OPEN_HELP_POPUP : function() { - this.oApp.exec("DISABLE_ALL_UI", [{aExceptions: ["se2_accessibility"]}]); - this.oApp.exec("SHOW_EDITING_AREA_COVER"); - this.oApp.exec("SELECT_UI", ["se2_accessibility"]); - - //아래 코드 없어야 블로그에서도 동일한 위치에 팝업 뜸.. - //this.elHelpPopupLayer.style.top = this.nDefaultTop+"px"; - - this.nCalcX = this.htTopLeftCorner.x + this.oApp.getEditingAreaWidth() - this.nLayerWidth; - this.nCalcY = this.htTopLeftCorner.y - 30; // 블로그버전이 아닌 경우 에디터영역을 벗어나는 문제가 있기 때문에 기본툴바(30px) 크기만큼 올려줌 - - this.oApp.exec("SHOW_DIALOG_LAYER", [this.elHelpPopupLayer, { - elHandle: this.elTitle, - nMinX : this.htTopLeftCorner.x + 0, - nMinY : this.nDefaultTop + 77, - nMaxX : this.nCalcX, - nMaxY : this.nCalcY - }]); - - // offset (nTop:Numeric, nLeft:Numeric) - this.welHelpPopupLayer.offset(this.nCalcY, (this.nCalcX)/2); - - //[SMARTEDITORSUS-1327] IE에서 포커스 이슈로 IE에 대해서만 window.focus실행함. - if(jindo.$Agent().navigator().ie) { - window.focus(); + continue; } - - var self = this; - setTimeout(function(){ - try{ - self.oCloseButton2.focus(); - }catch(e){ - } - },200); - }, - - $ON_CLOSE_HELP_POPUP : function() { - this.oApp.exec("ENABLE_ALL_UI"); // 모든 UI 활성화. - this.oApp.exec("DESELECT_UI", ["helpPopup"]); - this.oApp.exec("HIDE_ALL_DIALOG_LAYER", []); - this.oApp.exec("HIDE_EDITING_AREA_COVER"); // 편집 영역 활성화. - - this.oApp.exec("FOCUS"); - } + } + + // 대상 노드를 기준으로, 전위순회로 조건에 부합하는 노드 탐색 + this._recursivePreorderTraversalFilter(el, this._isFocusTag); + + if (this._nextFocusElement) { + target = this._nextFocusElement; + + // 탐색에 사용했던 변수 초기화 + this._bStopFindingNextElement = false; + this._nextFocusElement = null; + + break; + } else { + // 대상 노드 대신 nextSibling을 찾되, 부모를 거슬러 올라갈 수도 있다. + // document.body까지 거슬러 올라가게 되면 탐색 종료 + el = this._switchToSiblingOrNothing(el); + if (!el) { + break; + } + } + } + + // target이 존재하지 않으면 null 반환 + return target; + }, + + /** + * [SMARTEDITORSUS-1594] 대상 노드를 기준으로 하여, nextSibling 또는 previousSibling을 찾는다. + * nextSibling 또는 previousSibling이 없다면, + * 부모를 거슬러 올라가면서 첫 nextSibling 또는 previousSibling을 찾는다. + * document의 body까지 올라가도 nextSibling 또는 previousSibling이 나타나지 않는다면 + * 탐색 대상으로 null을 반환한다. + * @param {NodeElement} 대상 노드 (주의:NodeElement에 대한 null 체크 안함) + * @param {Boolean} 생략하거나 false이면 nextSibling을 찾고, true이면 previousSibling을 찾는다. + * */ + _switchToSiblingOrNothing: function (targetElement, isPreviousOrdered) { + var el = targetElement; + + if (isPreviousOrdered) { + if (el.previousSibling) { + el = el.previousSibling; + } else { + // 형제가 없다면 부모를 거슬러 올라가면서 탐색 + + // 이 루프의 종료 조건 + // 1. 부모를 거슬러 올라가다가 el이 document.body가 되는 시점 + // - 더 이상 previousSibling을 탐색할 수 없음 + // 2. el이 부모로 대체된 뒤 previousSibling이 존재하는 경우 + while (el.nodeName.toUpperCase() != "BODY" && !el.previousSibling) { + el = el.parentNode; + } + + if (el.nodeName.toUpperCase() == "BODY") { + el = null; + } else { + el = el.previousSibling; + } + } + } else { + if (el.nextSibling) { + el = el.nextSibling; + } else { + // 형제가 없다면 부모를 거슬러 올라가면서 탐색 + + // 이 루프의 종료 조건 + // 1. 부모를 거슬러 올라가다가 el이 document.body가 되는 시점 + // - 더 이상 nextSibling을 탐색할 수 없음 + // 2. el이 부모로 대체된 뒤 nextSibling이 존재하는 경우 + while (el.nodeName.toUpperCase() != "BODY" && !el.nextSibling) { + el = el.parentNode; + } + + if (el.nodeName.toUpperCase() == "BODY") { + el = null; + } else { + el = el.nextSibling; + } + } + } + + return el; + }, + + /** + * [SMARTEDITORSUS-1594] 대상 노드를 기준으로 하는 트리를 전위순회를 거쳐, 필터 조건에 부합하는 첫 노드를 찾는다. + * @param {NodeElement} 탐색하려는 트리의 루트 노드 + * @param {Function} 필터 조건으로 사용할 함수 + * @param {Boolean} 생략하거나 false이면 순수 전위순회(루트 - 좌측 - 우측 순)로 탐색하고, true이면 반대 방향의 전위순회(루트 - 우측 - 좌측)로 탐색한다. + * */ + _recursivePreorderTraversalFilter: function (node, filterFunction, isReversed) { + var self = this; + + // 현재 노드를 기준으로 필터링 + var _bStopFindingNextElement = filterFunction.apply(node); + + if (_bStopFindingNextElement) { + // 최초로 포커스 태그를 찾는다면 탐색 중단용 flag 변경 + self._bStopFindingNextElement = true; + + if (isReversed) { + self._previousFocusElement = node; + } else { + self._nextFocusElement = node; + } + + return; + } else { + // 필터링 조건에 부합하지 않는다면, 자식들을 기준으로 반복하게 된다. + if (isReversed) { + for (var len = node.childNodes.length, i = len - 1; i >= 0; i--) { + self._recursivePreorderTraversalFilter(node.childNodes[i], filterFunction, true); + if (!!this._bStopFindingNextElement) { + break; + } + } + } else { + for (var i = 0, len = node.childNodes.length; i < len; i++) { + self._recursivePreorderTraversalFilter(node.childNodes[i], filterFunction); + if (!!this._bStopFindingNextElement) { + break; + } + } + } + } + }, + + /** + * [SMARTEDITORSUS-1594] 필터 함수로, 이 노드가 tab 키로 포커스를 이동하는 태그에 해당하는지 확인한다. + * */ + _isFocusTag: function () { + var self = this; + + // tab 키로 포커스를 잡아주는 태그 목록 + var aFocusTagViaTabKey = ["A", "BUTTON", "INPUT", "TEXTAREA"]; + + // 포커스 태그가 현재 노드에 존재하는지 확인하기 위한 flag + var bFocusTagExists = false; + + for (var i = 0, len = aFocusTagViaTabKey.length; i < len; i++) { + if (self.nodeType === 1 && self.nodeName && self.nodeName.toUpperCase() == aFocusTagViaTabKey[i] && !self.disabled && jindo.$Element(self).visible()) { + bFocusTagExists = true; + break; + } + } + + return bFocusTagExists; + }, + + /** + * [SMARTEDITORSUS-1594] + * SE2M_Configuration_General에서 포커스를 이동할 에디터 영역 이전의 엘레먼트를 지정해 두었다면, 설정값을 따른다. + * 지정하지 않았거나 빈 String이라면, elAppContainer를 기준으로 자동 탐색한다. + * */ + $ON_FOCUS_BEFORE_ELEMENT: function () { + // 포커스 캐싱 + this._currentPreviousFocusElement = null; // 새로운 포커스 이동이 발생할 때마다 캐싱 초기화 + + if (this.htAccessOption.sBeforeElementId) { + this._currentPreviousFocusElement = document.getElementById(this.htAccessOption.sBeforeElementId); + } else { + this._currentPreviousFocusElement = this._findPreviousFocusElement(this.elAppContainer); // 삽입될 대상 + } + + if (this._currentPreviousFocusElement) { + window.focus(); // [SMARTEDITORSUS-1360] IE7에서는 element에 대한 focus를 주기 위해 선행되어야 한다. + this._currentPreviousFocusElement.focus(); + } else if (parent && parent.nhn && parent.nhn.husky && parent.nhn.husky.EZCreator && parent.nhn.husky.EZCreator.elIFrame) { + parent.focus(); + if (this._currentPreviousFocusElement = this._findPreviousFocusElement(parent.nhn.husky.EZCreator.elIFrame)) { + this._currentPreviousFocusElement.focus(); + } + } + }, + + /** + * [SMARTEDITORSUS-1594] DIV#smart_editor2 이전 요소에서 가장 가까운 포커스용 태그를 탐색 + * */ + _findPreviousFocusElement: function (targetElement) { + var target = null; + + var el = targetElement.previousSibling; + + while (el) { + if (el.nodeType !== 1) { // Element Node만을 대상으로 한다. + // 대상 노드 대신 previousSibling을 찾되, 부모를 거슬러 올라갈 수도 있다. + // document.body까지 거슬러 올라가게 되면 탐색 종료 + el = this._switchToSiblingOrNothing(el, /*isReversed*/true); + if (!el) { + break; + } else { + continue; + } + } + + // 대상 노드를 기준으로, 역 전위순회로 조건에 부합하는 노드 탐색 + this._recursivePreorderTraversalFilter(el, this._isFocusTag, true); + + if (this._previousFocusElement) { + target = this._previousFocusElement; + + // 탐색에 사용했던 변수 초기화 + this._bStopFindingNextElement = false; + this._previousFocusElement = null; + + break; + } else { + // 대상 노드 대신 previousSibling을 찾되, 부모를 거슬러 올라갈 수도 있다. + // document.body까지 거슬러 올라가게 되면 탐색 종료 + el = this._switchToSiblingOrNothing(el, /*isReversed*/true); + if (!el) { + break; + } + } + } + + // target이 존재하지 않으면 null 반환 + return target; + }, + + $ON_FOCUS_TOOLBAR_AREA: function () { + this.oButton = jindo.$$.getSingle("BUTTON.se2_font_family", this.elAppContainer); + if (this.oButton && !this.oButton.disabled) { // [SMARTEDITORSUS-1369] IE9이하에서 disabled 요소에 포커스를 주면 오류 발생 + window.focus(); + this.oButton.focus(); + } + }, + + $ON_OPEN_HELP_POPUP: function () { + this.oApp.exec("DISABLE_ALL_UI", [{ aExceptions: ["se2_accessibility"] }]); + this.oApp.exec("SHOW_EDITING_AREA_COVER"); + this.oApp.exec("SELECT_UI", ["se2_accessibility"]); + + //아래 코드 없어야 블로그에서도 동일한 위치에 팝업 뜸.. + //this.elHelpPopupLayer.style.top = this.nDefaultTop+"px"; + + this.nCalcX = this.htTopLeftCorner.x + this.oApp.getEditingAreaWidth() - this.nLayerWidth; + this.nCalcY = this.htTopLeftCorner.y - 30; // 블로그버전이 아닌 경우 에디터영역을 벗어나는 문제가 있기 때문에 기본툴바(30px) 크기만큼 올려줌 + + this.oApp.exec("SHOW_DIALOG_LAYER", [this.elHelpPopupLayer, { + elHandle: this.elTitle, + nMinX: this.htTopLeftCorner.x + 0, + nMinY: this.nDefaultTop + 77, + nMaxX: this.nCalcX, + nMaxY: this.nCalcY + }]); + + // offset (nTop:Numeric, nLeft:Numeric) + this.welHelpPopupLayer.offset(this.nCalcY, (this.nCalcX) / 2); + + //[SMARTEDITORSUS-1327] IE에서 포커스 이슈로 IE에 대해서만 window.focus실행함. + if (jindo.$Agent().navigator().ie) { + window.focus(); + } + + var self = this; + setTimeout(function () { + try { + self.oCloseButton2.focus(); + } catch (e) { + } + }, 200); + }, + + $ON_CLOSE_HELP_POPUP: function () { + this.oApp.exec("ENABLE_ALL_UI"); // 모든 UI 활성화. + this.oApp.exec("DESELECT_UI", ["helpPopup"]); + this.oApp.exec("HIDE_ALL_DIALOG_LAYER", []); + this.oApp.exec("HIDE_EDITING_AREA_COVER"); // 편집 영역 활성화. + + this.oApp.exec("FOCUS"); + } }); //} @@ -11716,426 +11720,426 @@ nhn.husky.SE2M_Accessibility = jindo.$Class({ * @required HuskyRangeManager */ nhn.husky.SE2M_SCharacter = jindo.$Class({ - name : "SE2M_SCharacter", + name: "SE2M_SCharacter", - $ON_MSG_APP_READY : function(){ - this.oApp.exec("REGISTER_UI_EVENT", ["sCharacter", "click", "TOGGLE_SCHARACTER_LAYER"]); - this.oApp.registerLazyMessage(["TOGGLE_SCHARACTER_LAYER"], ["hp_SE2M_SCharacter$Lazy.js"]); - } + $ON_MSG_APP_READY: function () { + this.oApp.exec("REGISTER_UI_EVENT", ["sCharacter", "click", "TOGGLE_SCHARACTER_LAYER"]); + this.oApp.registerLazyMessage(["TOGGLE_SCHARACTER_LAYER"], ["hp_SE2M_SCharacter$Lazy.js"]); + } }); /** * @fileOverview This file contains Husky plugin that takes care of the operations related to Find/Replace * @name hp_SE2M_FindReplacePlugin.js */ nhn.husky.SE2M_FindReplacePlugin = jindo.$Class({ - name : "SE2M_FindReplacePlugin", - oEditingWindow : null, - oFindReplace : null, - bFindMode : true, - bLayerShown : false, + name: "SE2M_FindReplacePlugin", + oEditingWindow: null, + oFindReplace: null, + bFindMode: true, + bLayerShown: false, - $init : function(){ - this.nDefaultTop = 20; - }, - - $ON_MSG_APP_READY : function(){ - // the right document will be available only when the src is completely loaded - this.oEditingWindow = this.oApp.getWYSIWYGWindow(); + $init: function () { + this.nDefaultTop = 20; + }, - // [SMARTEDITORSUS-2260] 메일 > Mac에서 ctrl 조합 단축키 모두 meta 조합으로 변경 - if (jindo.$Agent().os().mac) { - this.oApp.exec("REGISTER_HOTKEY", ["meta+f", "SHOW_FIND_LAYER", []]); - this.oApp.exec("REGISTER_HOTKEY", ["meta+h", "SHOW_REPLACE_LAYER", []]); - } else { - this.oApp.exec("REGISTER_HOTKEY", ["ctrl+f", "SHOW_FIND_LAYER", []]); - this.oApp.exec("REGISTER_HOTKEY", ["ctrl+h", "SHOW_REPLACE_LAYER", []]); - } + $ON_MSG_APP_READY: function () { + // the right document will be available only when the src is completely loaded + this.oEditingWindow = this.oApp.getWYSIWYGWindow(); - this.oApp.exec("REGISTER_UI_EVENT", ["findAndReplace", "click", "TOGGLE_FIND_REPLACE_LAYER"]); - this.oApp.registerLazyMessage(["TOGGLE_FIND_REPLACE_LAYER","SHOW_FIND_LAYER","SHOW_REPLACE_LAYER","SHOW_FIND_REPLACE_LAYER"], ["hp_SE2M_FindReplacePlugin$Lazy.js","N_FindReplace.js"]); - }, - - $ON_SHOW_ACTIVE_LAYER : function(){ - this.oApp.exec("HIDE_DIALOG_LAYER", [this.elDropdownLayer]); - } + // [SMARTEDITORSUS-2260] 메일 > Mac에서 ctrl 조합 단축키 모두 meta 조합으로 변경 + if (jindo.$Agent().os().mac) { + this.oApp.exec("REGISTER_HOTKEY", ["meta+f", "SHOW_FIND_LAYER", []]); + this.oApp.exec("REGISTER_HOTKEY", ["meta+h", "SHOW_REPLACE_LAYER", []]); + } else { + this.oApp.exec("REGISTER_HOTKEY", ["ctrl+f", "SHOW_FIND_LAYER", []]); + this.oApp.exec("REGISTER_HOTKEY", ["ctrl+h", "SHOW_REPLACE_LAYER", []]); + } + + this.oApp.exec("REGISTER_UI_EVENT", ["findAndReplace", "click", "TOGGLE_FIND_REPLACE_LAYER"]); + this.oApp.registerLazyMessage(["TOGGLE_FIND_REPLACE_LAYER", "SHOW_FIND_LAYER", "SHOW_REPLACE_LAYER", "SHOW_FIND_REPLACE_LAYER"], ["hp_SE2M_FindReplacePlugin$Lazy.js", "N_FindReplace.js"]); + }, + + $ON_SHOW_ACTIVE_LAYER: function () { + this.oApp.exec("HIDE_DIALOG_LAYER", [this.elDropdownLayer]); + } }); - /** - * @fileOverview This file contains Husky plugin that takes care of the operations related to quote - * @name hp_SE_Quote.js - * @required SE_EditingArea_WYSIWYG - */ +/** +* @fileOverview This file contains Husky plugin that takes care of the operations related to quote +* @name hp_SE_Quote.js +* @required SE_EditingArea_WYSIWYG +*/ nhn.husky.SE2M_Quote = jindo.$Class({ - name : "SE2M_Quote", - - htQuoteStyles_view : null, + name: "SE2M_Quote", - $init : function(){ - var htConfig = nhn.husky.SE2M_Configuration.Quote || {}; - var sImageBaseURL = htConfig.sImageBaseURL; - - this.nMaxLevel = htConfig.nMaxLevel || 14; - - this.htQuoteStyles_view = {}; - this.htQuoteStyles_view["se2_quote1"] = "_zoom:1;padding:0 8px; margin:0 0 30px 20px; margin-right:15px; border-left:2px solid #cccccc;color:#888888;"; - this.htQuoteStyles_view["se2_quote2"] = "_zoom:1;margin:0 0 30px 13px;padding:0 8px 0 16px;background:url("+sImageBaseURL+"/bg_quote2.gif) 0 3px no-repeat;color:#888888;"; - this.htQuoteStyles_view["se2_quote3"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:1px dashed #cccccc;color:#888888;"; - this.htQuoteStyles_view["se2_quote4"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:1px dashed #66b246;color:#888888;"; - this.htQuoteStyles_view["se2_quote5"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:1px dashed #cccccc;background:url("+sImageBaseURL+"/bg_b1.png) repeat;_background:none;_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+sImageBaseURL+"/bg_b1.png',sizingMethod='scale');color:#888888;"; - this.htQuoteStyles_view["se2_quote6"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:1px solid #e5e5e5;color:#888888;"; - this.htQuoteStyles_view["se2_quote7"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:1px solid #66b246;color:#888888;"; - this.htQuoteStyles_view["se2_quote8"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:1px solid #e5e5e5;background:url("+sImageBaseURL+"/bg_b1.png) repeat;_background:none;_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+sImageBaseURL+"/bg_b1.png',sizingMethod='scale');color:#888888;"; - this.htQuoteStyles_view["se2_quote9"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:2px solid #e5e5e5;color:#888888;"; - this.htQuoteStyles_view["se2_quote10"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:2px solid #e5e5e5;background:url("+sImageBaseURL+"/bg_b1.png) repeat;_background:none;_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='"+sImageBaseURL+"/bg_b1.png',sizingMethod='scale');color:#888888;"; - }, + htQuoteStyles_view: null, - _assignHTMLElements : function(){ - //@ec - this.elDropdownLayer = jindo.$$.getSingle("DIV.husky_seditor_blockquote_layer", this.oApp.htOptions.elAppContainer); - this.aLI = jindo.$$("LI", this.elDropdownLayer); - }, - - $ON_REGISTER_CONVERTERS : function(){ - this.oApp.exec("ADD_CONVERTER", ["DB_TO_IR", jindo.$Fn(function(sContents){ - sContents = sContents.replace(/<(blockquote)[^>]*class=['"]?(se2_quote[0-9]+)['"]?[^>]*>/gi, "<$1 class=$2>"); - return sContents; - }, this).bind()]); - - this.oApp.exec("ADD_CONVERTER", ["IR_TO_DB", jindo.$Fn(function(sContents){ - var htQuoteStyles_view = this.htQuoteStyles_view; - sContents = sContents.replace(/<(blockquote)[^>]*class=['"]?(se2_quote[0-9]+)['"]?[^>]*>/gi, function(sAll, sTag, sClassName){ - return '<'+sTag+' class='+sClassName+' style="'+htQuoteStyles_view[sClassName]+'">'; - }); - return sContents; - }, this).bind()]); + $init: function () { + var htConfig = nhn.husky.SE2M_Configuration.Quote || {}; + var sImageBaseURL = htConfig.sImageBaseURL; - this.htSE1toSE2Map = { - "01" : "1", - "02" : "2", - "03" : "6", - "04" : "8", - "05" : "9", - "07" : "3", - "08" : "5" - }; - // convert SE1's quotes to SE2's - // -> 블로그 개발 쪽에서 처리 하기로 함. - /* - this.oApp.exec("ADD_CONVERTER", ["DB_TO_IR", jindo.$Fn(function(sContents){ - return sContents.replace(/]* class="?vview_quote([0-9]+)"?[^>]*>((?:\s|.)*?)<\/blockquote>/ig, jindo.$Fn(function(m0,sQuoteType,sQuoteContents){ - if (/((?:\s|.)*?)/ig.test(sQuoteContents)){ - if(!this.htSE1toSE2Map[sQuoteType]){ - return m0; - } - - return '
    '+RegExp.$1+'
    '; - }else{ - return ''; - } - }, this).bind()); - }, this).bind()]); - */ - }, + this.nMaxLevel = htConfig.nMaxLevel || 14; - $LOCAL_BEFORE_FIRST : function(){ - this._assignHTMLElements(); + this.htQuoteStyles_view = {}; + this.htQuoteStyles_view["se2_quote1"] = "_zoom:1;padding:0 8px; margin:0 0 30px 20px; margin-right:15px; border-left:2px solid #cccccc;color:#888888;"; + this.htQuoteStyles_view["se2_quote2"] = "_zoom:1;margin:0 0 30px 13px;padding:0 8px 0 16px;background:url(" + sImageBaseURL + "/bg_quote2.gif) 0 3px no-repeat;color:#888888;"; + this.htQuoteStyles_view["se2_quote3"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:1px dashed #cccccc;color:#888888;"; + this.htQuoteStyles_view["se2_quote4"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:1px dashed #66b246;color:#888888;"; + this.htQuoteStyles_view["se2_quote5"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:1px dashed #cccccc;background:url(" + sImageBaseURL + "/bg_b1.png) repeat;_background:none;_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + sImageBaseURL + "/bg_b1.png',sizingMethod='scale');color:#888888;"; + this.htQuoteStyles_view["se2_quote6"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:1px solid #e5e5e5;color:#888888;"; + this.htQuoteStyles_view["se2_quote7"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:1px solid #66b246;color:#888888;"; + this.htQuoteStyles_view["se2_quote8"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:1px solid #e5e5e5;background:url(" + sImageBaseURL + "/bg_b1.png) repeat;_background:none;_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + sImageBaseURL + "/bg_b1.png',sizingMethod='scale');color:#888888;"; + this.htQuoteStyles_view["se2_quote9"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:2px solid #e5e5e5;color:#888888;"; + this.htQuoteStyles_view["se2_quote10"] = "_zoom:1;margin:0 0 30px 0;padding:10px;border:2px solid #e5e5e5;background:url(" + sImageBaseURL + "/bg_b1.png) repeat;_background:none;_filter:progid:DXImageTransform.Microsoft.AlphaImageLoader(src='" + sImageBaseURL + "/bg_b1.png',sizingMethod='scale');color:#888888;"; + }, - this.oApp.registerBrowserEvent(this.elDropdownLayer, "click", "EVENT_SE2_BLOCKQUOTE_LAYER_CLICK", []); - this.oApp.delayedExec("SE2_ATTACH_HOVER_EVENTS", [this.aLI], 0); - }, - - $ON_MSG_APP_READY: function(){ - this.oApp.exec("REGISTER_UI_EVENT", ["quote", "click", "TOGGLE_BLOCKQUOTE_LAYER"]); - this.oApp.registerLazyMessage(["TOGGLE_BLOCKQUOTE_LAYER"], ["hp_SE2M_Quote$Lazy.js"]); - }, + _assignHTMLElements: function () { + //@ec + this.elDropdownLayer = jindo.$$.getSingle("DIV.husky_seditor_blockquote_layer", this.oApp.htOptions.elAppContainer); + this.aLI = jindo.$$("LI", this.elDropdownLayer); + }, - // [SMARTEDITORSUS-209] 인용구 내에 내용이 없을 때 Backspace 로 인용구가 삭제되도록 처리 - $ON_EVENT_EDITING_AREA_KEYDOWN : function(weEvent) { - var oSelection, - elParentQuote; - - if ('WYSIWYG' !== this.oApp.getEditingMode()){ - return; - } - - if(8 !== weEvent.key().keyCode){ - return; - } - - oSelection = this.oApp.getSelection(); - oSelection.fixCommonAncestorContainer(); - elParentQuote = this._findParentQuote(oSelection.commonAncestorContainer); + $ON_REGISTER_CONVERTERS: function () { + this.oApp.exec("ADD_CONVERTER", ["DB_TO_IR", jindo.$Fn(function (sContents) { + sContents = sContents.replace(/<(blockquote)[^>]*class=['"]?(se2_quote[0-9]+)['"]?[^>]*>/gi, "<$1 class=$2>"); + return sContents; + }, this).bind()]); - if(!elParentQuote){ - return; - } - - if(this._isBlankQuote(elParentQuote)){ - weEvent.stop(jindo.$Event.CANCEL_DEFAULT); - - oSelection.selectNode(elParentQuote); - oSelection.collapseToStart(); - - jindo.$Element(elParentQuote).leave(); - - oSelection.select(); - } - }, - - // [SMARTEDITORSUS-215] Delete 로 인용구 뒤의 P 가 제거되지 않도록 처리 - $ON_EVENT_EDITING_AREA_KEYUP : function(weEvent) { - var oSelection, - elParentQuote, - oP; - - if ('WYSIWYG' !== this.oApp.getEditingMode()){ - return; - } - - if(46 !== weEvent.key().keyCode){ - return; - } - - oSelection = this.oApp.getSelection(); - oSelection.fixCommonAncestorContainer(); - elParentQuote = this._findParentQuote(oSelection.commonAncestorContainer); - - if(!elParentQuote){ - return false; - } - - if(!elParentQuote.nextSibling){ - weEvent.stop(jindo.$Event.CANCEL_DEFAULT); - - oP = oSelection._document.createElement("P"); - oP.innerHTML = " "; - - jindo.$Element(elParentQuote).after(oP); - - setTimeout(jindo.$Fn(function(oSelection){ - var sBookmarkID = oSelection.placeStringBookmark(); - - oSelection.select(); - oSelection.removeStringBookmark(sBookmarkID); - },this).bind(oSelection), 0); - } - }, - - _isBlankQuote : function(elParentQuote){ - var elChild, - aChildNodes, - i, nLen, - bChrome = this.oApp.oNavigator.chrome, - bSafari = this.oApp.oNavigator.safari, - isBlankText = function(sText){ - sText = sText.replace(/[\r\n]/ig, '').replace(unescape("%uFEFF"), ''); + this.oApp.exec("ADD_CONVERTER", ["IR_TO_DB", jindo.$Fn(function (sContents) { + var htQuoteStyles_view = this.htQuoteStyles_view; + sContents = sContents.replace(/<(blockquote)[^>]*class=['"]?(se2_quote[0-9]+)['"]?[^>]*>/gi, function (sAll, sTag, sClassName) { + return '<' + sTag + ' class=' + sClassName + ' style="' + htQuoteStyles_view[sClassName] + '">'; + }); + return sContents; + }, this).bind()]); - if(sText === ""){ - return true; - } - - if(sText === " " || sText === " "){ // [SMARTEDITORSUS-479] - return true; - } - - return false; - }, - isBlank = function(oNode){ - if(oNode.nodeType === 3 && isBlankText(oNode.nodeValue)){ - return true; - } - - if((oNode.tagName === "P" || oNode.tagName === "SPAN") && - (isBlankText(oNode.innerHTML) || oNode.innerHTML === "
    ")){ - return true; - } + this.htSE1toSE2Map = { + "01": "1", + "02": "2", + "03": "6", + "04": "8", + "05": "9", + "07": "3", + "08": "5" + }; + // convert SE1's quotes to SE2's + // -> 블로그 개발 쪽에서 처리 하기로 함. + /* + this.oApp.exec("ADD_CONVERTER", ["DB_TO_IR", jindo.$Fn(function(sContents){ + return sContents.replace(/]* class="?vview_quote([0-9]+)"?[^>]*>((?:\s|.)*?)<\/blockquote>/ig, jindo.$Fn(function(m0,sQuoteType,sQuoteContents){ + if (/((?:\s|.)*?)/ig.test(sQuoteContents)){ + if(!this.htSE1toSE2Map[sQuoteType]){ + return m0; + } + + return '
    '+RegExp.$1+'
    '; + }else{ + return ''; + } + }, this).bind()); + }, this).bind()]); + */ + }, - return false; - }, - isBlankTable = function(oNode){ - if((jindo.$$("tr", oNode)).length === 0){ - return true; - } - - return false; - }; + $LOCAL_BEFORE_FIRST: function () { + this._assignHTMLElements(); - if(isBlankText(elParentQuote.innerHTML) || elParentQuote.innerHTML === "
    "){ - return true; - } - - if(bChrome || bSafari){ // [SMARTEDITORSUS-352], [SMARTEDITORSUS-502] - var aTable = jindo.$$("TABLE", elParentQuote), - nTable = aTable.length, - elTable; - - for(i=0; i")) { + return true; + } + + return false; + }, + isBlankTable = function (oNode) { + if ((jindo.$$("tr", oNode)).length === 0) { + return true; + } + + return false; + }; + + if (isBlankText(elParentQuote.innerHTML) || elParentQuote.innerHTML === "
    ") { + return true; + } + + if (bChrome || bSafari) { // [SMARTEDITORSUS-352], [SMARTEDITORSUS-502] + var aTable = jindo.$$("TABLE", elParentQuote), + nTable = aTable.length, + elTable; + + for (i = 0; i < nTable; i++) { + elTable = aTable[i]; + + if (isBlankTable(elTable)) { + jindo.$Element(elTable).leave(); + } + } + } + + aChildNodes = elParentQuote.childNodes; + + for (i = 0, nLen = aChildNodes.length; i < nLen; i++) { + elChild = aChildNodes[i]; + + if (!isBlank(elChild)) { + return false; + } + } + + return true; + }, + + _findParentQuote: function (el) { + return this._findAncestor(jindo.$Fn(function (elNode) { + if (!elNode) { return false; } + if (elNode.tagName !== "BLOCKQUOTE") { return false; } + if (!elNode.className) { return false; } + + var sClassName = elNode.className; + if (!this.htQuoteStyles_view[sClassName]) { return false; } + + return true; + }, this).bind(), el); + }, + + _findAncestor: function (fnCondition, elNode) { + while (elNode && !fnCondition(elNode)) { elNode = elNode.parentNode; } + + return elNode; + } }); /** * @fileOverview This file contains Husky plugin that takes care of the operations related to table creation * @name hp_SE_Table.js */ nhn.husky.SE2M_TableCreator = jindo.$Class({ - name : "SE2M_TableCreator", + name: "SE2M_TableCreator", - _sSETblClass : "__se_tbl", - - nRows : 3, - nColumns : 4, - nBorderSize : 1, - sBorderColor : "#000000", - sBGColor: "#000000", - - nBorderStyleIdx : 3, - nTableStyleIdx : 1, - - nMinRows : 1, - nMaxRows : 20, - nMinColumns : 1, - nMaxColumns : 20, - nMinBorderWidth : 1, - nMaxBorderWidth : 10, - - rxLastDigits : null, - sReEditGuideMsg_table : null, - - // 테두리 스타일 목록 - // 표 스타일 스타일 목록 - oSelection : null, - - $ON_MSG_APP_READY : function(){ - this.sReEditGuideMsg_table = this.oApp.$MSG("SE2M_ReEditAction.reEditGuideMsg.table"); - this.oApp.exec("REGISTER_UI_EVENT", ["table", "click", "TOGGLE_TABLE_LAYER"]); - this.oApp.registerLazyMessage(["TOGGLE_TABLE_LAYER"], ["hp_SE2M_TableCreator$Lazy.js"]); - }, - - // [SMARTEDITORSUS-365] 테이블퀵에디터 > 속성 직접입력 > 테두리 스타일 - // - 테두리 없음을 선택하는 경우 본문에 삽입하는 표에 가이드 라인을 표시해 줍니다. 보기 시에는 테두리가 보이지 않습니다. - $ON_REGISTER_CONVERTERS : function(){ - this.oApp.exec("ADD_CONVERTER_DOM", ["IR_TO_DB", jindo.$Fn(this.irToDbDOM, this).bind()]); - this.oApp.exec("ADD_CONVERTER_DOM", ["DB_TO_IR", jindo.$Fn(this.dbToIrDOM, this).bind()]); - }, - - irToDbDOM : function(oTmpNode){ - /** - * 저장을 위한 Table Tag 는 아래와 같이 변경됩니다. - * (1) - *
    - * -->
    - * (2) - * --> - */ - var aNoBorderTable = []; - var aTables = jindo.$$('table[class=__se_tbl]', oTmpNode, {oneTimeOffCache:true}); - - // 테두리가 없음 속성의 table (임의로 추가한 attr_no_border_tbl 속성이 있는 table 을 찾음) - jindo.$A(aTables).forEach(function(oValue, nIdx, oArray) { - if(jindo.$Element(oValue).attr("attr_no_border_tbl")){ - aNoBorderTable.push(oValue); - } - }, this); - - if(aNoBorderTable.length < 1){ - return; - } - - // [SMARTEDITORSUS-410] 글 저장 시, 테두리 없음 속성을 선택할 때 임의로 표시한 가이드 라인 property 만 style 에서 제거해 준다. - //
    - *

     

     
    의 속성값을 변경 및 제거 - var aTDs = [], oTable; - for(var i = 0, nCount = aNoBorderTable.length; i < nCount; i++){ - oTable = aNoBorderTable[i]; - - // 에서 border, cellpadding 속성값 변경, style property 제거 - jindo.$Element(oTable).css({"border": "", "borderLeft": "", "borderBottom": ""}); - jindo.$Element(oTable).attr({"border": 0, "cellpadding": 1}); - - //
    에서는 background-color 를 제외한 style 을 모두 제거 - aTDs = jindo.$$('tbody>tr>td', oTable); - jindo.$A(aTDs).forEach(function(oTD, nIdx, oTDArray) { - jindo.$Element(oTD).css({"border": "", "borderTop": "", "borderRight": ""}); - }); - } - }, - - dbToIrDOM : function(oTmpNode){ - /** - * 수정을 위한 Table Tag 는 아래와 같이 변경됩니다. - * (1) - *
    - * -->
    - * (2) - * --> - */ - var aNoBorderTable = []; - var aTables = jindo.$$('table[class=__se_tbl]', oTmpNode, {oneTimeOffCache:true}); - - // 테두리가 없음 속성의 table (임의로 추가한 attr_no_border_tbl 속성이 있는 table 을 찾음) - jindo.$A(aTables).forEach(function(oValue, nIdx, oArray) { - if(jindo.$Element(oValue).attr("attr_no_border_tbl")){ - aNoBorderTable.push(oValue); - } - }, this); - - if(aNoBorderTable.length < 1){ - return; - } - - //
    - *  

     

    의 속성값을 변경/추가 - var aTDs = [], oTable; - for(var i = 0, nCount = aNoBorderTable.length; i < nCount; i++){ - oTable = aNoBorderTable[i]; - - // 에서 border, cellpadding 속성값 변경/ style 속성 추가 - jindo.$Element(oTable).css({"border": "1px dashed #c7c7c7", "borderLeft": 0, "borderBottom": 0}); - jindo.$Element(oTable).attr({"border": 1, "cellpadding": 0}); - - //
    에서 style 속성값 추가 - aTDs = jindo.$$('tbody>tr>td', oTable); - jindo.$A(aTDs).forEach(function(oTD, nIdx, oTDArray) { - jindo.$Element(oTD).css({"border": "1px dashed #c7c7c7", "borderTop": 0, "borderRight": 0}); - }); - } - } + _sSETblClass: "__se_tbl", + + nRows: 3, + nColumns: 4, + nBorderSize: 1, + sBorderColor: "#000000", + sBGColor: "#000000", + + nBorderStyleIdx: 3, + nTableStyleIdx: 1, + + nMinRows: 1, + nMaxRows: 20, + nMinColumns: 1, + nMaxColumns: 20, + nMinBorderWidth: 1, + nMaxBorderWidth: 10, + + rxLastDigits: null, + sReEditGuideMsg_table: null, + + // 테두리 스타일 목록 + // 표 스타일 스타일 목록 + oSelection: null, + + $ON_MSG_APP_READY: function () { + this.sReEditGuideMsg_table = this.oApp.$MSG("SE2M_ReEditAction.reEditGuideMsg.table"); + this.oApp.exec("REGISTER_UI_EVENT", ["table", "click", "TOGGLE_TABLE_LAYER"]); + this.oApp.registerLazyMessage(["TOGGLE_TABLE_LAYER"], ["hp_SE2M_TableCreator$Lazy.js"]); + }, + + // [SMARTEDITORSUS-365] 테이블퀵에디터 > 속성 직접입력 > 테두리 스타일 + // - 테두리 없음을 선택하는 경우 본문에 삽입하는 표에 가이드 라인을 표시해 줍니다. 보기 시에는 테두리가 보이지 않습니다. + $ON_REGISTER_CONVERTERS: function () { + this.oApp.exec("ADD_CONVERTER_DOM", ["IR_TO_DB", jindo.$Fn(this.irToDbDOM, this).bind()]); + this.oApp.exec("ADD_CONVERTER_DOM", ["DB_TO_IR", jindo.$Fn(this.dbToIrDOM, this).bind()]); + }, + + irToDbDOM: function (oTmpNode) { + /** + * 저장을 위한 Table Tag 는 아래와 같이 변경됩니다. + * (1) + *
    + * -->
    + * (2) + * --> + */ + var aNoBorderTable = []; + var aTables = jindo.$$('table[class=__se_tbl]', oTmpNode, { oneTimeOffCache: true }); + + // 테두리가 없음 속성의 table (임의로 추가한 attr_no_border_tbl 속성이 있는 table 을 찾음) + jindo.$A(aTables).forEach(function (oValue, nIdx, oArray) { + if (jindo.$Element(oValue).attr("attr_no_border_tbl")) { + aNoBorderTable.push(oValue); + } + }, this); + + if (aNoBorderTable.length < 1) { + return; + } + + // [SMARTEDITORSUS-410] 글 저장 시, 테두리 없음 속성을 선택할 때 임의로 표시한 가이드 라인 property 만 style 에서 제거해 준다. + //
    + *

     

     
    의 속성값을 변경 및 제거 + var aTDs = [], oTable; + for (var i = 0, nCount = aNoBorderTable.length; i < nCount; i++) { + oTable = aNoBorderTable[i]; + + // 에서 border, cellpadding 속성값 변경, style property 제거 + jindo.$Element(oTable).css({ "border": "", "borderLeft": "", "borderBottom": "" }); + jindo.$Element(oTable).attr({ "border": 0, "cellpadding": 1 }); + + //
    에서는 background-color 를 제외한 style 을 모두 제거 + aTDs = jindo.$$('tbody>tr>td', oTable); + jindo.$A(aTDs).forEach(function (oTD, nIdx, oTDArray) { + jindo.$Element(oTD).css({ "border": "", "borderTop": "", "borderRight": "" }); + }); + } + }, + + dbToIrDOM: function (oTmpNode) { + /** + * 수정을 위한 Table Tag 는 아래와 같이 변경됩니다. + * (1) + *
    + * -->
    + * (2) + * --> + */ + var aNoBorderTable = []; + var aTables = jindo.$$('table[class=__se_tbl]', oTmpNode, { oneTimeOffCache: true }); + + // 테두리가 없음 속성의 table (임의로 추가한 attr_no_border_tbl 속성이 있는 table 을 찾음) + jindo.$A(aTables).forEach(function (oValue, nIdx, oArray) { + if (jindo.$Element(oValue).attr("attr_no_border_tbl")) { + aNoBorderTable.push(oValue); + } + }, this); + + if (aNoBorderTable.length < 1) { + return; + } + + //
    + *  

     

    의 속성값을 변경/추가 + var aTDs = [], oTable; + for (var i = 0, nCount = aNoBorderTable.length; i < nCount; i++) { + oTable = aNoBorderTable[i]; + + // 에서 border, cellpadding 속성값 변경/ style 속성 추가 + jindo.$Element(oTable).css({ "border": "1px dashed #c7c7c7", "borderLeft": 0, "borderBottom": 0 }); + jindo.$Element(oTable).attr({ "border": 1, "cellpadding": 0 }); + + //
    에서 style 속성값 추가 + aTDs = jindo.$$('tbody>tr>td', oTable); + jindo.$A(aTDs).forEach(function (oTD, nIdx, oTDArray) { + jindo.$Element(oTD).css({ "border": "1px dashed #c7c7c7", "borderTop": 0, "borderRight": 0 }); + }); + } + } }); /** * @fileOverview This file contains Husky plugin that takes care of the operations related to changing the font style in the table. @@ -12143,79 +12147,79 @@ nhn.husky.SE2M_TableCreator = jindo.$Class({ * @name SE2M_TableBlockManager */ nhn.husky.SE2M_TableBlockStyler = jindo.$Class({ - name : "SE2M_TableBlockStyler", - nSelectedTD : 0, - htSelectedTD : {}, - aTdRange : [], - - $init : function(){ }, - - $LOCAL_BEFORE_ALL : function(){ - return (this.oApp.getEditingMode() == "WYSIWYG"); - }, - - $ON_MSG_APP_READY : function(){ - this.oDocument = this.oApp.getWYSIWYGDocument(); - }, - - $ON_EVENT_EDITING_AREA_MOUSEUP : function(wevE){ - if(this.oApp.getEditingMode() != "WYSIWYG") return; - this.setTdBlock(); - }, - - /** - * selected Area가 td block인지 체크하는 함수. - */ - $ON_IS_SELECTED_TD_BLOCK : function(sAttr,oReturn) { - if( this.nSelectedTD > 0){ - oReturn[sAttr] = true; - return oReturn[sAttr]; - }else{ - oReturn[sAttr] = false; - return oReturn[sAttr]; - } - }, - - /** - * - */ - $ON_GET_SELECTED_TD_BLOCK : function(sAttr,oReturn){ - //use : this.oApp.exec("GET_SELECTED_TD_BLOCK",['aCells',this.htSelectedTD]); - oReturn[sAttr] = this.htSelectedTD.aTdCells; - }, - - setTdBlock : function() { - this.oApp.exec("GET_SELECTED_CELLS",['aTdCells',this.htSelectedTD]); //tableEditor로 부터 얻어온다. - var aNodes = this.htSelectedTD.aTdCells; - if(aNodes){ - this.nSelectedTD = aNodes.length; - } - }, - - $ON_DELETE_BLOCK_CONTENTS : function(){ - var self = this, welParent, oBlockNode, oChildNode; - - this.setTdBlock(); - for (var j = 0; j < this.nSelectedTD ; j++){ - jindo.$Element(this.htSelectedTD.aTdCells[j]).child( function(elChild){ - - welParent = jindo.$Element(elChild._element.parentNode); - welParent.remove(elChild); + name: "SE2M_TableBlockStyler", + nSelectedTD: 0, + htSelectedTD: {}, + aTdRange: [], + + $init: function () { }, + + $LOCAL_BEFORE_ALL: function () { + return (this.oApp.getEditingMode() == "WYSIWYG"); + }, + + $ON_MSG_APP_READY: function () { + this.oDocument = this.oApp.getWYSIWYGDocument(); + }, + + $ON_EVENT_EDITING_AREA_MOUSEUP: function (wevE) { + if (this.oApp.getEditingMode() != "WYSIWYG") return; + this.setTdBlock(); + }, + + /** + * selected Area가 td block인지 체크하는 함수. + */ + $ON_IS_SELECTED_TD_BLOCK: function (sAttr, oReturn) { + if (this.nSelectedTD > 0) { + oReturn[sAttr] = true; + return oReturn[sAttr]; + } else { + oReturn[sAttr] = false; + return oReturn[sAttr]; + } + }, + + /** + * + */ + $ON_GET_SELECTED_TD_BLOCK: function (sAttr, oReturn) { + //use : this.oApp.exec("GET_SELECTED_TD_BLOCK",['aCells',this.htSelectedTD]); + oReturn[sAttr] = this.htSelectedTD.aTdCells; + }, + + setTdBlock: function () { + this.oApp.exec("GET_SELECTED_CELLS", ['aTdCells', this.htSelectedTD]); //tableEditor로 부터 얻어온다. + var aNodes = this.htSelectedTD.aTdCells; + if (aNodes) { + this.nSelectedTD = aNodes.length; + } + }, + + $ON_DELETE_BLOCK_CONTENTS: function () { + var self = this, welParent, oBlockNode, oChildNode; + + this.setTdBlock(); + for (var j = 0; j < this.nSelectedTD; j++) { + jindo.$Element(this.htSelectedTD.aTdCells[j]).child(function (elChild) { + + welParent = jindo.$Element(elChild._element.parentNode); + welParent.remove(elChild); + + oBlockNode = self.oDocument.createElement('P'); + + if (jindo.$Agent().navigator().firefox) { + oChildNode = self.oDocument.createElement('BR'); + } else { + oChildNode = self.oDocument.createTextNode('\u00A0'); + } + + oBlockNode.appendChild(oChildNode); + welParent.append(oBlockNode); + }, 1); + } + } - oBlockNode = self.oDocument.createElement('P'); - - if (jindo.$Agent().navigator().firefox) { - oChildNode = self.oDocument.createElement('BR'); - } else { - oChildNode = self.oDocument.createTextNode('\u00A0'); - } - - oBlockNode.appendChild(oChildNode); - welParent.append(oBlockNode); - }, 1); - } - } - }); //{ /** @@ -12223,453 +12227,453 @@ nhn.husky.SE2M_TableBlockStyler = jindo.$Class({ * @name hp_SE2M_StyleRemover.js */ nhn.husky.SE2M_StyleRemover = jindo.$Class({ - name: "SE2M_StyleRemover", + name: "SE2M_StyleRemover", - $ON_MSG_APP_READY : function(){ - this.oApp.exec("REGISTER_UI_EVENT", ["styleRemover", "click", "CHOOSE_REMOVE_STYLE", []]); - }, + $ON_MSG_APP_READY: function () { + this.oApp.exec("REGISTER_UI_EVENT", ["styleRemover", "click", "CHOOSE_REMOVE_STYLE", []]); + }, - $LOCAL_BEFORE_FIRST : function(){ - // The plugin may be used in view and when it is used there, EditingAreaManager plugin is not loaded. - // So, get the document from the selection instead of EditingAreaManager. - this.oHuskyRange = this.oApp.getEmptySelection(); - this._document = this.oHuskyRange._document; - }, - - $ON_CHOOSE_REMOVE_STYLE : function(oSelection){ - var bSelectedBlock = false; - var htSelectedTDs = {}; - this.oApp.exec("IS_SELECTED_TD_BLOCK",['bIsSelectedTd',htSelectedTDs]); - bSelectedBlock = htSelectedTDs.bIsSelectedTd; + $LOCAL_BEFORE_FIRST: function () { + // The plugin may be used in view and when it is used there, EditingAreaManager plugin is not loaded. + // So, get the document from the selection instead of EditingAreaManager. + this.oHuskyRange = this.oApp.getEmptySelection(); + this._document = this.oHuskyRange._document; + }, - this.oApp.exec("RECORD_UNDO_BEFORE_ACTION", ["REMOVE STYLE", {bMustBlockElement:true}]); - - if( bSelectedBlock ){ - this.oApp.exec("REMOVE_STYLE_IN_BLOCK", []); - }else{ - this.oApp.exec("REMOVE_STYLE", []); - } - - this.oApp.exec("RECORD_UNDO_AFTER_ACTION", ["REMOVE STYLE", {bMustBlockElement:true}]); - - this.oApp.exec('MSG_NOTIFY_CLICKCR', ['noeffect']); - }, - - $ON_REMOVE_STYLE_IN_BLOCK : function(oSelection){ - var htSelectedTDs = {}; - this.oSelection = this.oApp.getSelection(); - this.oApp.exec("GET_SELECTED_TD_BLOCK",['aTdCells',htSelectedTDs]); - var aNodes = htSelectedTDs.aTdCells; - - for( var j = 0; j < aNodes.length ; j++){ - this.oSelection.selectNodeContents(aNodes[j]); - this.oSelection.select(); - this.oApp.exec("REMOVE_STYLE", []); - } - }, - - $ON_REMOVE_STYLE : function(oSelection){ - if(!oSelection || !oSelection.commonAncestorContainer){ - oSelection = this.oApp.getSelection(); - } + $ON_CHOOSE_REMOVE_STYLE: function (oSelection) { + var bSelectedBlock = false; + var htSelectedTDs = {}; + this.oApp.exec("IS_SELECTED_TD_BLOCK", ['bIsSelectedTd', htSelectedTDs]); + bSelectedBlock = htSelectedTDs.bIsSelectedTd; - if(oSelection.collapsed){return;} + this.oApp.exec("RECORD_UNDO_BEFORE_ACTION", ["REMOVE STYLE", { bMustBlockElement: true }]); - oSelection.expandBothEnds(); + if (bSelectedBlock) { + this.oApp.exec("REMOVE_STYLE_IN_BLOCK", []); + } else { + this.oApp.exec("REMOVE_STYLE", []); + } - var sBookmarkID = oSelection.placeStringBookmark(); - var aNodes = oSelection.getNodes(true); + this.oApp.exec("RECORD_UNDO_AFTER_ACTION", ["REMOVE STYLE", { bMustBlockElement: true }]); - this._removeStyle(aNodes); - oSelection.moveToBookmark(sBookmarkID); + this.oApp.exec('MSG_NOTIFY_CLICKCR', ['noeffect']); + }, - var aNodes = oSelection.getNodes(true); - for(var i=0; i 테이블 외부로 커서 이동 불가 - if( htBrowser.ie ){ - sHtml += "

     

    "; - }else if(htBrowser.firefox){ - //[SMARTEDITORSUS-477][개별블로그](파폭특정)포스트쓰기>요약글을 삽입 후 요약글 아래 임의의 본문영역에 마우스 클릭 시 커서가 요약안에 노출됩니다. - // 본문에 table만 있는 경우, 커서가 밖으로 못나오는 현상이 있음.FF버그임. - sHtml += "

    

    "; - } - - oSelection.selectNode(elDivHolder); - oSelection.pasteHTML(sHtml); - - //Table인경우, 커서를 테이블 첫 TD에 넣기 위한 작업. - aNodesInSelection = oSelection.getNodes() || []; - for(var i = 0; i < aNodesInSelection.length ; i++){ - if(!!aNodesInSelection[i].tagName && aNodesInSelection[i].tagName.toLowerCase() == 'td'){ - elFirstTD = aNodesInSelection[i]; - oSelection.selectNodeContents(elFirstTD.firstChild || elFirstTD); - oSelection.collapseToStart(); - oSelection.select(); - break; - } - } - - oSelection.collapseToEnd(); //파란색 커버 제거. - oSelection.select(); - this.oApp.exec("FOCUS"); - if (!elDivHolder) {// 임시 div 삭제. - elDivHolder.parentNode.removeChild(elDivHolder); - } - - if(!bNoUndo){ - this.oApp.exec("RECORD_UNDO_AFTER_ACTION", ["REMOVE STYLE AND PASTE HTML"]); - } - }, - - _removeStyle : function(aNodes){ - var arNodes = jindo.$A(aNodes); - for(var i=0; i current node will be handled in the next iteration - if(arNodes.indexOf(this._getVeryLast(oNode.nextSibling)) != -1){continue;} + // [SMARTEDITORSUS-1750] 스타일제거를 위해 selection을 확장(oSelection.expandBothEnds)하면 TR까지 확장되는데 IE10에서만 execCommand 가 제대로 동작하지 않는 문제가 발생하기 때문에 확장전 selection으로 복원하도록 수정 + // [SMARTEDITORSUS-1893] 테이블밖에서는 마지막라인이 풀리는 이슈가 발생하여 commonAncestorContainer가 TBODY 인 경우에만 selection을 복원하도록 제한 + if (oSelection.commonAncestorContainer.tagName === "TBODY") { + oSelection = this.oApp.getSelection(); + } + oSelection.select(); - // Since the case - // 1. oNode is the right most node - // 2. oNode's got a right sibling that is included in the selection - // were all taken care of above, so from here we just need take care of the case when oNode is NOT the right most node and oNode's right sibling is NOT included in the selection + // use a custom removeStringBookmark here as the string bookmark could've been cloned and there are some additional cases that need to be considered - // Case 3: the rest - // When all of the left siblings were selected, take all the left siblings and current node and append them to the left of the parent node. - // H - // | - // P - // / | | \ - // A B C D - // -> if A, B and C were selected, then make it - // H - // / | | \ - // A B C P - // | - // D - i--; - // bring up selected prev siblings - if(arNodes.indexOf(this._getVeryFirst(oNode.parentNode)) != -1){ - // move - var tmp = oNode; - var lastInserted = parent; - while(tmp){ - var prevNode = tmp.previousSibling; - parent.parentNode.insertBefore(tmp, lastInserted); - lastInserted = tmp; - - if(!prevNode){break;} - tmp = prevNode; - } - if(parent.childNodes.length === 0){parent.parentNode.removeChild(parent);} - // When NOT all of the left siblings were selected, split the parent node and insert the selected nodes in between. - // H - // | - // P - // / | | \ - // A B C D - // -> if B and C were selected, then make it - // H - // / | | \ - // P B C P - // | | - // A D - }else{ - //split - if(bDontSplit){ - i++; - continue; - } - - var oContainer = this._document.createElement("SPAN"); - var tmp = oNode; - parent.insertBefore(oContainer, tmp.nextSibling); - while(tmp){ - var prevNode = tmp.previousSibling; - oContainer.insertBefore(tmp, oContainer.firstChild); + // remove start marker + var oMarker = this._document.getElementById(oSelection.HUSKY_BOOMARK_START_ID_PREFIX + sBookmarkID); + while (oMarker) { + oParent = nhn.DOMFix.parentNode(oMarker); + oParent.removeChild(oMarker); + while (oParent && (!oParent.firstChild || (!oParent.firstChild.nextSibling && oSelection._isBlankTextNode(oParent.firstChild)))) { + var oNextParent = oParent.parentNode; + oParent.parentNode.removeChild(oParent); + oParent = oNextParent; + } + oMarker = this._document.getElementById(oSelection.HUSKY_BOOMARK_START_ID_PREFIX + sBookmarkID); + } - if(!prevNode){break;} - if(arNodes.indexOf(this._getVeryFirst(prevNode)) == -1){break;} - tmp = prevNode; - } - - this._splitAndAppendAtTop(oContainer); - while(oContainer.firstChild){ - oContainer.parentNode.insertBefore(oContainer.firstChild, oContainer); - } - oContainer.parentNode.removeChild(oContainer); - } - } - }, + // remove end marker + var oMarker = this._document.getElementById(oSelection.HUSKY_BOOMARK_END_ID_PREFIX + sBookmarkID); + while (oMarker) { + oParent = nhn.DOMFix.parentNode(oMarker); + oParent.removeChild(oMarker); + while (oParent && (!oParent.firstChild || (!oParent.firstChild.nextSibling && oSelection._isBlankTextNode(oParent.firstChild)))) { + var oNextParent = oParent.parentNode; + oParent.parentNode.removeChild(oParent); + oParent = oNextParent; + } + oMarker = this._document.getElementById(oSelection.HUSKY_BOOMARK_END_ID_PREFIX + sBookmarkID); + } - _splitAndAppendAtTop : function(oSpliter){ - var targetNode = oSpliter; - var oTmp = targetNode; - var oCopy = oTmp; + this.oApp.exec("CHECK_STYLE_CHANGE"); + }, - while(jindo.$Element(oTmp.parentNode).css("display") == "inline"){ - var oNode = oTmp.parentNode.cloneNode(false); + $ON_REMOVE_STYLE2: function (aNodes) { + this._removeStyle(aNodes); + }, - while(oTmp.nextSibling){ - oNode.appendChild(oTmp.nextSibling); - } + $ON_REMOVE_STYLE_AND_PASTE_HTML: function (sHtml, bNoUndo) { + var htBrowser, + elDivHolder, + elFirstTD, + aNodesInSelection, + oSelection; - oTmp = oTmp.parentNode; + htBrowser = jindo.$Agent().navigator(); - oNode.insertBefore(oCopy, oNode.firstChild); - oCopy = oNode; - } + if (!sHtml) { return false; } + if (this.oApp.getEditingMode() != "WYSIWYG") { + this.oApp.exec("CHANGE_EDITING_MODE", ["WYSIWYG"]); + } - oTop = oTmp.parentNode; - oTop.insertBefore(targetNode, oTmp.nextSibling); - oTop.insertBefore(oCopy, targetNode.nextSibling); - }, - - _getVeryFirst : function(oNode){ - if(!oNode){return null;} + if (!bNoUndo) { + this.oApp.exec("RECORD_UNDO_BEFORE_ACTION", ["REMOVE STYLE AND PASTE HTML"]); + } - if(oNode.firstChild){ - return this.oHuskyRange._getVeryFirstRealChild(oNode); - }else{ - return oNode; - } - }, - - _getVeryLast : function(oNode){ - if(!oNode){return null;} - - if(oNode.lastChild){ - return this.oHuskyRange._getVeryLastRealChild(oNode); - }else{ - return oNode; - } - } + oSelection = this.oApp.getSelection(); + oSelection.deleteContents(); // remove select node - for dummy image, reedit object + + // If the table were inserted within a styled(strikethough & etc) paragraph, the table may inherit the style in IE. + elDivHolder = this.oApp.getWYSIWYGDocument().createElement("DIV"); + oSelection.insertNode(elDivHolder); + + if (!!htBrowser.webkit) { + elDivHolder.innerHTML = " "; // for browser bug! - summary reiteration + } + + oSelection.selectNode(elDivHolder); + this.oApp.exec("REMOVE_STYLE", [oSelection]); + + //[SMARTEDITORSUS-181][IE9] 표나 요약글 등의 테이블에서 > 테이블 외부로 커서 이동 불가 + if (htBrowser.ie) { + sHtml += "

     

    "; + } else if (htBrowser.firefox) { + //[SMARTEDITORSUS-477][개별블로그](파폭특정)포스트쓰기>요약글을 삽입 후 요약글 아래 임의의 본문영역에 마우스 클릭 시 커서가 요약안에 노출됩니다. + // 본문에 table만 있는 경우, 커서가 밖으로 못나오는 현상이 있음.FF버그임. + sHtml += "

    

    "; + } + + oSelection.selectNode(elDivHolder); + oSelection.pasteHTML(sHtml); + + //Table인경우, 커서를 테이블 첫 TD에 넣기 위한 작업. + aNodesInSelection = oSelection.getNodes() || []; + for (var i = 0; i < aNodesInSelection.length; i++) { + if (!!aNodesInSelection[i].tagName && aNodesInSelection[i].tagName.toLowerCase() == 'td') { + elFirstTD = aNodesInSelection[i]; + oSelection.selectNodeContents(elFirstTD.firstChild || elFirstTD); + oSelection.collapseToStart(); + oSelection.select(); + break; + } + } + + oSelection.collapseToEnd(); //파란색 커버 제거. + oSelection.select(); + this.oApp.exec("FOCUS"); + if (!elDivHolder) {// 임시 div 삭제. + elDivHolder.parentNode.removeChild(elDivHolder); + } + + if (!bNoUndo) { + this.oApp.exec("RECORD_UNDO_AFTER_ACTION", ["REMOVE STYLE AND PASTE HTML"]); + } + }, + + _removeStyle: function (aNodes) { + var arNodes = jindo.$A(aNodes); + for (var i = 0; i < aNodes.length; i++) { + var oNode = aNodes[i]; + + // oNode had been removed from the document already + if (!oNode || !oNode.parentNode || !oNode.parentNode.tagName) { continue; } + + var bDontSplit = false; + // If oNode is direct child of a block level node, don't do anything. (should not move up the hierarchy anymore) + if (jindo.$Element(oNode.parentNode).css("display") != "inline") { + continue; + } + + var parent = oNode.parentNode; + + // do not proceed if oNode is not completely selected + if (oNode.firstChild) { + if (arNodes.indexOf(this.oHuskyRange._getVeryLastRealChild(oNode)) == -1) { continue; } + if (arNodes.indexOf(this.oHuskyRange._getVeryFirstRealChild(oNode)) == -1) { continue; } + } + + // Case 1: oNode is the right most node + // + // If oNode were C(right most node) from + // H + // | + // P + // / | \ + // A B C + // + // and B and C were selected, bring up all the (selected) left siblings to the right of the parent and and make it + // H + // / | \ + // P B C + // | + // A + // =========================================================== + // If A, B and C were selected from + // H + // | + // P + // / | \ + // A B C + // + // append them to the right of the parent and make it + // H + // / | | \ + // P A B C + // + // and then remove P as it's got no child and make it + // H + // / | \ + // A B C + if (!oNode.nextSibling) { + i--; + var tmp = oNode; + // bring up left siblings + while (tmp) { + var prevNode = tmp.previousSibling; + parent.parentNode.insertBefore(tmp, parent.nextSibling); + if (!prevNode) { break; } + if (arNodes.indexOf(this._getVeryFirst(prevNode)) == -1) { break; } + tmp = prevNode; + } + + // remove the parent if it's got no child now + if (parent.childNodes.length === 0) { parent.parentNode.removeChild(parent); } + + continue; + } + + // Case 2: oNode's got a right sibling that is included in the selection + // + // if the next sibling is included in the selection, stop current iteration + // -> current node will be handled in the next iteration + if (arNodes.indexOf(this._getVeryLast(oNode.nextSibling)) != -1) { continue; } + + // Since the case + // 1. oNode is the right most node + // 2. oNode's got a right sibling that is included in the selection + // were all taken care of above, so from here we just need take care of the case when oNode is NOT the right most node and oNode's right sibling is NOT included in the selection + + // Case 3: the rest + // When all of the left siblings were selected, take all the left siblings and current node and append them to the left of the parent node. + // H + // | + // P + // / | | \ + // A B C D + // -> if A, B and C were selected, then make it + // H + // / | | \ + // A B C P + // | + // D + i--; + // bring up selected prev siblings + if (arNodes.indexOf(this._getVeryFirst(oNode.parentNode)) != -1) { + // move + var tmp = oNode; + var lastInserted = parent; + while (tmp) { + var prevNode = tmp.previousSibling; + parent.parentNode.insertBefore(tmp, lastInserted); + lastInserted = tmp; + + if (!prevNode) { break; } + tmp = prevNode; + } + if (parent.childNodes.length === 0) { parent.parentNode.removeChild(parent); } + // When NOT all of the left siblings were selected, split the parent node and insert the selected nodes in between. + // H + // | + // P + // / | | \ + // A B C D + // -> if B and C were selected, then make it + // H + // / | | \ + // P B C P + // | | + // A D + } else { + //split + if (bDontSplit) { + i++; + continue; + } + + var oContainer = this._document.createElement("SPAN"); + var tmp = oNode; + parent.insertBefore(oContainer, tmp.nextSibling); + while (tmp) { + var prevNode = tmp.previousSibling; + oContainer.insertBefore(tmp, oContainer.firstChild); + + if (!prevNode) { break; } + if (arNodes.indexOf(this._getVeryFirst(prevNode)) == -1) { break; } + tmp = prevNode; + } + + this._splitAndAppendAtTop(oContainer); + while (oContainer.firstChild) { + oContainer.parentNode.insertBefore(oContainer.firstChild, oContainer); + } + oContainer.parentNode.removeChild(oContainer); + } + } + }, + + _splitAndAppendAtTop: function (oSpliter) { + var targetNode = oSpliter; + var oTmp = targetNode; + var oCopy = oTmp; + + while (jindo.$Element(oTmp.parentNode).css("display") == "inline") { + var oNode = oTmp.parentNode.cloneNode(false); + + while (oTmp.nextSibling) { + oNode.appendChild(oTmp.nextSibling); + } + + oTmp = oTmp.parentNode; + + oNode.insertBefore(oCopy, oNode.firstChild); + oCopy = oNode; + } + + oTop = oTmp.parentNode; + oTop.insertBefore(targetNode, oTmp.nextSibling); + oTop.insertBefore(oCopy, targetNode.nextSibling); + }, + + _getVeryFirst: function (oNode) { + if (!oNode) { return null; } + + if (oNode.firstChild) { + return this.oHuskyRange._getVeryFirstRealChild(oNode); + } else { + return oNode; + } + }, + + _getVeryLast: function (oNode) { + if (!oNode) { return null; } + + if (oNode.lastChild) { + return this.oHuskyRange._getVeryLastRealChild(oNode); + } else { + return oNode; + } + } }); //} nhn.husky.SE2M_TableEditor = jindo.$Class({ - name : "SE2M_TableEditor", - - _sSETblClass : "__se_tbl", - _sSEReviewTblClass : "__se_tbl_review", + name: "SE2M_TableEditor", - STATUS : { - S_0 : 1, // neither cell selection nor cell resizing is active - MOUSEDOWN_CELL : 2, // mouse down on a table cell - CELL_SELECTING : 3, // cell selection is in progress - CELL_SELECTED : 4, // cell selection was (completely) made - MOUSEOVER_BORDER : 5, // mouse is over a table/cell border and the cell resizing grip is shown - MOUSEDOWN_BORDER : 6 // mouse down on the cell resizing grip (cell resizing is in progress) - }, - - CELL_SELECTION_CLASS : "se2_te_selection", - - MIN_CELL_WIDTH : 5, - MIN_CELL_HEIGHT : 5, - - TMP_BGC_ATTR : "_se2_tmp_te_bgc", - TMP_BGIMG_ATTR : "_se2_tmp_te_bg_img", - ATTR_TBL_TEMPLATE : "_se2_tbl_template", - - nStatus : 1, - nMouseEventsStatus : 0, - - aSelectedCells : [], + _sSETblClass: "__se_tbl", + _sSEReviewTblClass: "__se_tbl_review", - $ON_REGISTER_CONVERTERS : function(){ - // remove the cell selection class - this.oApp.exec("ADD_CONVERTER_DOM", ["WYSIWYG_TO_IR", jindo.$Fn(function(elTmpNode){ - if(this.aSelectedCells.length < 1){ - //return sContents; - return; - } + STATUS: { + S_0: 1, // neither cell selection nor cell resizing is active + MOUSEDOWN_CELL: 2, // mouse down on a table cell + CELL_SELECTING: 3, // cell selection is in progress + CELL_SELECTED: 4, // cell selection was (completely) made + MOUSEOVER_BORDER: 5, // mouse is over a table/cell border and the cell resizing grip is shown + MOUSEDOWN_BORDER: 6 // mouse down on the cell resizing grip (cell resizing is in progress) + }, - var aCells; - var aCellType = ["TD", "TH"]; + CELL_SELECTION_CLASS: "se2_te_selection", - for(var n = 0; n < aCellType.length; n++){ - aCells = elTmpNode.getElementsByTagName(aCellType[n]); - for(var i = 0, nLen = aCells.length; i < nLen; i++){ - if(aCells[i].className){ - aCells[i].className = aCells[i].className.replace(this.CELL_SELECTION_CLASS, ""); - if(aCells[i].getAttribute(this.TMP_BGC_ATTR)){ - aCells[i].style.backgroundColor = aCells[i].getAttribute(this.TMP_BGC_ATTR); - aCells[i].removeAttribute(this.TMP_BGC_ATTR); - }else if(aCells[i].getAttribute(this.TMP_BGIMG_ATTR)){ - jindo.$Element(this.aCells[i]).css("backgroundImage",aCells[i].getAttribute(this.TMP_BGIMG_ATTR)); - aCells[i].removeAttribute(this.TMP_BGIMG_ATTR); - } - } - } - } + MIN_CELL_WIDTH: 5, + MIN_CELL_HEIGHT: 5, -// this.wfnMouseDown.attach(this.elResizeCover, "mousedown"); + TMP_BGC_ATTR: "_se2_tmp_te_bgc", + TMP_BGIMG_ATTR: "_se2_tmp_te_bg_img", + ATTR_TBL_TEMPLATE: "_se2_tbl_template", -// return elTmpNode.innerHTML; -// var rxSelectionColor = new RegExp("<(TH|TD)[^>]*)("+this.TMP_BGC_ATTR+"=[^> ]*)([^>]*>)", "gi"); - }, this).bind()]); - }, + nStatus: 1, + nMouseEventsStatus: 0, - $ON_MSG_APP_READY : function(){ - this.oApp.registerLazyMessage(["EVENT_EDITING_AREA_MOUSEMOVE","STYLE_TABLE"], ["hp_SE2M_TableEditor$Lazy.js","SE2M_TableTemplate.js"]); - } + aSelectedCells: [], + + $ON_REGISTER_CONVERTERS: function () { + // remove the cell selection class + this.oApp.exec("ADD_CONVERTER_DOM", ["WYSIWYG_TO_IR", jindo.$Fn(function (elTmpNode) { + if (this.aSelectedCells.length < 1) { + //return sContents; + return; + } + + var aCells; + var aCellType = ["TD", "TH"]; + + for (var n = 0; n < aCellType.length; n++) { + aCells = elTmpNode.getElementsByTagName(aCellType[n]); + for (var i = 0, nLen = aCells.length; i < nLen; i++) { + if (aCells[i].className) { + aCells[i].className = aCells[i].className.replace(this.CELL_SELECTION_CLASS, ""); + if (aCells[i].getAttribute(this.TMP_BGC_ATTR)) { + aCells[i].style.backgroundColor = aCells[i].getAttribute(this.TMP_BGC_ATTR); + aCells[i].removeAttribute(this.TMP_BGC_ATTR); + } else if (aCells[i].getAttribute(this.TMP_BGIMG_ATTR)) { + jindo.$Element(this.aCells[i]).css("backgroundImage", aCells[i].getAttribute(this.TMP_BGIMG_ATTR)); + aCells[i].removeAttribute(this.TMP_BGIMG_ATTR); + } + } + } + } + + // this.wfnMouseDown.attach(this.elResizeCover, "mousedown"); + + // return elTmpNode.innerHTML; + // var rxSelectionColor = new RegExp("<(TH|TD)[^>]*)("+this.TMP_BGC_ATTR+"=[^> ]*)([^>]*>)", "gi"); + }, this).bind()]); + }, + + $ON_MSG_APP_READY: function () { + this.oApp.registerLazyMessage(["EVENT_EDITING_AREA_MOUSEMOVE", "STYLE_TABLE"], ["hp_SE2M_TableEditor$Lazy.js", "SE2M_TableTemplate.js"]); + } }); /** * @name SE2M_QuickEditor_Common @@ -12680,552 +12684,552 @@ nhn.husky.SE2M_TableEditor = jindo.$Class({ * @since 2009.09.29 * */ nhn.husky.SE2M_QuickEditor_Common = jindo.$Class({ - /** - * class 이름 - * @type {String} - */ - name : "SE2M_QuickEditor_Common", - /** - * 환경 정보. - * @type {Object} - */ - _environmentData : "", - /** - * 현재 타입 (table|img) - * @type {String} - */ - _currentType :"", - /** - * 이벤트가 레이어 안에서 호출되었는지 알기 위한 변수 - * @type {Boolean} - */ - _in_event : false, - /** - * Ajax처리를 하지 않음 - * @type {Boolean} - */ - _bUseConfig : false, - - /** - * 공통 서버에서 개인 설정 받아오는 AjaxUrl - * @See SE2M_Configuration.js - */ - _sBaseAjaxUrl : "", - _sAddTextAjaxUrl : "", - - /** - * 초기 인스턴스 생성 실행되는 함수. - */ - $init : function() { - this.waHotkeys = new jindo.$A([]); - this.waHotkeyLayers = new jindo.$A([]); - }, - - $ON_MSG_APP_READY : function() { - var htConfiguration = nhn.husky.SE2M_Configuration.QuickEditor; + /** + * class 이름 + * @type {String} + */ + name: "SE2M_QuickEditor_Common", + /** + * 환경 정보. + * @type {Object} + */ + _environmentData: "", + /** + * 현재 타입 (table|img) + * @type {String} + */ + _currentType: "", + /** + * 이벤트가 레이어 안에서 호출되었는지 알기 위한 변수 + * @type {Boolean} + */ + _in_event: false, + /** + * Ajax처리를 하지 않음 + * @type {Boolean} + */ + _bUseConfig: false, - if(htConfiguration){ - this._bUseConfig = (!!htConfiguration.common && typeof htConfiguration.common.bUseConfig !== "undefined") ? htConfiguration.common.bUseConfig : true; - } + /** + * 공통 서버에서 개인 설정 받아오는 AjaxUrl + * @See SE2M_Configuration.js + */ + _sBaseAjaxUrl: "", + _sAddTextAjaxUrl: "", - if(!this._bUseConfig){ - this.setData("{table:'full',img:'full',review:'full'}"); - } else { - this._sBaseAjaxUrl = htConfiguration.common.sBaseAjaxUrl; - this._sAddTextAjaxUrl = htConfiguration.common.sAddTextAjaxUrl; - - this.getData(); - } - this.oApp.registerLazyMessage(["OPEN_QE_LAYER"], ["hp_SE2M_QuickEditor_Common$Lazy.js"]); - }, - - //삭제 시에 qe layer close - $ON_EVENT_EDITING_AREA_KEYDOWN : function(oEvent){ - var oKeyInfo = oEvent.key(); - //Backspace : 8, Delete :46 - if (oKeyInfo.keyCode == 8 || oKeyInfo.keyCode == 46 ) { - // [SMARTEDITORSUS-1213][IE9, 10] 사진 삭제 후 zindex 1000인 div가 잔존하는데, 그 위로 썸네일 drag를 시도하다 보니 drop이 불가능. - var htBrowser = jindo.$Agent().navigator(); - if(htBrowser.ie && htBrowser.nativeVersion > 8){ - var elFirstChild = jindo.$$.getSingle("DIV.husky_seditor_editing_area_container").childNodes[0]; - if((elFirstChild.tagName == "DIV") && (elFirstChild.style.zIndex == 1000)){ - elFirstChild.parentNode.removeChild(elFirstChild); - } - } - // --[SMARTEDITORSUS-1213] - this.oApp.exec("CLOSE_QE_LAYER", [oEvent]); - } - }, - - getData : function() { - var self = this; - jindo.$Ajax(self._sBaseAjaxUrl, { - type : "jsonp", - timeout : 1, - onload: function(rp) { - var result = rp.json().result; - // [SMARTEDITORSUS-1028][SMARTEDITORSUS-1517] QuickEditor 설정 API 개선 - //if (!!result && !!result.length) { - if (!!result && !!result.text_data) { - //self.setData(result[result.length - 1]); - self.setData(result.text_data); - } else { - self.setData("{table:'full',img:'full',review:'full'}"); - } - // --[SMARTEDITORSUS-1028][SMARTEDITORSUS-1517] - }, - - onerror : function() { - self.setData("{table:'full',img:'full',review:'full'}"); - }, - - ontimeout : function() { - self.setData("{table:'full',img:'full',review:'full'}"); - } - }).request({ text_key : "qeditor_fold" }); - }, - - setData : function(sResult){ - var oResult = { - table : "full", - img : "full", - review : "full" - }; - - if(sResult){ - oResult = eval("("+sResult+")"); - } - - this._environmentData = { - table : { - isOpen : false, - type : oResult["table"],//full,fold, - isFixed : false, - position : [] - }, - img : { - isOpen : false, - type : oResult["img"],//full,fold - isFixed : false - }, - review : { - isOpen : false, - type : oResult["review"],//full,fold - isFixed : false, - position : [] - } - }; - - - this.waTableTagNames =jindo.$A(["table","tbody","td","tfoot","th","thead","tr"]); - }, - - /** - * 위지윅 영역에 단축키가 등록될 때, - * tab 과 shift+tab (들여쓰기 / 내어쓰기 ) 를 제외한 단축키 리스트를 저장한다. - */ - $ON_REGISTER_HOTKEY : function(sHotkey, sCMD, aArgs){ - if(sHotkey != "tab" && sHotkey != "shift+tab"){ - this.waHotkeys.push([sHotkey, sCMD, aArgs]); - } - } + /** + * 초기 인스턴스 생성 실행되는 함수. + */ + $init: function () { + this.waHotkeys = new jindo.$A([]); + this.waHotkeyLayers = new jindo.$A([]); + }, + + $ON_MSG_APP_READY: function () { + var htConfiguration = nhn.husky.SE2M_Configuration.QuickEditor; + + if (htConfiguration) { + this._bUseConfig = (!!htConfiguration.common && typeof htConfiguration.common.bUseConfig !== "undefined") ? htConfiguration.common.bUseConfig : true; + } + + if (!this._bUseConfig) { + this.setData("{table:'full',img:'full',review:'full'}"); + } else { + this._sBaseAjaxUrl = htConfiguration.common.sBaseAjaxUrl; + this._sAddTextAjaxUrl = htConfiguration.common.sAddTextAjaxUrl; + + this.getData(); + } + this.oApp.registerLazyMessage(["OPEN_QE_LAYER"], ["hp_SE2M_QuickEditor_Common$Lazy.js"]); + }, + + //삭제 시에 qe layer close + $ON_EVENT_EDITING_AREA_KEYDOWN: function (oEvent) { + var oKeyInfo = oEvent.key(); + //Backspace : 8, Delete :46 + if (oKeyInfo.keyCode == 8 || oKeyInfo.keyCode == 46) { + // [SMARTEDITORSUS-1213][IE9, 10] 사진 삭제 후 zindex 1000인 div가 잔존하는데, 그 위로 썸네일 drag를 시도하다 보니 drop이 불가능. + var htBrowser = jindo.$Agent().navigator(); + if (htBrowser.ie && htBrowser.nativeVersion > 8) { + var elFirstChild = jindo.$$.getSingle("DIV.husky_seditor_editing_area_container").childNodes[0]; + if ((elFirstChild.tagName == "DIV") && (elFirstChild.style.zIndex == 1000)) { + elFirstChild.parentNode.removeChild(elFirstChild); + } + } + // --[SMARTEDITORSUS-1213] + this.oApp.exec("CLOSE_QE_LAYER", [oEvent]); + } + }, + + getData: function () { + var self = this; + jindo.$Ajax(self._sBaseAjaxUrl, { + type: "jsonp", + timeout: 1, + onload: function (rp) { + var result = rp.json().result; + // [SMARTEDITORSUS-1028][SMARTEDITORSUS-1517] QuickEditor 설정 API 개선 + //if (!!result && !!result.length) { + if (!!result && !!result.text_data) { + //self.setData(result[result.length - 1]); + self.setData(result.text_data); + } else { + self.setData("{table:'full',img:'full',review:'full'}"); + } + // --[SMARTEDITORSUS-1028][SMARTEDITORSUS-1517] + }, + + onerror: function () { + self.setData("{table:'full',img:'full',review:'full'}"); + }, + + ontimeout: function () { + self.setData("{table:'full',img:'full',review:'full'}"); + } + }).request({ text_key: "qeditor_fold" }); + }, + + setData: function (sResult) { + var oResult = { + table: "full", + img: "full", + review: "full" + }; + + if (sResult) { + oResult = eval("(" + sResult + ")"); + } + + this._environmentData = { + table: { + isOpen: false, + type: oResult["table"],//full,fold, + isFixed: false, + position: [] + }, + img: { + isOpen: false, + type: oResult["img"],//full,fold + isFixed: false + }, + review: { + isOpen: false, + type: oResult["review"],//full,fold + isFixed: false, + position: [] + } + }; + + + this.waTableTagNames = jindo.$A(["table", "tbody", "td", "tfoot", "th", "thead", "tr"]); + }, + + /** + * 위지윅 영역에 단축키가 등록될 때, + * tab 과 shift+tab (들여쓰기 / 내어쓰기 ) 를 제외한 단축키 리스트를 저장한다. + */ + $ON_REGISTER_HOTKEY: function (sHotkey, sCMD, aArgs) { + if (sHotkey != "tab" && sHotkey != "shift+tab") { + this.waHotkeys.push([sHotkey, sCMD, aArgs]); + } + } }); /** * @classDescription shortcut * @author AjaxUI Lab - mixed */ -function Shortcut(sKey,sId){ - var sKey = sKey.replace(/\s+/g,""); - var store = Shortcut.Store; - var action = Shortcut.Action; - if(typeof sId === "undefined"&&sKey.constructor == String){ - store.set("document",sKey,document); - return action.init(store.get("document"),sKey); - }else if(sId.constructor == String&&sKey.constructor == String){ - store.set(sId,sKey,jindo.$(sId)); - return action.init(store.get(sId),sKey); - }else if(sId.constructor != String&&sKey.constructor == String){ - var fakeId = "nonID"+new Date().getTime(); - fakeId = Shortcut.Store.searchId(fakeId,sId); - store.set(fakeId,sKey,sId); - return action.init(store.get(fakeId),sKey); - } - alert(sId+"는 반드시 string이거나 없어야 됩니다."); +function Shortcut(sKey, sId) { + var sKey = sKey.replace(/\s+/g, ""); + var store = Shortcut.Store; + var action = Shortcut.Action; + if (typeof sId === "undefined" && sKey.constructor == String) { + store.set("document", sKey, document); + return action.init(store.get("document"), sKey); + } else if (sId.constructor == String && sKey.constructor == String) { + store.set(sId, sKey, jindo.$(sId)); + return action.init(store.get(sId), sKey); + } else if (sId.constructor != String && sKey.constructor == String) { + var fakeId = "nonID" + new Date().getTime(); + fakeId = Shortcut.Store.searchId(fakeId, sId); + store.set(fakeId, sKey, sId); + return action.init(store.get(fakeId), sKey); + } + alert(sId + "는 반드시 string이거나 없어야 됩니다."); }; Shortcut.Store = { - anthorKeyHash:{}, - datas:{}, - currentId:"", - currentKey:"", - searchId:function(sId,oElement){ - // [SMARTEDITORSUS-2103] - var isElementInData = false; - jindo.$H(this.datas).forEach(function(oValue,sKey){ - if(oElement == oValue.element){ - sId = sKey; - isElementInData = true; - jindo.$H.Break(); - } - }); + anthorKeyHash: {}, + datas: {}, + currentId: "", + currentKey: "", + searchId: function (sId, oElement) { + // [SMARTEDITORSUS-2103] + var isElementInData = false; + jindo.$H(this.datas).forEach(function (oValue, sKey) { + if (oElement == oValue.element) { + sId = sKey; + isElementInData = true; + jindo.$H.Break(); + } + }); - /* - * element context는 서로 다르지만 - * 이미 추가된 Shortcut.Data 객체와 timestamp가 중복되는 경우, - * - * 원래 동작해야 할 element context가 아닌, - * 이미 추가된 element context에서 잘못 동작할 수 있다. - * - * 이 경우에는 timestamp 기반 id를 새롭개 생성해서 - * 원래 동작해야 할 element context에서 잘 동작할 수 있도록 한다. - */ - if(!isElementInData && (sId in this.datas)){ - var newFakeId = sId; - while(newFakeId in this.datas){ - newFakeId = "nonID"+new Date().getTime(); - } - return newFakeId; - } - - return sId; - // --[SMARTEDITORSUS-2103] - }, - set:function(sId,sKey,oElement){ - this.currentId = sId; - this.currentKey = sKey; - var idData = this.get(sId); - this.datas[sId] = idData?idData.createKey(sKey):new Shortcut.Data(sId,sKey,oElement); - }, - get:function(sId,sKey){ - if(sKey){ - return this.datas[sId].keys[sKey]; - }else{ - return this.datas[sId]; - } - }, - reset:function(sId){ - var data = this.datas[sId]; - Shortcut.Helper.bind(data.func,data.element,"detach"); - - delete this.datas[sId]; - }, - allReset: function(){ - jindo.$H(this.datas).forEach(jindo.$Fn(function(value,key) { - this.reset(key); - },this).bind()); - } + /* + * element context는 서로 다르지만 + * 이미 추가된 Shortcut.Data 객체와 timestamp가 중복되는 경우, + * + * 원래 동작해야 할 element context가 아닌, + * 이미 추가된 element context에서 잘못 동작할 수 있다. + * + * 이 경우에는 timestamp 기반 id를 새롭개 생성해서 + * 원래 동작해야 할 element context에서 잘 동작할 수 있도록 한다. + */ + if (!isElementInData && (sId in this.datas)) { + var newFakeId = sId; + while (newFakeId in this.datas) { + newFakeId = "nonID" + new Date().getTime(); + } + return newFakeId; + } + + return sId; + // --[SMARTEDITORSUS-2103] + }, + set: function (sId, sKey, oElement) { + this.currentId = sId; + this.currentKey = sKey; + var idData = this.get(sId); + this.datas[sId] = idData ? idData.createKey(sKey) : new Shortcut.Data(sId, sKey, oElement); + }, + get: function (sId, sKey) { + if (sKey) { + return this.datas[sId].keys[sKey]; + } else { + return this.datas[sId]; + } + }, + reset: function (sId) { + var data = this.datas[sId]; + Shortcut.Helper.bind(data.func, data.element, "detach"); + + delete this.datas[sId]; + }, + allReset: function () { + jindo.$H(this.datas).forEach(jindo.$Fn(function (value, key) { + this.reset(key); + }, this).bind()); + } }; Shortcut.Data = jindo.$Class({ - $init:function(sId,sKey,oElement){ - this.id = sId; - this.element = oElement; - this.func = jindo.$Fn(this.fire,this).bind(); - - Shortcut.Helper.bind(this.func,oElement,"attach"); - this.keys = {}; - this.keyStemp = {}; - this.createKey(sKey); - }, - createKey:function(sKey){ - this.keyStemp[Shortcut.Helper.keyInterpretor(sKey)] = sKey; - this.keys[sKey] = {}; - var data = this.keys[sKey]; - data.key = sKey; - data.events = []; - data.commonExceptions = []; -// data.keyAnalysis = Shortcut.Helper.keyInterpretor(sKey); - data.stopDefalutBehavior = true; - - return this; - }, - getKeyStamp : function(eEvent){ - - - var sKey = eEvent.keyCode || eEvent.charCode; - var returnVal = ""; - - returnVal += eEvent.altKey?"1":"0"; - returnVal += eEvent.ctrlKey?"1":"0"; - returnVal += eEvent.metaKey?"1":"0"; - returnVal += eEvent.shiftKey?"1":"0"; - returnVal += sKey; - return returnVal; - }, - fire:function(eEvent){ - eEvent = eEvent||window.eEvent; - - var oMatchKeyData = this.keyStemp[this.getKeyStamp(eEvent)]; - - if(oMatchKeyData){ - this.excute(new jindo.$Event(eEvent),oMatchKeyData); - } - - }, - excute:function(weEvent,sRawKey){ - var isExcute = true; - var staticFun = Shortcut.Helper; - var data = this.keys[sRawKey]; - - if(staticFun.notCommonException(weEvent,data.commonExceptions)){ - jindo.$A(data.events).forEach(function(v){ - if(data.stopDefalutBehavior){ - var leng = v.exceptions.length; - if(leng){ - for(var i=0;i", - sDefaultXPath : "/HTML[0]/BODY[0]", + name: "SE_UndoRedo", - $init : function(){ - this.aUndoHistory = []; - this.oCurStateIdx = {nIdx: 0, nStep: 0}; - this.nHardLimit = this.nMaxUndoCount + this.nAfterMaxDeleteBuffer; - }, + oCurStateIdx: null, + iMinimumSizeChange: 1, - $LOCAL_BEFORE_ALL : function(sCmd){ - if(sCmd.match(/_DO_RECORD_UNDO_HISTORY_AT$/)){ - return true; - } + // limit = nMaxUndoCount + nAfterMaxDeleteBuffer. When the limit is reached delete [0...nAfterMaxDeleteBuffer] so only nMaxUndoCount histories will be left + nMaxUndoCount: 20, // 1000 + nAfterMaxDeleteBuffer: 100, - try{ - if(this.oApp.getEditingMode() != "WYSIWYG"){ - return false; - } - }catch(e){ - return false; - } - - return true; - }, - - $BEFORE_MSG_APP_READY : function(){ - this._historyLength = 0; - this.oApp.exec("ADD_APP_PROPERTY", ["getUndoHistory", jindo.$Fn(this._getUndoHistory, this).bind()]); - this.oApp.exec("ADD_APP_PROPERTY", ["getUndoStateIdx", jindo.$Fn(this._getUndoStateIdx, this).bind()]); - this.oApp.exec("ADD_APP_PROPERTY", ["saveSnapShot", jindo.$Fn(this._saveSnapShot, this).bind()]); - this.oApp.exec("ADD_APP_PROPERTY", ["getLastKey", jindo.$Fn(this._getLastKey, this).bind()]); - this.oApp.exec("ADD_APP_PROPERTY", ["setLastKey", jindo.$Fn(this._setLastKey, this).bind()]); + sBlankContentsForFF: "
    ", + sDefaultXPath: "/HTML[0]/BODY[0]", - this._saveSnapShot(); - - this.oApp.exec("DO_RECORD_UNDO_HISTORY_AT", [this.oCurStateIdx, "", "", "", null, this.sDefaultXPath]); - }, - - _getLastKey : function(){ - return this.sLastKey; - }, + $init: function () { + this.aUndoHistory = []; + this.oCurStateIdx = { nIdx: 0, nStep: 0 }; + this.nHardLimit = this.nMaxUndoCount + this.nAfterMaxDeleteBuffer; + }, - _setLastKey : function(sLastKey){ - this.sLastKey = sLastKey; - }, - - $ON_MSG_APP_READY : function(){ - var oNavigator = jindo.$Agent().navigator(); - this.bIE = oNavigator.ie; - this.bFF = oNavigator.firefox; - //this.bChrome = oNavigator.chrome; - //this.bSafari = oNavigator.safari; + $LOCAL_BEFORE_ALL: function (sCmd) { + if (sCmd.match(/_DO_RECORD_UNDO_HISTORY_AT$/)) { + return true; + } - this.oApp.exec("REGISTER_UI_EVENT", ["undo", "click", "UNDO"]); - this.oApp.exec("REGISTER_UI_EVENT", ["redo", "click", "REDO"]); + try { + if (this.oApp.getEditingMode() != "WYSIWYG") { + return false; + } + } catch (e) { + return false; + } - // [SMARTEDITORSUS-2260] 메일 > Mac에서 ctrl 조합 단축키 모두 meta 조합으로 변경 - if (jindo.$Agent().os().mac) { - this.oApp.exec("REGISTER_HOTKEY", ["meta+z", "UNDO"]); - this.oApp.exec("REGISTER_HOTKEY", ["meta+y", "REDO"]); - } else { - this.oApp.exec("REGISTER_HOTKEY", ["ctrl+z", "UNDO"]); - this.oApp.exec("REGISTER_HOTKEY", ["ctrl+y", "REDO"]); - } - - // this.htOptions = this.oApp.htOptions["SE_UndoRedo"] || {}; - }, - - $ON_UNDO : function(){ - this._doRecordUndoHistory("UNDO", { nStep : 0, bSkipIfEqual : true, bMustBlockContainer : true }); - - if(this.oCurStateIdx.nIdx <= 0){ - return; - } - - // 현재의 상태에서 Undo 했을 때 처리해야 할 메시지 호출 - var oUndoCallback = this.aUndoHistory[this.oCurStateIdx.nIdx].oUndoCallback[this.oCurStateIdx.nStep]; - var sCurrentPath = this.aUndoHistory[this.oCurStateIdx.nIdx].sParentXPath[this.oCurStateIdx.nStep]; - - if(oUndoCallback){ - this.oApp.exec(oUndoCallback.sMsg, oUndoCallback.aParams); - } + return true; + }, - if(this.oCurStateIdx.nStep > 0){ - this.oCurStateIdx.nStep--; - }else{ - var oTmpHistory = this.aUndoHistory[this.oCurStateIdx.nIdx]; + $BEFORE_MSG_APP_READY: function () { + this._historyLength = 0; + this.oApp.exec("ADD_APP_PROPERTY", ["getUndoHistory", jindo.$Fn(this._getUndoHistory, this).bind()]); + this.oApp.exec("ADD_APP_PROPERTY", ["getUndoStateIdx", jindo.$Fn(this._getUndoStateIdx, this).bind()]); + this.oApp.exec("ADD_APP_PROPERTY", ["saveSnapShot", jindo.$Fn(this._saveSnapShot, this).bind()]); + this.oApp.exec("ADD_APP_PROPERTY", ["getLastKey", jindo.$Fn(this._getLastKey, this).bind()]); + this.oApp.exec("ADD_APP_PROPERTY", ["setLastKey", jindo.$Fn(this._setLastKey, this).bind()]); - this.oCurStateIdx.nIdx--; + this._saveSnapShot(); - if(oTmpHistory.nTotalSteps>1){ - this.oCurStateIdx.nStep = 0; - }else{ - oTmpHistory = this.aUndoHistory[this.oCurStateIdx.nIdx]; - this.oCurStateIdx.nStep = oTmpHistory.nTotalSteps-1; - } - } - - var sUndoHistoryPath = this.aUndoHistory[this.oCurStateIdx.nIdx].sParentXPath[this.oCurStateIdx.nStep]; - var bUseDefault = false; - - if(sUndoHistoryPath !== sCurrentPath && sUndoHistoryPath.indexOf(sCurrentPath) === 0){ // 현재의 Path가 Undo의 Path보다 범위가 큰 경우 - bUseDefault = true; - } + this.oApp.exec("DO_RECORD_UNDO_HISTORY_AT", [this.oCurStateIdx, "", "", "", null, this.sDefaultXPath]); + }, - this.oApp.exec("RESTORE_UNDO_HISTORY", [this.oCurStateIdx.nIdx, this.oCurStateIdx.nStep, bUseDefault]); - this.oApp.exec("CHECK_STYLE_CHANGE", []); - - this.sLastKey = null; - }, + _getLastKey: function () { + return this.sLastKey; + }, + + _setLastKey: function (sLastKey) { + this.sLastKey = sLastKey; + }, + + $ON_MSG_APP_READY: function () { + var oNavigator = jindo.$Agent().navigator(); + this.bIE = oNavigator.ie; + this.bFF = oNavigator.firefox; + //this.bChrome = oNavigator.chrome; + //this.bSafari = oNavigator.safari; + + this.oApp.exec("REGISTER_UI_EVENT", ["undo", "click", "UNDO"]); + this.oApp.exec("REGISTER_UI_EVENT", ["redo", "click", "REDO"]); + + // [SMARTEDITORSUS-2260] 메일 > Mac에서 ctrl 조합 단축키 모두 meta 조합으로 변경 + if (jindo.$Agent().os().mac) { + this.oApp.exec("REGISTER_HOTKEY", ["meta+z", "UNDO"]); + this.oApp.exec("REGISTER_HOTKEY", ["meta+y", "REDO"]); + } else { + this.oApp.exec("REGISTER_HOTKEY", ["ctrl+z", "UNDO"]); + this.oApp.exec("REGISTER_HOTKEY", ["ctrl+y", "REDO"]); + } + + // this.htOptions = this.oApp.htOptions["SE_UndoRedo"] || {}; + }, + + $ON_UNDO: function () { + this._doRecordUndoHistory("UNDO", { nStep: 0, bSkipIfEqual: true, bMustBlockContainer: true }); + + if (this.oCurStateIdx.nIdx <= 0) { + return; + } + + // 현재의 상태에서 Undo 했을 때 처리해야 할 메시지 호출 + var oUndoCallback = this.aUndoHistory[this.oCurStateIdx.nIdx].oUndoCallback[this.oCurStateIdx.nStep]; + var sCurrentPath = this.aUndoHistory[this.oCurStateIdx.nIdx].sParentXPath[this.oCurStateIdx.nStep]; + + if (oUndoCallback) { + this.oApp.exec(oUndoCallback.sMsg, oUndoCallback.aParams); + } + + if (this.oCurStateIdx.nStep > 0) { + this.oCurStateIdx.nStep--; + } else { + var oTmpHistory = this.aUndoHistory[this.oCurStateIdx.nIdx]; + + this.oCurStateIdx.nIdx--; + + if (oTmpHistory.nTotalSteps > 1) { + this.oCurStateIdx.nStep = 0; + } else { + oTmpHistory = this.aUndoHistory[this.oCurStateIdx.nIdx]; + this.oCurStateIdx.nStep = oTmpHistory.nTotalSteps - 1; + } + } + + var sUndoHistoryPath = this.aUndoHistory[this.oCurStateIdx.nIdx].sParentXPath[this.oCurStateIdx.nStep]; + var bUseDefault = false; + + if (sUndoHistoryPath !== sCurrentPath && sUndoHistoryPath.indexOf(sCurrentPath) === 0) { // 현재의 Path가 Undo의 Path보다 범위가 큰 경우 + bUseDefault = true; + } + + this.oApp.exec("RESTORE_UNDO_HISTORY", [this.oCurStateIdx.nIdx, this.oCurStateIdx.nStep, bUseDefault]); + this.oApp.exec("CHECK_STYLE_CHANGE", []); + + this.sLastKey = null; + }, - $ON_REDO : function(){ - if(this.oCurStateIdx.nIdx >= this.aUndoHistory.length){ - return; - } + $ON_REDO: function () { + if (this.oCurStateIdx.nIdx >= this.aUndoHistory.length) { + return; + } - var oCurHistory = this.aUndoHistory[this.oCurStateIdx.nIdx]; - - if(this.oCurStateIdx.nIdx == this.aUndoHistory.length-1 && this.oCurStateIdx.nStep >= oCurHistory.nTotalSteps-1){ - return; - } - - if(this.oCurStateIdx.nStep < oCurHistory.nTotalSteps-1){ - this.oCurStateIdx.nStep++; - }else{ - this.oCurStateIdx.nIdx++; - oCurHistory = this.aUndoHistory[this.oCurStateIdx.nIdx]; - this.oCurStateIdx.nStep = oCurHistory.nTotalSteps-1; - } + var oCurHistory = this.aUndoHistory[this.oCurStateIdx.nIdx]; - // 원복될 상태에서 Redo 했을 때 처리해야 할 메시지 호출 - var oRedoCallback = this.aUndoHistory[this.oCurStateIdx.nIdx].oRedoCallback[this.oCurStateIdx.nStep]; - - if(oRedoCallback){ - this.oApp.exec(oRedoCallback.sMsg, oRedoCallback.aParams); - } + if (this.oCurStateIdx.nIdx == this.aUndoHistory.length - 1 && this.oCurStateIdx.nStep >= oCurHistory.nTotalSteps - 1) { + return; + } - this.oApp.exec("RESTORE_UNDO_HISTORY", [this.oCurStateIdx.nIdx, this.oCurStateIdx.nStep]); - this.oApp.exec("CHECK_STYLE_CHANGE", []); - - this.sLastKey = null; - }, + if (this.oCurStateIdx.nStep < oCurHistory.nTotalSteps - 1) { + this.oCurStateIdx.nStep++; + } else { + this.oCurStateIdx.nIdx++; + oCurHistory = this.aUndoHistory[this.oCurStateIdx.nIdx]; + this.oCurStateIdx.nStep = oCurHistory.nTotalSteps - 1; + } - $ON_RECORD_UNDO_ACTION : function(sAction, oSaveOption){ - oSaveOption = oSaveOption || { sSaveTarget : null, elSaveTarget : null, bMustBlockElement : false, bMustBlockContainer : false, bDontSaveSelection : false }; - oSaveOption.nStep = 0; - oSaveOption.bSkipIfEqual = false; - oSaveOption.bTwoStepAction = false; - - this._doRecordUndoHistory(sAction, oSaveOption); - }, + // 원복될 상태에서 Redo 했을 때 처리해야 할 메시지 호출 + var oRedoCallback = this.aUndoHistory[this.oCurStateIdx.nIdx].oRedoCallback[this.oCurStateIdx.nStep]; - $ON_RECORD_UNDO_BEFORE_ACTION : function(sAction, oSaveOption){ - oSaveOption = oSaveOption || { sSaveTarget : null, elSaveTarget : null, bMustBlockElement : false, bMustBlockContainer : false, bDontSaveSelection : false }; - oSaveOption.nStep = 0; - oSaveOption.bSkipIfEqual = false; - oSaveOption.bTwoStepAction = true; - - this._doRecordUndoHistory(sAction, oSaveOption); - }, + if (oRedoCallback) { + this.oApp.exec(oRedoCallback.sMsg, oRedoCallback.aParams); + } - $ON_RECORD_UNDO_AFTER_ACTION : function(sAction, oSaveOption){ - oSaveOption = oSaveOption || { sSaveTarget : null, elSaveTarget : null, bMustBlockElement : false, bMustBlockContainer : false, bDontSaveSelection : false }; - oSaveOption.nStep = 1; - oSaveOption.bSkipIfEqual = false; - oSaveOption.bTwoStepAction = true; - - this._doRecordUndoHistory(sAction, oSaveOption); - }, + this.oApp.exec("RESTORE_UNDO_HISTORY", [this.oCurStateIdx.nIdx, this.oCurStateIdx.nStep]); + this.oApp.exec("CHECK_STYLE_CHANGE", []); - $ON_RESTORE_UNDO_HISTORY : function(nUndoIdx, nUndoStateStep, bUseDefault){ - this.oApp.exec("HIDE_ACTIVE_LAYER"); + this.sLastKey = null; + }, - this.oCurStateIdx.nIdx = nUndoIdx; - this.oCurStateIdx.nStep = nUndoStateStep; + $ON_RECORD_UNDO_ACTION: function (sAction, oSaveOption) { + oSaveOption = oSaveOption || { sSaveTarget: null, elSaveTarget: null, bMustBlockElement: false, bMustBlockContainer: false, bDontSaveSelection: false }; + oSaveOption.nStep = 0; + oSaveOption.bSkipIfEqual = false; + oSaveOption.bTwoStepAction = false; - var oCurHistory = this.aUndoHistory[this.oCurStateIdx.nIdx], - sContent = oCurHistory.sContent[this.oCurStateIdx.nStep], - sFullContents = oCurHistory.sFullContents[this.oCurStateIdx.nStep], - oBookmark = oCurHistory.oBookmark[this.oCurStateIdx.nStep], - sParentXPath = oCurHistory.sParentXPath[this.oCurStateIdx.nStep], - oParent = null, - sCurContent = "", - oSelection = this.oApp.getEmptySelection(); - - this.oApp.exec("RESTORE_IE_SELECTION"); // this is done to null the ie selection - - if(bUseDefault){ - this.oApp.getWYSIWYGDocument().body.innerHTML = sFullContents; - sFullContents = this.oApp.getWYSIWYGDocument().body.innerHTML; - sCurContent = sFullContents; - sParentXPath = this.sDefaultXPath; - }else{ - oParent = oSelection._evaluateXPath(sParentXPath, oSelection._document); - try{ - oParent.innerHTML = sContent; - sCurContent = oParent.innerHTML; - }catch(e){ // Path 노드를 찾지 못하는 경우 - this.oApp.getWYSIWYGDocument().body.innerHTML = sFullContents; - sFullContents = this.oApp.getWYSIWYGDocument().body.innerHTML; // setting the innerHTML may change the internal DOM structure, so save the value again. - sCurContent = sFullContents; - sParentXPath = this.sDefaultXPath; - } - } + this._doRecordUndoHistory(sAction, oSaveOption); + }, - if(this.bFF && sCurContent == this.sBlankContentsForFF){ - sCurContent = ""; - } - - oCurHistory.sFullContents[this.oCurStateIdx.nStep] = sFullContents; - oCurHistory.sContent[this.oCurStateIdx.nStep] = sCurContent; - oCurHistory.sParentXPath[this.oCurStateIdx.nStep] = sParentXPath; + $ON_RECORD_UNDO_BEFORE_ACTION: function (sAction, oSaveOption) { + oSaveOption = oSaveOption || { sSaveTarget: null, elSaveTarget: null, bMustBlockElement: false, bMustBlockContainer: false, bDontSaveSelection: false }; + oSaveOption.nStep = 0; + oSaveOption.bSkipIfEqual = false; + oSaveOption.bTwoStepAction = true; - if(oBookmark && oBookmark.sType == "scroll"){ - setTimeout(jindo.$Fn(function(){this.oApp.getWYSIWYGDocument().documentElement.scrollTop = oBookmark.nScrollTop;}, this).bind(), 0); - }else{ - oSelection = this.oApp.getEmptySelection(); - if(oSelection.selectionLoaded){ - if(oBookmark){ - oSelection.moveToXPathBookmark(oBookmark); - }else{ - oSelection = this.oApp.getEmptySelection(); - } - - oSelection.select(); - } - } - }, - - _doRecordUndoHistory : function(sAction, htRecordOption){ - /* - htRecordOption = { - nStep : 0 | 1, - bSkipIfEqual : false | true, - bTwoStepAction : false | true, - sSaveTarget : [TAG] | null - elSaveTarget : [Element] | null - bDontSaveSelection : false | true - bMustBlockElement : false | true - bMustBlockContainer : false | true - }; - */ - - htRecordOption = htRecordOption || {}; - - var nStep = htRecordOption.nStep || 0, - bSkipIfEqual = htRecordOption.bSkipIfEqual || false, - bTwoStepAction = htRecordOption.bTwoStepAction || false, - sSaveTarget = htRecordOption.sSaveTarget || null, - elSaveTarget = htRecordOption.elSaveTarget || null, - bDontSaveSelection = htRecordOption.bDontSaveSelection || false, - bMustBlockElement = htRecordOption.bMustBlockElement || false, - bMustBlockContainer = htRecordOption.bMustBlockContainer || false, - oUndoCallback = htRecordOption.oUndoCallback, - oRedoCallback = htRecordOption.oRedoCallback; - - // if we're in the middle of some action history, - // remove everything after current idx if any "little" change is made - this._historyLength = this.aUndoHistory.length; - - if(this.oCurStateIdx.nIdx !== this._historyLength-1){ - bSkipIfEqual = true; - } + this._doRecordUndoHistory(sAction, oSaveOption); + }, - var oCurHistory = this.aUndoHistory[this.oCurStateIdx.nIdx], - sHistoryFullContents = oCurHistory.sFullContents[this.oCurStateIdx.nStep], - sCurContent = "", - sFullContents = "", - sParentXPath = "", - oBookmark = null, - oSelection = null, - oInsertionIdx = {nIdx:this.oCurStateIdx.nIdx, nStep:this.oCurStateIdx.nStep}; // 히스토리를 저장할 위치 + $ON_RECORD_UNDO_AFTER_ACTION: function (sAction, oSaveOption) { + oSaveOption = oSaveOption || { sSaveTarget: null, elSaveTarget: null, bMustBlockElement: false, bMustBlockContainer: false, bDontSaveSelection: false }; + oSaveOption.nStep = 1; + oSaveOption.bSkipIfEqual = false; + oSaveOption.bTwoStepAction = true; - oSelection = this.oApp.getSelection(); - - if(oSelection.selectionLoaded){ - oBookmark = oSelection.getXPathBookmark(); - } - - if(elSaveTarget){ - sParentXPath = oSelection._getXPath(elSaveTarget); - }else if(sSaveTarget){ - sParentXPath = this._getTargetXPath(oBookmark, sSaveTarget); - }else{ - sParentXPath = this._getParentXPath(oBookmark, bMustBlockElement, bMustBlockContainer); - } - - sFullContents = this.oApp.getWYSIWYGDocument().body.innerHTML; - // sCurContent = this.oApp.getWYSIWYGDocument().body.innerHTML.replace(/ *_cssquery_UID="[^"]+" */g, ""); + this._doRecordUndoHistory(sAction, oSaveOption); + }, - if(sParentXPath === this.sDefaultXPath){ - sCurContent = sFullContents; - }else{ - sCurContent = oSelection._evaluateXPath(sParentXPath, oSelection._document).innerHTML; - } + $ON_RESTORE_UNDO_HISTORY: function (nUndoIdx, nUndoStateStep, bUseDefault) { + this.oApp.exec("HIDE_ACTIVE_LAYER"); - if(this.bFF && sCurContent == this.sBlankContentsForFF){ - sCurContent = ""; - } + this.oCurStateIdx.nIdx = nUndoIdx; + this.oCurStateIdx.nStep = nUndoStateStep; - // every TwoStepAction needs to be recorded - if(!bTwoStepAction && bSkipIfEqual){ - if(sHistoryFullContents.length === sFullContents.length){ - return; - } - - // 저장된 데이터와 같음에도 다르다고 처리되는 경우에 대한 처리 - // (예, P안에 Block엘리먼트가 추가된 경우 P를 분리) - //if(this.bChrome || this.bSafari){ - var elCurrentDiv = document.createElement("div"); - var elHistoryDiv = document.createElement("div"); + var oCurHistory = this.aUndoHistory[this.oCurStateIdx.nIdx], + sContent = oCurHistory.sContent[this.oCurStateIdx.nStep], + sFullContents = oCurHistory.sFullContents[this.oCurStateIdx.nStep], + oBookmark = oCurHistory.oBookmark[this.oCurStateIdx.nStep], + sParentXPath = oCurHistory.sParentXPath[this.oCurStateIdx.nStep], + oParent = null, + sCurContent = "", + oSelection = this.oApp.getEmptySelection(); - elCurrentDiv.innerHTML = sFullContents; - elHistoryDiv.innerHTML = sHistoryFullContents; - - var elDocFragment = document.createDocumentFragment(); - elDocFragment.appendChild(elCurrentDiv); - elDocFragment.appendChild(elHistoryDiv); + this.oApp.exec("RESTORE_IE_SELECTION"); // this is done to null the ie selection - sFullContents = elCurrentDiv.innerHTML; - sHistoryFullContents = elHistoryDiv.innerHTML; + if (bUseDefault) { + this.oApp.getWYSIWYGDocument().body.innerHTML = sFullContents; + sFullContents = this.oApp.getWYSIWYGDocument().body.innerHTML; + sCurContent = sFullContents; + sParentXPath = this.sDefaultXPath; + } else { + oParent = oSelection._evaluateXPath(sParentXPath, oSelection._document); + try { + oParent.innerHTML = sContent; + sCurContent = oParent.innerHTML; + } catch (e) { // Path 노드를 찾지 못하는 경우 + this.oApp.getWYSIWYGDocument().body.innerHTML = sFullContents; + sFullContents = this.oApp.getWYSIWYGDocument().body.innerHTML; // setting the innerHTML may change the internal DOM structure, so save the value again. + sCurContent = sFullContents; + sParentXPath = this.sDefaultXPath; + } + } - elCurrentDiv = null; - elHistoryDiv = null; - elDocFragment = null; + if (this.bFF && sCurContent == this.sBlankContentsForFF) { + sCurContent = ""; + } - if(sHistoryFullContents.length === sFullContents.length){ - return; - } - //} - } - - if(bDontSaveSelection){ - oBookmark = { sType : "scroll", nScrollTop : this.oApp.getWYSIWYGDocument().documentElement.scrollTop }; - } - - oInsertionIdx.nStep = nStep; + oCurHistory.sFullContents[this.oCurStateIdx.nStep] = sFullContents; + oCurHistory.sContent[this.oCurStateIdx.nStep] = sCurContent; + oCurHistory.sParentXPath[this.oCurStateIdx.nStep] = sParentXPath; - if(oInsertionIdx.nStep === 0 && this.oCurStateIdx.nStep === oCurHistory.nTotalSteps-1){ - oInsertionIdx.nIdx = this.oCurStateIdx.nIdx+1; - } + if (oBookmark && oBookmark.sType == "scroll") { + setTimeout(jindo.$Fn(function () { this.oApp.getWYSIWYGDocument().documentElement.scrollTop = oBookmark.nScrollTop; }, this).bind(), 0); + } else { + oSelection = this.oApp.getEmptySelection(); + if (oSelection.selectionLoaded) { + if (oBookmark) { + oSelection.moveToXPathBookmark(oBookmark); + } else { + oSelection = this.oApp.getEmptySelection(); + } - this._doRecordUndoHistoryAt(oInsertionIdx, sAction, sCurContent, sFullContents, oBookmark, sParentXPath, oUndoCallback, oRedoCallback); - }, - - $ON_DO_RECORD_UNDO_HISTORY_AT : function(oInsertionIdx, sAction, sContent, sFullContents, oBookmark, sParentXPath){ - this._doRecordUndoHistoryAt(oInsertionIdx, sAction, sContent, sFullContents, oBookmark, sParentXPath); - }, - - _doRecordUndoHistoryAt : function(oInsertionIdx, sAction, sContent, sFullContents, oBookmark, sParentXPath, oUndoCallback, oRedoCallback){ - if(oInsertionIdx.nStep !== 0){ - this.aUndoHistory[oInsertionIdx.nIdx].nTotalSteps = oInsertionIdx.nStep+1; - this.aUndoHistory[oInsertionIdx.nIdx].sContent[oInsertionIdx.nStep] = sContent; - this.aUndoHistory[oInsertionIdx.nIdx].sFullContents[oInsertionIdx.nStep] = sFullContents; - this.aUndoHistory[oInsertionIdx.nIdx].oBookmark[oInsertionIdx.nStep] = oBookmark; - this.aUndoHistory[oInsertionIdx.nIdx].sParentXPath[oInsertionIdx.nStep] = sParentXPath; - this.aUndoHistory[oInsertionIdx.nIdx].oUndoCallback[oInsertionIdx.nStep] = oUndoCallback; - this.aUndoHistory[oInsertionIdx.nIdx].oRedoCallback[oInsertionIdx.nStep] = oRedoCallback; - }else{ - var oNewHistory = {sAction:sAction, nTotalSteps: 1}; - oNewHistory.sContent = []; - oNewHistory.sContent[0] = sContent; + oSelection.select(); + } + } + }, - oNewHistory.sFullContents = []; - oNewHistory.sFullContents[0] = sFullContents; + _doRecordUndoHistory: function (sAction, htRecordOption) { + /* + htRecordOption = { + nStep : 0 | 1, + bSkipIfEqual : false | true, + bTwoStepAction : false | true, + sSaveTarget : [TAG] | null + elSaveTarget : [Element] | null + bDontSaveSelection : false | true + bMustBlockElement : false | true + bMustBlockContainer : false | true + }; + */ - oNewHistory.oBookmark = []; - oNewHistory.oBookmark[0] = oBookmark; - - oNewHistory.sParentXPath = []; - oNewHistory.sParentXPath[0] = sParentXPath; - - oNewHistory.oUndoCallback = []; - oNewHistory.oUndoCallback[0] = oUndoCallback; - - oNewHistory.oRedoCallback = []; - oNewHistory.oRedoCallback[0] = oRedoCallback; - - this.aUndoHistory.splice(oInsertionIdx.nIdx, this._historyLength - oInsertionIdx.nIdx, oNewHistory); - this._historyLength = this.aUndoHistory.length; - } + htRecordOption = htRecordOption || {}; - if(this._historyLength > this.nHardLimit){ - this.aUndoHistory.splice(0, this.nAfterMaxDeleteBuffer); - oInsertionIdx.nIdx -= this.nAfterMaxDeleteBuffer; - } - this.oCurStateIdx.nIdx = oInsertionIdx.nIdx; - this.oCurStateIdx.nStep = oInsertionIdx.nStep; - }, + var nStep = htRecordOption.nStep || 0, + bSkipIfEqual = htRecordOption.bSkipIfEqual || false, + bTwoStepAction = htRecordOption.bTwoStepAction || false, + sSaveTarget = htRecordOption.sSaveTarget || null, + elSaveTarget = htRecordOption.elSaveTarget || null, + bDontSaveSelection = htRecordOption.bDontSaveSelection || false, + bMustBlockElement = htRecordOption.bMustBlockElement || false, + bMustBlockContainer = htRecordOption.bMustBlockContainer || false, + oUndoCallback = htRecordOption.oUndoCallback, + oRedoCallback = htRecordOption.oRedoCallback; - _saveSnapShot : function(){ - this.oSnapShot = { - oBookmark : this.oApp.getSelection().getXPathBookmark() - }; - }, - - _getTargetXPath : function(oBookmark, sSaveTarget){ // ex. A, TABLE ... - var sParentXPath = this.sDefaultXPath, - aStartXPath = oBookmark[0].sXPath.split("/"), - aEndXPath = oBookmark[1].sXPath.split("/"), - aParentPath = [], - nPathLen = aStartXPath.length < aEndXPath.length ? aStartXPath.length : aEndXPath.length, - nPathIdx = 0, nTargetIdx = -1; + // if we're in the middle of some action history, + // remove everything after current idx if any "little" change is made + this._historyLength = this.aUndoHistory.length; - if(sSaveTarget === "BODY"){ - return sParentXPath; - } - - for(nPathIdx=0; nPathIdx -1){ - nTargetIdx = nPathIdx; - } - } + if (this.oCurStateIdx.nIdx !== this._historyLength - 1) { + bSkipIfEqual = true; + } - if(nTargetIdx > -1){ - aParentPath.length = nTargetIdx; // Target 의 상위 노드까지 지정 - } - - sParentXPath = aParentPath.join("/"); - - if(sParentXPath.length < this.sDefaultXPath.length){ - sParentXPath = this.sDefaultXPath; - } - - return sParentXPath; - }, - - _getParentXPath : function(oBookmark, bMustBlockElement, bMustBlockContainer){ - var sParentXPath = this.sDefaultXPath, - aStartXPath, aEndXPath, - aSnapShotStart, aSnapShotEnd, - nSnapShotLen, nPathLen, - aParentPath = ["", "HTML[0]", "BODY[0]"], - nPathIdx = 0, nBlockIdx = -1, - // rxBlockContainer = /\bUL|OL|TD|TR|TABLE|BLOCKQUOTE\[/i, // DL - // rxBlockElement = /\bP|LI|DIV|UL|OL|TD|TR|TABLE|BLOCKQUOTE\[/i, // H[1-6]|DD|DT|DL|PRE - // rxBlock, - sPath, sTag; - - if(!oBookmark){ - return sParentXPath; - } - - // 가능한 중복되는 Parent 를 검색 - if(oBookmark[0].sXPath === sParentXPath || oBookmark[1].sXPath === sParentXPath){ - return sParentXPath; - } + var oCurHistory = this.aUndoHistory[this.oCurStateIdx.nIdx], + sHistoryFullContents = oCurHistory.sFullContents[this.oCurStateIdx.nStep], + sCurContent = "", + sFullContents = "", + sParentXPath = "", + oBookmark = null, + oSelection = null, + oInsertionIdx = { nIdx: this.oCurStateIdx.nIdx, nStep: this.oCurStateIdx.nStep }; // 히스토리를 저장할 위치 - aStartXPath = oBookmark[0].sXPath.split("/"); - aEndXPath = oBookmark[1].sXPath.split("/"); - aSnapShotStart = this.oSnapShot.oBookmark[0].sXPath.split("/"); - aSnapShotEnd = this.oSnapShot.oBookmark[1].sXPath.split("/"); - - nSnapShotLen = aSnapShotStart.length < aSnapShotEnd.length ? aSnapShotStart.length : aSnapShotEnd.length; - nPathLen = aStartXPath.length < aEndXPath.length ? aStartXPath.length : aEndXPath.length; - nPathLen = nPathLen < nSnapShotLen ? nPathLen : nSnapShotLen; + oSelection = this.oApp.getSelection(); - if(nPathLen < 3){ // BODY - return sParentXPath; - } - - bMustBlockElement = bMustBlockElement || false; - bMustBlockContainer = bMustBlockContainer || false; - // rxBlock = bMustBlockElement ? rxBlockElement : rxBlockContainer; - - for(nPathIdx=3; nPathIdx -1){ - aParentPath.length = nBlockIdx + 1; - }else if(bMustBlockElement || bMustBlockContainer){ - return sParentXPath; - } + if (sParentXPath === this.sDefaultXPath) { + sCurContent = sFullContents; + } else { + sCurContent = oSelection._evaluateXPath(sParentXPath, oSelection._document).innerHTML; + } - return aParentPath.join("/"); - }, + if (this.bFF && sCurContent == this.sBlankContentsForFF) { + sCurContent = ""; + } - _getUndoHistory : function(){ - return this.aUndoHistory; - }, + // every TwoStepAction needs to be recorded + if (!bTwoStepAction && bSkipIfEqual) { + if (sHistoryFullContents.length === sFullContents.length) { + return; + } - _getUndoStateIdx : function(){ - return this.oCurStateIdx; - } + // 저장된 데이터와 같음에도 다르다고 처리되는 경우에 대한 처리 + // (예, P안에 Block엘리먼트가 추가된 경우 P를 분리) + //if(this.bChrome || this.bSafari){ + var elCurrentDiv = document.createElement("div"); + var elHistoryDiv = document.createElement("div"); + + elCurrentDiv.innerHTML = sFullContents; + elHistoryDiv.innerHTML = sHistoryFullContents; + + var elDocFragment = document.createDocumentFragment(); + elDocFragment.appendChild(elCurrentDiv); + elDocFragment.appendChild(elHistoryDiv); + + sFullContents = elCurrentDiv.innerHTML; + sHistoryFullContents = elHistoryDiv.innerHTML; + + elCurrentDiv = null; + elHistoryDiv = null; + elDocFragment = null; + + if (sHistoryFullContents.length === sFullContents.length) { + return; + } + //} + } + + if (bDontSaveSelection) { + oBookmark = { sType: "scroll", nScrollTop: this.oApp.getWYSIWYGDocument().documentElement.scrollTop }; + } + + oInsertionIdx.nStep = nStep; + + if (oInsertionIdx.nStep === 0 && this.oCurStateIdx.nStep === oCurHistory.nTotalSteps - 1) { + oInsertionIdx.nIdx = this.oCurStateIdx.nIdx + 1; + } + + this._doRecordUndoHistoryAt(oInsertionIdx, sAction, sCurContent, sFullContents, oBookmark, sParentXPath, oUndoCallback, oRedoCallback); + }, + + $ON_DO_RECORD_UNDO_HISTORY_AT: function (oInsertionIdx, sAction, sContent, sFullContents, oBookmark, sParentXPath) { + this._doRecordUndoHistoryAt(oInsertionIdx, sAction, sContent, sFullContents, oBookmark, sParentXPath); + }, + + _doRecordUndoHistoryAt: function (oInsertionIdx, sAction, sContent, sFullContents, oBookmark, sParentXPath, oUndoCallback, oRedoCallback) { + if (oInsertionIdx.nStep !== 0) { + this.aUndoHistory[oInsertionIdx.nIdx].nTotalSteps = oInsertionIdx.nStep + 1; + this.aUndoHistory[oInsertionIdx.nIdx].sContent[oInsertionIdx.nStep] = sContent; + this.aUndoHistory[oInsertionIdx.nIdx].sFullContents[oInsertionIdx.nStep] = sFullContents; + this.aUndoHistory[oInsertionIdx.nIdx].oBookmark[oInsertionIdx.nStep] = oBookmark; + this.aUndoHistory[oInsertionIdx.nIdx].sParentXPath[oInsertionIdx.nStep] = sParentXPath; + this.aUndoHistory[oInsertionIdx.nIdx].oUndoCallback[oInsertionIdx.nStep] = oUndoCallback; + this.aUndoHistory[oInsertionIdx.nIdx].oRedoCallback[oInsertionIdx.nStep] = oRedoCallback; + } else { + var oNewHistory = { sAction: sAction, nTotalSteps: 1 }; + oNewHistory.sContent = []; + oNewHistory.sContent[0] = sContent; + + oNewHistory.sFullContents = []; + oNewHistory.sFullContents[0] = sFullContents; + + oNewHistory.oBookmark = []; + oNewHistory.oBookmark[0] = oBookmark; + + oNewHistory.sParentXPath = []; + oNewHistory.sParentXPath[0] = sParentXPath; + + oNewHistory.oUndoCallback = []; + oNewHistory.oUndoCallback[0] = oUndoCallback; + + oNewHistory.oRedoCallback = []; + oNewHistory.oRedoCallback[0] = oRedoCallback; + + this.aUndoHistory.splice(oInsertionIdx.nIdx, this._historyLength - oInsertionIdx.nIdx, oNewHistory); + this._historyLength = this.aUndoHistory.length; + } + + if (this._historyLength > this.nHardLimit) { + this.aUndoHistory.splice(0, this.nAfterMaxDeleteBuffer); + oInsertionIdx.nIdx -= this.nAfterMaxDeleteBuffer; + } + this.oCurStateIdx.nIdx = oInsertionIdx.nIdx; + this.oCurStateIdx.nStep = oInsertionIdx.nStep; + }, + + _saveSnapShot: function () { + this.oSnapShot = { + oBookmark: this.oApp.getSelection().getXPathBookmark() + }; + }, + + _getTargetXPath: function (oBookmark, sSaveTarget) { // ex. A, TABLE ... + var sParentXPath = this.sDefaultXPath, + aStartXPath = oBookmark[0].sXPath.split("/"), + aEndXPath = oBookmark[1].sXPath.split("/"), + aParentPath = [], + nPathLen = aStartXPath.length < aEndXPath.length ? aStartXPath.length : aEndXPath.length, + nPathIdx = 0, nTargetIdx = -1; + + if (sSaveTarget === "BODY") { + return sParentXPath; + } + + for (nPathIdx = 0; nPathIdx < nPathLen; nPathIdx++) { + if (aStartXPath[nPathIdx] !== aEndXPath[nPathIdx]) { + break; + } + + aParentPath.push(aStartXPath[nPathIdx]); + + if (aStartXPath[nPathIdx] === "" || aStartXPath[nPathIdx] === "HTML" || aStartXPath[nPathIdx] === "BODY") { + continue; + } + + if (aStartXPath[nPathIdx].indexOf(sSaveTarget) > -1) { + nTargetIdx = nPathIdx; + } + } + + if (nTargetIdx > -1) { + aParentPath.length = nTargetIdx; // Target 의 상위 노드까지 지정 + } + + sParentXPath = aParentPath.join("/"); + + if (sParentXPath.length < this.sDefaultXPath.length) { + sParentXPath = this.sDefaultXPath; + } + + return sParentXPath; + }, + + _getParentXPath: function (oBookmark, bMustBlockElement, bMustBlockContainer) { + var sParentXPath = this.sDefaultXPath, + aStartXPath, aEndXPath, + aSnapShotStart, aSnapShotEnd, + nSnapShotLen, nPathLen, + aParentPath = ["", "HTML[0]", "BODY[0]"], + nPathIdx = 0, nBlockIdx = -1, + // rxBlockContainer = /\bUL|OL|TD|TR|TABLE|BLOCKQUOTE\[/i, // DL + // rxBlockElement = /\bP|LI|DIV|UL|OL|TD|TR|TABLE|BLOCKQUOTE\[/i, // H[1-6]|DD|DT|DL|PRE + // rxBlock, + sPath, sTag; + + if (!oBookmark) { + return sParentXPath; + } + + // 가능한 중복되는 Parent 를 검색 + if (oBookmark[0].sXPath === sParentXPath || oBookmark[1].sXPath === sParentXPath) { + return sParentXPath; + } + + aStartXPath = oBookmark[0].sXPath.split("/"); + aEndXPath = oBookmark[1].sXPath.split("/"); + aSnapShotStart = this.oSnapShot.oBookmark[0].sXPath.split("/"); + aSnapShotEnd = this.oSnapShot.oBookmark[1].sXPath.split("/"); + + nSnapShotLen = aSnapShotStart.length < aSnapShotEnd.length ? aSnapShotStart.length : aSnapShotEnd.length; + nPathLen = aStartXPath.length < aEndXPath.length ? aStartXPath.length : aEndXPath.length; + nPathLen = nPathLen < nSnapShotLen ? nPathLen : nSnapShotLen; + + if (nPathLen < 3) { // BODY + return sParentXPath; + } + + bMustBlockElement = bMustBlockElement || false; + bMustBlockContainer = bMustBlockContainer || false; + // rxBlock = bMustBlockElement ? rxBlockElement : rxBlockContainer; + + for (nPathIdx = 3; nPathIdx < nPathLen; nPathIdx++) { + sPath = aStartXPath[nPathIdx]; + + if (sPath !== aEndXPath[nPathIdx] || + sPath !== aSnapShotStart[nPathIdx] || + sPath !== aSnapShotEnd[nPathIdx] || + aEndXPath[nPathIdx] !== aSnapShotStart[nPathIdx] || + aEndXPath[nPathIdx] !== aSnapShotEnd[nPathIdx] || + aSnapShotStart[nPathIdx] !== aSnapShotEnd[nPathIdx]) { + + break; + } + + aParentPath.push(sPath); + + sTag = sPath.substring(0, sPath.indexOf("[")); + + if (bMustBlockElement && (sTag === "P" || sTag === "LI" || sTag === "DIV")) { + nBlockIdx = nPathIdx; + } else if (sTag === "UL" || sTag === "OL" || sTag === "TD" || sTag === "TR" || sTag === "TABLE" || sTag === "BLOCKQUOTE") { + nBlockIdx = nPathIdx; + } + + // if(rxBlock.test(sPath)){ + // nBlockIdx = nPathIdx; + // } + } + + if (nBlockIdx > -1) { + aParentPath.length = nBlockIdx + 1; + } else if (bMustBlockElement || bMustBlockContainer) { + return sParentXPath; + } + + return aParentPath.join("/"); + }, + + _getUndoHistory: function () { + return this.aUndoHistory; + }, + + _getUndoStateIdx: function () { + return this.oCurStateIdx; + } }); /*[ * ATTACH_HOVER_EVENTS @@ -13879,50 +13883,50 @@ nhn.husky.SE_UndoRedo = jindo.$Class({ /** * @pluginDesc Husky Framework에서 자주 사용되는 유틸성 메시지를 처리하는 플러그인 */ - nhn.husky.Utils = jindo.$Class({ - name : "Utils", +nhn.husky.Utils = jindo.$Class({ + name: "Utils", - $init : function(){ - var oAgentInfo = jindo.$Agent(); - var oNavigatorInfo = oAgentInfo.navigator(); + $init: function () { + var oAgentInfo = jindo.$Agent(); + var oNavigatorInfo = oAgentInfo.navigator(); - if(oNavigatorInfo.ie && oNavigatorInfo.version == 6){ - try{ - document.execCommand('BackgroundImageCache', false, true); - }catch(e){} - } - }, - - $BEFORE_MSG_APP_READY : function(){ - this.oApp.exec("ADD_APP_PROPERTY", ["htBrowser", jindo.$Agent().navigator()]); - }, - - $ON_ATTACH_HOVER_EVENTS : function(aElms, htOptions){ - htOptions = htOptions || []; - var sHoverClass = htOptions.sHoverClass || "hover"; - var fnElmToSrc = htOptions.fnElmToSrc || function(el){return el}; - var fnElmToTarget = htOptions.fnElmToTarget || function(el){return el}; - - if(!aElms) return; - - var wfAddClass = jindo.$Fn(function(wev){ - jindo.$Element(fnElmToTarget(wev.currentElement)).addClass(sHoverClass); - }, this); - - var wfRemoveClass = jindo.$Fn(function(wev){ - jindo.$Element(fnElmToTarget(wev.currentElement)).removeClass(sHoverClass); - }, this); - - for(var i=0, len = aElms.length; itag 사이에 '\n\'개행문자를 넣어준다. - if( jindo.$Agent().navigator().ie ){ - sTmpStr = sTmpStr +'(\r\n)?'; //ie+win에서는 개행이 \r\n로 들어감. - rxTmpStr = new RegExp(sTmpStr , "g"); - } - } - - aConverters = this.oConverters[sRuleName]; - if(aConverters){ - for(var i=0; itag 사이에 '\n\'개행문자를 넣어준다. + if (jindo.$Agent().navigator().ie) { + sTmpStr = sTmpStr + '(\r\n)?'; //ie+win에서는 개행이 \r\n로 들어감. + rxTmpStr = new RegExp(sTmpStr, "g"); + } + } + + aConverters = this.oConverters[sRuleName]; + if (aConverters) { + for (var i = 0; i < aConverters.length; i++) { + var sTmpContents = aConverters[i](sContents); + if (typeof sTmpContents != "undefined") { + sContents = sTmpContents; + } + } + } + + oRes = { sContents: sContents }; + this.oApp.exec("MSG_STRING_CONVERTER_ENDED", [sRuleName, oRes]); + + oRes.sContents = oRes.sContents.replace(rxTmpStr, ""); + return oRes.sContents; + }, + + $ON_ADD_CONVERTER: function (sRuleName, funcConverter) { + var aCallerStack = this.oApp.aCallerStack; + funcConverter.sPluginName = aCallerStack[aCallerStack.length - 2].name; + this.addConverter(sRuleName, funcConverter); + }, + + $ON_ADD_CONVERTER_DOM: function (sRuleName, funcConverter) { + var aCallerStack = this.oApp.aCallerStack; + funcConverter.sPluginName = aCallerStack[aCallerStack.length - 2].name; + this.addConverter_DOM(sRuleName, funcConverter); + }, + + addConverter: function (sRuleName, funcConverter) { + var aConverters = this.oConverters[sRuleName]; + if (!aConverters) { + this.oConverters[sRuleName] = []; + } + + this.oConverters[sRuleName][this.oConverters[sRuleName].length] = funcConverter; + }, + + addConverter_DOM: function (sRuleName, funcConverter) { + var aConverters = this.oConverters_DOM[sRuleName]; + if (!aConverters) { + this.oConverters_DOM[sRuleName] = []; + } + + this.oConverters_DOM[sRuleName][this.oConverters_DOM[sRuleName].length] = funcConverter; + } }); //} /** @@ -14181,71 +14185,71 @@ nhn.husky.StringConverterManager = jindo.$Class({ * @name hp_MessageManager.js */ nhn.husky.MessageManager = jindo.$Class({ - name : "MessageManager", + name: "MessageManager", - _oMessageMapSet : {}, - _sDefaultLocale : "ko_KR", - - $init : function(oMessageMap, sLocale){ - // 하위호환을 위해 기존 코드 유지 - var oTmpMessageMap; - switch(sLocale) { - case "ja_JP" : - oTmpMessageMap = oMessageMap_ja_JP; - break; - case "en_US" : - oTmpMessageMap = oMessageMap_en_US; - break; - case "zh_CN" : - oTmpMessageMap = oMessageMap_zh_CN; - break; - default : // Korean - oTmpMessageMap = oMessageMap; - break; - } - oTmpMessageMap = oTmpMessageMap || oMessageMap; + _oMessageMapSet: {}, + _sDefaultLocale: "ko_KR", - this._sDefaultLocale = sLocale || this._sDefaultLocale; - this._setMessageMap(oTmpMessageMap, this._sDefaultLocale); - }, + $init: function (oMessageMap, sLocale) { + // 하위호환을 위해 기존 코드 유지 + var oTmpMessageMap; + switch (sLocale) { + case "ja_JP": + oTmpMessageMap = oMessageMap_ja_JP; + break; + case "en_US": + oTmpMessageMap = oMessageMap_en_US; + break; + case "zh_CN": + oTmpMessageMap = oMessageMap_zh_CN; + break; + default: // Korean + oTmpMessageMap = oMessageMap; + break; + } + oTmpMessageMap = oTmpMessageMap || oMessageMap; - /** - * 로케일에 해당하는 메시지맵을 세팅한다. - * @param {Object} oMessageMap 세팅할 메시지맵 객체 - * @param {String} [sLocale] 메시지맵 객체를 세팅할 로케일 정보 - */ - _setMessageMap : function(oMessageMap, sLocale){ - sLocale = sLocale || this._sDefaultLocale; - if(oMessageMap){ - this._oMessageMapSet[sLocale] = oMessageMap; - } - }, + this._sDefaultLocale = sLocale || this._sDefaultLocale; + this._setMessageMap(oTmpMessageMap, this._sDefaultLocale); + }, - /** - * 로케일에 해당하는 메시지맵을 가져온다. - * @param {String} [sLocale] 가져올 메시지맵 객체의 로케일 정보 - * @returns {Object} 메시지맵 - */ - _getMessageMap : function(sLocale){ - return this._oMessageMapSet[sLocale] || this._oMessageMapSet[this._sDefaultLocale] || {}; - }, + /** + * 로케일에 해당하는 메시지맵을 세팅한다. + * @param {Object} oMessageMap 세팅할 메시지맵 객체 + * @param {String} [sLocale] 메시지맵 객체를 세팅할 로케일 정보 + */ + _setMessageMap: function (oMessageMap, sLocale) { + sLocale = sLocale || this._sDefaultLocale; + if (oMessageMap) { + this._oMessageMapSet[sLocale] = oMessageMap; + } + }, - $BEFORE_MSG_APP_READY : function(){ - this.oApp.exec("ADD_APP_PROPERTY", ["$MSG", jindo.$Fn(this.getMessage, this).bind()]); - }, + /** + * 로케일에 해당하는 메시지맵을 가져온다. + * @param {String} [sLocale] 가져올 메시지맵 객체의 로케일 정보 + * @returns {Object} 메시지맵 + */ + _getMessageMap: function (sLocale) { + return this._oMessageMapSet[sLocale] || this._oMessageMapSet[this._sDefaultLocale] || {}; + }, - /** - * 메시지문자열을 가져온다. - * @param {String} sMsg 메시지키 - * @param {String} [sLocale] 가져올 메시지의 로케일 정보 - * @returns {String} 해당로케일의 메시지문자열 - */ - getMessage : function(sMsg, sLocale){ - var oMessageMap = this._getMessageMap(sLocale); + $BEFORE_MSG_APP_READY: function () { + this.oApp.exec("ADD_APP_PROPERTY", ["$MSG", jindo.$Fn(this.getMessage, this).bind()]); + }, - if(oMessageMap[sMsg]){return unescape(oMessageMap[sMsg]);} - return sMsg; - } + /** + * 메시지문자열을 가져온다. + * @param {String} sMsg 메시지키 + * @param {String} [sLocale] 가져올 메시지의 로케일 정보 + * @returns {String} 해당로케일의 메시지문자열 + */ + getMessage: function (sMsg, sLocale) { + var oMessageMap = this._getMessageMap(sLocale); + + if (oMessageMap[sMsg]) { return unescape(oMessageMap[sMsg]); } + return sMsg; + } }); //{ /** @@ -14253,120 +14257,120 @@ nhn.husky.MessageManager = jindo.$Class({ * @name hp_LazyLoader.js */ nhn.husky.LazyLoader = jindo.$Class({ - name : "LazyLoader", + name: "LazyLoader", - // sMsg : KEY - // contains htLoadingInfo : {} - htMsgInfo : null, - - // contains objects - // sURL : HTML to be loaded - // elTarget : where to append the HTML - // sSuccessCallback : message name - // sFailureCallback : message name - // nLoadingStatus : - // 0 : loading not started - // 1 : loading started - // 2 : loading ended - aLoadingInfo : null, + // sMsg : KEY + // contains htLoadingInfo : {} + htMsgInfo: null, - // aToDo : [{aMsgs: ["EXECCOMMAND"], sURL: "http://127.0.0.1/html_snippet.txt", elTarget: elPlaceHolder}, ...] - $init : function(aToDo){ - this.htMsgInfo = {}; - this.aLoadingInfo = []; - this.aToDo = aToDo; - }, - - $ON_MSG_APP_READY : function(){ - for(var i=0; i -1 && ua.indexOf("Version/4.0") > -1){ - // [SMARTEDITORSUS-1802] G패드 기본브라우저 - browser.bGPadBrowser = true; - } - // [SMARTEDITORSUS-1860] iOS 버전 확인용 - // os 에서 ios 여부 및 version 정보는 jindo2.3.0 부터 추가되었음 - if(typeof os.ios === 'undefined'){ - os.ios = ua.indexOf("iPad") > -1 || ua.indexOf("iPhone") > -1; - if(os.ios){ - aMatch = ua.match(/(iPhone )?OS ([\d|_]+)/); - if(aMatch != null && aMatch[2] != undefined){ - os.version = String(aMatch[2]).split("_").join("."); - } - } + // [SMARTEDITORSUS-1795] 갤럭시노트 기본브라우저 구분을 위해 구분필드 추가 + var aMatch = ua.match(/(SHW-|Chrome|Safari)/gi) || ""; + if (aMatch.length === 2 && aMatch[0] === "SHW-" && aMatch[1] === "Safari") { + // 갤럭시노트 기본브라우저 + browser.bGalaxyBrowser = true; + } else if (ua.indexOf("LG-V500") > -1 && ua.indexOf("Version/4.0") > -1) { + // [SMARTEDITORSUS-1802] G패드 기본브라우저 + browser.bGPadBrowser = true; + } + // [SMARTEDITORSUS-1860] iOS 버전 확인용 + // os 에서 ios 여부 및 version 정보는 jindo2.3.0 부터 추가되었음 + if (typeof os.ios === 'undefined') { + os.ios = ua.indexOf("iPad") > -1 || ua.indexOf("iPhone") > -1; + if (os.ios) { + aMatch = ua.match(/(iPhone )?OS ([\d|_]+)/); + if (aMatch != null && aMatch[2] != undefined) { + os.version = String(aMatch[2]).split("_").join("."); + } } + } })(); nhn.husky.SE2M_UtilPlugin = jindo.$Class({ - name : "SE2M_UtilPlugin", + name: "SE2M_UtilPlugin", - $BEFORE_MSG_APP_READY : function(){ - this.oApp.exec("ADD_APP_PROPERTY", ["oAgent", jindo.$Agent()]); - this.oApp.exec("ADD_APP_PROPERTY", ["oNavigator", jindo.$Agent().navigator()]); - this.oApp.exec("ADD_APP_PROPERTY", ["oUtils", this]); - }, - - $ON_REGISTER_HOTKEY : function(sHotkey, sCMD, aArgs, elTarget) { - this.oApp.exec("ADD_HOTKEY", [sHotkey, sCMD, aArgs, (elTarget || this.oApp.getWYSIWYGDocument())]); - }, + $BEFORE_MSG_APP_READY: function () { + this.oApp.exec("ADD_APP_PROPERTY", ["oAgent", jindo.$Agent()]); + this.oApp.exec("ADD_APP_PROPERTY", ["oNavigator", jindo.$Agent().navigator()]); + this.oApp.exec("ADD_APP_PROPERTY", ["oUtils", this]); + }, - $ON_SE2_ATTACH_HOVER_EVENTS : function(aElms){ - this.oApp.exec("ATTACH_HOVER_EVENTS", [aElms, {fnElmToSrc: this._elm2Src, fnElmToTarget: this._elm2Target}]); - }, - - _elm2Src : function(el){ - if(el.tagName == "LI" && el.firstChild && el.firstChild.tagName == "BUTTON"){ - return el.firstChild; - }else{ - return el; - } - }, - - _elm2Target : function(el){ - if(el.tagName == "BUTTON" && el.parentNode.tagName == "LI"){ - return el.parentNode; - }else{ - return el; - } - }, - - getScrollXY : function(){ - var scrollX,scrollY; - var oAppWindow = this.oApp.getWYSIWYGWindow(); - if(typeof oAppWindow.scrollX == "undefined"){ - scrollX = oAppWindow.document.documentElement.scrollLeft; - scrollY = oAppWindow.document.documentElement.scrollTop; - }else{ - scrollX = oAppWindow.scrollX; - scrollY = oAppWindow.scrollY; - } - - return {x:scrollX, y:scrollY}; - } + $ON_REGISTER_HOTKEY: function (sHotkey, sCMD, aArgs, elTarget) { + this.oApp.exec("ADD_HOTKEY", [sHotkey, sCMD, aArgs, (elTarget || this.oApp.getWYSIWYGDocument())]); + }, + + $ON_SE2_ATTACH_HOVER_EVENTS: function (aElms) { + this.oApp.exec("ATTACH_HOVER_EVENTS", [aElms, { fnElmToSrc: this._elm2Src, fnElmToTarget: this._elm2Target }]); + }, + + _elm2Src: function (el) { + if (el.tagName == "LI" && el.firstChild && el.firstChild.tagName == "BUTTON") { + return el.firstChild; + } else { + return el; + } + }, + + _elm2Target: function (el) { + if (el.tagName == "BUTTON" && el.parentNode.tagName == "LI") { + return el.parentNode; + } else { + return el; + } + }, + + getScrollXY: function () { + var scrollX, scrollY; + var oAppWindow = this.oApp.getWYSIWYGWindow(); + if (typeof oAppWindow.scrollX == "undefined") { + scrollX = oAppWindow.document.documentElement.scrollLeft; + scrollY = oAppWindow.document.documentElement.scrollTop; + } else { + scrollX = oAppWindow.scrollX; + scrollY = oAppWindow.scrollY; + } + + return { x: scrollX, y: scrollY }; + } }); nhn.husky.SE2M_Utils = { - sURLPattern : '(http|https|ftp|mailto):(?:\\/\\/)?((:?\\w|-)+(:?\\.(:?\\w|-)+)+)([^ <>]+)?', - rxDateFormat : /^(?:\d{4}\.)?\d{1,2}\.\d{1,2}$/, - _rxTable : /^(?:CAPTION|TBODY|THEAD|TFOOT|TR|TD|TH|COLGROUP|COL)$/i, - _rxSpaceOnly : /^\s+$/, - _rxFontStart : /]*)?>/i, - _htFontSize : { // @see http://jerekdain.com/fontconversion.html - "1" : "7pt", - "2" : "10pt", - "3" : "12pt", - "4" : "13.5pt", - "5" : "18pt", - "6" : "24pt" - }, + sURLPattern: '(http|https|ftp|mailto):(?:\\/\\/)?((:?\\w|-)+(:?\\.(:?\\w|-)+)+)([^ <>]+)?', + rxDateFormat: /^(?:\d{4}\.)?\d{1,2}\.\d{1,2}$/, + _rxTable: /^(?:CAPTION|TBODY|THEAD|TFOOT|TR|TD|TH|COLGROUP|COL)$/i, + _rxSpaceOnly: /^\s+$/, + _rxFontStart: /]*)?>/i, + _htFontSize: { // @see http://jerekdain.com/fontconversion.html + "1": "7pt", + "2": "10pt", + "3": "12pt", + "4": "13.5pt", + "5": "18pt", + "6": "24pt" + }, - /** - * 유효하지 않은 노드가 TBODY, TR, TD 등등 사이에 있는지 판별한다. - * @param oNode {Node} 검사할 노드 - * @return {Boolean} TBODY, TR, TD 등등 사이에 있는 유효하지 않은 노드인지 여부 - */ - isInvalidNodeInTable : function(oNode){ - // 해당노드가 table 관련노드들이 아닌데 table 관련노드들 사이에 있는지 검사한다. - // TODO: 좀 더 정확하게 검사할 필요가 있을까? - if(oNode && !this._rxTable.test(oNode.nodeName)){ - var oTmpNode; - if((oTmpNode = oNode.previousSibling) && this._rxTable.test(oTmpNode.nodeName)){ - return true; - } - if((oTmpNode = oNode.nextSibling) && this._rxTable.test(oTmpNode.nodeName)){ - return true; - } - } - return false; - }, + /** + * 유효하지 않은 노드가 TBODY, TR, TD 등등 사이에 있는지 판별한다. + * @param oNode {Node} 검사할 노드 + * @return {Boolean} TBODY, TR, TD 등등 사이에 있는 유효하지 않은 노드인지 여부 + */ + isInvalidNodeInTable: function (oNode) { + // 해당노드가 table 관련노드들이 아닌데 table 관련노드들 사이에 있는지 검사한다. + // TODO: 좀 더 정확하게 검사할 필요가 있을까? + if (oNode && !this._rxTable.test(oNode.nodeName)) { + var oTmpNode; + if ((oTmpNode = oNode.previousSibling) && this._rxTable.test(oTmpNode.nodeName)) { + return true; + } + if ((oTmpNode = oNode.nextSibling) && this._rxTable.test(oTmpNode.nodeName)) { + return true; + } + } + return false; + }, - /** - * [SMARTEDITORSUS-1584][SMARTEDITORSUS-2237] - * TBODY, TR, TD 사이에 있는 유효하지 않은 노드이면 제거한다. - * @param oNode {Node} 검사할 노드 - */ - removeInvalidNodeInTable : function(oNode){ - if(this.isInvalidNodeInTable(oNode) && oNode.parentNode){ - oNode.parentNode.removeChild(oNode); - } - }, + /** + * [SMARTEDITORSUS-1584][SMARTEDITORSUS-2237] + * TBODY, TR, TD 사이에 있는 유효하지 않은 노드이면 제거한다. + * @param oNode {Node} 검사할 노드 + */ + removeInvalidNodeInTable: function (oNode) { + if (this.isInvalidNodeInTable(oNode) && oNode.parentNode) { + oNode.parentNode.removeChild(oNode); + } + }, - /** - * [SMARTEDITORSUS-2315] 유효하지 않은 폰트태그를 제거한다. - * @param {Element} el 태그정제를 제한할 상위 요소 - */ - removeInvalidFont : function(el){ - if(!el){ - return; - } + /** + * [SMARTEDITORSUS-2315] 유효하지 않은 폰트태그를 제거한다. + * @param {Element} el 태그정제를 제한할 상위 요소 + */ + removeInvalidFont: function (el) { + if (!el) { + return; + } - this._removeInvalidFontInTable(el); - this._removeEmptyFont(el); - }, + this._removeInvalidFontInTable(el); + this._removeEmptyFont(el); + }, - /** - * [SMARTEDITORSUS-2237] IE11 에서 엑셀을 복사붙여넣기하면 유효하지 않은 위치(tbody, tr, td 사이)에 font 태그가 삽입되므로 제거 - * @param {Element} el 태그제거를 제한할 상위 요소 - */ - _removeInvalidFontInTable : function(el){ - var aelFonts = jindo.$$("table font", el); - for(var i = 0, elFont; (elFont = aelFonts[i]); i++){ - this.removeInvalidNodeInTable(elFont); - } - }, + /** + * [SMARTEDITORSUS-2237] IE11 에서 엑셀을 복사붙여넣기하면 유효하지 않은 위치(tbody, tr, td 사이)에 font 태그가 삽입되므로 제거 + * @param {Element} el 태그제거를 제한할 상위 요소 + */ + _removeInvalidFontInTable: function (el) { + var aelFonts = jindo.$$("table font", el); + for (var i = 0, elFont; (elFont = aelFonts[i]); i++) { + this.removeInvalidNodeInTable(elFont); + } + }, - /** - * 빈 폰트태그를 제거한다. - * @param {Element} el 태그제거를 제한할 상위 요소 - */ - _removeEmptyFont : function(el){ - var aelFonts = jindo.$$("font", el); - for(var i = 0, elFont, sInner; (elFont = aelFonts[i]); i++){ - sInner = elFont.innerHTML || ""; - sInner = sInner.replace(this._rxSpaceOnly, ""); - if (!sInner) { - elFont.parentNode.removeChild(elFont); - } - } - }, + /** + * 빈 폰트태그를 제거한다. + * @param {Element} el 태그제거를 제한할 상위 요소 + */ + _removeEmptyFont: function (el) { + var aelFonts = jindo.$$("font", el); + for (var i = 0, elFont, sInner; (elFont = aelFonts[i]); i++) { + sInner = elFont.innerHTML || ""; + sInner = sInner.replace(this._rxSpaceOnly, ""); + if (!sInner) { + elFont.parentNode.removeChild(elFont); + } + } + }, - /** - * [SMARTEDITORSUS-2315] font 태그를 span 으로 변환 - * 포스트이슈[MUG-7757] 처리 로직 참고 - * @param {Element} el 태그변환을 제한할 상위 요소 - */ - convertFontToSpan : function(el){ - if(!el){ - return; - } + /** + * [SMARTEDITORSUS-2315] font 태그를 span 으로 변환 + * 포스트이슈[MUG-7757] 처리 로직 참고 + * @param {Element} el 태그변환을 제한할 상위 요소 + */ + convertFontToSpan: function (el) { + if (!el) { + return; + } - var oDoc = el.ownerDocument || document; - var aelTarget = jindo.$$("font", el); + var oDoc = el.ownerDocument || document; + var aelTarget = jindo.$$("font", el); - for(var i = 0, elTarget, elSpan, sAttrValue; (elTarget = aelTarget[i]); i++){ - elSpan = elTarget.parentNode; - if(elSpan.tagName !== "SPAN" || elSpan.childNodes.length > 1){ - elSpan = oDoc.createElement("SPAN"); - elTarget.parentNode.insertBefore(elSpan, elTarget); - } + for (var i = 0, elTarget, elSpan, sAttrValue; (elTarget = aelTarget[i]); i++) { + elSpan = elTarget.parentNode; + if (elSpan.tagName !== "SPAN" || elSpan.childNodes.length > 1) { + elSpan = oDoc.createElement("SPAN"); + elTarget.parentNode.insertBefore(elSpan, elTarget); + } - sAttrValue = elTarget.getAttribute("face"); - if(sAttrValue){ - elSpan.style.fontFamily = sAttrValue; - } - sAttrValue = this._htFontSize[elTarget.getAttribute("size")]; - if(sAttrValue){ - elSpan.style.fontSize = sAttrValue; - } - sAttrValue = elTarget.getAttribute("color"); - if(sAttrValue){ - elSpan.style.color = sAttrValue; - } + sAttrValue = elTarget.getAttribute("face"); + if (sAttrValue) { + elSpan.style.fontFamily = sAttrValue; + } + sAttrValue = this._htFontSize[elTarget.getAttribute("size")]; + if (sAttrValue) { + elSpan.style.fontSize = sAttrValue; + } + sAttrValue = elTarget.getAttribute("color"); + if (sAttrValue) { + elSpan.style.color = sAttrValue; + } - // font태그 안쪽 내용을 span에 넣는다. - this._switchFontInnerToSpan(elTarget, elSpan); + // font태그 안쪽 내용을 span에 넣는다. + this._switchFontInnerToSpan(elTarget, elSpan); - if(elTarget.parentNode){ - elTarget.parentNode.removeChild(elTarget); - } - } - }, + if (elTarget.parentNode) { + elTarget.parentNode.removeChild(elTarget); + } + } + }, - /** - * font태그 안쪽 내용을 span에 넣는다. - * @param {Element} elFont 대상FONT태그 - * @param {Element} elSpan 빈SPAN태그 - */ - _switchFontInnerToSpan : function(elFont, elSpan){ - var sInnerHTML = elFont.innerHTML; - /** - * 폰트태그안에 폰트태그가 있을때 innerHTML으로 넣으면 안쪽 폰트태그는 span변환작업에서 누락될 수 있기 때문에 - * 폰트태그가 중첩해서 있으면 appendChild를 이용하고 그렇지 않으면 innerHTML을 이용 - */ - if(this._rxFontStart.test(sInnerHTML)){ - for(var elChild; (elChild = elFont.firstChild);){ - elSpan.appendChild(elChild); - } - }else{ - elSpan.innerHTML = sInnerHTML; - } - }, + /** + * font태그 안쪽 내용을 span에 넣는다. + * @param {Element} elFont 대상FONT태그 + * @param {Element} elSpan 빈SPAN태그 + */ + _switchFontInnerToSpan: function (elFont, elSpan) { + var sInnerHTML = elFont.innerHTML; + /** + * 폰트태그안에 폰트태그가 있을때 innerHTML으로 넣으면 안쪽 폰트태그는 span변환작업에서 누락될 수 있기 때문에 + * 폰트태그가 중첩해서 있으면 appendChild를 이용하고 그렇지 않으면 innerHTML을 이용 + */ + if (this._rxFontStart.test(sInnerHTML)) { + for (var elChild; (elChild = elFont.firstChild);) { + elSpan.appendChild(elChild); + } + } else { + elSpan.innerHTML = sInnerHTML; + } + }, - /** - * 대상요소의 하위 요소들을 모두 밖으로 빼내고 대상요소를 제거한다. - * @param {Element} el 제거 대상 요소 - */ - stripTag : function(el){ - for(var elChild; (elChild = el.firstChild);){ - el.parentNode.insertBefore(elChild, el); - } - el.parentNode.removeChild(el); - }, + /** + * 대상요소의 하위 요소들을 모두 밖으로 빼내고 대상요소를 제거한다. + * @param {Element} el 제거 대상 요소 + */ + stripTag: function (el) { + for (var elChild; (elChild = el.firstChild);) { + el.parentNode.insertBefore(elChild, el); + } + el.parentNode.removeChild(el); + }, - /** - * 노드 하위의 특정태그를 모두 제거한다. - * @param {Element} el 확인 대상 요소 - * @param {String} sTagName 제거 대상 태그명 (대문자) - */ - stripTags :function(el, sTagName){ - var aelTarget = jindo.$$(sTagName, el); - for(var i = 0, elTarget; (elTarget = aelTarget[i]); i++){ - this.stripTag(elTarget); - } - }, + /** + * 노드 하위의 특정태그를 모두 제거한다. + * @param {Element} el 확인 대상 요소 + * @param {String} sTagName 제거 대상 태그명 (대문자) + */ + stripTags: function (el, sTagName) { + var aelTarget = jindo.$$(sTagName, el); + for (var i = 0, elTarget; (elTarget = aelTarget[i]); i++) { + this.stripTag(elTarget); + } + }, - /** - * [SMARTEDITORSUS-2203] 날짜표기를 바로 잡는다. ex) 2015.10.13 -> 2015.10.13. - * @param {String} sDate 확인할 문자열 - * @returns {String} 올바른 표기로 변환한 문자열 - */ - reviseDateFormat : function(sDate){ - if(sDate && sDate.replace){ - sDate = sDate.replace(this.rxDateFormat, "$&."); - } - return sDate; - }, + /** + * [SMARTEDITORSUS-2203] 날짜표기를 바로 잡는다. ex) 2015.10.13 -> 2015.10.13. + * @param {String} sDate 확인할 문자열 + * @returns {String} 올바른 표기로 변환한 문자열 + */ + reviseDateFormat: function (sDate) { + if (sDate && sDate.replace) { + sDate = sDate.replace(this.rxDateFormat, "$&."); + } + return sDate; + }, - /** - * 사용자 클래스 정보를 추출한다. - * @param {String} sStr 추출 String - * @param {rx} rxValue rx type 형식의 값 - * @param {String} sDivision value의 split 형식 - * @return {Array} - */ - getCustomCSS : function(sStr, rxValue, sDivision) { - var ret = []; - if('undefined' == typeof(sStr) || 'undefined' == typeof(rxValue) || !sStr || !rxValue) { - return ret; - } - - var aMatch = sStr.match(rxValue); - if(aMatch && aMatch[0]&&aMatch[1]) { - if(sDivision) { - ret = aMatch[1].split(sDivision); - } else { - ret[0] = aMatch[1]; - } - } - - return ret; - }, - /** - * HashTable로 구성된 Array의 같은 프로퍼티를 sSeperator 로 구분된 String 값으로 변환 - * @param {Object} v - * @param {Object} sKey - * @author senxation - * @example + /** + * 사용자 클래스 정보를 추출한다. + * @param {String} sStr 추출 String + * @param {rx} rxValue rx type 형식의 값 + * @param {String} sDivision value의 split 형식 + * @return {Array} + */ + getCustomCSS: function (sStr, rxValue, sDivision) { + var ret = []; + if ('undefined' == typeof (sStr) || 'undefined' == typeof (rxValue) || !sStr || !rxValue) { + return ret; + } + + var aMatch = sStr.match(rxValue); + if (aMatch && aMatch[0] && aMatch[1]) { + if (sDivision) { + ret = aMatch[1].split(sDivision); + } else { + ret[0] = aMatch[1]; + } + } + + return ret; + }, + /** + * HashTable로 구성된 Array의 같은 프로퍼티를 sSeperator 로 구분된 String 값으로 변환 + * @param {Object} v + * @param {Object} sKey + * @author senxation + * @example a = [{ b : "1" }, { b : "2" }] toStringSamePropertiesOfArray(a, "b", ", "); ==> "1, 2" - */ - toStringSamePropertiesOfArray : function(v, sKey, sSeperator) { - if(!v){ - return ""; - } - if (v instanceof Array) { - var a = []; - for (var i = 0; i < v.length; i++) { - a.push(v[i][sKey]); - } - return a.join(",").replace(/,/g, sSeperator); - } - else { - if (typeof v[sKey] == "undefined") { - return ""; - } - if (typeof v[sKey] == "string") { - return v[sKey]; - } - } - }, - - /** - * 단일 객체를 배열로 만들어줌 - * @param {Object} v - * @return {Array} - * @author senxation - * @example + */ + toStringSamePropertiesOfArray: function (v, sKey, sSeperator) { + if (!v) { + return ""; + } + if (v instanceof Array) { + var a = []; + for (var i = 0; i < v.length; i++) { + a.push(v[i][sKey]); + } + return a.join(",").replace(/,/g, sSeperator); + } + else { + if (typeof v[sKey] == "undefined") { + return ""; + } + if (typeof v[sKey] == "string") { + return v[sKey]; + } + } + }, + + /** + * 단일 객체를 배열로 만들어줌 + * @param {Object} v + * @return {Array} + * @author senxation + * @example makeArray("test"); ==> ["test"] - */ - makeArray : function(v) { - if (v === null || typeof v === "undefined"){ - return []; - } - if (v instanceof Array) { - return v; - } - var a = []; - a.push(v); - return a; - }, - - /** - * 말줄임을 할때 줄일 내용과 컨테이너가 다를 경우 처리 - * 컨테이너의 css white-space값이 "normal"이어야한다. (컨테이너보다 텍스트가 길면 여러행으로 표현되는 상태) - * @param {HTMLElement} elText 말줄임할 엘리먼트 - * @param {HTMLElement} elContainer 말줄임할 엘리먼트를 감싸는 컨테이너 - * @param {String} sStringTail 말줄임을 표현할 문자열 (미지정시 ...) - * @param {Number} nLine 최대 라인수 (미지정시 1) - * @author senxation - * @example + */ + makeArray: function (v) { + if (v === null || typeof v === "undefined") { + return []; + } + if (v instanceof Array) { + return v; + } + var a = []; + a.push(v); + return a; + }, + + /** + * 말줄임을 할때 줄일 내용과 컨테이너가 다를 경우 처리 + * 컨테이너의 css white-space값이 "normal"이어야한다. (컨테이너보다 텍스트가 길면 여러행으로 표현되는 상태) + * @param {HTMLElement} elText 말줄임할 엘리먼트 + * @param {HTMLElement} elContainer 말줄임할 엘리먼트를 감싸는 컨테이너 + * @param {String} sStringTail 말줄임을 표현할 문자열 (미지정시 ...) + * @param {Number} nLine 최대 라인수 (미지정시 1) + * @author senxation + * @example //div가 2줄 이하가 되도록 strong 내부의 내용을 줄임
    - 말줄임을적용할내용말줄임을적용할내용말줄임을적용할내용상세보기 + 말줄임을적용할내용말줄임을적용할내용말줄임을적용할내용상세보기
    ellipsis(jindo.$("a"), jindo.$("a").parentNode, "...", 2); - */ - ellipsis : function(elText, elContainer, sStringTail, nLine) { - sStringTail = sStringTail || "..."; - if (typeof nLine == "undefined") { - nLine = 1; - } - var welText = jindo.$Element(elText); - var welContainer = jindo.$Element(elContainer); - - var sText = welText.html(); - var nLength = sText.length; - var nCurrentHeight = welContainer.height(); - var nIndex = 0; - welText.html('A'); - var nHeight = welContainer.height(); + */ + ellipsis: function (elText, elContainer, sStringTail, nLine) { + sStringTail = sStringTail || "..."; + if (typeof nLine == "undefined") { + nLine = 1; + } + var welText = jindo.$Element(elText); + var welContainer = jindo.$Element(elContainer); - if (nCurrentHeight < nHeight * (nLine + 0.5)) { - return welText.html(sText); - } - - /** - * 지정된 라인보다 커질때까지 전체 남은 문자열의 절반을 더해나감 - */ - nCurrentHeight = nHeight; - while(nCurrentHeight < nHeight * (nLine + 0.5)) { - nIndex += Math.max(Math.ceil((nLength - nIndex)/2), 1); - welText.html(sText.substring(0, nIndex) + sStringTail); - nCurrentHeight = welContainer.height(); - } - - /** - * 지정된 라인이 될때까지 한글자씩 잘라냄 - */ - while(nCurrentHeight > nHeight * (nLine + 0.5)) { - nIndex--; - welText.html(sText.substring(0, nIndex) + sStringTail); - nCurrentHeight = welContainer.height(); - } - }, - - /** - * 최대 가로사이즈를 지정하여 말줄임한다. - * elText의 css white-space값이 "nowrap"이어야한다. (컨테이너보다 텍스트가 길면 행변환되지않고 가로로 길게 표현되는 상태) - * @param {HTMLElement} elText 말줄임할 엘리먼트 - * @param {String} sStringTail 말줄임을 표현할 문자열 (미지정시 ...) - * @param {Function} fCondition 조건 함수. 내부에서 true를 리턴하는 동안에만 말줄임을 진행한다. - * @author senxation - * @example + var sText = welText.html(); + var nLength = sText.length; + var nCurrentHeight = welContainer.height(); + var nIndex = 0; + welText.html('A'); + var nHeight = welContainer.height(); + + if (nCurrentHeight < nHeight * (nLine + 0.5)) { + return welText.html(sText); + } + + /** + * 지정된 라인보다 커질때까지 전체 남은 문자열의 절반을 더해나감 + */ + nCurrentHeight = nHeight; + while (nCurrentHeight < nHeight * (nLine + 0.5)) { + nIndex += Math.max(Math.ceil((nLength - nIndex) / 2), 1); + welText.html(sText.substring(0, nIndex) + sStringTail); + nCurrentHeight = welContainer.height(); + } + + /** + * 지정된 라인이 될때까지 한글자씩 잘라냄 + */ + while (nCurrentHeight > nHeight * (nLine + 0.5)) { + nIndex--; + welText.html(sText.substring(0, nIndex) + sStringTail); + nCurrentHeight = welContainer.height(); + } + }, + + /** + * 최대 가로사이즈를 지정하여 말줄임한다. + * elText의 css white-space값이 "nowrap"이어야한다. (컨테이너보다 텍스트가 길면 행변환되지않고 가로로 길게 표현되는 상태) + * @param {HTMLElement} elText 말줄임할 엘리먼트 + * @param {String} sStringTail 말줄임을 표현할 문자열 (미지정시 ...) + * @param {Function} fCondition 조건 함수. 내부에서 true를 리턴하는 동안에만 말줄임을 진행한다. + * @author senxation + * @example //150픽셀 이하가 되도록 strong 내부의 내용을 줄임 말줄임을적용할내용말줄임을적용할내용말줄임을적용할내용> ellipsisByPixel(jindo.$("a"), "...", 150); - */ - ellipsisByPixel : function(elText, sStringTail, nPixel, fCondition) { - sStringTail = sStringTail || "..."; - var welText = jindo.$Element(elText); - var nCurrentWidth = welText.width(); - if (nCurrentWidth < nPixel) { - return; - } - - var sText = welText.html(); - var nLength = sText.length; + */ + ellipsisByPixel: function (elText, sStringTail, nPixel, fCondition) { + sStringTail = sStringTail || "..."; + var welText = jindo.$Element(elText); + var nCurrentWidth = welText.width(); + if (nCurrentWidth < nPixel) { + return; + } - var nIndex = 0; - if (typeof fCondition == "undefined") { - var nWidth = welText.html('A').width(); - nCurrentWidth = nWidth; - - while(nCurrentWidth < nPixel) { - nIndex += Math.max(Math.ceil((nLength - nIndex)/2), 1); - welText.html(sText.substring(0, nIndex) + sStringTail); - nCurrentWidth = welText.width(); - } - - fCondition = function() { - return true; - }; - } - - nIndex = welText.html().length - sStringTail.length; - - while(nCurrentWidth > nPixel) { - if (!fCondition()) { - break; - } - nIndex--; - welText.html(sText.substring(0, nIndex) + sStringTail); - nCurrentWidth = welText.width(); - } - }, - - /** - * 여러개의 엘리먼트를 각각의 지정된 최대너비로 말줄임한다. - * 말줄임할 엘리먼트의 css white-space값이 "nowrap"이어야한다. (컨테이너보다 텍스트가 길면 행변환되지않고 가로로 길게 표현되는 상태) - * @param {Array} aElement 말줄임할 엘리먼트의 배열. 지정된 순서대로 말줄임한다. - * @param {String} sStringTail 말줄임을 표현할 문자열 (미지정시 ...) - * @param {Array} aMinWidth 말줄임할 너비의 배열. - * @param {Function} fCondition 조건 함수. 내부에서 true를 리턴하는 동안에만 말줄임을 진행한다. - * @example + var sText = welText.html(); + var nLength = sText.length; + + var nIndex = 0; + if (typeof fCondition == "undefined") { + var nWidth = welText.html('A').width(); + nCurrentWidth = nWidth; + + while (nCurrentWidth < nPixel) { + nIndex += Math.max(Math.ceil((nLength - nIndex) / 2), 1); + welText.html(sText.substring(0, nIndex) + sStringTail); + nCurrentWidth = welText.width(); + } + + fCondition = function () { + return true; + }; + } + + nIndex = welText.html().length - sStringTail.length; + + while (nCurrentWidth > nPixel) { + if (!fCondition()) { + break; + } + nIndex--; + welText.html(sText.substring(0, nIndex) + sStringTail); + nCurrentWidth = welText.width(); + } + }, + + /** + * 여러개의 엘리먼트를 각각의 지정된 최대너비로 말줄임한다. + * 말줄임할 엘리먼트의 css white-space값이 "nowrap"이어야한다. (컨테이너보다 텍스트가 길면 행변환되지않고 가로로 길게 표현되는 상태) + * @param {Array} aElement 말줄임할 엘리먼트의 배열. 지정된 순서대로 말줄임한다. + * @param {String} sStringTail 말줄임을 표현할 문자열 (미지정시 ...) + * @param {Array} aMinWidth 말줄임할 너비의 배열. + * @param {Function} fCondition 조건 함수. 내부에서 true를 리턴하는 동안에만 말줄임을 진행한다. + * @example //#a #b #c의 너비를 각각 100, 50, 50픽셀로 줄임 (div#parent 가 200픽셀 이하이면 중단) //#c의 너비를 줄이는 동안 fCondition에서 false를 리턴하면 b, a는 말줄임 되지 않는다.
    - 말줄임을적용할내용 - 말줄임을적용할내용 - 말줄임을적용할내용 + 말줄임을적용할내용 + 말줄임을적용할내용 + 말줄임을적용할내용
    ellipsisElementsToDesinatedWidth([jindo.$("c"), jindo.$("b"), jindo.$("a")], "...", [100, 50, 50], function(){ - if (jindo.$Element("parent").width() > 200) { - return true; - } - return false; + if (jindo.$Element("parent").width() > 200) { + return true; + } + return false; }); - */ - ellipsisElementsToDesinatedWidth : function(aElement, sStringTail, aMinWidth, fCondition) { - jindo.$A(aElement).forEach(function(el, i){ - if (!el) { - jindo.$A.Continue(); - } - nhn.husky.SE2M_Utils.ellipsisByPixel(el, sStringTail, aMinWidth[i], fCondition); - }); - }, - - /** - * 숫자를 입력받아 정해진 길이만큼 앞에 "0"이 추가된 문자열을 구한다. - * @param {Number} nNumber - * @param {Number} nLength - * @return {String} - * @example + */ + ellipsisElementsToDesinatedWidth: function (aElement, sStringTail, aMinWidth, fCondition) { + jindo.$A(aElement).forEach(function (el, i) { + if (!el) { + jindo.$A.Continue(); + } + nhn.husky.SE2M_Utils.ellipsisByPixel(el, sStringTail, aMinWidth[i], fCondition); + }); + }, + + /** + * 숫자를 입력받아 정해진 길이만큼 앞에 "0"이 추가된 문자열을 구한다. + * @param {Number} nNumber + * @param {Number} nLength + * @return {String} + * @example paddingZero(10, 5); ==> "00010" (String) - */ - paddingZero : function(nNumber, nLength) { - var sResult = nNumber.toString(); - while (sResult.length < nLength) { - sResult = ("0" + sResult); - } - return sResult; - }, - - /** - * string을 byte 단위로 짤라서 tail를 붙힌다. - * @param {String} sString - * @param {Number} nByte - * @param {String} sTail - * @example - cutStringToByte('일이삼사오육', 6, '...') ==> '일이삼...' (string) - */ - cutStringToByte : function(sString, nByte, sTail){ - if(sString === null || sString.length === 0) { - return sString; - } - - sString = sString.replace(/ +/g, " "); - if (!sTail && sTail != "") { - sTail = "..."; - } - - var maxByte = nByte; - var n=0; - var nLen = sString.length; - for(var i=0; i maxByte ) { - return sString.substring(0, i)+sTail; - } - } - return sString; - }, - - /** - * 입력받은 문자의 byte 구한다. - * @param {String} ch - * - */ - getCharByte : function(ch){ - if (ch === null || ch.length < 1) { - return 0; - } - - var byteSize = 0; - var str = escape(ch); - - if ( str.length == 1 ) { // when English then 1byte - byteSize ++; - } else if ( str.indexOf("%u") != -1 ) { // when Korean then 2byte - byteSize += 2; - } else if ( str.indexOf("%") != -1 ) { // else 3byte - byteSize += str.length/3; - } - return byteSize; - }, - - /** - * Hash Table에서 원하는 키값만을 가지는 필터된 새로운 Hash Table을 구한다. - * @param {HashTable} htUnfiltered - * @param {Array} aKey - * @return {HashTable} - * @author senxation - * @example + */ + paddingZero: function (nNumber, nLength) { + var sResult = nNumber.toString(); + while (sResult.length < nLength) { + sResult = ("0" + sResult); + } + return sResult; + }, + + /** + * string을 byte 단위로 짤라서 tail를 붙힌다. + * @param {String} sString + * @param {Number} nByte + * @param {String} sTail + * @example + cutStringToByte('일이삼사오육', 6, '...') ==> '일이삼...' (string) + */ + cutStringToByte: function (sString, nByte, sTail) { + if (sString === null || sString.length === 0) { + return sString; + } + + sString = sString.replace(/ +/g, " "); + if (!sTail && sTail != "") { + sTail = "..."; + } + + var maxByte = nByte; + var n = 0; + var nLen = sString.length; + for (var i = 0; i < nLen; i++) { + n += this.getCharByte(sString.charAt(i)); + if (n == maxByte) { + if (i == nLen - 1) { + return sString; + } else { + return sString.substring(0, i) + sTail; + } + } else if (n > maxByte) { + return sString.substring(0, i) + sTail; + } + } + return sString; + }, + + /** + * 입력받은 문자의 byte 구한다. + * @param {String} ch + * + */ + getCharByte: function (ch) { + if (ch === null || ch.length < 1) { + return 0; + } + + var byteSize = 0; + var str = escape(ch); + + if (str.length == 1) { // when English then 1byte + byteSize++; + } else if (str.indexOf("%u") != -1) { // when Korean then 2byte + byteSize += 2; + } else if (str.indexOf("%") != -1) { // else 3byte + byteSize += str.length / 3; + } + return byteSize; + }, + + /** + * Hash Table에서 원하는 키값만을 가지는 필터된 새로운 Hash Table을 구한다. + * @param {HashTable} htUnfiltered + * @param {Array} aKey + * @return {HashTable} + * @author senxation + * @example getFilteredHashTable({ - a : 1, - b : 2, - c : 3, - d : 4 + a : 1, + b : 2, + c : 3, + d : 4 }, ["a", "c"]); ==> { a : 1, c : 3 } - */ - getFilteredHashTable : function(htUnfiltered, vKey) { - if (!(vKey instanceof Array)) { - return arguments.callee.call(this, htUnfiltered, [ vKey ]); - } - - var waKey = jindo.$A(vKey); - return jindo.$H(htUnfiltered).filter(function(vValue, sKey){ - if (waKey.has(sKey) && vValue) { - return true; - } else { - return false; - } - }).$value(); - }, - - isBlankNode : function(elNode){ - var isBlankTextNode = this.isBlankTextNode; - - var bEmptyContent = function(elNode){ - if(!elNode) { - return true; - } - - if(isBlankTextNode(elNode)){ - return true; - } + */ + getFilteredHashTable: function (htUnfiltered, vKey) { + if (!(vKey instanceof Array)) { + return arguments.callee.call(this, htUnfiltered, [vKey]); + } - if(elNode.tagName == "BR") { - return true; - } - - if(elNode.innerHTML == " " || elNode.innerHTML == "") { - return true; - } - - return false; - }; - var bEmptyP = function(elNode){ - if(elNode.tagName == "IMG" || elNode.tagName == "IFRAME"){ - return false; - } - - if(bEmptyContent(elNode)){ - return true; - } - - if(elNode.tagName == "P"){ - for(var i=elNode.childNodes.length-1; i>=0; i--){ - var elTmp = elNode.childNodes[i]; - if(isBlankTextNode(elTmp)){ - elTmp.parentNode.removeChild(elTmp); - } - } - - if(elNode.childNodes.length == 1){ - if(elNode.firstChild.tagName == "IMG" || elNode.firstChild.tagName == "IFRAME"){ - return false; - } - if(bEmptyContent(elNode.firstChild)){ - return true; - } - } - } - - return false; - }; + var waKey = jindo.$A(vKey); + return jindo.$H(htUnfiltered).filter(function (vValue, sKey) { + if (waKey.has(sKey) && vValue) { + return true; + } else { + return false; + } + }).$value(); + }, - if(bEmptyP(elNode)){ - return true; - } + isBlankNode: function (elNode) { + var isBlankTextNode = this.isBlankTextNode; - for(var i=0, nLen=elNode.childNodes.length; i]*))", 'i'); - var aResult = rx.exec(sHtmlTag); - - if (!aResult) { - return ''; - } - - var sAttrTmp = (aResult[1] || aResult[2] || aResult[3]); // for chrome 5.x bug! - if (!!sAttrTmp) { - sAttrTmp = sAttrTmp.replace(/[\"]/g, ''); - } - - return sAttrTmp; - }, - - - /** - * iframe 영역의 aling 정보를 다시 세팅하는 부분. - * iframe 형태의 산출물을 에디터에 삽입 이후에 에디터 정렬기능을 추가 하였을때 ir_to_db 이전 시점에서 div태그에 정렬을 넣어주는 로직임. - * 브라우저 형태에 따라 정렬 태그가 iframe을 감싸는 div 혹은 p 태그에 정렬이 추가된다. - * @param {HTMLElement} el iframe의 parentNode - * @param {Document} oDoc document - */ - // [COM-1151] SE2M_PreStringConverter 에서 수정하도록 변경 - iframeAlignConverter : function(el, oDoc){ - var sTagName = el.tagName.toUpperCase(); - - if(sTagName == "DIV" || sTagName == 'P'){ - //irToDbDOM 에서 최상위 노드가 div 엘리먼트 이므로 parentNode가 없으면 최상의 div 노드 이므로 리턴한다. - if(el.parentNode === null ){ - return; - } - var elWYSIWYGDoc = oDoc; - var wel = jindo.$Element(el); - var sHtml = wel.html(); - //현재 align을 얻어오기. - var sAlign = jindo.$Element(el).attr('align') || jindo.$Element(el).css('text-align'); - //if(!sAlign){ // P > DIV의 경우 문제 발생, 수정 화면에 들어 왔을 때 태그 깨짐 - // return; - //} - //새로운 div 노드 생성한다. - var welAfter = jindo.$Element(jindo.$('
    ', elWYSIWYGDoc)); - welAfter.html(sHtml).attr('align', sAlign); - wel.replace(welAfter); - } - }, - - /** - * jindo.$JSON.fromXML을 변환한 메서드. - * 소숫점이 있는 경우의 처리 시에 숫자로 변환하지 않도록 함(parseFloat 사용 안하도록 수정) - * 관련 BTS : [COM-1093] - * @param {String} sXML XML 형태의 문자열 - * @return {jindo.$JSON} - */ - getJsonDatafromXML : function(sXML) { - var o = {}; - var re = /\s*<(\/?[\w:\-]+)((?:\s+[\w:\-]+\s*=\s*(?:"(?:\\"|[^"])*"|'(?:\\'|[^'])*'))*)\s*((?:\/>)|(?:><\/\1>|\s*))|\s*\s*|\s*>?([^<]*)/ig; - var re2= /^[0-9]+(?:\.[0-9]+)?$/; - var re3= /^\s+$/g; - var ec = {"&":"&"," ":" ",""":"\"","<":"<",">":">"}; - var fg = {tags:["/"],stack:[o]}; - var es = function(s){ - if (typeof s == "undefined") { - return ""; - } - return s.replace(/&[a-z]+;/g, function(m){ return (typeof ec[m] == "string")?ec[m]:m; }); - }; - var at = function(s,c) { - s.replace(/([\w\:\-]+)\s*=\s*(?:"((?:\\"|[^"])*)"|'((?:\\'|[^'])*)')/g, function($0,$1,$2,$3) { - c[$1] = es(($2?$2.replace(/\\"/g,'"'):undefined)||($3?$3.replace(/\\'/g,"'"):undefined)); - }); - }; - - var em = function(o) { - for(var x in o){ - if (o.hasOwnProperty(x)) { - if(Object.prototype[x]) { - continue; - } - return false; - } - } - return true; - }; - - // $0 : 전체 - // $1 : 태그명 - // $2 : 속성문자열 - // $3 : 닫는태그 - // $4 : CDATA바디값 - // $5 : 그냥 바디값 - var cb = function($0,$1,$2,$3,$4,$5) { - var cur, cdata = ""; - var idx = fg.stack.length - 1; - - if (typeof $1 == "string" && $1) { - if ($1.substr(0,1) != "/") { - var has_attr = (typeof $2 == "string" && $2); - var closed = (typeof $3 == "string" && $3); - var newobj = (!has_attr && closed)?"":{}; + if (elNode.innerHTML == " " || elNode.innerHTML == "") { + return true; + } - cur = fg.stack[idx]; - - if (typeof cur[$1] == "undefined") { - cur[$1] = newobj; - cur = fg.stack[idx+1] = cur[$1]; - } else if (cur[$1] instanceof Array) { - var len = cur[$1].length; - cur[$1][len] = newobj; - cur = fg.stack[idx+1] = cur[$1][len]; - } else { - cur[$1] = [cur[$1], newobj]; - cur = fg.stack[idx+1] = cur[$1][1]; - } - - if (has_attr) { - at($2,cur); - } + return false; + }; + var bEmptyP = function (elNode) { + if (elNode.tagName == "IMG" || elNode.tagName == "IFRAME") { + return false; + } - fg.tags[idx+1] = $1; + if (bEmptyContent(elNode)) { + return true; + } - if (closed) { - fg.tags.length--; - fg.stack.length--; - } - } else { - fg.tags.length--; - fg.stack.length--; - } - } else if (typeof $4 == "string" && $4) { - cdata = $4; - } else if (typeof $5 == "string" && $5.replace(re3, "")) { // [SMARTEDITORSUS-1525] 닫는 태그인데 공백문자가 들어있어 cdata 값을 덮어쓰는 경우 방지 - cdata = es($5); - } - - if (cdata.length > 0) { - var par = fg.stack[idx-1]; - var tag = fg.tags[idx]; + if (elNode.tagName == "P") { + for (var i = elNode.childNodes.length - 1; i >= 0; i--) { + var elTmp = elNode.childNodes[i]; + if (isBlankTextNode(elTmp)) { + elTmp.parentNode.removeChild(elTmp); + } + } - if (re2.test(cdata)) { - //cdata = parseFloat(cdata); - }else if (cdata == "true" || cdata == "false"){ - cdata = new Boolean(cdata); - } + if (elNode.childNodes.length == 1) { + if (elNode.firstChild.tagName == "IMG" || elNode.firstChild.tagName == "IFRAME") { + return false; + } + if (bEmptyContent(elNode.firstChild)) { + return true; + } + } + } - if(typeof par =='undefined') { - return; - } - - if (par[tag] instanceof Array) { - var o = par[tag]; - if (typeof o[o.length-1] == "object" && !em(o[o.length-1])) { - o[o.length-1].$cdata = cdata; - o[o.length-1].toString = function(){ return cdata; }; - } else { - o[o.length-1] = cdata; - } - } else { - if (typeof par[tag] == "object" && !em(par[tag])) { - par[tag].$cdata = cdata; - par[tag].toString = function() { return cdata; }; - } else { - par[tag] = cdata; - } - } - } - }; - - sXML = sXML.replace(/<(\?|\!-)[^>]*>/g, ""); - sXML.replace(re, cb); - - return jindo.$Json(o); - }, - /** - * 문자열내 자주 사용되는 특수문자 5개 (", ', &, <, >)를 HTML Entity Code 로 변경하여 반환 - * @see http://www.w3.org/TR/html4/charset.html#entities - * @param {String} sString 원본 문자열 - * @returns {String} 변경된 문자열 - * @example - * replaceSpecialChar() or replaceSpecialChar(123) - * // 결과: "" - * - * replaceSpecialChar("", ', &, <, >") - * // 결과: &quot;, &#39;, &amp;, &lt;, &gt; - */ - replaceSpecialChar : function(sString){ - return (typeof(sString) == "string") ? (sString.replace(/\&/g, "&").replace(/\"/g, """).replace(/\'/g, "'").replace(//g, ">")) : ""; - }, - /** - * 문자열내 자주 사용되는 HTML Entity Code 5개를 원래 문자로 (", ', &, <, >)로 변경하여 반환 - * @see http://www.w3.org/TR/html4/charset.html#entities - * @param {String} sString 원본 문자열 - * @returns {String} 변경된 문자열 - * @example - * restoreSpecialChar() or restoreSpecialChar(123) - * // 결과: "" - * - * restoreSpecialChar("&quot;, &#39;, &amp;, &lt;, &gt;") - * // 결과: ", ', &, <, > - */ - restoreSpecialChar : function(sString){ - return (typeof(sString) == "string") ? (sString.replace(/"/g, "\"").replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")) : ""; - } + return false; + }; + + if (bEmptyP(elNode)) { + return true; + } + + for (var i = 0, nLen = elNode.childNodes.length; i < nLen; i++) { + var elTmp = elNode.childNodes[i]; + if (!bEmptyP(elTmp)) { + return false; + } + } + + return true; + }, + + isBlankTextNode: function (oNode) { + var sText; + + if (oNode.nodeType == 3) { + sText = oNode.nodeValue; + sText = sText.replace(unescape("%uFEFF"), ''); + + if (sText == "") { + return true; + } + } + + return false; + }, + + isFirstChildOfNode: function (sTagName, sParentTagName, elNode) { + if (!elNode) { + return false; + } + + if (elNode.tagName == sParentTagName && elNode.firstChild.tagName == sTagName) { + return true; + } + + return false; + }, + + /** + * elNode의 상위 노드 중 태그명이 sTagName과 일치하는 것이 있다면 반환. + * @param {String} sTagName 검색 할 태그명(반드시 대문자를 사용할 것) + * @param {HTMLElement} elNode 검색 시작점으로 사용 할 노드 + * @return {HTMLElement} 부모 노드 중 태그명이 sTagName과 일치하는 노드. 없을 경우 null 반환 + */ + findAncestorByTagName: function (sTagName, elNode) { + while (elNode && elNode.tagName != sTagName) { + elNode = elNode.parentNode; + } + + return elNode; + }, + + /** + * [SMARTEDITORSUS-1735] + * elNode의 상위 노드 중 태그명이 sTagName과 일치하는 것이 있다면 + * 해당 노드와 재귀 탐색 횟수가 담긴 객체를 반환. + * @param {String} sTagName 검색 할 태그명(반드시 대문자를 사용할 것) + * @param {HTMLElement} elNode 검색 시작점으로 사용 할 노드 + * @return {Object} + * {HTMLElement} Object.elNode 부모 노드 중 태그명이 sTagName과 일치하는 노드. 없을 경우 null 반환 + * {Number} Object.nRecursiveCount 재귀 탐색 횟수 + */ + findAncestorByTagNameWithCount: function (sTagName, elNode) { + var nRecursiveCount = 0; + var htResult = {}; + + while (elNode && elNode.tagName != sTagName) { + elNode = elNode.parentNode; + nRecursiveCount += 1; + } + + htResult = { + elNode: elNode, + nRecursiveCount: nRecursiveCount + } + + return htResult; + }, + + /** + * [SMARTEDITORSUS-1672] elNode의 상위 노드 중 태그명이 aTagName 의 요소 중 하나와 일치하는 것이 있다면 반환. + * @param {String} aTagName 검색 할 태그명이 담긴 배열 + * @param {HTMLElement} elNode 검색 시작점으로 사용 할 노드 + * @return {HTMLElement} 부모 노드 중 태그명이 aTagName의 요소 중 하나와 일치하는 노드. 없을 경우 null 반환 + */ + findClosestAncestorAmongTagNames: function (aTagName, elNode) { + var rxTagNames = new RegExp("^(" + aTagName.join("|") + ")$", "i"); + + while (elNode && !rxTagNames.test(elNode.tagName)) { + elNode = elNode.parentNode; + } + + return elNode; + }, + + /** + * [SMARTEDITORSUS-1735] + * elNode의 상위 노드 중 태그명이 aTagName 의 요소 중 하나와 일치하는 것이 있다면 + * 해당 노드와 재귀 탐색 횟수가 담긴 객체를 반환. + * @param {String} aTagName 검색 할 태그명이 담긴 배열 + * @param {HTMLElement} elNode 검색 시작점으로 사용 할 노드 + * @return {Object} + * {HTMLElement} Object.elNode 부모 노드 중 태그명이 aTagName의 요소 중 하나와 일치하는 노드. 없을 경우 null 반환 + * {Number} Object.nRecursiveCount 재귀 탐색 횟수 + */ + findClosestAncestorAmongTagNamesWithCount: function (aTagName, elNode) { + var nRecursiveCount = 0; + var htResult = {}; + + var rxTagNames = new RegExp("^(" + aTagName.join("|") + ")$", "i"); + + while (elNode && !rxTagNames.test(elNode.tagName)) { + elNode = elNode.parentNode; + nRecursiveCount += 1; + } + + htResult = { + elNode: elNode, + nRecursiveCount: nRecursiveCount + } + + return htResult; + }, + + /** + * [SMARTEDITORSUS-2136] 대상이 Number인지 확인한다. + * + * @param {Number} n 판별 대상 + * @return {Boolean} 대상이 Number이다. + * @see http://stackoverflow.com/questions/18082/validate-decimal-numbers-in-javascript-isnumeric/1830844#1830844 + * */ + isNumber: function (n) { + return !isNaN(parseFloat(n)) && isFinite(n); + }, + + /** + * [SMARTEDITORSUS-2136] 객체의 property를 delete 연산자로 제거하는 polyfill + * + * @param {Object} oTarget 대상 객체 + * @param {String} sProp property 명 + * + * @example nhn.husky.SE2M_Utils.deleteProperty(elTable, 'propA'); // elTable.propA를 delete + * + * @see http://stackoverflow.com/questions/1073414/deleting-a-window-property-in-ie + * */ + deleteProperty: function (oTarget, sProp) { + if ((typeof (oTarget) !== 'object') || (typeof (sProp) !== 'string') || (typeof (oTarget[sProp]) === 'undefined')) { + return; + } + + oTarget[sProp] = undefined; + try { + delete oTarget[sProp]; + } catch (e) { + // [IE 8-] + } + }, + + loadCSS: function (url, fnCallback) { + var oDoc = document; + var elHead = oDoc.getElementsByTagName("HEAD")[0]; + var elStyle = oDoc.createElement("LINK"); + elStyle.setAttribute("type", "text/css"); + elStyle.setAttribute("rel", "stylesheet"); + elStyle.setAttribute("href", url); + if (fnCallback) { + if ('onload' in elStyle) { + elStyle.onload = function () { + fnCallback(); + // [SMARTEDITORSUS-2032] IE10호환성보기(IE7) 에서 onload 이벤트가 2번 발생할 수 있는 가능성이 있기 때문에 + // 콜백실행 후 onload 이벤트 핸들러를 제거 + this.onload = null; + }; + } else { + elStyle.onreadystatechange = function () { + if (elStyle.readyState != "complete") { + return; + } + + // [SMARTEDITORSUS-308] [IE9] 응답이 304인 경우 + // onreadystatechage 핸들러에서 readyState 가 complete 인 경우가 두 번 발생 + // LINK 엘리먼트의 속성으로 콜백 실행 여부를 플래그로 남겨놓아 처리함 + if (elStyle.getAttribute("_complete")) { + return; + } + + elStyle.setAttribute("_complete", true); + + fnCallback(); + }; + } + } + elHead.appendChild(elStyle); + }, + + getUniqueId: function (sPrefix) { + return (sPrefix || '') + jindo.$Date().time() + (Math.random() * 100000).toFixed(); + }, + + /** + * @param {Object} oSrc value copy할 object + * @return {Object} + * @example + * var oSource = [1, 3, 4, { a:1, b:2, c: { a:1 }}]; + var oTarget = oSource; // call by reference + oTarget = nhn.husky.SE2M_Utils.clone(oSource); + + oTarget[1] = 2; + oTarget[3].a = 100; + console.log(oSource); // check for deep copy + console.log(oTarget, oTarget instanceof Object); // check instance type! + */ + clone: function (oSrc, oChange) { + if ('undefined' != typeof (oSrc) && !!oSrc && (oSrc.constructor == Array || oSrc.constructor == Object)) { + var oCopy = (oSrc.constructor == Array ? [] : {}); + for (var property in oSrc) { + if ('undefined' != typeof (oChange) && !!oChange[property]) { + oCopy[property] = arguments.callee(oChange[property]); + } else { + oCopy[property] = arguments.callee(oSrc[property]); + } + } + + return oCopy; + } + + return oSrc; + }, + + getHtmlTagAttr: function (sHtmlTag, sAttr) { + var rx = new RegExp('\\s' + sAttr + "=('([^']*)'|\"([^\"]*)\"|([^\"' >]*))", 'i'); + var aResult = rx.exec(sHtmlTag); + + if (!aResult) { + return ''; + } + + var sAttrTmp = (aResult[1] || aResult[2] || aResult[3]); // for chrome 5.x bug! + if (!!sAttrTmp) { + sAttrTmp = sAttrTmp.replace(/[\"]/g, ''); + } + + return sAttrTmp; + }, + + + /** + * iframe 영역의 aling 정보를 다시 세팅하는 부분. + * iframe 형태의 산출물을 에디터에 삽입 이후에 에디터 정렬기능을 추가 하였을때 ir_to_db 이전 시점에서 div태그에 정렬을 넣어주는 로직임. + * 브라우저 형태에 따라 정렬 태그가 iframe을 감싸는 div 혹은 p 태그에 정렬이 추가된다. + * @param {HTMLElement} el iframe의 parentNode + * @param {Document} oDoc document + */ + // [COM-1151] SE2M_PreStringConverter 에서 수정하도록 변경 + iframeAlignConverter: function (el, oDoc) { + var sTagName = el.tagName.toUpperCase(); + + if (sTagName == "DIV" || sTagName == 'P') { + //irToDbDOM 에서 최상위 노드가 div 엘리먼트 이므로 parentNode가 없으면 최상의 div 노드 이므로 리턴한다. + if (el.parentNode === null) { + return; + } + var elWYSIWYGDoc = oDoc; + var wel = jindo.$Element(el); + var sHtml = wel.html(); + //현재 align을 얻어오기. + var sAlign = jindo.$Element(el).attr('align') || jindo.$Element(el).css('text-align'); + //if(!sAlign){ // P > DIV의 경우 문제 발생, 수정 화면에 들어 왔을 때 태그 깨짐 + // return; + //} + //새로운 div 노드 생성한다. + var welAfter = jindo.$Element(jindo.$('
    ', elWYSIWYGDoc)); + welAfter.html(sHtml).attr('align', sAlign); + wel.replace(welAfter); + } + }, + + /** + * jindo.$JSON.fromXML을 변환한 메서드. + * 소숫점이 있는 경우의 처리 시에 숫자로 변환하지 않도록 함(parseFloat 사용 안하도록 수정) + * 관련 BTS : [COM-1093] + * @param {String} sXML XML 형태의 문자열 + * @return {jindo.$JSON} + */ + getJsonDatafromXML: function (sXML) { + var o = {}; + var re = /\s*<(\/?[\w:\-]+)((?:\s+[\w:\-]+\s*=\s*(?:"(?:\\"|[^"])*"|'(?:\\'|[^'])*'))*)\s*((?:\/>)|(?:><\/\1>|\s*))|\s*\s*|\s*>?([^<]*)/ig; + var re2 = /^[0-9]+(?:\.[0-9]+)?$/; + var re3 = /^\s+$/g; + var ec = { "&": "&", " ": " ", """: "\"", "<": "<", ">": ">" }; + var fg = { tags: ["/"], stack: [o] }; + var es = function (s) { + if (typeof s == "undefined") { + return ""; + } + return s.replace(/&[a-z]+;/g, function (m) { return (typeof ec[m] == "string") ? ec[m] : m; }); + }; + var at = function (s, c) { + s.replace(/([\w\:\-]+)\s*=\s*(?:"((?:\\"|[^"])*)"|'((?:\\'|[^'])*)')/g, function ($0, $1, $2, $3) { + c[$1] = es(($2 ? $2.replace(/\\"/g, '"') : undefined) || ($3 ? $3.replace(/\\'/g, "'") : undefined)); + }); + }; + + var em = function (o) { + for (var x in o) { + if (o.hasOwnProperty(x)) { + if (Object.prototype[x]) { + continue; + } + return false; + } + } + return true; + }; + + // $0 : 전체 + // $1 : 태그명 + // $2 : 속성문자열 + // $3 : 닫는태그 + // $4 : CDATA바디값 + // $5 : 그냥 바디값 + var cb = function ($0, $1, $2, $3, $4, $5) { + var cur, cdata = ""; + var idx = fg.stack.length - 1; + + if (typeof $1 == "string" && $1) { + if ($1.substr(0, 1) != "/") { + var has_attr = (typeof $2 == "string" && $2); + var closed = (typeof $3 == "string" && $3); + var newobj = (!has_attr && closed) ? "" : {}; + + cur = fg.stack[idx]; + + if (typeof cur[$1] == "undefined") { + cur[$1] = newobj; + cur = fg.stack[idx + 1] = cur[$1]; + } else if (cur[$1] instanceof Array) { + var len = cur[$1].length; + cur[$1][len] = newobj; + cur = fg.stack[idx + 1] = cur[$1][len]; + } else { + cur[$1] = [cur[$1], newobj]; + cur = fg.stack[idx + 1] = cur[$1][1]; + } + + if (has_attr) { + at($2, cur); + } + + fg.tags[idx + 1] = $1; + + if (closed) { + fg.tags.length--; + fg.stack.length--; + } + } else { + fg.tags.length--; + fg.stack.length--; + } + } else if (typeof $4 == "string" && $4) { + cdata = $4; + } else if (typeof $5 == "string" && $5.replace(re3, "")) { // [SMARTEDITORSUS-1525] 닫는 태그인데 공백문자가 들어있어 cdata 값을 덮어쓰는 경우 방지 + cdata = es($5); + } + + if (cdata.length > 0) { + var par = fg.stack[idx - 1]; + var tag = fg.tags[idx]; + + if (re2.test(cdata)) { + //cdata = parseFloat(cdata); + } else if (cdata == "true" || cdata == "false") { + cdata = new Boolean(cdata); + } + + if (typeof par == 'undefined') { + return; + } + + if (par[tag] instanceof Array) { + var o = par[tag]; + if (typeof o[o.length - 1] == "object" && !em(o[o.length - 1])) { + o[o.length - 1].$cdata = cdata; + o[o.length - 1].toString = function () { return cdata; }; + } else { + o[o.length - 1] = cdata; + } + } else { + if (typeof par[tag] == "object" && !em(par[tag])) { + par[tag].$cdata = cdata; + par[tag].toString = function () { return cdata; }; + } else { + par[tag] = cdata; + } + } + } + }; + + sXML = sXML.replace(/<(\?|\!-)[^>]*>/g, ""); + sXML.replace(re, cb); + + return jindo.$Json(o); + }, + /** + * 문자열내 자주 사용되는 특수문자 5개 (", ', &, <, >)를 HTML Entity Code 로 변경하여 반환 + * @see http://www.w3.org/TR/html4/charset.html#entities + * @param {String} sString 원본 문자열 + * @returns {String} 변경된 문자열 + * @example + * replaceSpecialChar() or replaceSpecialChar(123) + * // 결과: "" + * + * replaceSpecialChar("", ', &, <, >") + * // 결과: &quot;, &#39;, &amp;, &lt;, &gt; + */ + replaceSpecialChar: function (sString) { + return (typeof (sString) == "string") ? (sString.replace(/\&/g, "&").replace(/\"/g, """).replace(/\'/g, "'").replace(//g, ">")) : ""; + }, + /** + * 문자열내 자주 사용되는 HTML Entity Code 5개를 원래 문자로 (", ', &, <, >)로 변경하여 반환 + * @see http://www.w3.org/TR/html4/charset.html#entities + * @param {String} sString 원본 문자열 + * @returns {String} 변경된 문자열 + * @example + * restoreSpecialChar() or restoreSpecialChar(123) + * // 결과: "" + * + * restoreSpecialChar("&quot;, &#39;, &amp;, &lt;, &gt;") + * // 결과: ", ', &, <, > + */ + restoreSpecialChar: function (sString) { + return (typeof (sString) == "string") ? (sString.replace(/"/g, "\"").replace(/'/g, "'").replace(/</g, "<").replace(/>/g, ">").replace(/&/g, "&")) : ""; + } }; /** @@ -15608,90 +15612,90 @@ getFilteredHashTable({ * HTML모드와 TEXT 모드의 편집 영역인 TEXTAREA에 대한 자동확장 처리 */ nhn.husky.AutoResizer = jindo.$Class({ - welHiddenDiv : null, - welCloneDiv : null, - elContainer : null, - $init : function(el, htOption){ - var aCopyStyle = [ - 'lineHeight', 'textDecoration', 'letterSpacing', - 'fontSize', 'fontFamily', 'fontStyle', 'fontWeight', - 'textTransform', 'textAlign', 'direction', 'wordSpacing', 'fontSizeAdjust', - 'paddingTop', 'paddingLeft', 'paddingBottom', 'paddingRight', 'width' - ], - i = aCopyStyle.length, - oCss = { - "position" : "absolute", - "top" : -9999, - "left" : -9999, - "opacity": 0, - "overflow": "hidden", - "wordWrap" : "break-word" - }; - - this.nMinHeight = htOption.nMinHeight; - this.wfnCallback = htOption.wfnCallback; - - this.elContainer = el.parentNode; - this.welTextArea = jindo.$Element(el); // autoresize를 적용할 TextArea - this.welHiddenDiv = jindo.$Element('
    '); + welHiddenDiv: null, + welCloneDiv: null, + elContainer: null, + $init: function (el, htOption) { + var aCopyStyle = [ + 'lineHeight', 'textDecoration', 'letterSpacing', + 'fontSize', 'fontFamily', 'fontStyle', 'fontWeight', + 'textTransform', 'textAlign', 'direction', 'wordSpacing', 'fontSizeAdjust', + 'paddingTop', 'paddingLeft', 'paddingBottom', 'paddingRight', 'width' + ], + i = aCopyStyle.length, + oCss = { + "position": "absolute", + "top": -9999, + "left": -9999, + "opacity": 0, + "overflow": "hidden", + "wordWrap": "break-word" + }; - this.wfnResize = jindo.$Fn(this._resize, this); + this.nMinHeight = htOption.nMinHeight; + this.wfnCallback = htOption.wfnCallback; - this.sOverflow = this.welTextArea.css("overflow"); - this.welTextArea.css("overflow", "hidden"); + this.elContainer = el.parentNode; + this.welTextArea = jindo.$Element(el); // autoresize를 적용할 TextArea + this.welHiddenDiv = jindo.$Element('
    '); - while(i--){ - oCss[aCopyStyle[i]] = this.welTextArea.css(aCopyStyle[i]); - } - - this.welHiddenDiv.css(oCss); - - this.nLastHeight = this.welTextArea.height(); - }, - bind : function(){ - this.welCloneDiv = jindo.$Element(this.welHiddenDiv.$value().cloneNode(false)); - - this.wfnResize.attach(this.welTextArea, "keyup"); - this.welCloneDiv.appendTo(this.elContainer); - - this._resize(); - }, - unbind : function(){ - this.wfnResize.detach(this.welTextArea, "keyup"); - this.welTextArea.css("overflow", this.sOverflow); - - if(this.welCloneDiv){ - this.welCloneDiv.leave(); - } - }, - _resize : function(){ - var sContents = this.welTextArea.$value().value, - bExpand = false, - nHeight; + this.wfnResize = jindo.$Fn(this._resize, this); - if(sContents === this.sContents){ - return; - } - - this.sContents = sContents.replace(/&/g, '&').replace(//g, '>').replace(/ /g, ' ').replace(/\n/g, '
    '); - this.sContents += "
    "; // 마지막 개행 뒤에
    을 더 붙여주어야 늘어나는 높이가 동일함 - - this.welCloneDiv.html(this.sContents); - nHeight = this.welCloneDiv.height(); - - if(nHeight < this.nMinHeight){ - nHeight = this.nMinHeight; - } + this.sOverflow = this.welTextArea.css("overflow"); + this.welTextArea.css("overflow", "hidden"); - this.welTextArea.css("height", nHeight + "px"); - this.elContainer.style.height = nHeight + "px"; - - if(this.nLastHeight < nHeight){ - bExpand = true; - } + while (i--) { + oCss[aCopyStyle[i]] = this.welTextArea.css(aCopyStyle[i]); + } - this.wfnCallback(bExpand); - } + this.welHiddenDiv.css(oCss); + + this.nLastHeight = this.welTextArea.height(); + }, + bind: function () { + this.welCloneDiv = jindo.$Element(this.welHiddenDiv.$value().cloneNode(false)); + + this.wfnResize.attach(this.welTextArea, "keyup"); + this.welCloneDiv.appendTo(this.elContainer); + + this._resize(); + }, + unbind: function () { + this.wfnResize.detach(this.welTextArea, "keyup"); + this.welTextArea.css("overflow", this.sOverflow); + + if (this.welCloneDiv) { + this.welCloneDiv.leave(); + } + }, + _resize: function () { + var sContents = this.welTextArea.$value().value, + bExpand = false, + nHeight; + + if (sContents === this.sContents) { + return; + } + + this.sContents = sContents.replace(/&/g, '&').replace(//g, '>').replace(/ /g, ' ').replace(/\n/g, '
    '); + this.sContents += "
    "; // 마지막 개행 뒤에
    을 더 붙여주어야 늘어나는 높이가 동일함 + + this.welCloneDiv.html(this.sContents); + nHeight = this.welCloneDiv.height(); + + if (nHeight < this.nMinHeight) { + nHeight = this.nMinHeight; + } + + this.welTextArea.css("height", nHeight + "px"); + this.elContainer.style.height = nHeight + "px"; + + if (this.nLastHeight < nHeight) { + bExpand = true; + } + + this.wfnCallback(bExpand); + } }); /** @@ -15706,32 +15710,32 @@ nhn.husky.AutoResizer = jindo.$Class({ var sTmp3 = new StringBuffer('1').append('2').append('3'); */ -if ('undefined' != typeof(StringBuffer)) { - StringBuffer = {}; +if ('undefined' != typeof (StringBuffer)) { + StringBuffer = {}; } -StringBuffer = function(str) { - this._aString = []; - if ('undefined' != typeof(str)) { - this.append(str); - } +StringBuffer = function (str) { + this._aString = []; + if ('undefined' != typeof (str)) { + this.append(str); + } }; -StringBuffer.prototype.append = function(str) { - this._aString.push(str); - return this; +StringBuffer.prototype.append = function (str) { + this._aString.push(str); + return this; }; -StringBuffer.prototype.toString = function() { - return this._aString.join(''); +StringBuffer.prototype.toString = function () { + return this._aString.join(''); }; -StringBuffer.prototype.setLength = function(nLen) { - if('undefined' == typeof(nLen) || 0 >= nLen) { - this._aString.length = 0; - } else { - this._aString.length = nLen; - } +StringBuffer.prototype.setLength = function (nLen) { + if ('undefined' == typeof (nLen) || 0 >= nLen) { + this._aString.length = 0; + } else { + this._aString.length = nLen; + } }; /** @@ -15741,39 +15745,39 @@ StringBuffer.prototype.setLength = function(nLen) { * @see http://remysharp.com/2008/07/08/how-to-detect-if-a-font-is-installed-only-using-javascript/ */ -(function() { +(function () { - var oDummy = null, rx = /,/gi; + var oDummy = null, rx = /,/gi; - IsInstalledFont = function(sFont) { + IsInstalledFont = function (sFont) { - var sDefFont = sFont == 'Comic Sans MS' ? 'Courier New' : 'Comic Sans MS'; - if (!oDummy) { - oDummy = document.createElement('div'); - } - - var sStyle = 'position:absolute !important; font-size:200px !important; left:-9999px !important; top:-9999px !important;'; - oDummy.innerHTML = 'mmmmiiiii'+unescape('%uD55C%uAE00'); - oDummy.style.cssText = sStyle + 'font-family:"' + sDefFont + '" !important'; - - var elBody = document.body || document.documentElement; - if(elBody.firstChild){ - elBody.insertBefore(oDummy, elBody.firstChild); - }else{ - document.body.appendChild(oDummy); - } - - var sOrg = oDummy.offsetWidth + '-' + oDummy.offsetHeight; + var sDefFont = sFont == 'Comic Sans MS' ? 'Courier New' : 'Comic Sans MS'; + if (!oDummy) { + oDummy = document.createElement('div'); + } - oDummy.style.cssText = sStyle + 'font-family:"' + sFont.replace(rx, '","') + '", "' + sDefFont + '" !important'; + var sStyle = 'position:absolute !important; font-size:200px !important; left:-9999px !important; top:-9999px !important;'; + oDummy.innerHTML = 'mmmmiiiii' + unescape('%uD55C%uAE00'); + oDummy.style.cssText = sStyle + 'font-family:"' + sDefFont + '" !important'; - var bInstalled = sOrg != (oDummy.offsetWidth + '-' + oDummy.offsetHeight); - - document.body.removeChild(oDummy); - - return bInstalled; - - }; + var elBody = document.body || document.documentElement; + if (elBody.firstChild) { + elBody.insertBefore(oDummy, elBody.firstChild); + } else { + document.body.appendChild(oDummy); + } + + var sOrg = oDummy.offsetWidth + '-' + oDummy.offsetHeight; + + oDummy.style.cssText = sStyle + 'font-family:"' + sFont.replace(rx, '","') + '", "' + sDefFont + '" !important'; + + var bInstalled = sOrg != (oDummy.offsetWidth + '-' + oDummy.offsetHeight); + + document.body.removeChild(oDummy); + + return bInstalled; + + }; })(); //{ @@ -15782,209 +15786,209 @@ StringBuffer.prototype.setLength = function(nLen) { * @name hp_SE2B_CSSLoader.js */ nhn.husky.SE2B_CSSLoader = jindo.$Class({ - name : "SE2B_CSSLoader", - bCssLoaded : false, - - // load & continue with the message right away. - aInstantLoadTrigger : ["OPEN_QE_LAYER", "SHOW_ACTIVE_LAYER", "SHOW_DIALOG_LAYER", "START_SPELLCHECK"], - // if a rendering bug occurs in IE, give some delay before continue processing the message. - aDelayedLoadTrigger : ["MSG_SE_OBJECT_EDIT_REQUESTED", "OBJECT_MODIFY", "MSG_SE_DUMMY_OBJECT_EDIT_REQUESTED", "TOGGLE_TOOLBAR_ACTIVE_LAYER", "SHOW_TOOLBAR_ACTIVE_LAYER"], + name: "SE2B_CSSLoader", + bCssLoaded: false, - $init : function(){ - this.htOptions = nhn.husky.SE2M_Configuration.SE2B_CSSLoader; - - // only IE's slow - if(!jindo.$Agent().navigator().ie){ - this.$ON_MSG_APP_READY = jindo.$Fn(function(){ - this.loadSE2CSS(); - }, this).bind() - }else{ - for(var i=0, nLen = this.aInstantLoadTrigger.length; i= 33 && oKeyInfo.keyCode <= 40){ - this.oApp.exec("MSG_EDITING_AREA_RESIZE_STARTED", []); - this.oApp.exec("RESIZE_EDITING_AREA_BY", [0, this.aHeightChangeKeyMap[oKeyInfo.keyCode-33]]); - this.oApp.exec("MSG_EDITING_AREA_RESIZE_ENDED", []); + this.elIFrame = window.frameElement; + this.welIFrame = $Element(this.elIFrame); + }, - oEvent.stop(); - } - }, - - _mousedown : function(oEvent){ - this.iStartHeight = oEvent.pos().clientY; - this.iStartHeightOffset = oEvent.pos().layerY; + $ON_MSG_APP_READY: function () { + this.oApp.exec("SE_FIT_IFRAME", []); + }, - this.$FnMouseMove.attach(document, "mousemove"); - this.$FnMouseMove_Parent.attach(parent.document, "mousemove"); - - this.$FnMouseUp.attach(document, "mouseup"); - this.$FnMouseUp.attach(parent.document, "mouseup"); + $ON_MSG_EDITING_AREA_SIZE_CHANGED: function () { + this.oApp.exec("SE_FIT_IFRAME", []); + }, - this.iStartHeight = oEvent.pos().clientY; - this.oApp.exec("MSG_EDITING_AREA_RESIZE_STARTED", [this.$FnMouseDown, this.$FnMouseMove, this.$FnMouseUp]); - }, + $ON_SE_FIT_IFRAME: function () { + this.elIFrame.style.height = document.body.offsetHeight + "px"; + }, - _mousemove : function(oEvent){ - var iHeightChange = oEvent.pos().clientY - this.iStartHeight; - this.oApp.exec("RESIZE_EDITING_AREA_BY", [0, iHeightChange]); - }, + $AFTER_RESIZE_EDITING_AREA_BY: function (ipWidthChange, ipHeightChange) { + this.oApp.exec("SE_FIT_IFRAME", []); + }, - _mousemove_parent : function(oEvent){ - var iHeightChange = oEvent.pos().pageY - (this.welIFrame.offset().top + this.iStartHeight); - this.oApp.exec("RESIZE_EDITING_AREA_BY", [0, iHeightChange]); - }, + _keydown: function (oEvent) { + var oKeyInfo = oEvent.key(); - _mouseup : function(oEvent){ - this.$FnMouseMove.detach(document, "mousemove"); - this.$FnMouseMove_Parent.detach(parent.document, "mousemove"); - this.$FnMouseUp.detach(document, "mouseup"); - this.$FnMouseUp.detach(parent.document, "mouseup"); + // 33, 34: page up/down, 35,36: end/home, 37,38,39,40: left, up, right, down + if (oKeyInfo.keyCode >= 33 && oKeyInfo.keyCode <= 40) { + this.oApp.exec("MSG_EDITING_AREA_RESIZE_STARTED", []); + this.oApp.exec("RESIZE_EDITING_AREA_BY", [0, this.aHeightChangeKeyMap[oKeyInfo.keyCode - 33]]); + this.oApp.exec("MSG_EDITING_AREA_RESIZE_ENDED", []); - this.oApp.exec("MSG_EDITING_AREA_RESIZE_ENDED", [this.$FnMouseDown, this.$FnMouseMove, this.$FnMouseUp]); - } + oEvent.stop(); + } + }, + + _mousedown: function (oEvent) { + this.iStartHeight = oEvent.pos().clientY; + this.iStartHeightOffset = oEvent.pos().layerY; + + this.$FnMouseMove.attach(document, "mousemove"); + this.$FnMouseMove_Parent.attach(parent.document, "mousemove"); + + this.$FnMouseUp.attach(document, "mouseup"); + this.$FnMouseUp.attach(parent.document, "mouseup"); + + this.iStartHeight = oEvent.pos().clientY; + this.oApp.exec("MSG_EDITING_AREA_RESIZE_STARTED", [this.$FnMouseDown, this.$FnMouseMove, this.$FnMouseUp]); + }, + + _mousemove: function (oEvent) { + var iHeightChange = oEvent.pos().clientY - this.iStartHeight; + this.oApp.exec("RESIZE_EDITING_AREA_BY", [0, iHeightChange]); + }, + + _mousemove_parent: function (oEvent) { + var iHeightChange = oEvent.pos().pageY - (this.welIFrame.offset().top + this.iStartHeight); + this.oApp.exec("RESIZE_EDITING_AREA_BY", [0, iHeightChange]); + }, + + _mouseup: function (oEvent) { + this.$FnMouseMove.detach(document, "mousemove"); + this.$FnMouseMove_Parent.detach(parent.document, "mousemove"); + this.$FnMouseUp.detach(document, "mouseup"); + this.$FnMouseUp.detach(parent.document, "mouseup"); + + this.oApp.exec("MSG_EDITING_AREA_RESIZE_ENDED", [this.$FnMouseDown, this.$FnMouseMove, this.$FnMouseUp]); + } }); // Sample plugin. Use CTRL+T to toggle the toolbar nhn.husky.SE_ToolbarToggler = $Class({ - name : "SE_ToolbarToggler", - bUseToolbar : true, - - $init : function(oAppContainer, bUseToolbar){ - this._assignHTMLObjects(oAppContainer, bUseToolbar); - }, + name: "SE_ToolbarToggler", + bUseToolbar: true, - _assignHTMLObjects : function(oAppContainer, bUseToolbar){ - oAppContainer = jindo.$(oAppContainer) || document; - - this.toolbarArea = cssquery.getSingle(".se2_tool", oAppContainer); - - //설정이 없거나, 사용하겠다고 표시한 경우 block 처리 - if( typeof(bUseToolbar) == 'undefined' || bUseToolbar === true){ - this.toolbarArea.style.display = "block"; - }else{ - this.toolbarArea.style.display = "none"; - } - }, - - $ON_MSG_APP_READY : function(){ - this.oApp.exec("REGISTER_HOTKEY", ["ctrl+t", "SE_TOGGLE_TOOLBAR", []]); - }, - - $ON_SE_TOGGLE_TOOLBAR : function(){ - this.toolbarArea.style.display = (this.toolbarArea.style.display == "none")?"block":"none"; - this.oApp.exec("MSG_EDITING_AREA_SIZE_CHANGED", []); - } + $init: function (oAppContainer, bUseToolbar) { + this._assignHTMLObjects(oAppContainer, bUseToolbar); + }, + + _assignHTMLObjects: function (oAppContainer, bUseToolbar) { + oAppContainer = jindo.$(oAppContainer) || document; + + this.toolbarArea = cssquery.getSingle(".se2_tool", oAppContainer); + + //설정이 없거나, 사용하겠다고 표시한 경우 block 처리 + if (typeof (bUseToolbar) == 'undefined' || bUseToolbar === true) { + this.toolbarArea.style.display = "block"; + } else { + this.toolbarArea.style.display = "none"; + } + }, + + $ON_MSG_APP_READY: function () { + this.oApp.exec("REGISTER_HOTKEY", ["ctrl+t", "SE_TOGGLE_TOOLBAR", []]); + }, + + $ON_SE_TOGGLE_TOOLBAR: function () { + this.toolbarArea.style.display = (this.toolbarArea.style.display == "none") ? "block" : "none"; + this.oApp.exec("MSG_EDITING_AREA_SIZE_CHANGED", []); + } }); nhn.husky.HuskyCore.addLoadedFile("hp_SE2M_FindReplacePlugin$Lazy.js"); @@ -16136,261 +16140,261 @@ nhn.husky.HuskyCore.addLoadedFile("hp_SE2M_FindReplacePlugin$Lazy.js"); * this.oApp.registerLazyMessage(["TOGGLE_FIND_REPLACE_LAYER","SHOW_FIND_LAYER","SHOW_REPLACE_LAYER","SHOW_FIND_REPLACE_LAYER"], ["hp_SE2M_FindReplacePlugin$Lazy.js","N_FindReplace.js"]); */ nhn.husky.HuskyCore.mixin(nhn.husky.SE2M_FindReplacePlugin, { - //@lazyload_js TOGGLE_FIND_REPLACE_LAYER,SHOW_FIND_LAYER,SHOW_REPLACE_LAYER,SHOW_FIND_REPLACE_LAYER:N_FindReplace.js[ - _assignHTMLElements : function(){ - var oAppContainer = this.oApp.htOptions.elAppContainer; + //@lazyload_js TOGGLE_FIND_REPLACE_LAYER,SHOW_FIND_LAYER,SHOW_REPLACE_LAYER,SHOW_FIND_REPLACE_LAYER:N_FindReplace.js[ + _assignHTMLElements: function () { + var oAppContainer = this.oApp.htOptions.elAppContainer; - this.oApp.exec("LOAD_HTML", ["find_and_replace"]); -// this.oEditingWindow = jindo.$$.getSingle("IFRAME", oAppContainer); - this.elDropdownLayer = jindo.$$.getSingle("DIV.husky_se2m_findAndReplace_layer", oAppContainer); - this.welDropdownLayer = jindo.$Element(this.elDropdownLayer); - var oTmp = jindo.$$("LI", this.elDropdownLayer); - - this.oFindTab = oTmp[0]; - this.oReplaceTab = oTmp[1]; - - oTmp = jindo.$$(".container > .bx", this.elDropdownLayer); + this.oApp.exec("LOAD_HTML", ["find_and_replace"]); + // this.oEditingWindow = jindo.$$.getSingle("IFRAME", oAppContainer); + this.elDropdownLayer = jindo.$$.getSingle("DIV.husky_se2m_findAndReplace_layer", oAppContainer); + this.welDropdownLayer = jindo.$Element(this.elDropdownLayer); + var oTmp = jindo.$$("LI", this.elDropdownLayer); - this.oFindInputSet = jindo.$$.getSingle(".husky_se2m_find_ui", this.elDropdownLayer); - this.oReplaceInputSet = jindo.$$.getSingle(".husky_se2m_replace_ui", this.elDropdownLayer); - - this.elTitle = jindo.$$.getSingle("H3", this.elDropdownLayer); + this.oFindTab = oTmp[0]; + this.oReplaceTab = oTmp[1]; - this.oFindInput_Keyword = jindo.$$.getSingle("INPUT", this.oFindInputSet); + oTmp = jindo.$$(".container > .bx", this.elDropdownLayer); - oTmp = jindo.$$("INPUT", this.oReplaceInputSet); - this.oReplaceInput_Original = oTmp[0]; - this.oReplaceInput_Replacement = oTmp[1]; + this.oFindInputSet = jindo.$$.getSingle(".husky_se2m_find_ui", this.elDropdownLayer); + this.oReplaceInputSet = jindo.$$.getSingle(".husky_se2m_replace_ui", this.elDropdownLayer); - this.oFindNextButton = jindo.$$.getSingle("BUTTON.husky_se2m_find_next", this.elDropdownLayer); + this.elTitle = jindo.$$.getSingle("H3", this.elDropdownLayer); - this.oReplaceFindNextButton = jindo.$$.getSingle("BUTTON.husky_se2m_replace_find_next", this.elDropdownLayer); + this.oFindInput_Keyword = jindo.$$.getSingle("INPUT", this.oFindInputSet); - this.oReplaceButton = jindo.$$.getSingle("BUTTON.husky_se2m_replace", this.elDropdownLayer); - this.oReplaceAllButton = jindo.$$.getSingle("BUTTON.husky_se2m_replace_all", this.elDropdownLayer); - - this.aCloseButtons = jindo.$$("BUTTON.husky_se2m_cancel", this.elDropdownLayer); - }, + oTmp = jindo.$$("INPUT", this.oReplaceInputSet); + this.oReplaceInput_Original = oTmp[0]; + this.oReplaceInput_Replacement = oTmp[1]; - $LOCAL_BEFORE_FIRST : function(sMsg){ - this._assignHTMLElements(); + this.oFindNextButton = jindo.$$.getSingle("BUTTON.husky_se2m_find_next", this.elDropdownLayer); - this.oFindReplace = new nhn.FindReplace(this.oEditingWindow); + this.oReplaceFindNextButton = jindo.$$.getSingle("BUTTON.husky_se2m_replace_find_next", this.elDropdownLayer); - for(var i=0; i= this.nMaxLevel){ - return nMaxCount; - } - } - - elChild = elChild.nextSibling; - } - - return nMaxCount; - }; - - return (countChildQuote(elNode) >= this.nMaxLevel); - }, - - _unwrapBlock : function(tag){ - var oSelection = this.oApp.getSelection(); - var elCommonAncestor = oSelection.commonAncestorContainer; + var sClass = elButton.className; + this.oApp.exec("APPLY_BLOCKQUOTE", [sClass]); + }, - while(elCommonAncestor && elCommonAncestor.tagName != tag){elCommonAncestor = elCommonAncestor.parentNode;} - if(!elCommonAncestor){return;} + $ON_APPLY_BLOCKQUOTE: function (sClass) { + if (sClass.match(/(se2_quote[0-9]+)/)) { + this._wrapBlock("BLOCKQUOTE", RegExp.$1); + } else { + this._unwrapBlock("BLOCKQUOTE"); + } - this.oApp.exec("RECORD_UNDO_BEFORE_ACTION", ["CANCEL BLOCK QUOTE", {sSaveTarget:"BODY"}]); + this.oApp.exec("HIDE_ACTIVE_LAYER", []); + }, - // [SMARTEDITORSUS-1782] 인용구가 제거되기 전에 선택 영역안에 있는 마지막 텍스트노드를 미리 찾아둔다. - var oLastTextNode = oSelection.commonAncestorContainer; - if(oLastTextNode.nodeType !== 3){ // 텍스트노드가 아니면 - var aTextNodesInRange = oSelection.getTextNodes() || "", - nLastIndex = aTextNodesInRange.length - 1; - oLastTextNode = (nLastIndex > -1) ? aTextNodesInRange[nLastIndex] : null; - } + /** + * 인용구의 중첩 가능한 최대 개수를 넘었는지 확인함 + * 인용구 내부에서 인용구를 적용하면 중첩되지 않으므로 자식노드에 대해서만 확인함 + */ + _isExceedMaxDepth: function (elNode) { + var countChildQuote = function (elNode) { + var elChild = elNode.firstChild; + var nCount = 0; + var nMaxCount = 0; - // 인용구내의 요소들을 바깥으로 모두 꺼낸 후 인용구요소를 제거 - while(elCommonAncestor.firstChild){elCommonAncestor.parentNode.insertBefore(elCommonAncestor.firstChild, elCommonAncestor);} - elCommonAncestor.parentNode.removeChild(elCommonAncestor); + if (!elChild) { + if (elNode.tagName && elNode.tagName === "BLOCKQUOTE") { + return 1; + } else { + return 0; + } + } - // [SMARTEDITORSUS-1782] 찾아둔 마지막 텍스트노드 끝으로 커서를 이동시킨다. - if(oLastTextNode){ - oSelection.selectNodeContents(oLastTextNode); - oSelection.collapseToEnd(); - oSelection.select(); - } - - this.oApp.exec("RECORD_UNDO_AFTER_ACTION", ["CANCEL BLOCK QUOTE", {sSaveTarget:"BODY"}]); - }, - - _wrapBlock : function(tag, className){ - var oSelection, - oLineInfo, - oStart, oEnd, - rxDontUseAsWhole = /BODY|TD|LI/i, - oStartNode, oEndNode, oNode, - elCommonAncestor, - elCommonNode, - elParentQuote, - elInsertBefore, - oFormattingNode, - elNextNode, - elParentNode, - aQuoteChild, - aQuoteCloneChild, - i, nLen, oP, - sBookmarkID; - - this.oApp.exec("RECORD_UNDO_BEFORE_ACTION", ["BLOCK QUOTE", {sSaveTarget:"BODY"}]); - oSelection = this.oApp.getSelection(); -// var sBookmarkID = oSelection.placeStringBookmark(); + while (elChild) { + if (elChild.nodeType === 1) { + nCount = countChildQuote(elChild); - // [SMARTEDITORSUS-430] 문자를 입력하고 Enter 후 인용구를 적용할 때 위의 문자들이 인용구 안에 들어가는 문제 - // [SMARTEDITORSUS-1323] 사진 첨부 후 인용구 적용 시 첨부한 사진이 삭제되는 현상 - if(oSelection.startContainer === oSelection.endContainer && - oSelection.startContainer.nodeType === 1 && - oSelection.startContainer.tagName === "P"){ - if(nhn.husky.SE2M_Utils.isBlankNode(oSelection.startContainer) || - nhn.husky.SE2M_Utils.isFirstChildOfNode("IMG", oSelection.startContainer.tagName, oSelection.startContainer) || - nhn.husky.SE2M_Utils.isFirstChildOfNode("IFRAME", oSelection.startContainer.tagName, oSelection.startContainer)){ - oLineInfo = oSelection.getLineInfo(true); - }else{ - oLineInfo = oSelection.getLineInfo(false); - } - }else{ - oLineInfo = oSelection.getLineInfo(false); - } - - oStart = oLineInfo.oStart; - oEnd = oLineInfo.oEnd; - - if(oStart.bParentBreak && !rxDontUseAsWhole.test(oStart.oLineBreaker.tagName)){ - oStartNode = oStart.oNode.parentNode; - }else{ - oStartNode = oStart.oNode; - } + if (elChild.tagName === "BLOCKQUOTE") { + nCount += 1; + } - if(oEnd.bParentBreak && !rxDontUseAsWhole.test(oEnd.oLineBreaker.tagName)){ - oEndNode = oEnd.oNode.parentNode; - }else{ - oEndNode = oEnd.oNode; - } + if (nMaxCount < nCount) { + nMaxCount = nCount; + } - oSelection.setStartBefore(oStartNode); - oSelection.setEndAfter(oEndNode); + if (nMaxCount >= this.nMaxLevel) { + return nMaxCount; + } + } - oNode = this._expandToTableStart(oSelection, oEndNode); - if(oNode){ - oEndNode = oNode; - oSelection.setEndAfter(oNode); - } + elChild = elChild.nextSibling; + } - oNode = this._expandToTableStart(oSelection, oStartNode); - if(oNode){ - oStartNode = oNode; - oSelection.setStartBefore(oNode); - } + return nMaxCount; + }; - oNode = oStartNode; - // IE에서는 commonAncestorContainer 자체는 select 가능하지 않고, 하위에 commonAncestorContainer를 대체 하더라도 똑같은 영역이 셀렉트 되어 보이는 - // 노드가 있을 경우 하위 노드가 commonAncestorContainer로 반환됨. - // 그래서, 스크립트로 commonAncestorContainer 계산 하도록 함. - // 예) - //

    TEST

    를 선택 할 경우, TEST가 commonAncestorContainer로 잡힘 - oSelection.fixCommonAncestorContainer(); - elCommonAncestor = oSelection.commonAncestorContainer; + return (countChildQuote(elNode) >= this.nMaxLevel); + }, - if(oSelection.startContainer == oSelection.endContainer && oSelection.endOffset-oSelection.startOffset == 1){ - elCommonNode = oSelection.startContainer.childNodes[oSelection.startOffset]; - }else{ - elCommonNode = oSelection.commonAncestorContainer; - } - - elParentQuote = this._findParentQuote(elCommonNode); + _unwrapBlock: function (tag) { + var oSelection = this.oApp.getSelection(); + var elCommonAncestor = oSelection.commonAncestorContainer; - if(elParentQuote){ - elParentQuote.className = className; - - // [SMARTEDITORSUS-1239] blockquote 태그교체시 style 적용 - this._setStyle(elParentQuote, this.htQuoteStyles_view[className]); - // --[SMARTEDITORSUS-1239] - return; - } + while (elCommonAncestor && elCommonAncestor.tagName != tag) { elCommonAncestor = elCommonAncestor.parentNode; } + if (!elCommonAncestor) { return; } - while(!elCommonAncestor.tagName || (elCommonAncestor.tagName && elCommonAncestor.tagName.match(/UL|OL|LI|IMG|IFRAME/))){ - elCommonAncestor = elCommonAncestor.parentNode; - } + this.oApp.exec("RECORD_UNDO_BEFORE_ACTION", ["CANCEL BLOCK QUOTE", { sSaveTarget: "BODY" }]); - // find the insertion position for the formatting tag right beneath the common ancestor container - while(oNode && oNode != elCommonAncestor && oNode.parentNode != elCommonAncestor){oNode = oNode.parentNode;} + // [SMARTEDITORSUS-1782] 인용구가 제거되기 전에 선택 영역안에 있는 마지막 텍스트노드를 미리 찾아둔다. + var oLastTextNode = oSelection.commonAncestorContainer; + if (oLastTextNode.nodeType !== 3) { // 텍스트노드가 아니면 + var aTextNodesInRange = oSelection.getTextNodes() || "", + nLastIndex = aTextNodesInRange.length - 1; + oLastTextNode = (nLastIndex > -1) ? aTextNodesInRange[nLastIndex] : null; + } - if(oNode == elCommonAncestor){ - elInsertBefore = elCommonAncestor.firstChild; - }else{ - elInsertBefore = oNode; - } - - oFormattingNode = oSelection._document.createElement(tag); - if(className){ - oFormattingNode.className = className; - // [SMARTEDITORSUS-1239] 에디터에서 인용구 5개이상 상입 시 에디터를 뚫고 노출되는 현상 - // [SMARTEDITORSUS-1229] 인용구 여러 개 중첩하면 에디터 본문 영역을 벗어나는 현상 - // blockquate style 적용 - this._setStyle(oFormattingNode, this.htQuoteStyles_view[className]); - } + // 인용구내의 요소들을 바깥으로 모두 꺼낸 후 인용구요소를 제거 + while (elCommonAncestor.firstChild) { elCommonAncestor.parentNode.insertBefore(elCommonAncestor.firstChild, elCommonAncestor); } + elCommonAncestor.parentNode.removeChild(elCommonAncestor); - elCommonAncestor.insertBefore(oFormattingNode, elInsertBefore); + // [SMARTEDITORSUS-1782] 찾아둔 마지막 텍스트노드 끝으로 커서를 이동시킨다. + if (oLastTextNode) { + oSelection.selectNodeContents(oLastTextNode); + oSelection.collapseToEnd(); + oSelection.select(); + } - oSelection.setStartAfter(oFormattingNode); + this.oApp.exec("RECORD_UNDO_AFTER_ACTION", ["CANCEL BLOCK QUOTE", { sSaveTarget: "BODY" }]); + }, - oSelection.setEndAfter(oEndNode); - oSelection.surroundContents(oFormattingNode); - - if(this._isExceedMaxDepth(oFormattingNode)){ - alert(this.oApp.$MSG("SE2M_Quote.exceedMaxCount").replace("#MaxCount#", (this.nMaxLevel + 1))); - - this.oApp.exec("HIDE_ACTIVE_LAYER", []); - - elNextNode = oFormattingNode.nextSibling; - elParentNode = oFormattingNode.parentNode; - aQuoteChild = oFormattingNode.childNodes; - aQuoteCloneChild = []; - - jindo.$Element(oFormattingNode).leave(); - for(i = 0, nLen = aQuoteChild.length; i < nLen; i++){ - aQuoteCloneChild[i] = aQuoteChild[i]; - } - for(i = 0, nLen = aQuoteCloneChild.length; i < nLen; i++){ - if(!!elNextNode){ - jindo.$Element(elNextNode).before(aQuoteCloneChild[i]); - }else{ - jindo.$Element(elParentNode).append(aQuoteCloneChild[i]); - } - } - - return; - } + _wrapBlock: function (tag, className) { + var oSelection, + oLineInfo, + oStart, oEnd, + rxDontUseAsWhole = /BODY|TD|LI/i, + oStartNode, oEndNode, oNode, + elCommonAncestor, + elCommonNode, + elParentQuote, + elInsertBefore, + oFormattingNode, + elNextNode, + elParentNode, + aQuoteChild, + aQuoteCloneChild, + i, nLen, oP, + sBookmarkID; - oSelection.selectNodeContents(oFormattingNode); + this.oApp.exec("RECORD_UNDO_BEFORE_ACTION", ["BLOCK QUOTE", { sSaveTarget: "BODY" }]); + oSelection = this.oApp.getSelection(); + // var sBookmarkID = oSelection.placeStringBookmark(); - // insert an empty line below, so the text cursor can move there - if(oFormattingNode && oFormattingNode.parentNode && oFormattingNode.parentNode.tagName == "BODY" && !oFormattingNode.nextSibling){ - oP = oSelection._document.createElement("P"); - //oP.innerHTML = unescape("
    "); - oP.innerHTML = " "; - oFormattingNode.parentNode.insertBefore(oP, oFormattingNode.nextSibling); - } + // [SMARTEDITORSUS-430] 문자를 입력하고 Enter 후 인용구를 적용할 때 위의 문자들이 인용구 안에 들어가는 문제 + // [SMARTEDITORSUS-1323] 사진 첨부 후 인용구 적용 시 첨부한 사진이 삭제되는 현상 + if (oSelection.startContainer === oSelection.endContainer && + oSelection.startContainer.nodeType === 1 && + oSelection.startContainer.tagName === "P") { + if (nhn.husky.SE2M_Utils.isBlankNode(oSelection.startContainer) || + nhn.husky.SE2M_Utils.isFirstChildOfNode("IMG", oSelection.startContainer.tagName, oSelection.startContainer) || + nhn.husky.SE2M_Utils.isFirstChildOfNode("IFRAME", oSelection.startContainer.tagName, oSelection.startContainer)) { + oLineInfo = oSelection.getLineInfo(true); + } else { + oLineInfo = oSelection.getLineInfo(false); + } + } else { + oLineInfo = oSelection.getLineInfo(false); + } - // oSelection.removeStringBookmark(sBookmarkID); - // Insert an empty line inside the blockquote if it's empty. - // This is done to position the cursor correctly when the contents of the blockquote is empty in Chrome. - if(nhn.husky.SE2M_Utils.isBlankNode(oFormattingNode)){ - // [SMARTEDITORSUS-1751] 현재 undo/redo 기능을 사용하지 않고 ie7은 주요브라우저에서 제외되었기 때문에 다른 이슈들 처리시 복잡도를 줄이기 위해 코멘트처리함 - // [SMARTEDITORSUS-645] 편집영역 포커스 없이 인용구 추가했을 때 IE7에서 박스가 늘어나는 문제 - //oFormattingNode.innerHTML = " "; + oStart = oLineInfo.oStart; + oEnd = oLineInfo.oEnd; - // [SMARTEDITORSUS-1567] P 태그로 감싸주지 않으면 크롬에서 blockquote 태그에 정렬이 적용되는데 IR_TO_DB 컨버터에서 style을 리셋하고 있기 때문에 저장되는 시점에 정렬이 제거된다. - // [SMARTEDITORSUS-1229] 인용구 여러 개 중첩하면 에디터 본문 영역을 벗어나는 현상 - oFormattingNode.innerHTML = " "; - // [SMARTEDITORSUS-1741] 커서가 p태그 안으로 들어가도록 세팅 - oSelection.selectNodeContents(oFormattingNode.firstChild); - oSelection.collapseToStart(); - oSelection.select(); - } + if (oStart.bParentBreak && !rxDontUseAsWhole.test(oStart.oLineBreaker.tagName)) { + oStartNode = oStart.oNode.parentNode; + } else { + oStartNode = oStart.oNode; + } - //oSelection.select(); - setTimeout(jindo.$Fn(function(oSelection){ - sBookmarkID = oSelection.placeStringBookmark(); - - oSelection.select(); - oSelection.removeStringBookmark(sBookmarkID); - - this.oApp.exec("FOCUS"); // [SMARTEDITORSUS-469] [SMARTEDITORSUS-434] 에디터 로드 후 최초 삽입한 인용구 안에 포커스가 가지 않는 문제 - },this).bind(oSelection), 0); + if (oEnd.bParentBreak && !rxDontUseAsWhole.test(oEnd.oLineBreaker.tagName)) { + oEndNode = oEnd.oNode.parentNode; + } else { + oEndNode = oEnd.oNode; + } - this.oApp.exec("RECORD_UNDO_AFTER_ACTION", ["BLOCK QUOTE", {sSaveTarget:"BODY"}]); - - return oFormattingNode; - }, + oSelection.setStartBefore(oStartNode); + oSelection.setEndAfter(oEndNode); - _expandToTableStart : function(oSelection, oNode){ - var elCommonAncestor = oSelection.commonAncestorContainer; - var oResultNode = null; + oNode = this._expandToTableStart(oSelection, oEndNode); + if (oNode) { + oEndNode = oNode; + oSelection.setEndAfter(oNode); + } - var bLastIteration = false; - while(oNode && !bLastIteration){ - if(oNode == elCommonAncestor){bLastIteration = true;} + oNode = this._expandToTableStart(oSelection, oStartNode); + if (oNode) { + oStartNode = oNode; + oSelection.setStartBefore(oNode); + } - if(/TBODY|TFOOT|THEAD|TR/i.test(oNode.tagName)){ - oResultNode = this._getTableRoot(oNode); - break; - } - oNode = oNode.parentNode; - } - - return oResultNode; - }, - - _getTableRoot : function(oNode){ - while(oNode && oNode.tagName != "TABLE"){oNode = oNode.parentNode;} - - return oNode; - }, - - _setStyle : function(el, sStyle) { - el.setAttribute("style", sStyle); - el.style.cssText = sStyle; - } - //@lazyload_js] + oNode = oStartNode; + // IE에서는 commonAncestorContainer 자체는 select 가능하지 않고, 하위에 commonAncestorContainer를 대체 하더라도 똑같은 영역이 셀렉트 되어 보이는 + // 노드가 있을 경우 하위 노드가 commonAncestorContainer로 반환됨. + // 그래서, 스크립트로 commonAncestorContainer 계산 하도록 함. + // 예) + //

    TEST

    를 선택 할 경우, TEST가 commonAncestorContainer로 잡힘 + oSelection.fixCommonAncestorContainer(); + elCommonAncestor = oSelection.commonAncestorContainer; + + if (oSelection.startContainer == oSelection.endContainer && oSelection.endOffset - oSelection.startOffset == 1) { + elCommonNode = oSelection.startContainer.childNodes[oSelection.startOffset]; + } else { + elCommonNode = oSelection.commonAncestorContainer; + } + + elParentQuote = this._findParentQuote(elCommonNode); + + if (elParentQuote) { + elParentQuote.className = className; + + // [SMARTEDITORSUS-1239] blockquote 태그교체시 style 적용 + this._setStyle(elParentQuote, this.htQuoteStyles_view[className]); + // --[SMARTEDITORSUS-1239] + return; + } + + while (!elCommonAncestor.tagName || (elCommonAncestor.tagName && elCommonAncestor.tagName.match(/UL|OL|LI|IMG|IFRAME/))) { + elCommonAncestor = elCommonAncestor.parentNode; + } + + // find the insertion position for the formatting tag right beneath the common ancestor container + while (oNode && oNode != elCommonAncestor && oNode.parentNode != elCommonAncestor) { oNode = oNode.parentNode; } + + if (oNode == elCommonAncestor) { + elInsertBefore = elCommonAncestor.firstChild; + } else { + elInsertBefore = oNode; + } + + oFormattingNode = oSelection._document.createElement(tag); + if (className) { + oFormattingNode.className = className; + // [SMARTEDITORSUS-1239] 에디터에서 인용구 5개이상 상입 시 에디터를 뚫고 노출되는 현상 + // [SMARTEDITORSUS-1229] 인용구 여러 개 중첩하면 에디터 본문 영역을 벗어나는 현상 + // blockquate style 적용 + this._setStyle(oFormattingNode, this.htQuoteStyles_view[className]); + } + + elCommonAncestor.insertBefore(oFormattingNode, elInsertBefore); + + oSelection.setStartAfter(oFormattingNode); + + oSelection.setEndAfter(oEndNode); + oSelection.surroundContents(oFormattingNode); + + if (this._isExceedMaxDepth(oFormattingNode)) { + alert(this.oApp.$MSG("SE2M_Quote.exceedMaxCount").replace("#MaxCount#", (this.nMaxLevel + 1))); + + this.oApp.exec("HIDE_ACTIVE_LAYER", []); + + elNextNode = oFormattingNode.nextSibling; + elParentNode = oFormattingNode.parentNode; + aQuoteChild = oFormattingNode.childNodes; + aQuoteCloneChild = []; + + jindo.$Element(oFormattingNode).leave(); + for (i = 0, nLen = aQuoteChild.length; i < nLen; i++) { + aQuoteCloneChild[i] = aQuoteChild[i]; + } + for (i = 0, nLen = aQuoteCloneChild.length; i < nLen; i++) { + if (!!elNextNode) { + jindo.$Element(elNextNode).before(aQuoteCloneChild[i]); + } else { + jindo.$Element(elParentNode).append(aQuoteCloneChild[i]); + } + } + + return; + } + + oSelection.selectNodeContents(oFormattingNode); + + // insert an empty line below, so the text cursor can move there + if (oFormattingNode && oFormattingNode.parentNode && oFormattingNode.parentNode.tagName == "BODY" && !oFormattingNode.nextSibling) { + oP = oSelection._document.createElement("P"); + //oP.innerHTML = unescape("
    "); + oP.innerHTML = " "; + oFormattingNode.parentNode.insertBefore(oP, oFormattingNode.nextSibling); + } + + // oSelection.removeStringBookmark(sBookmarkID); + // Insert an empty line inside the blockquote if it's empty. + // This is done to position the cursor correctly when the contents of the blockquote is empty in Chrome. + if (nhn.husky.SE2M_Utils.isBlankNode(oFormattingNode)) { + // [SMARTEDITORSUS-1751] 현재 undo/redo 기능을 사용하지 않고 ie7은 주요브라우저에서 제외되었기 때문에 다른 이슈들 처리시 복잡도를 줄이기 위해 코멘트처리함 + // [SMARTEDITORSUS-645] 편집영역 포커스 없이 인용구 추가했을 때 IE7에서 박스가 늘어나는 문제 + //oFormattingNode.innerHTML = " "; + + // [SMARTEDITORSUS-1567] P 태그로 감싸주지 않으면 크롬에서 blockquote 태그에 정렬이 적용되는데 IR_TO_DB 컨버터에서 style을 리셋하고 있기 때문에 저장되는 시점에 정렬이 제거된다. + // [SMARTEDITORSUS-1229] 인용구 여러 개 중첩하면 에디터 본문 영역을 벗어나는 현상 + oFormattingNode.innerHTML = " "; + // [SMARTEDITORSUS-1741] 커서가 p태그 안으로 들어가도록 세팅 + oSelection.selectNodeContents(oFormattingNode.firstChild); + oSelection.collapseToStart(); + oSelection.select(); + } + + //oSelection.select(); + setTimeout(jindo.$Fn(function (oSelection) { + sBookmarkID = oSelection.placeStringBookmark(); + + oSelection.select(); + oSelection.removeStringBookmark(sBookmarkID); + + this.oApp.exec("FOCUS"); // [SMARTEDITORSUS-469] [SMARTEDITORSUS-434] 에디터 로드 후 최초 삽입한 인용구 안에 포커스가 가지 않는 문제 + }, this).bind(oSelection), 0); + + this.oApp.exec("RECORD_UNDO_AFTER_ACTION", ["BLOCK QUOTE", { sSaveTarget: "BODY" }]); + + return oFormattingNode; + }, + + _expandToTableStart: function (oSelection, oNode) { + var elCommonAncestor = oSelection.commonAncestorContainer; + var oResultNode = null; + + var bLastIteration = false; + while (oNode && !bLastIteration) { + if (oNode == elCommonAncestor) { bLastIteration = true; } + + if (/TBODY|TFOOT|THEAD|TR/i.test(oNode.tagName)) { + oResultNode = this._getTableRoot(oNode); + break; + } + oNode = oNode.parentNode; + } + + return oResultNode; + }, + + _getTableRoot: function (oNode) { + while (oNode && oNode.tagName != "TABLE") { oNode = oNode.parentNode; } + + return oNode; + }, + + _setStyle: function (el, sStyle) { + el.setAttribute("style", sStyle); + el.style.cssText = sStyle; + } + //@lazyload_js] }); nhn.husky.HuskyCore.addLoadedFile("hp_SE2M_SCharacter$Lazy.js"); /** @@ -16724,170 +16728,170 @@ nhn.husky.HuskyCore.addLoadedFile("hp_SE2M_SCharacter$Lazy.js"); * this.oApp.registerLazyMessage(["TOGGLE_SCHARACTER_LAYER"], ["hp_SE2M_SCharacter$Lazy.js"]); */ nhn.husky.HuskyCore.mixin(nhn.husky.SE2M_SCharacter, { - //@lazyload_js TOGGLE_SCHARACTER_LAYER[ - _assignHTMLObjects : function(oAppContainer){ - oAppContainer = jindo.$(oAppContainer) || document; + //@lazyload_js TOGGLE_SCHARACTER_LAYER[ + _assignHTMLObjects: function (oAppContainer) { + oAppContainer = jindo.$(oAppContainer) || document; - this.elDropdownLayer = jindo.$$.getSingle("DIV.husky_seditor_sCharacter_layer", oAppContainer); + this.elDropdownLayer = jindo.$$.getSingle("DIV.husky_seditor_sCharacter_layer", oAppContainer); - this.oTextField = jindo.$$.getSingle("INPUT", this.elDropdownLayer); - this.oInsertButton = jindo.$$.getSingle("BUTTON.se2_confirm", this.elDropdownLayer); - this.aCloseButton = jindo.$$("BUTTON.husky_se2m_sCharacter_close", this.elDropdownLayer); - this.aSCharList = jindo.$$("UL.husky_se2m_sCharacter_list", this.elDropdownLayer); - var oLabelUL = jindo.$$.getSingle("UL.se2_char_tab", this.elDropdownLayer); - this.aLabel = jindo.$$(">LI", oLabelUL); - }, - - $LOCAL_BEFORE_FIRST : function(sFullMsg){ - this.bIE = jindo.$Agent().navigator().ie; + this.oTextField = jindo.$$.getSingle("INPUT", this.elDropdownLayer); + this.oInsertButton = jindo.$$.getSingle("BUTTON.se2_confirm", this.elDropdownLayer); + this.aCloseButton = jindo.$$("BUTTON.husky_se2m_sCharacter_close", this.elDropdownLayer); + this.aSCharList = jindo.$$("UL.husky_se2m_sCharacter_list", this.elDropdownLayer); + var oLabelUL = jindo.$$.getSingle("UL.se2_char_tab", this.elDropdownLayer); + this.aLabel = jindo.$$(">LI", oLabelUL); + }, - this._assignHTMLObjects(this.oApp.htOptions.elAppContainer); + $LOCAL_BEFORE_FIRST: function (sFullMsg) { + this.bIE = jindo.$Agent().navigator().ie; - this.charSet = []; - this.charSet[0] = unescape('FF5B FF5D 3014 3015 3008 3009 300A 300B 300C 300D 300E 300F 3010 3011 2018 2019 201C 201D 3001 3002 %B7 2025 2026 %A7 203B 2606 2605 25CB 25CF 25CE 25C7 25C6 25A1 25A0 25B3 25B2 25BD 25BC 25C1 25C0 25B7 25B6 2664 2660 2661 2665 2667 2663 2299 25C8 25A3 25D0 25D1 2592 25A4 25A5 25A8 25A7 25A6 25A9 %B1 %D7 %F7 2260 2264 2265 221E 2234 %B0 2032 2033 2220 22A5 2312 2202 2261 2252 226A 226B 221A 223D 221D 2235 222B 222C 2208 220B 2286 2287 2282 2283 222A 2229 2227 2228 FFE2 21D2 21D4 2200 2203 %B4 FF5E 02C7 02D8 02DD 02DA 02D9 %B8 02DB %A1 %BF 02D0 222E 2211 220F 266D 2669 266A 266C 327F 2192 2190 2191 2193 2194 2195 2197 2199 2196 2198 321C 2116 33C7 2122 33C2 33D8 2121 2668 260F 260E 261C 261E %B6 2020 2021 %AE %AA %BA 2642 2640').replace(/(\S{4})/g, function(a){return "%u"+a;}).split(' '); - this.charSet[1] = unescape('%BD 2153 2154 %BC %BE 215B 215C 215D 215E %B9 %B2 %B3 2074 207F 2081 2082 2083 2084 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 FFE6 %24 FFE5 FFE1 20AC 2103 212B 2109 FFE0 %A4 2030 3395 3396 3397 2113 3398 33C4 33A3 33A4 33A5 33A6 3399 339A 339B 339C 339D 339E 339F 33A0 33A1 33A2 33CA 338D 338E 338F 33CF 3388 3389 33C8 33A7 33A8 33B0 33B1 33B2 33B3 33B4 33B5 33B6 33B7 33B8 33B9 3380 3381 3382 3383 3384 33BA 33BB 33BC 33BD 33BE 33BF 3390 3391 3392 3393 3394 2126 33C0 33C1 338A 338B 338C 33D6 33C5 33AD 33AE 33AF 33DB 33A9 33AA 33AB 33AC 33DD 33D0 33D3 33C3 33C9 33DC 33C6').replace(/(\S{4})/g, function(a){return "%u"+a;}).split(' '); - this.charSet[2] = unescape('3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 326A 326B 326C 326D 326E 326F 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 327A 327B 24D0 24D1 24D2 24D3 24D4 24D5 24D6 24D7 24D8 24D9 24DA 24DB 24DC 24DD 24DE 24DF 24E0 24E1 24E2 24E3 24E4 24E5 24E6 24E7 24E8 24E9 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 246A 246B 246C 246D 246E 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 320A 320B 320C 320D 320E 320F 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 321A 321B 249C 249D 249E 249F 24A0 24A1 24A2 24A3 24A4 24A5 24A6 24A7 24A8 24A9 24AA 24AB 24AC 24AD 24AE 24AF 24B0 24B1 24B2 24B3 24B4 24B5 2474 2475 2476 2477 2478 2479 247A 247B 247C 247D 247E 247F 2480 2481 2482').replace(/(\S{4})/g, function(a){return "%u"+a;}).split(' '); - this.charSet[3] = unescape('3131 3132 3133 3134 3135 3136 3137 3138 3139 313A 313B 313C 313D 313E 313F 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 314A 314B 314C 314D 314E 314F 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 315A 315B 315C 315D 315E 315F 3160 3161 3162 3163 3165 3166 3167 3168 3169 316A 316B 316C 316D 316E 316F 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 317A 317B 317C 317D 317E 317F 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 318A 318B 318C 318D 318E').replace(/(\S{4})/g, function(a){return "%u"+a;}).split(' '); - this.charSet[4] = unescape('0391 0392 0393 0394 0395 0396 0397 0398 0399 039A 039B 039C 039D 039E 039F 03A0 03A1 03A3 03A4 03A5 03A6 03A7 03A8 03A9 03B1 03B2 03B3 03B4 03B5 03B6 03B7 03B8 03B9 03BA 03BB 03BC 03BD 03BE 03BF 03C0 03C1 03C3 03C4 03C5 03C6 03C7 03C8 03C9 %C6 %D0 0126 0132 013F 0141 %D8 0152 %DE 0166 014A %E6 0111 %F0 0127 I 0133 0138 0140 0142 0142 0153 %DF %FE 0167 014B 0149 0411 0413 0414 0401 0416 0417 0418 0419 041B 041F 0426 0427 0428 0429 042A 042B 042C 042D 042E 042F 0431 0432 0433 0434 0451 0436 0437 0438 0439 043B 043F 0444 0446 0447 0448 0449 044A 044B 044C 044D 044E 044F').replace(/(\S{4})/g, function(a){return "%u"+a;}).split(' '); - this.charSet[5] = unescape('3041 3042 3043 3044 3045 3046 3047 3048 3049 304A 304B 304C 304D 304E 304F 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 305A 305B 305C 305D 305E 305F 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 306A 306B 306C 306D 306E 306F 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 307A 307B 307C 307D 307E 307F 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 308A 308B 308C 308D 308E 308F 3090 3091 3092 3093 30A1 30A2 30A3 30A4 30A5 30A6 30A7 30A8 30A9 30AA 30AB 30AC 30AD 30AE 30AF 30B0 30B1 30B2 30B3 30B4 30B5 30B6 30B7 30B8 30B9 30BA 30BB 30BC 30BD 30BE 30BF 30C0 30C1 30C2 30C3 30C4 30C5 30C6 30C7 30C8 30C9 30CA 30CB 30CC 30CD 30CE 30CF 30D0 30D1 30D2 30D3 30D4 30D5 30D6 30D7 30D8 30D9 30DA 30DB 30DC 30DD 30DE 30DF 30E0 30E1 30E2 30E3 30E4 30E5 30E6 30E7 30E8 30E9 30EA 30EB 30EC 30ED 30EE 30EF 30F0 30F1 30F2 30F3 30F4 30F5 30F6').replace(/(\S{4})/g, function(a){return "%u"+a;}).split(' '); - - var funcInsert = jindo.$Fn(this.oApp.exec, this.oApp).bind("INSERT_SCHARACTERS", [this.oTextField.value]); - jindo.$Fn(funcInsert, this).attach(this.oInsertButton, "click"); + this._assignHTMLObjects(this.oApp.htOptions.elAppContainer); - this.oApp.exec("SET_SCHARACTER_LIST", [this.charSet]); + this.charSet = []; + this.charSet[0] = unescape('FF5B FF5D 3014 3015 3008 3009 300A 300B 300C 300D 300E 300F 3010 3011 2018 2019 201C 201D 3001 3002 %B7 2025 2026 %A7 203B 2606 2605 25CB 25CF 25CE 25C7 25C6 25A1 25A0 25B3 25B2 25BD 25BC 25C1 25C0 25B7 25B6 2664 2660 2661 2665 2667 2663 2299 25C8 25A3 25D0 25D1 2592 25A4 25A5 25A8 25A7 25A6 25A9 %B1 %D7 %F7 2260 2264 2265 221E 2234 %B0 2032 2033 2220 22A5 2312 2202 2261 2252 226A 226B 221A 223D 221D 2235 222B 222C 2208 220B 2286 2287 2282 2283 222A 2229 2227 2228 FFE2 21D2 21D4 2200 2203 %B4 FF5E 02C7 02D8 02DD 02DA 02D9 %B8 02DB %A1 %BF 02D0 222E 2211 220F 266D 2669 266A 266C 327F 2192 2190 2191 2193 2194 2195 2197 2199 2196 2198 321C 2116 33C7 2122 33C2 33D8 2121 2668 260F 260E 261C 261E %B6 2020 2021 %AE %AA %BA 2642 2640').replace(/(\S{4})/g, function (a) { return "%u" + a; }).split(' '); + this.charSet[1] = unescape('%BD 2153 2154 %BC %BE 215B 215C 215D 215E %B9 %B2 %B3 2074 207F 2081 2082 2083 2084 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 2171 2172 2173 2174 2175 2176 2177 2178 2179 FFE6 %24 FFE5 FFE1 20AC 2103 212B 2109 FFE0 %A4 2030 3395 3396 3397 2113 3398 33C4 33A3 33A4 33A5 33A6 3399 339A 339B 339C 339D 339E 339F 33A0 33A1 33A2 33CA 338D 338E 338F 33CF 3388 3389 33C8 33A7 33A8 33B0 33B1 33B2 33B3 33B4 33B5 33B6 33B7 33B8 33B9 3380 3381 3382 3383 3384 33BA 33BB 33BC 33BD 33BE 33BF 3390 3391 3392 3393 3394 2126 33C0 33C1 338A 338B 338C 33D6 33C5 33AD 33AE 33AF 33DB 33A9 33AA 33AB 33AC 33DD 33D0 33D3 33C3 33C9 33DC 33C6').replace(/(\S{4})/g, function (a) { return "%u" + a; }).split(' '); + this.charSet[2] = unescape('3260 3261 3262 3263 3264 3265 3266 3267 3268 3269 326A 326B 326C 326D 326E 326F 3270 3271 3272 3273 3274 3275 3276 3277 3278 3279 327A 327B 24D0 24D1 24D2 24D3 24D4 24D5 24D6 24D7 24D8 24D9 24DA 24DB 24DC 24DD 24DE 24DF 24E0 24E1 24E2 24E3 24E4 24E5 24E6 24E7 24E8 24E9 2460 2461 2462 2463 2464 2465 2466 2467 2468 2469 246A 246B 246C 246D 246E 3200 3201 3202 3203 3204 3205 3206 3207 3208 3209 320A 320B 320C 320D 320E 320F 3210 3211 3212 3213 3214 3215 3216 3217 3218 3219 321A 321B 249C 249D 249E 249F 24A0 24A1 24A2 24A3 24A4 24A5 24A6 24A7 24A8 24A9 24AA 24AB 24AC 24AD 24AE 24AF 24B0 24B1 24B2 24B3 24B4 24B5 2474 2475 2476 2477 2478 2479 247A 247B 247C 247D 247E 247F 2480 2481 2482').replace(/(\S{4})/g, function (a) { return "%u" + a; }).split(' '); + this.charSet[3] = unescape('3131 3132 3133 3134 3135 3136 3137 3138 3139 313A 313B 313C 313D 313E 313F 3140 3141 3142 3143 3144 3145 3146 3147 3148 3149 314A 314B 314C 314D 314E 314F 3150 3151 3152 3153 3154 3155 3156 3157 3158 3159 315A 315B 315C 315D 315E 315F 3160 3161 3162 3163 3165 3166 3167 3168 3169 316A 316B 316C 316D 316E 316F 3170 3171 3172 3173 3174 3175 3176 3177 3178 3179 317A 317B 317C 317D 317E 317F 3180 3181 3182 3183 3184 3185 3186 3187 3188 3189 318A 318B 318C 318D 318E').replace(/(\S{4})/g, function (a) { return "%u" + a; }).split(' '); + this.charSet[4] = unescape('0391 0392 0393 0394 0395 0396 0397 0398 0399 039A 039B 039C 039D 039E 039F 03A0 03A1 03A3 03A4 03A5 03A6 03A7 03A8 03A9 03B1 03B2 03B3 03B4 03B5 03B6 03B7 03B8 03B9 03BA 03BB 03BC 03BD 03BE 03BF 03C0 03C1 03C3 03C4 03C5 03C6 03C7 03C8 03C9 %C6 %D0 0126 0132 013F 0141 %D8 0152 %DE 0166 014A %E6 0111 %F0 0127 I 0133 0138 0140 0142 0142 0153 %DF %FE 0167 014B 0149 0411 0413 0414 0401 0416 0417 0418 0419 041B 041F 0426 0427 0428 0429 042A 042B 042C 042D 042E 042F 0431 0432 0433 0434 0451 0436 0437 0438 0439 043B 043F 0444 0446 0447 0448 0449 044A 044B 044C 044D 044E 044F').replace(/(\S{4})/g, function (a) { return "%u" + a; }).split(' '); + this.charSet[5] = unescape('3041 3042 3043 3044 3045 3046 3047 3048 3049 304A 304B 304C 304D 304E 304F 3050 3051 3052 3053 3054 3055 3056 3057 3058 3059 305A 305B 305C 305D 305E 305F 3060 3061 3062 3063 3064 3065 3066 3067 3068 3069 306A 306B 306C 306D 306E 306F 3070 3071 3072 3073 3074 3075 3076 3077 3078 3079 307A 307B 307C 307D 307E 307F 3080 3081 3082 3083 3084 3085 3086 3087 3088 3089 308A 308B 308C 308D 308E 308F 3090 3091 3092 3093 30A1 30A2 30A3 30A4 30A5 30A6 30A7 30A8 30A9 30AA 30AB 30AC 30AD 30AE 30AF 30B0 30B1 30B2 30B3 30B4 30B5 30B6 30B7 30B8 30B9 30BA 30BB 30BC 30BD 30BE 30BF 30C0 30C1 30C2 30C3 30C4 30C5 30C6 30C7 30C8 30C9 30CA 30CB 30CC 30CD 30CE 30CF 30D0 30D1 30D2 30D3 30D4 30D5 30D6 30D7 30D8 30D9 30DA 30DB 30DC 30DD 30DE 30DF 30E0 30E1 30E2 30E3 30E4 30E5 30E6 30E7 30E8 30E9 30EA 30EB 30EC 30ED 30EE 30EF 30F0 30F1 30F2 30F3 30F4 30F5 30F6').replace(/(\S{4})/g, function (a) { return "%u" + a; }).split(' '); - for(var i=0; i 1){return;} + // [SMARTEDITORSUS-1767] + this.oApp.registerBrowserEvent(this.oTextField, "keydown", "EVENT_SCHARACTER_KEYDOWN"); + // --[SMARTEDITORSUS-1767] + }, - this.oApp.exec("SELECT_SCHARACTER", [sChar]); - weEvent.stop(); - }, + // [SMARTEDITORSUS-1767] + $ON_EVENT_SCHARACTER_KEYDOWN: function (oEvent) { + if (oEvent.key().enter) { + this.oApp.exec("INSERT_SCHARACTERS"); + oEvent.stop(); + } + }, + // --[SMARTEDITORSUS-1767] - $ON_SELECT_SCHARACTER : function(schar){ - this.oTextField.value += schar; + $ON_TOGGLE_SCHARACTER_LAYER: function () { + this.oTextField.value = ""; + this.oSelection = this.oApp.getSelection(); - if(this.oTextField.createTextRange){ - var oTextRange = this.oTextField.createTextRange(); - oTextRange.collapse(false); - oTextRange.select(); - }else{ - if(this.oTextField.selectionEnd){ - this.oTextField.selectionEnd = this.oTextField.value.length; - this.oTextField.focus(); - } - } - }, - - $ON_INSERT_SCHARACTERS : function(){ - this.oApp.exec("RECORD_UNDO_BEFORE_ACTION", ["INSERT SCHARACTER"]); - this.oApp.exec("PASTE_HTML", [this.oTextField.value]); - this.oApp.exec("FOCUS"); - this.oApp.exec("RECORD_UNDO_AFTER_ACTION", ["INSERT SCHARACTER"]); - - this.oApp.exec("HIDE_ACTIVE_LAYER", []); - }, + this.oApp.exec("TOGGLE_TOOLBAR_ACTIVE_LAYER", [this.elDropdownLayer, null, "MSG_SCHARACTER_LAYER_SHOWN", [], "MSG_SCHARACTER_LAYER_HIDDEN", [""]]); + this.oApp.exec('MSG_NOTIFY_CLICKCR', ['symbol']); + }, - $ON_CHANGE_SCHARACTER_SET : function(nSCharSet){ - for(var i=0; i 1) { return; } - var button, span; - for(var ii=0; ii"); + this.oApp.exec("SELECT_SCHARACTER", [sChar]); + weEvent.stop(); + }, - if(this.bIE){ - button = jindo.$("