@zonuexe/techbook-mcp 0.2.2 → 0.2.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/.claude/settings.local.json +3 -1
  2. package/CHANGELOG.md +14 -1
  3. package/dist/adapters/calil.d.ts +10 -0
  4. package/dist/adapters/calil.d.ts.map +1 -0
  5. package/dist/adapters/calil.js +45 -0
  6. package/dist/adapters/calil.js.map +1 -0
  7. package/dist/adapters/openbd.d.ts +57 -0
  8. package/dist/adapters/openbd.d.ts.map +1 -0
  9. package/dist/adapters/openbd.js +87 -0
  10. package/dist/adapters/openbd.js.map +1 -0
  11. package/dist/adapters/publishers/tatsu-zine.d.ts.map +1 -1
  12. package/dist/adapters/publishers/tatsu-zine.js +6 -18
  13. package/dist/adapters/publishers/tatsu-zine.js.map +1 -1
  14. package/dist/application/get-book-by-isbn.d.ts +12 -0
  15. package/dist/application/get-book-by-isbn.d.ts.map +1 -0
  16. package/dist/application/get-book-by-isbn.js +42 -0
  17. package/dist/application/get-book-by-isbn.js.map +1 -0
  18. package/dist/application/get-book-detail.d.ts.map +1 -1
  19. package/dist/application/get-book-detail.js +16 -1
  20. package/dist/application/get-book-detail.js.map +1 -1
  21. package/dist/application/search-books.d.ts.map +1 -1
  22. package/dist/application/search-books.js +20 -0
  23. package/dist/application/search-books.js.map +1 -1
  24. package/dist/mcp/server.d.ts.map +1 -1
  25. package/dist/mcp/server.js +10 -0
  26. package/dist/mcp/server.js.map +1 -1
  27. package/dist/mcp/tools.d.ts +13 -0
  28. package/dist/mcp/tools.d.ts.map +1 -1
  29. package/dist/mcp/tools.js +16 -0
  30. package/dist/mcp/tools.js.map +1 -1
  31. package/package.json +1 -1
  32. package/src/adapters/calil.ts +57 -0
  33. package/src/adapters/openbd.ts +142 -0
  34. package/src/adapters/publishers/tatsu-zine.ts +7 -19
  35. package/src/application/get-book-by-isbn.ts +50 -0
  36. package/src/application/get-book-detail.ts +17 -1
  37. package/src/application/search-books.ts +20 -0
  38. package/src/mcp/server.ts +10 -0
  39. package/src/mcp/tools.ts +17 -0
  40. package/tests/fixtures/calil-book.html +987 -0
  41. package/tests/fixtures/openbd-response.json +110 -0
  42. package/tests/fixtures/tatsu-zine-detail-free.html +14 -12
  43. package/tests/unit/adapters/calil.test.ts +69 -0
  44. package/tests/unit/adapters/openbd.test.ts +185 -0
  45. package/tests/unit/application/get-book-by-isbn.test.ts +176 -0
