Dotclear

source: admin/js/jquery/jquery.autocomplete.js @ 2114:7f63aa8eeebf

Revision 2114:7f63aa8eeebf, 21.8 KB checked in by Nicolas <nikrou77@…>, 12 years ago (diff)

Upgrade jquery.autocomplete.js from agarzola fork ; addresses #1636
 https://github.com/agarzola/jQueryAutocompletePlugin/blob/master/jquery.autocomplete.js
A good idea should be to migrate to ui.autocomplete

Line 
1/*
2 * jQuery Autocomplete plugin 1.2.3
3 *
4 * Copyright (c) 2009 Jörn Zaefferer
5 *
6 * Dual licensed under the MIT and GPL licenses:
7 *   http://www.opensource.org/licenses/mit-license.php
8 *   http://www.gnu.org/licenses/gpl.html
9 *
10 * With small modifications by Alfonso Gómez-Arzola.
11 * See changelog for details.
12 *
13 */
14
15;(function($) {
16
17$.fn.extend({
18     autocomplete: function(urlOrData, options) {
19          var isUrl = typeof urlOrData == "string";
20          options = $.extend({}, $.Autocompleter.defaults, {
21               url: isUrl ? urlOrData : null,
22               data: isUrl ? null : urlOrData,
23               delay: isUrl ? $.Autocompleter.defaults.delay : 10,
24               max: options && !options.scroll ? 10 : 150,
25               noRecord: "No Records."
26          }, options);
27
28          // if highlight is set to false, replace it with a do-nothing function
29          options.highlight = options.highlight || function(value) { return value; };
30
31          // if the formatMatch option is not specified, then use formatItem for backwards compatibility
32          options.formatMatch = options.formatMatch || options.formatItem;
33
34          return this.each(function() {
35               new $.Autocompleter(this, options);
36          });
37     },
38     result: function(handler) {
39          return this.bind("result", handler);
40     },
41     search: function(handler) {
42          return this.trigger("search", [handler]);
43     },
44     flushCache: function() {
45          return this.trigger("flushCache");
46     },
47     setOptions: function(options){
48          return this.trigger("setOptions", [options]);
49     },
50     unautocomplete: function() {
51          return this.trigger("unautocomplete");
52     }
53});
54
55$.Autocompleter = function(input, options) {
56
57     var KEY = {
58          UP: 38,
59          DOWN: 40,
60          DEL: 46,
61          TAB: 9,
62          RETURN: 13,
63          ESC: 27,
64          COMMA: 188,
65          PAGEUP: 33,
66          PAGEDOWN: 34,
67          BACKSPACE: 8
68     };
69
70     var globalFailure = null;
71     if(options.failure != null && typeof options.failure == "function") {
72       globalFailure = options.failure;
73     }
74
75     // Create $ object for input element
76     var $input = $(input).attr("autocomplete", "off").addClass(options.inputClass);
77
78     var timeout;
79     var previousValue = "";
80     var cache = $.Autocompleter.Cache(options);
81     var hasFocus = 0;
82     var lastKeyPressCode;
83     var config = {
84          mouseDownOnSelect: false
85     };
86     var select = $.Autocompleter.Select(options, input, selectCurrent, config);
87
88     var blockSubmit;
89
90     // prevent form submit in opera when selecting with return key
91  navigator.userAgent.indexOf("Opera") != -1 && $(input.form).bind("submit.autocomplete", function() {
92          if (blockSubmit) {
93               blockSubmit = false;
94               return false;
95          }
96     });
97
98     // older versions of opera don't trigger keydown multiple times while pressed, others don't work with keypress at all
99     $input.bind((navigator.userAgent.indexOf("Opera") != -1 && !'KeyboardEvent' in window ? "keypress" : "keydown") + ".autocomplete", function(event) {
100          // a keypress means the input has focus
101          // avoids issue where input had focus before the autocomplete was applied
102          hasFocus = 1;
103          // track last key pressed
104          lastKeyPressCode = event.keyCode;
105          switch(event.keyCode) {
106
107               case KEY.UP:
108                    if ( select.visible() ) {
109                         event.preventDefault();
110                         select.prev();
111                    } else {
112                         onChange(0, true);
113                    }
114                    break;
115
116               case KEY.DOWN:
117                    if ( select.visible() ) {
118                         event.preventDefault();
119                         select.next();
120                    } else {
121                         onChange(0, true);
122                    }
123                    break;
124
125               case KEY.PAGEUP:
126                    if ( select.visible() ) {
127                    event.preventDefault();
128                         select.pageUp();
129                    } else {
130                         onChange(0, true);
131                    }
132                    break;
133
134               case KEY.PAGEDOWN:
135                    if ( select.visible() ) {
136                    event.preventDefault();
137                         select.pageDown();
138                    } else {
139                         onChange(0, true);
140                    }
141                    break;
142
143               // matches also semicolon
144               case options.multiple && $.trim(options.multipleSeparator) == "," && KEY.COMMA:
145               case KEY.TAB:
146               case KEY.RETURN:
147                    if( selectCurrent() ) {
148                         // stop default to prevent a form submit, Opera needs special handling
149                         event.preventDefault();
150                         blockSubmit = true;
151                         return false;
152                    }
153                    break;
154
155               case KEY.ESC:
156                    select.hide();
157                    break;
158
159               default:
160                    clearTimeout(timeout);
161                    timeout = setTimeout(onChange, options.delay);
162                    break;
163          }
164     }).focus(function(){
165          // track whether the field has focus, we shouldn't process any
166          // results if the field no longer has focus
167          hasFocus++;
168     }).blur(function() {
169       hasFocus = 0;
170          if (!config.mouseDownOnSelect) {
171               hideResults();
172          }
173     }).click(function() {
174          // show select when clicking in a focused field
175          // but if clickFire is true, don't require field
176          // to be focused to begin with; just show select
177          if( options.clickFire ) {
178            if ( !select.visible() ) {
179               onChange(0, true);
180          }
181          } else {
182            if ( hasFocus++ > 1 && !select.visible() ) {
183               onChange(0, true);
184          }
185          }
186     }).bind("search", function() {
187          // TODO why not just specifying both arguments?
188          var fn = (arguments.length > 1) ? arguments[1] : null;
189          function findValueCallback(q, data) {
190               var result;
191               if( data && data.length ) {
192                    for (var i=0; i < data.length; i++) {
193                         if( data[i].result.toLowerCase() == q.toLowerCase() ) {
194                              result = data[i];
195                              break;
196                         }
197                    }
198               }
199               if( typeof fn == "function" ) fn(result);
200               else $input.trigger("result", result && [result.data, result.value]);
201          }
202          $.each(trimWords($input.val()), function(i, value) {
203               request(value, findValueCallback, findValueCallback);
204          });
205     }).bind("flushCache", function() {
206          cache.flush();
207     }).bind("setOptions", function() {
208          $.extend(true, options, arguments[1]);
209          // if we've updated the data, repopulate
210          if ( "data" in arguments[1] )
211               cache.populate();
212     }).bind("unautocomplete", function() {
213          select.unbind();
214          $input.unbind();
215          $(input.form).unbind(".autocomplete");
216     });
217
218
219     function selectCurrent() {
220          var selected = select.selected();
221          if( !selected )
222               return false;
223
224          var v = selected.result;
225          previousValue = v;
226
227          if ( options.multiple ) {
228               var words = trimWords($input.val());
229               if ( words.length > 1 ) {
230                    var seperator = options.multipleSeparator.length;
231                    var cursorAt = $(input).selection().start;
232                    var wordAt, progress = 0;
233                    $.each(words, function(i, word) {
234                         progress += word.length;
235                         if (cursorAt <= progress) {
236                              wordAt = i;
237                              return false;
238                         }
239                         progress += seperator;
240                    });
241                    words[wordAt] = v;
242                    // TODO this should set the cursor to the right position, but it gets overriden somewhere
243                    //$.Autocompleter.Selection(input, progress + seperator, progress + seperator);
244                    v = words.join( options.multipleSeparator );
245               }
246               v += options.multipleSeparator;
247          }
248
249          $input.val(v);
250          hideResultsNow();
251          $input.trigger("result", [selected.data, selected.value]);
252          return true;
253     }
254
255     function onChange(crap, skipPrevCheck) {
256          if( lastKeyPressCode == KEY.DEL ) {
257               select.hide();
258               return;
259          }
260
261          var currentValue = $input.val();
262
263          if ( !skipPrevCheck && currentValue == previousValue )
264               return;
265
266          previousValue = currentValue;
267
268          currentValue = lastWord(currentValue);
269          if ( currentValue.length >= options.minChars) {
270               $input.addClass(options.loadingClass);
271               if (!options.matchCase)
272                    currentValue = currentValue.toLowerCase();
273               request(currentValue, receiveData, hideResultsNow);
274          } else {
275               stopLoading();
276               select.hide();
277          }
278     };
279
280     function trimWords(value) {
281          if (!value)
282               return [""];
283          if (!options.multiple)
284               return [$.trim(value)];
285          return $.map(value.split(options.multipleSeparator), function(word) {
286               return $.trim(value).length ? $.trim(word) : null;
287          });
288     }
289
290     function lastWord(value) {
291          if ( !options.multiple )
292               return value;
293          var words = trimWords(value);
294          if (words.length == 1)
295               return words[0];
296          var cursorAt = $(input).selection().start;
297          if (cursorAt == value.length) {
298               words = trimWords(value)
299          } else {
300               words = trimWords(value.replace(value.substring(cursorAt), ""));
301          }
302          return words[words.length - 1];
303     }
304
305     // fills in the input box w/the first match (assumed to be the best match)
306     // q: the term entered
307     // sValue: the first matching result
308     function autoFill(q, sValue){
309          // autofill in the complete box w/the first match as long as the user hasn't entered in more data
310          // if the last user key pressed was backspace, don't autofill
311          if( options.autoFill && (lastWord($input.val()).toLowerCase() == q.toLowerCase()) && lastKeyPressCode != KEY.BACKSPACE ) {
312               // fill in the value (keep the case the user has typed)
313               $input.val($input.val() + sValue.substring(lastWord(previousValue).length));
314               // select the portion of the value not typed by the user (so the next character will erase)
315               $(input).selection(previousValue.length, previousValue.length + sValue.length);
316          }
317     };
318
319     function hideResults() {
320          clearTimeout(timeout);
321          timeout = setTimeout(hideResultsNow, 200);
322     };
323
324     function hideResultsNow() {
325          var wasVisible = select.visible();
326          select.hide();
327          clearTimeout(timeout);
328          stopLoading();
329          if (options.mustMatch) {
330               // call search and run callback
331               $input.search(
332                    function (result){
333                         // if no value found, clear the input box
334                         if( !result ) {
335                              if (options.multiple) {
336                                   var words = trimWords($input.val()).slice(0, -1);
337                                   $input.val( words.join(options.multipleSeparator) + (words.length ? options.multipleSeparator : "") );
338                              }
339                              else {
340                                   $input.val( "" );
341                                   $input.trigger("result", null);
342                              }
343                         }
344                    }
345               );
346          }
347     };
348
349     function receiveData(q, data) {
350          if ( data && data.length && hasFocus ) {
351               stopLoading();
352               select.display(data, q);
353               autoFill(q, data[0].value);
354               select.show();
355          } else {
356               hideResultsNow();
357          }
358     };
359
360     function request(term, success, failure) {
361          if (!options.matchCase)
362               term = term.toLowerCase();
363          var data = cache.load(term);
364          // recieve the cached data
365          if (data) {
366               if(data.length)     {
367                    success(term, data);
368               }
369               else{
370                    var parsed = options.parse && options.parse(options.noRecord) || parse(options.noRecord); 
371                    success(term,parsed);
372               }
373          // if an AJAX url has been supplied, try loading the data now
374          } else if( (typeof options.url == "string") && (options.url.length > 0) ){
375
376               var extraParams = {
377                    timestamp: +new Date()
378               };
379               $.each(options.extraParams, function(key, param) {
380                    extraParams[key] = typeof param == "function" ? param() : param;
381               });
382
383               $.ajax({
384                    // try to leverage ajaxQueue plugin to abort previous requests
385                    mode: "abort",
386                    // limit abortion to this input
387                    port: "autocomplete" + input.name,
388                    dataType: options.dataType,
389                    url: options.url,
390                    data: $.extend({
391                         q: lastWord(term),
392                         limit: options.max
393                    }, extraParams),
394                    success: function(data) {
395                         var parsed = options.parse && options.parse(data) || parse(data);
396                         cache.add(term, parsed);
397                         success(term, parsed);
398                    }
399               });
400          } else {
401               // if we have a failure, we need to empty the list -- this prevents the the [TAB] key from selecting the last successful match
402               select.emptyList();
403               if(globalFailure != null) {
404        globalFailure();
405      }
406      else {
407        failure(term);
408               }
409          }
410     };
411
412     function parse(data) {
413          var parsed = [];
414          var rows = data.split("\n");
415          for (var i=0; i < rows.length; i++) {
416               var row = $.trim(rows[i]);
417               if (row) {
418                    row = row.split("|");
419                    parsed[parsed.length] = {
420                         data: row,
421                         value: row[0],
422                         result: options.formatResult && options.formatResult(row, row[0]) || row[0]
423                    };
424               }
425          }
426          return parsed;
427     };
428
429     function stopLoading() {
430          $input.removeClass(options.loadingClass);
431     };
432
433};
434
435$.Autocompleter.defaults = {
436     inputClass: "ac_input",
437     resultsClass: "ac_results",
438     loadingClass: "ac_loading",
439     minChars: 1,
440     delay: 400,
441     matchCase: false,
442     matchSubset: true,
443     matchContains: false,
444     cacheLength: 100,
445     max: 1000,
446     mustMatch: false,
447     extraParams: {},
448     selectFirst: true,
449     formatItem: function(row) { return row[0]; },
450     formatMatch: null,
451     autoFill: false,
452     width: 0,
453     multiple: false,
454     multipleSeparator: " ",
455     inputFocus: true,
456     clickFire: false,
457     highlight: function(value, term) {
458          return value.replace(new RegExp("(?![^&;]+;)(?!<[^<>]*)(" + term.replace(/([\^\$\(\)\[\]\{\}\*\.\+\?\|\\])/gi, "\\$1") + ")(?![^<>]*>)(?![^&;]+;)", "gi"), "<strong>$1</strong>");
459     },
460    scroll: true,
461    scrollHeight: 180,
462    scrollJumpPosition: true
463};
464
465$.Autocompleter.Cache = function(options) {
466
467     var data = {};
468     var length = 0;
469
470     function matchSubset(s, sub) {
471          if (!options.matchCase)
472               s = s.toLowerCase();
473          var i = s.indexOf(sub);
474          if (options.matchContains == "word"){
475               i = s.toLowerCase().search("\\b" + sub.toLowerCase());
476          }
477          if (i == -1) return false;
478          return i == 0 || options.matchContains;
479     };
480
481     function add(q, value) {
482          if (length > options.cacheLength){
483               flush();
484          }
485          if (!data[q]){
486               length++;
487          }
488          data[q] = value;
489     }
490
491     function populate(){
492          if( !options.data ) return false;
493          // track the matches
494          var stMatchSets = {},
495               nullData = 0;
496
497          // no url was specified, we need to adjust the cache length to make sure it fits the local data store
498          if( !options.url ) options.cacheLength = 1;
499
500          // track all options for minChars = 0
501          stMatchSets[""] = [];
502
503          // loop through the array and create a lookup structure
504          for ( var i = 0, ol = options.data.length; i < ol; i++ ) {
505               var rawValue = options.data[i];
506               // if rawValue is a string, make an array otherwise just reference the array
507               rawValue = (typeof rawValue == "string") ? [rawValue] : rawValue;
508
509               var value = options.formatMatch(rawValue, i+1, options.data.length);
510               if ( typeof(value) === 'undefined' || value === false )
511                    continue;
512
513               var firstChar = value.charAt(0).toLowerCase();
514               // if no lookup array for this character exists, look it up now
515               if( !stMatchSets[firstChar] )
516                    stMatchSets[firstChar] = [];
517
518               // if the match is a string
519               var row = {
520                    value: value,
521                    data: rawValue,
522                    result: options.formatResult && options.formatResult(rawValue) || value
523               };
524
525               // push the current match into the set list
526               stMatchSets[firstChar].push(row);
527
528               // keep track of minChars zero items
529               if ( nullData++ < options.max ) {
530                    stMatchSets[""].push(row);
531               }
532          };
533
534          // add the data items to the cache
535          $.each(stMatchSets, function(i, value) {
536               // increase the cache size
537               options.cacheLength++;
538               // add to the cache
539               add(i, value);
540          });
541     }
542
543     // populate any existing data
544     setTimeout(populate, 25);
545
546     function flush(){
547          data = {};
548          length = 0;
549     }
550
551     return {
552          flush: flush,
553          add: add,
554          populate: populate,
555          load: function(q) {
556               if (!options.cacheLength || !length)
557                    return null;
558               /*
559                * if dealing w/local data and matchContains than we must make sure
560                * to loop through all the data collections looking for matches
561                */
562               if( !options.url && options.matchContains ){
563                    // track all matches
564                    var csub = [];
565                    // loop through all the data grids for matches
566                    for( var k in data ){
567                         // don't search through the stMatchSets[""] (minChars: 0) cache
568                         // this prevents duplicates
569                         if( k.length > 0 ){
570                              var c = data[k];
571                              $.each(c, function(i, x) {
572                                   // if we've got a match, add it to the array
573                                   if (matchSubset(x.value, q)) {
574                                        csub.push(x);
575                                   }
576                              });
577                         }
578                    }
579                    return csub;
580               } else
581               // if the exact item exists, use it
582               if (data[q]){
583                    return data[q];
584               } else
585               if (options.matchSubset) {
586                    for (var i = q.length - 1; i >= options.minChars; i--) {
587                         var c = data[q.substr(0, i)];
588                         if (c) {
589                              var csub = [];
590                              $.each(c, function(i, x) {
591                                   if (matchSubset(x.value, q)) {
592                                        csub[csub.length] = x;
593                                   }
594                              });
595                              return csub;
596                         }
597                    }
598               }
599               return null;
600          }
601     };
602};
603
604$.Autocompleter.Select = function (options, input, select, config) {
605     var CLASSES = {
606          ACTIVE: "ac_over"
607     };
608
609     var listItems,
610          active = -1,
611          data,
612          term = "",
613          needsInit = true,
614          element,
615          list;
616
617     // Create results
618     function init() {
619          if (!needsInit)
620               return;
621          element = $("<div/>")
622          .hide()
623          .addClass(options.resultsClass)
624          .css("position", "absolute")
625          .appendTo(document.body)
626          .hover(function(event) {
627            // Browsers except FF do not fire mouseup event on scrollbars, resulting in mouseDownOnSelect remaining true, and results list not always hiding.
628            if($(this).is(":visible")) {
629              input.focus();
630            }
631            config.mouseDownOnSelect = false;
632          });
633
634          list = $("<ul/>").appendTo(element).mouseover( function(event) {
635               if(target(event).nodeName && target(event).nodeName.toUpperCase() == 'LI') {
636                 active = $("li", list).removeClass(CLASSES.ACTIVE).index(target(event));
637                   $(target(event)).addClass(CLASSES.ACTIVE);
638             }
639          }).click(function(event) {
640               $(target(event)).addClass(CLASSES.ACTIVE);
641               select();
642               if( options.inputFocus )
643                 input.focus();
644               return false;
645          }).mousedown(function() {
646               config.mouseDownOnSelect = true;
647          }).mouseup(function() {
648               config.mouseDownOnSelect = false;
649          });
650
651          if( options.width > 0 )
652               element.css("width", options.width);
653
654          needsInit = false;
655     }
656
657     function target(event) {
658          var element = event.target;
659          while(element && element.tagName != "LI")
660               element = element.parentNode;
661          // more fun with IE, sometimes event.target is empty, just ignore it then
662          if(!element)
663               return [];
664          return element;
665     }
666
667     function moveSelect(step) {
668          listItems.slice(active, active + 1).removeClass(CLASSES.ACTIVE);
669          movePosition(step);
670        var activeItem = listItems.slice(active, active + 1).addClass(CLASSES.ACTIVE);
671        if(options.scroll) {
672            var offset = 0;
673            listItems.slice(0, active).each(function() {
674                    offset += this.offsetHeight;
675               });
676            if((offset + activeItem[0].offsetHeight - list.scrollTop()) > list[0].clientHeight) {
677                list.scrollTop(offset + activeItem[0].offsetHeight - list.innerHeight());
678            } else if(offset < list.scrollTop()) {
679                list.scrollTop(offset);
680            }
681        }
682     };
683
684     function movePosition(step) {
685          if (options.scrollJumpPosition || (!options.scrollJumpPosition && !((step < 0 && active == 0) || (step > 0 && active == listItems.size() - 1)) )) {
686               active += step;
687               if (active < 0) {
688                    active = listItems.size() - 1;
689               } else if (active >= listItems.size()) {
690                    active = 0;
691               }
692          }
693     }
694
695
696     function limitNumberOfItems(available) {
697          return options.max && options.max < available
698               ? options.max
699               : available;
700     }
701
702     function fillList() {
703          list.empty();
704          var max = limitNumberOfItems(data.length);
705          for (var i=0; i < max; i++) {
706               if (!data[i])
707                    continue;
708               var formatted = options.formatItem(data[i].data, i+1, max, data[i].value, term);
709               if ( formatted === false )
710                    continue;
711               var li = $("<li/>").html( options.highlight(formatted, term) ).addClass(i%2 == 0 ? "ac_even" : "ac_odd").appendTo(list)[0];
712               $.data(li, "ac_data", data[i]);
713          }
714          listItems = list.find("li");
715          if ( options.selectFirst ) {
716               listItems.slice(0, 1).addClass(CLASSES.ACTIVE);
717               active = 0;
718          }
719          // apply bgiframe if available
720          if ( $.fn.bgiframe )
721               list.bgiframe();
722     }
723
724     return {
725          display: function(d, q) {
726               init();
727               data = d;
728               term = q;
729               fillList();
730          },
731          next: function() {
732               moveSelect(1);
733          },
734          prev: function() {
735               moveSelect(-1);
736          },
737          pageUp: function() {
738               if (active != 0 && active - 8 < 0) {
739                    moveSelect( -active );
740               } else {
741                    moveSelect(-8);
742               }
743          },
744          pageDown: function() {
745               if (active != listItems.size() - 1 && active + 8 > listItems.size()) {
746                    moveSelect( listItems.size() - 1 - active );
747               } else {
748                    moveSelect(8);
749               }
750          },
751          hide: function() {
752               element && element.hide();
753               listItems && listItems.removeClass(CLASSES.ACTIVE);
754               active = -1;
755          },
756          visible : function() {
757               return element && element.is(":visible");
758          },
759          current: function() {
760               return this.visible() && (listItems.filter("." + CLASSES.ACTIVE)[0] || options.selectFirst && listItems[0]);
761          },
762          show: function() {
763               var offset = $(input).offset();
764               element.css({
765                    width: typeof options.width == "string" || options.width > 0 ? options.width : $(input).width(),
766                    top: offset.top + input.offsetHeight,
767                    left: offset.left
768               }).show();
769            if(options.scroll) {
770                list.scrollTop(0);
771                list.css({
772                         maxHeight: options.scrollHeight,
773                         overflow: 'auto'
774                    });
775
776                if(navigator.userAgent.indexOf("MSIE") != -1 && typeof document.body.style.maxHeight === "undefined") {
777                         var listHeight = 0;
778                         listItems.each(function() {
779                              listHeight += this.offsetHeight;
780                         });
781                         var scrollbarsVisible = listHeight > options.scrollHeight;
782                    list.css('height', scrollbarsVisible ? options.scrollHeight : listHeight );
783                         if (!scrollbarsVisible) {
784                              // IE doesn't recalculate width when scrollbar disappears
785                              listItems.width( list.width() - parseInt(listItems.css("padding-left")) - parseInt(listItems.css("padding-right")) );
786                         }
787                }
788
789            }
790          },
791          selected: function() {
792               var selected = listItems && listItems.filter("." + CLASSES.ACTIVE).removeClass(CLASSES.ACTIVE);
793               return selected && selected.length && $.data(selected[0], "ac_data");
794          },
795          emptyList: function (){
796               list && list.empty();
797          },
798          unbind: function() {
799               element && element.remove();
800          }
801     };
802};
803
804$.fn.selection = function(start, end) {
805     if (start !== undefined) {
806          return this.each(function() {
807               if( this.createTextRange ){
808                    var selRange = this.createTextRange();
809                    if (end === undefined || start == end) {
810                         selRange.move("character", start);
811                         selRange.select();
812                    } else {
813                         selRange.collapse(true);
814                         selRange.moveStart("character", start);
815                         selRange.moveEnd("character", end);
816                         selRange.select();
817                    }
818               } else if( this.setSelectionRange ){
819                    this.setSelectionRange(start, end);
820               } else if( this.selectionStart ){
821                    this.selectionStart = start;
822                    this.selectionEnd = end;
823               }
824          });
825     }
826     var field = this[0];
827     if ( field.createTextRange ) {
828          var range = document.selection.createRange(),
829               orig = field.value,
830               teststring = "<->",
831               textLength = range.text.length;
832          range.text = teststring;
833          var caretAt = field.value.indexOf(teststring);
834          field.value = orig;
835          this.selection(caretAt, caretAt + textLength);
836          return {
837               start: caretAt,
838               end: caretAt + textLength
839          }
840     } else if( field.selectionStart !== undefined ){
841          return {
842               start: field.selectionStart,
843               end: field.selectionEnd
844          }
845     }
846};
847
848})(jQuery);
Note: See TracBrowser for help on using the repository browser.

Sites map