fustrate-rails 0.4.1.1 → 0.10.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. checksums.yaml +5 -5
  2. data/config/initializers/jbuilder.rb +13 -0
  3. data/config/initializers/renderers.rb +40 -0
  4. data/config/initializers/sanitize.rb +112 -0
  5. data/config/rubocop/default.yml +96 -0
  6. data/config/rubocop/rails.yml +22 -0
  7. data/lib/fustrate/rails/concerns/clean_attributes.rb +49 -0
  8. data/lib/fustrate/rails/concerns/model.rb +48 -0
  9. data/lib/fustrate/rails/concerns/sanitize_html.rb +34 -0
  10. data/lib/fustrate/rails/engine.rb +14 -7
  11. data/lib/fustrate/rails/services/base.rb +46 -0
  12. data/lib/fustrate/rails/services/generate_csv.rb +35 -0
  13. data/lib/fustrate/rails/services/generate_excel.rb +32 -0
  14. data/lib/fustrate/rails/services/log_edit.rb +132 -0
  15. data/lib/fustrate/rails/spec_helper.rb +62 -0
  16. data/lib/fustrate/rails/version.rb +5 -1
  17. data/lib/fustrate-rails.rb +5 -2
  18. metadata +44 -140
  19. data/vendor/assets/javascripts/awesomplete.js +0 -402
  20. data/vendor/assets/javascripts/fustrate/_module.coffee +0 -140
  21. data/vendor/assets/javascripts/fustrate/components/_module.coffee +0 -3
  22. data/vendor/assets/javascripts/fustrate/components/alert_box.coffee +0 -10
  23. data/vendor/assets/javascripts/fustrate/components/autocomplete.coffee +0 -161
  24. data/vendor/assets/javascripts/fustrate/components/disclosure.coffee +0 -12
  25. data/vendor/assets/javascripts/fustrate/components/drop_zone.coffee +0 -9
  26. data/vendor/assets/javascripts/fustrate/components/dropdown.coffee +0 -48
  27. data/vendor/assets/javascripts/fustrate/components/file_picker.coffee +0 -11
  28. data/vendor/assets/javascripts/fustrate/components/flash.coffee +0 -31
  29. data/vendor/assets/javascripts/fustrate/components/modal.coffee +0 -273
  30. data/vendor/assets/javascripts/fustrate/components/pagination.coffee +0 -84
  31. data/vendor/assets/javascripts/fustrate/components/tabs.coffee +0 -28
  32. data/vendor/assets/javascripts/fustrate/components/tooltip.coffee +0 -72
  33. data/vendor/assets/javascripts/fustrate/generic_form.coffee +0 -30
  34. data/vendor/assets/javascripts/fustrate/generic_page.coffee +0 -40
  35. data/vendor/assets/javascripts/fustrate/generic_table.coffee +0 -57
  36. data/vendor/assets/javascripts/fustrate/listenable.coffee +0 -25
  37. data/vendor/assets/javascripts/fustrate/object.coffee +0 -21
  38. data/vendor/assets/javascripts/fustrate/record.coffee +0 -23
  39. data/vendor/assets/javascripts/fustrate.coffee +0 -6
  40. data/vendor/assets/stylesheets/_fustrate.sass +0 -7
  41. data/vendor/assets/stylesheets/awesomplete.sass +0 -76
  42. data/vendor/assets/stylesheets/fustrate/_colors.sass +0 -12
  43. data/vendor/assets/stylesheets/fustrate/_settings.sass +0 -19
  44. data/vendor/assets/stylesheets/fustrate/components/_components.sass +0 -36
  45. data/vendor/assets/stylesheets/fustrate/components/_functions.sass +0 -41
  46. data/vendor/assets/stylesheets/fustrate/components/alerts.sass +0 -86
  47. data/vendor/assets/stylesheets/fustrate/components/buttons.sass +0 -99
  48. data/vendor/assets/stylesheets/fustrate/components/disclosures.sass +0 -23
  49. data/vendor/assets/stylesheets/fustrate/components/dropdowns.sass +0 -36
  50. data/vendor/assets/stylesheets/fustrate/components/flash.sass +0 -38
  51. data/vendor/assets/stylesheets/fustrate/components/forms.sass +0 -195
  52. data/vendor/assets/stylesheets/fustrate/components/grid.sass +0 -196
  53. data/vendor/assets/stylesheets/fustrate/components/labels.sass +0 -64
  54. data/vendor/assets/stylesheets/fustrate/components/modals.sass +0 -167
  55. data/vendor/assets/stylesheets/fustrate/components/pagination.sass +0 -69
  56. data/vendor/assets/stylesheets/fustrate/components/panels.sass +0 -67
  57. data/vendor/assets/stylesheets/fustrate/components/popovers.sass +0 -19
  58. data/vendor/assets/stylesheets/fustrate/components/tables.sass +0 -62
  59. data/vendor/assets/stylesheets/fustrate/components/tabs.sass +0 -44
  60. data/vendor/assets/stylesheets/fustrate/components/tooltips.sass +0 -28
  61. data/vendor/assets/stylesheets/fustrate/components/typography.sass +0 -391
