LoginSignup
2
0

クリップボード経由で全角半角を変換するスクリプトを HTA でささっと作る(IE のバージョン違いに悩む)

Last updated at Posted at 2024-01-07

0. はじめに

筆者は半角カタカナ滅ぶべし・・・と思っている。他にも英数字の半角・全角を混合したファイル名や文章を平気で量産している人種も美意識が欠けており,前世紀中に滅ぶべきだった・・・

かつてはそんな全角・半角混合文章を手作業でチマチマ直していたが,すぐに自動変換できるプログラム,EXCEL ならば VBA マクロやアドインを作ったりもした。しかし,アプリ毎にマクロやアドインを作るのも骨が折れるものだ。

テキストデータであれば,いったん Windows のクリップボードに預けることができるので,クリップボードを介した変換プログラムを作成すれば良いことに気づいた。

そこでクリップボードをサポートし,お手軽にユーザーインターフェースを作れる HTA(HTML Application)で作成することにする。

1. IE のバージョン問題

HTML アプリケーションでは IE のバージョン(厳密にはモードと呼ぶべきか)を選択することができる。具体的には <head> タグ内に下記の一文を追加することで,IE バージョンを指定することができる。※下記は IE6 の場合

<meta http-equiv="X-UA-Compatible" content="IE=6">

1.1 <HTA:APPLICATION> タグサポート

HTML アプリケーションは <head> タグ内に記述する <HTA:APPLICATION> タグを用いて様々なオプションを指定することができる。具体的には最大化・最小化ボタンの表示/非表示や,ボーダーラインの設定,すなわちウィンドウサイズの可変/固定化,アイコンの設定である。コチラは IE10 以降は対応していないようだ。

1.2 ボタンホバー機能(デフォルト)

ボタンホバーというのは図1に示すようにボタンの上にマウスカーソルを置くとボタンの色が変わる機能のことであるが,何故か IE8 以降はデフォルトでは色が変わらない。もちろん CSS で <button> タグに :hover セレクタを用いて色を指定すれば IE8 以降でも可能である。ただ,デフォルトの上品な色合いが上手く出せないのだ。

1.3 ラベルホバー機能

一方,ラベルホバーとは図2に示すようにラベル,すなわち <label> タグに囲まれた領域の上にマウスカーソルを置くとラベルの色が変わる機能のことである。ただし,デフォルトでは色が変わらないので CSS を用いて以下のような設定を行う。すなわち CSS の <label> タグで :hover セレクタに対応しているかという意味である。コチラは IE6 のみ対応していない。

label:hover {
  background-color: #FFCCCC;
}

1.4 まとめ

以上をまとめると表1のようになる。全ての機能に対応したバージョンは IE7 モードのみである。

表1 IE のバージョンによる違い
6 7 8 9 10 11 edge
<HTA:APPLICATION>タグ × × ×
ボタンホバー(デフォルト) × × × × ×
ラベルホバー ×

2. ウィンドウのリサイズ

先に IE7 モードで作成することにしたが,基本的にすべてのバージョンで動作するように設計したい。これは長らく使われてきた IE6 モードと異なり,それ以降のバージョンは使用されていた期間も短かったことからバグが潰し切れていない可能性があり,イザというときには他のバージョンに切り替えられるようにするためだ。

2.1 ウィンドウの高さ

拙作のスクリプトではウィンドウサイズを必要最小限にするため,起動時にウィンドウサイズおよび最小限の表示領域サイズを取得し,最小限の表示領域サイズに合わせてウィンドウをリサイズする。本スクリプトの起動時のウィンドウの高さを実測した結果を以下に示す。

表2 起動時のウィンドウの高さ
6 7 8 9 10 11 edge
window.innerHeight × × × 750 750 750 750
document.documentElement
.offsetHeight
750 750 750 750 750 125 125

IE9 以降は window.innerHeight で取得できるが,IE8 以前は得られない(値が undefined になってしまう)ので document.documentElement.offsetHeight で代用しなくてはならない。

2.2 インライン属性

