Fix account summary display in Evergreen 2.9+
[sitka/overdrive-evergreen-opac.git] / src / od_pages_myopac.coffee
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
4 define [
5         'jquery-noconflict'
6         'lodash'
7         'od_config'
8         'jquery-ui'
9         'od_action'
10         'od_pages_opac'
11 ], ($, _, config) ->
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
27                                 @append $('<div id="dashboard">')
28
29                         else
30                                 # Add a new dashboard for to show counts of e-items; start with
31                                 # zero counts
32                                 base = '/eg/opac/myopac'
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()
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
57                                 @_counters x # Change the values of the counters
58
59                         return @
60
61                 # Replace account summary area with one that shows links to go to
62                 # physical and e-items lists
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
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]
71
72                                 tpl = _.template """
73                                 <tbody>
74                                         <tr>
75                                                 <td>
76                                                         <a href="/eg/opac/myopac/circs">
77                                                                 <span><span class="ncheckouts" /> <%= ncheckouts %> Items Currently Checked out</span>
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>
86                                                         <a href="/eg/opac/myopac/holds"><span class="nholds" /> <%= nholds %> Items Currently on Hold</a>
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>
94                                                         <a href="/eg/opac/myopac/holds?available=1"><span class="nready" /> <%= nready %> Items ready for pickup</a>
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                                 """
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()
118
119                         else
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()
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
301                         suspended = if hold.holdSuspension? then 1
302
303                         @find 'td.availability'
304                                 ._holds_row_avail1 hold
305                                 .end()
306                         .find 'td.actions'
307                                 ._actions hold.actions, hold.reserveId, suspended
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'
314                         try @find(".#{n}").progressbar(status) for n in ['title', 'author', 'formats']
315
316                         return @ unless meta
317
318                         $title = $ """
319                         <a href="/eg/opac/results?query=#{meta.title};qtype=title">#{meta.title}</a>
320                         """
321                         $thumbnail = $ """
322                         <img src="#{meta.images?.thumbnail?.href}" alt="#{meta.title}" />
323                         """
324                         $author = $ """
325                         <a href="/eg/opac/results?query=#{meta.author};qtype=author">#{meta.author}</a>
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
333                                         when 'formats'   then $n._show_formats meta?.formats
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>
366                         <a href="<%= baseURL %>?ID=<%= id %>">Link to Overdrive Account to change preferences</a>
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()
375                                 baseURL:   config.baseURL
376                                 id:        hold.reserveId
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.
453                         if $rows.length > 0
454                                 @find 'tbody'
455                                         .empty().append $rows
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'
469                                 ._actions circ.actions, circ.reserveId
470                                 .end()
471                         .find 'td.formats'
472                                 ._formats circ.formats, circ.reserveId
473                                 .end()
474
475                 _checkouts_row_avail: (circ) ->
476
477                         tpl = _.template """
478                         <div>Expires <%= expires_relatively %></div>
479                         <div><%= expires_exactly %></div>
480                         <a href="<%= baseURL %>?ID=<%= id %>">Click to access online (library card required)</a>
481                         """
482                         @empty().append tpl
483                                 expires_relatively: circ.expires.fromNow()
484                                 expires_exactly:    circ.expires.format 'YYYY MMM D, h:mm:ss a'
485                                 baseURL:            config.baseURL
486                                 id:                 circ.reserveId
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'
543                                 ._actions avail.actions, avail.id
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