@@ -1,402 +0,0 @@
1
- /* 1795543d988d0fd9ca6237a5ac176f8e88d63990 */
2
-
3
- /**
4
- * Simple, lightweight, usable local autocomplete library for modern browsers
5
- * Because there weren’t enough autocomplete scripts in the world? Because I’m completely insane and have NIH syndrome? Probably both. :P
6
- * @author Lea Verou http://leaverou.github.io/awesomplete
7
- * MIT license
8
- */
9
-
10
- (function () {
11
-
12
- var _ = function (input, o) {
13
- var me = this;
14
-
15
- // Setup
16
-
17
- this.input = $(input);
18
- this.input.setAttribute("autocomplete", "off");
19
- this.input.setAttribute("aria-autocomplete", "list");
20
-
21
- o = o || {};
22
-
23
- configure(this, {
24
- minChars: 2,
25
- maxItems: 10,
26
- autoFirst: false,
27
- filter: _.FILTER_CONTAINS,
28
- sort: _.SORT_BYLENGTH,
29
- item: _.ITEM,
30
- replace: _.REPLACE
31
- }, o);
32
-
33
- this.index = -1;
34
-
35
- // Create necessary elements
36
-
37
- this.container = $.create("div", {
38
- className: "awesomplete",
39
- around: input
40
- });
41
-
42
- this.ul = $.create("ul", {
43
- hidden: "hidden",
44
- inside: this.container
45
- });
46
-
47
- this.status = $.create("span", {
48
- className: "visually-hidden",
49
- role: "status",
50
- "aria-live": "assertive",
51
- "aria-relevant": "additions",
52
- inside: this.container
53
- });
54
-
55
- // Bind events
56
-
57
- $.bind(this.input, {
58
- "input": this.evaluate.bind(this),
59
- "blur": this.close.bind(this),
60
- "keydown": function(evt) {
61
- var c = evt.keyCode;
62
-
63
- // If the dropdown `ul` is in view, then act on keydown for the following keys:
64
- // Enter / Esc / Up / Down
65
- if(me.opened) {
66
- if (c === 13 && me.selected) { // Enter
67
- evt.preventDefault();
68
- me.select();
69
- }
70
- else if (c === 27) { // Esc
71
- me.close();
72
- }
73
- else if (c === 38 || c === 40) { // Down/Up arrow
74
- evt.preventDefault();
75
- me[c === 38? "previous" : "next"]();
76
- }
77
- }
78
- }
79
- });
80
-
81
- $.bind(this.input.form, {"submit": this.close.bind(this)});
82
-
83
- $.bind(this.ul, {"mousedown": function(evt) {
84
- var li = evt.target;
85
-
86
- if (li !== this) {
87
-
88
- while (li && !/li/i.test(li.nodeName)) {
89
- li = li.parentNode;
90
- }
91
-
92
- if (li && evt.button === 0) { // Only select on left click
93
- evt.preventDefault();
94
- me.select(li, evt.target);
95
- }
96
- }
97
- }});
98
-
99
- if (this.input.hasAttribute("list")) {
100
- this.list = "#" + this.input.getAttribute("list");
101
- this.input.removeAttribute("list");
102
- }
103
- else {
104
- this.list = this.input.getAttribute("data-list") || o.list || [];
105
- }
106
-
107
- _.all.push(this);
108
- };
109
-
110
- _.prototype = {
111
- set list(list) {
112
- if (Array.isArray(list)) {
113
- this._list = list;
114
- }
115
- else if (typeof list === "string" && list.indexOf(",") > -1) {
116
- this._list = list.split(/\s*,\s*/);
117
- }
118
- else { // Element or CSS selector
119
- list = $(list);
120
-
121
- if (list && list.children) {
122
- this._list = slice.apply(list.children).map(function (el) {
123
- return el.textContent.trim();
124
- });
125
- }
126
- }
127
-
128
- if (document.activeElement === this.input) {
129
- this.evaluate();
130
- }
131
- },
132
-
133
- get selected() {
134
- return this.index > -1;
135
- },
136
-
137
- get opened() {
138
- return !this.ul.hasAttribute("hidden");
139
- },
140
-
141
- close: function () {
142
- this.ul.setAttribute("hidden", "");
143
- this.index = -1;
144
-
145
- $.fire(this.input, "awesomplete-close");
146
- },
147
-
148
- open: function () {
149
- this.ul.removeAttribute("hidden");
150
-
151
- if (this.autoFirst && this.index === -1) {
152
- this.goto(0);
153
- }
154
-
155
- $.fire(this.input, "awesomplete-open");
156
- },
157
-
158
- next: function () {
159
- var count = this.ul.children.length;
160
-
161
- this.goto(this.index < count - 1? this.index + 1 : -1);
162
- },
163
-
164
- previous: function () {
165
- var count = this.ul.children.length;
166
-
167
- this.goto(this.selected? this.index - 1 : count - 1);
168
- },
169
-
170
- // Should not be used, highlights specific item without any checks!
171
- goto: function (i) {
172
- var lis = this.ul.children;
173
-
174
- if (this.selected) {
175
- lis[this.index].setAttribute("aria-selected", "false");
176
- }
177
-
178
- this.index = i;
179
-
180
- if (i > -1 && lis.length > 0) {
181
- lis[i].setAttribute("aria-selected", "true");
182
- this.status.textContent = lis[i].textContent;
183
- }
184
-
185
- $.fire(this.input, "awesomplete-highlight");
186
- },
187
-
188
- select: function (selected, origin) {
189
- selected = selected || this.ul.children[this.index];
190
-
191
- if (selected) {
192
- var allowed = $.fire(this.input, "awesomplete-select", {
193
- text: selected.textContent,
194
- data: this.suggestions[$.siblingIndex(selected)],
195
- origin: origin || selected
196
- });
197
-
198
- if (allowed) {
199
- this.replace(selected.textContent);
200
- this.close();
201
- $.fire(this.input, "awesomplete-selectcomplete");
202
- }
203
- }
204
- },
205
-
206
- evaluate: function() {
207
- var me = this;
208
- var value = this.input.value;
209
-
210
- if (value.length >= this.minChars && this._list.length > 0) {
211
- this.index = -1;
212
- // Populate list with options that match
213
- this.ul.innerHTML = "";
214
-
215
- this.suggestions = this._list
216
- .filter(function(item) {
217
- return me.filter(item, value);
218
- })
219
- .sort(this.sort)
220
- .slice(0, this.maxItems);
221
-
222
- this.suggestions.forEach(function(text) {
223
- me.ul.appendChild(me.item(text, value));
224
- });
225
-
226
- if (this.ul.children.length === 0) {
227
- this.close();
228
- } else {
229
- this.open();
230
- }
231
- }
232
- else {
233
- this.close();
234
- }
235
- }
236
- };
237
-
238
- // Static methods/properties
239
-
240
- _.all = [];
241
-
242
- _.FILTER_CONTAINS = function (text, input) {
243
- return RegExp($.regExpEscape(input.trim()), "i").test(text);
244
- };
245
-
246
- _.FILTER_STARTSWITH = function (text, input) {
247
- return RegExp("^" + $.regExpEscape(input.trim()), "i").test(text);
248
- };
249
-
250
- _.SORT_BYLENGTH = function (a, b) {
251
- if (a.length !== b.length) {
252
- return a.length - b.length;
253
- }
254
-
255
- return a < b? -1 : 1;
256
- };
257
-
258
- _.ITEM = function (text, input) {
259
- var html = input === '' ? text : text.replace(RegExp($.regExpEscape(input.trim()), "gi"), "<mark>$&</mark>");
260
- return $.create("li", {
261
- innerHTML: html,
262
- "aria-selected": "false"
263
- });
264
- };
265
-
266
- _.REPLACE = function (text) {
267
- this.input.value = text;
268
- };
269
-
270
- // Private functions
271
-
272
- function configure(instance, properties, o) {
273
- for (var i in properties) {
274
- var initial = properties[i],
275
- attrValue = instance.input.getAttribute("data-" + i.toLowerCase());
276
-
277
- if (typeof initial === "number") {
278
- instance[i] = parseInt(attrValue);
279
- }
280
- else if (initial === false) { // Boolean options must be false by default anyway
281
- instance[i] = attrValue !== null;
282
- }
283
- else if (initial instanceof Function) {
284
- instance[i] = null;
285
- }
286
- else {
287
- instance[i] = attrValue;
288
- }
289
-
290
- if (!instance[i] && instance[i] !== 0) {
291
- instance[i] = (i in o)? o[i] : initial;
292
- }
293
- }
294
- }
295
-
296
- // Helpers
297
-
298
- var slice = Array.prototype.slice;
299
-
300
- function $(expr, con) {
301
- return typeof expr === "string"? (con || document).querySelector(expr) : expr || null;
302
- }
303
-
304
- function $$(expr, con) {
305
- return slice.call((con || document).querySelectorAll(expr));
306
- }
307
-
308
- $.create = function(tag, o) {
309
- var element = document.createElement(tag);
310
-
311
- for (var i in o) {
312
- var val = o[i];
313
-
314
- if (i === "inside") {
315
- $(val).appendChild(element);
316
- }
317
- else if (i === "around") {
318
- var ref = $(val);
319
- ref.parentNode.insertBefore(element, ref);
320
- element.appendChild(ref);
321
- }
322
- else if (i in element) {
323
- element[i] = val;
324
- }
325
- else {
326
- element.setAttribute(i, val);
327
- }
328
- }
329
-
330
- return element;
331
- };
332
-
333
- $.bind = function(element, o) {
334
- if (element) {
335
- for (var event in o) {
336
- var callback = o[event];
337
-
338
- event.split(/\s+/).forEach(function (event) {
339
- element.addEventListener(event, callback);
340
- });
341
- }
342
- }
343
- };
344
-
345
- $.fire = function(target, type, properties) {
346
- var evt = document.createEvent("HTMLEvents");
347
-
348
- evt.initEvent(type, true, true );
349
-
350
- for (var j in properties) {
351
- evt[j] = properties[j];
352
- }
353
-
354
- return target.dispatchEvent(evt);
355
- };
356
-
357
- $.regExpEscape = function (s) {
358
- return s.replace(/[-\\^$*+?.()|[\]{}]/g, "\\$&");
359
- };
360
-
361
- $.siblingIndex = function (el) {
362
- /* eslint-disable no-cond-assign */
363
- for (var i = 0; el = el.previousElementSibling; i++);
364
- return i;
365
- };
366
-
367
- // Initialization
368
-
369
- function init() {
370
- $$("input.awesomplete").forEach(function (input) {
371
- new _(input);
372
- });
373
- }
374
-
375
- // Are we in a browser? Check for Document constructor
376
- if (typeof Document !== "undefined") {
377
- // DOM already loaded?
378
- if (document.readyState !== "loading") {
379
- init();
380
- }
381
- else {
382
- // Wait for it
383
- document.addEventListener("DOMContentLoaded", init);
384
- }
385
- }
386
-
387
- _.$ = $;
388
- _.$$ = $$;
389
-
390
- // Make sure to export Awesomplete on self when in a browser
391
- if (typeof self !== "undefined") {
392
- self.Awesomplete = _;
393
- }
394
-
395
- // Expose Awesomplete as a CJS module
396
- if (typeof module === "object" && module.exports) {
397
- module.exports = _;
398
- }
399
-
400
- return _;
401
-
402
- }());
@@ -1,140 +0,0 @@
1
- #= require_self
2
- #= require_directory .
3
- #= require_tree .
4
-
5
- class window.Fustrate
6
- @VERSION: '0.4.1.1'
7
- @libs: {}
8
-
9
- constructor: ->
10
- lib.init() for own name, lib of @constructor.libs
11
-
12
- for own component of Fustrate.Components
13
- Fustrate.Components[component].initialize()
14
-
15
- @initialize()
16
-
17
- initialize: ->
18
- # Loop through every element on the page with a data-js-class attribute
19
- # and convert the data attribute's value to a real object. Then instantiate
20
- # a new object of that class.
21
- $('[data-js-class]').each (index, elem) ->
22
- element = $(elem)
23
- klass = Fustrate._stringToClass element.data('js-class')
24
-
25
- element.data 'js-object', new klass(element)
26
-
27
- $.ajaxSetup
28
- cache: false
29
- beforeSend: $.rails.CSRFProtection
30
-
31
- $('table').wrap '<div class="responsive-table"></div>'
32
-
33
- $('.number').each (index, elem) ->
34
- elem = $ @
35
-
36
- number = if elem.data('number') isnt undefined
37
- elem.data('number')
38
- else
39
- elem.html()
40
-
41
- elem.addClass 'negative' if parseInt(number, 10) < 0
42
-
43
- # Take a string like 'Asgard.Whiteboard.Entry' and retrieve the real class
44
- # with that name. Start at `window` and work down from there.
45
- @_stringToClass: (string) ->
46
- pieces = string.split('.')
47
-
48
- Fustrate._arrayToClass(pieces, window)
49
-
50
- @_arrayToClass: (pieces, root) ->
51
- return root[pieces[0]] if pieces.length is 1
52
-
53
- Fustrate._arrayToClass pieces.slice(1), root[pieces[0]]
54
-
55
- # Very similar to the Rails helper `link_to`. Returns an HTML string.
56
- @linkTo: (text, path, options = {}) ->
57
- $('<a>').prop('href', path).html(text).prop(options).outerHTML()
58
-
59
- @ajaxUpload: (url, data) ->
60
- formData = new FormData
61
-
62
- formData.append key, value for key, value of data
63
-
64
- $.ajax
65
- url: url
66
- type: 'POST'
67
- data: formData
68
- processData: false
69
- contentType: false
70
- dataType: 'script'
71
- beforeSend: (xhr) ->
72
- $.rails.CSRFProtection xhr
73
-
74
- @humanDate: (date, time = false) ->
75
- if date.year() is moment().year()
76
- date.format("M/D#{if time then ' h:mm A' else ''}")
77
- else
78
- date.format("M/D/YY#{if time then ' h:mm A' else ''}")
79
-
80
- @label: (text, type) ->
81
- type = if type then "#{type} " else ''
82
-
83
- $('<span>')
84
- .text(text)
85
- .prop('class', "label #{type}#{text}".toLowerCase())
86
-
87
- @icon: (types) ->
88
- classes = ("fa-#{type}" for type in types.split(' ')).join(' ')
89
-
90
- "<i class=\"fa #{classes}\"></i>"
91
-
92
- # Replicate a few common prototype methods on String and Array
93
- String::titleize = ->
94
- @replace(/_/g, ' ').replace /\b[a-z]/g, (char) -> char.toUpperCase()
95
-
96
- String::capitalize = ->
97
- @charAt(0).toUpperCase() + @slice(1)
98
-
99
- String::phoneFormat = ->
100
- if /^(\d+)(ext|x)(\d+)$/.test @
101
- @replace /(\d+)(ext|x)(\d+)/, ->
102
- arguments[1].phoneFormat() + ' x' + arguments[3]
103
- else if /^1\d{10}$/.test @
104
- @replace /1(\d{3})(\d{3})(\d{4})/, '1 ($1) $2-$3'
105
- else if /^\d{10}$/.test @
106
- @replace /(\d{3})(\d{3})(\d{4})/, '($1) $2-$3'
107
- else if /^\d{7}$/.test @
108
- @replace /(\d{3})(\d{4})/, '$1-$2'
109
- else
110
- @
111
-
112
- Array::toSentence = ->
113
- switch @length
114
- when 0 then ''
115
- when 1 then @[0]
116
- when 2 then "#{@[0]} and #{@[1]}"
117
- else "#{@slice(0, -1).join(', ')}, and #{@[@length - 1]}"
118
-
119
- Array::remove = (object) ->
120
- index = @indexOf object
121
- @splice index, 1 if index isnt -1
122
-
123
- Array::first = ->
124
- @[0]
125
-
126
- Array::last = ->
127
- @[@length - 1]
128
-
129
- Array::peek = Array::last
130
-
131
- # Used to define getters and setters
132
- Function::define = (name, methods) ->
133
- Object.defineProperty @::, name, methods
134
-
135
- jQuery.fn.outerHTML = ->
136
- return '' unless @length
137
-
138
- return @[0].outerHTML if @[0].outerHTML
139
-
140
- $('<div>').append(@[0].clone()).remove().html()
@@ -1,3 +0,0 @@
1
- class Fustrate.Components
2
- class @Base extends Fustrate.Listenable
3
- @initialize: ->
@@ -1,10 +0,0 @@
1
- class Fustrate.Components.AlertBox extends Fustrate.Components.Base
2
- @fadeSpeed: 300
3
-
4
- @initialize: ->
5
- $('.alert-box').on 'click', '.close', (e) ->
6
- alert_box = $(e.currentTarget).closest('.alert-box')
7
-
8
- alert_box.fadeOut @constructor.fadeSpeed, alert_box.remove
9
-
10
- false
@@ -1,161 +0,0 @@
1
- class Fustrate.Components.Autocomplete extends Fustrate.Components.Base
2
- @types:
3
- plain:
4
- displayKey: 'value'
5
- item: (object, userInput) -> "<li>#{@highlight object.value}</li>"
6
- filter: (object, userInput) -> object.value.indexOf(userInput) >= 0
7
-
8
- @initialize: ->
9
- # Override the default sort
10
- Awesomplete.SORT_BYLENGTH = ->
11
-
12
- constructor: (@input, types) ->
13
- if $.isArray types
14
- types = {
15
- plain:
16
- list: types.map (value) -> { value: value }
17
- }
18
-
19
- @sources = for own type, options of types
20
- $.extend({}, @constructor.types[type], options)
21
-
22
- @sources = [@sources] if $.isPlainObject @sources
23
-
24
- existing = @input.data('awesomplete')
25
-
26
- if existing
27
- existing.sources = @sources
28
- return
29
-
30
- @awesomplete = new Awesomplete(
31
- @input[0]
32
- minChars: 0
33
- maxItems: 25
34
- filter: -> true
35
- item: (option, userInput) -> option # Items are pre-rendered
36
- )
37
-
38
- @input
39
- .data 'awesomplete', @
40
- .on 'awesomplete-highlight', @onHighlight
41
- .on 'awesomplete-select', @onSelect
42
- .on 'keyup', @constructor.debounce(@onKeyup)
43
- .on 'focus', @onFocus
44
-
45
- blanked: =>
46
- return unless @input.val().trim() is ''
47
-
48
- @awesomplete.close()
49
-
50
- $("~ input:hidden##{@input.attr('id')}_id", @awesomplete.container)
51
- .val null
52
- $("~ input:hidden##{@input.attr('id')}_type", @awesomplete.container)
53
- .val null
54
-
55
- @input.trigger 'blanked.autocomplete'
56
-
57
- onFocus: =>
58
- @items = []
59
- @value = @input.val().trim() ? ''
60
-
61
- for source in @sources when source.list?.length
62
- for datum in source.list when source.filter(datum, @value)
63
- @items.push @createListItem(datum, source)
64
-
65
- @awesomplete.list = @items
66
-
67
- onHighlight: =>
68
- item = $('+ ul li[aria-selected="true"]', @input)
69
-
70
- return unless item[0]
71
-
72
- item[0].scrollIntoView false
73
- @replace item.data('datum')._displayValue
74
-
75
- onSelect: (e) =>
76
- # aria-selected isn't set on click
77
- item = $(e.originalEvent.origin).closest('li')
78
- datum = item.data('datum')
79
-
80
- @replace datum._displayValue
81
- @awesomplete.close()
82
-
83
- $("~ input:hidden##{@input.attr('id')}_id", @awesomplete.container)
84
- .val datum.id
85
- $("~ input:hidden##{@input.attr('id')}_type", @awesomplete.container)
86
- .val datum._type
87
-
88
- @input.data(datum: datum).trigger('finished.autocomplete')
89
-
90
- false
91
-
92
- onKeyup: (e) =>
93
- keyCode = e.which or e.keyCode
94
-
95
- value = @input.val().trim()
96
-
97
- return @blanked() if value is ''
98
-
99
- # Ignore: Tab, Enter, Esc, Left, Up, Right, Down
100
- return if keyCode in [9, 13, 27, 37, 38, 39, 40]
101
-
102
- # Don't perform the same search twice in a row
103
- return unless value isnt @value and value.length >= 2
104
-
105
- @value = value
106
- @items = []
107
-
108
- for source in @sources
109
- if source.url?
110
- @performSearch(source)
111
- else if source.list?
112
- for datum in source.list when source.filter(datum, @value)
113
- @items.push @createListItem(datum, source)
114
-
115
- @awesomplete.list = @items
116
-
117
- performSearch: (source) =>
118
- $.get source.url(search: @value, commit: 1, format: 'json')
119
- .done (response) =>
120
- for datum in response
121
- @items.push @createListItem(datum, source)
122
-
123
- @awesomplete.list = @items
124
-
125
- createListItem: (datum, source) ->
126
- datum._displayValue = datum[source.displayKey]
127
- datum._type = source.type
128
-
129
- $ source.item.call(@, datum, @value)
130
- .data datum: datum
131
- .get(0)
132
-
133
- highlight: (text) =>
134
- return '' unless text
135
-
136
- text.replace RegExp("(#{@value.split(/\s+/).join('|')})", 'gi'),
137
- '<mark>$&</mark>'
138
-
139
- replace: (text) =>
140
- @awesomplete.replace(text)
141
-
142
- @debounce: (func, milliseconds = 300, immediate = false) ->
143
- timeout = null
144
-
145
- (args...) ->
146
- delayed = =>
147
- func.apply(@, args) unless immediate
148
- timeout = null
149
-
150
- if timeout
151
- clearTimeout(timeout)
152
- else if immediate
153
- func.apply(@, args)
154
-
155
- timeout = setTimeout delayed, milliseconds
156
-
157
- @addType: (name, func) =>
158
- @types[name] = func
159
-
160
- @addTypes: (types) ->
161
- @addType(name, func) for own name, func of types