Skip to content
This repository has been archived by the owner on Sep 23, 2019. It is now read-only.

Commit

Permalink
started
Browse files Browse the repository at this point in the history
  • Loading branch information
dazld committed Mar 2, 2014
1 parent 13d262c commit c70adc4
Show file tree
Hide file tree
Showing 6 changed files with 226 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
@@ -0,0 +1 @@
node_modules
10 changes: 10 additions & 0 deletions example/index.html
@@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<title>Backbone Autocomplete View test</title>
</head>
<body>
<div id="app"></div>
<script type="text/javascript"></script>
</body>
</html>
97 changes: 97 additions & 0 deletions index.js
@@ -0,0 +1,97 @@
var Backbone = require('backbone');
var _ = require('backbone/node_modules/underscore');
var ResultsView = require('./results-view');

// Captured by the keyfilter to control search results
var keys = {
9: 'enter',
13: 'enter',
38: 'up',
40: 'down',
27: 'escape'
};

var noop = function(){};

var MIN_INPUT_LENGTH = 3;

var AutocompleteView = Backbone.View.extend({
tagName: 'div',
className: 'bb-autocomplete',
events: {
'keyup .ac-user-input':'onKeyup',
'blur .ac-user-input':'onBlur',
'change .ac-user-input': 'onChange'
},
template: _.template('<input class="ac-user-input" type="text" /><div class="ac-results"></div>'),
initialize: function initialize(options) {
options = options || {};

this.MIN_INPUT_LENGTH = options.minimumInputLength || MIN_INPUT_LENGTH;

this.collection = options.collection; // data to search against
this.resultsCollection = new Backbone.Collection(); // where to put results of search
// method on data collection which gives us results
// should return an array of JS objects representing the data
this.shouldFetch = options.shouldFetch || false;


this.resultsView = new ResultsView({
parentView: this,
collection: this.resultsCollection
});

},
setupEvents: function setupEvents(){

},
onKeyup: function onKeydown(evt){
var input = evt.target;
// see if the user has pressed a key we are explicitly wanting to control
// the behaviour for
if (keys[evt.keyCode]) {
evt.preventDefault();
switch (keys[evt.keyCode]){
case 'enter':
break;
case 'escape':
input.blur();
break;
default:
this.resultsView.trigger(keys[evt.keyCode]);
break;
}
return false;
}
},
onBlur: function onBlur(evt) {
this.resultsView.trigger('hide');
},
onChange: function(evt){
this.searchValue = evt.target.value;
},

doSearch: function(){
var filteredResults = this.collection.filter(this.searchMethod);
this.resultsCollection.reset(filteredResults);
},
render: function render (){
this.$el.html(this.template());
return this;
},
// overwritten when subclassing
onSelect: noop // callback that receives the model that was chosen
searchMethod: function searchMethod(item) {
var label = item.get('label');
if (label.indexOf(this.searchValue) !== -1) {
return true;
} else {
return false;
}
}

});


module.exports = AutocompleteView;

14 changes: 14 additions & 0 deletions package.json
@@ -0,0 +1,14 @@
{
"name": "backbone-autocomplete",
"version": "0.0.0",
"description": "",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
"license": "ISC",
"dependencies": {
"backbone": "~1.1.2"
}
}
5 changes: 5 additions & 0 deletions readme.md
@@ -0,0 +1,5 @@
# Backbone Autocomplete View

Made with browserify or similar in mind.

MIT License (c) Dan Peddle 2014
99 changes: 99 additions & 0 deletions results-view.js
@@ -0,0 +1,99 @@
var Backbone = require('backbone');
var _ = require('backbone/node_modules/underscore');

var ItemView = Backbone.View.extend({
tagName: 'span',
className: 'ac-result',
template: _.template('<%= label %>'),
events: {
'click':'onSelect',
'mouseenter':'onHover'
},
initialize: function(options){
this.parentView = options.parentView;
},
onHover: function(){
this.parentView.trigger('highlight', this.model);
},
onSelect: function(){
this.parentView.trigger('chosen', this.model);
}
});

var ResultsView = Backbone.View.extend({
tagName: 'div',
className: 'bb-autocomplete-results',
initialize: function initialize(options) {
this.collection = options.collection;
this.parentView = options.parentView;
this.noResultsText = options.noResultsText || 'No results';
this.on('show', this.show, this);
this.on('hide', this.hide, this);

var moveUp = _.bind(this.moveHighlight, this, -1);
var moveDown = _.bind(this.moveHighlight, this, 1);
this.on('up', moveUp);
this.on('down', moveDown);
this.on('highlight', this.highlight, this);
this.on('chosen', function(model) {
this.parentView.trigger('chosen', model);
}, this);
},
show: function show() {
this.$el.removeClass('hidden');
},
hide: function hide() {
this.$el.addClass('hidden');
}
resetHighlightIndex: function resetHighlightIndex() {
this.removeHighlights();
this.highlightIndex = false;
if (this.collection.length === 0) {
var localisedWarning = this.noResultsText;
this.$el.html('<span class="bb-autocomplete-no-results">' + localisedWarning + '</span>');
}
},
removeHighlights: function removeHighlights() {
this.$el.find('.highlight').removeClass('highlight');
},
highlight: function(model) {
this.removeHighlights();
model = model || this.collection.at(this.highlightIndex);
var viewToHighlight = this._getViewByModel(model);
if (viewToHighlight) {
viewToHighlight.$el.addClass('highlight');
this.highlightIndex = this.collection.indexOf(model);
}
},
moveHighlight: function(direction) {
if (this.collection.length === 0) {
// do nothing as we have no results
return;
}

if (this.highlightIndex === false) {
if (direction < 0) {
this.highlightIndex = this.collection.length - 1;
} else if (direction > 0) {
this.highlightIndex = 0;
}
} else {

this.highlightIndex += direction;

if (this.highlightIndex < 0) {
this.highlightIndex = this.collection.length - 1;
} else if (this.highlightIndex >= this.collection.length) {
this.highlightIndex = 0;
}

}

this.highlight();
},
render: function render() {

}
});

module.exports = ResultsView;

0 comments on commit c70adc4

Please sign in to comment.