# レガシースキンAPI

> 古いチャットルームが使うレガシースキン(basic)のroom.skin API — jQuery DOM、メニューバー、ポップアップ、ニックネームメニュー — の一覧と、基本スキンへの移行対応表です。

レガシースキン(`basic`。管理ページでは「レガシースキン(旧バージョン)」と表示)は、古いチャットルームが使っているスキンです。新しいチャットルームは基本スキン(`basic-v2`)で作成され、新機能は基本スキンにだけ追加されます。このページは、まだレガシースキンを使っているチャットルーム向けの `room.skin` 一覧です。基本スキンのAPIは[スキンUI API](/ja/doc/3.4)をご覧ください。

ページスクリプトの `U.chat()`、イベント、`room.action`、チャンネル(`room.plugin`)、サーバーAPIは両方のスキンで同じです。違うのは `room.skin` だけです。

## どのスキンかを確認する {#detect}

`room.skin` のメンバーはスキンが画面を作ったあとに生まれるため、`after.create` の中で確認します。

```js
U.chat('my-room').on('after.create', function (room) {
	if (room.skin.menubar) {
		// レガシースキン
	} else if (room.skin.menu) {
		// 基本スキン
	}
});
```

`userMenu` は両方のスキンにあるので、区別には使わないでください。スキンは管理ページの**スキン**メニューか、`<u-chat skin="basic-v2">` 属性で変更できます。会員連携のようにスキンと関係ない機能は、変更してもそのまま動作します。

## 原則 {#principles}

- レガシースキンは、チャットルームiframe内のDOMをjQuery(1.12)で直接操作します。スキンのHTML構造やクラス名は決まった契約ではなく、手を加えた画面はスキンが変わると崩れることがあります。
- 登録はすべて `after.create` の中で行います。それより前には `room.skin.arguments` と `room.skin.messages` しかありません。
- uchatのロゴや情報表示を隠したり消したりしないでください。

## room.skin一覧 {#members}

