Fader
以下は、Mac OS X 10.4.11 上の Xcode 2.5 で説明しています。このウィジェットは、BlankWidgetで解説したことは繰り返しませんので、そちらを参照してください。また、このサンプルで使われている Apple クラスのうち Animator は Stretcher で説明したので、その部分も繰り返しません。
1 ウィジェット
Fader.wdgt は、以下のようなウィジェットです。
「EAT」という文字列が表示されていますが、これは時間とともに中央の「AT」、右側の「JOE'S」に移り変わります。また右下に情報ボタンが表示されているのに注意してください。これはマウスカーソルがウィジェット内に入ると表示され、さらに近づくと「i」のまわりに円が表示されます。クリックすると、裏面の環境設定が表示されます。
上の「Fade speed (ms):」(フェード速度)というフィールドでは、大きい数字を入力すれば移り変わりの速度が遅くなります。「Fade delay (ms):」(フェードの遅れ)というフィールドでも、大きい数字を入力すればフェードインとフェードアウトの間の時間が長くなります。「Done(完了)」をクリックして、再び環境設定に戻ったとき、前の数値が保存されていることに注意してください。
2 ファイル構成
このサンプルのファイル構成は以下のようになります。
AppleClasses 内には、AppleAnimator.js、AppleButton.js、AppleInfoButton.js という 3 つの JavaScript ファイルがあります。Apple クラスの組み込みと Info.plist、そして AppleAnimator.js については、Stretcher を見てください。主要 HTML は Fader.html に指定されています。
Default.png は、ウィジェットの背景が含まれている画像です。back.png はウィジェット裏面の背景の画像です。EATNeon.png、ATNeon.png、JOESNeon.png は、それぞれ表示される文字列の画像です。
CSS ファイルは Fader.css です。スクリプトファイルは FaderMain.js と Fader.js の 2 つあることに注意してください。
3 Fader.html
それでは、主要 HTML ファイルを見てみます。
Fader.html
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
"http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>
<meta http-equiv='Content-Type' content='text/html; charset=ISO-8859-1'>
<title>Fader Example Widget</title>
<!-- Pre-10.4.3 より前の互換性:
ウィジェットの一番上に AppleClass ディレクトリを含め、相対パスを使用 -->
<script type='text/javascript' src='AppleClasses/AppleButton.js'></script>
<script type='text/javascript' src='AppleClasses/AppleInfoButton.js'></script>
<script type='text/javascript' src='AppleClasses/AppleAnimator.js'></script>
<script type='text/javascript' src='Scripts/FaderMain.js'></script>
<script type='text/javascript' src='Scripts/Fader.js'></script>
<style type='text/css'>
@import "Fader.css";
</style>
</head>
<body onLoad='loaded();'>
<div id='front'>
<img class='faderDiv' id='eat' src='Images/EATNeon.png' alt=''/>
<img class='faderDiv' id='at' src='Images/ATNeon.png' alt=''/>
<img class='faderDiv' id='joes' src='Images/JOESNeon.png' alt=''/>
<div id='flipper'></div>
</div>
<div id='back'>
<div class='captionText' id='speedCaption'>Fade speed (ms):</div>
<input type='text' id='speedField'/>
<div class='captionText' id='delayCaption'>Fade delay (ms):</div>
<input type='text' id='delayField'/>
<div id='doneButton'></div>
</div>
<div id='debugDiv'></div>
</body>
</html>
構造は単純です。body 要素には、3 つの div 要素 front、back、debugDiv があります。このデバッグ用領域である debugDiv とそれに関する説明については、Stretcher を見てください。ここでは説明しません。front は表面、back は裏面を表しています。このように、環境設定を裏面にもつウィジェットは body 要素直下に表面・裏面の div 要素をもち、表示を切り替えているのがよくある形です。
表面の div 要素内には表示される 3 つの文字列の画像の img 要素と、flipper という div 要素があります。最後のものは読み込み完了時のハンドラ内で情報ボタンと関連付けられています。
裏面の div 要素内には、入力フィールドのタイトルとなる 2 つの div 要素と、入力フィールドである 2 つの input 要素があります。そして Apple のガラスボタンに関連付けられている doneButton という div が置かれています。
4 Fader.css
スタイルシートを見てみましょう。
Fader.css
...
#front {
...
height: 139px;
width: 506px;
background-image: url("Default.png");
background-repeat: no-repeat;
}
.faderDiv {
opacity: 0;
...
}
#back {
...
height: 139px;
width: 506px;
background-image: url("Images/back.png");
background-repeat: no-repeat;
display: none;
}
表面と裏面の幅と高さに同じ数値が設定されているのに注意してください。それぞれ背景画像を設定し、繰り返しなしに設定しています。裏面は表示しないように指定されています。また、faderDiv クラスセレクタで不透明度が 0、すなわち完全に透明で目に見えないようになっていることに注意してください。初期値では 3 つの文字列とも透明で見えません。
5 FaderMain.js
FaderMain.js では、読み込み完了時のハンドラなどウィジェット全体の JavaScript が収められています。Fader.js では、実際のフェードを行うオブジェクトが実装されています。まず、FaderMain.js を見てみます。
FaderMain.js > グローバル変数と関数外コード
var flipper;
var transFader;
var glassButton;
if (window.widget) {
widget.onhide = onHide;
widget.onshow = onShow;
}
情報ボタンが格納される flipper、フェードを行う TransitionFader オブジェクトが格納される transFader、Done ボタンが格納される glassButton という 3 つの変数が宣言されています。
その後に、このファイルが読み込まれた時に実行される関数外のコードがあります。Dashboard 内で起動されたなら、表示された場合と隠された場合のハンドラを設定します。
つぎに body 要素の読み込み完了時のハンドラを見てみます。
FaderMain.js > loaded
function loaded() {
flipper = new AppleInfoButton(document.getElementById("flipper"),
document.getElementById("front"), "white", "white", showBack);
glassButton = new AppleGlassButton(document.getElementById("doneButton"),
"Done", showFront);
var fades = new Array;
fades[0] = document.getElementById("eat");
fades[1] = document.getElementById("at");
fades[2] = document.getElementById("joes");
transFader = new TransitionFader(fades, 250, 1000);
transFader.start();
}
まず表面の flipper が情報ボタンと関連付けられ、作成されたボタンをグローバル変数内に格納しています。情報ボタンのコンストラクタの引数は、順番に、ボタン要素、前面要素、前景色、背景色、裏面表示の際のハンドラです。くわしいことは後の情報ボタンの説明を見てください。
つぎに裏面の doneButton がガラスボタンと関連付けられ、作成されたボタンをグローバル変数内に格納しています。コンストラクタの引数は、順番に、ボタン要素、タイトル、クリックされた場合のハンドラです。くわしいことは後のガラスボタンの説明を見てください。
それから fades という配列を作成し、そこに表示される文字列の img 要素であった 3 つの要素を格納しています。これはフェードを行うオブジェクトにおいて順番に表示される対象となります。それから TransitionFader オブジェクトを作り、グローバル変数に格納しています。コンストラクタの引数は、対象、フェード速度、フェードの遅れです。これについては、Fader.js の説明を見てください。
最初の表示されるときと隠される場合のハンドラは簡単なので先に見ます。
FaderMain.js > onHide と onShow
function onHide () {
transFader.pause();
}
function onShow() {
transFader.resume();
}
単純に変数に格納されているフェードオブジェクトに対して一時停止と再開を指示しているだけです。
それから情報ボタン、ガラスボタンに設定されたハンドラが実装されています。まず、情報ボタンに設定されたハンドラです。
FaderMain.js > showBack
function showBack(event) {
if (window.widget) {
widget.prepareForTransition("ToBack");
// 表裏の切り替え準備
}
transFader.pause();
// フェードを一時停止
document.getElementById("front").style.display = "none";
// 表面を表示しない
document.getElementById("back").style.display = "block";
// 裏面を表示
document.getElementById("speedField").value = transFader.getFadeTime();
// 現在のフェード速度を取得し設定
document.getElementById("delayField").value = transFader.delay;
// 現在のフェード遅れを取得し設定
if (window.widget) {
setTimeout("widget.performTransition()", 0);
// 表面切替実行
}
}
まず、表裏の切り替えを準備します。これは表示を凍結します。こうしなければ、以下の表示変更が反映されてしまい、見ぐるしい表示になるでしょう。
表示が凍結されたので、自由に表示内容を変更できます。まずフェードを一時停止し、表面と裏面の表示設定を行い、それから裏面が表示されたときに現在の値として表示される値を設定します。準備がすべて終わったら、切り替えを実行するように指示しています。
ここで、setTimeout を使って、performTransition を呼び出しています。遅れが 0 なのですぐに実行されることになります。これは『Dashboard プログラミングトピック』>「ウィジェットの裏面と環境設定」でも書かれていて、両面を正しく反転するための処理です。
つぎは裏面から表面に戻るときのハンドラです。
FaderMain.js > showFront
function showFront(event) {
if (window.widget) {
widget.prepareForTransition("ToFront");
// 表裏の切り替え準備
}
document.getElementById("back").style.display = "none";
// 各種設定値を反映
transFader.setFadeTime(document.getElementById("speedField").value);
transFader.delay = document.getElementById("delayField").value;
document.getElementById("front").style.display = "block";
transFader.resume();
// フェードを再開
if (window.widget) {
setTimeout("widget.performTransition()", 0);
// 表面切替実行
}
}
切り替え準備をした後で、各種設定値を設定します。それからフェードを再開させ、切り替えを実行しています。
これで、このファイル内の関数はすべて説明しました。後は Fader.js で実装されているフェードオブジェクトについて理解すれば、このサンプルの動作はほとんどわかります。
6 Fader.js
このファイル内では、単一の要素をフェードさせる Fader オブジェクトと、複数の要素をフェードさせながら切り替える TransitionFader オブジェクトの 2 つが実装されていることに注意してください。このうち、このサンプルで外部から使用されているのは後の TransitionFader だけです。前者は後のオブジェクト内でフェードを行うために利用されています。
まず単一要素をフェードさせる Fader オブジェクトから見ていきます。
Fader.js > Fader
/*
* Fader コンストラクタ。引数:
* - element: フェードインまたはアウトさせる要素
* - callback: フェードが完了した時に呼ばれることになる関数
* - fadeTime: フェードがどのぐらい長くかかったほうがいいか(ms)(setFadeTime()を参照)
*/
function Fader (element, callback, fadeTime, minOpacity, maxOpacity) {
this.element = element;
this.startTime = 0;
this.timer = null;
this.onFinished = callback;
// フェードインに対する初期化;これらの値はfadeIn/fadeOut関数でリセットされることになる
this.fadingIn;
this.now = 0.0;
this.from = 0.0;
this.to = 1.0;
this.minOpacity = minOpacity ? minOpacity : 0;
this.maxOpacity = maxOpacity ? maxOpacity : 1;
this.setFadeTime(fadeTime);
var self = this;
// プライベート変数
// 特権メソッド
this.nextFrame = function (animation, now, first, done) {
self.element.style.opacity = now;
}
}
説明がない残りの引数は、最小と最大の不透明度です。指定しなければ 0 と 1 になります。基本的に各種変数を初期化しているだけです。タイマーコールバック内で呼ばれるハンドラを特権メソッドとして設定しています。
フェード時間はメソッドを使って設定されています。
Fader.js > Fader.setFadeTime
/*
* フェードの持続時間の設定関数(250msを下限とする)
*/
Fader.prototype.setFadeTime = function (fadeTime) {
this.fadeTime = fadeTime > 250 ? fadeTime : 250;
}
メソッドを使っているのは、単に最小時間を 250 にするためです。
こうして作成された Fader オブジェクトは、fadeIn/fadeOut メソッドを使って、出現と消滅のどちらかに設定されます。
Fader.js > Fader.fadeOut
Fader.prototype.fadeOut = function () {
if (this.fadingIn == false) {
// 設定済なら終了
return;
}
var from = this.maxOpacity;
// 開始値を最大不透明度に
if (this.element.style.opacity) {
from = parseInt(this.element.style.opacity);
// スタイル設定済なら設定値を優先
}
this.fadeTo(from, this.minOpacity);
// 終了値を最小不透明度に
this.fadingIn = false;
// フェードアウトであることを示す
}
すでにフェードアウトならそのまま戻ります。開始値を最大不透明度にしますが、すでに既存の不透明度があるならそちらを使います。それから終了値を最小とし、フェードアウトであることを示すようにします。
Fader.js > Fader.fadeIn
Fader.prototype.fadeIn = function () {
if (this.fadingIn == true) {
// 設定済なら終了
return;
}
var from = this.minOpacity;
// 開始値を最小不透明度に
if (this.element.style.opacity) {
from = parseInt(this.element.style.opacity);
// スタイル設定済なら設定値を優先
}
this.fadeTo(from, this.maxOpacity);
// 終了値を最大不透明度に
this.fadingIn = true;
// フェードインであることを示す
}
フェードアウトの場合と設定値が違う以外は同じ構造です。
こうして設定されたフェードは fadeTo メソッドを使って実行されます。
Fader.js > Fader.fadeTo
Fader.prototype.fadeTo = function (newFrom, newTo) {
if (this.fadeAnimator) {
// すでにアニメーターがあるなら
this.fadeAnimator.stop();
// 停止して
delete this.fadeAnimator;
// 削除
}
this.fadeAnimator
= new AppleAnimator(this.fadeTime, 13, newFrom, newTo, this.nextFrame);
// アニメーターを作成(アニメーション同時作成版)
this.fadeAnimator.oncomplete = this.onFinished;
// 完了ハンドラを設定
this.fadeAnimator.start();
//アニメーションを開始
}
アニメーションを行う Apple クラスの使い方については、 Stretcher の解説、あるいは『Dashboard プログラミングトピック』>「アニメーションの使用」を見てください。
アニメーターがあるなら停止して削除し、新規アニメーターを設定値で作成し、終了ハンドラを設定した後で実行しているだけです。あとのことは Apple クラスが行ってくれます。
これで Fader オブジェクトのメソッドはすべてです。つぎは サンプルから呼ばれていた TransitionFader オブジェクトを見てみましょう。まずはコンストラクタです。
Fader.js > TransitionFader
/*
* TransitionFader コンストラクタ。引数:
* - elements: 配列であるべき。単一要素のフェードイン・アウトには Fader オブジェクト使用
* - fadeTime: フェードがどのぐらい長くかかったほうがいいか(ms単位)
* - inDelay: 遷移フェードの間の一時休止(ms単位)(setFadeDelay()参照)
* Fader におけるように、すべてのメソッドは、インスタンス変数を通じて呼び出されたほうがいい
*/
function TransitionFader (elements, fadeTime, inDelay) {
// 入来と退去の 2 つのFaderオブジェクトを作成
// フェードが完了したとき要素配列を循環できるように
// 一方のFaderのコールバックとして prepNextFade を設定
var self = this;
// プライベート変数
// プライベート変数を使う特権メソッド定義
// その間で遷移を行うのがどの2つの要素かを管理
this.prepNextFade = function() {
if (self.outFader.element) {
self.outFader.element.style.opacity = 0;
}
// フェードインする新しい要素を選択
self.currentIndex = (self.currentIndex + 1) % self.elements.length;
// +1 の余りを使うことで循環させる
self.outFader = new Fader(self.inFader.element, null, self.inFader.fadeTime);
// フェードアウトする方を設定
self.inFader = new Fader(self.elements[self.currentIndex],
self.prepNextFade, self.inFader.fadeTime);
// フェードインの方を設定
// delay プロパティを使って次の遷移をキューに入れる
window.setTimeout(function() { self.fade(); }, self.delay);
}
// 実際のコンストラクタ作業
this.inFader = new Fader(elements[0], this.prepNextFade, fadeTime);
// フェードインの方を配列の最初の要素に
this.outFader = new Fader(null, null, fadeTime);
// 最初のフェードアウトなし
// すべての要素の間で遷移させたい
// 同時に2つを行う(フェードインとアウト)が
// 任意サイズ配列を通じて移動できる(prepNextFade()参照)
this.elements = elements;
this.currentIndex = 0;
this.setFadeDelay(inDelay);
}
最初にプライベート変数に自身を格納しています。次は、コンストラクタ作業というより、特権メソッドの定義です。この prepNextFade は、フェードインの完了ハンドラとして指定され、フェードインが完了した後で、次の遷移の組を作成します。割った余りを使うことで、任意サイズの配列を循環させることができます。フェードインにおいて、完了ハンドラとして prepNextFaded が指定されていることに注意してください。遷移が中止されまで、これがずっと繰り返されることになります。そして setTimeout を使って、delay 後にフェードを実行させることで、フェードインからフェードアウトが開始されるまでの遅れを管理しています。
その後で、実際のコンストラクタ呼び出し時の作業が続きます。最初に使われるフェードの組を作成します。最初は表示がないので、片方がフェードインされるだけです。それから渡された引数等をパブリック変数に設定しています。
Fader.js > TransitionFader.setFadeDelay
/*
* フェード間の遅れに対する設定メソッド(500msを下限とする)
*/
TransitionFader.prototype.setFadeDelay = function (inDelay) {
this.delay = inDelay > 500 ? inDelay : 500;
}
単に遅れを 500ms 以上にするためにメソッドを使っているだけです。持続時間の設定は少し違います。
Fader.js > TransitionFader.setFadeTime
TransitionFader.prototype.setFadeTime = function (newFadeTime) {
this.inFader.setFadeTime(newFadeTime);
this.outFader.setFadeTime(newFadeTime);
}
フェードイン・アウトの両 Fader オブジェクトのメソッドを使って持続時間を設定しています。
さて、FaderMain.js の loaded において、オブジェクトが作成された後で start() が呼ばれていました。これを見てみます。
Fader.js > TransitionFader.start
TransitionFader.prototype.start = function () {
this.inFader.fadeIn();
}
これだけです。最初はフェードインから始まるので、単にフェードインを行っているだけです。
毎回の prepNextFade では、fade が setTimeout を使って実行されていました。これを見てみましょう。
Fader.js > TransitionFader.fade
/*
* フェードを実行する
*
* paused を true に設定する前に
* prepNextFade からの遅れた呼び出しがすでに実行中だった場合のために
* paused フラグをチェックする
* そうだった場合、フェードを延期し、interrupted を設定(resume() を参照)
*/
TransitionFader.prototype.fade = function () {
if (this.paused) {
// paused フラグが設定されていた場合
this.interrupted = true;
// interruptedフラグを設定
return;
}
this.inFader.fadeIn();
// フェードイン実行
this.outFader.fadeOut();
// フェードアウト実行
}
フェードは setTimeout で遅れて実行されるため、実行までの間に一時停止された場合に備えて paused フラグをチェックしています。問題なければ、フェードイン、フェードアウトを行います。
では、一時停止メソッドを見てみましょう。
Fader.js > TransitionFader.pause
/*
* 遷移タイマーが次回にヒットした時に停止するよう paused フラグを設定
*/
TransitionFader.prototype.pause = function () {
this.paused = true;
}
単に paused フラグを設定しています。では再開の場合どうなるのでしょう。
Fader.js > TransitionFader.resume
/*
* フェード中ならpausedをfalseに設定することはタイマーが通常どおり継続することができるようにする
* フェードが延期されたら、明示的に物事が再び起こるようにする必要がある
*/
TransitionFader.prototype.resume = function () {
this.paused = false;
if (this.interrupted) {
this.interrupted = false;
this.fade();
}
}
コメントで十分わかると思います。pause が呼ばれた後、すでに fade が呼ばれていたら、interrupted が設定されているので、これが設定されていたら、実行されずに終わったフェードがあるので、それをすぐに実行します。そうでなければ、paused フラグを設定しているだけです。
7 まとめ
このサンプルでは、情報ボタンを使い、裏面の環境設定を使っていますが、設定値は保存されるわけではありません。オブジェクトが維持している変数の値を使って表示し、修正値をオブジェクトに設定しているだけです。ファイルに書き出して、システム起動ごとに同じ設定を使えるようにするには、設定値をウィジェットのメソッドを使い格納する必要があります。これについては、『Dashboard プログラミングトピック』>「ウィジェットの裏面と環境設定」、もしくは、Framing Gallery サンプルを見てください。
8 AppleButton.js
サンプルは理解できましたが、さらに Apple クラスである AppleButton.js を調べてみます。このファイルでは、一般的なボタン動作を実装する Apple ボタンと、そのサブクラスである裏面で使われている Apple ガラスボタンが実装されています。まず、Apple ボタンから見ていきます。
AppleButton.js > AppleButton
function AppleButton(button, text, height,
imgLeft, imgLeftClicked, imgLeftWidth,
imgMiddle, imgMiddleClicked,
imgRight, imgRightClicked, imgRightWidth,
onclick)
{
if (button == null)
return;
/* オブジェクト */
this.textElement = null;
/* 読み書き可能プロパティ */
this.onclick = onclick;
/* 読みとり専用プロパティ */
this.enabled = true;
this._init(button, text, height,
imgLeft, imgLeftClicked, imgLeftWidth,
imgMiddle, imgMiddleClicked,
imgRight, imgRightClicked, imgRightWidth);
this.textElement.innerHTML = text;
}
コンスラクタには、非常に多くの引数があります。このうち多くは _init メソッドに渡されています。ほとんどが画像のパスとサイズなので、理解するのは簡単です。button は、ボタンと関連付けられる DOM 要素で、通常は div 要素です。text は、ボタンに表示されるラベルです。height はボタンで使われる画像すべての高さです。imgLeft はボタン左側で使われる画像を示すパスです。imgLeftClicked は、クリックされた時にボタン左側で使われる画像を示すパスです。imgLeftWidth は、左側で使われる画像に共通の幅です。imgMiddle は、ボタン中間部分で使われる画像のパスです。imgMiddleClicked は、クリックされた時の中間部分で使われる画像のパスです。幅の指定はありませんが、ボタンの中間部分はテキスト表示幅にもとづいて繰り返されることになります。そのため、画像を作成するときは、横に並べても大丈夫なようにすべきです。imgRight はボタン右側で使われる画像を示すパスです。imgRightClicked は、クリックされた時にボタン右側で使われる画像を示すパスです。imgRightWidth は、右側で使われる画像に共通の幅です。最後の onclick は、ボタンがクリックされた時に呼ばれる関数です。
画像については、システム>ライブラリ>WidgetResources>button 内のガラスボタンの画像を参考にしてください。クリックされた時の画像がより濃い灰色になっています。
構造は簡単です。まずボタン要素が渡されなければすぐに戻ります。次にテキスト要素を null に初期化し、クリックハンドラを渡されたものに設定し、ボタンが有効であるようにします。それから _init で初期化を行い、最初にテキスト要素の内部 HTML に渡されたラベルを設定しています。
コンストラクタから呼ばれていた _init メソッドは以下のようになります。
AppleButton.js > AppleButton._init
AppleButton.prototype._init = function(button, text, height,
imgLeft, imgLeftClicked, imgLeftWidth,
imgMiddle, imgMiddleClicked,
imgRight, imgRightClicked, imgRightWidth)
{
// 渡されたパスを保存しておく
this._imgLeftPath = imgLeft;
this._imgLeftClickedPath = imgLeftClicked;
this._imgMiddlePath = imgMiddle;
this._imgMiddleClickedPath = imgMiddleClicked;
this._imgRightPath = imgRight;
this._imgRightClickedPath = imgRightClicked;
// ボタン要素の子になり部品をまとめるdiv作成
var container = document.createElement("div");
this._container = container;
button.appendChild(container);
// JavaScript イベントハンドラのため
var _self = this;
// 自身を格納するプライベート変数
// 以下は特権メソッド
this._mousedownHandler = function(event) { _self._mousedown(event); }
this._mousemoveHandler = function(event)
{
event.stopPropagation();
event.preventDefault();
}
this._mouseoverHandler = function(event) { _self._mouseover(event); }
this._mouseoutHandler = function(event) { _self._mouseout(event); }
this._mouseupHandler = function(event) { _self._mouseup(event); }
// 内部要素作成
// 左側部分作成
var element = document.createElement("div");
var style = element.style;
style.display = "inline-block";
style.background = "url(" + this._imgLeftPath + ") no-repeat top left";
style.height = height + "px";
style.width = imgLeftWidth + "px";
container.appendChild(element);
// テキスト部分作成
element = document.createElement("div");
element.innerText = text;
style = element.style;
style.display = "inline-block";
style.backgroundRepeat = "repeat-x";
// 背景繰り返し
style.backgroundImage = "url(" + this._imgMiddlePath + ")";
style.lineHeight = height + "px";
style.height = height + "px";
style.overflow = "hidden";
style.whiteSpace = "nowrap";
container.appendChild(element);
this.textElement = element;
// 右側部分作成
element = document.createElement("div");
style = element.style;
style.display = "inline-block";
style.background = "url(" + this._imgRightPath + ") no-repeat top left";
style.height = height + "px";
style.width = imgLeftWidth + "px";
container.appendChild(element);
style = container.style;
style.appleDashboardRegion = "dashboard-region(control rectangle)";
style.height = height + "px";
// クリックされた時の画像を前もって読み込んでおく
var img = new Image(imgLeftWidth, height);
img.src = this._imgLeftClickedPath;
img = new Image();
img.src = this._imgMiddleClickedPath;
img = new Image(imgRightWidth, height);
img.src = this._imgRightClickedPath;
// マウスダウンを監視するハンドラを追加
container.addEventListener("mousedown", this._mousedownHandler, true);
}
長いですが、構造は簡単です。最初に渡された画像パスを保存しておきます。次にボタン要素の子となり、以後作られる div 要素の親となる div 要素を作ります。マウスダウンハンドラもこの要素に対して追加されます。
それからしばらくは、プライベート変数作成と、それを使うイベントハンドラ設定です。
その後、先ほど作った div 要素の子として、ボタン各部を作成していきます。右・真ん中・左という順番です。真ん中はラベル表示も兼ねていることに気をつけてください。また、テキスト要素として真ん中の要素を保存しています。右側画像の幅として左側の値が使われているのに注意してください。これはバグで、Mac OS X v10.4.11 のシステム内のファイルでは修正されています。
最後は、マウスダウン時のハンドラを設定しています。これらのハンドラは後でひとつずつ見ています。これでボタン作成はわかったと思います。あとは、こうして作られたボタンがどう動作するかですが、その前にパブリックなメソッドを見ていきます。
つぎは、ボタンを除去するメソッド remove です。
AppleButton.js > AppleButton.remove
AppleButton.prototype.remove = function()
{
var parent = this._container.parentNode;
parent.removeChild(this._container);
}
単にボタン要素から部品をまとめている div 要素を除去しているだけです。
つぎは無効時のボタン表示に使われる画像を設定するメソッドです。
AppleButton.js > AppleButton.setDisabledImages
AppleButton.prototype.setDisabledImages
= function(imgLeftDisabled, imgMiddleDisabled, imgRightDisabled)
{
this._imgLeftDisabledPath = imgLeftDisabled;
this._imgMiddleDisabledPath = imgMiddleDisabled;
this._imgRightDisabledPath = imgRightDisabled;
}
単に画像パスを保存しているだけです。
つぎはボタンの有効・無効を設定するメソッドです。
AppleButton.js > AppleButton.setEnabled
AppleButton.prototype.setEnabled = function(enabled)
{
this.enabled = enabled;
if (enabled)
{
this._container.children[0].style.backgroundImage
= "url(" + this._imgLeftPath + ")";
this._container.children[1].style.backgroundImage
= "url(" + this._imgMiddlePath + ")";
this._container.children[2].style.backgroundImage
= "url(" + this._imgRightPath + ")";
this._container.style.appleDashboardRegion
= "dashboard-region(control rectangle)";
}
else if (this._imgLeftDisabledPath !== undefined)
{
this._container.children[0].style.backgroundImage
= "url(" + this._imgLeftDisabledPath + ")";
this._container.children[1].style.backgroundImage
= "url(" + this._imgMiddleDisabledPath + ")";
this._container.children[2].style.backgroundImage
= "url(" + this._imgRightDisabledPath + ")";
this._container.style.appleDashboardRegion = "none";
}
}
まず有効・無効状態を変数に保存します。それから有効なら、画像を有効状態のものにして、コントロール領域としてヒタイル設定します。無効で、しかも無効画像が設定されていたら、それを設定します。コントロール領域には設定せず、通常の動作を行います。画像が存在しなかった場合は、コントロール領域のままなので、イベント時に設定した独自ハンドラが呼ばれることになります。そのため、ハンドラで有効・無効の状態をチェックしています。画像設定によって、このような動作の違いが起こるので気をつけたほうがいいでしょう。
さて、このクラスの残りはプライベートなハンドラメソッドです。マウス移動ハンドラは _init 内で、単にイベントを伝播しないものとしてその場で実装されていたことに注意してください。残りのものは関数として他の場所に実装されています。
マウスダウンハンドラは、_init の最後でイベントハンドラとして設定されていました。この時、3 番目の引数が true だったので、このハンドラはイベントが起こった時すぐに呼ばれることになることに注意してください。
AppleButton.js > AppleButton._mousedown
AppleButton.prototype._mousedown = function(event)
{
// 無効なら、何も行わない
if (!this.enabled)
{
event.stopPropagation();
event.preventDefault();
return;
}
// 画像をクリックされた状態に変更する
this._setPressed(true);
// 一時的なイベントハンドラを追加する
document.addEventListener("mousemove", this._mousemoveHandler, true);
document.addEventListener("mouseup", this._mouseupHandler, true);
this._container.addEventListener("mouseover", this._mouseoverHandler, true);
this._container.addEventListener("mouseout", this._mouseoutHandler, true);
this._inside = true;
event.stopPropagation();
event.preventDefault();
}
まず無効かどうかをチェックして、無効なら何も行わず戻ります。
つぎに画像をクリック状態へと変更します。これには別の関数を使っています。
AppleButton.js > AppleButton._setPressed
AppleButton.prototype._setPressed = function(pressed)
{
if (pressed)
{
this._container.children[0].style.backgroundImage
= "url(" + this._imgLeftClickedPath + ")";
this._container.children[1].style.backgroundImage
= "url(" + this._imgMiddleClickedPath + ")";
this._container.children[2].style.backgroundImage
= "url(" + this._imgRightClickedPath + ")";
}
else
{
this._container.children[0].style.backgroundImage
= "url(" + this._imgLeftPath + ")";
this._container.children[1].style.backgroundImage
= "url(" + this._imgMiddlePath + ")";
this._container.children[2].style.backgroundImage
= "url(" + this._imgRightPath + ")";
}
}
true が渡された場合、クリックされた画像に、false なら通常画像に設定します。
それから一時的なイベントハンドラを設定しています。その後 _inside を true にし、それ以上のイベントの伝播を防いでいます。
ここで設定されたイベントハンドラを見てみます。移動については、_init 内で実装されていました。ボタンを離した場合は、最終動作となるので最後に説明します。まず上にある場合のハンドラを見てみます。
AppleButton.js > AppleButton._mouseover
AppleButton.prototype._mouseover = function(event)
{
// 画像をクリックした状態へと変更
this._setPressed(true);
this._inside = true;
event.stopPropagation();
event.preventDefault();
}
マウスダウンからコードを除去した形になっています。つぎはマウス退出です。
AppleButton.js > AppleButton._mouseout
AppleButton.prototype._mouseout = function(event)
{
// 画像を通常の状態に変更
this._setPressed(false);
this._inside = false;
event.stopPropagation();
event.preventDefault();
}
_inside を false にしています。これによって、ボタンをクリックした後で、ボタンの外にマウスを動かしてから離せば、クリックをキャンセルできるようになっています。それを示すために、画像も通常の状態に戻します。最後はマウスが離された時のハンドラです。
AppleButton.js > AppleButton._mouseout
AppleButton.prototype._mouseup = function(event)
{
// 画像を通常の状態に変更
this._setPressed(false);
// 一時的なイベント監視を除去
document.removeEventListener("mousemove", this._mousemoveHandler, true);
document.removeEventListener("mouseup", this._mouseupHandler, true);
this._container.removeEventListener("mouseover", this._mouseoverHandler, true);
this._container.removeEventListener("mouseout", this._mouseoutHandler, true);
// ボタン内にある場合だけコールバックを実行
try {
if (this._inside && this.onclick != null)
this.onclick(event);
} catch(ex) {
throw ex;
} finally {
event.stopPropagation();
event.preventDefault();
delete this._inside;
}
}
上で書いたように、ボタン内にある場合だけコントスラクタで変数に保存されていたコールバックを呼び出しています。あとは、ダウンメソッドの逆なのでわかると思います。
これで AppleButton クラスのメソッドは全て解説しました。わかってみると簡単な実装だと思うでしょう。このクラスのサブクラスとして実装されている AppleGlassButton を見てみましょう。まずコンストラクタと継承関係です。
AppleButton.js > AppleGlassButton.AppleGlassButton
function AppleGlassButton(button, text, onclick)
{
/* オブジェクト */
this.textElement = null;
/* 読み書きプロパティ */
this.onclick = onclick;
/* 読みとり専用プロパティ */
this.enabled = true;
this._init(button, text, 23,
"file:///System/Library/WidgetResources/button/glassbuttonleft.png",
"file:///System/Library/WidgetResources/button/glassbuttonleftclicked.png",
10,
"file:///System/Library/WidgetResources/button/glassbuttonmiddle.png",
"file:///System/Library/WidgetResources/button/glassbuttonmiddleclicked.png",
"file:///System/Library/WidgetResources/button/glassbuttonright.png",
"file:///System/Library/WidgetResources/button/glassbuttonrightclicked.png",
10);
var style = this.textElement.style;
style.fontSize = "12px";
style.fontFamily = "Helvetica Neue";
style.color = "white";
style.fontWeight = "bold";
}
// AppleButton から継承
AppleGlassButton.prototype = new AppleButton(null);
プロトタイプを継承しているため、そこに設定されたメソッドが使えます。コンストラクタの引数は、画像がシステムのものを使うため簡単になっています。ボタン要素、表示されるラベル、クリックされた時に呼ばれる関数です。
後の設定は AppleButton クラスを思い出せばほとんど説明は必要ないと思います。
このクラスでは、1 つのメソットだけをオーバーライドしています。
AppleButton.js > AppleGlassButton.AppleGlassButton
// 通常の無効機能をオーバーライド
AppleGlassButton.prototype.setEnabled = function(enabled)
{
this.enabled = enabled;
if (enabled)
{
this._container.children[1].style.color = "white";
this._container.style.appleDashboardRegion
= "dashboard-region(control rectangle)";
}
else
{
this._container.children[1].style.color = "rgb(150,150,150)";
this._container.style.appleDashboardRegion = "none";
}
}
通常の AppleButton と違って、無効画像の設定を反映せず、無効状態を文字色を灰色にすることで示しています。無効の場合、画像設定の有無に関わらず、コントロール領域ではなくなっていることに注意してください。
これで AppleButton.js は終わりです。ファイルを見れば、けっこう長く複雑に見えますが、ひとつひとつ見てみると本当に単純に実装されていることがわかります。
9 AppleInfoButton.js
AppleInfoButton は、画像を設定したり、ユーザーが自由にスタイル変更するのではなく、システム画像を使って、一定の見た目と動作を提供することが目的です。そのため、多くのメソッドがコンストラクタ内の特権メソッドとして実装されています。コンストラクタのソースが長くなるため、特権メソッド部分を抽出して各個に説明することにします。
AppleInfoButton.js > AppleInfoButton
function AppleInfoButton(flipper, front, foregroundStyle, backgroundStyle, onclick)
{
/* 読み書き可能プロパティ */
this.onclick = onclick;
/* 内部 */
this._front = front;
// 親要素
this._flipper = flipper;
// ボタン要素
this._flipLabel = document.createElement("img");
// 画像要素作成
this._flipLabel.width = 13;
this._flipLabel.height = 13;
this._flipLabel.setAttribute("alt", "Info");
// 代替テキスト設定
this._flipCircle = document.createElement("div");
// ラベル周囲の円要素
flipper.appendChild(this._flipCircle);
flipper.appendChild(this._flipLabel);
this._labelshown = false;
// 最初はラベルを表示しない
// For JavaScript event handlers
var _self = this;
// 自身を格納するプライベート変数
// 以下は特権メソッド
... 中略 ...
// スタイルのセットアップ
var style = this._flipLabel.style;
// ラベル部分のスタイル
style.position = "absolute";
style.top = 0;
style.left = 0;
style.opacity = 0;
style = this._flipCircle.style;
// 周囲の円のスタイル
style.position = "absolute";
style.top = 0;
style.left = 0;
style.width = "13px";
style.height = "13px";
this.setCircleOpacity(0.25);
style.visibility = "hidden";
this.setStyle(foregroundStyle, backgroundStyle);
// 前面要素の前面 div へハンドラ設定
this._front.addEventListener("mousemove", this._frontMove, true);
this._front.addEventListener("mouseout", this._frontOutDelay, true);
// ボタン要素へのハンドラ設定
this._flipper.addEventListener("click", this._labelClicked, true);
this._flipper.addEventListener("mouseover", this._labelOver, true);
this._flipper.addEventListener("mouseout", this._labelOut, true);
}
特権メソッド定義部分をはぶくとすっきりして判りやすくなったと思います。まずクリック時のハンドラを保存します。それから内部変数に親要素とボタン要素を代入します。つぎに「i」のラベル部分の img 要素を作成し、その周囲に描かれる円の div 要素を作成し、それぞれをボタン要素の子にします。AppleButton と違って直接の子にされていることに注意してください。そして最初はラベル(すなわちボタン)を表示しないように設定しています。
その後、自身をプラベート変数に格納し、それを扱える特権メソッドを定義しています。この部分のメソッドは後で説明します。
その後で、各部分のスタイルを設定しています。ラベル部分、周囲の円の順で設定します。ここで setCircleOpacity と setStyle はこのファイルで実装されているメソッドです。
AppleInfoButton.js > AppleInfoButton.setCircleOpacity
AppleInfoButton.prototype.setCircleOpacity = function(opacity)
{
this._flipCircle.style.opacity = opacity;
}
単に円の部分のスタイルの不透明度を渡されたものに設定しているだけです。これは外部からアクセスできるようメソッドにされています。コンストラクタではデフォルトで 0.25 という薄い不透明度を使っています。
AppleInfoButton.js > AppleInfoButton.setStyle
AppleInfoButton.prototype.setStyle = function(foregroundStyle, backgroundStyle)
{
this._flipLabel.src = "file:///System/Library/WidgetResources/ibutton/"
+ foregroundStyle + "_i.png";
this._flipCircle.style.background = "url(file:///System/Library/WidgetResources/ibutton/"
+ backgroundStyle + "_rollie.png) no-repeat top left";
}
単に前景色と背景色にしたがってシステム内の画像パスを設定しているだけです。実際にシステムを見てみると、これは「black」もしくは「white」のいずれかを使用するようになっています。これ以外の色を使いたいなら、独自の画像を用意して、この部分のパスを置き換え、システムの Apple クラスを使わないように設定すればいいでしょう。
最後にハンドラを設定しています。前面要素に対して、マウス移動とマウス退出イベントハンドラを設定していることに注意してください。これにより、マウスがウィジェット外にある場合に情報ボタンを表示しないようにしたりしています。
ボタン要素に対しては、マウスクリック、マウスが上に来たとき、マウス退出のイベントハンドラが設定されています。
コンストラクタが理解できたと思うので、まず親要素のイベントハンドラから見てみます。
AppleInfoButton.js > AppleInfoButton._frontMove
this._frontMove = function(event)
{
if (_self._outdelay !== undefined)
// _outdelay があれば
{
clearInterval(_self._outdelay);
// _outdelay タイマー除去
delete _self._outdelay;
// タイマー削除
}
if (_self._labelshown)
// ラベルがすでに表示なら終了
return;
var from = 0.0;
// 見えない状態から
var duration = 500;
// アニメーションの継続時間
if (_self._animation !== undefined)
// アニメーションがすでにあれば
{
from = _self._animation.now;
// 現在の状態から
duration = (new Date).getTime() - _self._animator.startTime;
// 継続時間調整
_self._animator.stop();
// 現在のものは中止
}
_self._labelshown = true;
// ラベル表示状態
var animator = new AppleAnimator(duration, 13);
// アニメーター作成
animator.oncomplete = _self._animationComplete;
// 終了ハンドラ設定
_self._animator = animator;
// アニメーター保存
_self._animation = new AppleAnimation(from, 1.0, _self._updateOpacity);
// アニメーション作成
animator.addAnimation(_self._animation);
// 作成アニメーション追加
animator.start();
// アニメーション開始
}
マウスが進入したとき、アニメーション表示で情報ボタンを表示します。そのための設定が行われます。
まず、_outdelay が存在すれば、タイマーを削除します。つぎに from と duration 値を初期化し、現在アニメーション中なら、from と duration を調整しなおします。それからラベル表示状態であるようにします。最後にアニメーターとアニメーションを作成し、情報ボタン表示アニメーションを開始します。ここでアニメーションのハンドラとして呼ばれているのが _updateOpacity てす。
AppleInfoButton.js > AppleInfoButton._updateOpacity
this._updateOpacity = function(animation, now, first, done)
{
_self._flipLabel.style.opacity = now;
}
単純に情報ボタンのラベルの不透明度をアニメーションの現在値にしています。また、アニメーション完了時のハンドラとして、_animationComplete が設定されていました。
AppleInfoButton.js > AppleInfoButton._animationComplete
this._animationComplete = function()
{
delete _self._animation;
delete _self._animator;
}
単にアニメーターとアニメーションを削除しているだけです。
さて、つぎはマウス退出イベントです。
AppleInfoButton.js > AppleInfoButton._frontOutDelay
this._frontOutDelay = function(event)
{
_self._outdelay = setInterval(_self._frontOut, 0, _self);
}
単に遅れなしで _frontOut を実行するタイマーを _outdelay に設定しています。ただ、タイマーのような繰り返し作業を使うのもどうかと思いますが、これは Mac OS X v10.4.11 で修正されています。_frontOut は以下のようなものです。
AppleInfoButton.js > AppleInfoButton._frontOut
this._frontOut = function(_self)
{
if (_self._outdelay !== undefined)
// _outdelay があれば
{
clearInterval(_self._outdelay);
// _outdelay タイマー除去
delete _self._outdelay;
// タイマー削除
}
if (!_self._labelshown)
// ラベル表示されていないなら終了
return;
var from = 1.0;
// 見える状態から
var duration = 500;
// アニメーションの継続時間
if (_self._animation !== undefined)
// アニメーションがすでにあれば
{
from = _self._animation.now;
// 現在の状態から
duration = (new Date).getTime() - _self._animator.startTime;
// 継続時間調整
_self._animator.stop();
// 現在のものは中止
}
var animator = new AppleAnimator(duration, 13);
// アニメーター作成
animator.oncomplete = _self._animationComplete;
// 終了ハンドラ設定
_self._animator = animator;
// アニメーター保存
_self._animation = new AppleAnimation(from, 0.0, _self._updateOpacity);
// アニメーション作成
animator.addAnimation(_self._animation);
// 作成アニメーション追加
animator.start();
// アニメーション開始
_self._labelshown = false;
// ラベル表示なし
}
移動の場合とほとんど同じなので、説明しなくてもわかると思います。
では、ラベルのハンドラに移ります。まずマウス進入イベントです。
AppleInfoButton.js > AppleInfoButton._labelOver
this._labelOver = function(event)
{
_self._flipCircle.style.visibility = "visible";
}
ラベルのまわりの円の部分を見えるようにしています。逆の退出イベントを見てみます。
AppleInfoButton.js > AppleInfoButton._labelOut
this._labelOut = function(event)
{
_self._flipCircle.style.visibility = "hidden";
}
まわりの円の部分を隠しています。では、クリックされた時のハンドラを見てみます。
AppleInfoButton.js > AppleInfoButton._labelOut
this._labelClicked = function(event)
{
_self._flipCircle.style.visibility = "hidden";
try {
if (_self.onclick != null)
// ハンドラが設定されていたら
_self.onclick(event);
} catch(ex) {
throw ex;
} finally {
event.stopPropagation();
event.preventDefault();
}
}
最初に円の部分を見えなくします。次にハンドラが設定されていたらそれを実行し、最後にイベントの伝播を防いでいます。例外処理が使われているのは、ここで呼ばれるハンドラがユーザーが定義するもので、Apple クラス内で処理がうまくいくことを保証できないからでしょう。これでイベントハンドラは終わりです。マウスにともなうボタンの表示の変化などが理解できたと思います。
あとは外部からボタンを除去するときに呼ばれる remove が残っているだけです。
AppleInfoButton.js > AppleInfoButton._labelOut
AppleInfoButton.prototype.remove = function()
{
this._front.removeEventListener("mousemove", this._frontMove, true);
this._front.removeEventListener("mouseout", this._frontOutDelay, true);
this._flipper.removeEventListener("click", this._labelClicked, true);
this._flipper.removeEventListener("mouseover", this._labelOver, true);
this._flipper.removeEventListener("mouseout", this._labelOut, true);
var parent = this._flipLabel.parentNode;
parent.removeChild(this._flipCircle);
parent.removeChild(this._flipLabel);
}
まずコンストラクタで追加したイベントハンドラを除去しています。それから親要素を取得し、そこから円の部分とラベルの部分を除去しています。
これで情報ボタンの実装についてわかったと思います。細かい所を見れば、もう少し他の実装もできそうな気がしますが、それについては触れないことにします。
10 その後の修正
10.1 Mac OS X v10.4.11
Mac OS X v10.4.11 において、Apple クラスが修正されています。そのため、ここでの解説で使っているものと少し違っています。変更点を調べてみます。これはもっと前のバージョンで修正されているのだと思いますが、正確にどのバージョンからかは不明です。
まず、AppleButton.js ですが、これはバグ修正です。説明のときにも触れました。
サンプルの AppleButton.js > AppleButton._init
AppleButton.prototype._init = function(button, text, height,
imgLeft, imgLeftClicked, imgLeftWidth,
imgMiddle, imgMiddleClicked,
imgRight, imgRightClicked, imgRightWidth)
{
...
// 右側部分作成
...
style.width = imgLeftWidth + "px";
...
}
Mac OS X v10.4.11 の AppleButton.js > AppleButton._init
AppleButton.prototype._init = function(button, text, height,
imgLeft, imgLeftClicked, imgLeftWidth,
imgMiddle, imgMiddleClicked,
imgRight, imgRightClicked, imgRightWidth)
{
...
// 右側部分作成
...
style.width = imgRightWidth + "px";
...
}
ちゃんと右側の画像幅で作られるように修正されています。
AppleInfoButton.js は、いくつかの場所が修正されています。まず _outdelay 関連です。
サンプルの AppleInfoButton.js > AppleInfoButton._frontOutDelay
this._frontOutDelay = function(event)
{
_self._outdelay = setInterval(_self._frontOut, 0, _self);
}
Mac OS X v10.4.11 の AppleInfoButton.js > AppleInfoButton._frontOutDelay
this._frontOutDelay = function(event)
{
if (_self._outdelay === undefined)
{
_self._outdelay = setTimeout(_self._frontOut, 0, _self);
}
}
_outdelay が未定義の場合だけ、setTimeout で一度だけ関数を呼び出しています。これにともなって、他のハンドラも修正されています。
サンプルの AppleInfoButton.js > AppleInfoButton._frontMove
this._frontMove = function(event)
{
if (_self._outdelay !== undefined)
// _outdelay があれば
{
clearInterval(_self._outdelay);
// _outdelay タイマー除去
delete _self._outdelay;
// タイマー削除
}
...
}
Mac OS X v10.4.11 の AppleInfoButton.js > AppleInfoButton._frontOutDelay
this._frontMove = function(event)
{
if (_self._outdelay !== undefined)
// _outdelay があれば
{
clearTimeout(_self._outdelay);
// _outdelay タイマー除去
delete _self._outdelay;
// タイマー削除
}
...
}
frontOut も全く同様に修正されています。
さらに、イベントハンドラが追加されています。
サンプルの AppleInfoButton.js > AppleInfoButton
this._front.addEventListener("mousemove", this._frontMove, true);
this._front.addEventListener("mouseout", this._frontOutDelay, true);
this._flipper.addEventListener("click", this._labelClicked, true);
this._flipper.addEventListener("mouseover", this._labelOver, true);
Mac OS X v10.4.11 の AppleInfoButton.js > AppleInfoButton
this._front.addEventListener("mouseout", this._frontOutDelay, true);
// 一時的なもの
this._flipper.addEventListener("mousedown", this._tempLabelDown, true);
this._flipper.setAttribute
("onclick", "event.stopPropagation(); event.preventDefault();");
// 後でこれに切り替え
// this._flipper.addEventListener("click", this._labelClicked, true);
this._flipper.addEventListener("mouseover", this._labelOver, true);
クリックハンドラがコメントアウトされ、マウスダウンハンドラに切り替えられ、クリックハンドラがイベントを伝播しないものにされています。その結果、_tempLabelDown というメソッドが追加されています。これに対応して、remove のイベントハンドラ除去部分も修正されていますが、すぐわかると思うのでここには示しません。
Mac OS X v10.4.11 の AppleInfoButton.js > AppleInfoButton._tempLabelDown
this._tempLabelDown = function(event)
{
document.addEventListener("mouseup", _self._tempDocUp, true);
event.stopPropagation();
event.preventDefault();
}
マウスボタンが離された場合のハンドラを追加し、イベントの伝播を防いでいるだけです。ここで追加されたハンドラは以下のようになっています。
Mac OS X v10.4.11 の AppleInfoButton.js > AppleInfoButton._tempDocUp
this._tempDocUp = function(event)
{
document.removeEventListener("mouseup", _self._tempDocUp, true);
// ラベル上にあった場合
if (_self._tempMouseOver !== undefined)
{
delete _self._tempMouseOver;
_self._labelClicked(event);
}
}
一時的なイベントハンドラである自身を除去します。次に _tempMouseOver が未定義でなければ、それを削除し、ラベルがクリックされた場合のイベントハンドラを呼び出します。
ここで _tempMouseOver がありましたが、これも追加されています。
サンプルの AppleInfoButton.js > AppleInfoButton._labelOver
this._labelOver = function(event)
{
_self._flipCircle.style.visibility = "visible";
}
Mac OS X v10.4.11 の AppleInfoButton.js > AppleInfoButton._labelOver
this._labelOver = function(event)
{
_self._tempMouseOver = true; // 後で除去
_self._flipCircle.style.visibility = "visible";
}
マウスが上にきた時のハンドラで値を設定しています。退出の時は除去します。
サンプルの AppleInfoButton.js > AppleInfoButton._labelOut
this._labelOut = function(event)
{
_self._flipCircle.style.visibility = "hidden";
}
Mac OS X v10.4.11 の AppleInfoButton.js > AppleInfoButton._labelOut
this._labelOut = function(event)
{
delete _self._tempMouseOver; // 後で除去
_self._flipCircle.style.visibility = "hidden";
}
10.2 Mac OS X v10.5.1
Mac OS X v10.5.1 でも、さらに修正されています。
AppleButton.js では、垂直揃えがスタイルとして設定されています。3か所修正されていますが1つだけ示します。
Mac OS X v10.4.11 の AppleButton.js > AppleButton._init
AppleButton.prototype._init = function(button, text, height,
imgLeft, imgLeftClicked, imgLeftWidth,
imgMiddle, imgMiddleClicked,
imgRight, imgRightClicked, imgRightWidth)
{
...
// 左側部分作成
...
style.display = "inline-block";
style.background = "url(" + this._imgLeftPath + ") no-repeat top left";
...
}
Mac OS X v10.5.1 の AppleButton.js > AppleButton._init
AppleButton.prototype._init = function(button, text, height,
imgLeft, imgLeftClicked, imgLeftWidth,
imgMiddle, imgMiddleClicked,
imgRight, imgRightClicked, imgRightWidth)
{
...
// 左側部分作成
...
style.display = "inline-block";
style.verticalAlign = "bottom";
style.background = "url(" + this._imgLeftPath + ") no-repeat top left";
...
}
AppleInfoButton.js は、v10.4.11 と同じです。
管理人:神吉 秀典 E-mail: