less-rails-semantic_ui 2.2.6.0 → 2.2.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (24) hide show
  1. checksums.yaml +4 -4
  2. data/assets/fonts/semantic_ui/themes/default/assets/fonts/icons.eot +0 -0
  3. data/assets/fonts/semantic_ui/themes/default/assets/fonts/icons.svg +2668 -682
  4. data/assets/fonts/semantic_ui/themes/default/assets/fonts/icons.ttf +0 -0
  5. data/assets/fonts/semantic_ui/themes/default/assets/fonts/icons.woff +0 -0
  6. data/assets/fonts/semantic_ui/themes/default/assets/fonts/icons.woff2 +0 -0
  7. data/assets/javascripts/semantic_ui/definitions/behaviors/form.js +64 -19
  8. data/assets/javascripts/semantic_ui/definitions/behaviors/visibility.js +7 -2
  9. data/assets/javascripts/semantic_ui/definitions/modules/dropdown.js +43 -17
  10. data/assets/javascripts/semantic_ui/definitions/modules/search.js +88 -53
  11. data/assets/javascripts/semantic_ui/definitions/modules/tab.js +12 -6
  12. data/assets/stylesheets/semantic_ui/definitions/elements/button.less +1 -1
  13. data/assets/stylesheets/semantic_ui/definitions/elements/header.less +2 -2
  14. data/assets/stylesheets/semantic_ui/definitions/elements/segment.less +7 -0
  15. data/assets/stylesheets/semantic_ui/definitions/modules/dropdown.less +26 -1
  16. data/assets/stylesheets/semantic_ui/definitions/modules/sidebar.less +0 -4
  17. data/assets/stylesheets/semantic_ui/definitions/views/item.less +17 -7
  18. data/assets/stylesheets/semantic_ui/themes/default/elements/flag.overrides +2 -2
  19. data/assets/stylesheets/semantic_ui/themes/default/elements/icon.overrides +54 -4
  20. data/assets/stylesheets/semantic_ui/themes/default/globals/site.variables +8 -8
  21. data/assets/stylesheets/semantic_ui/themes/default/views/comment.variables +0 -6
  22. data/assets/stylesheets/semantic_ui/themes/default/views/item.variables +5 -2
  23. data/lib/less/rails/semantic_ui/version.rb +1 -1
  24. metadata +7 -7