| 名前 | 説明 |
| --- | --- |
| `room.skin.arguments` | スキンの設定値(管理ページのスキン設定 + `skin_arguments` 属性 + グローバルの `Uchat_argument`)。色の既定値はスキンが補います |
| `room.skin.messages` | スキン画面の文言(現在の言語) |
| `room.skin.window` | チャットルームiframeの `window`。jQueryは `room.skin.window.$` |
| `room.skin.document` | チャットルームiframeの `document` |
| `room.skin.dom` | 主要な領域のjQueryオブジェクト。下の[dom](#dom) |
| `room.skin.menubar` | 入力欄の下のメニューバー。下の[menubar](#menubar) |
| `room.skin.popup` | ポップアップ(通知・確認・入力・選択)。下の[popup](#popup) |
| `room.skin.userMenu` | ニックネームをクリックすると出るメニュー。下の[userMenu](#user-menu) |
| `room.skin.scrollDown(force)` | 会話を一番下までスクロールします。`force` が真でなければ、ユーザーが上にスクロールしている間はスクロールしません |

## dom {#dom}

すべてチャットルームiframe内の要素のjQueryオブジェクトです。

| キー | 領域 |
| --- | --- |
| `wrap` | チャットルーム全体(`.wrap`) |
| `top` | 上部のタイトル領域(`.top`) |
| `middle` | 会話とユーザー一覧の領域(`.middle`) |
| `content` | 会話のスクロール領域(`.contentWrap`) |
| `content_target` | 会話の行が追加される要素(`.content`) |
| `userList` | ユーザー一覧のスクロール領域(`.userListWrap`) |
| `contour` | ユーザー一覧と会話の間の区切り線(`.contour`) |
| `bottom` | 下部の入力領域(`.bottom`) |
| `inputContent` | 入力欄(`.inputContent`) |
| `menubar` | メニューバー(`.menuBar`) |
| `latest_chat` | 上にスクロールしたときに出る最新の会話の通知(`.latest_chat`) |
| `setting`, `settingBG` | 設定ウィンドウとその背景(`.settingContent`, `.settingBackground`) |

```js
U.chat('my-room').on('after.create', function (room) {
	var $ = room.skin.window.$;
	var banner = $('<div/>').text('今夜8時にイベントがあります!').css({ padding: '6px', background: '#fff4cc' });
	room.skin.dom.middle.before(banner);
	$(room.skin.window).resize(); // 高さが変わったらスキンに再配置させる
});
```

要素を追加して高さが変わったら、`$(room.skin.window).resize()` を呼んでスキンに会話領域のサイズを再計算させます。ユーザーの入力は `.text()` で入れ、`.html()` には入れないでください。

## menubar {#menubar}

入力欄の下のメニューバーにボタンを追加します。

```js
U.chat('my-room').on('after.create', function (room) {
	room.skin.menubar.add({
		id: 'hello',
		title: 'あいさつを送る',
		html: '<div>あいさつ</div>',
		condition: function (room, item) {
			return room.my.auth >= 1; // false なら表示しない
		},
		onClick: function (room, item) {
			room.action.send('こんにちは!');
		},
	});
});
```

| キー | 説明 |
| --- | --- |
| `id` | 必須。重複すると登録されません。ボタンに `menubar-ID` クラスが付きます |
| `title` | マウスを乗せたときの説明 |
| `html` | ボタンの内容(HTML文字列)。なければ空の `<div>` |
| `icon` | 背景画像のURL |
| `condition(room, item)` | 厳密に `false` を返すと描画しません |
| `onClick(room, item)` | クリックしたとき。`item.event` がクリックイベントです |
| `beforeLoad(room, item)`, `afterLoad(room, item)` | 描画の前後。`beforeLoad` が `false` なら描画しません |
| `beforeUnload()`, `afterUnload()` | 削除の前後 |

`item` は `add` に渡したオブジェクトそのもので、描画されたボタンのjQueryオブジェクトが `item.layout` に入ります。

| 関数 | 説明 |
| --- | --- |
| `add(item)` | 登録します。成功すると `true`、IDがないか重複していれば `false` |
| `refresh()` | 登録済みのボタンを `condition` に従って描画します |
| `remove(id)` | 登録を削除します |
| `hide()`, `show()`, `toggle()` | メニューバー全体の非表示・表示・切り替え |
| `list` | 登録された項目の配列 |

- **`add` だけでは画面に表示されません。** スキンは入室のたびに `refresh()` を呼ぶため、`after.create` で登録すれば入室時に表示されます。入室後に登録した場合は `room.skin.menubar.refresh()` を自分で呼んでください。
- `condition` は `refresh()` のときにだけ再計算されます。すでに描画されたボタンの `condition` が `false` になると登録が削除され、あとで再び真になっても戻りません。権限に応じてボタンを表示・非表示にしたい場合は、`condition` の代わりに `afterLoad` で受け取った `item.layout` を `toggle()` してください。
- `remove(id)` は、IDが数値でないと描画済みのボタンを画面から消せません。ボタンを消すときは `item.layout.remove()` も一緒に呼んでください。
- 既定のボタンのIDは `scroll`、`bold`、`picture`、`call_admin` などです。

## popup {#popup}

チャットルーム内にポップアップを表示します。ポップアップは一度に1つだけ表示され、残りは順番に待ちます。すべての関数がポップアップ番号を返し、`close(番号)` で閉じられます。結果はPromiseではなくコールバックで受け取ります。

```js
var popup = room.skin.popup;

popup.confirm('じゃんけん', '挑戦を受けますか?', function (ok) {
	// OK なら true、キャンセルなら false、外側をクリックして閉じると null
});

popup.prompt('ニックネーム', '新しいニックネームを入力してください。', '既定値', function (value) {
	// 入力した文字列、キャンセルすると null
});

popup.select('選択', 'ひとつ選んでください。', { r: 'グー', p: 'パー', s: 'チョキ' }, function (key) {
	// 選んだキー(文字列)、キャンセルすると null
});

var id = popup.alert('お知らせ', '10秒後に閉じます。');
setTimeout(function () { popup.close(id); }, 10000);
```

| 関数 | コールバックが受け取る値 |
| --- | --- |
| `alert(title, html, onOpen, onClose)` | `onOpen(layer, pb, pu)` は**開くときに**呼ばれ、閉じるときは `onClose()` が呼ばれます。`html` はHTMLとして入るため、ユーザーの値はエスケープしてください |
| `confirm(title, subtitle, fn)` | `true`、`false`、外側をクリックすると `null` |
| `prompt(title, subtitle, value, fn)` | 入力した文字列、キャンセルすると `null` |
| `password(title, subtitle, value, fn)` | `prompt` と同じで、入力が伏せ字になります |
| `select(title, subtitle, list, fn)` | `list`(オブジェクトまたは配列)のキー、キャンセルすると `null` |
| `add(html, type, fn(layer, pb, pu), after)` | 自作のポップアップ。`layer` は `{ close(), centering() }`、`pb` は背景、`pu` はポップアップ本体(jQuery)。`after()` は閉じるとき |
| `close([番号])` | そのポップアップを閉じるか、待ち行列から外します。番号を省くと、いま表示中のポップアップを閉じます |
| `closeByType(type)` | `add` に渡した `type` が同じポップアップをすべて閉じます |

- 入力ポップアップでEnterを押すとOKと同じです。
- ブラウザのウィンドウがフォーカスを失うと、いま表示中のポップアップは外側をクリックしたときと同じように閉じます(`confirm`・`prompt`・`select` は `null`)。別のウィンドウを開いて戻ってくる流れでは、結果が `null` になりうる前提でコードを書いてください。

## userMenu {#user-menu}

ニックネームをクリックすると出るメニューに項目を追加します。メニューはクリックのたびに作り直されるため、`condition` は毎回再計算されます。

```js
room.skin.userMenu.add({
	id: 'profile',
	text: 'プロフィールを見る',
	condition: function (room, data) {
		return data.target !== room.my.nick; // 自分のニックネームでは非表示
	},
	onClick: function (room, data) {
		var user = room.user.get(data.target);
		if (user && user.id) window.open('/profile/' + encodeURIComponent(user.id));
	},
});
```

| キー | 説明 |
| --- | --- |
| `id` | 必須。重複すると登録されません(`false`) |
| `text` | 項目の文字列 |
| `condition(room, data)` | 厳密に `false` を返すと非表示になります。`data.target` はクリックされた**ニックネームの文字列**です |
| `onClick(room, data)` | クリックしたとき。`data` は `{ event, id, target, messageUid }` |

| 関数 | 説明 |
| --- | --- |
| `add(item)` | 項目を1つ追加します |
| `group(left, right)` | 2つの項目を1行に並べます |
| `hr([{ condition }])` | 区切り線。IDは常に `hr` なので、`remove('hr')` は区切り線をすべて削除します |
| `remove(id)` | そのIDの項目をすべて削除します(既定の項目を含む) |
| `list` | 登録された項目の配列 |

既定の項目のIDは `info`、`whisper`、`call`、`ignore`、`un_ignore`、`report`、`invite`、`individual`、`mute`、`unmute`、`ban`、`ip`、`op`、`deop`、`freeze`、`unfreeze`、`clearlog`、`removeChat`、`logout` などです。既定の項目を隠すには `remove(id)` を呼んでください。メニューを隠しても、権限のチェックはサーバーが行います。

## レガシー専用のイベント {#legacy-events}

`before.user.list`、`after.user.list`(`data` は `{ list }`)は、レガシースキンがユーザー一覧を開いて全体の一覧を受け取るときに発生します。基本スキンはユーザー一覧を分割して受け取るため、このイベントは発生しません。そのほかのイベントは[イベント](/ja/doc/3.3)と同じです。

## 基本スキンへの移行 {#migrate}

| やりたいこと | レガシースキン | 基本スキン |
| --- | --- | --- |
| ボタンを追加 | `menubar.add({ id, title, html, condition(room, item), onClick(room, item) })` | `menu.add({ id, label, title, condition(ctx), onSelect(ctx) })` |
| ボタンを再計算 | `menubar.refresh()` | `menu.resolver.refresh()` |
| ニックネームメニュー | `userMenu.add({ id, text, onClick(room, data) })`、`data.target` はニックネーム | `userMenu.add({ id, label, onSelect(ctx) })`、`ctx.target` はユーザーオブジェクト |
| 既定の項目を隠す | `userMenu.remove('ignore')` | `userMenu.resolver.add(...)` で `skin.ignore` を除外 |
| ポップアップ | `popup.confirm(t, s, fn)` などのコールバック | `dialog.confirm(t, s)` などのPromise |
| 画面に領域を入れる | `dom.middle.before(...)` などのDOM直接操作 | `mount.messageList({ position: 'before', mount(host) })` など |
| 会話の上に重ねる | `dom.content.append(...)` + CSS | `mount.overlay({ layer, scope, mount(host) })` |
| ニックネーム・メッセージの装飾 | `window.$` で `<style>` を挿入 | `nickname.resolver`、`message.resolver` |
| 一番下へ | `scrollDown(force)` | `scrollToBottom(force)` |

基本スキンでは、スキンのDOM、jQuery、`<style>` の挿入はサポートしていません。移行後の使い方は[スキンUIを拡張する](/ja/doc/2.3)からご覧ください。
