public/lib/semantic/components/search.js   F
last analyzed

Complexity

Total Complexity 276
Complexity/F 2.58

Size

Lines of Code 1441
Function Count 107

Duplication

Duplicated Lines 1441
Ratio 100 %

Importance

Changes 0
Metric Value
cc 0
eloc 956
nc 0
dl 1441
loc 1441
rs 1.644
c 0
b 0
f 0
wmc 276
mnd 5
bc 250
fnc 107
bpm 2.3363
cpm 2.5794
noi 34

9 Functions

Rating   Name   Duplication   Size   Complexity  
A $.fn.search.settings.onResults 1 1 1
A $.fn.search.settings.templates.escape 21 21 2
A $.fn.search.settings.templates.message 22 22 4
A $.fn.search.settings.onSearchQuery 1 1 1
B $.fn.search.settings.templates.category 62 62 3
A $.fn.search.settings.onResultsOpen 1 1 1
A $.fn.search.settings.templates.standard 47 47 3
B $.fn.search 1140 1140 2
A $.fn.search.settings.onResultsClose 1 1 1

How to fix   Duplicated Code    Complexity   

Duplicated Code

Duplicate code is one of the most pungent code smells. A rule that is often used is to re-structure code once it is duplicated in three or more places.

Common duplication problems, and corresponding solutions are:

Complexity

 Tip:   Before tackling complexity, make sure that you eliminate any duplication first. This often can reduce the size of classes significantly.

Complex classes like public/lib/semantic/components/search.js often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

1
/*!
2
 * # Semantic UI 2.2.11 - Search
3
 * http://github.com/semantic-org/semantic-ui/
4
 *
5
 *
6
 * Released under the MIT license
7
 * http://opensource.org/licenses/MIT
8
 *
9
 */