@@ -57,6 +57,7 @@ $.fn.form = function(parameters) {
57
57
  metadata,
58
58
  selector,
59
59
  className,
60
+ regExp,
60
61
  error,
61
62
 
62
63
  namespace,
@@ -233,6 +234,20 @@ $.fn.form = function(parameters) {
233
234
  ;
234
235
  },
235
236
 
237
+ determine: {
238
+ isValid: function() {
239
+ var
240
+ allValid = true
241
+ ;
242
+ $.each(validation, function(fieldName, field) {
243
+ if( !( module.validate.field(field, fieldName, true) ) ) {
244
+ allValid = false;
245
+ }
246
+ });
247
+ return allValid;
248
+ }
249
+ },
250
+
236
251
  is: {
237
252
  bracketedRule: function(rule) {
238
253
  return (rule.type && rule.type.match(settings.regExp.bracket));
@@ -251,17 +266,23 @@ $.fn.form = function(parameters) {
251
266
  blank: function($field) {
252
267
  return $.trim($field.val()) === '';
253
268
  },
254
- valid: function() {
269
+ valid: function(field) {
255
270
  var
256
271
  allValid = true
257
272
  ;
258
- module.verbose('Checking if form is valid');
259
- $.each(validation, function(fieldName, field) {
260
- if( !( module.validate.field(field, fieldName) ) ) {
261
- allValid = false;
262
- }
263
- });
264
- return allValid;
273
+ if(field) {
274
+ module.verbose('Checking if field is valid', field);
275
+ return module.validate.field(validation[field], field, false);
276
+ }
277
+ else {
278
+ module.verbose('Checking if form is valid');
279
+ $.each(validation, function(fieldName, field) {
280
+ if( !module.is.valid(fieldName) ) {
281
+ allValid = false;
282
+ }
283
+ });
284
+ return allValid;
285
+ }
265
286
  }
266
287
  },
267
288
 
@@ -338,7 +359,7 @@ $.fn.form = function(parameters) {
338
359
  $fieldGroup = $field.closest($group),
339
360
  validationRules = module.get.validation($field)
340
361
  ;
341
- if(settings.on == 'change' || ( $fieldGroup.hasClass(className.error) && settings.revalidate) ) {
362
+ if(validationRules && (settings.on == 'change' || ( $fieldGroup.hasClass(className.error) && settings.revalidate) )) {
342
363
  clearTimeout(module.timer);
343
364
  module.timer = setTimeout(function() {
344
365
  module.debug('Revalidating field', $field, module.get.validation($field));
@@ -465,6 +486,7 @@ $.fn.form = function(parameters) {
465
486
  metadata = settings.metadata;
466
487
  selector = settings.selector;
467
488
  className = settings.className;
489
+ regExp = settings.regExp;
468
490
  error = settings.error;
469
491
  moduleNamespace = 'module-' + namespace;
470
492
  eventNamespace = '.' + namespace;
@@ -477,7 +499,8 @@ $.fn.form = function(parameters) {
477
499
  },
478
500
  field: function(identifier) {
479
501
  module.verbose('Finding field with identifier', identifier);
480
- if( $field.filter('#' + identifier).length > 0 ) {
502
+ identifier = module.escape.string(identifier);
503
+ if($field.filter('#' + identifier).length > 0 ) {
481
504
  return $field.filter('#' + identifier);
482
505
  }
483
506
  else if( $field.filter('[name="' + identifier +'"]').length > 0 ) {
@@ -592,10 +615,11 @@ $.fn.form = function(parameters) {
592
615
 
593
616
  field: function(identifier) {
594
617
  module.verbose('Checking for existence of a field with identifier', identifier);
618
+ identifier = module.escape.string(identifier);
595
619
  if(typeof identifier !== 'string') {
596
620
  module.error(error.identifier, identifier);
597
621
  }
598
- if( $field.filter('#' + identifier).length > 0 ) {
622
+ if($field.filter('#' + identifier).length > 0 ) {
599
623
  return true;
600
624
  }
601
625
  else if( $field.filter('[name="' + identifier +'"]').length > 0 ) {
@@ -609,6 +633,13 @@ $.fn.form = function(parameters) {
609
633
 
610
634
  },
611
635
 
636
+ escape: {
637
+ string: function(text) {
638
+ text = String(text);
639
+ return text.replace(regExp.escape, '\\$&');
640
+ }
641
+ },
642
+
612
643
  add: {
613
644
  prompt: function(identifier, errors) {
614
645
  var
@@ -794,7 +825,7 @@ $.fn.form = function(parameters) {
794
825
 
795
826
  // reset errors
796
827
  formErrors = [];
797
- if( module.is.valid() ) {
828
+ if( module.determine.isValid() ) {
798
829
  module.debug('Form has no validation errors, submitting');
799
830
  module.set.success();
800
831
  if(ignoreCallbacks !== true) {
@@ -818,7 +849,16 @@ $.fn.form = function(parameters) {
818
849
  },
819
850
 
820
851
  // takes a validation object and returns whether field passes validation
821
- field: function(field, fieldName) {
852
+ field: function(field, fieldName, showErrors) {
853
+ showErrors = (showErrors !== undefined)
854
+ ? showErrors
855
+ : true
856
+ ;
857
+ if(typeof field == 'string') {
858
+ module.verbose('Validating field', field);
859
+ fieldName = field;
860
+ field = validation[field];
861
+ }
822
862
  var
823
863
  identifier = field.identifier || fieldName,
824
864
  $field = module.get.field(identifier),
@@ -854,13 +894,17 @@ $.fn.form = function(parameters) {
854
894
  });
855
895
  }
856
896
  if(fieldValid) {
857
- module.remove.prompt(identifier, fieldErrors);
858
- settings.onValid.call($field);
897
+ if(showErrors) {
898
+ module.remove.prompt(identifier, fieldErrors);
899
+ settings.onValid.call($field);
900
+ }
859
901
  }
860
902
  else {
861
- formErrors = formErrors.concat(fieldErrors);
862
- module.add.prompt(identifier, fieldErrors);
863
- settings.onInvalid.call($field, fieldErrors);
903
+ if(showErrors) {
904
+ formErrors = formErrors.concat(fieldErrors);
905
+ module.add.prompt(identifier, fieldErrors);
906
+ settings.onInvalid.call($field, fieldErrors);
907
+ }
864
908
  return false;
865
909
  }
866
910
  return true;
@@ -1090,8 +1134,9 @@ $.fn.form.settings = {
1090
1134
  },
1091
1135
 
1092
1136
  regExp: {
1137
+ htmlID : /^[a-zA-Z][\w:.-]*$/g,
1093
1138
  bracket : /\[(.*)\]/i,
1094
- decimal : /^\d*(\.)\d+/,
1139
+ decimal : /^\d+\.?\d*$/,
1095
1140
  email : /^[a-z0-9!#$%&'*+\/=?^_`{|}~.-]+@[a-z0-9]([a-z0-9-]*[a-z0-9])?(\.[a-z0-9]([a-z0-9-]*[a-z0-9])?)*$/i,
1096
1141
  escape : /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,
1097
1142
  flags : /^\/(.*)\/(.*)?/,
@@ -387,7 +387,11 @@ $.fn.visibility = function(parameters) {
387
387
  .attr('src', src)
388
388
  ;
389
389
  if(settings.transition) {
390
- if( $.fn.transition !== undefined ) {
390
+ if( $.fn.transition !== undefined) {
391
+ if($module.hasClass(className.visible)) {
392
+ module.debug('Transition already occurred on this image, skipping animation');
393
+ return;
394
+ }
391
395
  $module.transition(settings.transition, settings.duration, callback);
392
396
  }
393
397
  else {
@@ -1270,7 +1274,8 @@ $.fn.visibility.settings = {
1270
1274
 
1271
1275
  className: {
1272
1276
  fixed : 'fixed',
1273
- placeholder : 'placeholder'
1277
+ placeholder : 'placeholder',
1278
+ visible : 'visible'
1274
1279
  },
1275
1280
 
1276
1281
  error : {
@@ -461,6 +461,10 @@ $.fn.dropdown = function(parameters) {
461
461
  ? callback
462
462
  : function(){}
463
463
  ;
464
+ if(!module.can.show() && module.is.remote()) {
465
+ module.debug('No API results retrieved, searching before show');
466
+ module.queryRemote(module.get.query(), module.show);
467
+ }
464
468
  if( module.can.show() && !module.is.active() ) {
465
469
  module.debug('Showing dropdown');
466
470
  if(module.has.message() && !(module.has.maxSelections() || module.has.allResultsFiltered()) ) {
@@ -617,8 +621,17 @@ $.fn.dropdown = function(parameters) {
617
621
  .on('mousedown' + eventNamespace, module.event.mousedown)
618
622
  .on('mouseup' + eventNamespace, module.event.mouseup)
619
623
  .on('focus' + eventNamespace, module.event.focus)
620
- .on('blur' + eventNamespace, module.event.blur)
621
624
  ;
625
+ if(module.has.menuSearch() ) {
626
+ $module
627
+ .on('blur' + eventNamespace, selector.search, module.event.search.blur)
628
+ ;
629
+ }
630
+ else {
631
+ $module
632
+ .on('blur' + eventNamespace, module.event.blur)
633
+ ;
634
+ }
622
635
  }
623
636
  $menu
624
637
  .on('mouseenter' + eventNamespace, selector.item, module.event.item.mouseenter)
@@ -702,6 +715,9 @@ $.fn.dropdown = function(parameters) {
702
715
  if(settings.apiSettings) {
703
716
  if( module.can.useAPI() ) {
704
717
  module.queryRemote(searchTerm, function() {
718
+ if(settings.filterRemoteData) {
719
+ module.filterItems(searchTerm);
720
+ }
705
721
  afterFiltered();
706
722
  });
707
723
  }
@@ -757,7 +773,7 @@ $.fn.dropdown = function(parameters) {
757
773
  ? query
758
774
  : module.get.query(),
759
775
  results = null,
760
- escapedTerm = module.escape.regExp(searchTerm),
776
+ escapedTerm = module.escape.string(searchTerm),
761
777
  beginsWithRegExp = new RegExp('^' + escapedTerm, 'igm')
762
778
  ;
763
779
  // avoid loop if we're matching nothing
@@ -789,12 +805,15 @@ $.fn.dropdown = function(parameters) {
789
805
  }
790
806
  if(settings.match == 'both' || settings.match == 'value') {
791
807
  value = String(module.get.choiceValue($choice, text));
792
-
793
808
  if(value.search(beginsWithRegExp) !== -1) {
794
809
  results.push(this);
795
810
  return true;
796
811
  }
797
- else if(settings.fullTextSearch && module.fuzzySearch(searchTerm, value)) {
812
+ else if (settings.fullTextSearch === 'exact' && module.exactSearch(searchTerm, value)) {
813
+ results.push(this);
814
+ return true;
815
+ }
816
+ else if (settings.fullTextSearch === true && module.fuzzySearch(searchTerm, value)) {
798
817
  results.push(this);
799
818
  return true;
800
819
  }
@@ -876,7 +895,7 @@ $.fn.dropdown = function(parameters) {
876
895
  : $activeItem,
877
896
  hasSelected = ($selectedItem.length > 0)
878
897
  ;
879
- if(hasSelected) {
898
+ if(hasSelected && !module.is.multiple()) {
880
899
  module.debug('Forcing partial selection to selected item', $selectedItem);
881
900
  module.event.item.click.call($selectedItem, {}, true);
882
901
  return;
@@ -2183,7 +2202,7 @@ $.fn.dropdown = function(parameters) {
2183
2202
  $text.addClass(className.placeholder);
2184
2203
  },
2185
2204
  tabbable: function() {
2186
- if( module.has.search() ) {
2205
+ if( module.is.searchSelection() ) {
2187
2206
  module.debug('Added tabindex to searchable dropdown');
2188
2207
  $search
2189
2208
  .val('')
@@ -2295,12 +2314,13 @@ $.fn.dropdown = function(parameters) {
2295
2314
  },
2296
2315
  selectedItem: function($item) {
2297
2316
  var
2298
- value = module.get.choiceValue($item),
2299
- text = module.get.choiceText($item, false)
2317
+ value = module.get.choiceValue($item),
2318
+ searchText = module.get.choiceText($item, false),
2319
+ text = module.get.choiceText($item, true)
2300
2320
  ;
2301
2321
  module.debug('Setting user selection to item', $item);
2302
2322
  module.remove.activeItem();
2303
- module.set.partialSearch(text);
2323
+ module.set.partialSearch(searchText);
2304
2324
  module.set.activeItem($item);
2305
2325
  module.set.selected(value, $item);
2306
2326
  module.set.text(text);
@@ -2509,7 +2529,7 @@ $.fn.dropdown = function(parameters) {
2509
2529
  ;
2510
2530
  $label = $('<a />')
2511
2531
  .addClass(className.label)
2512
- .attr('data-value', escapedValue)
2532
+ .attr('data-' + metadata.value, escapedValue)
2513
2533
  .html(templates.label(escapedValue, text))
2514
2534
  ;
2515
2535
  $label = settings.onLabelCreate.call($label, escapedValue, text);
@@ -2557,7 +2577,7 @@ $.fn.dropdown = function(parameters) {
2557
2577
  optionValue: function(value) {
2558
2578
  var
2559
2579
  escapedValue = module.escape.value(value),
2560
- $option = $input.find('option[value="' + escapedValue + '"]'),
2580
+ $option = $input.find('option[value="' + module.escape.string(escapedValue) + '"]'),
2561
2581
  hasOption = ($option.length > 0)
2562
2582
  ;
2563
2583
  if(hasOption) {
@@ -2730,7 +2750,7 @@ $.fn.dropdown = function(parameters) {
2730
2750
  optionValue: function(value) {
2731
2751
  var
2732
2752
  escapedValue = module.escape.value(value),
2733
- $option = $input.find('option[value="' + escapedValue + '"]'),
2753
+ $option = $input.find('option[value="' + module.escape.string(escapedValue) + '"]'),
2734
2754
  hasOption = ($option.length > 0)
2735
2755
  ;
2736
2756
  if(!hasOption || !$option.hasClass(className.addition)) {
@@ -2849,7 +2869,7 @@ $.fn.dropdown = function(parameters) {
2849
2869
  label: function(value, shouldAnimate) {
2850
2870
  var
2851
2871
  $labels = $module.find(selector.label),
2852
- $removedLabel = $labels.filter('[data-value="' + value +'"]')
2872
+ $removedLabel = $labels.filter('[data-' + metadata.value + '="' + module.escape.string(value) +'"]')
2853
2873
  ;
2854
2874
  module.verbose('Removing label', $removedLabel);
2855
2875
  $removedLabel.remove();
@@ -2889,7 +2909,7 @@ $.fn.dropdown = function(parameters) {
2889
2909
  ;
2890
2910
  },
2891
2911
  tabbable: function() {
2892
- if( module.has.search() ) {
2912
+ if( module.is.searchSelection() ) {
2893
2913
  module.debug('Searchable dropdown initialized');
2894
2914
  $search
2895
2915
  .removeAttr('tabindex')
@@ -2963,7 +2983,7 @@ $.fn.dropdown = function(parameters) {
2963
2983
  escapedValue = module.escape.value(value),
2964
2984
  $labels = $module.find(selector.label)
2965
2985
  ;
2966
- return ($labels.filter('[data-value="' + escapedValue +'"]').length > 0);
2986
+ return ($labels.filter('[data-' + metadata.value + '="' + module.escape.string(escapedValue) +'"]').length > 0);
2967
2987
  },
2968
2988
  maxSelections: function() {
2969
2989
  return (settings.maxSelections && module.get.selectionCount() >= settings.maxSelections);
@@ -3083,6 +3103,9 @@ $.fn.dropdown = function(parameters) {
3083
3103
  multiple: function() {
3084
3104
  return $module.hasClass(className.multiple);
3085
3105
  },
3106
+ remote: function() {
3107
+ return settings.apiSettings && module.can.useAPI();
3108
+ },
3086
3109
  single: function() {
3087
3110
  return !module.is.multiple();
3088
3111
  },
@@ -3288,7 +3311,7 @@ $.fn.dropdown = function(parameters) {
3288
3311
  hasQuotes = (stringValue && value.search(regExp.quote) !== -1),
3289
3312
  values = []
3290
3313
  ;
3291
- if(!module.has.selectInput() || isUnparsable || !hasQuotes) {
3314
+ if(isUnparsable || !hasQuotes) {
3292
3315
  return value;
3293
3316
  }
3294
3317
  module.debug('Encoding quote values for use in select', value);
@@ -3300,7 +3323,7 @@ $.fn.dropdown = function(parameters) {
3300
3323
  }
3301
3324
  return value.replace(regExp.quote, '&quot;');
3302
3325
  },
3303
- regExp: function(text) {
3326
+ string: function(text) {
3304
3327
  text = String(text);
3305
3328
  return text.replace(regExp.escape, '\\$&');
3306
3329
  }
@@ -3503,7 +3526,10 @@ $.fn.dropdown.settings = {
3503
3526
  apiSettings : false,
3504
3527
  selectOnKeydown : true, // Whether selection should occur automatically when keyboard shortcuts used
3505
3528
  minCharacters : 0, // Minimum characters required to trigger API call
3529
+
3530
+ filterRemoteData : false, // Whether API results should be filtered after being returned for query term
3506
3531
  saveRemoteData : true, // Whether remote name/value pairs should be stored in sessionStorage to allow remote data to be restored on page refresh
3532
+
3507
3533
  throttle : 200, // How long to wait after last user input to search remotely
3508
3534
 
3509
3535
  context : window, // Context to use when determining if on screen
@@ -39,28 +39,29 @@ $.fn.search = function(parameters) {
39
39
  ? $.extend(true, {}, $.fn.search.settings, parameters)
40
40
  : $.extend({}, $.fn.search.settings),
41
41
 
42
- className = settings.className,
43
- metadata = settings.metadata,
44
- regExp = settings.regExp,
45
- fields = settings.fields,
46
- selector = settings.selector,
47
- error = settings.error,
48
- namespace = settings.namespace,
49
-
50
- eventNamespace = '.' + namespace,
51
- moduleNamespace = namespace + '-module',
52
-
53
- $module = $(this),
54
- $prompt = $module.find(selector.prompt),
55
- $searchButton = $module.find(selector.searchButton),
56
- $results = $module.find(selector.results),
57
- $result = $module.find(selector.result),
58
- $category = $module.find(selector.category),
59
-
60
- element = this,
61
- instance = $module.data(moduleNamespace),
62
-
63
- disabledBubbled = false,
42
+ className = settings.className,
43
+ metadata = settings.metadata,
44
+ regExp = settings.regExp,
45
+ fields = settings.fields,
46
+ selector = settings.selector,
47
+ error = settings.error,
48
+ namespace = settings.namespace,
49
+
50
+ eventNamespace = '.' + namespace,
51
+ moduleNamespace = namespace + '-module',
52
+
53
+ $module = $(this),
54
+ $prompt = $module.find(selector.prompt),
55
+ $searchButton = $module.find(selector.searchButton),
56
+ $results = $module.find(selector.results),
57
+ $result = $module.find(selector.result),
58
+ $category = $module.find(selector.category),
59
+
60
+ element = this,
61
+ instance = $module.data(moduleNamespace),
62
+
63
+ disabledBubbled = false,
64
+ resultsDismissed = false,
64
65
 
65
66
  module
66
67
  ;
@@ -147,11 +148,12 @@ $.fn.search = function(parameters) {
147
148
  },
148
149
  focus: function() {
149
150
  module.set.focus();
150
- if( module.has.minimumCharacters() ) {
151
- module.query();
152
- if( module.can.show() ) {
153
- module.showResults();
154
- }
151
+ if(settings.searchOnFocus && module.has.minimumCharacters() ) {
152
+ module.query(function() {
153
+ if(module.can.show() ) {
154
+ module.showResults();
155
+ }
156
+ });
155
157
  }
156
158
  },
157
159
  blur: function(event) {
@@ -166,6 +168,7 @@ $.fn.search = function(parameters) {
166
168
  if(pageLostFocus) {
167
169
  return;
168
170
  }
171
+ resultsDismissed = false;
169
172
  if(module.resultsClicked) {
170
173
  module.debug('Determining if user action caused search to close');
171
174
  $module
@@ -258,7 +261,8 @@ $.fn.search = function(parameters) {
258
261
  // search shortcuts
259
262
  if(keyCode == keys.escape) {
260
263
  module.verbose('Escape key pressed, blurring search field');
261
- module.trigger.blur();
264
+ module.hideResults();
265
+ resultsDismissed = true;
262
266
  }
263
267
  if( module.is.visible() ) {
264
268
  if(keyCode == keys.enter) {
@@ -318,7 +322,7 @@ $.fn.search = function(parameters) {
318
322
  },
319
323
 
320
324
  setup: {
321
- api: function(searchTerm) {
325
+ api: function(searchTerm, callback) {
322
326
  var
323
327
  apiSettings = {
324
328
  debug : settings.debug,
@@ -330,11 +334,13 @@ $.fn.search = function(parameters) {
330
334
  },
331
335
  onSuccess : function(response) {
332
336
  module.parse.response.call(element, response, searchTerm);
333
- },
334
- onAbort : function(response) {
337
+ callback();
335
338
  },
336
339
  onFailure : function() {
337
340
  module.displayMessage(error.serverError);
341
+ callback();
342
+ },
343
+ onAbort : function(response) {
338
344
  },
339
345
  onError : module.error
340
346
  },
@@ -386,20 +392,6 @@ $.fn.search = function(parameters) {
386
392
  }
387
393
  },
388
394
 
389
- trigger: {
390
- blur: function() {
391
- var
392
- events = document.createEvent('HTMLEvents'),
393
- promptElement = $prompt[0]
394
- ;
395
- if(promptElement) {
396
- module.verbose('Triggering native blur event');
397
- events.initEvent('blur', false, false);
398
- promptElement.dispatchEvent(events);
399
- }
400
- }
401
- },
402
-
403
395
  get: {
404
396
  inputEvent: function() {
405
397
  var
@@ -497,28 +489,36 @@ $.fn.search = function(parameters) {
497
489
  }
498
490
  },
499
491
 
500
- query: function() {
492
+ query: function(callback) {
493
+ callback = $.isFunction(callback)
494
+ ? callback
495
+ : function(){}
496
+ ;
501
497
  var
502
498
  searchTerm = module.get.value(),
503
499
  cache = module.read.cache(searchTerm)
504
500
  ;
501
+ callback = callback || function() {};
505
502
  if( module.has.minimumCharacters() ) {
506
503
  if(cache) {
507
504
  module.debug('Reading result from cache', searchTerm);
508
505
  module.save.results(cache.results);
509
506
  module.addResults(cache.html);
510
507
  module.inject.id(cache.results);
508
+ callback();
511
509
  }
512
510
  else {
513
511
  module.debug('Querying for', searchTerm);
514
512
  if($.isPlainObject(settings.source) || $.isArray(settings.source)) {
515
513
  module.search.local(searchTerm);
514
+ callback();
516
515
  }
517
516
  else if( module.can.useAPI() ) {
518
- module.search.remote(searchTerm);
517
+ module.search.remote(searchTerm, callback);
519
518
  }
520
519
  else {
521
520
  module.error(error.source);
521
+ callback();
522
522
  }
523
523
  }
524
524
  settings.onSearchQuery.call(element, searchTerm);
@@ -549,11 +549,15 @@ $.fn.search = function(parameters) {
549
549
  results : results
550
550
  });
551
551
  },
552
- remote: function(searchTerm) {
552
+ remote: function(searchTerm, callback) {
553
+ callback = $.isFunction(callback)
554
+ ? callback
555
+ : function(){}
556
+ ;
553
557
  if($module.api('is loading')) {
554
558
  $module.api('abort');
555
559
  }
556
- module.setup.api(searchTerm);
560
+ module.setup.api(searchTerm, callback);
557
561
  $module
558
562
  .api('query')
559
563
  ;
@@ -680,6 +684,15 @@ $.fn.search = function(parameters) {
680
684
  numCharacters = searchTerm.length
681
685
  ;
682
686
  return (numCharacters >= settings.minCharacters);
687
+ },
688
+ results: function() {
689
+ if($results.length === 0) {
690
+ return false;
691
+ }
692
+ var
693
+ html = $results.html()
694
+ ;
695
+ return html != '';
683
696
  }
684
697
  },
685
698
 
@@ -846,12 +859,21 @@ $.fn.search = function(parameters) {
846
859
  module.showResults();
847
860
  }
848
861
  else {
849
- module.hideResults();
862
+ module.hideResults(function() {
863
+ $results.empty();
864
+ });
850
865
  }
851
866
  },
852
867
 
853
- showResults: function() {
854
- if(!module.is.visible()) {
868
+ showResults: function(callback) {
869
+ callback = $.isFunction(callback)
870
+ ? callback
871
+ : function(){}
872
+ ;
873
+ if(resultsDismissed) {
874
+ return;
875
+ }
876
+ if(!module.is.visible() && module.has.results()) {
855
877
  if( module.can.transition() ) {
856
878
  module.debug('Showing results with css animations');
857
879
  $results
@@ -860,6 +882,9 @@ $.fn.search = function(parameters) {
860
882
  debug : settings.debug,
861
883
  verbose : settings.verbose,
862
884
  duration : settings.duration,
885
+ onComplete : function() {
886
+ callback();
887
+ },
863
888
  queue : true
864
889
  })
865
890
  ;
@@ -874,7 +899,11 @@ $.fn.search = function(parameters) {
874
899
  settings.onResultsOpen.call($results);
875
900
  }
876
901
  },
877
- hideResults: function() {
902
+ hideResults: function(callback) {
903
+ callback = $.isFunction(callback)
904
+ ? callback
905
+ : function(){}
906
+ ;
878
907
  if( module.is.visible() ) {
879
908
  if( module.can.transition() ) {
880
909
  module.debug('Hiding results with css animations');
@@ -884,6 +913,9 @@ $.fn.search = function(parameters) {
884
913
  debug : settings.debug,
885
914
  verbose : settings.verbose,
886
915
  duration : settings.duration,
916
+ onComplete : function() {
917
+ callback();
918
+ },
887
919
  queue : true
888
920
  })
889
921
  ;
@@ -1144,6 +1176,9 @@ $.fn.search.settings = {
1144
1176
  // object to search
1145
1177
  source : false,
1146
1178
 
1179
+ // Whether search should query current term on focus
1180
+ searchOnFocus : true,
1181
+
1147
1182
  // fields to search
1148
1183
  searchFields : [
1149
1184
  'title',