nf.UI = {
Pager: function(owner) {
this.owner = owner;
this.current = 1;
this.total = 1;
},
Actions: function(owner) {
this.owner = owner || window;
this.items = {};
this.rootElement = null;
this.searchField = null;
},
Labels: {
create: 'Create',
createAll: 'Create All',
search: 'Search',
accept: 'Accept',
unpair: 'Unpair',
cancel: 'Cancel',
acceptAll: 'Accept all suggestions',
goToFirst: String.fromCharCode(0xAB), // looks like <<
goToLast: String.fromCharCode(0xBB), // looks like >>
goToPrevious: String.fromCharCode(0x2039), // looks like <
goToNext: String.fromCharCode(0x203A), // looks like >
of: 'of'
},
Tests: {
email: function(a, b) {
// direct comparison (case sensitive)
if(a == b) {
return 1;
}
// go case-insensitive
var x = (a || '').toLowerCase(),
y = (b || '').toLowerCase();
// direct comparision
if(x == y) {
return .95;
}
// compare without domain suffixes
if(x.replace(arguments.callee.variation, '') ==
y.replace(arguments.callee.variation, '')) {
return .85;
}
// use deprioritised string comparison
return nf.UI.Tests.string(a, b) / 3;
},
fullName: function(a, b) {
a = (a || '').trim();
b = (b || '').trim();
// direct comparison
if(a == b) {
return 1;
}
else if(a.toLowerCase() == b.toLowerCase()) {
return .9;
}
// go to word by word
var p = [a, b].map(function(x) {
return x
.trim()
.split(/[^a-z]+/i)
.filter(function(y) {
return y && nf.UI.Tests.fullName.salutations.indexOf(y.toLowerCase()) == -1;
});
});
var score = 0, i, j, k = p[0].length, l = p[1].length, x, y;
for(i = 0; i < k; i++) {
for(j = 0; j < l; j++) {
x = p[0][i]; // word from a
y = p[1][j]; // word from b
// whole word match (with case):
if(x == y) {
score += 1;
}
// go case insensitive
else {
x = x.toLowerCase();
y = y.toLowerCase();
// whole word match:
if(x == y) {
score += .8;
}
// shortened version (eg Rob for Robert or J for John):
else if(x.indexOf(y) == 0 || y.indexOf(x) == 0) {
score += .5;
}
// contained (eg Liz for Elizabeth):
else if(x.indexOf(y) >= 0 || y.indexOf(x) >= 0) {
score += .3;
}
// first letter (eg Jim for James):
else if(x.substr(0, 1) == y.substr(0, 1)) {
score += .1;
}
}
}
}
// max score will be 0.8. normalise to highest word count.
return .8 * score / Math.max(p[0].length, p[1].length);
},
string: function(a, b) {
if(a == b) {
return 1;
}
a = a || '';
b = b || '';
var i, x, y;
if(a.length > b.length) {
x = a.toLowerCase().trim();
y = b.toLowerCase().trim();
} else {
x = b.toLowerCase().trim();
y = a.toLowerCase().trim();
}
if(x === '' || y === '') {
return 0;
}
if(x.indexOf(y) != -1) {
return .95 * y.length / x.length;
}
return 0;
},
amount: function(a, b) {
if(a == b) {
return 1;
}
var x = parseFloat((a || 0).toString().replace(/[^-.\d]+/g, '')),
y = parseFloat((b || 0).toString().replace(/[^-.\d]+/g, ''));
if(isNaN(x) || isNaN(y)) {
return 0;
}
if(x == y) {
return 1;
}
if(x == 0 || y == 0) {
return 0;
}
return (x > y ? y / x : x / y) * 1.8 - 1;
}
}
};
nf.UI.Pager.prototype = {
goTo: function(dest) {
this.current = Math.max(Math.min(this.total, dest), 1);
this.update();
this.owner.update();
},
update: function(totalItems, itemsPerPage) {
if(typeof totalItems == 'number' && typeof itemsPerPage == 'number') {
this.total = Math.max(1, Math.ceil(totalItems / itemsPerPage));
this.current = Math.max(Math.min(this.current, this.total), 1);
}
this.currentElement.innerHTML = this.current.toString();
this.totalElement.innerHTML = this.total.toString();
nf.className(this.rootElement, 'bof', this.current == 1 ? 1 : -1);
nf.className(this.rootElement, 'eof', this.current == this.total ? 1 : -1);
},
init: function() {
var l = nf.UI.Labels, pager = this;
nf('<a>', this.rootElement, {
className: 'first',
href: 'javascript:',
innerHTML: l.goToFirst.toHTML(),
onclick: function() {
pager.goTo(1)
}
});
nf('<', this.rootElement, ' ');
nf('<a>', this.rootElement, {
className: 'previous',
href: 'javascript:',
innerHTML: l.goToPrevious.toHTML(),
onclick: function() {
pager.goTo(pager.current - 1)
}
});
nf('<', this.rootElement, ' ');
this.currentElement = nf('<span>', this.rootElement, {className: 'current'});
nf('<', this.rootElement, ' ');
nf('<span>', this.rootElement, {className: 'of', innerHTML: l.of.toHTML()});
nf('<', this.rootElement, ' ');
this.totalElement = nf('<span>', this.rootElement, {className: 'total'});
nf('<', this.rootElement, ' ');
nf('<a>', this.rootElement, {
className: 'next',
href: 'javascript:',
innerHTML: l.goToNext.toHTML(),
onclick: function() {
pager.goTo(pager.current + 1)
}
});
nf('<', this.rootElement, ' ');
nf('<a>', this.rootElement, {
className: 'last',
href: 'javascript:',
innerHTML: l.goToLast.toHTML(),
onclick: function() {
pager.goTo(pager.total)
}
});
this.update();
}
};
nf.UI.Actions.prototype = {
add: function(id, label, callback) {
if(!(id in this.items)) {
this.items[id] = [label, callback];
this.redraw();
}
},
remove: function(id) {
if(id in this.items) {
delete this.items[id];
this.redraw();
}
},
redraw: function() {
if(!this.rootElement) {
return;
}
this.rootElement.innerHTML = '';
var i = null;
for(i in this.items) {
nf('<a>', nf('<li>', this.rootElement, {className: i}), {
href: 'javascript:',
innerHTML: this.items[i][0].toHTML(),
onclick: (function(c, that) {
return function() {
c.call(that)
}
})(this.items[i][1], this.owner)
});
}
if(i) {
nf.className(this.rootElement.firstChild, 'first', 1);
nf.className(this.rootElement.childNodes[this.rootElement.childNodes.length - 1], 'last', 1);
}
this.redrawSearch();
},
redrawSearch: function() {
var searchValue = this.searchField ? this.searchField.value : '',
id, li, l, o;
if(this.owner.searchable) {
l = nf.UI.Labels.search;
li = nf('<li>', this.rootElement, {className: 'last search'});
if(this.rootElement.childNodes.length == 1) {
nf.className(li, 'first', 1);
}
else {
nf.className(li.previousSibling, 'last', -1);
}
id = this.owner.appId + 'Search';
nf('<label>', li, {
innerHTML: l.toHTML(),
htmlFor: id
});
o = this.owner;
this.searchField = nf('<input>', li, {
placeholder: l,
title: l,
type: 'text',
id: id,
value: searchValue,
onkeyup: function() {
o.update()
}
});
}
}
};
// list view class nf.ListView = function(rootElement) {
this.rootElement = rootElement; this.clear();
};
// list view class nf.ListView.prototype = {
addItem: function() {
var ret = nf('<li>');
this.rootElement.appendChild(ret);
this.refreshClasses();
return ret;
},
activateItem: function(item) {
if(typeof item == 'number') {
item = this.rootElement.childNodes[item];
}
var i = this.rootElement.childNodes.length;
while(i--) {
nf.className(
this.rootElement.childNodes[i],
'active',
this.rootElement.childNodes[i] === item ? 1 : -1
);
}
},
removeItem: function(item) {
if(typeof item == 'number') {
item = this.rootElement.childNodes[item];
}
this.rootElement.removeChild(item);
this.refreshClasses();
},
refreshClasses: function() {
var c = this.rootElement.childNodes,
i = c.length;
while(i--) {
nf.className(c[i], 'first', !i ? 1 : -1);
nf.className(c[i], 'last', i == c.length - 1 ? 1 : -1);
}
},
clear: function() {
if(this.rootElement) {
this.rootElement.innerHTML = '';
}
}
};
nf.UI.Tests.fullName.salutations = “mr mrs ms dr prof”.split(' ');
nf.UI.Tests.email.variation = /.(com|net|org|gov|edu|{1,2})b.*|.+$/i;