10
11 View Code Duplication
;(function ($, window, document, undefined) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated in your project.
Loading history...
12
13
"use strict";
14
15
window = (typeof window != 'undefined' && window.Math == Math)
16
  ? window
17
  : (typeof self != 'undefined' && self.Math == Math)
0 ignored issues
show
Bug introduced by
The variable self seems to be never declared. If this is a global, consider adding a /** global: self */ comment.

This checks looks for references to variables that have not been declared. This is most likey a typographical error or a variable has been renamed.

To learn more about declaring variables in Javascript, see the MDN.

Loading history...
18
    ? self
19
    : Function('return this')()
0 ignored issues
show
Performance Best Practice introduced by
Using new Function() to create a function is slow and difficult to debug. Such functions do not create a closure. Consider using another way to define your function.
Loading history...
20
;
21
22
$.fn.search = function(parameters) {
23
  var
24
    $allModules     = $(this),
25
    moduleSelector  = $allModules.selector || '',
26
27
    time            = new Date().getTime(),
28
    performance     = [],
29
30
    query           = arguments[0],
31
    methodInvoked   = (typeof query == 'string'),
32
    queryArguments  = [].slice.call(arguments, 1),
33
    returnedValue
34
  ;
35
  $(this)
36
    .each(function() {
37
      var
38
        settings          = ( $.isPlainObject(parameters) )
39
          ? $.extend(true, {}, $.fn.search.settings, parameters)
40
          : $.extend({}, $.fn.search.settings),
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),
0 ignored issues
show
Unused Code introduced by
The variable $category seems to be never used. Consider removing it.
Loading history...
59
60
        element          = this,
61
        instance         = $module.data(moduleNamespace),
62
63
        disabledBubbled  = false,
64
        resultsDismissed = false,
65
66
        module
67
      ;
68
69
      module = {
70
71
        initialize: function() {
72
          module.verbose('Initializing module');
73
          module.determine.searchFields();
74
          module.bind.events();
75
          module.set.type();
76
          module.create.results();
77
          module.instantiate();
78
        },
79
        instantiate: function() {
80
          module.verbose('Storing instance of module', module);
81
          instance = module;
82
          $module
83
            .data(moduleNamespace, module)
84
          ;
85
        },
86
        destroy: function() {
87
          module.verbose('Destroying instance');
88
          $module
89
            .off(eventNamespace)
90
            .removeData(moduleNamespace)
91
          ;
92
        },
93
94
        refresh: function() {
95
          module.debug('Refreshing selector cache');
96
          $prompt         = $module.find(selector.prompt);
97
          $searchButton   = $module.find(selector.searchButton);
98
          $category       = $module.find(selector.category);
0 ignored issues
show
Unused Code introduced by
The variable $category seems to be never used. Consider removing it.
Loading history...
99
          $results        = $module.find(selector.results);
100
          $result         = $module.find(selector.result);
101
        },
102
103
        refreshResults: function() {
104
          $results = $module.find(selector.results);
105
          $result  = $module.find(selector.result);
106
        },
107
108
        bind: {
109
          events: function() {
110
            module.verbose('Binding events to search');
111
            if(settings.automatic) {
112
              $module
113
                .on(module.get.inputEvent() + eventNamespace, selector.prompt, module.event.input)
114
              ;
115
              $prompt
116
                .attr('autocomplete', 'off')
117
              ;
118
            }
119
            $module
120
              // prompt
121
              .on('focus'     + eventNamespace, selector.prompt, module.event.focus)
122
              .on('blur'      + eventNamespace, selector.prompt, module.event.blur)
123
              .on('keydown'   + eventNamespace, selector.prompt, module.handleKeyboard)
124
              // search button
125
              .on('click'     + eventNamespace, selector.searchButton, module.query)
126
              // results
127
              .on('mousedown' + eventNamespace, selector.results, module.event.result.mousedown)
128
              .on('mouseup'   + eventNamespace, selector.results, module.event.result.mouseup)
129
              .on('click'     + eventNamespace, selector.result,  module.event.result.click)
130
            ;
131
          }
132
        },
133
134
        determine: {
135
          searchFields: function() {
136
            // this makes sure $.extend does not add specified search fields to default fields
137
            // this is the only setting which should not extend defaults
138
            if(parameters && parameters.searchFields !== undefined) {
139
              settings.searchFields = parameters.searchFields;
140
            }
141
          }
142
        },
143
144
        event: {
145
          input: function() {
146
            if(settings.searchDelay) {
147
              clearTimeout(module.timer);
148
              module.timer = setTimeout(function() {
149
                if(module.is.focused()) {
150
                  module.query();
151
                }
152
              }, settings.searchDelay);
153
            }
154
            else {
155
              module.query();
156
            }
157
          },
158
          focus: function() {
159
            module.set.focus();
160
            if(settings.searchOnFocus && module.has.minimumCharacters() ) {
161
              module.query(function() {
162
                if(module.can.show() ) {
163
                  module.showResults();
164
                }
165
              });
166
            }
167
          },
168
          blur: function(event) {
169
            var
170
              pageLostFocus = (document.activeElement === this),
171
              callback      = function() {
172
                module.cancel.query();
173
                module.remove.focus();
174
                module.timer = setTimeout(module.hideResults, settings.hideDelay);
175
              }
176
            ;
177
            if(pageLostFocus) {
178
              return;
179
            }
180
            resultsDismissed = false;
181
            if(module.resultsClicked) {
182
              module.debug('Determining if user action caused search to close');
183
              $module
184
                .one('click.close' + eventNamespace, selector.results, function(event) {
185
                  if(module.is.inMessage(event) || disabledBubbled) {
186
                    $prompt.focus();
187
                    return;
188
                  }
189
                  disabledBubbled = false;
190
                  if( !module.is.animating() && !module.is.hidden()) {
191
                    callback();
192
                  }
193
                })
194
              ;
195
            }
196
            else {
197
              module.debug('Input blurred without user action, closing results');
198
              callback();
199
            }
200
          },
201
          result: {
202
            mousedown: function() {
203
              module.resultsClicked = true;
204
            },
205
            mouseup: function() {
206
              module.resultsClicked = false;
207
            },
208
            click: function(event) {
209
              module.debug('Search result selected');
210
              var
211
                $result = $(this),
212
                $title  = $result.find(selector.title).eq(0),
213
                $link   = $result.is('a[href]')
214
                  ? $result
215
                  : $result.find('a[href]').eq(0),
216
                href    = $link.attr('href')   || false,
217
                target  = $link.attr('target') || false,
218
                title   = $title.html(),
0 ignored issues
show
Unused Code introduced by
The assignment to variable title seems to be never used. Consider removing it.
Loading history...
219
                // title is used for result lookup
220
                value   = ($title.length > 0)
221
                  ? $title.text()
222
                  : false,
223
                results = module.get.results(),
224
                result  = $result.data(metadata.result) || module.get.result(value, results),
225
                returnedValue
0 ignored issues
show
Unused Code introduced by
The variable returnedValue seems to be never used. Consider removing it.
Loading history...
226
              ;
227
              if( $.isFunction(settings.onSelect) ) {
228
                if(settings.onSelect.call(element, result, results) === false) {
229
                  module.debug('Custom onSelect callback cancelled default select action');
230
                  disabledBubbled = true;
231
                  return;
232
                }
233
              }
234
              module.hideResults();
235
              if(value) {
236
                module.set.value(value);
237
              }
238
              if(href) {
239
                module.verbose('Opening search link found in result', $link);
240
                if(target == '_blank' || event.ctrlKey) {
241
                  window.open(href);
242
                }
243
                else {
244
                  window.location.href = (href);
245
                }
246
              }
247
            }
248
          }
249
        },
250
        handleKeyboard: function(event) {
251
          var
252
            // force selector refresh
253
            $result         = $module.find(selector.result),
254
            $category       = $module.find(selector.category),
255
            $activeResult   = $result.filter('.' + className.active),
256
            currentIndex    = $result.index( $activeResult ),
257
            resultSize      = $result.length,
258
            hasActiveResult = $activeResult.length > 0,
259
260
            keyCode         = event.which,
261
            keys            = {
262
              backspace : 8,
263
              enter     : 13,
264
              escape    : 27,
265
              upArrow   : 38,
266
              downArrow : 40
267
            },
268
            newIndex
269
          ;
270
          // search shortcuts
271
          if(keyCode == keys.escape) {
272
            module.verbose('Escape key pressed, blurring search field');
273
            module.hideResults();
274
            resultsDismissed = true;
275
          }
276
          if( module.is.visible() ) {
277
            if(keyCode == keys.enter) {
278
              module.verbose('Enter key pressed, selecting active result');
279
              if( $result.filter('.' + className.active).length > 0 ) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if $result.filter("." + className.active).length > 0 is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
280
                module.event.result.click.call($result.filter('.' + className.active), event);
281
                event.preventDefault();
282
                return false;
283
              }
284
            }
285
            else if(keyCode == keys.upArrow && hasActiveResult) {
286
              module.verbose('Up key pressed, changing active result');
287
              newIndex = (currentIndex - 1 < 0)
288
                ? currentIndex
289
                : currentIndex - 1
290
              ;
291
              $category
292
                .removeClass(className.active)
293
              ;
294
              $result
295
                .removeClass(className.active)
296
                .eq(newIndex)
297
                  .addClass(className.active)
298
                  .closest($category)
299
                    .addClass(className.active)
300
              ;
301
              event.preventDefault();
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
302
            }
303
            else if(keyCode == keys.downArrow) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if keyCode == keys.downArrow is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
304
              module.verbose('Down key pressed, changing active result');
305
              newIndex = (currentIndex + 1 >= resultSize)
306
                ? currentIndex
307
                : currentIndex + 1
308
              ;
309
              $category
310
                .removeClass(className.active)
311
              ;
312
              $result
313
                .removeClass(className.active)
314
                .eq(newIndex)
315
                  .addClass(className.active)
316
                  .closest($category)
317
                    .addClass(className.active)
318
              ;
319
              event.preventDefault();
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
320
            }
321
          }
322
          else {
323
            // query shortcuts
324
            if(keyCode == keys.enter) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if keyCode == keys.enter is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
325
              module.verbose('Enter key pressed, executing query');
326
              module.query();
327
              module.set.buttonPressed();
328
              $prompt.one('keyup', module.remove.buttonFocus);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
329
            }
330
          }
331
        },
332
333
        setup: {
334
          api: function(searchTerm, callback) {
335
            var
336
              apiSettings = {
337
                debug             : settings.debug,
338
                on                : false,
339
                cache             : true,
340
                action            : 'search',
341
                urlData           : {
342
                  query : searchTerm
343
                },
344
                onSuccess         : function(response) {
345
                  module.parse.response.call(element, response, searchTerm);
346
                  callback();
347
                },
348
                onFailure         : function() {
349
                  module.displayMessage(error.serverError);
350
                  callback();
351
                },
352
                onAbort : function(response) {
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
353
                },
354
                onError           : module.error
355
              },
356
              searchHTML
0 ignored issues
show
Unused Code introduced by
The variable searchHTML seems to be never used. Consider removing it.
Loading history...
357
            ;
358
            $.extend(true, apiSettings, settings.apiSettings);
359
            module.verbose('Setting up API request', apiSettings);
360
            $module.api(apiSettings);
361
          }
362
        },
363
364
        can: {
365
          useAPI: function() {
366
            return $.fn.api !== undefined;
367
          },
368
          show: function() {
369
            return module.is.focused() && !module.is.visible() && !module.is.empty();
370
          },
371
          transition: function() {
372
            return settings.transition && $.fn.transition !== undefined && $module.transition('is supported');
373
          }
374
        },
375
376
        is: {
377
          animating: function() {
378
            return $results.hasClass(className.animating);
379
          },
380
          hidden: function() {
381
            return $results.hasClass(className.hidden);
382
          },
383
          inMessage: function(event) {
384
            if(!event.target) {
385
              return;
0 ignored issues
show
Comprehensibility Best Practice introduced by
Are you sure this return statement is not missing an argument? If this is intended, consider adding an explicit undefined like return undefined;.
Loading history...
386
            }
387
            var
388
              $target = $(event.target),
389
              isInDOM = $.contains(document.documentElement, event.target)
390
            ;
391
            return (isInDOM && $target.closest(selector.message).length > 0);
392
          },
393
          empty: function() {
394
            return ($results.html() === '');
395
          },
396
          visible: function() {
397
            return ($results.filter(':visible').length > 0);
398
          },
399
          focused: function() {
400
            return ($prompt.filter(':focus').length > 0);
401
          }
402
        },
403
404
        get: {
405
          inputEvent: function() {
406
            var
407
              prompt = $prompt[0],
408
              inputEvent   = (prompt !== undefined && prompt.oninput !== undefined)
409
                ? 'input'
410
                : (prompt !== undefined && prompt.onpropertychange !== undefined)
411
                  ? 'propertychange'
412
                  : 'keyup'
413
            ;
414
            return inputEvent;
415
          },
416
          value: function() {
417
            return $prompt.val();
418
          },
419
          results: function() {
420
            var
421
              results = $module.data(metadata.results)
422
            ;
423
            return results;
424
          },
425
          result: function(value, results) {
426
            var
427
              lookupFields = ['title', 'id'],
428
              result       = false
429
            ;
430
            value = (value !== undefined)
431
              ? value
432
              : module.get.value()
433
            ;
434
            results = (results !== undefined)
435
              ? results
436
              : module.get.results()
437
            ;
438
            if(settings.type === 'category') {
439
              module.debug('Finding result that matches', value);
440
              $.each(results, function(index, category) {
441
                if($.isArray(category.results)) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if $.isArray(category.results) is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
442
                  result = module.search.object(value, category.results, lookupFields)[0];
443
                  // don't continue searching if a result is found
444
                  if(result) {
0 ignored issues
show
Complexity Best Practice introduced by
There is no return statement if result is false. Are you sure this is correct? If so, consider adding return; explicitly.

This check looks for functions where a return statement is found in some execution paths, but not in all.

Consider this little piece of code

function isBig(a) {
    if (a > 5000) {
        return "yes";
    }
}

console.log(isBig(5001)); //returns yes
console.log(isBig(42)); //returns undefined

The function isBig will only return a specific value when its parameter is bigger than 5000. In any other case, it will implicitly return undefined.

This behaviour may not be what you had intended. In any case, you can add a return undefined to the other execution path to make the return value explicit.

Loading history...
445
                    return false;
446
                  }
447
                }
448
              });
449
            }
450
            else {
451
              module.debug('Finding result in results object', value);
452
              result = module.search.object(value, results, lookupFields)[0];
453
            }
454
            return result || false;
455
          },
456
        },
