// Copyright (c) 2005-2008 Thomas Fuchs (http://script.aculo.us, http://mir.aculo.us)
// (c) 2005-2008 Ivan Krstic (http://blogs.law.harvard.edu/ivan)
// (c) 2005-2008 Jon Tirsen (http://www.tirsen.com)
// Contributors:
// Richard Livsey
// Rahul Bhargava
// Rob Wills
//
// script.aculo.us is freely distributable under the terms of an MIT-style license.
// For details, see the script.aculo.us web site: http://script.aculo.us/
// Autocompleter.Base handles all the autocompletion functionality
// that's independent of the data source for autocompletion. This
// includes drawing the autocompletion menu, observing keyboard
// and mouse events, and similar.
//
// Specific autocompleters need to provide, at the very least,
// a getUpdatedChoices function that will be invoked every time
// the text inside the monitored textbox changes. This method
// should get the text for which to provide autocompletion by
// invoking this.getToken(), NOT by directly accessing
// this.element.value. This is to allow incremental tokenized
// autocompletion. Specific auto-completion logic (AJAX, etc)
// belongs in getUpdatedChoices.
//
// Tokenized incremental autocompletion is enabled automatically
// when an autocompleter is instantiated with the 'tokens' option
// in the options parameter, e.g.:
// new Ajax.Autocompleter('id','upd', '/url/', { tokens: ',' });
// will incrementally autocomplete with a comma as the token.
// Additionally, ',' in the above example can be replaced with
// a token array, e.g. { tokens: [',', '\n'] } which
// enables autocompletion on multiple tokens. This is most
// useful when one of the tokens is \n (a newline), as it
// allows smart autocompletion after linebreaks.
if(typeof Effect == 'undefined')
throw("controls.js requires including script.aculo.us' effects.js library");
var Autocompleter = { };
this.hasFocus = false;
this.changed = false;
this.active = false;
this.index = 0;
this.entryCount = 0;
if(this.setOptions)
this.setOptions(options);
else
setHeight: false,
});
}
};
// Force carriage returns as token delimiters anyway
this.observer = null;
},
show: function() {
if(Element.getStyle(this.update, 'display')=='none') this.options.onShow(this.element, this.update);
if(!this.iefix &&
'style="display:none;position:absolute;filter:progid:DXImageTransform.Microsoft.Alpha(opacity=0);" ' +
'src="javascript:false;" frameborder="0" scrolling="no"></iframe>');
}
},
fixIEOverlapping: function() {
},
hide: function() {
this.stopIndicator();
if(Element.getStyle(this.update, 'display')!='none') this.options.onHide(this.element, this.update);
},
startIndicator: function() {
},
stopIndicator: function() {
},
onKeyPress: function(event) {
if(this.active)
case Event.KEY_RETURN:
this.selectEntry();
this.hide();
this.active = false;
return;
return;
this.markPrevious();
this.render();
return;
this.markNext();
this.render();
return;
}
else
this.changed = true;
this.hasFocus = true;
this.observer =
},
activate: function() {
this.changed = false;
this.hasFocus = true;
this.getUpdatedChoices();
},
{
this.render();
}
},
this.selectEntry();
this.hide();
},
// needed to make click events working
this.hasFocus = false;
this.active = false;
},
render: function() {
if(this.entryCount > 0) {
for (var i = 0; i < this.entryCount; i++)
this.index==i ?
if(this.hasFocus) {
this.show();
this.active = true;
}
} else {
this.active = false;
this.hide();
}
},
markPrevious: function() {
},
markNext: function() {
else this.index = 0;
},
},
getCurrentEntry: function() {
},
selectEntry: function() {
this.active = false;
this.updateElement(this.getCurrentEntry());
},
updateElement: function(selectedElement) {
if (this.options.updateElement) {
return;
}
var value = '';
} else
var bounds = this.getTokenBounds();
if (whitespace)
} else {
}
if (this.options.afterUpdateElement)
},
updateChoices: function(choices) {
this.entryCount =
for (var i = 0; i < this.entryCount; i++) {
entry.autocompleteIndex = i;
this.addObservers(entry);
}
} else {
this.entryCount = 0;
}
this.stopIndicator();
this.index = 0;
this.selectEntry();
this.hide();
} else {
this.render();
}
}
},
addObservers: function(element) {
},
onObserverEvent: function() {
this.changed = false;
this.tokenBounds = null;
this.getUpdatedChoices();
} else {
this.active = false;
this.hide();
}
},
getToken: function() {
var bounds = this.getTokenBounds();
},
getTokenBounds: function() {
if (null != this.tokenBounds) return this.tokenBounds;
var tp;
}
}
});
return index;
return boundary;
};
this.options.asynchronous = true;
},
getUpdatedChoices: function() {
this.startIndicator();
encodeURIComponent(this.getToken());
if(this.options.defaultParams)
},
onComplete: function(request) {
}
});
// The local array autocompleter. Used when you'd prefer to
// inject an array of autocompletion options into the page, rather
// than sending out Ajax queries, which can be quite slow sometimes.
//
// The constructor takes four parameters. The first two are, as usual,
// the id of the monitored textbox, and id of the autocompletion menu.
// The third is the array you want to autocomplete from, and the fourth
// is the options block.
//
// Extra local autocompletion options:
// - choices - How many autocompletion choices to offer
//
// - partialSearch - If false, the autocompleter will match entered
// text only at the beginning of strings in the
// autocomplete array. Defaults to true, which will
// match text at the beginning of any *word* in the
// strings in the autocomplete array. If you want to
// search anywhere in the string, additionally set
// the option fullSearch to true (default: off).
//
// - fullSsearch - Search anywhere in autocomplete array strings.
//
// - partialChars - How many characters to enter before triggering
// a partial match (unlike minChars, which defines
// how many characters are required to do any match
// at all). Defaults to 2.
//
// - ignoreCase - Whether to ignore case when autocompleting.
// Defaults to true.
//
// It's possible to pass in a custom function as the 'selector'
// option, if you prefer to write your own autocompletion logic.
// In that case, the other options above will not apply unless
// you support them.
},
getUpdatedChoices: function() {
},
setOptions: function(options) {
choices: 10,
partialSearch: true,
partialChars: 2,
ignoreCase: true,
fullSearch: false,
var ret = []; // Beginning matches
var partial = []; // Inside matches
var count = 0;
while (foundPos != -1) {
break;
break;
}
}
}
}
}
}, options || { });
}
});
// AJAX in-place editor and collection editor
// Full rewrite by Christophe Porteneuve <tdd@tddsworld.com> (April 2007).
// Use this if you notice weird scrolling problems on some browsers,
// the DOM might be a bit confused when this gets called so do this
// waits 1 ms (with setTimeout) until it does the activation
setTimeout(function() {
}, 1);
};
this.prepareOptions();
this._controls = { };
}
if (this.options.externalControl)
if (!this.options.externalControl)
this.options.externalControlOnly = false;
this.registerListeners();
},
checkForEscapeOrReturn: function(e) {
this.handleFormCancellation(e);
this.handleFormSubmission(e);
},
if ('button' == control) {
if ('cancel' == mode)
} else if ('link' == control) {
if (extraClasses)
}
},
createEditField: function() {
var fld;
} else {
}
if (this.options.submitOnBlur)
if (this.options.loadTextURL)
this.loadExternalText();
},
createForm: function() {
var ipe = this;
};
this.createEditField();
if (this.options.onFormCustomization)
},
destroy: function() {
if (this._oldInnerHTML)
this.leaveEditMode();
this.unregisterListeners();
},
enterEditMode: function(e) {
this._editing = true;
this.triggerCallback('onEnterEditMode');
if (this.options.externalControl)
this.createForm();
if (!this.options.loadTextURL)
this.postProcessEditField();
},
enterHover: function(e) {
if (this.options.hoverClassName)
if (this._saving) return;
this.triggerCallback('onEnterHover');
},
getText: function() {
},
handleAJAXFailure: function(transport) {
if (this._oldInnerHTML) {
this._oldInnerHTML = null;
}
},
handleFormCancellation: function(e) {
this.wrapUp();
},
handleFormSubmission: function(e) {
this.prepareSubmission();
if (this.options.htmlResponse) {
onComplete: this._boundWrapperHandler,
});
} else {
onComplete: this._boundWrapperHandler,
});
}
},
leaveEditMode: function() {
this.removeForm();
this.leaveHover();
if (this.options.externalControl)
this._saving = false;
this._editing = false;
this._oldInnerHTML = null;
this.triggerCallback('onLeaveEditMode');
},
leaveHover: function(e) {
if (this.options.hoverClassName)
if (this._saving) return;
this.triggerCallback('onLeaveHover');
},
loadExternalText: function() {
if (this.options.stripLoadedTextTags)
this.postProcessEditField();
}.bind(this),
});
},
postProcessEditField: function() {
if (fpc)
},
prepareOptions: function() {
}.bind(this));
},
prepareSubmission: function() {
this._saving = true;
this.removeForm();
this.leaveHover();
this.showSaving();
},
registerListeners: function() {
this._listeners = { };
var listener;
if (!this.options.externalControlOnly)
if (this.options.externalControl)
}.bind(this));
},
removeForm: function() {
if (!this._form) return;
this._form = null;
this._controls = { };
},
showSaving: function() {
},
}
},
unregisterListeners: function() {
if (!this.options.externalControlOnly)
if (this.options.externalControl)
}.bind(this));
},
this.leaveEditMode();
// Can't use triggerCallback due to backward compatibility: requires
// binding + direct element
}
});
});
},
createEditField: function() {
if (this.options.loadCollectionURL)
this.loadCollection();
else
this.checkForExternalText();
},
loadCollection: function() {
throw('Server returned an invalid collection representation.');
this.checkForExternalText();
}.bind(this),
});
},
showLoadingText: function(text) {
if (!tempOption) {
tempOption.selected = true;
}
},
checkForExternalText: function() {
if (this.options.loadTextURL)
this.loadExternalText();
else
this.buildOptionList();
},
loadExternalText: function() {
this.buildOptionList();
}.bind(this),
});
},
buildOptionList: function() {
});
}.bind(this));
var option;
}.bind(this));
}
});
//**** DEPRECATION LAYER FOR InPlace[Collection]Editor! ****
//**** This only exists for a while, in order to let ****
//**** users adapt to the new API. Read up on the new ****
//**** API and convert your code to it ASAP! ****
if (!options) return;
};
};
ajaxOptions: { },
cancelText: 'cancel',
clickToEditText: 'Click to edit',
externalControl: null, // id|elt
externalControlOnly: false,
formClassName: 'inplaceeditor-form',
formId: null, // id|elt
highlightColor: '#ffff99',
highlightEndColor: '#ffffff',
hoverClassName: '',
htmlResponse: true,
loadingClassName: 'inplaceeditor-loading',
loadingText: 'Loading...',
okText: 'ok',
paramName: 'value',
savingClassName: 'inplaceeditor-saving',
savingText: 'Saving...',
size: 0,
stripLoadedTextTags: false,
submitOnBlur: false,
textAfterControls: '',
textBeforeControls: '',
},
},
// For backward compatibility, this one is bound to the IPE, and passes
// the element directly. It was too often customized, so we don't break it.
},
onEnterEditMode: null,
onEnterHover: function(ipe) {
},
},
onFormCustomization: null, // Takes the IPE and its generated form, after editor, before controls.
onLeaveEditMode: null,
onLeaveHover: function(ipe) {
});
}
},
Listeners: {
click: 'enterEditMode',
keydown: 'checkForEscapeOrReturn',
mouseover: 'enterHover',
mouseout: 'leaveHover'
}
});
loadingCollectionText: 'Loading options...'
};
// Delayed observer, like Form.Element.Observer,
// but waits for delay after last key input
// Ideal for live-search fields
this.timer = null;
},
delayedListener: function(event) {
},
onTimerEvent: function() {
this.timer = null;
}
});