Skip to content
This repository was archived by the owner on Sep 16, 2021. It is now read-only.

[WIP] An autocompletion plugin #101

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Resources/public/js/adapter/fancytree.js
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,10 @@

addAction: function (name, url, icon) {
this.actions[name] = { url: url, icon: icon };
},

getFromCache: function (name) {
return cache.hasOwnProperty(name) ? cache[name] : undefined;
}
};

Expand Down
85 changes: 85 additions & 0 deletions Resources/public/js/jquery.cmf_autocomplete.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
!function () {
var cache = [];

/**
* A small plugin to add autocompletion features for paths
* in browsers.
*
* @author Wouter J <wouter@wouterj.nl>
*/
window.jQuery.fn.cmfAutoComplete = function (options) {
options = jQuery.extend({
/**
* The element used to show the suggestions.
*
* This has to be a <datalist> element.
*
* @var string|null A selector or use the selector in
* the `list` attribute (when null is used)
*/
datalist_selector: null,

/**
* A callback to retrieve the data.
*
* @return array
*/
data: function (path, next) { next([]); },
}, options);

var $input = $(this);
var $autocompleteList;
var previousBase;

// find datalist
if (options.datalist_selector) {
$autocompleteList = $(options.datalist_selector);
$(this).attr('list', options.datalist_selector);
} else if ($(this).attr('list')) {
$autocompleteList = $('#' + $(this).attr('list'));
} else {
throw 'No datalist selected';
}

if ('DATALIST' !== $autocompleteList.prop('tagName')) {
throw 'The configured datalist element should be a <datalist> element, <' + $autocompleteList.prop('tagName').toLowerCase() + '> given.';
}

// find autocompletion values
$input.on('keyup', function (e) {
// arrows are used to navigate through the data list, don't update it then
if (e.keyCode >= 37 && e.keyCode <= 40) {
return;
}

var path = $(this).val();
var base = path.substr(0, path.lastIndexOf('/')); // get everything except the child

// skip if still in same node
if (base === previousBase) {
return;
}
previousBase = base;

// cache node
if (!cache[base]) {
options.data(base, function (nodes) {
cache[base] = nodes;
updateAutocompleteList(base);
});
} else {
updateAutocompleteList(base);
}
});

function updateAutocompleteList(base) {
// clear datalist
$autocompleteList.empty();

// add new autocomplete values
cache[base].forEach(function (child) {
$autocompleteList.append('<option label="' + child + '" value="' + base + child + '">');
});
}
};
}();
1 change: 1 addition & 0 deletions Resources/views/Base/scripts.html.twig
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<script src="{{ asset('bundles/cmftreebrowser/js/adapter/' ~ treeAdapter ~ '.js') }}"></script>
<script src="{{ asset('bundles/cmftreebrowser/js/jquery.cmf_context_menu.js') }}"></script>
<script src="{{ asset('bundles/cmftreebrowser/js/jquery.cmf_tree.js') }}"></script>
<script src="{{ asset('bundles/cmftreebrowser/js/jquery.cmf_autocomplete.js') }}"></script>

<link rel="stylesheet" href="{{ asset(assetsBasePath ~ '/fancytree/dist/skin-win8/ui.fancytree.min.css') }}" media="all"/>
<link rel="stylesheet" href="{{ asset('bundles/cmftreebrowser/css/fontawesome-style.css') }}" media="all"/>
58 changes: 58 additions & 0 deletions Tests/js/cmfAutoCompleteSpec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
describe('The cmfAutoComplete plugin', function () {

beforeEach(function () {
setFixtures('<input type=text id=field list=suggestion-list><datalist id="suggestion-list"></datalist>');
this.$field = $('#field');
this.$list = $('#suggestion-list');
});

function type($field, value) {
$field.val(value);
$field.trigger('keyup');
}

it('has a constructor named cmfAutoComplete', function () {
expect(jQuery.fn.cmfAutoComplete).toBeDefined();
});

it('updates the datalist with possible elements', function () {
this.$field.cmfAutoComplete({
data: function (path, next) {
next(['some suggestion', 'another suggestion']);
}
});

type(this.$field, 'value');

expect(this.$list.find('option')).toHaveLength(2);
});

it('calls the data function with the base node', function () {
var spy = jasmine.createSpy('data');

this.$field.cmfAutoComplete({
data: spy
});

type(this.$field, '/cms/cont');
type(this.$field, '/cms/content/h');

expect(spy.calls.count()).toBe(2);
expect(spy.calls.argsFor(0)[0]).toBe('/cms');
expect(spy.calls.argsFor(1)[0]).toBe('/cms/content');
});

it('caches previous data', function () {
var spy = jasmine.createSpy('data');

this.$field.cmfAutoComplete({
data: spy
});

type(this.$field, '/cms/cont');
type(this.$field, '/cms/me');

expect(spy.calls.count()).toBe(1);
});

});