457
458
        select: {
459
          firstResult: function() {
460
            module.verbose('Selecting first result');
461
            $result.first().addClass(className.active);
462
          }
463
        },
464
465
        set: {
466
          focus: function() {
467
            $module.addClass(className.focus);
468
          },
469
          loading: function() {
470
            $module.addClass(className.loading);
471
          },
472
          value: function(value) {
473
            module.verbose('Setting search input value', value);
474
            $prompt
475
              .val(value)
476
            ;
477
          },
478
          type: function(type) {
479
            type = type || settings.type;
480
            if(settings.type == 'category') {
481
              $module.addClass(settings.type);
482
            }
483
          },
484
          buttonPressed: function() {
485
            $searchButton.addClass(className.pressed);
486
          }
487
        },
488
489
        remove: {
490
          loading: function() {
491
            $module.removeClass(className.loading);
492
          },
493
          focus: function() {
494
            $module.removeClass(className.focus);
495
          },
496
          buttonPressed: function() {
497
            $searchButton.removeClass(className.pressed);
498
          }
499
        },
500
501
        query: function(callback) {
502
          callback = $.isFunction(callback)
503
            ? callback
504
            : function(){}
505
          ;
506
          var
507
            searchTerm = module.get.value(),
508
            cache = module.read.cache(searchTerm)
509
          ;
510
          callback = callback || function() {};
511
          if( module.has.minimumCharacters() )  {
512
            if(cache) {
513
              module.debug('Reading result from cache', searchTerm);
514
              module.save.results(cache.results);
515
              module.addResults(cache.html);
516
              module.inject.id(cache.results);
517
              callback();
518
            }
519
            else {
520
              module.debug('Querying for', searchTerm);
521
              if($.isPlainObject(settings.source) || $.isArray(settings.source)) {
522
                module.search.local(searchTerm);
523
                callback();
524
              }
525
              else if( module.can.useAPI() ) {
526
                module.search.remote(searchTerm, callback);
527
              }
528
              else {
529
                module.error(error.source);
530
                callback();
531
              }
532
            }
533
            settings.onSearchQuery.call(element, searchTerm);
534
          }
535
          else {
536
            module.hideResults();
537
          }
538
        },
