Alien-GvaScript
view release on metacpan or search on metacpan
src/autoCompleter.js view on Meta::CPAN
});
},
_updateChoicesFromCallback : function(val_to_complete, continuation) {
continuation(this._datasource(val_to_complete));
},
_updateChoicesFromJSONP : function(val_to_complete, continuation) {
if(val_to_complete) {
var _url = this._datasource.json_url.replace(/\?1/, val_to_complete).replace(/\?2/, '?');
var that = this;
Element.addClassName(that.inputElement, that.classes.loading);
Prototype.getJSON(_url, function(data) {
var _data_list = data;
if(that._datasource.json_list)
that._datasource.json_list.split('/').each(function(p) {
_data_list = _data_list[p];
});
Element.removeClassName(that.inputElement, that.classes.loading);
continuation(_data_list);
});
}
},
_updateChoicesFromArray : function(val_to_complete, continuation) {
if (this.options.ignorePrefix) {
// store the index of the initial value
if (val_to_complete) {
this._idx_to_hilite = (val_to_complete == ''? 0 : -1);
$A(this._datasource).each(function(choice, index) {
switch(typeof choice) {
case "object" : value = choice[this.options.valueField]; break;
case "number" : value = choice.toString(10); break;
case "string" : value = choice; break;
default: throw new Error("unexpected type of value");
}
if(value.toLowerCase().startsWith(val_to_complete.toLowerCase())) {
this._idx_to_hilite = index;
throw $break;
}
}, this);
}
continuation(this._datasource);
}
else {
var regex = new RegExp("^" + RegExp.escape(val_to_complete),
this.options.caseSensitive ? "" : "i");
var matchPrefix = function(choice) {
var value;
switch(typeof choice) {
case "object" : value = choice[this.options.valueField]; break;
case "number" : value = choice.toString(10); break;
case "string" : value = choice; break;
default: throw new Error("unexpected type of value");
}
return value.search(regex) > -1;
};
continuation(this._datasource.select(matchPrefix.bind(this)));
}
},
_updateChoices : function (continuation) {
var value = this._getValueToComplete();
// if (window.console) console.log('updateChoices', value);
this._updateChoicesHandler(value, continuation);
},
// does the reverse of "autocomplete()"
// doesnot fire if input blurred from click on choice list
_blurHandler: function(event) {
// remove choice list
if (this.dropdownDiv) this._removeDropdownDiv();
// xhr is still active: waiting for response from server
if (_xhr = this._runningAjax[this.inputElement.name]) {
// if autocompleter is strict, need to wait for xhr to
// finish before calling the _blurHandler to fire the
// autocompleter's finalState
if (this.options.strict) {
_xhr['blurAfterSuccess'] = true;
return;
}
_xhr.transport.abort();
_xhr = null;
Element.removeClassName(this.inputElement, this.classes.loading);
}
// if strict mode, inform client about the final status
if (this.options.strict) {
var value = this._getValueToComplete();
// if value has changed, invalidate previous list of choices
if (value != this.lastValue) {
this.choices = null;
}
// if blank and blankOK, this is a legal value
if (!value && this.options.blankOK) {
this._updateDependentFields(this.inputElement, "");
this.fireEvent({ type : "LegalValue",
value : "",
choice : null,
controller : null }, this.inputElement);
}
// if choices are known, just inspect status
else if (this.choices) {
this._fireFinalStatus(this.inputElement, this.choices);
}
// if not enough chars to get valid choices, this is illegal
src/autoCompleter.js view on Meta::CPAN
}
else if (this.options.strict && (!this.options.blankOK)) {
this.inputElement.style.backgroundColor = this.options.colorIllegal;
}
};
// now call updateChoices (which then will call the continuation)
this._updateChoices(continuation.bind(this));
}
},
// return the value to be completed
// TODO : for multivalued, should return the value under the cursor,
// instead returning sytematically the last value
_getValueToComplete : function(value) {
// NOTE: the explicit value as argument is only used from
//_fireFinalStatus(), when we can no longer rely on
// this.inputElement.value
value = value || this.inputElement.value;
if (this.options.multivalued) {
var vals = value.split(this.options.multivalue_separator);
value = vals[vals.length-1];
}
return value;
},
_setValue : function(value, inputElement) {
// NOTE: the explicit inputElement as argument is only used from
// _fireFinalStatus(), when we can no longer rely on this.inputElement
// default inputElement is the one bound to this autocompleter
if (!inputElement) inputElement = this.inputElement;
// if multivalued, the completed value replaces the last one in the list
if (this.options.multivalued) {
var _sep = inputElement.value.match(this.options.multivalue_separator);
if (_sep) {
var vals = inputElement.value.split(this.options.multivalue_separator);
vals[vals.length-1] = value;
value = vals.join(_sep[0]); // join all vals with first separator found
}
}
// setting value in input field
inputElement.value = this.lastValue = value;
},
_typeAhead : function () {
var curLen = this.lastTypedValue.length;
var index = this.choiceList.currentHighlightedIndex;
var suggestion = this._valueFromChoice(index);
var newLen = suggestion.length;
this._setValue(suggestion);
if (this.inputElement.createTextRange){ // MSIE
var range = this.inputElement.createTextRange();
range.moveStart("character", curLen); // no need to moveEnd
range.select(); // will call focus();
}
else if (this.inputElement.setSelectionRange){ // Mozilla
this.inputElement.setSelectionRange(curLen, newLen);
}
},
//----------------------------------------------------------------------
// methods for the dropdown list of choices
//----------------------------------------------------------------------
_mkDropdownDiv : function() {
this._removeDropdownDiv();
// the autocompleter has been blurred ->
// do not display the div
if(!this.inputElement) return null;
// if observed element for scroll, reposition
var movedUpBy = 0;
var movedLeftBy = 0;
if (this.observed_scroll) {
movedUpBy = this.observed_scroll.scrollTop;
movedLeftBy = this.observed_scroll.scrollLeft;
}
// create div
var div = new Element('div');
div.className = this.classes.dropdown;
// positioning
var coords = Position.cumulativeOffset(this.inputElement);
var dim = Element.getDimensions(this.inputElement);
div.style.left = coords[0] + this.options.offsetX - movedLeftBy + "px";
div.style.top = coords[1] + dim.height -movedUpBy + "px";
div.style.maxHeight = this.options.maxHeight + "px";
div.style.minWidth = this.options.minWidth + "px";
div.style.zIndex = 32767; //Seems to be the highest valid value
// insert into DOM
document.body.appendChild(div);
// simulate minWidth on old MSIE (must be AFTER appendChild())
// maxHeight cannot be simulated untill displayChoices
if (navigator.userAgent.match(/\bMSIE [456]\b/)) {
div.style.width = this.options.minWidth + "px";
}
// mouseenter and mouseleave events to control
// whether autocompleter has been blurred
var elem = this.inputElement;
div.observe('mouseenter', function(e) {
Element.stopObserving(elem, "blur", this.reuse.onblur);
}.bind(this));
div.observe('mouseleave', function(e) {
Element.observe(elem, "blur", this.reuse.onblur);
}.bind(this));
return this.dropdownDiv = div;
src/autoCompleter.js view on Meta::CPAN
choiceItemTagName : this.options.choiceItemTagName,
htmlWrapper : this.options.htmlWrapper
});
cl.currentHighlightedIndex = ac._idx_to_hilite;
// TODO: explain and publish method "choiceElementHTML", or redesign
// and make it a private method
if ( this.choiceElementHTML ) {
cl.choiceElementHTML = this.choiceElementHTML;
}
cl.onHighlight = function(event) {
if (ac.options.typeAhead)
ac._typeAhead();
ac.fireEvent(event, ac.inputElement);
};
cl.onPing = function(event) {
ac._completeFromChoiceElem(event.target);
};
cl.onCancel = function(event) {
ac._removeDropdownDiv();
};
// append div to DOM
var choices_div = this._mkDropdownDiv();
// fill div now so that the keymap gets initialized
cl.fillContainer(choices_div);
// set height of div for IE6 (no suppport for maxHeight!)
if (navigator.userAgent.match(/\bMSIE [456]\b/)) {
choices_div.style.height =
(choices_div.scrollHeight > this.options.maxHeight)?
this.options.maxHeight + 'px' :
'auto';
}
// determine if there is a space to dislay
// the choices list under the input
// if not, display above.
// onscreen height needed for displaying the choices list
var _h_needed = Element.viewportOffset(this.inputElement)[1]
+ this.inputElement.offsetHeight
+ choices_div.offsetHeight;
var _h_avail = document.viewport.getHeight();
// move choices list on top of the input element
if(_h_needed >= _h_avail) {
var div_top = choices_div.offsetTop
- choices_div.offsetHeight
- this.inputElement.offsetHeight;
if (div_top >= 0)
choices_div.style.top = div_top + 'px';
}
// catch keypress on TAB while choiceList has focus
cl.keymap.rules[0].TAB = cl.keymap.rules[0].S_TAB = function(event) {
var index = cl.currentHighlightedIndex;
if (index != undefined) {
var elem = cl._choiceElem(index);
// generate a "Ping" on the choiceList, like if user had
// pressed RETURN to select the current highlighted item
cl.fireEvent({type : "Ping",
index: index}, elem, cl.container);
// NO Event.stop() here, because the navigator should
// do the tabbing (pass focus to next/previous element)
}
};
// more key handlers when the suggestion list is displayed
this.keymap.rules.push(cl.keymap.rules[0]);
}
else
this.displayMessage("pas de suggestion");
},
_removeDropdownDiv: function() {
// remove the dropdownDiv that was added previously by _mkDropdownDiv();
// that div contained either a menu of choices or a message to the user
if (this.dropdownDiv) {
// remove mouseenter and mouseleave observers
this.dropdownDiv.stopObserving();
Element.remove(this.dropdownDiv);
this.dropdownDiv = null;
}
// if applicable, also remove rules previously pushed by _displayChoices
if (this.keymap.rules.length > 1)
this.keymap.rules.pop();
},
_valueFromChoice: function(index) {
if (!this.choices) return null;
var choice = this.choices[index];
return (choice !== null) ? this._valueFromChoiceItem(choice) : null;
},
_valueFromChoiceItem: function(choice) {
return (typeof choice == "string") ? choice
: choice[this.options.valueField];
},
//triggered by the onPing event on the choicelist, i.e. when the user selects
//one of the choices in the list
_completeFromChoiceElem: function(elem) {
// identify the selected line and handle it
var num = parseInt(elem.id.match(/\.(\d+)$/)[1], 10);
// add the value to the input element
var value = this._valueFromChoice(num);
if (value !== null) {
this._setValue(value)
this._removeDropdownDiv();
// ADDED LEMOINEJ 26.09.13
this._timeLastCheck = this._timeLastKeyDown = 0;
this._checkNewValue();
if (!this.options.multivalued) {
this.inputElement.select();
}
this._updateDependentFields(this.inputElement, this.choices[num]);
// fire events: "Complete" for backwards compatibility, "LegalValue"
// for regular use
var eventNames = ["Complete", "LegalValue"];
// for loop : can't use .each() from prototype.js because it alters "this"
for (var i = 0; i < eventNames.length; i++) {
this.fireEvent({
type : eventNames[i],
referrer : "select", // choice selection fired this event
index : num,
choice : this.choices[num],
controller: {choices: this.choices}
}, elem, this.inputElement);
}
}
}
}
( run in 0.537 second using v1.01-cache-2.11-cpan-119454b85a5 )