PageSourceSearch

https://ebooks.edu.gr/ebooks/static/js/choice-support.js?v1.1

js ebooks.edu.gr collected 2026-09-24 09:16:53 UTC 21,952 bytes, 588 lines download raw bytes

1/*
2 * The contents of this file are subject to the license and copyright
3 * detailed in the LICENSE and NOTICE files at the root of the source
4 * tree and available online at
5 *
6 * http://www.dspace.org/license/
7 */
8// Client-side scripting to support DSpace Choice Control
9
10// IMPORTANT NOTE:
11//  This code depends on a *MODIFIED* version of the
12//  script.aculo.us controls v1.8.2, fixed to avoid a bug that
13//  affects autocomplete in Firefox 3.  This code is included in DSpace.
14
15// Entry points:
16//  1. DSpaceAutocomplete -- add autocomplete (suggest) to an input field
17//
18//  2.  DSpaceChoiceLookup -- create popup window with authority choices
19//
20//  @Author: Larry Stone  <[email protected]>
21//  $Revision $
22
23// -------------------- support for Autocomplete (Suggest)
24
25// Autocomplete utility:
26// Arguments:
27//   formID -- ID attribute of form tag
28//   args properties:
29//     metadataField -- metadata field e.g. dc_contributor_author
30//     inputName -- input field name for text input, or base of "Name" pair
31//     authorityName -- input field name in which to set authority
32//     containerID -- ID attribute of DIV to hold the menu objects
33//     indicatorID -- ID attribute of element to use as a "loading" indicator
34//     confidenceIndicatorID -- ID of element on which to set confidence
35//     confidenceName - NAME of confidence input (not ID)
36//     contextPath -- URL path prefix (i.e. webapp contextPath) for DSpace.
37//     collection -- db ID of dspace collection to serve as context
38//     isClosed -- true if authority value is required, false = non-auth allowed
39// XXX Can't really enforce "isClosed=true" with autocomplete, user can type anything
40//
41// NOTE: Successful autocomplete always sets confidence to 'accepted' since
42//  authority value (if any) *was* chosen interactively by a human.
43function DSpaceSetupAutocomplete(formID, args)
44{
45    if (args.authorityName == null)
46        args.authorityName = dspace_makeFieldInput(args.inputName,'_authority');
47
48    var authInput = jQuery('#'+formID + ' input[name=\''+args.authorityName+'\']');
49    var input = jQuery('#'+formID + ' input[name=\''+args.inputName+'\']');
50    input.parent('td').attr('style','white-space:nowrap;');
51    // AJAX menu source, can add &query=TEXT
52    var choiceURL = args.contextPath+"/choices/"+args.metadataField;
53    var collID = args.collection == null ? -1 : args.collection;
54    if (authInput != null)
55	{
56    	input.data('previousData', {authority: authInput.val(), value: input.val()});
57	}
58    var options = {
59    		lenght: 2,
60    		search: function(event, ui) {
61    			jQuery('#'+args.indicatorID).show();
62    		},
63    		source: function( request, response ) {			
64    			jQuery.ajax({
65    				url: choiceURL,			
66    				dataType: "xml",
67    				data: {
68    					query: request.term,
69    					collection: collID,
70    					format: 'ul'
71    				},    			
72    				success: function( data ) {
73    					jQuery('#'+args.indicatorID).hide();
74    					
75    					response(jQuery("li", data ).map(function() {
76    						return {
77    							authority : jQuery(this).attr('authority'),
78    							label : jQuery('span.label',this).text(),
79    							value : jQuery('span.value',this).text()
80    						};
81    					}));
82    				}
83    			});
84    		},   				
85    		select: function(event, ui){
86    			input.data('previousData', ui.item);
87    			var authority = ui.item.authority;
88				var authValue = authority;
89				if (authInput != null)
90                {
91					authInput.val(authValue);
92                    // update confidence input's value too if available.
93                    if (args.confidenceName != null)
94                    {
95                        var confInput = jQuery('#'+formID + ' input[name=\''+args.confidenceName+'\']');
96                        if (confInput != null)
97                    	{
98                        	if (authority != '')
99                        	{
100                        		confInput.val('accepted');
101                        	}
102                        	else
103                    		{
104                        		confInput.val('');
105                        	}
106                    	}
107                    }
108                }
109				// make indicator blank if no authority value
110                DSpaceUpdateConfidence(document, args.confidenceIndicatorID,
111                    authValue == null || authValue == '' ? 'blank' :'accepted');
112			}			
113    };
114    input.autocomplete(options).change(function(){
115		var lastSel = input.data('previousData');
116		var newauth = '';
117		var newval = input.val();
118		if (authInput != null)
119		{
120			newauth = authInput.val();
121		}
122		if (newauth != lastSel.authority || newval != lastSel.value)
123		{
124			if (authInput != null)
125			{
126				authInput.val(' ');
127				DSpaceUpdateConfidence(document, args.confidenceIndicatorID, 'blank');
128			}	
129		}
130	});
131}
132
133// -------------------- support for Lookup Popup
134
135// Create popup window with authority choices for value of an input field.
136// This is intended to be called by onClick of a "Lookup" or "Add"  button.
137function DSpaceChoiceLookup(url, field, formID, valueInput, authInput,
138    confIndicatorID, collectionID, isName, isRepeating)
139{
140    // fill in URL
141    url += '?field='+field+'&formID='+formID+'&valueInput='+valueInput+
142             '&authorityInput='+authInput+'&collection='+collectionID+
143             '&isName='+isName+'&isRepeating='+isRepeating+'&confIndicatorID='+confIndicatorID;
144
145    // primary input field - for positioning popup.
146    var inputFieldName = isName ? dspace_makeFieldInput(valueInput,'_last') : valueInput;
147    var inputField = document.getElementById(formID).elements[inputFieldName];
148    // scriptactulous magic to figure out true offset:
149    var cOffset = 0;
150    if (inputField != null)
151        cOffset = $(inputField).cumulativeOffset();
152    var width = 600;  // XXX guesses! these should be params, or configured..
153    var height = 470;
154    var left; var top;
155    if (window.screenX == null) {
156        left = window.screenLeft + cOffset.left - (width/2);
157        top = window.screenTop + cOffset.top - (height/2);
158    } else {
159        left = window.screenX + cOffset.left - (width/2);
160        top = window.screenY + cOffset.top - (height/2);
161    }
162    if (left < 0) left = 0;
163    if (top < 0) top = 0;
164    var pw = window.open(url, 'ignoreme',
165         'width='+width+',height='+height+',left='+left+',top='+top+
166         ',toolbar=no,menubar=no,location=no,status=no,resizable');
167    if (window.focus) pw.focus();
168    return false;
169}
170
171// Run this as the Lookup page is loaded to initialize DOM objects, load choices
172function DSpaceChoicesSetup(form)
173{
174    // find the "LEGEND" in fieldset, which acts as page title,
175    // and save it as a bogus form element, e.g. elements['statline']
176    var fieldset = document.getElementById('aspect_general_ChoiceLookupTransformer_list_choicesList');
177    for (i = 0; i < fieldset.childNodes.length; ++i)
178    {
179      if (fieldset.childNodes[i].nodeName == 'LEGEND')
180      {
181          form.statline = fieldset.childNodes[i];
182          form.statline_template = fieldset.childNodes[i].innerHTML;
183          fieldset.childNodes[i].innerHTML = "Loading...";
184          break;
185      }
186    }
187    DSpaceChoicesLoad(form);
188}
189
190
191// populate the "select" with options from ajax request
192// stash some parameters as properties of the "select" so we can add to
193// the last start index to query for next set of results.
194function DSpaceChoicesLoad(form)
195{
196    var field = form.elements['paramField'].value;
197    var value = form.elements['paramValue'].value;
198    var start = form.elements['paramStart'].value;
199    var limit = form.elements['paramLimit'].value;
200    var formID = form.elements['paramFormID'].value;
201    var collID = form.elements['paramCollection'].value;
202    var isName = form.elements['paramIsName'].value == 'true';
203    var isRepeating = form.elements['paramIsRepeating'].value == 'true';
204    var isClosed = form.elements['paramIsClosed'].value == 'true';
205    var contextPath = form.elements['contextPath'].value;
206    var fail = form.elements['paramFail'].value;
207    var valueInput = form.elements['paramValueInput'].value;
208    var nonAuthority = "";
209    if (form.elements['paramNonAuthority'] != null)
210        nonAuthority = form.elements['paramNonAuthority'].value;
211
212    // get value from form inputs in opener if not explicitly supplied
213    if (value.length == 0)
214    {
215        var of = window.opener.document.getElementById(formID);
216        if (isName)
217            value = makePersonName(of.elements[dspace_makeFieldInput(valueInput,'_last')].value,
218                                   of.elements[dspace_makeFieldInput(valueInput,'_first')].value);
219        else
220            value = of.elements[valueInput].value;
221
222        // if this is a repeating input, clear the source value so that e.g.
223        // clicking "Next" on a submit-describe page will not *add* the proposed
224        // lookup text as a metadata value:
225        if (isRepeating)
226        {
227            if (isName)
228            {
229                of.elements[dspace_makeFieldInput(valueInput,'_last')].value = null;
230                of.elements[dspace_makeFieldInput(valueInput,'_first')].value = null;
231            }
232            else
233                of.elements[valueInput].value = null;
234        }
235    }
236
237    // start spinner
238    var indicator = document.getElementById('lookup_indicator_id');
239    if (indicator != null)
240        indicator.style.display = "inline";
241
242    new Ajax.Request(contextPath+"/choices/"+field,
243      {
244        method: "get",
245        parameters: {query: value, format: 'select', collection: collID,
246                     start: start, limit: limit},
247        // triggered by any exception, even in success
248        onException: function(req, e) {
249          window.alert(fail+" Exception="+e);
250          if (indicator != null) indicator.style.display = "none";
251        },
252        // when http load of choices fails
253        onFailure: function() {
254          window.alert(fail+" HTTP error resonse");
255          if (indicator != null) indicator.style.display = "none";
256        },
257        // format is <select><option authority="key" value="val">label</option>...
258        onSuccess: function(transport) {
259          var ul = transport.responseXML.documentElement;
260          var err = ul.getAttributeNode('error');
261          if (err != null && err.value == 'true')
262              window.alert(fail+" Server indicates error in response.");
263          var opts = ul.getElementsByTagName('option');
264
265          // update range message and update 'more' button
266          var oldStart = 1 * ul.getAttributeNode('start').value;
267          var nextStart = oldStart + opts.length;
268          var lastTotal = ul.getAttributeNode('total').value;
269          var resultMore = ul.getAttributeNode('more');
270          form.elements['more'].disabled = !(resultMore != null && resultMore.value == 'true');
271          form.elements['paramStart'].value = nextStart;
272
273          // clear select first
274          var select = form.elements['chooser'];
275          for (var i = select.length-1; i >= 0; --i)
276            select.remove(i);
277
278          // load select and look for default selection
279          var selectedByValue = -1; // select by value match
280          var selectedByChoices = -1;  // Choice says its selected
281          for (var i = 0; i < opts.length; ++i)
282          {
283            var opt = opts.item(i);
284            var olabel = "";
285            for (var j = 0; j < opt.childNodes.length; ++j)
286            {
287               var node = opt.childNodes[j];
288               if (node.nodeName == "#text")
289                 olabel += node.data;
290            }
291            var ovalue = opt.getAttributeNode('value').value;
292            var option = new Option(olabel, ovalue);
293            option.authority = opt.getAttributeNode('authority').value;
294            select.add(option, null);
295            if (value == ovalue)
296                selectedByValue = select.options.length - 1;
297            if (opt.getAttributeNode('selected') != null)
298                selectedByChoices = select.options.length - 1;
299          }
300          // add non-authority option if needed.
301          if (!isClosed)
302          {
303            select.add(new Option(dspace_formatMessage(nonAuthority, value), value), null);
304          }
305          var defaultSelected = -1;
306          if (selectedByChoices >= 0)
307            defaultSelected = selectedByChoices;
308          else if (selectedByValue >= 0)
309            defaultSelected = selectedByValue;
310          else if (select.options.length == 1)
311            defaultSelected = 0;
312
313          // load default-selected value
314          if (defaultSelected >= 0)
315          {
316            select.options[defaultSelected].defaultSelected = true;
317            var so = select.options[defaultSelected];
318            if (isName)
319            {
320                form.elements['text1'].value = lastNameOf(so.value);
321                form.elements['text2'].value = firstNameOf(so.value);
322            }
323            else
324                form.elements['text1'].value = so.value;
325          }
326
327          // turn off spinner
328          if (indicator != null)
329              indicator.style.display = "none";
330
331          // "results" status line
332          var statLast =  nextStart + (isClosed ? 2 : 1);
333
334          form.statline.innerHTML =
335            dspace_formatMessage(form.statline_template,
336              oldStart+1, statLast, Math.max(lastTotal,statLast), value);
337        }
338      });
339}
340
341// handler for change event on choice selector - load new values
342function DSpaceChoicesSelectOnChange ()
343{
344    // "this" is the window,
345    var form = document.getElementById('aspect_general_ChoiceLookupTransformer_div_lookup');
346    var select = form.elements['chooser'];
347    var so = select.options[select.selectedIndex];
348    var isName = form.elements['paramIsName'].value == 'true';
349
350    if (isName)
351    {
352        form.elements['text1'].value = lastNameOf(so.value);
353        form.elements['text2'].value = firstNameOf(so.value);
354    }
355    else
356        form.elements['text1'].value = so.value;
357}
358
359// handler for lookup popup's accept (or add) button
360//  stuff values back to calling page, force an add if necessary, and close.
361function DSpaceChoicesAcceptOnClick ()
362{
363    var select = this.form.elements['chooser'];
364    var isName = this.form.elements['paramIsName'].value == 'true';
365    var isRepeating = this.form.elements['paramIsRepeating'].value == 'true';
366    var valueInput = this.form.elements['paramValueInput'].value;
367    var authorityInput = this.form.elements['paramAuthorityInput'].value;
368    var formID = this.form.elements['paramFormID'].value;
369    var confIndicatorID = this.form.elements['paramConfIndicatorID'] == null?null:this.form.elements['paramConfIndicatorID'].value;
370
371    // default the authority input if not supplied.
372    if (authorityInput.length == 0)
373        authorityInput = dspace_makeFieldInput(valueInput,'_authority');
374
375    // always stuff text fields back into caller's value input(s)
376    if (valueInput.length > 0)
377    {
378        var of = window.opener.document.getElementById(formID);
379        if (isName)
380        {
381            of.elements[dspace_makeFieldInput(valueInput,'_last')].value = this.form.elements['text1'].value;
382            of.elements[dspace_makeFieldInput(valueInput,'_first')].value = this.form.elements['text2'].value;
383        }
384        else
385            of.elements[valueInput].value = this.form.elements['text1'].value;
386
387        if (authorityInput.length > 0 && of.elements[authorityInput] != null)
388        {
389            // conf input is auth input, substitute '_confidence' for '_authority'
390            // if conf fieldname is  FIELD_confidence_NUMBER, then '_authority_' => '_confidence_'
391            var confInput = "";
392
393            var ci = authorityInput.lastIndexOf("_authority_");
394            if (ci < 0)
395                confInput = authorityInput.substring(0, authorityInput.length-10)+'_confidence';
396            else
397                confInput = authorityInput.substring(0, ci)+"_confidence_"+authorityInput.substring(ci+11);
398            // DEBUG:
399            // window.alert('Setting fields auth="'+authorityInput+'", conf="'+confInput+'"');
400
401            var authValue = null;
402            if (select.selectedIndex >= 0 && select.options[select.selectedIndex].authority != null)
403            {
404                authValue = select.options[select.selectedIndex].authority;
405                of.elements[authorityInput].value = authValue;
406            }
407            if (of.elements[confInput] != null)
408                of.elements[confInput].value = 'accepted';
409            // make indicator blank if no authority value
410            DSpaceUpdateConfidence(window.opener.document, confIndicatorID,
411                    authValue == null || authValue == '' ? 'blank' :'accepted');
412        }
413
414        // force the submit button -- if there is an "add"
415        if (isRepeating)
416        {
417            var add = of.elements["submit_"+valueInput+"_add"];
418            if (add != null)
419                add.click();
420            else
421                alert('Sanity check: Cannot find button named "submit_'+valueInput+'_add"');
422        }
423    }
424    window.close();
425    return false;
426}
427
428// handler for lookup popup's more button
429function DSpaceChoicesMoreOnClick ()
430{
431    DSpaceChoicesLoad(this.form);
432}
433
434// handler for lookup popup's cancel button
435function DSpaceChoicesCancelOnClick ()
436{
437    window.close();
438    return false;
439}
440
441// -------------------- Utilities
442
443// DSpace person-name conventions, see DCPersonName
444function makePersonName(lastName, firstName)
445{
446    return (firstName == null || firstName.length == 0) ? lastName :
447        lastName+", "+firstName;
448}
449
450// DSpace person-name conventions, see DCPersonName
451function firstNameOf(personName)
452{
453    var comma = personName.indexOf(",");
454    return (comma < 0) ? "" : stringTrim(personName.substring(comma+1));
455}
456
457// DSpace person-name conventions, see DCPersonName
458function lastNameOf(personName)
459{
460    var comma = personName.indexOf(",");
461    return stringTrim((comma < 0) ? personName : personName.substring(0, comma));
462}
463
464// replicate java String.trim()
465function stringTrim(str)
466{
467    var start = 0;
468    var end = str.length;
469    for (; str.charAt(start) == ' '&& start < end; ++start) ;
470    for (; end > start && str.charAt(end-1) == ' '; --end) ;
471    return str.slice(start, end);
472}
473
474// format utility - replace @1@, @2@ etc with args 1, 2, 3..
475// NOTE params MUST be monotonically increasing
476// NOTE we can't use "{1}" like the i18n catalog because it elides them!!
477// ...UNLESS maybe it were to be fixed not to when no params...
478function dspace_formatMessage()
479{
480    var template = dspace_formatMessage.arguments[0];
481    var i;
482    for (i = 1; i < arguments.length; ++i)
483    {
484        var pattern = '@'+i+'@';
485        if (template.search(pattern) >= 0)
486            template = template.replace(pattern, dspace_formatMessage.arguments[i]);
487    }
488    return template;
489}
490
491// utility to make sub-field name of input field, e.g. _last, _first, _auth..
492// if name ends with _1, _2 etc, put sub-name BEFORE the number
493function dspace_makeFieldInput(name, sub)
494{
495    var i = name.search("_[0-9]+$");
496    if (i < 0)
497        return name+sub;
498    else
499        return name.substr(0, i)+sub+name.substr(i);
500}
501
502// update the class value of confidence-indicating element
503function DSpaceUpdateConfidence(doc, confIndicatorID, newValue)
504{
505    // sanity checks - need valid ID and a real DOM object
506    if (confIndicatorID == null || confIndicatorID == "")
507        return;
508    var confElt = doc.getElementById(confIndicatorID);
509    if (confElt == null)
510        return;
511
512    // add or update CSS class with new confidence value, e.g. "cf-accepted".
513    if (confElt.className == null)
514        confElt.className = "cf-"+newValue;
515    else
516    {
517        var classes = confElt.className.split(" ");
518        var newClasses = "";
519        var found = false;
520        for (var i = 0; i < classes.length; ++i)
521        {
522            if (classes[i].match('^cf-[a-zA-Z0-9]+$'))
523            {
524                newClasses += "cf-"+newValue+" ";
525                found = true;
526            }
527            else
528                newClasses += classes[i]+" ";
529        }
530        if (!found)
531            newClasses += "cf-"+newValue+" ";
532        confElt.className = newClasses;
533    }
534}
535
536// respond to "onchanged" event on authority input field
537// set confidence to 'accepted' if authority was changed by user.
538function DSpaceAuthorityOnChange(self, confValueID, confIndicatorID)
539{
540    var confidence = 'accepted';
541    if (confValueID != null && confValueID != '')
542    {
543        var confValueField = document.getElementById(confValueID);
544        if (confValueField != null)
545            confValueField.value = confidence;
546    }
547    DSpaceUpdateConfidence(document, confIndicatorID, confidence)
548    return false;
549}
550
551// respond to click on the authority-value lock button in Edit Item Metadata:
552// "button" is bound to the image input for the lock button, "this"
553function DSpaceToggleAuthorityLock(button, authInputID)
554{
555    // sanity checks - need valid ID and a real DOM object
556    if (authInputID == null || authInputID == '')
557        return false;
558    var authInput = document.getElementById(authInputID);
559    if (authInput == null)
560        return false;
561
562    // look for is-locked or is-unlocked in class list:
563    var classes = button.className.split(' ');
564    var newClass = '';
565    var newLocked = false;
566    var found = false;
567    for (var i = 0; i < classes.length; ++i)
568    {
569        if (classes[i] == 'is-locked')
570        {
571            newLocked = false;
572            found = true;
573        }
574        else if (classes[i] == 'is-unlocked')
575        {
576            newLocked = true;
577            found = true;
578        }
579        else
580            newClass += classes[i]+' ';
581    }
582    if (!found)
583        return false;
584    // toggle the image, and set readability
585    button.className = newClass + (newLocked ? 'is-locked' : 'is-unlocked') + ' ';
586    authInput.readOnly = newLocked;
587    return false;
588}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.