539
540
        search: {
541
          local: function(searchTerm) {
542
            var
543
              results = module.search.object(searchTerm, settings.content),
544
              searchHTML
545
            ;
546
            module.set.loading();
547
            module.save.results(results);
548
            module.debug('Returned local search results', results);
549
550
            searchHTML = module.generateResults({
551
              results: results
552
            });
553
            module.remove.loading();
554
            module.addResults(searchHTML);
555
            module.inject.id(results);
556
            module.write.cache(searchTerm, {
557
              html    : searchHTML,
558
              results : results
559
            });
560
          },
561
          remote: function(searchTerm, callback) {
562
            callback = $.isFunction(callback)
563
              ? callback
564
              : function(){}
565
            ;
566
            if($module.api('is loading')) {
567
              $module.api('abort');
568
            }
569
            module.setup.api(searchTerm, callback);
570
            $module
571
              .api('query')
572
            ;
573
          },
574
          object: function(searchTerm, source, searchFields) {
575
            var
576
              results      = [],
577
              fuzzyResults = [],
578
              searchExp    = searchTerm.toString().replace(regExp.escape, '\\$&'),
579
              matchRegExp  = new RegExp(regExp.beginsWith + searchExp, 'i'),
580
581
              // avoid duplicates when pushing results
582
              addResult = function(array, result) {
583
                var
584
                  notResult      = ($.inArray(result, results) == -1),
585
                  notFuzzyResult = ($.inArray(result, fuzzyResults) == -1)
586
                ;
587
                if(notResult && notFuzzyResult) {
588
                  array.push(result);
589
                }
590
              }
591
            ;
592
            source = source || settings.source;
593
            searchFields = (searchFields !== undefined)
594
              ? searchFields
595
              : settings.searchFields
596
            ;
597
598
            // search fields should be array to loop correctly
599
            if(!$.isArray(searchFields)) {
600
              searchFields = [searchFields];
601
            }
602
603
            // exit conditions if no source
604
            if(source === undefined || source === false) {
605
              module.error(error.source);
606
              return [];
607
            }
608
609
            // iterate through search fields looking for matches
610
            $.each(searchFields, function(index, field) {
611
              $.each(source, function(label, content) {
612
                var
613
                  fieldExists = (typeof content[field] == 'string')
614
                ;
615
                if(fieldExists) {
616
                  if( content[field].search(matchRegExp) !== -1) {
617
                    // content starts with value (first in results)
618
                    addResult(results, content);
619
                  }
620
                  else if(settings.searchFullText && module.fuzzySearch(searchTerm, content[field]) ) {
621
                    // content fuzzy matches (last in results)
622
                    addResult(fuzzyResults, content);
623
                  }
624
                }
625
              });
626
            });
627
            return $.merge(results, fuzzyResults);
628
          }
629
        },
630
631
        fuzzySearch: function(query, term) {
632
          var
633
            termLength  = term.length,
634
            queryLength = query.length
635
          ;
636
          if(typeof query !== 'string') {
637
            return false;
638
          }
639
          query = query.toLowerCase();
640
          term  = term.toLowerCase();
641
          if(queryLength > termLength) {
642
            return false;
643
          }
644
          if(queryLength === termLength) {
645
            return (query === term);
646
          }
647
          search: for (var characterIndex = 0, nextCharacterIndex = 0; characterIndex < queryLength; characterIndex++) {
648
            var
649
              queryCharacter = query.charCodeAt(characterIndex)
650
            ;
651
            while(nextCharacterIndex < termLength) {
652
              if(term.charCodeAt(nextCharacterIndex++) === queryCharacter) {
653
                continue search;
654
              }
655
            }
656
            return false;
657
          }
658
          return true;
659
        },
660
661
        parse: {
662
          response: function(response, searchTerm) {
663
            var
664
              searchHTML = module.generateResults(response)
665
            ;
666
            module.verbose('Parsing server response', response);
667
            if(response !== undefined) {
668
              if(searchTerm !== undefined && response[fields.results] !== undefined) {
669
                module.addResults(searchHTML);
670
                module.inject.id(response[fields.results]);
671
                module.write.cache(searchTerm, {
672
                  html    : searchHTML,
673
                  results : response[fields.results]
674
                });
675
                module.save.results(response[fields.results]);
676
              }
677
            }
678
          }
679
        },
680
681
        cancel: {
682
          query: function() {
683
            if( module.can.useAPI() ) {
684
              $module.api('abort');
685
            }
686
          }
687
        },
688
689
        has: {
690
          minimumCharacters: function() {
691
            var
692
              searchTerm    = module.get.value(),
693
              numCharacters = searchTerm.length
694
            ;
695
            return (numCharacters >= settings.minCharacters);
696
          },
697
          results: function() {
698
            if($results.length === 0) {
699
              return false;
700
            }
701
            var
702
              html = $results.html()
703
            ;
704
            return html != '';
705
          }
706
        },
