/[webpac2]/Webpacus/root/js/controls.js
This is repository of my old source code which isn't updated any more. Go to git.rot13.org for current projects!
ViewVC logotype

Annotation of /Webpacus/root/js/controls.js

Parent Directory Parent Directory | Revision Log Revision Log


Revision 83 - (hide annotations)
Mon Nov 21 17:46:27 2005 UTC (18 years, 6 months ago) by dpavlin
File MIME type: text/cpp
File size: 15800 byte(s)
Import of web font-end for WebPAC v2 called WebPACus based on Catalyst
1 dpavlin 83 // Copyright (c) 2005 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
2     // (c) 2005 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
3     //
4     // Permission is hereby granted, free of charge, to any person obtaining
5     // a copy of this software and associated documentation files (the
6     // "Software"), to deal in the Software without restriction, including
7     // without limitation the rights to use, copy, modify, merge, publish,
8     // distribute, sublicense, and/or sell copies of the Software, and to
9     // permit persons to whom the Software is furnished to do so, subject to
10     // the following conditions:
11     //
12     // The above copyright notice and this permission notice shall be
13     // included in all copies or substantial portions of the Software.
14     //
15     // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16     // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17     // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18     // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19     // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20     // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21     // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22    
23     Element.collectTextNodesIgnoreClass = function(element, ignoreclass) {
24     var children = $(element).childNodes;
25     var text = "";
26     var classtest = new RegExp("^([^ ]+ )*" + ignoreclass+ "( [^ ]+)*$","i");
27    
28     for (var i = 0; i < children.length; i++) {
29     if(children[i].nodeType==3) {
30     text+=children[i].nodeValue;
31     } else {
32     if((!children[i].className.match(classtest)) && children[i].hasChildNodes())
33     text += Element.collectTextNodesIgnoreClass(children[i], ignoreclass);
34     }
35     }
36    
37     return text;
38     }
39    
40     // Autocompleter.Base handles all the autocompletion functionality
41     // that's independent of the data source for autocompletion. This
42     // includes drawing the autocompletion menu, observing keyboard
43     // and mouse events, and similar.
44     //
45     // Specific autocompleters need to provide, at the very least,
46     // a getUpdatedChoices function that will be invoked every time
47     // the text inside the monitored textbox changes. This method
48     // should get the text for which to provide autocompletion by
49     // invoking this.getEntry(), NOT by directly accessing
50     // this.element.value. This is to allow incremental tokenized
51     // autocompletion. Specific auto-completion logic (AJAX, etc)
52     // belongs in getUpdatedChoices.
53     //
54     // Tokenized incremental autocompletion is enabled automatically
55     // when an autocompleter is instantiated with the 'tokens' option
56     // in the options parameter, e.g.:
57     // new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
58     // will incrementally autocomplete with a comma as the token.
59     // Additionally, ',' in the above example can be replaced with
60     // a token array, e.g. { tokens: new Array (',', '\n') } which
61     // enables autocompletion on multiple tokens. This is most
62     // useful when one of the tokens is \n (a newline), as it
63     // allows smart autocompletion after linebreaks.
64    
65     var Autocompleter = {}
66     Autocompleter.Base = function() {};
67     Autocompleter.Base.prototype = {
68     base_initialize: function(element, update, options) {
69     this.element = $(element);
70     this.update = $(update);
71     this.has_focus = false;
72     this.changed = false;
73     this.active = false;
74     this.index = 0;
75     this.entry_count = 0;
76    
77     if (this.setOptions)
78     this.setOptions(options);
79     else
80     this.options = {}
81    
82     this.options.tokens = this.options.tokens || new Array();
83     this.options.frequency = this.options.frequency || 0.4;
84     this.options.min_chars = this.options.min_chars || 1;
85     this.options.onShow = this.options.onShow ||
86     function(element, update){
87     if(!update.style.position || update.style.position=='absolute') {
88     update.style.position = 'absolute';
89     var offsets = Position.cumulativeOffset(element);
90     update.style.left = offsets[0] + 'px';
91     update.style.top = (offsets[1] + element.offsetHeight) + 'px';
92     update.style.width = element.offsetWidth + 'px';
93     }
94     new Effect.Appear(update,{duration:0.15});
95     };
96     this.options.onHide = this.options.onHide ||
97     function(element, update){ new Effect.Fade(update,{duration:0.15}) };
98    
99     if(this.options.indicator)
100     this.indicator = $(this.options.indicator);
101    
102     if (typeof(this.options.tokens) == 'string')
103     this.options.tokens = new Array(this.options.tokens);
104    
105     this.observer = null;
106    
107     Element.hide(this.update);
108    
109     Event.observe(this.element, "blur", this.onBlur.bindAsEventListener(this));
110     Event.observe(this.element, "keypress", this.onKeyPress.bindAsEventListener(this));
111     },
112    
113     show: function() {
114     if(this.update.style.display=='none') this.options.onShow(this.element, this.update);
115     if(!this.iefix && (navigator.appVersion.indexOf('MSIE')>0) && this.update.style.position=='absolute') {
116     new Insertion.After(this.update,
117     '<iframe id="' + this.update.id + '_iefix" '+
118     'style="display:none;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
119     'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
120     this.iefix = $(this.update.id+'_iefix');
121     }
122     if(this.iefix) {
123     Position.clone(this.update, this.iefix);
124     this.iefix.style.zIndex = 1;
125     this.update.style.zIndex = 2;
126     Element.show(this.iefix);
127     }
128     },
129    
130     hide: function() {
131     if(this.update.style.display=='') this.options.onHide(this.element, this.update);
132     if(this.iefix) Element.hide(this.iefix);
133     },
134    
135     startIndicator: function() {
136     if(this.indicator) Element.show(this.indicator);
137     },
138    
139     stopIndicator: function() {
140     if(this.indicator) Element.hide(this.indicator);
141     },
142    
143     onKeyPress: function(event) {
144     if(this.active)
145     switch(event.keyCode) {
146     case Event.KEY_TAB:
147     case Event.KEY_RETURN:
148     this.select_entry();
149     Event.stop(event);
150     case Event.KEY_ESC:
151     this.hide();
152     this.active = false;
153     return;
154     case Event.KEY_LEFT:
155     case Event.KEY_RIGHT:
156     return;
157     case Event.KEY_UP:
158     this.mark_previous();
159     this.render();
160     if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
161     return;
162     case Event.KEY_DOWN:
163     this.mark_next();
164     this.render();
165     if(navigator.appVersion.indexOf('AppleWebKit')>0) Event.stop(event);
166     return;
167     }
168     else
169     if(event.keyCode==Event.KEY_TAB || event.keyCode==Event.KEY_RETURN)
170     return;
171    
172     this.changed = true;
173     this.has_focus = true;
174    
175     if(this.observer) clearTimeout(this.observer);
176     this.observer =
177     setTimeout(this.onObserverEvent.bind(this), this.options.frequency*1000);
178     },
179    
180     onHover: function(event) {
181     var element = Event.findElement(event, 'LI');
182     if(this.index != element.autocompleteIndex)
183     {
184     this.index = element.autocompleteIndex;
185     this.render();
186     }
187     Event.stop(event);
188     },
189    
190     onClick: function(event) {
191     var element = Event.findElement(event, 'LI');
192     this.index = element.autocompleteIndex;
193     this.select_entry();
194     Event.stop(event);
195     },
196    
197     onBlur: function(event) {
198     // needed to make click events working
199     setTimeout(this.hide.bind(this), 250);
200     this.has_focus = false;
201     this.active = false;
202     },
203    
204     render: function() {
205     if(this.entry_count > 0) {
206     for (var i = 0; i < this.entry_count; i++)
207     this.index==i ?
208     Element.addClassName(this.get_entry(i),"selected") :
209     Element.removeClassName(this.get_entry(i),"selected");
210    
211     if(this.has_focus) {
212     if(this.get_current_entry().scrollIntoView)
213     this.get_current_entry().scrollIntoView(false);
214    
215     this.show();
216     this.active = true;
217     }
218     } else this.hide();
219     },
220    
221     mark_previous: function() {
222     if(this.index > 0) this.index--
223     else this.index = this.entry_count-1;
224     },
225    
226     mark_next: function() {
227     if(this.index < this.entry_count-1) this.index++
228     else this.index = 0;
229     },
230    
231     get_entry: function(index) {
232     return this.update.firstChild.childNodes[index];
233     },
234    
235     get_current_entry: function() {
236     return this.get_entry(this.index);
237     },
238    
239     select_entry: function() {
240     this.active = false;
241     value = Element.collectTextNodesIgnoreClass(this.get_current_entry(), 'informal').unescapeHTML();
242     this.updateElement(value);
243     this.element.focus();
244     },
245    
246     updateElement: function(value) {
247     var last_token_pos = this.findLastToken();
248     if (last_token_pos != -1) {
249     var new_value = this.element.value.substr(0, last_token_pos + 1);
250     var whitespace = this.element.value.substr(last_token_pos + 1).match(/^\s+/);
251     if (whitespace)
252     new_value += whitespace[0];
253     this.element.value = new_value + value;
254     } else {
255     this.element.value = value;
256     }
257     },
258    
259     updateChoices: function(choices) {
260     if(!this.changed && this.has_focus) {
261     this.update.innerHTML = choices;
262     Element.cleanWhitespace(this.update);
263     Element.cleanWhitespace(this.update.firstChild);
264    
265     if(this.update.firstChild && this.update.firstChild.childNodes) {
266     this.entry_count =
267     this.update.firstChild.childNodes.length;
268     for (var i = 0; i < this.entry_count; i++) {
269     entry = this.get_entry(i);
270     entry.autocompleteIndex = i;
271     this.addObservers(entry);
272     }
273     } else {
274     this.entry_count = 0;
275     }
276    
277     this.stopIndicator();
278    
279     this.index = 0;
280     this.render();
281     }
282     },
283    
284     addObservers: function(element) {
285     Event.observe(element, "mouseover", this.onHover.bindAsEventListener(this));
286     Event.observe(element, "click", this.onClick.bindAsEventListener(this));
287     },
288    
289     onObserverEvent: function() {
290     this.changed = false;
291     if(this.getEntry().length>=this.options.min_chars) {
292     this.startIndicator();
293     this.getUpdatedChoices();
294     } else {
295     this.active = false;
296     this.hide();
297     }
298     },
299    
300     getEntry: function() {
301     var token_pos = this.findLastToken();
302     if (token_pos != -1)
303     var ret = this.element.value.substr(token_pos + 1).replace(/^\s+/,'').replace(/\s+$/,'');
304     else
305     var ret = this.element.value;
306    
307     return /\n/.test(ret) ? '' : ret;
308     },
309    
310     findLastToken: function() {
311     var last_token_pos = -1;
312    
313     for (var i=0; i<this.options.tokens.length; i++) {
314     var this_token_pos = this.element.value.lastIndexOf(this.options.tokens[i]);
315     if (this_token_pos > last_token_pos)
316     last_token_pos = this_token_pos;
317     }
318     return last_token_pos;
319     }
320     }
321    
322     Ajax.Autocompleter = Class.create();
323     Ajax.Autocompleter.prototype = Object.extend(new Autocompleter.Base(),
324     Object.extend(new Ajax.Base(), {
325     initialize: function(element, update, url, options) {
326     this.base_initialize(element, update, options);
327     this.options.asynchronous = true;
328     this.options.onComplete = this.onComplete.bind(this)
329     this.options.method = 'post';
330     this.options.defaultParams = this.options.parameters || null;
331     this.url = url;
332     },
333    
334     getUpdatedChoices: function() {
335     entry = encodeURIComponent(this.element.name) + '=' +
336     encodeURIComponent(this.getEntry());
337    
338     this.options.parameters = this.options.callback ?
339     this.options.callback(this.element, entry) : entry;
340    
341     if(this.options.defaultParams)
342     this.options.parameters += '&' + this.options.defaultParams;
343    
344     new Ajax.Request(this.url, this.options);
345     },
346    
347     onComplete: function(request) {
348     this.updateChoices(request.responseText);
349     }
350    
351     }));
352    
353     // The local array autocompleter. Used when you'd prefer to
354     // inject an array of autocompletion options into the page, rather
355     // than sending out Ajax queries, which can be quite slow sometimes.
356     //
357     // The constructor takes four parameters. The first two are, as usual,
358     // the id of the monitored textbox, and id of the autocompletion menu.
359     // The third is the array you want to autocomplete from, and the fourth
360     // is the options block.
361     //
362     // Extra local autocompletion options:
363     // - choices - How many autocompletion choices to offer
364     //
365     // - partial_search - If false, the autocompleter will match entered
366     // text only at the beginning of strings in the
367     // autocomplete array. Defaults to true, which will
368     // match text at the beginning of any *word* in the
369     // strings in the autocomplete array. If you want to
370     // search anywhere in the string, additionally set
371     // the option full_search to true (default: off).
372     //
373     // - full_search - Search anywhere in autocomplete array strings.
374     //
375     // - partial_chars - How many characters to enter before triggering
376     // a partial match (unlike min_chars, which defines
377     // how many characters are required to do any match
378     // at all). Defaults to 2.
379     //
380     // - ignore_case - Whether to ignore case when autocompleting.
381     // Defaults to true.
382     //
383     // It's possible to pass in a custom function as the 'selector'
384     // option, if you prefer to write your own autocompletion logic.
385     // In that case, the other options above will not apply unless
386     // you support them.
387    
388     Autocompleter.Local = Class.create();
389     Autocompleter.Local.prototype = Object.extend(new Autocompleter.Base(), {
390     initialize: function(element, update, array, options) {
391     this.base_initialize(element, update, options);
392     this.options.array = array;
393     },
394    
395     getUpdatedChoices: function() {
396     this.updateChoices(this.options.selector(this));
397     },
398    
399     setOptions: function(options) {
400     this.options = Object.extend({
401     choices: 10,
402     partial_search: true,
403     partial_chars: 2,
404     ignore_case: true,
405     full_search: false,
406     selector: function(instance) {
407     var ret = new Array(); // Beginning matches
408     var partial = new Array(); // Inside matches
409     var entry = instance.getEntry();
410     var count = 0;
411    
412     for (var i = 0; i < instance.options.array.length &&
413     ret.length < instance.options.choices ; i++) {
414     var elem = instance.options.array[i];
415     var found_pos = instance.options.ignore_case ?
416     elem.toLowerCase().indexOf(entry.toLowerCase()) :
417     elem.indexOf(entry);
418    
419     while (found_pos != -1) {
420     if (found_pos == 0 && elem.length != entry.length) {
421     ret.push("<li><strong>" + elem.substr(0, entry.length) + "</strong>" +
422     elem.substr(entry.length) + "</li>");
423     break;
424     } else if (entry.length >= instance.options.partial_chars &&
425     instance.options.partial_search && found_pos != -1) {
426     if (instance.options.full_search || /\s/.test(elem.substr(found_pos-1,1))) {
427     partial.push("<li>" + elem.substr(0, found_pos) + "<strong>" +
428     elem.substr(found_pos, entry.length) + "</strong>" + elem.substr(
429     found_pos + entry.length) + "</li>");
430     break;
431     }
432     }
433    
434     found_pos = instance.options.ignore_case ?
435     elem.toLowerCase().indexOf(entry.toLowerCase(), found_pos + 1) :
436     elem.indexOf(entry, found_pos + 1);
437    
438     }
439     }
440     if (partial.length)
441     ret = ret.concat(partial.slice(0, instance.options.choices - ret.length))
442     return "<ul>" + ret.join('') + "</ul>";
443     }
444     }, options || {});
445     }
446     });

Properties

Name Value
svn:mime-type text/cpp

  ViewVC Help
Powered by ViewVC 1.1.26