デフォルトで <form> タグは display: block 属性なので横幅を画面いっぱいまで広げてしまう。これを CSS で display: inline-block と指定すれば必要最小限の横幅に収まる。しかし,IE6 と IE7 は display: inline-block 属性に対応していない。一方,display: inline ならば IE6 と IE7 も対応している。しかし,高さが IE のバージョンによってバラバラなのだ。<form> タグの中には <table> タグと <button> が含まれているが,IE8 以降は <table> タグの高さ分,すなわち一行分の高さしか含まれていないようだ。

表3 インライン属性指定時の各エレメントの高さ
6 7 8 9 10 11 edge
document.documentElement
.offsetHeight
750 750 750 750 750 125 125
document.body.offsetHeight 750 122 118 117 117 117 117
document.body.offsetTop 0 4 0 0 0 0 0
Canvas.offsetHeight 122 122 114 113 113 113 113
Canvas.offsetTop 4 0 4 4 4 4 4

2.3 インラインブロック属性

これを display: inline-block ならば下記のようになり,高さとしては必要最小限の寸法を取得できる。ただし,IE6 や IE7 は未サポートであるからデフォルトの display: block と解釈され,横幅は最大限に広がってしまう。

表4 インラインブロック属性指定時の各エレメントの高さ
6 7 8 9 10 11 edge
document.documentElement
.offsetHeight
750 750 750 750 750 125 125
document.body.offsetHeight 750 122 118 117 117 117 117
document.body.offsetTop 0 4 0 0 0 0 0
Canvas.offsetHeight 122 122 118 117 117 117 117
Canvas.offsetTop 4 0 4 4 4 4 4

2.4 ユニバーサル設計

この不条理に対応するため「アンダースコアハック」と「スターハック」を用いる。詳しくは参考情報のサイトを参照されたい。

IE6 から edge まで使えるユニバーサルな CSS 設定
form {
  display: inline-block;
  *display: inline;
  _display: inline;
}

また IE7 のみ Canvas.offsetTop の値がゼロになってしまうので document.body.offsetTop で代用する。

IE6 から edge まで使えるユニバーサルなリサイズ処理
var width  = window.innerWidth  || document.documentElement.offsetWidth;
var height = window.innerHeight || document.documentElement.offsetHeight;
var minwidth  = Canvas.offsetWidth  + ( Canvas.offsetLeft + document.body.offsetLeft ) * 2;
var minheight = Canvas.offsetHeight + ( Canvas.offsetTop  + document.body.offsetTop  ) * 2;
window.resizeBy( minwidth - width, minheight - height );

なお,リサイズ後のウィンドウの高さはバージョンによって微妙に異なる。これはバージョンによってテーブルの高さ(セルの余白)が微妙に異なるためである。

表5 リサイズ後のウィンドウの高さ
6 7 8 9 10 11 edge
document.documentElement
.offsetHeight
130 130 126 125 125 125 125

3. 基本方針

本スクリプトの基本機能であるクリップボード変換の実装自体はさほど難しくはない。クリップボードからテキストを読み出し,所望の変換を行ってからクリップボードに戻せばよいだけだ。

//----------------------------------------------------------------------------
// クリップボードから読み出し
//----------------------------------------------------------------------------
var s = clipboardData.getData( "text" );
if( s == null ) {
  alert( "クリップボードの中身がありません!!" );
  return;
}

/* この間で文字列 s の置換を行う */

//----------------------------------------------------------------------------
// クリップボードへ書き込み
//----------------------------------------------------------------------------
clipboardData.setData( "text", s );

4. 基本仕様

また実装する変換機能の仕様を以下のようにまとめた。

  1. 半角カナを全角に変換
    半角カタカナおよび半角カナ記号6字を全角に変換する。
    濁音・半濁音記号は直前のカナ文字と融合する。例)ガ→ガ
  2. 全角英字を半角に変換
    全角英字(大文字・小文字)26×2=52字を半角に変換する。
  3. 全角数字を半角に変換
    全角数字10字を半角に変換する。
  4. 全角記号を半角に変換
    ASCIIコード内に収まる半角記号のうちチルダ「~」を除く31字を対象とする。
    なお全角の二重引用符「“」「”」はいずれも半角の「"」に変換される。
  5. 全角スペースを半角に変換

5. 画面デザイン