707
708
        clear: {
709
          cache: function(value) {
710
            var
711
              cache = $module.data(metadata.cache)
712
            ;
713
            if(!value) {
714
              module.debug('Clearing cache', value);
715
              $module.removeData(metadata.cache);
716
            }
717
            else if(value && cache && cache[value]) {
718
              module.debug('Removing value from cache', value);
719
              delete cache[value];
720
              $module.data(metadata.cache, cache);
721
            }
722
          }
723
        },
724
725
        read: {
726
          cache: function(name) {
727
            var
728
              cache = $module.data(metadata.cache)
729
            ;
730
            if(settings.cache) {
731
              module.verbose('Checking cache for generated html for query', name);
732
              return (typeof cache == 'object') && (cache[name] !== undefined)
733
                ? cache[name]
734
                : false
735
              ;
736
            }
737
            return false;
738
          }
739
        },
740
741
        create: {
742
          id: function(resultIndex, categoryIndex) {
743
            var
744
              resultID      = (resultIndex + 1), // not zero indexed
745
              categoryID    = (categoryIndex + 1),
0 ignored issues
show
Unused Code introduced by
The variable categoryID seems to be never used. Consider removing it.
Loading history...
746
              firstCharCode,
0 ignored issues
show
Unused Code introduced by
The variable firstCharCode seems to be never used. Consider removing it.
Loading history...
747
              letterID,
748
              id
749
            ;
750
            if(categoryIndex !== undefined) {
751
              // start char code for "A"
752
              letterID = String.fromCharCode(97 + categoryIndex);
753
              id          = letterID + resultID;
754
              module.verbose('Creating category result id', id);
755
            }
756
            else {
757
              id = resultID;
758
              module.verbose('Creating result id', id);
759
            }
760
            return id;
761
          },
762
          results: function() {
763
            if($results.length === 0) {
764
              $results = $('<div />')
765
                .addClass(className.results)
766
                .appendTo($module)
767
              ;
768
            }
769
          }
770
        },
771
772
        inject: {
773
          result: function(result, resultIndex, categoryIndex) {
774
            module.verbose('Injecting result into results');
775
            var
776
              $selectedResult = (categoryIndex !== undefined)
777
                ? $results
778
                    .children().eq(categoryIndex)
779
                      .children(selector.result).eq(resultIndex)
780
                : $results
781
                    .children(selector.result).eq(resultIndex)
782
            ;
783
            module.verbose('Injecting results metadata', $selectedResult);
784
            $selectedResult
785
              .data(metadata.result, result)
786
            ;
787
          },
788
          id: function(results) {
789
            module.debug('Injecting unique ids into results');
790
            var
791
              // since results may be object, we must use counters
792
              categoryIndex = 0,
793
              resultIndex   = 0
794
            ;
795
            if(settings.type === 'category') {
796
              // iterate through each category result
797
              $.each(results, function(index, category) {
798
                resultIndex = 0;
799
                $.each(category.results, function(index, value) {
0 ignored issues
show
Unused Code introduced by
The parameter value is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
800
                  var
801
                    result = category.results[index]
802
                  ;
803
                  if(result.id === undefined) {
804
                    result.id = module.create.id(resultIndex, categoryIndex);
805
                  }
806
                  module.inject.result(result, resultIndex, categoryIndex);
807
                  resultIndex++;
808
                });
809
                categoryIndex++;
810
              });
811
            }
812
            else {
813
              // top level
814
              $.each(results, function(index, value) {
0 ignored issues
show
Unused Code introduced by
The parameter value is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
815
                var
816
                  result = results[index]
817
                ;
818
                if(result.id === undefined) {
819
                  result.id = module.create.id(resultIndex);
820
                }
821
                module.inject.result(result, resultIndex);
822
                resultIndex++;
823
              });
824
            }
825
            return results;
826
          }
827
        },
828
829
        save: {
830
          results: function(results) {
831
            module.verbose('Saving current search results to metadata', results);
832
            $module.data(metadata.results, results);
833
          }
834
        },
835
836
        write: {
837
          cache: function(name, value) {
838
            var
839
              cache = ($module.data(metadata.cache) !== undefined)
840
                ? $module.data(metadata.cache)
841
                : {}
842
            ;
843
            if(settings.cache) {
844
              module.verbose('Writing generated html to cache', name, value);
845
              cache[name] = value;
846
              $module
847
                .data(metadata.cache, cache)
848
              ;
849
            }
850
          }
851
        },
852
853
        addResults: function(html) {
854
          if( $.isFunction(settings.onResultsAdd) ) {
855
            if( settings.onResultsAdd.call($results, html) === false ) {
856
              module.debug('onResultsAdd callback cancelled default action');
857
              return false;
858
            }
859
          }
860
          if(html) {
861
            $results
862
              .html(html)
863
            ;
864
            module.refreshResults();
865
            if(settings.selectFirstResult) {
866
              module.select.firstResult();
867
            }
868
            module.showResults();
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
869
          }
870
          else {
871
            module.hideResults(function() {
872
              $results.empty();
873
            });
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
874
          }
875
        },
876
877
        showResults: function(callback) {
878
          callback = $.isFunction(callback)
879
            ? callback
880
            : function(){}
881
          ;
882
          if(resultsDismissed) {
883
            return;
884
          }
885
          if(!module.is.visible() && module.has.results()) {
886
            if( module.can.transition() ) {
887
              module.debug('Showing results with css animations');
888
              $results
889
                .transition({
890
                  animation  : settings.transition + ' in',
891
                  debug      : settings.debug,
892
                  verbose    : settings.verbose,
893
                  duration   : settings.duration,
894
                  onComplete : function() {
895
                    callback();
896
                  },
897
                  queue      : true
898
                })
899
              ;
900
            }
901
            else {
902
              module.debug('Showing results with javascript');
903
              $results
904
                .stop()
905
                .fadeIn(settings.duration, settings.easing)
906
              ;
907
            }
908
            settings.onResultsOpen.call($results);
909
          }
910
        },
