initial commit
This commit is contained in:
commit
0340c4bd44
35
.gitignore
vendored
Normal file
35
.gitignore
vendored
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
#### joe made this: https://goel.io/joe
|
||||||
|
|
||||||
|
#####=== Node ===#####
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
|
||||||
|
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (http://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directory
|
||||||
|
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git-
|
||||||
|
node_modules
|
||||||
|
|
||||||
|
# Debug log from npm
|
||||||
|
npm-debug.log
|
||||||
|
|
40
Gruntfile.js
Normal file
40
Gruntfile.js
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
module.exports = function(grunt) {
|
||||||
|
grunt.initConfig({
|
||||||
|
babel: {
|
||||||
|
all: {
|
||||||
|
files: [{
|
||||||
|
cwd: 'src',
|
||||||
|
src: ['**/*.js', '!libs/*.js'],
|
||||||
|
dest: 'build',
|
||||||
|
expand: true
|
||||||
|
}, {
|
||||||
|
src: ['demo/**/*.js'],
|
||||||
|
dest: 'build',
|
||||||
|
expand: true
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
copy: {
|
||||||
|
libs: {
|
||||||
|
files: [{
|
||||||
|
cwd: 'src',
|
||||||
|
src: 'libs/**/*',
|
||||||
|
dest: 'build',
|
||||||
|
expand: true
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
watch: {
|
||||||
|
scripts: {
|
||||||
|
files: ['src/**/*.js', 'demo/**/*.js'],
|
||||||
|
tasks: ['babel']
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-copy');
|
||||||
|
grunt.loadNpmTasks('grunt-babel');
|
||||||
|
|
||||||
|
grunt.registerTask('default', ['babel', 'copy', 'watch']);
|
||||||
|
};
|
126
build/demo/add.js
Normal file
126
build/demo/add.js
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
document.addEventListener('DOMContentLoaded', function () {
|
||||||
|
var WIDTH = 700;
|
||||||
|
var HEIGHT = 600;
|
||||||
|
var MARGIN = 100;
|
||||||
|
|
||||||
|
var svg = d3.select('body').append('svg').attr('width', WIDTH + MARGIN).attr('height', HEIGHT + MARGIN).attr('viewBox', '-' + MARGIN / 2 + ' -' + MARGIN / 2 + ' ' + WIDTH + ' ' + HEIGHT);
|
||||||
|
|
||||||
|
svg.append('g').attr('class', 'lines');
|
||||||
|
|
||||||
|
var tree = d3.layout.tree().size([WIDTH - MARGIN, HEIGHT - MARGIN]);
|
||||||
|
|
||||||
|
draw();
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
console.dir(data.root);
|
||||||
|
var nodes = tree.nodes(data.root);
|
||||||
|
var links = tree.links(nodes);
|
||||||
|
|
||||||
|
var linkElements = svg.select('g.lines').selectAll('line').data(links);
|
||||||
|
linkElements.exit().remove();
|
||||||
|
linkElements.enter().append('line');
|
||||||
|
|
||||||
|
linkElements.attr('x1', function (d) {
|
||||||
|
return d.source.x;
|
||||||
|
}).attr('y1', function (d) {
|
||||||
|
return d.source.y;
|
||||||
|
}).attr('x2', function (d) {
|
||||||
|
return d.target.x;
|
||||||
|
}).attr('y2', function (d) {
|
||||||
|
return d.target.y;
|
||||||
|
}).style('stroke', 'rgb(72, 213, 91)').style('stroke-width', '2px');
|
||||||
|
|
||||||
|
var nodeElements = svg.selectAll('g.node').data(nodes);
|
||||||
|
nodeElements.exit().remove();
|
||||||
|
var nodesEnter = nodeElements.enter().append('g').attr('class', 'node');
|
||||||
|
|
||||||
|
nodeElements.attr('transform', function (d) {
|
||||||
|
return 'translate(' + d.x + ', ' + d.y + ')';
|
||||||
|
}).style('font-family', 'monospace').style('font-size', '11px').attr('data-word', function (d) {
|
||||||
|
return d.name;
|
||||||
|
});
|
||||||
|
|
||||||
|
nodesEnter.append('circle');
|
||||||
|
|
||||||
|
var circles = nodeElements.selectAll('circle');
|
||||||
|
circles.attr('r', 25).style('fill', 'rgb(28, 236, 166)').style('stroke', 'rgb(17, 172, 144)').style('fill', 'rgb(28, 236, 166)').style('stroke', 'rgb(17, 172, 144)').attr('r', 10).transition().ease(d3.ease('elastic')).duration(700).attr('r', 25);
|
||||||
|
|
||||||
|
nodesEnter.append('text');
|
||||||
|
var texts = svg.selectAll('g.node text').data(nodes);
|
||||||
|
|
||||||
|
texts.attr('dy', 5).html(function (d) {
|
||||||
|
return '<tspan>' + d.name.split('').join('</tspan><tspan>') + '</tspan>';
|
||||||
|
}).attr('text-anchor', 'middle').style('fill', 'white');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Form and other stuff
|
||||||
|
|
||||||
|
var form = document.querySelector('form');
|
||||||
|
var input = document.querySelector('input');
|
||||||
|
var explain = document.querySelector('#explain');
|
||||||
|
var explainWord = document.querySelector('#explain-word');
|
||||||
|
var explainIndex = document.querySelector('#explain-index');
|
||||||
|
|
||||||
|
form.addEventListener('submit', function (e) {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
var word = input.value;
|
||||||
|
|
||||||
|
if (!explain.checked) {
|
||||||
|
data.add(word);
|
||||||
|
draw();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var separated = '<span>' + word.split('').join('</span><span>') + '</span>';
|
||||||
|
explainWord.innerHTML = separated;
|
||||||
|
var indexLeft = explainWord.children[0].offsetLeft;
|
||||||
|
var indexMargin = 20;
|
||||||
|
var indexWidth = explainWord.children[0].offsetWidth + indexMargin;
|
||||||
|
|
||||||
|
var index = 0;
|
||||||
|
var key = '';
|
||||||
|
|
||||||
|
var intv = setInterval(function () {
|
||||||
|
d3.selectAll('tspan').attr('class', '');
|
||||||
|
d3.selectAll('#explain-word span').attr('class', '');
|
||||||
|
d3.selectAll('.node').attr('class', 'node');
|
||||||
|
|
||||||
|
var char = word[index];
|
||||||
|
|
||||||
|
if (!char) {
|
||||||
|
clearInterval(intv);
|
||||||
|
|
||||||
|
explainIndex.innerHTML = '';
|
||||||
|
explainWord.innerHTML = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
key += char;
|
||||||
|
|
||||||
|
var s = explainWord.children[index];
|
||||||
|
s.classList.add('highlight');
|
||||||
|
|
||||||
|
explainIndex.style.left = indexLeft + index * indexWidth + 'px';
|
||||||
|
explainIndex.innerHTML = index;
|
||||||
|
|
||||||
|
var node = document.querySelector('[data-word="' + key + '"]');
|
||||||
|
if (!node) {
|
||||||
|
data.add(word.slice(0, index + 1));
|
||||||
|
draw();
|
||||||
|
|
||||||
|
node = document.querySelector('[data-word="' + key + '"]');
|
||||||
|
}
|
||||||
|
|
||||||
|
node.classList.add('highlight');
|
||||||
|
|
||||||
|
var text = node.querySelector('text');
|
||||||
|
text.children[index].classList.add('highlight');
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}, 1000);
|
||||||
|
});
|
||||||
|
});
|
15
build/demo/data.js
Normal file
15
build/demo/data.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
window.data = new Trie();
|
||||||
|
|
||||||
|
data.add('colors');
|
||||||
|
data.add('coffee');
|
||||||
|
// data.add('codecs');
|
||||||
|
|
||||||
|
data.add('boo');
|
||||||
|
// data.add('boloss');
|
||||||
|
// data.add('badass');
|
||||||
|
|
||||||
|
// data.add('rebecca');
|
||||||
|
// data.add('robots');
|
||||||
|
// data.add('rio');
|
98
build/index.js
Normal file
98
build/index.js
Normal file
@ -0,0 +1,98 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var trie = new Trie();
|
||||||
|
|
||||||
|
var divs = document.querySelectorAll('div');
|
||||||
|
var colors = {};
|
||||||
|
|
||||||
|
var _iteratorNormalCompletion = true;
|
||||||
|
var _didIteratorError = false;
|
||||||
|
var _iteratorError = undefined;
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (var _iterator = divs[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
|
||||||
|
var div = _step.value;
|
||||||
|
|
||||||
|
colors[div.className] = div;
|
||||||
|
|
||||||
|
trie.add(div.className);
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
_didIteratorError = true;
|
||||||
|
_iteratorError = err;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (!_iteratorNormalCompletion && _iterator['return']) {
|
||||||
|
_iterator['return']();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (_didIteratorError) {
|
||||||
|
throw _iteratorError;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var input = document.querySelector('input');
|
||||||
|
|
||||||
|
input.addEventListener('keyup', function () {
|
||||||
|
divs.hide();
|
||||||
|
|
||||||
|
var nodes = trie.all(input.value);
|
||||||
|
|
||||||
|
if (!nodes) return;
|
||||||
|
|
||||||
|
var _iteratorNormalCompletion2 = true;
|
||||||
|
var _didIteratorError2 = false;
|
||||||
|
var _iteratorError2 = undefined;
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (var _iterator2 = nodes[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
|
||||||
|
var node = _step2.value;
|
||||||
|
|
||||||
|
var color = colors[node.value];
|
||||||
|
if (!color) continue;
|
||||||
|
|
||||||
|
color.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
_didIteratorError2 = true;
|
||||||
|
_iteratorError2 = err;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (!_iteratorNormalCompletion2 && _iterator2['return']) {
|
||||||
|
_iterator2['return']();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (_didIteratorError2) {
|
||||||
|
throw _iteratorError2;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
NodeList.prototype.hide = function () {
|
||||||
|
var _iteratorNormalCompletion3 = true;
|
||||||
|
var _didIteratorError3 = false;
|
||||||
|
var _iteratorError3 = undefined;
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (var _iterator3 = this[Symbol.iterator](), _step3; !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done); _iteratorNormalCompletion3 = true) {
|
||||||
|
var node = _step3.value;
|
||||||
|
|
||||||
|
node.classList.add('hidden');
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
_didIteratorError3 = true;
|
||||||
|
_iteratorError3 = err;
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
if (!_iteratorNormalCompletion3 && _iterator3['return']) {
|
||||||
|
_iterator3['return']();
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (_didIteratorError3) {
|
||||||
|
throw _iteratorError3;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
5
build/libs/d3.js
vendored
Normal file
5
build/libs/d3.js
vendored
Normal file
File diff suppressed because one or more lines are too long
91
build/trie.js
Normal file
91
build/trie.js
Normal file
@ -0,0 +1,91 @@
|
|||||||
|
'use strict';
|
||||||
|
|
||||||
|
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })();
|
||||||
|
|
||||||
|
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } }
|
||||||
|
|
||||||
|
var Node = function Node(value, parent) {
|
||||||
|
if (value === undefined) value = '';
|
||||||
|
|
||||||
|
_classCallCheck(this, Node);
|
||||||
|
|
||||||
|
this.name = value;
|
||||||
|
this.children = [];
|
||||||
|
this.parent = parent;
|
||||||
|
};
|
||||||
|
|
||||||
|
var Trie = (function () {
|
||||||
|
function Trie() {
|
||||||
|
_classCallCheck(this, Trie);
|
||||||
|
|
||||||
|
this.root = new Node();
|
||||||
|
}
|
||||||
|
|
||||||
|
_createClass(Trie, [{
|
||||||
|
key: 'add',
|
||||||
|
value: function add(value) {
|
||||||
|
var parent = arguments.length <= 1 || arguments[1] === undefined ? this.root : arguments[1];
|
||||||
|
|
||||||
|
var _loop = function (i, len) {
|
||||||
|
if (!parent.children) parent.children = [];
|
||||||
|
var node = parent.children.find(function (child) {
|
||||||
|
return child.name[i] === value[i];
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!node) {
|
||||||
|
node = new Node(value.slice(0, i + 1), parent.name);
|
||||||
|
parent.children.push(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
parent = node;
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var i = 0, len = value.length; i < len; i++) {
|
||||||
|
_loop(i, len);
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
key: 'find',
|
||||||
|
value: function find(value) {
|
||||||
|
var parent = arguments.length <= 1 || arguments[1] === undefined ? this.root : arguments[1];
|
||||||
|
|
||||||
|
var _loop2 = function (i, len) {
|
||||||
|
parent = parent.children.find(function (child) {
|
||||||
|
return child.name[i] === value[i];
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!parent) return {
|
||||||
|
v: null
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var i = 0, len = value.length; i < len; i++) {
|
||||||
|
var _ret2 = _loop2(i, len);
|
||||||
|
|
||||||
|
if (typeof _ret2 === 'object') return _ret2.v;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
key: 'all',
|
||||||
|
value: function all(search) {
|
||||||
|
var node = this.find(search);
|
||||||
|
|
||||||
|
if (!node) return null;
|
||||||
|
|
||||||
|
var all = [node];
|
||||||
|
|
||||||
|
node.children.forEach(function addToAll(child) {
|
||||||
|
all.push(child);
|
||||||
|
child.children.forEach(addToAll);
|
||||||
|
});
|
||||||
|
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
|
||||||
|
return Trie;
|
||||||
|
})();
|
109
demo/add.html
Normal file
109
demo/add.html
Normal file
@ -0,0 +1,109 @@
|
|||||||
|
<head>
|
||||||
|
<title>Trie add demo</title>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
html {
|
||||||
|
font-family: sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
position: fixed;
|
||||||
|
top: 30px;
|
||||||
|
right: 30px;
|
||||||
|
width: 300px;
|
||||||
|
height: 200px;
|
||||||
|
padding: 20px;
|
||||||
|
text-align: center;
|
||||||
|
background: rgb(106, 250, 211);
|
||||||
|
border: 2px solid rgb(29, 214, 131);
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-size: 20px;
|
||||||
|
|
||||||
|
color: rgb(13, 101, 71);
|
||||||
|
}
|
||||||
|
|
||||||
|
input, button {
|
||||||
|
display: block;
|
||||||
|
|
||||||
|
margin: 10px auto;
|
||||||
|
|
||||||
|
width: 80%;
|
||||||
|
border-radius: 2px;
|
||||||
|
|
||||||
|
height: 25px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
|
||||||
|
border: 1px solid rgb(13, 96, 98);
|
||||||
|
}
|
||||||
|
|
||||||
|
button {
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
#explain-word {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
#explain-index {
|
||||||
|
position: absolute;
|
||||||
|
left: 50%;
|
||||||
|
top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#explain-word span {
|
||||||
|
margin: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
label[for='explain'] {
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
#explain {
|
||||||
|
width: 20px;
|
||||||
|
margin: -2px 10px;
|
||||||
|
float: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
.highlight circle {
|
||||||
|
fill: rgb(0, 255, 169) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.highlight {
|
||||||
|
border-bottom: 2px solid currentColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
tspan.highlight {
|
||||||
|
text-decoration: underline;
|
||||||
|
fill: #00AD82;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<form>
|
||||||
|
<label for='new'>Add new nodes to the Trie</label>
|
||||||
|
<input id='new'>
|
||||||
|
<button>Add</button>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<input type='checkbox' id='explain' checked='checked'>
|
||||||
|
<label for='explain'>Show parent-finding guides</label>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
<p id='explain-word'></p>
|
||||||
|
<p id='explain-index'></p>
|
||||||
|
|
||||||
|
<script src='../build/libs/d3.js'></script>
|
||||||
|
<script src='../build/trie.js'></script>
|
||||||
|
<script src='../build/demo/data.js'></script>
|
||||||
|
<script src='../build/demo/add.js'></script>
|
136
demo/add.js
Normal file
136
demo/add.js
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
document.addEventListener('DOMContentLoaded', () => {
|
||||||
|
const WIDTH = 700;
|
||||||
|
const HEIGHT = 600;
|
||||||
|
const MARGIN = 100;
|
||||||
|
|
||||||
|
let svg = d3.select('body').append('svg')
|
||||||
|
.attr('width', WIDTH + MARGIN)
|
||||||
|
.attr('height', HEIGHT + MARGIN)
|
||||||
|
.attr('viewBox', `-${MARGIN / 2} -${MARGIN / 2} ${WIDTH} ${HEIGHT}`);
|
||||||
|
|
||||||
|
svg.append('g').attr('class', 'lines');
|
||||||
|
|
||||||
|
let tree = d3.layout.tree()
|
||||||
|
.size([WIDTH - MARGIN, HEIGHT - MARGIN]);
|
||||||
|
|
||||||
|
draw();
|
||||||
|
|
||||||
|
function draw() {
|
||||||
|
console.dir(data.root);
|
||||||
|
let nodes = tree.nodes(data.root);
|
||||||
|
let links = tree.links(nodes);
|
||||||
|
|
||||||
|
let linkElements = svg.select('g.lines').selectAll('line').data(links);
|
||||||
|
linkElements.exit().remove();
|
||||||
|
linkElements.enter().append('line');
|
||||||
|
|
||||||
|
linkElements.attr('x1', d => d.source.x)
|
||||||
|
.attr('y1', d => d.source.y)
|
||||||
|
.attr('x2', d => d.target.x)
|
||||||
|
.attr('y2', d => d.target.y)
|
||||||
|
.style('stroke', 'rgb(72, 213, 91)')
|
||||||
|
.style('stroke-width', '2px');
|
||||||
|
|
||||||
|
let nodeElements = svg.selectAll('g.node').data(nodes);
|
||||||
|
nodeElements.exit().remove();
|
||||||
|
let nodesEnter = nodeElements.enter().append('g').attr('class', 'node');
|
||||||
|
|
||||||
|
nodeElements.attr('transform', d => `translate(${d.x}, ${d.y})`)
|
||||||
|
.style('font-family', 'monospace')
|
||||||
|
.style('font-size', '11px')
|
||||||
|
.attr('data-word', d => d.name);
|
||||||
|
|
||||||
|
nodesEnter.append('circle');
|
||||||
|
|
||||||
|
let circles = nodeElements.selectAll('circle');
|
||||||
|
circles.attr('r', 25)
|
||||||
|
.style('fill', 'rgb(28, 236, 166)')
|
||||||
|
.style('stroke', 'rgb(17, 172, 144)')
|
||||||
|
.style('fill', 'rgb(28, 236, 166)')
|
||||||
|
.style('stroke', 'rgb(17, 172, 144)')
|
||||||
|
.attr('r', 10)
|
||||||
|
.transition()
|
||||||
|
.ease(d3.ease('elastic'))
|
||||||
|
.duration(700)
|
||||||
|
.attr('r', 25);
|
||||||
|
|
||||||
|
|
||||||
|
nodesEnter.append('text');
|
||||||
|
let texts = svg.selectAll('g.node text').data(nodes);
|
||||||
|
|
||||||
|
texts.attr('dy', 5)
|
||||||
|
.html(d => `<tspan>${d.name.split('').join('</tspan><tspan>')}</tspan>`)
|
||||||
|
.attr('text-anchor', 'middle')
|
||||||
|
.style('fill', 'white');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Form and other stuff
|
||||||
|
|
||||||
|
const form = document.querySelector('form');
|
||||||
|
const input = document.querySelector('input');
|
||||||
|
const explain = document.querySelector('#explain');
|
||||||
|
const explainWord = document.querySelector('#explain-word');
|
||||||
|
const explainIndex = document.querySelector('#explain-index');
|
||||||
|
|
||||||
|
form.addEventListener('submit', e => {
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
let word = input.value;
|
||||||
|
|
||||||
|
if (!explain.checked) {
|
||||||
|
data.add(word);
|
||||||
|
draw();
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let separated = `<span>${word.split('').join('</span><span>')}</span>`;
|
||||||
|
explainWord.innerHTML = separated;
|
||||||
|
const indexLeft = explainWord.children[0].offsetLeft;
|
||||||
|
const indexMargin = 20;
|
||||||
|
const indexWidth = explainWord.children[0].offsetWidth + indexMargin;
|
||||||
|
|
||||||
|
let index = 0;
|
||||||
|
let key = '';
|
||||||
|
|
||||||
|
const intv = setInterval(() => {
|
||||||
|
d3.selectAll('tspan').attr('class', '');
|
||||||
|
d3.selectAll('#explain-word span').attr('class', '');
|
||||||
|
d3.selectAll('.node').attr('class', 'node');
|
||||||
|
|
||||||
|
let char = word[index];
|
||||||
|
|
||||||
|
if (!char) {
|
||||||
|
clearInterval(intv);
|
||||||
|
|
||||||
|
explainIndex.innerHTML = '';
|
||||||
|
explainWord.innerHTML = '';
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
key += char;
|
||||||
|
|
||||||
|
let s = explainWord.children[index];
|
||||||
|
s.classList.add('highlight');
|
||||||
|
|
||||||
|
explainIndex.style.left = `${indexLeft + index * indexWidth}px`;
|
||||||
|
explainIndex.innerHTML = index;
|
||||||
|
|
||||||
|
let node = document.querySelector(`[data-word="${key}"]`);
|
||||||
|
if (!node) {
|
||||||
|
data.add(word.slice(0, index + 1));
|
||||||
|
draw();
|
||||||
|
|
||||||
|
node = document.querySelector(`[data-word="${key}"]`);
|
||||||
|
}
|
||||||
|
|
||||||
|
node.classList.add('highlight');
|
||||||
|
|
||||||
|
let text = node.querySelector('text');
|
||||||
|
text.children[index].classList.add('highlight');
|
||||||
|
|
||||||
|
index++;
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
|
});
|
||||||
|
});
|
13
demo/data.js
Normal file
13
demo/data.js
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
window.data = new Trie();
|
||||||
|
|
||||||
|
data.add('colors');
|
||||||
|
data.add('coffee');
|
||||||
|
// data.add('codecs');
|
||||||
|
|
||||||
|
data.add('boo');
|
||||||
|
// data.add('boloss');
|
||||||
|
// data.add('badass');
|
||||||
|
|
||||||
|
// data.add('rebecca');
|
||||||
|
// data.add('robots');
|
||||||
|
// data.add('rio');
|
24
index.html
Normal file
24
index.html
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
<head>
|
||||||
|
<link rel='stylesheet' href='./style.css'>
|
||||||
|
<title>Autocomplete</title>
|
||||||
|
</head>
|
||||||
|
|
||||||
|
<input>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<div class='colors'>COLORS - #C01025</div>
|
||||||
|
<div class='coffee'>COFFEE - #COFFEE</div>
|
||||||
|
<div class='codecs'>CODECS - #C0D3C5</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<div class='rebecca'>Rebecca - #663399</div>
|
||||||
|
<div class='robots'>ROBOTS - #208075</div>
|
||||||
|
<div class='rio'>RIO - #210</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<div class='boo'>BOO - #B00</div>
|
||||||
|
<div class='boloss'>BOLOSS - #B01055</div>
|
||||||
|
<div class='badass'>BADASS - #BADA55</div>
|
||||||
|
|
||||||
|
<script src='./build/trie.js'></script>
|
||||||
|
<script src='./build/index.js'></script>
|
17
package.json
Normal file
17
package.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"name": "autocomplete",
|
||||||
|
"version": "1.0.0",
|
||||||
|
"description": "",
|
||||||
|
"main": "index.js",
|
||||||
|
"scripts": {
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1"
|
||||||
|
},
|
||||||
|
"author": "",
|
||||||
|
"license": "MIT",
|
||||||
|
"dependencies": {
|
||||||
|
"grunt": "^0.4.5",
|
||||||
|
"grunt-babel": "^5.0.1",
|
||||||
|
"grunt-contrib-copy": "^0.8.0",
|
||||||
|
"grunt-contrib-watch": "^0.6.1"
|
||||||
|
}
|
||||||
|
}
|
34
src/index.js
Normal file
34
src/index.js
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
const trie = new Trie();
|
||||||
|
|
||||||
|
|
||||||
|
const divs = document.querySelectorAll('div');
|
||||||
|
const colors = {};
|
||||||
|
|
||||||
|
for (let div of divs) {
|
||||||
|
colors[div.className] = div;
|
||||||
|
|
||||||
|
trie.add(div.className);
|
||||||
|
}
|
||||||
|
|
||||||
|
const input = document.querySelector('input');
|
||||||
|
|
||||||
|
input.addEventListener('keyup', () => {
|
||||||
|
divs.hide();
|
||||||
|
|
||||||
|
const nodes = trie.all(input.value);
|
||||||
|
|
||||||
|
if (!nodes) return;
|
||||||
|
|
||||||
|
for (let node of nodes) {
|
||||||
|
const color = colors[node.value];
|
||||||
|
if (!color) continue;
|
||||||
|
|
||||||
|
color.classList.remove('hidden');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
NodeList.prototype.hide = function() {
|
||||||
|
for (let node of this) {
|
||||||
|
node.classList.add('hidden');
|
||||||
|
}
|
||||||
|
};
|
5
src/libs/d3.js
vendored
Normal file
5
src/libs/d3.js
vendored
Normal file
File diff suppressed because one or more lines are too long
54
src/trie.js
Normal file
54
src/trie.js
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
class Node {
|
||||||
|
constructor(value = '', parent) {
|
||||||
|
this.name = value;
|
||||||
|
this.children = [];
|
||||||
|
this.parent = parent;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class Trie {
|
||||||
|
constructor() {
|
||||||
|
this.root = new Node();
|
||||||
|
}
|
||||||
|
|
||||||
|
add(value, parent = this.root) {
|
||||||
|
for (let i = 0, len = value.length; i < len; i++) {
|
||||||
|
if (!parent.children) parent.children = [];
|
||||||
|
let node = parent.children.find(child => child.name[i] === value[i]);
|
||||||
|
|
||||||
|
if (!node) {
|
||||||
|
node = new Node(value.slice(0, i + 1), parent.name);
|
||||||
|
parent.children.push(node);
|
||||||
|
}
|
||||||
|
|
||||||
|
parent = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
find(value, parent = this.root) {
|
||||||
|
for (let i = 0, len = value.length; i < len; i++) {
|
||||||
|
parent = parent.children.find(child => child.name[i] === value[i]);
|
||||||
|
|
||||||
|
if (!parent) return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return parent;
|
||||||
|
}
|
||||||
|
|
||||||
|
all(search) {
|
||||||
|
let node = this.find(search);
|
||||||
|
|
||||||
|
if (!node) return null;
|
||||||
|
|
||||||
|
let all = [node];
|
||||||
|
|
||||||
|
node.children.forEach(function addToAll(child) {
|
||||||
|
all.push(child);
|
||||||
|
child.children.forEach(addToAll);
|
||||||
|
});
|
||||||
|
|
||||||
|
return all;
|
||||||
|
}
|
||||||
|
}
|
58
style.css
Normal file
58
style.css
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
div {
|
||||||
|
width: 100px;
|
||||||
|
height: 70px;
|
||||||
|
|
||||||
|
box-sizing: border-box;
|
||||||
|
padding-top: 23px;
|
||||||
|
text-align: center;
|
||||||
|
color: white;
|
||||||
|
|
||||||
|
font-size: 13px;
|
||||||
|
font-family: sans-serif;
|
||||||
|
font-weight: bold;
|
||||||
|
|
||||||
|
display: inline-block;
|
||||||
|
float: left;
|
||||||
|
|
||||||
|
margin-top: 15px;
|
||||||
|
|
||||||
|
text-shadow: 0 0 1px black;
|
||||||
|
}
|
||||||
|
|
||||||
|
br {
|
||||||
|
clear: both;
|
||||||
|
}
|
||||||
|
|
||||||
|
.hidden {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.colors {
|
||||||
|
background: #c01025;
|
||||||
|
}
|
||||||
|
.coffee {
|
||||||
|
background: #c0ffee;
|
||||||
|
}
|
||||||
|
.codecs {
|
||||||
|
background: #c0d3c5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.rebecca {
|
||||||
|
background: #663399;
|
||||||
|
}
|
||||||
|
.robots {
|
||||||
|
background: #208075;
|
||||||
|
}
|
||||||
|
.rio {
|
||||||
|
background: #210;
|
||||||
|
}
|
||||||
|
|
||||||
|
.boo {
|
||||||
|
background: #b00;
|
||||||
|
}
|
||||||
|
.boloss {
|
||||||
|
background: #b01055;
|
||||||
|
}
|
||||||
|
.badass {
|
||||||
|
background: #bada55;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user