余分なスペースを挟まないようタグの間をコメントで区切っている。なお,この記事の作成中に <label> タグの for オプションを省略できる方法を知った。詳しくは参考情報サイトを参照されたい。

<body><!-- NOBR
--><form name="Form" id="Canvas"><!-- NOBR
--><table cellpadding=0 cellspacing=0><!-- NOBR
--><tr><td><label><input type="checkbox" name="checkbox" id="kana"   value="kana"   accesskey="K" />半角カナ文字を全角に変換します(<u>K</u>)</label></td></tr><!-- NOBR
--><tr><td><label><input type="checkbox" name="checkbox" id="alpha"  value="alpha"  accesskey="A" />全角英字を半角に変換します(<u>A</u>)</label></td></tr><!-- NOBR
--><tr><td><label><input type="checkbox" name="checkbox" id="number" value="number" accesskey="N" />全角数字を半角に変換します(<u>N</u>)</label></td></tr><!-- NOBR
--><tr><td><label><input type="checkbox" name="checkbox" id="symbol" value="symbol" accesskey="H" />全角記号を半角に変換します(<u>H</u>)</label></td></tr><!-- NOBR
--><tr><td><label><input type="checkbox" name="checkbox" id="space"  value="space"  accesskey="S" />全角スぺースを半角に変換します(<u>S</u>)</label></td></tr><!-- NOBR
--></table><!-- NOBR
--><button accesskey="C" onclick="javascript:convert(); return false;">変換(<u>C</u>)</button><!-- NOBR
--><button accesskey="X" onclick="javascript:exit(); return false;">終了(<u>X</u>)</button><!-- NOBR
--></form><!-- NOBR
--></body>

6. 実装コード

実装コードを以下に示す。

詳細はコチラ
ClipboardConverter.HTA
<html>
<head>
<meta http-equiv="MSThemeCompatible" content="yes" />
<meta http-equiv="X-UA-Compatible" content="IE=7">
<meta http-equiv="content-type" content="text/html; charset=Shift_JIS" />
<title>Clipboard Converter</title>
<HTA:APPLICATION ID="Clipboard Converter"
  APPLICATIONNAME="Clipboard Converter"
  BORDER="thin"
  BORDERSTYLE="normal"
  CAPTION="yes"
  ICON="ClipboardConverter.ICO"
  INNERBORDER="no"
  MAXIMIZEBUTTON="no"
  MINIMIZEBUTTON="no"
  SHOWINTASKBAR="yes"
  SINGLEINSTANCE="yes"
  SYSMENU="yes"
  VERSION="1.0"
  WINDOWSTATE="normal"
  SCROLL="no"
  SCROLLFLAT="no"
  SELECTION="yes"
  CONTEXTMENU="yes"
  NAVIGABLE="yes"
