From 4383d9daa8cfde1544a95bc340a00626154e3556 Mon Sep 17 00:00:00 2001 From: Vitaliy Filippov Date: Tue, 5 Mar 2013 16:26:55 +0000 Subject: [PATCH] Support disabled items, rework #MORE and prompt --- hinter.css | 10 ++++++ hinter.js | 102 +++++++++++++++++++++++++++++------------------------ 2 files changed, 66 insertions(+), 46 deletions(-) diff --git a/hinter.css b/hinter.css index 9abe905..ea134fc 100644 --- a/hinter.css +++ b/hinter.css @@ -11,6 +11,11 @@ .hintEmptyText { padding: 3px; } +.hintPrompt { + padding: 3px; + color: gray; + font-style: italic; +} .hintItem { color: black; cursor: pointer; @@ -18,6 +23,11 @@ vertical-align: middle; white-space: nowrap; } +.hintDisabledItem { + color: gray; + padding: 1px 3px; + white-space: nowrap; +} .hintActiveItem { color: white; background-color: #008; diff --git a/hinter.js b/hinter.js index edcd31b..289db72 100644 --- a/hinter.js +++ b/hinter.js @@ -12,10 +12,10 @@ input The input, either id or DOM element reference (the input must have an id anyway). dataLoader(hint, value[, more]) - Callback which should load autocomplete options and then call - hint.replaceItems(newOptions, keepPosition), where: - newOptions is [ [ name, value ], [ name, value ], ... ] and - keepPosition should be true when more was > 0. + Callback which should load autocomplete options and then call: + hint.replaceItems(newOptions, append) + newOptions is [ [ name, value[, disabled ] ], [ name, value ], ... ] + append=(more>0) Callback parameters: hint This SimpleAutocomplete object @@ -46,9 +46,8 @@ If emptyText === false, the hint will be hidden instead of showing text. allowHTML If true, HTML code will be allowed in option names. - promptHTML - The HTML code to be displayed before the option list ("input prompt"). - Empty by default. + prompt + HTML text to be displayed before a non-empty option list. Empty by default. delay If this is set to a non-zero value, the autocompleter does no more than 1 request in each delay milliseconds. @@ -60,6 +59,10 @@ at the end of the list, and when it will be clicked, SimpleAutocomplete will issue another request to dataLoader with incremented 'more' parameter. You can also set moreMarker to false to disable this feature. + + Other methods: + hint.remove() -- Destroy the instance + hint.enableItem(index, enabled) -- Enable or disable an item */ // *** Constructor *** @@ -79,7 +82,7 @@ var SimpleAutocomplete = function(input, dataLoader, params) this.maxHeight = params.maxHeight; this.emptyText = params.emptyText; this.allowHTML = params.allowHTML; - this.promptHTML = params.promptHTML || ''; + this.prompt = params.prompt; this.delay = params.delay; this.moreMarker = params.moreMarker; @@ -89,11 +92,10 @@ var SimpleAutocomplete = function(input, dataLoader, params) if (this.delay === undefined) this.delay = 300; if (this.emptyText === undefined) - this.emptyText = 'No items found'; + this.emptyText = false; // Variables this.origAutocomplete = input.autocomplete; - this.isMoreClick = false; this.more = 0; this.timer = null; this.closure = []; @@ -158,30 +160,24 @@ SimpleAutocomplete.prototype.init = function() this.onChange(); }; -// obj = [ [ name, value, disabled ], [ name, value ], ... ] -SimpleAutocomplete.prototype.replaceItems = function(items, keepPosition) +// items = [ [ name, value[, disabled] ], [ name, value ], ... ] +SimpleAutocomplete.prototype.replaceItems = function(items, append) { - this.hintLayer.innerHTML = ''; - if (!keepPosition) + if (!append) { this.hintLayer.scrollTop = 0; - } - this.items = []; - if (!items || items.length == 0) - { - if (this.emptyText) + this.items = []; + if (!items || items.length == 0) { - var d = document.createElement('div'); - d.className = 'hintEmptyText'; - d.innerHTML = this.emptyText; - this.hintLayer.appendChild(d); + if (this.emptyText) + this.hintLayer.innerHTML = '
'+this.emptyText+'
'; + else + this.disable(); + return; } - else - this.disable(); - return; + this.hintLayer.innerHTML = this.prompt ? '
'+this.prompt+'
' : ''; + this.enable(); } - this.hintLayer.innerHTML = this.promptHTML; - this.enable(); var h = {}; if (this.multipleDelimiter) { @@ -190,7 +186,7 @@ SimpleAutocomplete.prototype.replaceItems = function(items, keepPosition) h[old[i].trim()] = true; } for (var i in items) - this.hintLayer.appendChild(this.makeItem(items[i][0], items[i][1], h[items[i][1]])); + this.hintLayer.appendChild(this.makeItem(items[i][0], items[i][1], items[i][2], h[items[i][1]])); if (this.maxHeight) { this.hintLayer.style.height = @@ -230,12 +226,24 @@ SimpleAutocomplete.prototype.remove = function() this.items = null; }; +// Enable or disable an item +SimpleAutocomplete.prototype.enableItem = function(index, enabled) +{ + if (this.items && this.items[index]) + { + this.items[index][2] = enabled; + document.getElementById(this.id+'_item_'+index).style = enabled ? 'hintItem' : 'hintDisabledItem'; + if (this.multipleDelimiter) + document.getElementById(this.id+'_check_'+index).disabled = !enabled; + } +}; + // Create a drop-down list item, include checkbox if this.multipleDelimiter is true -SimpleAutocomplete.prototype.makeItem = function(name, value, checked) +SimpleAutocomplete.prototype.makeItem = function(name, value, disabled, checked) { var d = document.createElement('div'); d.id = this.id+'_item_'+this.items.length; - d.className = 'hintItem'; + d.className = disabled ? 'hintDisabledItem' : 'hintItem'; d.title = value; if (this.allowHTML) d.innerHTML = name; @@ -247,6 +255,7 @@ SimpleAutocomplete.prototype.makeItem = function(name, value, checked) c.type = 'checkbox'; c.id = this.id+'_check_'+this.items.length; c.checked = checked && true; + c.disabled = disabled && true; c.value = value; if (d.childNodes.length) d.insertBefore(c, d.firstChild); @@ -257,7 +266,7 @@ SimpleAutocomplete.prototype.makeItem = function(name, value, checked) var self = this; addListener(d, 'mouseover', function() { return self.onItemMouseOver(this); }); addListener(d, 'mousedown', function(ev) { return self.onItemClick(ev, this); }); - this.items.push([name, value, checked]); + this.items.push([name, value, disabled, checked]); return d; }; @@ -276,13 +285,16 @@ SimpleAutocomplete.prototype.moveHighlight = function(by) // Make item 'elem' active (highlighted) SimpleAutocomplete.prototype.highlightItem = function(elem) { + var ni = parseInt(elem.id.substr(this.id.length+6)); + if (this.items[ni][2]) + return false; if (this.selectedIndex >= 0) { var c = this.getItem(); if (c) c.className = 'hintItem'; } - this.selectedIndex = parseInt(elem.id.substr(this.id.length+6)); + this.selectedIndex = ni; elem.className = 'hintActiveItem'; return false; }; @@ -307,12 +319,12 @@ SimpleAutocomplete.prototype.selectItem = function(index) } else { - document.getElementById(this.id+'_check_'+index).checked = this.items[index][2] = !this.items[index][2]; + document.getElementById(this.id+'_check_'+index).checked = this.items[index][3] = !this.items[index][3]; var old = this.input.value.split(this.multipleDelimiter); for (var i = 0; i < old.length; i++) old[i] = old[i].trim(); // Turn the clicked item on or off, preserving order - if (!this.items[index][2]) + if (!this.items[index][3]) { for (var i = old.length-1; i >= 0; i--) if (old[i] == this.items[index][1]) @@ -323,7 +335,7 @@ SimpleAutocomplete.prototype.selectItem = function(index) { var h = {}; for (var i = 0; i < this.items.length; i++) - if (this.items[i][2]) + if (this.items[i][3]) h[this.items[i][1]] = true; var nl = []; for (var i = 0; i < old.length; i++) @@ -335,7 +347,7 @@ SimpleAutocomplete.prototype.selectItem = function(index) } } for (var i = 0; i < this.items.length; i++) - if (this.items[i][2] && h[this.items[i][1]]) + if (this.items[i][3] && h[this.items[i][1]]) nl.push(this.items[i][1]); this.input.value = nl.join(this.multipleDelimiter+' '); } @@ -417,10 +429,10 @@ SimpleAutocomplete.prototype.onItemClick = function(ev, elm) if (this.moreMarker && this.items[index][1] == this.moreMarker) { // User clicked 'more'. Load more items without delay. - this.isMoreClick = true; + this.items.splice(index, 1); + elm.parentNode.removeChild(elm); this.more++; - this.onChange(); - this.isMoreClick = false; + this.onChange(true); return true; } this.selectItem(index); @@ -428,17 +440,15 @@ SimpleAutocomplete.prototype.onItemClick = function(ev, elm) }; // Handle user input, load new items -SimpleAutocomplete.prototype.onChange = function() +SimpleAutocomplete.prototype.onChange = function(force) { var v = this.input.value.trim(); - if (!this.isMoreClick) - { + if (!force) this.more = 0; - } - if (v != this.curValue || this.isMoreClick) + if (v != this.curValue || force) { this.curValue = v; - if (!this.delay || this.isMoreClick) + if (!this.delay || force) this.dataLoader(this, v, this.more); else if (!this.timer) {