@@ -0,0 +1,987 @@
1
+ <!DOCTYPE html>
2
+ <html lang="ja">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>PHPを使おう: PHPで広がるWebビジネス展開 | カーリル</title>
7
+
8
+ <meta name="theme-color" content="#00A1EA">
9
+ <meta name="application-name" content="カーリル" />
10
+ <meta name="apple-mobile-web-app-title" content="カーリル">
11
+ <link rel="icon" type="image/png" href="/favicon-96x96.png" sizes="96x96">
12
+ <link rel="icon" type="image/svg+xml" href="/favicon.svg">
13
+ <link rel="shortcut icon" href="/favicon.ico">
14
+ <link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
15
+ <link rel="manifest" href="/site.webmanifest">
16
+
17
+ <link rel="search" type="application/opensearchdescription+xml" href="https://calil.jp/public/search.xml" title="カーリル" />
18
+ <link rel="alternate" type="application/rss+xml" title="カーリルのブログ" href="https://blog.calil.jp/feed" />
19
+ <link rel="stylesheet" href="/public/web-html/css/index.css">
20
+ <style type="text/css" media="print">
21
+ #menu, #menu_sp, .navigation {
22
+ display: none;
23
+ }
24
+ #next, #next * {
25
+ -webkit-print-color-adjust: exact;
26
+ }
27
+ </style>
28
+ <link rel="canonical" href="https://calil.jp/book/4901676032" />
29
+ <meta name="description" content="「PHPを使おう: PHPで広がるWebビジネス展開」を図書館から検索。カーリルは複数の図書館からまとめて蔵書検索ができるサービスです。">
30
+ <meta name="keywords"
31
+ content="PHPを使おう: PHPで広がるWebビジネス展開,図書館,蔵書検索,4901676032,WebビジネスPHP研究部会" />
32
+ <meta name="twitter:card" content="summary">
33
+ <meta name="twitter:site" content="@caliljp" />
34
+ <meta name="twitter:title" content="「PHPを使おう: PHPで広がるWebビジネス展開」を図書館から検索。" />
35
+ <meta name="twitter:description" content="カーリルは複数の図書館からまとめて蔵書検索ができるサービスです。" />
36
+ <meta name="twitter:domain" content="calil.jp">
37
+ <meta name="twitter:url" content="https://calil.jp/book/4901676032" />
38
+ <meta name="twitter:image:src"
39
+ content="https://m.media-amazon.com/images/I/5103GGFMD3L._SL500_.jpg" />
40
+ <meta property="fb:app_id" content="270660792981320" />
41
+ <meta property="og:title" content="「PHPを使おう: PHPで広がるWebビジネス展開」を図書館から検索。" />
42
+ <meta property="og:type" content="books.book" />
43
+ <meta property="og:url" content="https://calil.jp/book/4901676032" />
44
+ <meta property="og:image"
45
+ content="https://m.media-amazon.com/images/I/5103GGFMD3L._SL500_.jpg" />
46
+ <meta property="og:site_name" content="カーリル" />
47
+ <meta property="og:description" content="カーリルは全国7000以上の図書館から書籍とその貸し出し状況を簡単に横断検索できるサービスです。" />
48
+ <meta property="books:isbn" content="4901676032" />
49
+ <!-- Google tag (gtag.js) -->
50
+ <script async src="https://www.googletagmanager.com/gtag/js?id=G-S0SYRD0CEW"></script>
51
+ <script>
52
+ var dnt = navigator.doNotTrack || window.doNotTrack || navigator.msDoNotTrack;
53
+ if (dnt!=='1' && dnt!=='yes') {
54
+ window.dataLayer = window.dataLayer || [];
55
+ function gtag(){dataLayer.push(arguments);}
56
+ gtag('js', new Date());
57
+
58
+ gtag('config', 'G-S0SYRD0CEW');
59
+ } else {
60
+ console.log('Do-Not-Track, not loading analytics.');
61
+ window.gtag = function() {};
62
+ }
63
+ </script>
64
+
65
+ </head>
66
+ <body>
67
+
68
+
69
+ <div id="menu">
70
+ <div class="container">
71
+ <a href="/">
72
+ <img src="/public/web-html/assets/home.svg" title="ホーム">
73
+ </a>
74
+ <a href="/popular/">今話題の本</a><a href="/library/">図書館マップ</a><a href="/recipe">本のレシピ</a><a class="large" href="/list">読みたいリスト</a>
75
+ <div class="dropdown">
76
+ <button aria-disabled="true" onclick="event.target.focus();">もっと見る<span class="caret" aria-hidden="true">&#9660;</span></button>
77
+ <div class="menu" role="menubar">
78
+ <a tabindex="0" class="small" href="/list">読みたいリスト</a>
79
+ <a tabindex="0" href="/review/">書評</a>
80
+ <a tabindex="0" href="/local/">カーリルローカル</a>
81
+ <a tabindex="0" href="/touch/">カーリルタッチ</a>
82
+ <a tabindex="0" href="/stamp">図書館スタンプラリー</a>
83
+ </div>
84
+ </div>
85
+ <div class="right">
86
+
87
+ <a href="/login?redirect=/book/4901676032">ログイン・新規登録</a>
88
+ <div class="dropdown">
89
+ <button aria-disabled="true" onclick="event.target.focus();">設定<span class="caret">&#9660;</span>
90
+ </button>
91
+ <div class="menu" role="menubar" aria-label="設定">
92
+ <a tabindex="0" href="/settings">お気に入り図書館</a>
93
+ <a tabindex="0" href="/profile/source">高度な検索設定</a>
94
+
95
+ <a tabindex="0" href="/login?redirect=/book/4901676032">ログイン</a>
96
+ </div>
97
+ </div>
98
+ </div>
99
+ </div>
100
+ </div>
101
+ <div id="menu_sp" role="menu">
102
+ <div class="container">
103
+ <a class="logo" href="/">
104
+ <img src="/public/web-html/assets/logo-sp.svg" title="カーリル">
105
+ </a>
106
+ <a class="login" href="/login?redirect=/book/4901676032">ログイン</a>
107
+ <a class="setting" href="/settings">図書館の設定</a>
108
+ <a href="/popular/">今話題の本</a>
109
+ <a href="/library/">図書館マップ</a>
110
+ <a href="/recipe">本のレシピ</a>
111
+ <a href="/list">読みたいリスト</a>
112
+ <a href="/review/">書評</a>
113
+ <a href="/local/">ローカル</a>
114
+ <a href="/stamp">スタンプラリー</a>
115
+ </div>
116
+ </div>
117
+ <nav class="navigation">
118
+ <button class="menu_icon" aria-label="メニュー" aria-haspopup="true" aria-controls="menu_sp"></button>
119
+
120
+ <button class="search_icon" aria-label="検索フォームの表示">
121
+
122
+ </nav>
123
+ <div id="next" class="page_hide">
124
+ <div class="container">
125
+ <a class="logo" href="/" title="カーリルのホームに戻る">
126
+ <picture>
127
+ <source media="(max-width: 1024px)" srcset="/public/web-html/assets/logo-mini.svg">
128
+ <img src="/public/web-html/assets/logo.svg" alt="日本最大の図書館蔵書検索・カーリル">
129
+ </picture>
130
+ </a>
131
+ <form action="/search" method="get"><input name="q" type="search" value="" placeholder="" autocomplete="off" autofocus="true"><button type="submit">さがす</button></form>
132
+ </div>
133
+ </div>
134
+ <div id="book">
135
+ <style>
136
+ @media all and (display-mode: standalone) {
137
+ #book .back {
138
+ display: block;
139
+ }
140
+ }
141
+ </style>
142
+ <div class="back"><a href="javascript:history.back();">« 前にもどる</a></div>
143
+ <div class="container">
144
+ <main>
145
+ <div class="left">
146
+ <a class="cover" href="https://www.amazon.co.jp/dp/4901676032?tag=calil-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" target="_blank">
147
+ <img src="https://m.media-amazon.com/images/I/5103GGFMD3L._SL500_.jpg" alt="" itemprop="image" />
148
+ </a>
149
+ <div itemprop="offers" itemscope itemtype="http://schema.org/Offer">
150
+ <span itemprop="price">
151
+ <a class="amazon" target="_blank" href="https://www.amazon.co.jp/dp/4901676032?tag=calil-22&amp;linkCode=ogi&amp;th=1&amp;psc=1" title="amazon.co.jpで購入"></a>
152
+ <span>(2026/04/13 22:39:15時点)</span>
153
+ </span>
154
+ </div>
155
+ </div>
156
+ <div class="right">
157
+ <div class="findbox">
158
+ <h3>近くの図書館から探してみよう</h3>
159
+ <p>カーリルは全国の図書館から本を検索できるサービスです</p>
160
+ <button class="geo" onclick="calil.geo.getGeoLib()" name="" value="">
161
+ <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 512 512">
162
+ <path
163
+ d="M444.52 3.52L28.74 195.42c-47.97 22.39-31.98 92.75 19.19 92.75h175.91v175.91c0 51.17 70.36 67.17 92.75 19.19l191.9-415.78c15.99-38.39-25.59-79.97-63.97-63.97z">
164
+ </path>
165
+ </svg>現在位置から探す
166
+ </button>
167
+ <p class="library_search"><a href="/settings">
168
+ <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 512 512" width="15" height="14">
169
+ <path
170
+ d="M505 442.7L405.3 343c-4.5-4.5-10.6-7-17-7H372c27.6-35.3 44-79.7 44-128C416 93.1 322.9 0 208 0S0 93.1 0 208s93.1 208 208 208c48.3 0 92.7-16.4 128-44v16.3c0 6.4 2.5 12.5 7 17l99.7 99.7c9.4 9.4 24.6 9.4 33.9 0l28.3-28.3c9.4-9.4 9.4-24.6.1-34zM208 336c-70.7 0-128-57.2-128-128 0-70.7 57.2-128 128-128 70.7 0 128 57.2 128 128 0 70.7-57.2 128-128 128z">
171
+ </path>
172
+ </svg> この本を図書館から検索する</a></p>
173
+ </div>
174
+ <h1 class="title" itemprop="name">
175
+ PHPを使おう: PHPで広がるWebビジネス展開
176
+ </h1>
177
+ <div class="author" itemprop="author">
178
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 448 512" width="11" height="13">
179
+ <path fill="#999" d="M224 256c70.7 0 128-57.3 128-128S294.7 0 224 0 96 57.3 96 128s57.3 128 128 128zm89.6 32h-16.7c-22.2 10.2-46.9 16-72.9 16s-50.6-5.8-72.9-16h-16.7C60.2 288 0 348.2 0 422.4V464c0 26.5 21.5 48 48 48h352c26.5 0 48-21.5 48-48v-41.6c0-74.2-60.2-134.4-134.4-134.4z" />
180
+ </svg>
181
+ <a href="/search?q=author:Web%E3%83%93%E3%82%B8%E3%83%8D%E3%82%B9PHP%E7%A0%94%E7%A9%B6%E9%83%A8%E4%BC%9A">WebビジネスPHP研究部会</a><span>(著)</span>
182
+ </div>
183
+ <div class="share_wish">
184
+ <ul class="share">
185
+ <li class="facebook">
186
+ <div class="fb-like" data-href="https://calil.jp/book/4901676032"
187
+ data-layout="button" data-action="like" data-show-faces="true" data-share="true"></div>
188
+ </li>
189
+ </ul>
190
+ <div class="wishlist">
191
+ <div class="wishlist_count">
192
+ 読みたいリスト
193
+ </div>
194
+ <div class="yomitaiButton"></div>
195
+ </div>
196
+ </div>
197
+ <div class="liblist">
198
+ </div>
199
+ <div class="more">
200
+ <div class="area_title">
201
+ <h3> <span id="find_more_txt">もっと</span>もっと探す</h3><a id="find_more_btn"
202
+ href="javascript:showFindMore();">+もっと</a>
203
+ <script>
204
+ function showFindMore() {
205
+ document.getElementById('find_more_txt').style.display = 'inline';
206
+ var find_more_btn = document.getElementById('find_more_btn');
207
+ find_more_btn.style.color = '#aaaaaa';
208
+ find_more_btn.style.textDecoration = 'none';
209
+ document.getElementById('find_more').style.display = 'block';
210
+ }
211
+ window.addEventListener('DOMContentLoaded', (event) => {
212
+ showFindMore();
213
+ });
214
+ </script>
215
+ </div>
216
+ <form action="/book/4901676032/search" method="GET" id="pref_search">
217
+ <select name="pref" onchange="document.getElementById('pref_search').submit()">
218
+ <option value=""></option>
219
+ <optgroup label="北海道・東北地方">
220
+ <option value="北海道">北海道</option>
221
+ <option value="青森県">青森県</option>
222
+ <option value="岩手県">岩手県</option>
223
+ <option value="秋田県">秋田県</option>
224
+ <option value="宮城県">宮城県</option>
225
+ <option value="山形県">山形県</option>
226
+ <option value="福島県">福島県</option>
227
+ </optgroup>
228
+ <optgroup label="関東地方">
229
+ <option value="茨城県">茨城県</option>
230
+ <option value="栃木県">栃木県</option>
231
+ <option value="群馬県">群馬県</option>
232
+ <option value="埼玉県">埼玉県</option>
233
+ <option value="千葉県">千葉県</option>
234
+ <option value="東京都">東京都</option>
235
+ <option value="東京都(大学)">東京都(大学)</option>
236
+ <option value="神奈川県">神奈川県</option>
237
+ </optgroup>
238
+ <optgroup label="中部地方">
239
+ <option value="新潟県">新潟県</option>
240
+ <option value="長野県">長野県</option>
241
+ <option value="山梨県">山梨県</option>
242
+ <option value="富山県">富山県</option>
243
+ <option value="石川県">石川県</option>
244
+ <option value="福井県">福井県</option>
245
+ </optgroup>
246
+ <optgroup label="東海地方">
247
+ <option value="岐阜県">岐阜県</option>
248
+ <option value="三重県">三重県</option>
249
+ <option value="静岡県">静岡県</option>
250
+ <option value="愛知県">愛知県</option>
251
+ </optgroup>
252
+ <optgroup label="近畿地方">
253
+ <option value="大阪府">大阪府</option>
254
+ <option value="京都府">京都府</option>
255
+ <option value="滋賀県">滋賀県</option>
256
+ <option value="兵庫県">兵庫県</option>
257
+ <option value="奈良県">奈良県</option>
258
+ <option value="和歌山県">和歌山県</option>
259
+ </optgroup>
260
+ <optgroup label="中国地方">
261
+ <option value="鳥取県">鳥取県</option>
262
+ <option value="島根県">島根県</option>
263
+ <option value="岡山県">岡山県</option>
264
+ <option value="広島県">広島県</option>
265
+ <option value="山口県">山口県</option>
266
+ </optgroup>
267
+ <optgroup label="四国地方">
268
+ <option value="愛媛県">愛媛県</option>
269
+ <option value="高知県">高知県</option>
270
+ <option value="香川県">香川県</option>
271
+ <option value="徳島県">徳島県</option>
272
+ </optgroup>
273
+ <optgroup label="九州地方">
274
+ <option value="福岡県">福岡県</option>
275
+ <option value="佐賀県">佐賀県</option>
276
+ <option value="長崎県">長崎県</option>
277
+ <option value="大分県">大分県</option>
278
+ <option value="熊本県">熊本県</option>
279
+ <option value="宮崎県">宮崎県</option>
280
+ <option value="鹿児島県">鹿児島県</option>
281
+ <option value="沖縄県">沖縄県</option>
282
+ </optgroup>
283
+ </select> の図書館をまとめて探す
284
+ </form>
285
+ <p class="openbs">
286
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 512"><!--!Font Awesome Free 6.5.2 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2024 Fonticons, Inc.--><path d="M36.8 192H603.2c20.3 0 36.8-16.5 36.8-36.8c0-7.3-2.2-14.4-6.2-20.4L558.2 21.4C549.3 8 534.4 0 518.3 0H121.7c-16 0-31 8-39.9 21.4L6.2 134.7c-4 6.1-6.2 13.2-6.2 20.4C0 175.5 16.5 192 36.8 192zM64 224V384v80c0 26.5 21.5 48 48 48H336c26.5 0 48-21.5 48-48V384 224H320V384H128V224H64zm448 0V480c0 17.7 14.3 32 32 32s32-14.3 32-32V224H512z"/></svg> <a href="https://demo.openbs.jp/4901676032" target="_blank">近くの書店在庫を調べる</a>
287
+ </p>
288
+ <style>
289
+ .openbs {
290
+ display: flex;
291
+ justify-content: flex-start;
292
+ align-items: center;
293
+ gap: 0.3rem;
294
+ }
295
+ .openbs svg {
296
+ height: 16px;
297
+ fill: #999999;
298
+ }
299
+ .openbs a {
300
+ color: #1F9AFF;
301
+ text-decoration: none;
302
+ position: relative;
303
+ top: 1px;
304
+ }
305
+ .openbs a:hover {
306
+ text-decoration: underline;
307
+ }
308
+ </style>
309
+ <div id="find_more">
310
+ <hr>
311
+ <form action="/book/4901676032/search" method="get" id="nearby">
312
+ <input id="nearby" type="search" placeholder="" name="nearby" value="">
313
+ <button>図書館を検索</button>
314
+ </form>
315
+ <p>
316
+ <img src="https://calil.jp/public/img/icon/cinii.png" width="16" height="16">
317
+ <a target="_blank" href="https://cir.nii.ac.jp/openurl/query?url_ver=Z39.88-2004&ctx_ver=Z39.88-2004&rft_val_fmt=info:ofi/fmt:kev:mtx:book&rft.genre=book&rft.isbn=9784901676038">
318
+ CiNii Researchで大学図書館の所蔵を調べる
319
+ </a>
320
+ </p>
321
+ </div>
322
+ </div>
323
+ <div class="detail" itemprop="description">
324
+ <div class="area_title">
325
+ <h3>詳しい情報</h3>
326
+ </div>
327
+ <p>出版社: <span itemprop="publisher">九天社</span><span itemprop="datePublished">(2002-02-01)</span></p>
328
+ <p>単行本: 189
329
+ ページ
330
+ </p>
331
+ <p><span itemprop="isbn">ISBN-10:</span> 4901676032
332
+ &nbsp;<span itemprop="isbn">ISBN-13:</span> 9784901676038
333
+ </p>
334
+ </div>
335
+ </div>
336
+ </main>
337
+ <aside>
338
+ <div class="other">
339
+ <h4>
340
+ <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 512 512" width="12" height="12">
341
+ <path
342
+ d="M432,320H400a16,16,0,0,0-16,16V448H64V128H208a16,16,0,0,0,16-16V80a16,16,0,0,0-16-16H48A48,48,0,0,0,0,112V464a48,48,0,0,0,48,48H400a48,48,0,0,0,48-48V336A16,16,0,0,0,432,320ZM488,0h-128c-21.37,0-32.05,25.91-17,41l35.73,35.73L135,320.37a24,24,0,0,0,0,34L157.67,377a24,24,0,0,0,34,0L435.28,133.32,471,169c15,15,41,4.5,41-17V24A24,24,0,0,0,488,0Z">
343
+ </path>
344
+ </svg>ほかのサービスで見る
345
+ </h4>
346
+ <div class="bottom_line"><a class="linkbutton" target="_blank" href="https://booklog.jp/item/1/4901676032"><img src="/public/img/gyazo/3a84e709fbf9419072b2768f6c8cf223.png">ブクログ</a><a class="linkbutton" target="_blank" href="https://bookmeter.com/b/4901676032"><img src="/public/img/gyazo/f8d5ff7e85b148cac2ac93a605563917.png">読書メーター</a><a class="linkbutton" target="_blank" href="https://www.librarything.com/isbn/4901676032"><img src="/public/img/gyazo/43d45cef4b83e16271c126dccb6bdeec.png">LibraryThing</a><a class="linkbutton" target="_blank" href="https://www.honzuki.jp/book/book/isbn9784901676038/index.html"><img src="/public/img/parts/honzuki.gif">本が好き!</a><a class="linkbutton" target="_blank" href="https://cir.nii.ac.jp/openurl/query?url_ver=Z39.88-2004&amp;ctx_ver=Z39.88-2004&amp;rft_val_fmt=info:ofi/fmt:kev:mtx:book&amp;rft.genre=book&amp;rft.isbn=9784901676038"><img src="/public/img/icon/cinii.png">CiNii Research</a><a class="linkbutton" target="_blank" href="https://ndlsearch.ndl.go.jp/api/openurl?isbn=9784901676038"><img src="/public/img/gyazo/cc01aedc5928c73652059a911c3badf0.png">国立国会図書館サーチ</a><a class="linkbutton" target="_blank" href="https://www.hanmoto.com/bd/isbn/9784901676038"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAQAAAC1+jfqAAAAIGNIUk0AAHomAACAhAAA+gAAAIDoAAB1MAAA6mAAADqYAAAXcJy6UTwAAAACYktHRAD/h4/MvwAAAAd0SU1FB+kGGgYTC7/LRhkAAADWSURBVCjPbZGhdcMwGIQ/54UHhGsDZwKRYvOAkGLxEm1QUN4NsoEmqEG5Jqg3yARfgRXXTnN6epKe7r+7X4IF1Un+YQ+QnChdpAeyX6vr3FYRMFplMz7cbwUnIPC5nIduUchiMIvxSZJFMmwMotA1Qotz5G1b+mdRLEIRey9eXaKvLQZLswmO4iDs7pweuLVdz0QCXlgR3gEYgROvQOXAeSaMQmi0H+DIeX4DQgc7+OZOuFEAiABcucwhk5gt4qHNuRNk7mY0Wq0Gs6PVweQkJIen//uAX3zRhpm7tj6jAAAAJXRFWHRkYXRlOmNyZWF0ZQAyMDI1LTA2LTI2VDA2OjE5OjEwKzAwOjAwDLBISwAAACV0RVh0ZGF0ZTptb2RpZnkAMjAyNS0wNi0yNlQwNjoxOToxMCswMDowMH3t8PcAAAAodEVYdGRhdGU6dGltZXN0YW1wADIwMjUtMDYtMjZUMDY6MTk6MTErMDA6MDCMj9qcAAAAAElFTkSuQmCC">版元ドットコム</a><a class="linkbutton" href="/profile/coop?redirect=/book/4901676032"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 512"><path d="M507.73 109.1c-2.24-9.03-13.54-12.09-20.12-5.51l-74.36 74.36-67.88-11.31-11.31-67.88 74.36-74.36c6.62-6.62 3.43-17.9-5.66-20.16-47.38-11.74-99.55.91-136.58 37.93-39.64 39.64-50.55 97.1-34.05 147.2L18.74 402.76c-24.99 24.99-24.99 65.51 0 90.5 24.99 24.99 65.51 24.99 90.5 0l213.21-213.21c50.12 16.71 107.47 5.68 147.37-34.22 37.07-37.07 49.7-89.32 37.91-136.73zM64 472c-13.25 0-24-10.75-24-24 0-13.26 10.75-24 24-24s24 10.74 24 24c0 13.25-10.75 24-24 24z"/></svg>カスタマイズ</a>
347
+ </div>
348
+ </div>
349
+ <div class="openbd">
350
+ </div>
351
+
352
+ <div class="review_area">
353
+ <h4>
354
+ <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 512 512" width="12" height="12">
355
+ <path
356
+ d="M256 32C114.6 32 0 125.1 0 240c0 49.6 21.4 95 57 130.7C44.5 421.1 2.7 466 2.2 466.5c-2.2 2.3-2.8 5.7-1.5 8.7S4.8 480 8 480c66.3 0 116-31.8 140.6-51.4 32.7 12.3 69 19.4 107.4 19.4 141.4 0 256-93.1 256-208S397.4 32 256 32z">
357
+ </path>
358
+ </svg>書評とコメント
359
+ </h4>
360
+ <div class="reviews">
361
+ <div class="review_post">
362
+
363
+
364
+ </div>
365
+ <script>
366
+ function post_like(id) {
367
+ var url = '/review/like';
368
+ var headers = {
369
+ 'Accept': 'application/json',
370
+ 'Content-Type': 'application/x-www-form-urlencoded; charset=utf-8'
371
+ }
372
+ var params = {id: id};
373
+ var body = Object.keys(params).map(function(key){return key+'='+encodeURIComponent(params[key])}).join('&');
374
+ fetch(url, {
375
+ method: 'POST',
376
+ headers: headers,
377
+ body: body
378
+ }).then(function(r){ return r.json() }).then(function(data) {
379
+ if (data.stat === 'ok') {
380
+ document.getElementById('like' + id).innerHTML = 'ありがとうございました。';
381
+ } else {
382
+ alert(data.message);
383
+ }
384
+ })
385
+ .catch(function() {
386
+ alert('サーバーからの応答がありません。');
387
+ });
388
+ };
389
+
390
+ </script>
391
+ </div>
392
+ <div class="lets_post">
393
+ <span>この本の感想を書いてみよう。</span>
394
+ <a href="/review/4901676032/post" class="post">
395
+ <svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 576 512" width="12" height="12">
396
+ <path
397
+ d="M402.3 344.9l32-32c5-5 13.7-1.5 13.7 5.7V464c0 26.5-21.5 48-48 48H48c-26.5 0-48-21.5-48-48V112c0-26.5 21.5-48 48-48h273.5c7.1 0 10.7 8.6 5.7 13.7l-32 32c-1.5 1.5-3.5 2.3-5.7 2.3H48v352h352V350.5c0-2.1.8-4.1 2.3-5.6zm156.6-201.8L296.3 405.7l-90.4 10c-26.2 2.9-48.5-19.2-45.6-45.6l10-90.4L432.9 17.1c22.9-22.9 59.9-22.9 82.7 0l43.2 43.2c22.9 22.9 22.9 60 .1 82.8zM460.1 174L402 115.9 216.2 301.8l-7.3 65.3 65.3-7.3L460.1 174zm64.8-79.7l-43.2-43.2c-4.1-4.1-10.8-4.1-14.8 0L436 82l58.1 58.1 30.9-30.9c4-4.2 4-10.8-.1-14.9z">
398
+ </path>
399
+ </svg>
400
+ この本にコメントする
401
+ </a>
402
+ </div>
403
+ </div>
404
+ </aside>
405
+ </div>
406
+ </div>
407
+ <div class="footer">
408
+ <p>
409
+ <a href="/doc/about.html">カーリルについて</a> -
410
+ <a href="/doc/lib.html">図書館の方へ</a> -
411
+ <a href="/local/">ローカル</a> -
412
+ <a href="/doc/api.html">図書館 API</a> -
413
+ <a href="/doc/download.html">バナー・素材</a>
414
+ </p>
415
+
416
+ <p>
417
+ <a href="/">ホーム</a> -
418
+ <a href="/company/">運営会社</a> -
419
+ <a href="https://blog.calil.jp/">ブログ</a> -
420
+ <a href="https://twitter.com/caliljp">Twitter</a> -
421
+ <a href="/privacy/freedom.html">図書館の自由</a> -
422
+ <a href="/privacy/">プライバシーポリシー</a> -
423
+ <a href="/doc/contact.html">お問い合わせ</a>
424
+ </p>
425
+ </div>
426
+ <script type="text/javascript" charset="utf-8">
427
+ var calil = {};
428
+ function get_query() {
429
+ var hash, hashes, i, vars;
430
+ vars = [];
431
+ hash = void 0;
432
+ hashes = window.location.href.slice(window.location.href.indexOf("?") + 1).split("&");
433
+ i = 0;
434
+ while (i < hashes.length) {
435
+ hash = hashes[i].split("=");
436
+ vars.push(hash[0]);
437
+ vars[hash[0]] = hash[1];
438
+ i++;
439
+ }
440
+ return vars;
441
+ }
442
+
443
+ function buildQueryString(params) {
444
+ var parts = [];
445
+ var add = function(key, value) {
446
+ parts.push(encodeURIComponent(key) + '=' + encodeURIComponent(value));
447
+ }
448
+ for (var key in params) {
449
+ var param = params[key];
450
+ if (Array.isArray(param)) {
451
+ param.map(function(value) {
452
+ add(key, value);
453
+ });
454
+ }
455
+ else {
456
+ add(key, param);
457
+ }
458
+ }
459
+ return '?' + parts.join('&').replace(/%20/g, '+');
460
+ }
461
+
462
+ calil.library = {
463
+ add_mode: false,
464
+ setAddMode: function() {
465
+ this.add_mode = true;
466
+ },
467
+ addLibrary: function(sysid, func) {
468
+ this.setLibrary({
469
+ sysid: sysid,
470
+ add: true,
471
+ func: func
472
+ });
473
+ },
474
+ setLibrary: function(param) {
475
+ if (this.add_mode) param['add'] = true;
476
+ var url = '/set_place_json' + buildQueryString(param);
477
+ fetch(url).then(function(r) { return r.json()}).then((function(_this) {
478
+ return function(data) {
479
+ if (data.stat === "ok") {
480
+ if (location.search.match('from=settings')) {
481
+ return location.href = '/settings';
482
+ } else if (location.pathname === "/" || location.pathname.indexOf('/library') === 0) {
483
+ return location.href = '/start';
484
+ } else if (location.pathname === '/settings') {
485
+ if ((get_query().redirect != null) === true) {
486
+ return location.href = get_query().redirect;
487
+ } else {
488
+ return location.href = location.pathname + location.search;
489
+ }
490
+ } else {
491
+ return location.reload();
492
+ }
493
+ } else if (data.stat === 'confirm') {
494
+ if (!confirm(data.message)) {
495
+ return;
496
+ }
497
+ param['force'] = 'true';
498
+ return _this.setLibrary(param);
499
+ } else {
500
+ return alert('エラー: ' + data.message);
501
+ }
502
+ };
503
+ })(this)
504
+ )
505
+ .catch(function() {
506
+ alert('サーバーからの応答がありません。');
507
+ })
508
+ }
509
+ };
510
+
511
+ // 位置情報から探す
512
+ calil.geo = {
513
+ getGeoLib: function() {
514
+ var callback;
515
+ callback = (function(_this) {
516
+ return function(lat, lng) {
517
+ var params = {
518
+ lat: lat,
519
+ lon: lng,
520
+ limit: 30
521
+ };
522
+ var url = '//api.calil.jp/mobile/recommend' + buildQueryString(params);
523
+ fetch(url).then(function(r) {return r.json()}).then(function(data) {
524
+ var j, len, message, n, nearest_lib, ref;
525
+ if (data.nearby.length <= 0) {
526
+ return alert('近くに図書館が見つかりません');
527
+ }
528
+ nearest_lib = null;
529
+ ref = data.nearby;
530
+ for (j = 0, len = ref.length; j < len; j++) {
531
+ n = ref[j];
532
+ if (n.icon !== 'school') {
533
+ nearest_lib = n;
534
+ break;
535
+ }
536
+ }
537
+ if (!nearest_lib) {
538
+ nearest_lib = data.nearby[0];
539
+ }
540
+ message = nearest_lib.name + 'が見つかりました。この図書館を設定しますか?';
541
+ if (confirm(message)) {
542
+ calil.library.setLibrary({
543
+ sysid: nearest_lib.id,
544
+ add: true
545
+ });
546
+ }
547
+ })
548
+ .catch(function() {
549
+ alert('読み込みに失敗しました。');
550
+ })
551
+ };
552
+ })(this);
553
+ return this._getGeolocation(callback);
554
+ },
555
+ _getGeolocation: function(callback, hideerror) {
556
+ var error;
557
+ if (!navigator.geolocation) {
558
+ return;
559
+ }
560
+ error = (hideerror ? function() {} : this._errorFunc);
561
+ return navigator.geolocation.getCurrentPosition((function(position) {
562
+ var lat, lng;
563
+ lat = position.coords.latitude;
564
+ lng = position.coords.longitude;
565
+ return callback(lat, lng);
566
+ }), error, {
567
+ enableHighAccuracy: true
568
+ });
569
+ },
570
+ _errorFunc: function(error) {
571
+ var message = '';
572
+ switch (error.code) {
573
+ case error.POSITION_UNAVAILABLE:
574
+ message = '位置情報の取得ができませんでした。';
575
+ break;
576
+ case error.PERMISSION_DENIED:
577
+ message = '位置情報取得の使用許可がされませんでした。';
578
+ break;
579
+ case error.PERMISSION_DENIED_TIMEOUT:
580
+ message = '位置情報取得中にタイムアウトしました。';
581
+ }
582
+ alert(message);
583
+ }
584
+ };
585
+
586
+ var create_uuid = function() {
587
+ var S4;
588
+ S4 = function() {
589
+ return (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1);
590
+ };
591
+ return S4() + S4() + "-" + S4() + "-" + S4() + "-" + S4() + "-" + S4() + S4() + S4();
592
+ };
593
+
594
+ // 蔵書検索
595
+ calil.api = {
596
+ search: {
597
+ server: 'https://api.calil.jp',
598
+ appkey: '1f797b9d960207280336610120edb44a',
599
+ uuid: null,
600
+ session: false,
601
+ order: false,
602
+ systemid_list: [],
603
+ isbn_list: [],
604
+ api_url: false,
605
+ first_apiurl: false,
606
+ api_retry_count: 0,
607
+ api_pause: false,
608
+ api_call_count: 0,
609
+ api_complete: false,
610
+ api_timeout_timer: 0,
611
+ data_cache: "",
612
+ initialize: function() {
613
+ this.uuid = create_uuid();
614
+ this.order = false;
615
+ this.systemid_list = [];
616
+ this.isbn_list = [];
617
+ this.api_url = false;
618
+ this.first_apiurl = false;
619
+ this.api_retry_count = 0;
620
+ this.api_pause = false;
621
+ this.api_call_count = 0;
622
+ this.api_complete = false;
623
+ this.data_cache = "";
624
+ },
625
+ add_systemid: function(sytemid) {
626
+ return this.systemid_list = this.systemid_list.concat(sytemid.split(","));
627
+ },
628
+ add_isbn: function(isbn) {
629
+ return this.isbn_list = this.isbn_list.concat(isbn.split(","));
630
+ },
631
+ start: function() {
632
+ this.first_apiurl = this.server + '/check?appkey=' + this.appkey + '&systemid=' + this.systemid_list.join(',') + '&isbn=' + this.isbn_list.join(',');
633
+ if (this.order) {
634
+ this.first_apiurl += '&order=' + this.order;
635
+ }
636
+ return this.call_api(this.first_apiurl);
637
+ },
638
+ api_timeout: function() {
639
+ return calil.api.render.timeoutSearchProgress();
640
+ },
641
+ check_change_uuid: function(uuid) {
642
+ if (this.uuid !== uuid) {
643
+ log('uuidが変更');
644
+ return true;
645
+ }
646
+ return false;
647
+ },
648
+ call_api: function(url, uuid) {
649
+ var data;
650
+ if (uuid == null) {
651
+ uuid = this.uuid;
652
+ }
653
+ this.api_url = url;
654
+ url += '&callback=no' + '&' + new Date().getTime() // IE11 でキャッシュされる問題対策
655
+ fetch(url, {cache: 'no-cache'}).then(function(r) {return r.json()}).then((function(_this) {
656
+ return function(data) {
657
+ if (_this.check_change_uuid(uuid)) {
658
+ return;
659
+ }
660
+ _this.callback(data, uuid);
661
+ };
662
+ })(this))
663
+ .catch((function(_this) {
664
+ return function(data) {
665
+ if (_this.check_change_uuid(uuid)) {
666
+ return;
667
+ }
668
+ if (_this.api_url && _this.api_retry_count <= 2) {
669
+ _this.api_call_count = 0;
670
+ return setTimeout((function() {
671
+ _this.call_api(_this.api_url, uuid);
672
+ return _this.api_retry_count++;
673
+ }), 5000);
674
+ } else {
675
+ return _this.api_timeout();
676
+ }
677
+ };
678
+ })(this));
679
+ },
680
+ callback: function(data, uuid) {
681
+ var session = data['session'];
682
+ var conti = data['continue'];
683
+ this.data_cache = data;
684
+ if (conti === 1) {
685
+ this.api_call_count++;
686
+ var seconds;
687
+ if (this.api_call_count < 3) {
688
+ seconds = 1000;
689
+ } else if (this.api_call_count < 7) {
690
+ seconds = 1200;
691
+ } else {
692
+ seconds = 3000;
693
+ }
694
+ var newurl = this.server + '/check?appkey=' + this.appkey + '&session=' + session;
695
+ if (this.order) {
696
+ newurl += '&order=' + this.order;
697
+ }
698
+ setTimeout(((function(_this) {
699
+ return function() {
700
+ return _this.call_api(newurl, uuid, data);
701
+ };
702
+ })(this)), seconds);
703
+ } else {
704
+ this.api_complete = true;
705
+ }
706
+ calil.api.render.render_books(data);
707
+ },
708
+ },
709
+ render: {
710
+ filter_system_id: "all",
711
+ filter_libkey: "",
712
+ initialize: function() {
713
+ this.filter_system_id = "all";
714
+ this.filter_libkey = "";
715
+ },
716
+ timeoutSearchProgress: function() {
717
+ var i;
718
+ i = 0;
719
+ [].slice.call(document.querySelectorAll('.system_status')).map(function(item, i) {
720
+ item.innerHTML = ':タイムアウト';
721
+ });
722
+ },
723
+ render_books: function(data) {
724
+ for (isbn in data.books) {
725
+ for (systemid in data.books[isbn]) {
726
+ this.render_detail(systemid, data.books[isbn][systemid]);
727
+ }
728
+ }
729
+ },
730
+ render_detail: function(systemid, systemData) {
731
+ if (systemData.status === 'Running') return;
732
+ if (systemData.status === 'Error') {
733
+ return document.querySelector('#' + systemid + ' .system_status').innerHTML = ':検索失敗';
734
+ }
735
+ var html = '<div class="wrap">';
736
+ var systemStatus = '蔵書なし';
737
+ for (i in systemData.libkey) {
738
+ systemStatus = systemData.libkey[i];
739
+ var statusId = this.getStatusId(systemStatus);
740
+ if (i === '電子書籍') {
741
+ html += '' + i;
742
+ } else {
743
+ html += '<a class="' + statusId + '" href="/library/search?s=' + systemid + '&k=' + (encodeURIComponent(i)) + '"><div class="lib"><div>' + i + '</div><div class="status">' + systemStatus + '</div></div></a>';
744
+ }
745
+ }
746
+ html += '</div>';
747
+ if (systemData.reserveurl !== '' && systemStatus !== '蔵書なし') {
748
+ html += '<a href="' + systemData.reserveurl + '" target="_blank" class="reservation">予約する</a>';
749
+ }
750
+ document.querySelector('#' + systemid + ' .system_status').innerHTML = ':' + systemStatus;
751
+ document.querySelector('#' + systemid + ' > .list').innerHTML = html;
752
+ // setTimeout((function() {
753
+ // document.querySelector('#' + systemid + ' > .result').innerHTML = html;
754
+ // }), 100);
755
+ },
756
+ getStatusId: function(status) {
757
+ var statusId = '';
758
+ var statusIds = {
759
+ '': 'nostatus',
760
+ '貸出可': 'available',
761
+ '蔵書あり': 'collection',
762
+ '館内のみ': 'inside',
763
+ '貸出中': 'lending',
764
+ '予約中': 'reserved',
765
+ '準備中': 'ordering',
766
+ '蔵書なし': 'notfound',
767
+ };
768
+ if (statusIds[status]) {
769
+ statusId = statusIds[status];
770
+ } else {
771
+ // 知らないstatusがきたら、"蔵書あり"と同等に扱う 例) 長期延滞、行方不明
772
+ statusId = 'hold';
773
+ }
774
+ return statusId;
775
+ }
776
+ }
777
+ };
778
+ </script>
779
+ <script type="text/javascript" charset="utf-8">
780
+ (new Image).src = '/public/img/icon/run.gif';
781
+ var book_title = 'PHPを使おう: PHPで広がるWebビジネス展開';
782
+ </script>
783
+ <script type="text/javascript" charset="utf-8">
784
+ var mode = 'single';
785
+ calil.api.search.notfound_func = showFindMore;
786
+ </script>
787
+ <script>
788
+ calil.api.search.initialize();
789
+ calil.api.render.initialize();
790
+ calil.api.search.add_isbn('4901676032');
791
+ </script>
792
+ <script>
793
+ (function (d, s, id) {
794
+ var js, fjs = d.getElementsByTagName(s)[0];
795
+ if (d.getElementById(id)) return;
796
+ js = d.createElement(s);
797
+ js.id = id;
798
+ js.async = true;
799
+ js.src = "//connect.facebook.net/ja_JP/sdk.js#xfbml=1&version=v19.0&appId=270660792981320";
800
+ fjs.parentNode.insertBefore(js, fjs);
801
+ }(document, 'script', 'facebook-jssdk'));
802
+ </script>
803
+ <style>
804
+ .yomitaiButton {
805
+ width: 180px;
806
+ position: relative;
807
+ display: flex;
808
+ border: 1px solid #dddddd;
809
+ border-radius: 100px;
810
+ background: #ffffff;
811
+ background: linear-gradient(to bottom, #ffffff 0%,#ffffff 68%,#e8e8e9 97%);
812
+ overflow: hidden;
813
+ }
814
+ .yomitaiButton button {
815
+ width: 70px;
816
+ cursor: pointer;
817
+ width: 50%;
818
+ padding: 5px 0;
819
+ display: block;
820
+ font-size: 1rem;
821
+ text-align: center;
822
+ color: #3E3E3E;
823
+ background: transparent;
824
+ z-index: 1;
825
+ border: none;
826
+ }
827
+ .yomitaiButton button.active {
828
+ position: relative;
829
+ color: #ffffff;
830
+ background: #F3AB00;
831
+ }
832
+ .yomitaiButton button.wish {
833
+ padding-left: 0.35rem;
834
+ }
835
+ .yomitaiButton button.read {
836
+ padding-right: 0.25rem;
837
+ }
838
+ .yomitaiButton.disabled button {
839
+ background-color: #E1E1E1;
840
+ cursor: not-allowed;
841
+ }
842
+ .yomitaiButton.disabled button.active {
843
+ background: #8B8B8B !important;
844
+ }
845
+ </style>
846
+ <script>
847
+
848
+
849
+ /**
850
+ * @typedef State
851
+ * @property {boolean} wish 読みたいリストに入っているかどうか
852
+ * @property {boolean} read 読んだリストに入っているかどうか
853
+ *
854
+ */
855
+
856
+ /**
857
+ * 読みたいリストボタンのクラス
858
+ *
859
+ * @class YomitaiButton
860
+ */
861
+ class YomitaiButton {
862
+ /**
863
+ * コンストラクタ
864
+ *
865
+ * @param {State[]} states 各ボタンの状態
866
+ * @param {string} selector ボタンのセレクタ
867
+ * @param {string} activeClass アクティブ状態のクラス名
868
+ */
869
+ constructor(states, selector, activeClass, callback) {
870
+ this.states = states
871
+ this.selector = selector
872
+ this.activeClass = activeClass
873
+ this.callback = callback
874
+ this.render()
875
+ }
876
+ setStates(states) {
877
+ this.states = states
878
+ this.render()
879
+ }
880
+ /**
881
+ * ボタンを描画する
882
+ *
883
+ */
884
+ render() {
885
+ const buttons = document.querySelectorAll(this.selector)
886
+ Array.from(buttons).forEach((button, index) => {
887
+ button.textContent = ''
888
+ button.appendChild(this.createButton('wish', '読みたい', index))
889
+ button.appendChild(this.createButton('read', '読んだ', index))
890
+ })
891
+ this.addEvent()
892
+ }
893
+ /**
894
+ * ボタンのエレメントを作成する
895
+ *
896
+ * @param {string} name リストの名前 wish or read
897
+ * @param {string} text ボタンのテキスト
898
+ * @param {number} index ボタンのインデックス
899
+ */
900
+ createButton(name, text, index) {
901
+ const button = document.createElement('button')
902
+ button.textContent = text
903
+ button.dataset.name = name
904
+ if (this.states[index][name]) button.classList.add(this.activeClass)
905
+ return button
906
+ }
907
+ addEvent() {
908
+ const buttons = document.querySelectorAll(this.selector + ' > button')
909
+ Array.from(buttons).forEach((button, i) => {
910
+ const index = Math.floor(i / 2)
911
+ 'mousedown keydown'.split(' ').forEach((eventName)=>{
912
+ button.addEventListener(eventName, (event) => {
913
+ if (event.type!=='mousedown') {
914
+ console.log(event)
915
+ if (event.type==='keydown') {
916
+ if (event.key!=='Tab') event.preventDefault()
917
+ console.log(event.key)
918
+ if (event.key!=='Enter' && event.keyCode!==32) return
919
+ }
920
+ }
921
+ const name = button.dataset.name
922
+ const states = this.states.concat() // statesのコピーを作成
923
+ const prevState = Object.assign({}, states[index]) // states[index]のコピーを作成
924
+ let action
925
+ if (states[index][name]===true) {
926
+ // すでにリストに登録されている場合は削除
927
+ action = 'delete';
928
+ } else {
929
+ // 別のリストに登録されている場合は移動
930
+ const otherName = name==='wish' ? 'read' : 'wish';
931
+ if (states[index][otherName]===true) {
932
+ action = 'move';
933
+ } else {
934
+ action = 'add';
935
+ }
936
+ }
937
+ if (action==='add') {
938
+ states[index][name] = true
939
+ }
940
+ if (action==='move') {
941
+ states[index] = {
942
+ wish: !states[index].wish,
943
+ read: !states[index].read
944
+ }
945
+ }
946
+ if (action==='delete') {
947
+ states[index] = {
948
+ wish: false,
949
+ read: false
950
+ }
951
+ }
952
+ this.setStates(states)
953
+ // キャンセルやエラー時に状態を戻す関数
954
+ const revertState = () => {
955
+ const states = this.states.concat() // statesのコピーを作成
956
+ states[index] = prevState
957
+ this.setStates(states)
958
+ }
959
+ this.callback(name, action, index, revertState)
960
+ })
961
+ })
962
+ })
963
+ }
964
+ }
965
+ const bookJSON = [
966
+ {
967
+ id: '4901676032',
968
+ isbn: '4901676032',
969
+ title: 'PHPを使おう: PHPで広がるWebビジネス展開',
970
+ volume: '',
971
+ author: 'WebビジネスPHP研究部会',
972
+ pubdate: '2002-02-01',
973
+ publisher: '九天社',
974
+ source: 'amazon',
975
+ },
976
+ ]
977
+ new YomitaiButton([
978
+ {wish: false, read: false},
979
+ ], '.yomitaiButton', 'active', (name, action, index, revertState) => {
980
+
981
+ return location.href = '/login?redirect=' + escape(location.pathname + location.search);
982
+
983
+ })
984
+ </script>
985
+ <script type="text/javascript" charset="utf-8" src="/public/web-html/js/menusp.js"></script>
986
+ </body>
987
+ </html>