911
        hideResults: function(callback) {
912
          callback = $.isFunction(callback)
913
            ? callback
914
            : function(){}
915
          ;
916
          if( module.is.visible() ) {
917
            if( module.can.transition() ) {
918
              module.debug('Hiding results with css animations');
919
              $results
920
                .transition({
921
                  animation  : settings.transition + ' out',
922
                  debug      : settings.debug,
923
                  verbose    : settings.verbose,
924
                  duration   : settings.duration,
925
                  onComplete : function() {
926
                    callback();
927
                  },
928
                  queue      : true
929
                })
930
              ;
931
            }
932
            else {
933
              module.debug('Hiding results with javascript');
934
              $results
935
                .stop()
936
                .fadeOut(settings.duration, settings.easing)
937
              ;
938
            }
939
            settings.onResultsClose.call($results);
940
          }
941
        },
942
943
        generateResults: function(response) {
944
          module.debug('Generating html from response', response);
945
          var
946
            template       = settings.templates[settings.type],
947
            isProperObject = ($.isPlainObject(response[fields.results]) && !$.isEmptyObject(response[fields.results])),
948
            isProperArray  = ($.isArray(response[fields.results]) && response[fields.results].length > 0),
949
            html           = ''
950
          ;
951
          if(isProperObject || isProperArray ) {
952
            if(settings.maxResults > 0) {
953
              if(isProperObject) {
954
                if(settings.type == 'standard') {
955
                  module.error(error.maxResults);
956
                }
957
              }
958
              else {
959
                response[fields.results] = response[fields.results].slice(0, settings.maxResults);
960
              }
961
            }
962
            if($.isFunction(template)) {
963
              html = template(response, fields);
964
            }
965
            else {
966
              module.error(error.noTemplate, false);
967
            }
968
          }
969
          else if(settings.showNoResults) {
970
            html = module.displayMessage(error.noResults, 'empty');
971
          }
972
          settings.onResults.call(element, response);
973
          return html;
974
        },
975
976
        displayMessage: function(text, type) {
977
          type = type || 'standard';
978
          module.debug('Displaying message', text, type);
979
          module.addResults( settings.templates.message(text, type) );
980
          return settings.templates.message(text, type);
981
        },
982
983
        setting: function(name, value) {
984
          if( $.isPlainObject(name) ) {
985
            $.extend(true, settings, name);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
986
          }
987
          else if(value !== undefined) {
988
            settings[name] = value;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
989
          }
990
          else {
991
            return settings[name];
992
          }
993
        },
994
        internal: function(name, value) {
995
          if( $.isPlainObject(name) ) {
996
            $.extend(true, module, name);
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
997
          }
998
          else if(value !== undefined) {
999
            module[name] = value;
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1000
          }
1001
          else {
1002
            return module[name];
1003
          }
1004
        },
1005
        debug: function() {
1006
          if(!settings.silent && settings.debug) {
1007
            if(settings.performance) {
1008
              module.performance.log(arguments);
1009
            }
1010
            else {
1011
              module.debug = Function.prototype.bind.call(console.info, console, settings.name + ':');
1012
              module.debug.apply(console, arguments);
1013
            }
1014
          }
1015
        },
1016
        verbose: function() {
1017
          if(!settings.silent && settings.verbose && settings.debug) {
1018
            if(settings.performance) {
1019
              module.performance.log(arguments);
1020
            }
1021
            else {
1022
              module.verbose = Function.prototype.bind.call(console.info, console, settings.name + ':');
1023
              module.verbose.apply(console, arguments);
1024
            }
1025
          }
1026
        },
1027
        error: function() {
1028
          if(!settings.silent) {
1029
            module.error = Function.prototype.bind.call(console.error, console, settings.name + ':');
1030
            module.error.apply(console, arguments);
1031
          }
1032
        },
1033
        performance: {
1034
          log: function(message) {
1035
            var
1036
              currentTime,
1037
              executionTime,
1038
              previousTime
1039
            ;
1040
            if(settings.performance) {
1041
              currentTime   = new Date().getTime();
1042
              previousTime  = time || currentTime;
1043
              executionTime = currentTime - previousTime;
1044
              time          = currentTime;
1045
              performance.push({
1046
                'Name'           : message[0],
1047
                'Arguments'      : [].slice.call(message, 1) || '',
1048
                'Element'        : element,
1049
                'Execution Time' : executionTime
1050
              });
1051
            }
1052
            clearTimeout(module.performance.timer);
1053
            module.performance.timer = setTimeout(module.performance.display, 500);
1054
          },
1055
          display: function() {
1056
            var
1057
              title = settings.name + ':',
1058
              totalTime = 0
1059
            ;
1060
            time = false;
1061
            clearTimeout(module.performance.timer);
1062
            $.each(performance, function(index, data) {
1063
              totalTime += data['Execution Time'];
1064
            });
1065
            title += ' ' + totalTime + 'ms';
1066
            if(moduleSelector) {
1067
              title += ' \'' + moduleSelector + '\'';
1068
            }
1069
            if($allModules.length > 1) {
1070
              title += ' ' + '(' + $allModules.length + ')';
1071
            }
1072
            if( (console.group !== undefined || console.table !== undefined) && performance.length > 0) {
1073
              console.groupCollapsed(title);
1074
              if(console.table) {
1075
                console.table(performance);
1076
              }
1077
              else {
1078
                $.each(performance, function(index, data) {
1079
                  console.log(data['Name'] + ': ' + data['Execution Time']+'ms');
0 ignored issues
show
Debugging Code introduced by
console.log looks like debug code. Are you sure you do not want to remove it?
Loading history...
1080
                });
1081
              }
1082
              console.groupEnd();
1083
            }
1084
            performance = [];
1085
          }
1086
        },
