| 14217 |
anikendra |
1 |
/*
|
|
|
2 |
* typeahead.js
|
|
|
3 |
* https://github.com/twitter/typeahead.js
|
|
|
4 |
* Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT
|
|
|
5 |
*/
|
|
|
6 |
|
|
|
7 |
var Dataset = (function() {
|
|
|
8 |
'use strict';
|
|
|
9 |
|
|
|
10 |
var datasetKey = 'ttDataset', valueKey = 'ttValue', datumKey = 'ttDatum';
|
|
|
11 |
|
|
|
12 |
// constructor
|
|
|
13 |
// -----------
|
|
|
14 |
|
|
|
15 |
function Dataset(o) {
|
|
|
16 |
o = o || {};
|
|
|
17 |
o.templates = o.templates || {};
|
|
|
18 |
|
|
|
19 |
if (!o.source) {
|
|
|
20 |
$.error('missing source');
|
|
|
21 |
}
|
|
|
22 |
|
|
|
23 |
if (o.name && !isValidName(o.name)) {
|
|
|
24 |
$.error('invalid dataset name: ' + o.name);
|
|
|
25 |
}
|
|
|
26 |
|
|
|
27 |
// tracks the last query the dataset was updated for
|
|
|
28 |
this.query = null;
|
|
|
29 |
|
|
|
30 |
this.highlight = !!o.highlight;
|
|
|
31 |
this.name = o.name || _.getUniqueId();
|
|
|
32 |
|
|
|
33 |
this.source = o.source;
|
|
|
34 |
this.displayFn = getDisplayFn(o.display || o.displayKey);
|
|
|
35 |
|
|
|
36 |
this.templates = getTemplates(o.templates, this.displayFn);
|
|
|
37 |
|
|
|
38 |
this.$el = $(html.dataset.replace('%CLASS%', this.name));
|
|
|
39 |
}
|
|
|
40 |
|
|
|
41 |
// static methods
|
|
|
42 |
// --------------
|
|
|
43 |
|
|
|
44 |
Dataset.extractDatasetName = function extractDatasetName(el) {
|
|
|
45 |
return $(el).data(datasetKey);
|
|
|
46 |
};
|
|
|
47 |
|
|
|
48 |
Dataset.extractValue = function extractDatum(el) {
|
|
|
49 |
return $(el).data(valueKey);
|
|
|
50 |
};
|
|
|
51 |
|
|
|
52 |
Dataset.extractDatum = function extractDatum(el) {
|
|
|
53 |
return $(el).data(datumKey);
|
|
|
54 |
};
|
|
|
55 |
|
|
|
56 |
// instance methods
|
|
|
57 |
// ----------------
|
|
|
58 |
|
|
|
59 |
_.mixin(Dataset.prototype, EventEmitter, {
|
|
|
60 |
|
|
|
61 |
// ### private
|
|
|
62 |
|
|
|
63 |
_render: function render(query, suggestions) {
|
|
|
64 |
if (!this.$el) { return; }
|
|
|
65 |
|
|
|
66 |
var that = this, hasSuggestions;
|
|
|
67 |
|
|
|
68 |
this.$el.empty();
|
|
|
69 |
hasSuggestions = suggestions && suggestions.length;
|
|
|
70 |
|
|
|
71 |
if (!hasSuggestions && this.templates.empty) {
|
|
|
72 |
this.$el
|
|
|
73 |
.html(getEmptyHtml())
|
|
|
74 |
.prepend(that.templates.header ? getHeaderHtml() : null)
|
|
|
75 |
.append(that.templates.footer ? getFooterHtml() : null);
|
|
|
76 |
}
|
|
|
77 |
|
|
|
78 |
else if (hasSuggestions) {
|
|
|
79 |
this.$el
|
|
|
80 |
.html(getSuggestionsHtml())
|
|
|
81 |
.prepend(that.templates.header ? getHeaderHtml() : null)
|
|
|
82 |
.append(that.templates.footer ? getFooterHtml() : null);
|
|
|
83 |
}
|
|
|
84 |
|
|
|
85 |
this.trigger('rendered');
|
|
|
86 |
|
|
|
87 |
function getEmptyHtml() {
|
|
|
88 |
return that.templates.empty({ query: query, isEmpty: true });
|
|
|
89 |
}
|
|
|
90 |
|
|
|
91 |
function getSuggestionsHtml() {
|
|
|
92 |
var $suggestions, nodes;
|
|
|
93 |
|
|
|
94 |
$suggestions = $(html.suggestions).css(css.suggestions);
|
|
|
95 |
|
|
|
96 |
// jQuery#append doesn't support arrays as the first argument
|
|
|
97 |
// until version 1.8, see http://bugs.jquery.com/ticket/11231
|
|
|
98 |
nodes = _.map(suggestions, getSuggestionNode);
|
|
|
99 |
$suggestions.append.apply($suggestions, nodes);
|
|
|
100 |
|
|
|
101 |
that.highlight && highlight({
|
|
|
102 |
className: 'tt-highlight',
|
|
|
103 |
node: $suggestions[0],
|
|
|
104 |
pattern: query
|
|
|
105 |
});
|
|
|
106 |
|
|
|
107 |
return $suggestions;
|
|
|
108 |
|
|
|
109 |
function getSuggestionNode(suggestion) {
|
|
|
110 |
var $el;
|
|
|
111 |
|
|
|
112 |
$el = $(html.suggestion)
|
|
|
113 |
.append(that.templates.suggestion(suggestion))
|
|
|
114 |
.data(datasetKey, that.name)
|
|
|
115 |
.data(valueKey, that.displayFn(suggestion))
|
|
|
116 |
.data(datumKey, suggestion);
|
|
|
117 |
|
|
|
118 |
$el.children().each(function() { $(this).css(css.suggestionChild); });
|
|
|
119 |
|
|
|
120 |
return $el;
|
|
|
121 |
}
|
|
|
122 |
}
|
|
|
123 |
|
|
|
124 |
function getHeaderHtml() {
|
|
|
125 |
return that.templates.header({
|
|
|
126 |
query: query,
|
|
|
127 |
isEmpty: !hasSuggestions
|
|
|
128 |
});
|
|
|
129 |
}
|
|
|
130 |
|
|
|
131 |
function getFooterHtml() {
|
|
|
132 |
return that.templates.footer({
|
|
|
133 |
query: query,
|
|
|
134 |
isEmpty: !hasSuggestions
|
|
|
135 |
});
|
|
|
136 |
}
|
|
|
137 |
},
|
|
|
138 |
|
|
|
139 |
// ### public
|
|
|
140 |
|
|
|
141 |
getRoot: function getRoot() {
|
|
|
142 |
return this.$el;
|
|
|
143 |
},
|
|
|
144 |
|
|
|
145 |
update: function update(query) {
|
|
|
146 |
var that = this;
|
|
|
147 |
|
|
|
148 |
this.query = query;
|
|
|
149 |
this.canceled = false;
|
|
|
150 |
this.source(query, render);
|
|
|
151 |
|
|
|
152 |
function render(suggestions) {
|
|
|
153 |
// if the update has been canceled or if the query has changed
|
|
|
154 |
// do not render the suggestions as they've become outdated
|
|
|
155 |
if (!that.canceled && query === that.query) {
|
|
|
156 |
that._render(query, suggestions);
|
|
|
157 |
}
|
|
|
158 |
}
|
|
|
159 |
},
|
|
|
160 |
|
|
|
161 |
cancel: function cancel() {
|
|
|
162 |
this.canceled = true;
|
|
|
163 |
},
|
|
|
164 |
|
|
|
165 |
clear: function clear() {
|
|
|
166 |
this.cancel();
|
|
|
167 |
this.$el.empty();
|
|
|
168 |
this.trigger('rendered');
|
|
|
169 |
},
|
|
|
170 |
|
|
|
171 |
isEmpty: function isEmpty() {
|
|
|
172 |
return this.$el.is(':empty');
|
|
|
173 |
},
|
|
|
174 |
|
|
|
175 |
destroy: function destroy() {
|
|
|
176 |
this.$el = null;
|
|
|
177 |
}
|
|
|
178 |
});
|
|
|
179 |
|
|
|
180 |
return Dataset;
|
|
|
181 |
|
|
|
182 |
// helper functions
|
|
|
183 |
// ----------------
|
|
|
184 |
|
|
|
185 |
function getDisplayFn(display) {
|
|
|
186 |
display = display || 'value';
|
|
|
187 |
|
|
|
188 |
return _.isFunction(display) ? display : displayFn;
|
|
|
189 |
|
|
|
190 |
function displayFn(obj) { return obj[display]; }
|
|
|
191 |
}
|
|
|
192 |
|
|
|
193 |
function getTemplates(templates, displayFn) {
|
|
|
194 |
return {
|
|
|
195 |
empty: templates.empty && _.templatify(templates.empty),
|
|
|
196 |
header: templates.header && _.templatify(templates.header),
|
|
|
197 |
footer: templates.footer && _.templatify(templates.footer),
|
|
|
198 |
suggestion: templates.suggestion || suggestionTemplate
|
|
|
199 |
};
|
|
|
200 |
|
|
|
201 |
function suggestionTemplate(context) {
|
|
|
202 |
return '<p>' + displayFn(context) + '</p>';
|
|
|
203 |
}
|
|
|
204 |
}
|
|
|
205 |
|
|
|
206 |
function isValidName(str) {
|
|
|
207 |
// dashes, underscores, letters, and numbers
|
|
|
208 |
return (/^[_a-zA-Z0-9-]+$/).test(str);
|
|
|
209 |
}
|
|
|
210 |
})();
|