/>
<style type="text/css">
body {
  margin: 4px;
  padding: 0px;
}
body, input, button, td {
  font-family: BIZ UDゴシック;
  font-size: 10.5pt;
}
form {
  display: inline-block;
  *display: inline;
  _display: inline;
}
label:hover {
  background-color: #FFCCCC;
}
</style>
<script languate="javascript">
//------------------------------------------------------------------------------
// 初期化
//------------------------------------------------------------------------------
function initial() {
  //----------------------------------------------------------------------------
  // ウィンドウサイズの調整
  //----------------------------------------------------------------------------
  var width  = window.innerWidth  || document.documentElement.offsetWidth;
  var height = window.innerHeight || document.documentElement.offsetHeight;
  var minwidth  = Canvas.offsetWidth  + ( Canvas.offsetLeft + document.body.offsetLeft ) * 2;
  var minheight = Canvas.offsetHeight + ( Canvas.offsetTop  + document.body.offsetTop  ) * 2;
  window.resizeBy( minwidth - width, minheight - height );
}
//------------------------------------------------------------------------------
// 変換
//------------------------------------------------------------------------------
function convert() {
  //----------------------------------------------------------------------------
  // チェックボックスのチェック
  //----------------------------------------------------------------------------
  var flag = false;
  for( var i = 0; i < Form.checkbox.length; i++ )
    if( Form.checkbox[i].checked ) flag = true;
  if( !flag ) {
    alert( "チェックボックスが押されていません!!" );
    return;
  }
  //----------------------------------------------------------------------------
  // クリップボードから読み出し
  //----------------------------------------------------------------------------
  var s = clipboardData.getData( "text" );
  if( s == null ) {
    alert( "クリップボードの中身がありません!!" );
    return;
  }
  //----------------------------------------------------------------------------
  // 変換
  //----------------------------------------------------------------------------
  for( var i = 0; i < Form.checkbox.length; i++ ) {
    if( Form.checkbox[i].checked ) {
      switch( Form.checkbox[i].value ) {
        case "kana":   s = convert_kana  ( s ); break;
        case "alpha":  s = convert_alpha ( s ); break;
        case "number": s = convert_number( s ); break;
        case "symbol": s = convert_symbol( s ); break;
        case "space":  s = convert_space ( s ); break;
        default:                                break;
      }
    }
  }
  //----------------------------------------------------------------------------
  // クリップボードへ書き込み
  //----------------------------------------------------------------------------
  clipboardData.setData( "text", s );
}
//------------------------------------------------------------------------------
// 終了
//------------------------------------------------------------------------------
function exit() {
  window.close();
}
//------------------------------------------------------------------------------
// 半角カナ文字を全角に変換します
//------------------------------------------------------------------------------
function convert_kana( s ) {
  var r = s;
  r = r.replace( /ヴ/g, "" );
  r = r.replace( /ガ/g, "" );
  r = r.replace( /ギ/g, "" );
  r = r.replace( /グ/g, "" );
  r = r.replace( /ゲ/g, "" );
  r = r.replace( /ゴ/g, "" );
  r = r.replace( /ザ/g, "" );
  r = r.replace( /ジ/g, "" );
  r = r.replace( /ズ/g, "" );
  r = r.replace( /ゼ/g, "" );
  r = r.replace( /ゾ/g, "" );
  r = r.replace( /ダ/g, "" );
  r = r.replace( /ヂ/g, "" );
  r = r.replace( /ヅ/g, "" );
  r = r.replace( /デ/g, "" );
  r = r.replace( /ド/g, "" );
  r = r.replace( /バ/g, "" );
  r = r.replace( /ビ/g, "" );
  r = r.replace( /ブ/g, "" );
  r = r.replace( /ベ/g, "" );
  r = r.replace( /ポ/g, "" );
  r = r.replace( /パ/g, "" );
  r = r.replace( /ピ/g, "" );
  r = r.replace( /プ/g, "" );
  r = r.replace( /ペ/g, "" );
  r = r.replace( /ポ/g, "" );
  r = r.replace( /ア/g, "" );
  r = r.replace( /イ/g, "" );
  r = r.replace( /ウ/g, "" );
  r = r.replace( /エ/g, "" );
  r = r.replace( /オ/g, "" );
  r = r.replace( /カ/g, "" );
  r = r.replace( /キ/g, "" );
  r = r.replace( /ク/g, "" );
  r = r.replace( /ケ/g, "" );
  r = r.replace( /コ/g, "" );
  r = r.replace( /サ/g, "" );
  r = r.replace( /シ/g, "" );
  r = r.replace( /ス/g, "" );
  r = r.replace( /セ/g, "" );
  r = r.replace( /ソ/g, "" );
  r = r.replace( /タ/g, "" );
  r = r.replace( /チ/g, "" );
  r = r.replace( /ツ/g, "" );
  r = r.replace( /テ/g, "" );
  r = r.replace( /ト/g, "" );
  r = r.replace( /ナ/g, "" );
  r = r.replace( /ニ/g, "" );
  r = r.replace( /ヌ/g, "" );
  r = r.replace( /ネ/g, "" );
  r = r.replace( /ノ/g, "" );
  r = r.replace( /ハ/g, "" );
  r = r.replace( /ヒ/g, "" );
  r = r.replace( /フ/g, "" );
  r = r.replace( /ヘ/g, "" );
  r = r.replace( /ホ/g, "" );
  r = r.replace( /マ/g, "" );
  r = r.replace( /ミ/g, "" );
  r = r.replace( /ム/g, "" );
  r = r.replace( /メ/g, "" );
  r = r.replace( /モ/g, "" );
  r = r.replace( /ヤ/g, "" );
  r = r.replace( /ユ/g, "" );
  r = r.replace( /ヨ/g, "" );
  r = r.replace( /ラ/g, "" );
  r = r.replace( /リ/g, "" );
  r = r.replace( /ル/g, "" );
  r = r.replace( /レ/g, "" );
  r = r.replace( /ロ/g, "" );
  r = r.replace( /ワ/g, "" );
  r = r.replace( /ヲ/g, "" );
  r = r.replace( /ン/g, "" );
  r = r.replace( /ァ/g, "" );
  r = r.replace( /ィ/g, "" );
  r = r.replace( /ゥ/g, "" );
  r = r.replace( /ェ/g, "" );
  r = r.replace( /ォ/g, "" );
  r = r.replace( /ャ/g, "" );
  r = r.replace( /ュ/g, "" );
  r = r.replace( /ョ/g, "" );
  r = r.replace( /ッ/g, "" );
  r = r.replace( /ー/g, "" );
  r = r.replace( /。/g, "" );
  r = r.replace( /「/g, "" );
  r = r.replace( /」/g, "" );
  r = r.replace( /、/g, "" );
  r = r.replace( /・/g, "" );
  return( r );
}
//------------------------------------------------------------------------------
// 全角英字を半角に変換します
//------------------------------------------------------------------------------
function convert_alpha( s ) {
  var r = s;
  r = r.replace( /A/g, "A" );
  r = r.replace( /B/g, "B" );
  r = r.replace( /C/g, "C" );
  r = r.replace( /D/g, "D" );
  r = r.replace( /E/g, "E" );
  r = r.replace( /F/g, "F" );
  r = r.replace( /G/g, "G" );
  r = r.replace( /H/g, "H" );
  r = r.replace( /I/g, "I" );
  r = r.replace( /J/g, "J" );
  r = r.replace( /K/g, "K" );
  r = r.replace( /L/g, "L" );
  r = r.replace( /M/g, "M" );
  r = r.replace( /N/g, "N" );
  r = r.replace( /O/g, "O" );
  r = r.replace( /P/g, "P" );
  r = r.replace( /Q/g, "Q" );
  r = r.replace( /R/g, "R" );
  r = r.replace( /S/g, "S" );
  r = r.replace( /T/g, "T" );
  r = r.replace( /U/g, "U" );
  r = r.replace( /V/g, "V" );
  r = r.replace( /W/g, "W" );
  r = r.replace( /X/g, "X" );
  r = r.replace( /Y/g, "Y" );
  r = r.replace( /Z/g, "Z" );
  r = r.replace( /a/g, "a" );
  r = r.replace( /b/g, "b" );
  r = r.replace( /c/g, "c" );
  r = r.replace( /d/g, "d" );
  r = r.replace( /e/g, "e" );
  r = r.replace( /f/g, "f" );
  r = r.replace( /g/g, "g" );
  r = r.replace( /h/g, "h" );
  r = r.replace( /i/g, "i" );
  r = r.replace( /j/g, "j" );
  r = r.replace( /k/g, "k" );
  r = r.replace( /l/g, "l" );
  r = r.replace( /m/g, "m" );
  r = r.replace( /n/g, "n" );
  r = r.replace( /o/g, "o" );
  r = r.replace( /p/g, "p" );
  r = r.replace( /q/g, "q" );
  r = r.replace( /r/g, "r" );
  r = r.replace( /s/g, "s" );
  r = r.replace( /t/g, "t" );
  r = r.replace( /u/g, "u" );
  r = r.replace( /v/g, "v" );
  r = r.replace( /w/g, "w" );
  r = r.replace( /x/g, "x" );
  r = r.replace( /y/g, "y" );
  r = r.replace( /z/g, "z" );
  return( r );
}
//------------------------------------------------------------------------------
// 全角数字を半角に変換します
//------------------------------------------------------------------------------
function convert_number( s ) {
  var r = s;
  r = r.replace( /0/g, "0" );
  r = r.replace( /1/g, "1" );
  r = r.replace( /2/g, "2" );
  r = r.replace( /3/g, "3" );
  r = r.replace( /4/g, "4" );
  r = r.replace( /5/g, "5" );
  r = r.replace( /6/g, "6" );
  r = r.replace( /7/g, "7" );
  r = r.replace( /8/g, "8" );
  r = r.replace( /9/g, "9" );
  return( r );
}
//------------------------------------------------------------------------------
// 全角記号を半角に変換します
//------------------------------------------------------------------------------
function convert_symbol( s ) {
  var r = s;
  r = r.replace( /!/g, "!" );
  r = r.replace( /“/g, "\"" );
  r = r.replace( /”/g, "\"" );
  r = r.replace( /#/g, "#" );
  r = r.replace( /$/g, "$" );
  r = r.replace( /%/g, "%" );
  r = r.replace( /&/g, "&" );
  r = r.replace( /’/g, "'" );
  r = r.replace( /(/g, "(" );
  r = r.replace( /)/g, ")" );
  r = r.replace( /*/g, "*" );
  r = r.replace( /+/g, "+" );
  r = r.replace( /,/g, "," );
  r = r.replace( /-/g, "-" );
  r = r.replace( /./g, "." );
  r = r.replace( ///g, "/" );
  r = r.replace( /:/g, ":" );
  r = r.replace( /;/g, ";" );
  r = r.replace( /</g, "<" );
  r = r.replace( /=/g, "=" );
  r = r.replace( />/g, ">" );
  r = r.replace( /?/g, "?" );
  r = r.replace( /@/g, "@" );
  r = r.replace( /[/g, "]" );
  r = r.replace( /¥/g, "\\" );
  r = r.replace( /]/g, "]" );
  r = r.replace( /^/g, "^" );
  r = r.replace( /_/g, "_" );
  r = r.replace( /‘/g, "`" );
  r = r.replace( /{/g, "{" );
  r = r.replace( /|/g, "|" );
  r = r.replace( /}/g, "}" );
//r = r.replace( /~/g, "~" );
  return( r );
}
//------------------------------------------------------------------------------
// 全角スペースを半角に変換します
//------------------------------------------------------------------------------
function convert_space( s ) {
  var r = s;
  r = r.replace( / /g, " " );
  return( r );
}
window.onload = initial;
</script>
</head>
<body><!-- NOBR
--><form name="Form" id="Canvas"><!-- NOBR
--><table cellpadding=0 cellspacing=0><!-- NOBR
--><tr><td><label><input type="checkbox" name="checkbox" id="kana"   value="kana"   accesskey="K" />半角カナ文字を全角に変換します(<u>K</u>)</label></td></tr><!-- NOBR
--><tr><td><label><input type="checkbox" name="checkbox" id="alpha"  value="alpha"  accesskey="A" />全角英字を半角に変換します(<u>A</u>)</label></td></tr><!-- NOBR
--><tr><td><label><input type="checkbox" name="checkbox" id="number" value="number" accesskey="N" />全角数字を半角に変換します(<u>N</u>)</label></td></tr><!-- NOBR
--><tr><td><label><input type="checkbox" name="checkbox" id="symbol" value="symbol" accesskey="H" />全角記号を半角に変換します(<u>H</u>)</label></td></tr><!-- NOBR
--><tr><td><label><input type="checkbox" name="checkbox" id="space"  value="space"  accesskey="S" />全角スぺースを半角に変換します(<u>S</u>)</label></td></tr><!-- NOBR
--></table><!-- NOBR
--><button accesskey="C" onclick="javascript:convert(); return false;">変換(<u>C</u>)</button><!-- NOBR
--><button accesskey="X" onclick="javascript:exit(); return false;">終了(<u>X</u>)</button><!-- NOBR
--></form><!-- NOBR
--></body>
</html>

7. アイコン画像

本スクリプトで使用しているアイコン ClipboardConverter.ICO は Windows の shell32.dll から抽出したものなので公開できない。なので各自用意して欲しい。※ただしアイコンがなくても動く

8. 実行画面

実行画面を以下に示す。チェックボックスは同時に複数指定可能である。

9. 参考情報

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0