1087
        invoke: function(query, passedArguments, context) {
1088
          var
1089
            object = instance,
1090
            maxDepth,
1091
            found,
1092
            response
1093
          ;
1094
          passedArguments = passedArguments || queryArguments;
1095
          context         = element         || context;
1096
          if(typeof query == 'string' && object !== undefined) {
1097
            query    = query.split(/[\. ]/);
1098
            maxDepth = query.length - 1;
1099
            $.each(query, function(depth, value) {
1100
              var camelCaseValue = (depth != maxDepth)
1101
                ? value + query[depth + 1].charAt(0).toUpperCase() + query[depth + 1].slice(1)
1102
                : query
1103
              ;
1104
              if( $.isPlainObject( object[camelCaseValue] ) && (depth != maxDepth) ) {
1105
                object = object[camelCaseValue];
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1106
              }
1107
              else if( object[camelCaseValue] !== undefined ) {
1108
                found = object[camelCaseValue];
1109
                return false;
1110
              }
1111
              else if( $.isPlainObject( object[value] ) && (depth != maxDepth) ) {
1112
                object = object[value];
0 ignored issues
show
Best Practice introduced by
There is no return statement in this branch, but you do return something in other branches. Did you maybe miss it? If you do not want to return anything, consider adding return undefined; explicitly.
Loading history...
1113
              }
1114
              else if( object[value] !== undefined ) {
1115
                found = object[value];
1116
                return false;
1117
              }
1118
              else {
1119
                return false;
1120
              }
1121
            });
1122
          }
1123
          if( $.isFunction( found ) ) {
1124
            response = found.apply(context, passedArguments);
1125
          }
1126
          else if(found !== undefined) {
1127
            response = found;
1128
          }
1129
          if($.isArray(returnedValue)) {
1130
            returnedValue.push(response);
0 ignored issues
show
Bug introduced by
The variable response does not seem to be initialized in case found !== undefined on line 1126 is false. Are you sure the function push handles undefined variables?
Loading history...
1131
          }
1132
          else if(returnedValue !== undefined) {
1133
            returnedValue = [returnedValue, response];
1134
          }
1135
          else if(response !== undefined) {
1136
            returnedValue = response;
1137
          }
1138
          return found;
1139
        }
1140
      };
1141
      if(methodInvoked) {
1142
        if(instance === undefined) {
1143
          module.initialize();
1144
        }
1145
        module.invoke(query);
1146
      }
1147
      else {
1148
        if(instance !== undefined) {
1149
          instance.invoke('destroy');
1150
        }
1151
        module.initialize();
1152
      }
1153
1154
    })
1155
  ;
1156
1157
  return (returnedValue !== undefined)
1158
    ? returnedValue
1159
    : this
1160
  ;
1161
};
1162
1163
$.fn.search.settings = {
1164
1165
  name              : 'Search',
1166
  namespace         : 'search',
1167
1168
  silent            : false,
1169
  debug             : false,
1170
  verbose           : false,
1171
  performance       : true,
1172
1173
  // template to use (specified in settings.templates)
1174
  type              : 'standard',
1175
1176
  // minimum characters required to search
1177
  minCharacters     : 1,
1178
1179
  // whether to select first result after searching automatically
1180
  selectFirstResult : false,
1181
1182
  // API config
1183
  apiSettings       : false,
1184
1185
  // object to search
1186
  source            : false,
1187
1188
  // Whether search should query current term on focus
1189
  searchOnFocus     : true,
1190
1191
  // fields to search
1192
  searchFields   : [
1193
    'title',
1194
    'description'
1195
  ],
1196
1197
  // field to display in standard results template
1198
  displayField   : '',
1199
1200
  // whether to include fuzzy results in local search
1201
  searchFullText : true,
1202
1203
  // whether to add events to prompt automatically
1204
  automatic      : true,
1205
1206
  // delay before hiding menu after blur
1207
  hideDelay      : 0,
1208
1209
  // delay before searching
1210
  searchDelay    : 200,
1211
1212
  // maximum results returned from local
1213
  maxResults     : 7,
1214
1215
  // whether to store lookups in local cache
1216
  cache          : true,
1217
1218
  // whether no results errors should be shown
1219
  showNoResults  : true,
1220
1221
  // transition settings
1222
  transition     : 'scale',
1223
  duration       : 200,
1224
  easing         : 'easeOutExpo',
1225
1226
  // callbacks
1227
  onSelect       : false,
1228
  onResultsAdd   : false,
1229
1230
  onSearchQuery  : function(query){},
0 ignored issues
show
Unused Code introduced by
The parameter query is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
1231
  onResults      : function(response){},
0 ignored issues
show
Unused Code introduced by
The parameter response is not used and could be removed.

This check looks for parameters in functions that are not used in the function body and are not followed by other parameters which are used inside the function.

Loading history...
1232
1233
  onResultsOpen  : function(){},
1234
  onResultsClose : function(){},
1235
1236
  className: {
1237
    animating : 'animating',
1238
    active    : 'active',
1239
    empty     : 'empty',
1240
    focus     : 'focus',
1241
    hidden    : 'hidden',
1242
    loading   : 'loading',
1243
    results   : 'results',
1244
    pressed   : 'down'
1245
  },
1246
1247
  error : {
1248
    source      : 'Cannot search. No source used, and Semantic API module was not included',
1249
    noResults   : 'Your search returned no results',
1250
    logging     : 'Error in debug logging, exiting.',
1251
    noEndpoint  : 'No search endpoint was specified',
1252
    noTemplate  : 'A valid template name was not specified.',
1253
    serverError : 'There was an issue querying the server.',
1254
    maxResults  : 'Results must be an array to use maxResults setting',
1255
    method      : 'The method you called is not defined.'
1256
  },
1257
1258
  metadata: {
1259
    cache   : 'cache',
1260
    results : 'results',
1261
    result  : 'result'
1262
  },
1263
1264
  regExp: {
1265
    escape     : /[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g,
1266
    beginsWith : '(?:\s|^)'
1267
  },
1268
1269
  // maps api response attributes to internal representation
1270
  fields: {
1271
    categories      : 'results',     // array of categories (category view)
1272
    categoryName    : 'name',        // name of category (category view)
1273
    categoryResults : 'results',     // array of results (category view)
1274
    description     : 'description', // result description
1275
    image           : 'image',       // result image
1276
    price           : 'price',       // result price
1277
    results         : 'results',     // array of results (standard)
1278
    title           : 'title',       // result title
1279
    url             : 'url',         // result url
1280
    action          : 'action',      // "view more" object name
1281
    actionText      : 'text',        // "view more" text
1282
    actionURL       : 'url'          // "view more" url
1283
  },
1284
1285
  selector : {
1286
    prompt       : '.prompt',
1287
    searchButton : '.search.button',
1288
    results      : '.results',
1289
    message      : '.results > .message',
1290
    category     : '.category',
1291
    result       : '.result',
1292
    title        : '.title, .name'
1293
  },
1294
1295
  templates: {
1296
    escape: function(string) {
1297
      var
1298
        badChars     = /[&<>"'`]/g,
1299
        shouldEscape = /[&<>"'`]/,
1300
        escape       = {
1301
          "&": "&amp;",
1302
          "<": "&lt;",
1303
          ">": "&gt;",
1304
          '"': "&quot;",
1305
          "'": "&#x27;",
1306
          "`": "&#x60;"
1307
        },
1308
        escapedChar  = function(chr) {
1309
          return escape[chr];
1310
        }
1311
      ;
1312
      if(shouldEscape.test(string)) {
1313
        return string.replace(badChars, escapedChar);
1314
      }
1315
      return string;
1316
    },
1317
    message: function(message, type) {
1318
      var
1319
        html = ''
1320
      ;
1321
      if(message !== undefined && type !== undefined) {
1322
        html +=  ''
1323
          + '<div class="message ' + type + '">'
1324
        ;
1325
        // message type
1326
        if(type == 'empty') {
1327
          html += ''
1328
            + '<div class="header">No Results</div class="header">'
1329
            + '<div class="description">' + message + '</div class="description">'
1330
          ;
1331
        }
1332
        else {
1333
          html += ' <div class="description">' + message + '</div>';
1334
        }
1335
        html += '</div>';
1336
      }
1337
      return html;
1338
    },
1339
    category: function(response, fields) {
1340
      var
1341
        html = '',
1342
        escape = $.fn.search.settings.templates.escape
0 ignored issues
show
Unused Code introduced by
The assignment to variable escape seems to be never used. Consider removing it.
Loading history...
1343
      ;
1344
      if(response[fields.categoryResults] !== undefined) {
1345
1346
        // each category
1347
        $.each(response[fields.categoryResults], function(index, category) {
1348
          if(category[fields.results] !== undefined && category.results.length > 0) {
1349
1350
            html  += '<div class="category">';
1351
1352
            if(category[fields.categoryName] !== undefined) {
1353
              html += '<div class="name">' + category[fields.categoryName] + '</div>';
1354
            }
1355
1356
            // each item inside category
1357
            $.each(category.results, function(index, result) {
1358
              if(result[fields.url]) {
1359
                html  += '<a class="result" href="' + result[fields.url] + '">';
1360
              }
1361
              else {
1362
                html  += '<a class="result">';
1363
              }
1364
              if(result[fields.image] !== undefined) {
1365
                html += ''
1366
                  + '<div class="image">'
1367
                  + ' <img src="' + result[fields.image] + '">'
1368
                  + '</div>'
1369
                ;
1370
              }
1371
              html += '<div class="content">';
1372
              if(result[fields.price] !== undefined) {
1373
                html += '<div class="price">' + result[fields.price] + '</div>';
1374
              }
1375
              if(result[fields.title] !== undefined) {
1376
                html += '<div class="title">' + result[fields.title] + '</div>';
1377
              }
1378
              if(result[fields.description] !== undefined) {
1379
                html += '<div class="description">' + result[fields.description] + '</div>';
1380
              }
1381
              html  += ''
1382
                + '</div>'
1383
              ;
1384
              html += '</a>';
1385
            });
1386
            html  += ''
1387
              + '</div>'
1388
            ;
1389
          }
1390
        });
1391
        if(response[fields.action]) {
1392
          html += ''
1393
          + '<a href="' + response[fields.action][fields.actionURL] + '" class="action">'
1394
          +   response[fields.action][fields.actionText]
1395
          + '</a>';
1396
        }
1397
        return html;
1398
      }
1399
      return false;
1400
    },
1401
    standard: function(response, fields) {
1402
      var
1403
        html = ''
1404
      ;
1405
      if(response[fields.results] !== undefined) {
1406
1407
        // each result
1408
        $.each(response[fields.results], function(index, result) {
1409
          if(result[fields.url]) {
1410
            html  += '<a class="result" href="' + result[fields.url] + '">';
1411
          }
1412
          else {
1413
            html  += '<a class="result">';
1414
          }
1415
          if(result[fields.image] !== undefined) {
1416
            html += ''
1417
              + '<div class="image">'
1418
              + ' <img src="' + result[fields.image] + '">'
1419
              + '</div>'
1420
            ;
1421
          }
1422
          html += '<div class="content">';
1423
          if(result[fields.price] !== undefined) {
1424
            html += '<div class="price">' + result[fields.price] + '</div>';
1425
          }
1426
          if(result[fields.title] !== undefined) {
1427
            html += '<div class="title">' + result[fields.title] + '</div>';
1428
          }
1429
          if(result[fields.description] !== undefined) {
1430
            html += '<div class="description">' + result[fields.description] + '</div>';
1431
          }
1432
          html  += ''
1433
            + '</div>'
1434
          ;
1435
          html += '</a>';
1436
        });
1437
1438
        if(response[fields.action]) {
1439
          html += ''
1440
          + '<a href="' + response[fields.action][fields.actionURL] + '" class="action">'
1441
          +   response[fields.action][fields.actionText]
1442
          + '</a>';
1443
        }
1444
        return html;
1445
      }
1446
      return false;
1447
    }
1448
  }
1449
};
1450
1451
})( jQuery, window, document );
1452