appdoc 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,962 @@
1
+ /**
2
+ SmartTextBox jQuery plugin
3
+ @version 1.2.0-rc1
4
+ @author Pierre Gayvallet
5
+ @see http://wayofspark.com/projects/smarttextbox
6
+ @copyright (c) 2009-2010 Pierre Gayvallet - GPL license.
7
+ */
8
+
9
+ (function($){
10
+
11
+ $.SmartTextBox = {};
12
+ var SmartTextBox = $.SmartTextBox;
13
+
14
+ /**
15
+ SmartTextBox.BaseClass
16
+ Allow pseudo inheritance in javascript objects
17
+ */
18
+ SmartTextBox.BaseClass = function(o){};
19
+ SmartTextBox.BaseClass.prototype.construct = function(){};
20
+ SmartTextBox.BaseClass.extend = function(def) {
21
+ var classDef = function() {
22
+ if (arguments[0] !== SmartTextBox.BaseClass) { this.construct.apply(this, arguments); }
23
+ };
24
+ var proto = new this(SmartTextBox.BaseClass);
25
+ var superClass = this.prototype;
26
+ for(var n in def) {
27
+ var item = def[n];
28
+ proto[n] = item;
29
+ }
30
+ classDef.prototype = proto;
31
+ classDef.extend = this.extend;
32
+ return classDef;
33
+ };
34
+
35
+ /**
36
+ SmartTextBox.SmartTextBox
37
+ Manager Class for a TextBox
38
+ */
39
+ SmartTextBox.SmartTextBox = SmartTextBox.BaseClass.extend({
40
+ _options : {
41
+ // Autocomplete configuration
42
+ autocomplete : true, // on/off
43
+ minSearchLength : 2, // min length to type to receive suggestions
44
+ maxResults : 10, // max number of results to display
45
+ caseSensitive : false, // case sensitive search
46
+ highlight : true, // highlight autocomplete search in results
47
+ fullSearch : true, // search at start of values or in all the string
48
+ autocompleteValues : [],
49
+ autocompleteUrl : null,
50
+ placeholder : "Please start typing to receive suggestions", // search placeholder text
51
+ // SmartTextBox configuration
52
+ onlyAutocomplete: false, // can only insert from autocomplete values
53
+ uniqueValues : true, // values can only be added once
54
+ submitKeys : [13], // list of keys to save an input to box
55
+ submitChars : [ ";", ","], // list of chars to save an input to box
56
+ separator : ";", // separator for serialize method
57
+ updateOriginal : true, // update the original input value when change
58
+ // Events handling
59
+ onElementAdd : null,
60
+ onElementRemove : null,
61
+ onElementFocus : null,
62
+ onElementBlur : null,
63
+ // Misc
64
+ hideEmptyInputs : true, // hide empty inputs
65
+ editOnFocus : false, // edit box on focus ( click or <- -> )
66
+ editOnDoubleClick : false, // edit box on doubleClick
67
+ // CSS
68
+ containerClass : "smartTextBox", // base CSS class for containers
69
+ // Debug configuration
70
+ debug : false // debug mode - show logs in firebug console
71
+ },
72
+ construct : function(el, o){
73
+ this.options = {};
74
+ var o = o || {};
75
+ $.extend(this.options, this._options);
76
+ $.extend(this.options, o);
77
+
78
+ var self = this;
79
+ this.original = $(el);
80
+ this.focused = false;
81
+ this.elements = [];
82
+
83
+ $(this.original).data("SmartTextBox", this);
84
+ this.original.hide();
85
+
86
+ this.container = $("<div></div>")
87
+ .addClass(this.options.containerClass)
88
+ .click(function(e){
89
+ if( (self.container.index(e.target)>-1 || self.list.index(e.target)>-1) &&
90
+ (!self.currentFocus || self.currentFocus != self.elements[self.elements.length-1]))
91
+ self.focusLast();
92
+ }).insertAfter(this.original);
93
+
94
+ this.list = $("<ul></ul>")
95
+ .addClass(this.options.containerClass + "-items")
96
+ .appendTo(this.container);
97
+
98
+ $(document).keydown(function(e){
99
+ self.onKeyDown(e)
100
+ })
101
+ .click(function(e){
102
+ if(!self.currentFocus) return;
103
+ if (e.target.className.indexOf(self.options.containerClass)>-1){
104
+ if(self.container.index(e.target)>-1) return;
105
+ var parent = $(e.target).parents('.' + self.options.containerClass);
106
+ if (parent.index(self.container)>-1) return;
107
+ }
108
+ self.blur();
109
+ });
110
+
111
+ if(this.options.autocomplete){
112
+ this.autocomplete = new SmartTextBox.AutoComplete(this, this.options);
113
+ }
114
+
115
+ this.add("input");
116
+ this.loadOriginal();
117
+ },
118
+ setValues : function(values){
119
+ this.removeAll();
120
+ var self = this;
121
+ $.each(values, function(i, el){
122
+ self.addBox(el);
123
+ });
124
+ },
125
+ removeAll : function() {
126
+ var toRemove = [];
127
+ $.each(this.elements, function(){
128
+ if(this.is('box')) {
129
+ toRemove.push(this);
130
+ }
131
+ })
132
+ $.each(toRemove, function(){
133
+ this.remove();
134
+ })
135
+ },
136
+ setAutocompleteValues : function(values){
137
+ if(!this.options.autocomplete) return;
138
+ if(typeof values == "string") {
139
+ this.autocomplete.setValues(values.split(this.options.separator));
140
+ } else {
141
+ this.autocomplete.setValues(values);
142
+ }
143
+ },
144
+ onKeyDown : function(event){
145
+ if(!this.currentFocus) return;
146
+
147
+ var caret = this.currentFocus.is('input') ? this.currentFocus.getCaret() : null;
148
+ var value = this.currentFocus.getValue();
149
+ var custom = (this.currentFocus.is('input') && this.currentFocus.isSelected());
150
+
151
+ // bug on FF making elem to loose focus... this fix it
152
+ this.currentFocus.valueContainer.focus();
153
+
154
+ switch(event.keyCode){
155
+ case 37: // <-
156
+ if( this.currentFocus.is('box') || ((caret == 0 || !value.length) && !custom) ){
157
+ event.preventDefault();
158
+ this.focusRelative('previous');
159
+ }
160
+ break;
161
+ case 39: // ->
162
+ if( this.currentFocus.is('box') || (caret == value.length && !custom)){
163
+ event.preventDefault();
164
+ this.focusRelative('next');
165
+ }
166
+ break;
167
+ case 8: // backspace
168
+ if(this.currentFocus.is('box')){
169
+ this.currentFocus.remove();
170
+ event.preventDefault();
171
+ } else if ((caret == 0 || !value.length) && !custom) {
172
+ this.focusRelative('previous');
173
+ event.preventDefault();
174
+ }
175
+ break;
176
+ case 27: // escape
177
+ this.blur();
178
+ break;
179
+ }
180
+ },
181
+ create : function(type, value, options){
182
+ var n;
183
+ if(type=="box"){
184
+ n = new SmartTextBox.BoxElement(value, this, options);
185
+ }
186
+ else if(type=="input"){
187
+ n = new SmartTextBox.InputElement(value, this, options);
188
+ }
189
+ else{
190
+ // handle case - do nothing for now.
191
+ }
192
+ return n;
193
+ },
194
+ getElementIndex : function(elem){
195
+ return $(this.elements).index(elem);
196
+ },
197
+ getElement : function(index){
198
+ if((index<0) || (index>this.elements.length-1)) return null;
199
+ return this.elements[index];
200
+ },
201
+ getElementsByType : function(type) {
202
+ var els = [];
203
+ $.each(this.elements, function(){
204
+ if(this.is(type)) {
205
+ els.push(this);
206
+ }
207
+ });
208
+ return els;
209
+ },
210
+ getLastBox : function(){
211
+ for(var i=this.elements.length-1;i>-1;i--){
212
+ if(this.elements[i].is('box')) return this.elements[i];
213
+ }
214
+ return null;
215
+ },
216
+ insertElement : function(element, index){
217
+ var i = (arguments.length > 1) ? index : this.elements.length;
218
+ this.elements.splice(i,0, element);
219
+ },
220
+ add : function(type, value, relTo, relPos){
221
+ var type = type || "box";
222
+ var value = value || "";
223
+ var relPos = relPos || "after"
224
+ var relTo = relTo || this.getLastBox();
225
+
226
+ var n = this.create(type, value);
227
+ var i = 0;
228
+ if(relTo) i = this.getElementIndex(relTo) + ((relPos=='after') ? 1 : 0);
229
+
230
+ this.insertElement(n, i);
231
+ relTo ? n.inject(relTo, relPos) : n.inject();
232
+ return n;
233
+ },
234
+ addBox : function(value, relTo, relPos){
235
+ if(!$.trim(value).length) return;
236
+ return this.add("box", $.trim(value), relTo, relPos);
237
+ },
238
+ removeBox : function(valueOrIndex) {
239
+ var found = null;
240
+ $.each(this.elements, function(){
241
+ if(this.getValue()==valueOrIndex) {
242
+ found = this;
243
+ return false;
244
+ }
245
+ });
246
+ if(!found&&!isNaN(valueOrIndex)) {
247
+ var boxes = this.getElementsByType("box");
248
+ if(boxes.length>valueOrIndex) {
249
+ found = boxes[valueOrIndex];
250
+ }
251
+ }
252
+ if(found) {
253
+ found.remove();
254
+ }
255
+ },
256
+ handleElementEvent : function(type, elem, options){
257
+ switch(type){
258
+ case "add":
259
+ this.onElementAdd(elem, options);
260
+ break;
261
+ case "focus":
262
+ this.onElementFocus(elem, options);
263
+ break;
264
+ case "blur" :
265
+ this.onElementBlur(elem, options);
266
+ break;
267
+ case "remove" :
268
+ this.onElementRemove(elem, options);
269
+ break;
270
+ }
271
+ },
272
+ onElementAdd : function(elem){
273
+ if(this.autocomplete) this.autocomplete.setupElement(elem);
274
+ if(elem.is('box')){
275
+ var i = this.getElementIndex(elem);
276
+ var prev = this.getElement(i-1);
277
+ if((prev && prev.is('box')) || (!prev)){
278
+ var b = this.add("input", "", elem, "before");
279
+ if(this.options.hideEmptyInputs) b.hide();
280
+ }
281
+ if(this.options.updateOriginal) this.updateOriginal();
282
+ if(this.options.onElementAdd) this.options.onElementAdd(elem, this);
283
+ }
284
+ },
285
+ onElementFocus : function(elem){
286
+ if(this.currentFocus==elem) return;
287
+ if(this.currentFocus) this.currentFocus.blur();
288
+ this.currentFocus = elem;
289
+ if(this.currentFocus.is('input')&&this.autocomplete) this.autocomplete.search(this.currentFocus);
290
+ if(this.options.onElementFocus) this.options.onElementFocus(elem, this);
291
+ },
292
+ onElementBlur : function(elem){
293
+ if(this.currentFocus==elem){
294
+ this.currentFocus = null;
295
+ if(this.autocomplete) this.autocomplete.hide();
296
+ if(this.options.onElementBlur) this.options.onElementBlur(elem, this);
297
+ }
298
+ },
299
+ onElementRemove : function(elem){
300
+ var removedIndex = this.getElementIndex(elem);
301
+ this.focusRelative((removedIndex==this.elements.length-1) ? 'previous' : 'next', elem);
302
+ this.elements.splice(removedIndex, 1);
303
+ if(this.getElement(removedIndex)&&this.getElement(removedIndex).is("input")) {
304
+ if((this.getElement(removedIndex+1)&&this.getElement(removedIndex+1).is("input"))||
305
+ (this.getElement(removedIndex-1)&&this.getElement(removedIndex-1).is("input"))) {
306
+ this.getElement(removedIndex).remove();
307
+ }
308
+ }
309
+ if(this.options.updateOriginal) this.updateOriginal();
310
+ if(this.elements.length==1&&this.elements[0].is("input")) {
311
+ this.elements[0].show();
312
+ }
313
+ if(this.options.onElementRemove) this.options.onElementRemove(elem, this);
314
+ },
315
+ blur: function(){
316
+ $.each(this.elements, function(i, el){
317
+ el.blur();
318
+ });
319
+ },
320
+ focusLast : function(){
321
+ this.elements[this.elements.length-1].focus();
322
+ },
323
+ focusRelative : function(dir, from){
324
+ var b = from || this.currentFocus;
325
+ var idx = $(this.elements).index(b);
326
+ idx = (dir == 'previous') ? idx-1 : idx+1;
327
+ if(idx<0) return; //idx = 0;
328
+ if(idx>=this.elements.length) return; //idx = this.elements.length-1;
329
+ this.elements[idx].focus();
330
+ if(this.elements[idx].is("input")){
331
+ (dir == 'previous') ? this.elements[idx].setCaretToEnd() : this.elements[idx].setCaretToStart();
332
+ }
333
+ },
334
+ getBoxValues : function() {
335
+ var values = [];
336
+ $.each(this.elements, function(i, el){
337
+ if(!this.is('box')) return;
338
+ values.push(this.getValue());
339
+ });
340
+ return values;
341
+ },
342
+ containsValue : function(value) {
343
+ return ($(this.getBoxValues()).index(value)>-1);
344
+ },
345
+ serialize : function(){
346
+ return this.getBoxValues().join(this.options.separator);
347
+ },
348
+ updateOriginal : function(){
349
+ this.original.attr('value', this.serialize());
350
+ },
351
+ loadOriginal : function(){
352
+ this.load(this.original.attr('value'));
353
+ },
354
+ load : function(values){
355
+ if(typeof values == "string") {
356
+ var values = values.split(this.options.separator);
357
+ }
358
+ this.setValues(values);
359
+ }
360
+ });
361
+
362
+ /**
363
+ SmartTextBox.AutoComplete
364
+ */
365
+ SmartTextBox.AutoComplete = SmartTextBox.BaseClass.extend({
366
+ _options : {
367
+ },
368
+ construct : function(stb, o){
369
+ this.stb = stb;
370
+ this.currentValue = "";
371
+ this.options = {};
372
+ $.extend(this.options, this._options);
373
+ $.extend(this.options, o || {});
374
+ if (this.options.autocompleteUrl) {
375
+ this.ajaxLoad(this.options.autocompleteUrl);
376
+ } else {
377
+ this.setValues(this.options.autocompleteValues);
378
+ }
379
+ var self = this;
380
+ this.baseClass = this.stb.options.containerClass + "-autocomplete";
381
+
382
+ this.container = $("<div></div>")
383
+ .addClass(this.baseClass)
384
+ .css("width", this.stb.container.width())
385
+ .appendTo(this.stb.container);
386
+ this.placeHolder = $("<div></div>")
387
+ .addClass(this.baseClass + "-placeholder")
388
+ .html(this.options.placeholder)
389
+ .appendTo(this.container)
390
+ .hide();
391
+ this.resultList = $("<div></div>")
392
+ .addClass(this.baseClass + "-results")
393
+ .appendTo(this.container)
394
+ .hide();
395
+ },
396
+ ajaxLoad : function(url) {
397
+ var self = this;
398
+ $.get(url, {}, function(response){
399
+ var values = response.values;
400
+ self.setValues(values);
401
+ }, 'json');
402
+ },
403
+ setValues : function(values){
404
+ this.values = values || [];
405
+ },
406
+ getValues : function(){
407
+ return this.values || [];
408
+ },
409
+ setupElement : function(element){
410
+ if(!element.is('input')) return;
411
+ var self = this;
412
+ element.valueContainer
413
+ .keydown(function(e){ self.navigate(e); })
414
+ .keyup(function(){ self.search(); });
415
+ },
416
+ navigate : function(e){
417
+ switch(e.keyCode){
418
+ case 38: // arrowUp
419
+ (this.currentSelection && this.currentSelection.prev().length) ? this.focusRelative('prev') : this.blur();
420
+ break;
421
+ case 40: // arrowDown
422
+ this.currentSelection ? this.focusRelative('next') : this.focusFirst();
423
+ break;
424
+ case 13: // enter
425
+ if(this.currentSelection){
426
+ e.stopPropagation();
427
+ this.addCurrent();
428
+ this.currentElem.focus();
429
+ this.search();
430
+ }
431
+ break;
432
+ };
433
+ },
434
+ search : function(elem){
435
+ if(elem) this.currentElem = elem;
436
+ if (!this.getValues().length) return;
437
+ window.clearTimeout(this.hidetimer);
438
+ var value = this.currentElem.getValue();
439
+ if(value.length<this.options.minSearchLength) this.showPlaceHolder();
440
+ if (value == this.currentValue) return;
441
+ this.currentValue = value;
442
+ this.hideResultList();
443
+ if(value.length<this.options.minSearchLength) return;
444
+ this.showResults(value);
445
+ },
446
+ showPlaceHolder : function(){
447
+ if(this.placeHolder) this.placeHolder.show();
448
+ },
449
+ hidePlaceHolder : function(){
450
+ if(this.placeHolder) this.placeHolder.hide();
451
+ },
452
+ showResultList : function(){
453
+ this.resultList.show();
454
+ },
455
+ hideResultList : function(){
456
+ this.resultList.hide();
457
+ },
458
+ hide : function(){
459
+ var self = this;
460
+ this.hidetimer = window.setTimeout(function(){
461
+ self.hidePlaceHolder();
462
+ self.hideResultList();
463
+ self.currentValue = "";
464
+ }, 150);
465
+ },
466
+ showResults : function(value){
467
+ var results = this.searchResults(value);
468
+ this.hidePlaceHolder();
469
+ if(!results.length) return;
470
+ this.blur();
471
+ this.resultList.empty().show();
472
+ var self = this;
473
+ results.sort();
474
+ $.each(results, function(i, e){
475
+ self.addResult(e, value);
476
+ });
477
+ this.results = results;
478
+ },
479
+ searchResults : function(value){
480
+ var newvals = [], regexp = new RegExp((this.options.fullSearch ? '' : '\\b') + value, this.caseSensitive ? '' : 'i');
481
+ var values = this.getValues();
482
+ for (var i = 0; i < values.length; i++){
483
+ if(this.stb.options.uniqueValues&&this.stb.containsValue(values[i])) continue;
484
+ if (regexp.test(values[i])) {
485
+ newvals.push(values[i]);
486
+ }
487
+ if (newvals.length >= this.options.maxResults) break;
488
+ }
489
+ return newvals;
490
+ },
491
+ addResult : function(result, value){
492
+ var self = this;
493
+ var newSel = $("<div></div>")
494
+ .addClass(this.baseClass+"-result")
495
+ .appendTo(this.resultList)
496
+ .mouseenter(function(e){
497
+ self.focus(this);
498
+ })
499
+ .mousedown(function(e){
500
+ e.stopPropagation();
501
+ window.clearTimeout(self.hidetimer);
502
+ self.doAdd = true;
503
+ })
504
+ .mouseup(function(e){
505
+ if(self.doAdd){
506
+ self.addCurrent();
507
+ self.currentElem.focus();
508
+ self.search();
509
+ self.doAdd = false;
510
+ }
511
+ });
512
+
513
+ var valueContainer = $("<span>")
514
+ .addClass(self.baseClass+"-value")
515
+ .html(result)
516
+ .css("display", "none")
517
+ .appendTo(newSel);
518
+ var displayContainer = $("<span>")
519
+ .addClass(self.baseClass+"-display")
520
+ .html(self.formatResult(result, value))
521
+ .appendTo(newSel);
522
+ },
523
+ formatResult : function(result, value) {
524
+ if (this.options.highlight) {
525
+ var lcr = result.toLowerCase();
526
+ var lcv = value.toLowerCase();
527
+ var idx = lcr.indexOf(lcv);
528
+ var formatted = "<span>" + result.substring(0, idx) + "</span>" +
529
+ "<span class='" + this.baseClass + "-highligh'>" + result.substring(idx, idx + value.length) + "</span>" +
530
+ "<span>" + result.substring(idx + value.length) + "</span>";
531
+ return formatted;
532
+ } else {
533
+ return result;
534
+ }
535
+ },
536
+ addCurrent : function(){
537
+ var value = $("."+this.baseClass+"-value", this.currentSelection).html();
538
+ this.currentElem.setValue(value);
539
+ this.currentElem.saveAsBox();
540
+ this.currentSelection = null;
541
+ },
542
+ focus : function(element){
543
+ this.blur();
544
+ this.currentSelection = $(element).addClass(this.baseClass + '-result-focus');
545
+ },
546
+ focusFirst : function(){
547
+ this.focus(this.resultList.children(":first"));
548
+ },
549
+ focusRelative : function(dir){
550
+ if(dir=='next'){
551
+ this.focus( this.currentSelection.next().length ? this.currentSelection.next() : this.currentSelection);
552
+ }
553
+ else{
554
+ this.focus( this.currentSelection.prev().length ? this.currentSelection.prev() : this.currentSelection);
555
+ }
556
+ },
557
+ blur : function(){
558
+ if(this.currentSelection){
559
+ this.currentSelection.removeClass(this.baseClass + "-result-focus");
560
+ this.currentSelection = null;
561
+ }
562
+ }
563
+ });
564
+
565
+
566
+ /**
567
+ SmartTextBox.GrowingInput
568
+ */
569
+ SmartTextBox.GrowingInput = SmartTextBox.BaseClass.extend({
570
+ options: {
571
+ min: 0,
572
+ max: null,
573
+ startWidth: 2,
574
+ correction: 10
575
+ },
576
+ construct : function(el, o){
577
+ var o = o || {}; $.extend(this.options, o);
578
+ var self = this;
579
+ this.element = $(el);
580
+
581
+ this.calc = $("<span></span>")
582
+ .css({
583
+ 'float': 'left',
584
+ 'display': 'inline-block',
585
+ 'position': 'absolute',
586
+ 'left': -1000
587
+ })
588
+ .insertAfter(this.element);
589
+
590
+ this.requiredStyles = ['font-size', 'font-family', 'padding-left', 'padding-top', 'padding-bottom',
591
+ 'padding-right', 'border-left', 'border-right', 'border-top', 'border-bottom',
592
+ 'word-spacing', 'letter-spacing', 'text-indent', 'text-transform'];
593
+
594
+ this.copyCat();
595
+ this.resize();
596
+ var resize = function(){ self.resize(); }
597
+ this.element.click(resize)
598
+ .blur(resize)
599
+ .keyup(resize)
600
+ .keydown(resize)
601
+ .keypress(resize);
602
+ },
603
+ copyCat : function(){
604
+ var self = this;
605
+ $.each(this.requiredStyles, function(i, el){
606
+ self.calc.css(el, self.element.css(el));
607
+ });
608
+ },
609
+ calculate: function(chars){
610
+ this.calc.html(chars);
611
+ var width = this.calc.width();
612
+ return (width ? width : this.options.startWidth) + this.options.correction;
613
+ },
614
+ resize: function(){
615
+ this.lastvalue = this.value;
616
+ this.value = this.element.attr('value');
617
+ var value = this.value;
618
+
619
+ if((this.options.min || (this.options.min==0)) && this.value.length < this.options.min){
620
+ if((this.lastvalue || (this.lastvalue==0)) && (this.lastvalue.length <= this.options.min)) return;
621
+ for(var i=0; i<this.options.min; i++){ value += "-";}
622
+ }
623
+ if((this.options.max || (this.options.max==0)) && this.value.length > this.options.max){
624
+ if((this.lastvalue || (this.lastvalue==0)) && (this.lastvalue.length >= this.options.max)) return;
625
+ value = this.value.substr(0, this.options.max);
626
+ }
627
+
628
+ var newWidth = this.calculate(value);
629
+ this.element.width(newWidth);
630
+ return this;
631
+ }
632
+ });
633
+
634
+ /**
635
+ * SmartTextBox.BaseElement - Base, abstract class for SmartTextBox elements
636
+ */
637
+ SmartTextBox.BaseElement = SmartTextBox.BaseClass.extend({
638
+ type : "base",
639
+ options : {
640
+ },
641
+ construct : function(value, stb, o){
642
+ var o = o || {}; $.extend(this.options, o);
643
+ this.value = value;
644
+ this.stb = stb;
645
+ this.className = this.stb.options.containerClass + "-elem";
646
+ this.elemClassName = this.stb.options.containerClass + "-" + this.type +"-elem";
647
+ this.focused = false;
648
+ this.init();
649
+ },
650
+ init : function(){
651
+ this.constructElement();
652
+ },
653
+ constructElement : function(){
654
+ this.el = null;
655
+ },
656
+ is : function(t){
657
+ return this.type==t;
658
+ },
659
+ inject : function(elem, pos){
660
+ if(elem) (pos == 'before') ? this.getElement().insertBefore(elem.getElement()) : this.getElement().insertAfter(elem.getElement());
661
+ else this.getElement().prependTo(this.stb.list);
662
+ this.notifyEvent("add");
663
+ },
664
+ remove : function(notify){
665
+ this.blur();
666
+ this.el.remove();
667
+ this.onRemove();
668
+ var n = (typeof notify == 'undefined') ? true : notify;
669
+ if(n) this.notifyEvent("remove");
670
+ },
671
+ focus : function(notify){
672
+ if(this.focused) return this;
673
+ this.show();
674
+ this.el.addClass(this.className + "-focus")
675
+ .addClass(this.className + "-" + this.type + "-focus");
676
+ this.focused = true;
677
+
678
+ var n = (typeof notify == 'undefined') ? true : notify;
679
+ if(n) this.notifyEvent('focus');
680
+ this.onFocus();
681
+
682
+ return this;
683
+ },
684
+ blur : function(notify){
685
+ if(!this.focused) return this;
686
+ this.el.removeClass(this.className + "-focus")
687
+ .removeClass(this.className + "-" + this.type + "-focus");
688
+ this.focused = false;
689
+
690
+ var n = (typeof notify == 'undefined') ? true : notify;
691
+ if(n) this.notifyEvent('blur');
692
+ this.onBlur();
693
+
694
+ return this;
695
+ },
696
+ onMouseIn : function(){
697
+ this.el.addClass(this.className + "-hover")
698
+ .addClass(this.className + "-" + this.type + "-hover");
699
+ },
700
+ onMouseOut : function(){
701
+ this.el.removeClass(this.className + "-hover")
702
+ .removeClass(this.className + "-" + this.type + "-hover");
703
+ },
704
+ onFocus : function(){
705
+ return;
706
+ },
707
+ onBlur : function(){
708
+ return;
709
+ },
710
+ onRemove : function(){
711
+ return;
712
+ },
713
+ show : function(notify){
714
+ this.el.show();
715
+ var n = (typeof notify == 'undefined') ? true : notify;
716
+ if(n) this.notifyEvent('show');
717
+ return this;
718
+ },
719
+ hide : function(notify){
720
+ this.el.hide();
721
+ var n = (typeof notify == 'undefined') ? true : notify;
722
+ if(n) this.notifyEvent('hide');
723
+ return this;
724
+ },
725
+ setValue : function(v, notify){
726
+ this.value = v;
727
+ this.valueContainer.text(v);
728
+ var n = (typeof notify == 'undefined') ? true : notify;
729
+ if(n) this.notifyEvent("setValue");
730
+ return this;
731
+ },
732
+ getValue : function(){
733
+ return this.value;
734
+ },
735
+ getElement : function(){
736
+ return this.el;
737
+ },
738
+ notifyEvent : function(type){
739
+ this.stb.handleElementEvent(type, this);
740
+ return this;
741
+ },
742
+ toString : function(){
743
+ return "[BoxElement type='" + this.type + "' value='" + this.getValue() + "']";
744
+ }
745
+ });
746
+
747
+ /**
748
+ SmartTextBox.BoxElement
749
+ */
750
+ SmartTextBox.BoxElement = SmartTextBox.BaseElement.extend({
751
+ type : "box",
752
+ constructElement : function(){
753
+ var self = this;
754
+ this.el = $("<li></li>")
755
+ .addClass(this.className)
756
+ .addClass(this.elemClassName)
757
+ .hover(
758
+ function(){ self.onMouseIn(); },
759
+ function(){ self.onMouseOut(); }
760
+ )
761
+ .mousedown(function(event){
762
+ self.focus();
763
+ });
764
+
765
+ if(this.stb.options.editOnDoubleClick){
766
+ this.el.dblclick(function(event){
767
+ event.preventDefault();
768
+ self.toInput();
769
+ });
770
+ }
771
+
772
+ this.valueContainer = $("<span></span>")
773
+ .addClass(this.className+"-valueContainer")
774
+ .appendTo(this.el);
775
+
776
+ this.removeButton = $("<a></a>")
777
+ .addClass(this.className+"-deleteButton")
778
+ .attr("href", "javascript:;")
779
+ .click(function(e){ self.remove(); e.stopPropagation(); })
780
+ .appendTo(this.el);
781
+
782
+ this.setValue(this.value);
783
+ },
784
+ onFocus : function(){
785
+ var self = this;
786
+ if(this.stb.options.editOnFocus){
787
+ window.setTimeout(function(){
788
+ self.toInput()
789
+ }, 50);
790
+ }
791
+ },
792
+ onBlur : function(){
793
+ return;
794
+ },
795
+ toInput : function(){
796
+ var v = this.getValue();
797
+ var idx = this.stb.getElementIndex(this);
798
+ this.remove();
799
+ var nextElem = this.stb.getElement(idx-1);
800
+ if(nextElem.is("input") && !$.trim(nextElem.getValue()).length){
801
+ nextElem.setValue(v);
802
+ nextElem.valueContainer.focus();
803
+ nextElem.setCaretToEnd();
804
+ }
805
+ }
806
+ });
807
+
808
+ /**
809
+ SmartTextBox.InputElement
810
+ */
811
+ SmartTextBox.InputElement = SmartTextBox.BaseElement.extend({
812
+ type : "input",
813
+ constructElement : function(){
814
+ var self = this;
815
+ this.el = $("<li></li>")
816
+ .addClass(this.className)
817
+ .addClass(this.elemClassName)
818
+ .hover(
819
+ function(){ self.onMouseIn(); },
820
+ function(){ self.onMouseOut(); }
821
+ )
822
+ .click(
823
+ function(){ self.focus(); }
824
+ );
825
+
826
+ this.valueContainer = $("<input />")
827
+ .addClass(this.elemClassName+"-valueInput")
828
+ .focus( function(event){ event.stopPropagation(); self.focus(); })
829
+ .blur( function(event){ self.blur(); })
830
+ .appendTo(this.el);
831
+
832
+ this.growingInput = new SmartTextBox.GrowingInput(this.valueContainer);
833
+
834
+ $(document).keydown( function(event){ self.onKeyDown(event) } );
835
+ $(document).keypress( function(event){ self.onKeyPress(event) } );
836
+
837
+ this.setValue(this.value);
838
+ },
839
+ getValue : function(){
840
+ return this.valueContainer.attr('value');
841
+ },
842
+ setValue : function(v){
843
+ this.value = v;
844
+ this.valueContainer.attr("value", v);
845
+ this.growingInput.resize();
846
+ this.notifyEvent("setValue");
847
+ return this;
848
+ },
849
+ getCaret: function(){
850
+ var elem = this.valueContainer[0];
851
+ if(elem.createTextRange) {
852
+ var r = document.selection.createRange().duplicate();
853
+ r.moveEnd('character', elem.value.length);
854
+ if (r.text === '') return elem.value.length;
855
+ return elem.value.lastIndexOf(r.text);
856
+ } else {
857
+ return elem.selectionStart;
858
+ }
859
+ },
860
+ getCaretEnd: function(){
861
+ var elem = this.valueContainer[0];
862
+ if (elem.createTextRange){
863
+ var r = document.selection.createRange().duplicate();
864
+ r.moveStart('character', -elem.value.length);
865
+ return r.text.length;
866
+ } else {
867
+ return elem.selectionEnd;
868
+ }
869
+ },
870
+ setCaret: function(pos){
871
+ var elem = this.valueContainer[0];
872
+ if(elem.createTextRange){
873
+ elem.focus ();
874
+ var sel = document.selection.createRange();
875
+ sel.moveStart('character', -elem.value.length);
876
+ sel.moveStart('character', pos);
877
+ sel.moveEnd('character', 0);
878
+ sel.select();
879
+ }
880
+ else{
881
+ elem.selectionStart = pos;
882
+ elem.selectionEnd = pos;
883
+ }
884
+ },
885
+ setCaretToStart : function(){
886
+ this.setCaret(0);
887
+ },
888
+ setCaretToEnd : function(){
889
+ this.setCaret(this.valueContainer.attr("value").length || 0);
890
+ },
891
+ isSelected: function(){
892
+ return this.focused && (this.getCaret() !== this.getCaretEnd());
893
+ },
894
+ saveAsBox : function(){
895
+ var v = this.getValue();
896
+ if(!v) return;
897
+ this.stb.add("box", $.trim(v), this, "before");
898
+ this.setValue("");
899
+ },
900
+ onKeyDown : function(event){
901
+ if(!this.focused) return;
902
+ if($.inArray(event.keyCode, this.stb.options.submitKeys)>-1 &&
903
+ !this.stb.options.onlyAutocomplete &&
904
+ !(this.stb.options.uniqueValues&&this.stb.containsValue(this.getValue()))) {
905
+ event.preventDefault();
906
+ this.saveAsBox();
907
+ }
908
+ },
909
+ onKeyPress : function(event){
910
+ if(!this.focused) return;
911
+ if($.inArray(String.fromCharCode(event.charCode || event.keyCode || 0), this.stb.options.submitChars)>-1 &&
912
+ !this.stb.options.onlyAutocomplete &&
913
+ !(this.stb.options.uniqueValues&&this.stb.containsValue(this.getValue()))) {
914
+ event.preventDefault();
915
+ this.saveAsBox();
916
+ }
917
+ },
918
+ onFocus : function(){
919
+ this.show();
920
+ this.valueContainer.focus();
921
+ this.growingInput.resize();
922
+ },
923
+ onBlur : function(){
924
+ this.valueContainer.blur();
925
+ if(this.stb.options.hideEmptyInputs && this.el.next().length && !this.getValue()) this.hide();
926
+ if(this.stb.options.editOnFocus){
927
+ this.saveAsBox();
928
+ }
929
+ }
930
+ });
931
+
932
+ // Adds SmartTextBox to jQuery fn functions
933
+ $.fn.extend({
934
+ smartTextBox: function(key, value){
935
+ return this.each(function(){
936
+ var Smb = $(this).data("SmartTextBox");
937
+ if(Smb) {
938
+ switch(key){
939
+ case "add":
940
+ Smb.addBox(value);
941
+ break;
942
+ case "remove":
943
+ Smb.removeBox(value);
944
+ break;
945
+ case "load":
946
+ Smb.load(value);
947
+ break;
948
+ case "clear":
949
+ Smb.removeAll();
950
+ break;
951
+ case "autocomplete":
952
+ Smb.setAutocompleteValues(value);
953
+ break;
954
+ }
955
+ } else {
956
+ new $.SmartTextBox.SmartTextBox(this, key);
957
+ }
958
+ });
959
+ }
960
+ });
961
+
962
+ })(jQuery);