1 //QuanLib: eBook Library
2 //(C) 2017 by Christian Jaekl (cejaekl@yahoo.com)
9 mousePos: { // Last known position of the mouse cursor
16 map: {}, // map from book.Id to index into cache[]
20 milliSecs: 500, // time to wait before displaying tooltip
25 threshold: 10, // number of pixels that mouse can move before tip is dismissed
30 document.onmousemove = onMouseMove;
32 function adjustPos(setting) {
33 var value = parseInt(setting);
35 if (g_state.first === value) {
40 var maxFirst = Math.max(0, g_state.count - g_state.pageSize);
44 } else if (value > maxFirst) {
45 g_state.first = maxFirst;
47 g_state.first = value;
50 g_state.last = g_state.first + g_state.pageSize - 1;
51 if (g_state.last >= g_state.count) {
52 g_state.last = g_state.count - 1;
55 document.getElementById('slider').value = setting;
60 function bookHtml(book) {
61 var result = '<div class="book">'
64 + '<td><a href="/book/' + book.Id + '">';
65 if (0 == book.CoverId) {
66 result += '(No cover available)';
68 result += '<img class="cover-thumb" src="/download/' + book.CoverId + '"/>';
71 + '<td onclick="displayDetails(' + book.Id + ');" '
72 + ' onmouseover="startTooltipTimer(' + book.Id + ');">'
73 + '<p><b>' + book.Title + '</b></p>'
75 + '<i>' + book.AuthorReading + '</i>';
76 if (typeof(book.SeriesName) !== 'undefined' && book.SeriesName.length > 0) {
77 result += '<br/><i>' + book.SeriesName + ' ' + book.Volume + '</i>';
87 //ce(s): "clear if empty()"
88 //return s, unless it's undefined, in which case return an empty ("clear") string
90 if (typeof s !== 'undefined') {
96 function constructSearchUrl() {
97 var url = window.location.protocol + '//' + window.location.host + '/search/';
100 var terms = ['aut', 'tit', 'ser'];
102 for (var idx in terms) {
103 var term = terms[idx];
104 var elem = document.getElementById(term);
106 console.log('Error: could not find form element for search term "' + term + '".');
110 var value = elem.value;
111 if (value.length > 0) {
119 url += term + '=' + encodeURIComponent('%' + value + '%');
126 // Set the book ID for the details pane, and then show it
127 function displayDetails(bookId) {
128 g_state.tooltip.bookId = bookId;
132 function hideDetails() {
133 g_state.tooltip.mousePos.x = undefined;
134 g_state.tooltip.mousePos.y = undefined;
136 var elem = document.getElementById('details');
138 elem.style.display = 'none';
141 function onMouseMove(event) {
142 if (typeof event === 'undefined') {
149 if ( x === g_state.mousePos.x
150 && y === g_state.mousePos.y)
152 // No change from previous known position.
153 // Nothing to see (or do) here, move along.
157 // Remember current mouse (x,y) position
158 g_state.mousePos.x = x;
159 g_state.mousePos.y = y;
161 // Is there an active tooltip?
162 if (typeof g_state.tooltip.mousePos.x === 'undefined') {
163 // No active tooltip, so nothing further to do
167 var deltaX = Math.abs(x - g_state.tooltip.mousePos.x);
168 var deltaY = Math.abs(y - g_state.tooltip.mousePos.y);
170 if ( deltaX > g_state.tooltip.threshold
171 || deltaY > g_state.tooltip.threshold )
178 if (g_state.last < (g_state.count - 1)) {
179 adjustPos(g_state.first + g_state.pageSize);
184 if (g_state.first > 0) {
185 adjustPos(g_state.first - g_state.pageSize);
189 function onSlide(value) {
193 function onSearch() {
194 var url = constructSearchUrl();
196 fetch(url, {method:'GET', cache:'default'})
197 .then(response => response.json())
198 .then((jsonValue) => {
199 // console.log('JSON response: ', jsonValue);
200 g_state.ids = jsonValue;
201 g_state.count = g_state.ids.length;
202 g_state.first = (-1);
204 var elem = document.getElementById('slider');
205 elem.max = g_state.count;
210 var msg = 'Error fetching JSON from URL: ' + url + ': ' + err + ':' + err.stack;
216 function refreshData() {
218 var url = '/info/?ids=';
220 for (i = g_state.first; i <= g_state.last; ++i) {
221 if (i > g_state.first) {
224 var id = g_state.ids[i];
226 g_state.map[id] = i - g_state.first;
229 fetch(url, {method:'GET', cache:'default'})
230 .then(response => response.json())
231 .then((jsonValue) => {
232 console.log('JSON response for info: ', jsonValue);
233 g_state.cache = jsonValue;
237 var msg = 'Error fetching book details via URL: ' + url + ': ' + err;
238 console.log(msg, err.stack);
243 function refreshLayout() {
246 var limit = g_state.last - g_state.first;
247 for (i = 0; i <= limit; ++i) {
248 var book = g_state.cache[i];
249 html += bookHtml(book);
252 document.getElementById('books').innerHTML = html;
253 document.getElementById('first').innerHTML = (g_state.first + 1);
254 document.getElementById('last').innerHTML = (g_state.last + 1);
255 document.getElementById('count').innerHTML = g_state.count;
258 function report(message) {
259 document.getElementById('books').innerHTML = message;
262 function showDetails() {
263 var id = g_state.tooltip.bookId;
264 var elem = document.getElementById('details');
265 var index = g_state.map[id];
266 var book = g_state.cache[index];
267 var html = '<div><p><b>' + book.Title + '</b></p>'
268 + '<p><i>' + ce(book.AuthorReading) + '<br/>' + ce(book.Series) + ' ' + ce(book.Volume) + '</i></p></div><div>'
269 + ce(book.Description)
272 // Remember the current mouse (x,y).
273 // If we move the mouse too far from this point, that will trigger hiding the tooltip.
274 g_state.tooltip.mousePos.x = g_state.mousePos.x;
275 g_state.tooltip.mousePos.y = g_state.mousePos.y;
277 elem.innerHTML = html;
279 elem.style.display = 'block'; // show, and calculate size, so that we can query it below
281 var x = g_state.mousePos.x;
282 var y = g_state.mousePos.y;
284 var bcr = elem.getBoundingClientRect();
286 var width = bcr.width;
287 var height = bcr.height;
289 x = Math.max(x - (width / 2), 0);
290 y = Math.max(y - (height / 2), 0);
292 elem.style.left = x + 'px';
293 elem.style.top = y + 'px';
296 function startTooltipTimer(bookId) {
297 if (typeof g_state.tooltip.timer !== 'undefined') {
298 clearTimeout(g_state.tooltip.timer);
300 g_state.tooltip.bookId = bookId;
301 g_state.tooltip.timer = setTimeout(showDetails, g_state.tooltip.milliSecs);
304 function stopTooltipTimer() {
305 if (typeof g_state.tooltip.timer === 'undefined') {
309 clearTimeout(g_state.tooltip.timer);
310 g_state.tooltip.timer = undefined;