alpaca
Version:
Alpaca provides the easiest and fastest way to generate interactive forms for the web and mobile devices. It runs simply as HTML5 or more elaborately using Bootstrap, jQuery Mobile or jQuery UI. Alpaca uses Handlebars to process JSON schema and provide
211 lines (157 loc) • 5.15 kB
JavaScript
/*
* typeahead.js
* https://github.com/twitter/typeahead.js
* Copyright 2013-2014 Twitter, Inc. and other contributors; Licensed MIT
*/
var Dataset = (function() {
'use strict';
var datasetKey = 'ttDataset', valueKey = 'ttValue', datumKey = 'ttDatum';
// constructor
// -----------
function Dataset(o) {
o = o || {};
o.templates = o.templates || {};
if (!o.source) {
$.error('missing source');
}
if (o.name && !isValidName(o.name)) {
$.error('invalid dataset name: ' + o.name);
}
// tracks the last query the dataset was updated for
this.query = null;
this.highlight = !!o.highlight;
this.name = o.name || _.getUniqueId();
this.source = o.source;
this.displayFn = getDisplayFn(o.display || o.displayKey);
this.templates = getTemplates(o.templates, this.displayFn);
this.$el = $(html.dataset.replace('%CLASS%', this.name));
}
// static methods
// --------------
Dataset.extractDatasetName = function extractDatasetName(el) {
return $(el).data(datasetKey);
};
Dataset.extractValue = function extractDatum(el) {
return $(el).data(valueKey);
};
Dataset.extractDatum = function extractDatum(el) {
return $(el).data(datumKey);
};
// instance methods
// ----------------
_.mixin(Dataset.prototype, EventEmitter, {
// ### private
_render: function render(query, suggestions) {
if (!this.$el) { return; }
var that = this, hasSuggestions;
this.$el.empty();
hasSuggestions = suggestions && suggestions.length;
if (!hasSuggestions && this.templates.empty) {
this.$el
.html(getEmptyHtml())
.prepend(that.templates.header ? getHeaderHtml() : null)
.append(that.templates.footer ? getFooterHtml() : null);
}
else if (hasSuggestions) {
this.$el
.html(getSuggestionsHtml())
.prepend(that.templates.header ? getHeaderHtml() : null)
.append(that.templates.footer ? getFooterHtml() : null);
}
this.trigger('rendered');
function getEmptyHtml() {
return that.templates.empty({ query: query, isEmpty: true });
}
function getSuggestionsHtml() {
var $suggestions, nodes;
$suggestions = $(html.suggestions).css(css.suggestions);
// jQuery#append doesn't support arrays as the first argument
// until version 1.8, see http://bugs.jquery.com/ticket/11231
nodes = _.map(suggestions, getSuggestionNode);
$suggestions.append.apply($suggestions, nodes);
that.highlight && highlight({
className: 'tt-highlight',
node: $suggestions[0],
pattern: query
});
return $suggestions;
function getSuggestionNode(suggestion) {
var $el;
$el = $(html.suggestion)
.append(that.templates.suggestion(suggestion))
.data(datasetKey, that.name)
.data(valueKey, that.displayFn(suggestion))
.data(datumKey, suggestion);
$el.children().each(function() { $(this).css(css.suggestionChild); });
return $el;
}
}
function getHeaderHtml() {
return that.templates.header({
query: query,
isEmpty: !hasSuggestions
});
}
function getFooterHtml() {
return that.templates.footer({
query: query,
isEmpty: !hasSuggestions
});
}
},
// ### public
getRoot: function getRoot() {
return this.$el;
},
update: function update(query) {
var that = this;
this.query = query;
this.canceled = false;
this.source(query, render);
function render(suggestions) {
// if the update has been canceled or if the query has changed
// do not render the suggestions as they've become outdated
if (!that.canceled && query === that.query) {
that._render(query, suggestions);
}
}
},
cancel: function cancel() {
this.canceled = true;
},
clear: function clear() {
this.cancel();
this.$el.empty();
this.trigger('rendered');
},
isEmpty: function isEmpty() {
return this.$el.is(':empty');
},
destroy: function destroy() {
this.$el = null;
}
});
return Dataset;
// helper functions
// ----------------
function getDisplayFn(display) {
display = display || 'value';
return _.isFunction(display) ? display : displayFn;
function displayFn(obj) { return obj[display]; }
}
function getTemplates(templates, displayFn) {
return {
empty: templates.empty && _.templatify(templates.empty),
header: templates.header && _.templatify(templates.header),
footer: templates.footer && _.templatify(templates.footer),
suggestion: templates.suggestion || suggestionTemplate
};
function suggestionTemplate(context) {
return '<p>' + displayFn(context) + '</p>';
}
}
function isValidName(str) {
// dashes, underscores, letters, and numbers
return (/^[_a-zA-Z0-9-]+$/).test(str);
}
})();