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