Last active
December 27, 2015 10:39
-
-
Save noblethrasher/7313193 to your computer and use it in GitHub Desktop.
Yet another JavaScript library...
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
window.addHandler = window.addEventListener || window.attachEvent; | |
window.removeHandler = window.removeEventListener || window.detachEvent; | |
Object.prototype.isArray = function () | |
{ | |
return this.hasOwnProperty('length') && this.pop != null && this.push != null && this.join != null; | |
} | |
var Random = new (function () | |
{ | |
var get_upper = function (rnd) | |
{ | |
return String.fromCharCode((Math.random() * 26) + 65) | |
} | |
var get_lower = function () | |
{ | |
return String.fromCharCode((Math.random() * 26) + 96) | |
} | |
var get_number = function () | |
{ | |
return String.fromCharCode((Math.random() * 10) + 48) | |
} | |
var fns = [get_upper, get_lower, get_number]; | |
this.getRandomString = function (n) | |
{ | |
if (n == null) | |
n = 8; | |
var chars = new Array(); | |
for (var i = 0; i < n; i++) | |
chars.push(fns[Math.floor(Math.random() * 3)]()); | |
return chars.join(''); | |
} | |
}); | |
function Xhr(url, success, failure) | |
{ | |
var xhr = new XMLHttpRequest(); | |
this.responseType = 'json'; | |
var that = this; | |
xhr.onreadystatechange = function () | |
{ | |
if (success != null) | |
{ | |
if (xhr.readyState == 4 && xhr.statusText == 'OK') | |
{ | |
var data = null; | |
switch (that.responseType) | |
{ | |
case 'json': | |
case 'application/json': | |
case 'text/json': | |
case 'application/javascript': | |
{ | |
eval('data = ' + xhr.responseText); | |
break; | |
} | |
case 'text': | |
case 'text/html': | |
{ | |
data = xhr.responseText; | |
break; | |
} | |
case 'xml': | |
case 'text/xml': | |
case 'application/xml': | |
case 'application/xhtml+xml': | |
{ | |
data = xhr.responseXML; | |
break; | |
} | |
default: | |
{ | |
data = xhr.responseText; | |
break; | |
} | |
} | |
success(data); | |
} | |
} | |
} | |
this.exec = function (method) | |
{ | |
var args = new Array(); | |
for (var p in this) | |
{ | |
if (p == 'exec' || p == 'responseType') | |
continue; | |
if (this.hasOwnProperty(p) && this[p] != null) | |
args.push(p + '=' + encodeURIComponent(this[p])); | |
} | |
var query = null; | |
if (args.length > 0) | |
{ | |
if (method == null) | |
method = 'POST'; | |
query = args.join('&'); | |
xhr.open(method, url); | |
xhr.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); | |
console.log(query); | |
} | |
else | |
{ | |
if (method == null) | |
method = 'GET'; | |
xhr.open(method, url); | |
} | |
xhr.responseType = this.responseType; | |
xhr.send(query); | |
} | |
} | |
if (window.attachEvent) | |
{ | |
HTMLElement.prototype.addHandler = function (event, fn) | |
{ | |
if (event.substring(0, 2).toLowerCase() != 'on') | |
event = 'on' + event; | |
this.attachEvent(event, fn); | |
} | |
HTMLElement.prototype.removeHandler = function (event, fn) | |
{ | |
if (event.substring(0, 2).toLowerCase() != 'on') | |
event = 'on' + event; | |
this.detachEvent(event, fn); | |
} | |
} | |
if (window.addEventListener) | |
{ | |
HTMLElement.prototype.addHandler = function (event, fn, capture) | |
{ | |
if (event.substring(0, 2).toLowerCase() == 'on') | |
event = event.substring(2); | |
this.addEventListener(event, fn, capture); | |
} | |
HTMLElement.prototype.removeHandler = function (event, fn, capture) | |
{ | |
if (event.substring(0, 2).toLowerCase() == 'on') | |
event = event.substring(2); | |
this.removeEventListener(event, fn, capture); | |
} | |
} | |
//credit and thanks to http://stackoverflow.com/a/3471664/3927 | |
HTMLElement.prototype.getOffset = function findPos() | |
{ | |
var obj = this; | |
var obj2 = obj; | |
var curtop = 0; | |
var curleft = 0; | |
if (document.getElementById || document.all) | |
{ | |
do | |
{ | |
curleft += obj.offsetLeft - obj.scrollLeft; | |
curtop += obj.offsetTop - obj.scrollTop; | |
obj = obj.offsetParent; | |
obj2 = obj2.parentNode; | |
while (obj2 != obj) | |
{ | |
curleft -= obj2.scrollLeft; | |
curtop -= obj2.scrollTop; | |
obj2 = obj2.parentNode; | |
} | |
} while (obj.offsetParent) | |
} else if (document.layers) | |
{ | |
curtop += obj.y; | |
curleft += obj.x; | |
} | |
return { x: curleft, y: curtop }; | |
} | |
function find(id) | |
{ | |
return document.getElementById(id); | |
} | |
function make(tag) | |
{ | |
return document.createElement(tag); | |
} | |
function autoSuggest(input, src) | |
{ | |
if (typeof (input) == 'string') | |
input = document.getElementById(input); | |
if (typeof (src) == 'string') | |
{ | |
var url = src; | |
src = function () { return url; } | |
} | |
if (typeof (src) != 'function' || typeof(src()) != 'string') | |
throw "'src' parameter must either be a string or a function that returns a string"; | |
if (input == null) | |
throw "No input element specified"; | |
//we need a way to determine whether the newly focused element should keep the suggest list open whenever either the input or any of the (dynamically added) suggested items loses focus | |
//if the newly focused element has the secret_context_key (of if it's the input element), we keep the suggest list open, otherwise we close it | |
var secret_context_key = Random.getRandomString(8); | |
var input_container = make('span'); | |
input.parentNode.replaceChild(input_container, input); | |
input_container.appendChild(input); | |
input.addHandler('keydown', function (e) | |
{ | |
if (e.which == 40) //down key | |
{ | |
var anchors = container.getElementsByTagName('a'); | |
if (anchors.length > 0) | |
anchors[0].focus(); | |
} | |
if (e.which == 27) //escape key | |
{ | |
var list = container.getElementsByTagName('ul'); | |
if (list.length > 0) | |
container.removeChild(list[0]); | |
} | |
}); | |
var container = make('div'); //the container for our autosuggest list | |
container.style.position = 'absolute'; | |
container.style.marginTop = '1px'; | |
container.className = '___autosuggest___'; | |
var style = make('style'); | |
var rules = document.createTextNode('.___autosuggest___ ul { margin: 0; padding:0; list-style-type:none;'); | |
if (style.styleSheet) | |
style.styleSheet.cssText = rules.nodeValue; | |
else | |
style.appendChild(rules); | |
document.getElementsByTagName('head')[0].appendChild(style); | |
function clear_container() | |
{ | |
var list = container.getElementsByTagName('ul'); | |
if (list.length > 0) | |
container.removeChild(list[0]); | |
} | |
function compute_container_offset() | |
{ | |
var offset = input.getOffset(); | |
container.style.top = offset.y + input.offsetHeight + 'px'; | |
container.style.left = offset.x + 'px'; | |
console.log(offset); | |
} | |
compute_container_offset(); | |
document.body.appendChild(container); | |
if (window.addEventListener) | |
{ | |
window.addEventListener('resize', compute_container_offset); | |
} | |
else | |
{ | |
if (window.attachEvent) | |
{ | |
window.attachEvent('onresize', compute_container_offset); | |
} | |
} | |
function handle_results(data) | |
{ | |
var existing = container.getElementsByTagName('ul'); | |
if (existing.length > 0) | |
container.removeChild(existing[0]); | |
var ul = make('ul'); | |
var array = null; | |
if (!data.isArray()) | |
array = [data]; | |
else | |
array = data; | |
var len = array.length; | |
var anchors = new Array(); | |
var navigation = function (n) | |
{ | |
return function (e) | |
{ | |
if (e.which == 40) | |
{ | |
anchors[(n + 1) % len].focus(); | |
} | |
if (e.which == 38) | |
{ | |
if (n == 0) | |
input.focus(); | |
else | |
anchors[n - 1].focus(); | |
} | |
if (e.which == 27) | |
{ | |
container.removeChild(ul); | |
} | |
} | |
} | |
if (len > 0) | |
{ | |
for (var i = 0; i < len; i++) | |
{ | |
var obj = array[i]; | |
var a = make('a'); | |
var li = make('li'); | |
anchors.push(a); | |
a.href = '#'; | |
a[secret_context_key] = true; | |
a.innerHTML = array[i].fullname + (new Date()); | |
li.appendChild(a); | |
ul.appendChild(li); | |
a.addHandler('keydown', navigation(i)); | |
a.addHandler('click', function (e) | |
{ | |
clear_container(); | |
e.stopPropagation(); | |
}); | |
var kill_container_on_blur = function() | |
{ | |
if(!document.activeElement[secret_context_key] && document.activeElement != input) | |
{ | |
console.log(document.activeElement); | |
clear_container(); | |
} | |
}; | |
a.addHandler('blur', function (e) | |
{ | |
setTimeout(kill_container_on_blur, 10); | |
}); | |
} | |
container.appendChild(ul); | |
} | |
} | |
function create_poll() | |
{ | |
var value = null; | |
return function () | |
{ | |
var d; | |
if (input.value.length < 3) | |
{ | |
var list = container.getElementsByTagName('ul'); | |
if(list.length > 0) | |
container.removeChild(list[0]); | |
return; | |
} | |
if (value == null || input.value != value) | |
{ | |
value = input.value; | |
new Xhr(src(), function (data) | |
{ | |
console.log(src()); | |
if (typeof (data) != 'array') | |
{ | |
handle_results(data); | |
} | |
}).exec(); | |
} | |
} | |
} | |
input.addHandler('focus', function (e) | |
{ | |
var poll = create_poll(); | |
console.log('start polling...'); | |
poll(); | |
var interval_id = window.setInterval(poll, 500); | |
var on_blur = function (e) | |
{ | |
window.clearInterval(interval_id); | |
input.removeHandler('blur', on_blur); | |
setTimeout(function () | |
{ | |
var active = document.activeElement; | |
if (!active[secret_context_key]) | |
{ | |
clear_container(); | |
} | |
}, 20); | |
} | |
input.addHandler('blur', on_blur); | |
}); | |
var all_children = document.body.getElementsByTagName('*'); | |
var count = all_children.length; | |
var seen_input = false; | |
for (var i = 0; i < count; i++) | |
{ | |
if (seen_input) | |
{ | |
//we want to ensure that the dynamically added controls are next in the tab order after the input element | |
all_children[i].tabIndex = all_children[i].tabIndex + 50; | |
continue; | |
} | |
if (all_children[i] == input) | |
seen_input = true; | |
} | |
} | |
function make_draggable(elem, init_drag, drop_fn) | |
{ | |
elem.style.cursor = 'move'; | |
function mouse_down(e) | |
{ | |
var pos = { x: null, y: null } | |
if (init_drag) | |
init_drag(); | |
var offset = elem.getOffset(); | |
var delta_x = e.clientX - offset.x; | |
var delta_y = e.clientY - offset.y; | |
elem.style.position = 'absolute'; | |
var old_pos = { x: null, y: null }; | |
function drag_state() | |
{ | |
if (pos.x == null) | |
return; | |
if (old_pos.x != pos.x || old_pos.y != pos.y) | |
{ | |
old_pos.x = pos.x; | |
old_pos.y = pos.y; | |
elem.style.top = (pos.y - delta_y) + 'px'; | |
elem.style.left = (pos.x - delta_x) + 'px'; | |
} | |
} | |
function mouse_up(e) | |
{ | |
window.removeHandler('mouseup', mouse_up); | |
window.removeHandler('mousemove', mouse_move); | |
if (drop_fn) | |
drop_fn(e); | |
} | |
function mouse_move(e) | |
{ | |
e = e || window.event; | |
pos.x = e.clientX; | |
pos.y = e.clientY; | |
elem.style.top = (pos.y - delta_y) + 'px'; | |
elem.style.left = (pos.x - delta_x) + 'px'; | |
} | |
window.addHandler('mousemove', mouse_move); | |
window.addHandler('mouseup', mouse_up); | |
} | |
elem.addHandler('mousedown', mouse_down); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment