Fix account summary display in Evergreen 2.9+
[sitka/overdrive-evergreen-opac.git] / src / od_pages_myopac.coffee
CommitLineData
8ebc3fff
SC
1# Define custom jQuery extensions to rewrite content of existing pages
2# None of the extensions directly use the API, but they depend on od_action which does.
3
4define [
b63fc3d5 5 'jquery-noconflict'
8ebc3fff 6 'lodash'
8b13a139 7 'od_config'
8ebc3fff
SC
8 'jquery-ui'
9 'od_action'
10 'od_pages_opac'
8b13a139 11], ($, _, config) ->
8ebc3fff
SC
12
13 $.fn.extend
14
15 # Given a map between classnames and numeric values,
16 # eg, { class1: 1, class2: -1 },
17 # increment the existing values of the containers with the classnames.
18 _counters: (x) ->
19 for n, v of x
20 $x = @find ".#{n}"
21 $x.text +($x.text()) + v
22 return @
23
24 _dashboard: (x) ->
25
26 if arguments.length is 0
c34f0459 27 @append $('<div id="dashboard">')
8ebc3fff 28
c34f0459 29 else
8ebc3fff
SC
30 # Add a new dashboard for to show counts of e-items; start with
31 # zero counts
32 base = '/eg/opac/myopac'
c34f0459
SC
33 @find 'div'
34 .eq 2
35 .append """
36 <span class="dash-align">
37 <a class="dash-link" href="#{base}/circs?e_items"><span class="ncheckouts" id="dash_checked">0</span> E-items Checked Out</a>
38 </span>
39 <span class="dash_divider">|</span>
40 <span class="dash-align">
41 <a class="dash-link" href="#{base}/holds?e_items"><span class="nholds" id="dash_holds">0</span> E-items on Hold</a>
42 </span>
43 <span class="dash_divider">|</span>
44 <span class="dash-align">
45 <a class="dash-link" href="#{base}/holds?e_items&available=1"><span class="nholdsready" id="dash_pickup">0</span> E-items Ready for Checkout</a>
46 </span>
47 """
48 .end()
49 .end()
8ebc3fff
SC
50
51 # The following sequence is necessary to align the new dashboard
52 # with the existing ones, but do not know why it needs to be done
53 @find 'div'
54 .css float: 'none'
55 .end()
56
8ebc3fff 57 @_counters x # Change the values of the counters
c34f0459 58
8ebc3fff
SC
59 return @
60
61 # Replace account summary area with one that shows links to go to
c34f0459 62 # physical and e-items lists
8ebc3fff
SC
63 _account_summary: (x) ->
64
65 if arguments.length is 0
66 # Parse a list of totals of physical items from the account summary table
2fe385eb
JD
67 totals = []
68 for v in @find('td').not '[align="right"]'
69 if ( v.textContent.match(/\d+/) && v.textContent.match('Items') )
70 totals.push v.textContent.match(/\d+/)[0]
8ebc3fff 71
c34f0459 72 tpl = _.template """
8ebc3fff
SC
73 <tbody>
74 <tr>
75 <td>
76 <a href="/eg/opac/myopac/circs">
c34f0459 77 <span><span class="ncheckouts" /> <%= ncheckouts %> Items Currently Checked out</span>
8ebc3fff
SC
78 </a>
79 </td>
80 <td align="right">
81 <a href="/eg/opac/myopac/circs?e_items"><span class="n_checkouts" /> E-items Currently Checked out</a>
82 </td>
83 </tr>
84 <tr>
85 <td>
c34f0459 86 <a href="/eg/opac/myopac/holds"><span class="nholds" /> <%= nholds %> Items Currently on Hold</a>
8ebc3fff
SC
87 </td>
88 <td align="right">
89 <a href="/eg/opac/myopac/holds?e_items"><span class="n_holds" /> E-items Currently on Hold</a>
90 </td>
91 </tr>
92 <tr>
93 <td>
c34f0459 94 <a href="/eg/opac/myopac/holds?available=1"><span class="nready" /> <%= nready %> Items ready for pickup</a>
8ebc3fff
SC
95 </td>
96 <td align="right">
97 <a href="/eg/opac/myopac/holds?e_items&available=1"><span class="n_ready" /> E-items ready for pickup</a>
98 </td>
99 </tr>
100 </tbody>
101 """
c34f0459
SC
102
103 # Build a new table consisting of two columns. The first
104 # column is for physical items with the existing totals. The
105 # second column is for e-items and is initially hidden until
106 # its total values are available.
107 @empty()
108 .append tpl
109 ncheckouts: totals[0]
110 nholds: totals[1]
111 nready: totals[2]
112 .find 'td'
113 .filter '[align="right"]'
114 .find 'a'
115 .hide()
116 .end()
117 .end()
8ebc3fff
SC
118
119 else
c34f0459
SC
120 # Change the values of the counters and reveal the e-items column
121 @_counters x
122 .find 'td'
123 .filter '[align="right"]'
124 .find 'a'
125 .show()
126 .end()
127 .end()
8ebc3fff
SC
128
129 # Relabel a history tab
130 _tab_history: ->
131 $x = $('a', @)
132 $x.text "#{ $x.text() } (Physical Items)"
133 return @
134
135 # Add a new tab for e-items and select a tab relevant for the current page name.
136 # If page name contains 'history' then select any tabs with 'history' in its ID
137 # otherwise, if search parameters has 'e_items' property then select any tabs with 'eitems' in its ID
138 #$('#acct_holds_tabs, #acct_checked_tabs')._etabs()
139 _etabs: (page_name, e_items) ->
140
141 # Tab replacement is identified by container's id
142 new_tabs =
143 acct_holds_tabs: """
144 <div id="acct_holds_tabs">
145 <div class="align" id='tab_holds'>
146 <a href="holds#">Items on Hold</a>
147 </div>
148 <div class="align" id='tab_holds_eitems'>
149 <a href="holds?e_items">E-items on Hold</a>
150 </div>
151 <div class="align" id='tab_holds_history'>
152 <a href="hold_history">Holds History</a>
153 </div>
154 </div>
155 """
156 acct_checked_tabs: """
157 <div id="acct_checked_tabs">
158 <div class="align" id='tab_circs'>
159 <a href="circs#">Current Items Checked Out</a>
160 </div>
161 <div class="align" id='tab_circs_eitems'>
162 <a href="circs?e_items">E-items Checked Out</a>
163 </div>
164 <div class="align" id='tab_circs_history'>
165 <a href="circ_history">Check Out History</a>
166 </div>
167 </div>
168 """
169 @replaceWith new_tabs[@prop 'id']
170
171 # Compute the selected tab of the current page name
172 $selected =
173 # if page name ends with '_history', select the tab with id
174 # that ends with '_history'
175 if /_history$/.test page_name
176 $('[id$=_history]')
177 # else if search parameters has 'e_items' property, select the
178 # tab with id that ends with '_eitems'
179 else if e_items
180 $('[id$=_eitems]')
181 # else select the remaining tab
182 else
183 $('[id^=tab_]').not '[id$=_history],[id$=_eitems]'
184
185 $selected.addClass 'selected'
186
187 return @
188
189
190 # Resize columns of a table, either to fixed widths, or to be equal
191 # widths, ie, 100% divided by number of columns.
192 # Also, force width of table to 100%; don't know why this is necessary.
193 _resizeCols: ->
194
195 $table = @find 'table'
196 .css 'width', '100%'
197
198 # Resize to percentage widths given in the argument list
199 if arguments.length > 0
200 $th = $table.find 'th'
201 $td = $table.find 'td'
202 for width, n in arguments
203 $th.eq(n).css 'width', width
204 $td.eq(n).css 'width', width
205
206 # Otherwise, resize to equal widths
207 else
208 ncols = @find('th').length or 1
209 width = "#{100 / ncols}%"
210
211 $table
212 .find 'th'
213 .css 'width', width
214 .end()
215 .find 'td'
216 .css 'width', width
217 .end()
218
219 return @
220
221 # Show a container having a class name from a list of candidate, and hide the rest
222 _show_from: (which, candidates...) ->
223
224 @find(x).hide() for x in candidates
225 @find candidates[which]
226 .show()
227 .end()
228
229 # Replace a title of table with new text
230 _replace_title: (x) ->
231
232 @find '.header_middle span'
233 .eq 0
234 .text x
235 .end()
236
237 # Build an empty table for showing a list of holds
238 _holds_main: ->
239
240 table = """
241 <table cellpadding="0" cellspacing="0" border="0">
242 <thead id="acct_holds_main_header"><tr>
243 <th></th>
244 <th>Title/Author</th>
245 <th>Availability</th>
246 <th>Formats</th>
247 <th>Actions</th>
248 </tr></thead>
249 <tbody id="holds_temp_parent"></tbody>
250 </table>
251 <div class="warning_box">No holds found.</div>
252 """
253 @empty().append table
254 ._resizeCols '15%', '20%', '30%', '20%', '15%'
255
256 # Build <tr> elements for showing a list of holds
257 _holds_rows: (holds) ->
258 return [] unless holds
259
260 tpl = _.template """
261 <tr id="<%= id %>" name="acct_holds_temp" class="acct_holds_temp inactive-hold">
262 <td class="thumbnail"></td>
263 <td>
264 <div class="title" /> by <div class="author" />
265 </td>
266 <td class="availability"></td>
267 <td class="formats"></td>
268 <td class="actions"></td>
269 </tr>
270 """
271
272 ids = []
273 $rows = for hold in holds
274
275 ids.push hold.reserveId
276
277 # Build an empty row element that is uniquely identified by a
278 # product ID
279 $row = $ tpl id: hold.reserveId
280
281 # Fill the row with hold values and proxy the rest of the row
282 # with progress bars
283 $row
284 ._holds_row hold # hold values
285 ._row_meta() # progress bar
286 ._holds_row_avail() # progress bar
287
288 # Add hold rows to <tbody> and remove the warning box.
289 if $rows.length > 0
290 @find 'tbody'
291 .empty().append $rows
292 .end()
293 .find '.warning_box'
294 .remove()
295 .end()
296
297 return ids
298
299 _holds_row: (hold) ->
300
53ade1ec
JD
301 suspended = if hold.holdSuspension? then 1
302
8ebc3fff
SC
303 @find 'td.availability'
304 ._holds_row_avail1 hold
305 .end()
306 .find 'td.actions'
53ade1ec 307 ._actions hold.actions, hold.reserveId, suspended
8ebc3fff
SC
308 .end()
309
310 # Show a title, author, or format by using the given metadata object
311 _row_meta: (meta, classnames...) ->
312
313 status = if arguments.length is 0 then value: false else 'destroy'
e07737bf 314 try @find(".#{n}").progressbar(status) for n in ['title', 'author', 'formats']
8ebc3fff
SC
315
316 return @ unless meta
317
318 $title = $ """
c3ef9f67 319 <a href="/eg/opac/results?query=#{meta.title};qtype=title">#{meta.title}</a>
8ebc3fff
SC
320 """
321 $thumbnail = $ """
508513a1 322 <img src="#{meta.images?.thumbnail?.href}" alt="#{meta.title}" />
8ebc3fff
SC
323 """
324 $author = $ """
c3ef9f67 325 <a href="/eg/opac/results?query=#{meta.author};qtype=author">#{meta.author}</a>
8ebc3fff
SC
326 """
327 for n in classnames
328 $n = @find ".#{n}"
329 switch n
330 when 'thumbnail' then $n.empty().append $thumbnail
331 when 'title' then $n.empty().append $title
332 when 'author' then $n.empty().append $author
e07737bf 333 when 'formats' then $n._show_formats meta?.formats
8ebc3fff
SC
334 return @
335
336 _holds_row_avail1: (hold) ->
337
338 hold_status = if hold.holdSuspension then 0 else if hold.actions.checkout then 1 else 2
339
340 x = if hold.holdSuspension?.suspensionType is 'limited' then 'show' else 'hide'
341
342 tpl = _.template """
343 <div class="suspended">
344 <div style="color: red">Suspended <span class="limited">until <%= activates %></span></div>
345 <ul style="padding-left: 20px">
346 <li name="acct_holds_status"><%= position %> / <%= nHolds %> holds <span class="copies" /></li>
347 <li>Email notification will be sent to <%= email %></li>
348 <li>Hold was placed <%= placed %></li>
349 </ul>
350 </div>
351 <div class="unavailable">
352 <div>Waiting for copy</div>
353 <ul style="padding-left: 20px">
354 <li name="acct_holds_status"><%= position %> / <%= nHolds %> holds <span class="copies" /></li>
355 <li>Email notification will be sent to <%= email %></li>
356 <li>Hold was placed <%= placed %></li>
357 </ul>
358 </div>
359 <div class="available">
360 <div style="color: green">Ready for checkout</div>
361 <ul style="padding-left: 20px">
362 <li name="acct_holds_status"><%= position %> / <%= nHolds %> holds <span class="copies" /></li>
363 <li>Hold will expire <%= expires %></li>
364 </ul>
365 </div>
8b13a139 366 <a href="<%= baseURL %>?ID=<%= id %>">Link to Overdrive Account to change preferences</a>
8ebc3fff
SC
367 """
368 @empty().append tpl
369 position: hold.holdListPosition
370 nHolds: hold.numberOfHolds
371 email: hold.emailAddress
372 expires: hold.holdExpires.fromNow()
373 placed: hold.holdPlacedDate.fromNow()
374 activates: hold.holdSuspension?.numberOfDays.calendar()
8b13a139 375 baseURL: config.baseURL
6a1128e6 376 id: hold.reserveId
8ebc3fff
SC
377
378 # Illuminate areas of this row according to the hold status
379 ._show_from hold_status, '.suspended', '.available', '.unavailable'
380 # Show the hold suspension date only if suspension type is limited
381 .find('.limited')[x]()
382 .end()
383
384 # Complete building a <tr> element for showing a hold by using the
385 # given availability object
386 _holds_row_avail: (avail) ->
387
388 status = if arguments.length is 0 then value: false else 'destroy'
389 @find '.copies'
390 .progressbar status
391 .end()
392
393 return @ unless avail
394
395 text = """
396 on #{avail.copiesOwned} copies
397 """
398 @find '.copies'
399 .text text
400 .end()
401
402 # Build an empty table for showing a list of checkouts
403 _checkouts_main: ->
404
405 table = """
406 <table cellpadding="0" cellspacing="0" border="0">
407 <thead id="acct_checked_main_header"><tr>
408 <th></th>
409 <th>Title/Author</th>
410 <th>Availability</th>
411 <th>Formats</th>
412 <th>Actions</th>
413 </tr></thead>
414 <tbody id="holds_temp_parent"></tbody>
415 </table>
416 <div class="warning_box">No checkouts found.</div>
417 """
418 @empty().append table
419 ._resizeCols()
420
421 # Build <tr> elements for showing a list of checkouts
422 _checkouts_rows: (circs) ->
423 return [] unless circs
424
425 tpl = _.template """
426 <tr id="<%= id %>" name="acct_checked_temp" class="acct_checked_temp inactive-hold">
427 <td class="thumbnail"></td>
428 <td>
429 <div class="title" /> by <div class="author" />
430 </td>
431 <td class="availability"></td>
432 <td class="formats"></td>
433 <td class="actions"></td>
434 </tr>
435 """
436
437 ids = []
438 $rows = for circ in circs
439
440 ids.push circ.reserveId
441
442 # Build an empty row element that is uniquely identified by a
443 # product ID
444 $row = $ tpl id: circ.reserveId
445
446 # Fill the row with circ values and proxy the rest of the row
447 # with progress bars
448 $row
449 ._row_checkout circ # circ values
450 ._row_meta() # progress bars
451
452 # Add checkout rows to <tbody> and remove the warning box.
603dd210 453 if $rows.length > 0
8ebc3fff
SC
454 @find 'tbody'
455 .empty().append $rows
8ebc3fff
SC
456 .end()
457 .find '.warning_box'
458 .remove()
459 .end()
460
461 return ids
462
463 _row_checkout: (circ) ->
464
465 @find 'td.availability'
466 ._checkouts_row_avail circ
467 .end()
468 .find 'td.actions'
e2ef6d2a 469 ._actions circ.actions, circ.reserveId
8ebc3fff
SC
470 .end()
471 .find 'td.formats'
e2ef6d2a 472 ._formats circ.formats, circ.reserveId
8ebc3fff
SC
473 .end()
474
475 _checkouts_row_avail: (circ) ->
476
477 tpl = _.template """
478 <div>Expires <%= expires_relatively %></div>
479 <div><%= expires_exactly %></div>
8b13a139 480 <a href="<%= baseURL %>?ID=<%= id %>">Click to access online (library card required)</a>
8ebc3fff
SC
481 """
482 @empty().append tpl
483 expires_relatively: circ.expires.fromNow()
484 expires_exactly: circ.expires.format 'YYYY MMM D, h:mm:ss a'
8b13a139 485 baseURL: config.baseURL
6a1128e6 486 id: circ.reserveId
8ebc3fff
SC
487
488 # Build a <tr> element to show the available actions of an item.
489 # If the item is available, the check out action should be possible,
490 # and if unavailable, the place hold action should be possible.
491 _holdings_row: (id) ->
492
493 tpl = _.template """
494 <tr id="<%= id %>" name="acct_holds_temp" class="acct_holds_temp inactive-hold">
495 <td class="thumbnail"></td>
496 <td>
497 <div class="title" /> by <div class="author" />
498 </td>
499 <td class="availability"></td>
500 <td class="formats"><ul></ul></td>
501 <td class="actions"></td>
502 </tr>
503 """
504 $row = $(tpl id: id)
505 ._row_meta() # progress bar
506 ._holdings_row_avail() # progress bar
507
508 @find 'tbody'
509 .empty().append $row
510 .end()
511 .find '.warning_box'
512 .remove()
513 .end()
514
515 # Complete building a <tr> element for a holding using the given availability object
516 _holdings_row_avail: (avail) ->
517
518 # Create or destroy progress bars
519 status = if arguments.length is 0 then value: false else 'destroy'
520 @find 'td.availability'
521 .progressbar status
522 .end()
523 .find 'td.actions'
524 .progressbar status
525 .end()
526
527 return @ unless avail
528
529 tpl = _.template """
530 <div class="unavailable">No copies are available for checkout</div>
531 <div class="available" style="color: green">A copy is available for checkout</div>
532 <div><%= n_avail %> of <%= n_owned %> available, <%= n_holds %> holds</div>
533 """
534 @find 'td.availability'
535 .append tpl
536 n_owned: avail.copiesOwned
537 n_avail: avail.copiesAvailable
538 n_holds: avail.numberOfHolds
539 .end()
540
541 # Build action buttons
542 .find 'td.actions'
e2ef6d2a 543 ._actions avail.actions, avail.id
8ebc3fff
SC
544 .end()
545
546 # Illuminate areas of this row according to the holdings status
547 ._show_from (if avail.available then 0 else 1), '.available', '.unavailable'
548