1 前言
项目需要,需要编写一个简单的Python脚本编辑器。Python脚本内容简单,模式固定,形式如下:
脚本中只有两三个固定的对象,对象的属性和方法已知。要求支持关键字高亮显示。并且能够具备类似IDE一样的对象属性联想功能。
拿到这个需求,首先想到的如果要支持高亮显示,只能使用DIV作为文本编辑区域.Dojo的Combox的autoComplet功能可以为我所用。就这么开始吧.......
2 重点代码介绍
2.1 自动完成功能
dojo的Combox是一个INPUT,而且其AutoComplete是会覆盖整个文本框内的文本,针对这种情况,必须自己定一个全新的Widget.我的策略如下,定义全新的Widget:
这个Widget的属性和代码基本完全照抄了dojo的dijit.form.ComboBoxMixin(定义在dojo的ComboBox.js)。完全拷贝下来不是我需要的,我做的第一步就是修改这个Widget的templateString
由于模版字符串的变化造成了很多原先的方法,属性都不在需要,我做了删除,这里不再赘述。接着需要修改的就是autoComplete提示框的位置.要能跟随光标的变化,IE下是可以获得光标位置的,因此增加如下代码
if (dojo.isIE)
{
dijit.removeWaiState(this.focusNode, "owns");
var range = document.selection.createRange();
this._popupWidget.domNode.parentNode.style.left = range.offsetLeft + 'px';
this._popupWidget.domNode.parentNode.style.top = (range.offsetTop + 13) + 'px';
this._popupWidget.domNode.parentNode.style.width = 200;
this._popupWidget.domNode.style.width = 200;
}
},
另外就是Combobox原先只是单个单词,而在线编辑器.需要根据当前鼠标的位置,获得左边的文本以及右边的文本,并获得光标前第一个单词的文本(用于联想对象属性),为此增加如下函数
其中_getCaretPos获得光标位置,getWordBeginIndex获得所联想的单词的起始位置,这样当自动完成的时候,我只需要把光标左边的文本,联想出的文本,光标右边的文本相加即为完成后的文本.如此一来,继续修改原dojo代码中的autocomplete部分代码即可,这一部分比较简单,读者可以参阅最后原代码中的_autoCompleteText函数即可.这里值得提出的就是当DIV中内容发生改变的时候,不能在修改完innerHTML后立即执行对新内容的操作,所以代码中通过setTimeout方式,延时处理修改后的内容。
接下来还需要考虑不同情况下, 下拉框结果集的变化,dojo的combobox是根据store属性中的值来构造下拉列表的,因此只需要修改store即可,为此我定义了如下两个成员entityStore,subStoreMap,其中entityStore就是一级对象成员,存储在Store中;二级属性成员存储在 subStoreMap中。举例说明:比如一级对象有个叫bus,r则subStoreMap['bus']存放bus对象的属性.在_onkeyPress函数中增加对'.'的处理 ,修改store属性即可实现对象属性的联想。当然这个键盘响应函数中还要增加其他的控制,来决定store为空还是一级对象集合.
2.2 关键字高亮功能
这个相对比较简单,大体思路是,找出文本中的关键字在文本中的位置,利用textRange的execCommand进行着色和加粗.值得注意的是,对于String的substring一个换行符是两个字符,而在textRange对象中换行是一个字符,所以代码如下
3 Widget代码
// searchDelay: Integer
// Delay in milliseconds between when user types something and we start
// searching based on that value
searchDelay: 100,
// searchAttr: String
// Searches pattern match against this field
searchAttr: "name",
// labelAttr: String
// Optional. The text that actually appears in the drop down.
// If not specified, the searchAttr text is used instead.
labelAttr: "",
// labelType: String
// "html" or "text"
labelType: "text",
// queryExpr: String
// dojo.data query expression pattern.
// `${0}` will be substituted for the user text.
// `*` is used for wildcards.
// `${0}*` means "starts with", `*${0}*` means "contains", `${0}` means "is"
queryExpr: "${0}*",
// ignoreCase: Boolean
// Set true if the ComboBox should ignore case when matching possible items
ignoreCase: true,
// hasDownArrow: Boolean
// Set this textbox to have a down arrow button.
// Defaults to true.
hasDownArrow:false,
templateString: '<div style="height:100px;overflow:auto;font-size=small;border: 2px solid" border="thick double yellow" contentEditable="true" autocomplete="off" dojoAttachEvent="onkeypress:_onKeyPress, onfocus:compositionend"/
dojoAttachPoint="textbox,focusNode" waiRole="textbox" waiState="haspopup-true,autocomplete-list"></div>',
baseClass:"dijitComboBox",
keyWords:['and', 'or', 'if'],
noliterWords:['/n', ' ', '.','(',')', '=', '>', '<'],
leftTextValue: '',
rigthtTextValue: '',
entityStore: null,
subStoreMap: null,
highlightColor: 'blue',
_getCaretPos: function(/*DomNode*/ element){
var CaretPos = 0; // IE Support
if (document.selection) {
element.focus ();
var Sel = document.selection.createRange();
Sel.moveStart ('character', -element.innerText.length);
var text = Sel.text;
for (var i = 0; i < element.innerText.length; i++)
{
if (element.innerText.substring(0, i + 1) == text.substring(text.length - i - 1, text.length))
{
CaretPos = i + 1;
}
}
}
// Firefox support
else if (element.selectionStart || element.selectionStart == '0')
CaretPos = element.selectionStart;
return (CaretPos);
},
_setCaretPos: function(/*DomNode*/ element, /*Number*/ location){
if(element.setSelectionRange)
{
element.focus();
element.setSelectionRange(location,location);
}
else if (dojo.doc.body.createTextRange) {
var range = dojo.doc.body.createTextRange();
range.moveToElementText(element);
range.collapse(true);
range.move('character', location);
range.select();
}
},
_onKeyPress: function(/*Event*/ evt){
// summary: handles keyboard events
var key = evt.charOrCode;
//except for cutting/pasting case - ctrl + x/v
if(evt.altKey || (evt.ctrlKey && (key != 'x' && key != 'v')) || evt.key == dojo.keys.SHIFT){
return; // throw out weird key combinations and spurious events
}
var doSearch = false;
var pw = this._popupWidget;
var dk = dojo.keys;
if(this._isShowingNow){
pw.handleKey(key);
}
switch(key){
case dk.PAGE_DOWN:
case dk.DOWN_ARROW:
if(!this._isShowingNow||this._prev_key_esc){
return
}else{
dojo.stopEvent(evt);
this._prev_key_backspace = false;
this._prev_key_esc = false;
}
break;
case dk.PAGE_UP:
case dk.UP_ARROW:
if (this._isShowingNow) {
dojo.stopEvent(evt);
this._prev_key_backspace = false;
this._prev_key_esc = false;
}
else{
return;
}
break;
case dojo.keys.ENTER:
case dojo.keys.TAB:
if (this._isShowingNow) {
this._announceOption(this._popupWidget.getHighlightedOption());
text = this.focusNode[dojo.isIE?'innerText':'textContent'];
//pos = this.leftTextValue.length;
//pos += this.focusNode[dojo.isIE?'innerText':'textContent'].length;
this.focusNode[dojo.isIE?'innerText':'textContent'] = text;
this.store = this.entityStore;
//this._setCaretPos(this.focusNode, pos);
this.clearTextValue();
evt.preventDefault();
this._prev_key_backspace = false;
this._prev_key_esc = false;
this._lastQuery = null; // in case results come back later
this._hideResultList();
}else{
return;
}
break;
case ' ':
if (this._isShowingNow && this._popupWidget.getHighlightedOption())
{
this._announceOption(this._popupWidget.getHighlightedOption());
text = this.focusNode[dojo.isIE?'innerText':'textContent'];
this.focusNode[dojo.isIE?'innerText':'textContent'] = text;
this.store = this.entityStore;
this.clearTextValue();
}
else
{
return;
}
break;
case dk.ESCAPE:
this._prev_key_backspace = false;
this._prev_key_esc = true;
if(this._isShowingNow){
dojo.stopEvent(evt);
this._hideResultList();
}else{
this.inherited(arguments);
}
break;
case dk.DELETE:
case dk.BACKSPACE:
if (this._isShowingNow) {
this._prev_key_esc = false;
this._prev_key_backspace = true;
doSearch = true;
}
else
{
return;
}
break;
case dk.RIGHT_ARROW: // fall through
case dk.LEFT_ARROW:
this._prev_key_backspace = false;
this._prev_key_esc = false;
if (!this._isShowingNow) {
return;
}
break;
case '.':
var key =this.getSearchKey();
if (this.subStoreMap[key])
{
this.store = this.subStoreMap[key];
doSearch = true;
}
else
{
doSearch = false;
}
break;
default: // non char keys (F1-F12 etc..) shouldn't open list
this._prev_key_backspace = false;
this._prev_key_esc = false;
doSearch = typeof key == 'string';
}
if(this.searchTimer){
clearTimeout(this.searchTimer);
}
if(doSearch){
// need to wait a tad before start search so that the event
// bubbles through DOM and we have value visible
setTimeout(dojo.hitch(this, "_startSearchFromInput"),1);
}
},
_autoCompleteText: function(/*String*/ text){
// summary:
// Fill in the textbox with the first item from the drop down
// list, and highlight the characters that were
// auto-completed. For example, if user typed "CA" and the
// drop down list appeared, the textbox would be changed to
// "California" and "ifornia" would be highlighted.
var fn = this.focusNode;
// IE7: clear selection so next highlight works all the time
dijit.selectInputText(fn, fn[dojo.isIE?'innerText':'textContent'].length);
// does text autoComplete the value in the textbox?
var caseFilter = this.ignoreCase? 'toLowerCase' : 'substr';
if(text[caseFilter](0).indexOf(this.focusNode[dojo.isIE?'innerText':'textContent'][caseFilter](0)) == 0){
var cpos = this._getCaretPos(fn);
// only try to extend if we added the last character at the end of the input
if((cpos+1) > fn[dojo.isIE?'innerText':'textContent'].length){
// only add to input node as we would overwrite Capitalisation of chars
// actually, that is ok
fn[dojo.isIE?'innerText':'textContent'] = text;//.substr(cpos);
// visually highlight the autocompleted characters
dijit.selectInputText(fn, cpos);
}
}else{
// text does not autoComplete; replace the whole value and highlight
fn[dojo.isIE?'innerText':'textContent'] = text;
dijit.selectInputText(fn);
}
var pos = (this.leftTextValue + this.focusNode[dojo.isIE?'innerText':'textContent']).length;
this.focusNode.innerHTML = (this.leftTextValue + this.focusNode[dojo.isIE?'innerText':'textContent'] + this.rigthtTextValue).split('/n').join('<BR>').split(' ').join(' ');
setTimeout(dojo.hitch(this, "_setCaretPos", this.focusNode, pos),10);
setTimeout(dojo.hitch(this, "highlightKeyWord"), 20);
},
_openResultList: function(/*Object*/ results, /*Object*/ dataObject){
setTimeout(dojo.hitch(this, "highlightKeyWord"),10);
if( this.disabled ||
this.readOnly ||
(dataObject.query[this.searchAttr] != this._lastQuery)
){
return;
}
this._popupWidget.clearResultList();
if(!results.length){
this._hideResultList();
return;
}
// Fill in the textbox with the first item from the drop down list,
// and highlight the characters that were auto-completed. For
// example, if user typed "CA" and the drop down list appeared, the
// textbox would be changed to "California" and "ifornia" would be
// highlighted.
var zerothvalue = new String(this.store.getValue(results[0], this.searchAttr));
if(zerothvalue && this.autoComplete && !this._prev_key_backspace &&
(dataObject.query[this.searchAttr] != "*")){
// when the user clicks the arrow button to show the full list,
// startSearch looks for "*".
// it does not make sense to autocomplete
// if they are just previewing the options available.
this._autoCompleteText(zerothvalue);
}
dataObject._maxOptions = this._maxOptions;
this._popupWidget.createOptions(
results,
dataObject,
dojo.hitch(this, "_getMenuLabelFromItem")
);
// show our list (only if we have content, else nothing)
this._showResultList();
// #4091:
// tell the screen reader that the paging callback finished by
// shouting the next choice
if(dataObject.direction){
if(1 == dataObject.direction){
this._popupWidget.highlightFirstOption();
}else if(-1 == dataObject.direction){
this._popupWidget.highlightLastOption();
}
//this._announceOption(this._popupWidget.getHighlightedOption());
}
},
_showResultList: function(){
this._hideResultList();
var items = this._popupWidget.getItems(),
visibleCount = Math.min(items.length,this.maxListLength);
// hide the tooltip
//this.displayMessage("");
// Position the list and if it's too big to fit on the screen then
// size it to the maximum possible height
// Our dear friend IE doesnt take max-height so we need to
// calculate that on our own every time
// TODO: want to redo this, see
// http://trac.dojotoolkit.org/ticket/3272
// and
// http://trac.dojotoolkit.org/ticket/4108
// natural size of the list has changed, so erase old
// width/height settings, which were hardcoded in a previous
// call to this function (via dojo.marginBox() call)
dojo.style(this._popupWidget.domNode, {width: "", height: ""});
var best = this.open();
// #3212:
// only set auto scroll bars if necessary prevents issues with
// scroll bars appearing when they shouldn't when node is made
// wider (fractional pixels cause this)
var popupbox = dojo.marginBox(this._popupWidget.domNode);
this._popupWidget.domNode.style.overflow =
((best.h==popupbox.h)&&(best.w==popupbox.w)) ? "hidden" : "auto";
// #4134:
// borrow TextArea scrollbar test so content isn't covered by
// scrollbar and horizontal scrollbar doesn't appear
var newwidth = best.w;
if(best.h < this._popupWidget.domNode.scrollHeight){
newwidth += 16;
}
dojo.marginBox(this._popupWidget.domNode, {
h: best.h,
w: Math.max(newwidth, this.domNode.offsetWidth)
});
//dijit.setWaiState(this.comboNode, "expanded", "true");
if (dojo.isIE)
{
dijit.removeWaiState(this.focusNode, "owns");
var range = document.selection.createRange();
this._popupWidget.domNode.parentNode.style.left = range.offsetLeft + 'px';
this._popupWidget.domNode.parentNode.style.top = (range.offsetTop + 13) + 'px';
this._popupWidget.domNode.parentNode.style.width = 200;
this._popupWidget.domNode.style.width = 200;
}
},
_hideResultList: function(){
if(this._isShowingNow){
dijit.popup.close(this._popupWidget);
this._isShowingNow=false;
//dijit.setWaiState(this.comboNode, "expanded", "false");
dijit.removeWaiState(this.focusNode,"activedescendant");
}
},
_announceOption: function(/*Node*/ node){
// summary:
// a11y code that puts the highlighted option in the textbox
// This way screen readers will know what is happening in the
// menu
this.setTextValue();
if(node == null){
return;
}
// pull the text value from the item attached to the DOM node
var newValue;
if( node == this._popupWidget.nextButton ||
node == this._popupWidget.previousButton){
newValue = node.innerHTML;
}else{
newValue = this.store.getValue(node.item, this.searchAttr);
}
// get the text that the user manually entered (cut off autocompleted text)
this.focusNode[dojo.isIE?'innerText':'textContent'] = this.focusNode[dojo.isIE?'innerText':'textContent'].substring(0, this._getCaretPos(this.focusNode));
//set up ARIA activedescendant
dijit.setWaiState(this.focusNode, "activedescendant", dojo.attr(node, "id"));
// autocomplete the rest of the option to announce change
this._autoCompleteText(newValue);
},
_selectOption: function(/*Event*/ evt){
var tgt = null;
if(!evt){
evt ={ target: this._popupWidget.getHighlightedOption()};
}
// what if nothing is highlighted yet?
if(!evt.target){
// handle autocompletion where the the user has hit ENTER or TAB
this.attr('displayedValue', this.attr('displayedValue'));
return;
// otherwise the user has accepted the autocompleted value
}else{
tgt = evt.target;
}
if(!evt.noHide){
this._hideResultList();
this._setCaretPos(this.focusNode, this.store.getValue(tgt.item, this.searchAttr).length);
}
this._doSelect(tgt);
},
_doSelect: function(tgt){
this.item = tgt.item;
this.attr('value', this.store.getValue(tgt.item, this.searchAttr));
},
_startSearchFromInput: function(){
this._startSearch(this.focusNode[dojo.isIE?'innerText':'textContent']);
},
_getQueryString: function(/*String*/ text){
return dojo.string.substitute(this.queryExpr, [text]);
},
_startSearch: function(/*String*/ key){
var index = this.getWordBeginIndex();
key = key.substring(0, this._getCaretPos(this.focusNode));
key = dojo.trim(key.substring(index + 1, key.length));
if(!this._popupWidget){
var popupId = this.id + "_popup";
this._popupWidget = new dijit.form._ComboBoxMenu({
onChange: dojo.hitch(this, this._selectOption),
id:popupId
});
this.connect(this._popupWidget, '_onMouseUp', function(event){
this.focusNode.innerHTML = (this.leftTextValue + this.value + this.rigthtTextValue).split('/n').join('<BR>');
this.clearTextValue();
this.store = this.entityStore;
});
this.connect(this._popupWidget, '_onMouseDown', function(event){
this.setTextValue();
});
dijit.removeWaiState(this.focusNode,"activedescendant");
dijit.setWaiState(this.textbox,"owns",popupId); // associate popup with textbox
}
// create a new query to prevent accidentally querying for a hidden
// value from FilteringSelect's keyField
this.item = null; // #4872
var query = dojo.clone(this.query); // #5970
this._lastInput = key; // Store exactly what was entered by the user.
this._lastQuery = query[this.searchAttr] = this._getQueryString(key);
// #5970: set _lastQuery, *then* start the timeout
// otherwise, if the user types and the last query returns before the timeout,
// _lastQuery won't be set and their input gets rewritten
this.searchTimer=setTimeout(dojo.hitch(this, function(query, _this){
var fetch = {
queryOptions: {
ignoreCase: this.ignoreCase,
deep: true
},
query: query,
onBegin: dojo.hitch(this, "_setMaxOptions"),
onComplete: dojo.hitch(this, "_openResultList"),
onError: function(errText){
console.error('dijit.form.ComboBox: ' + errText);
dojo.hitch(_this, "_hideResultList")();
},
start:0,
count:this.pageSize
};
dojo.mixin(fetch, _this.fetchProperties);
var dataObject = _this.store.fetch(fetch);
var nextSearch = function(dataObject, direction){
dataObject.start += dataObject.count*direction;
// #4091:
// tell callback the direction of the paging so the screen
// reader knows which menu option to shout
dataObject.direction = direction;
this.store.fetch(dataObject);
};
this._nextSearch = this._popupWidget.onPage = dojo.hitch(this, nextSearch, dataObject);
}, query, this), this.searchDelay);
},
_setMaxOptions: function(size, request){
this._maxOptions = size;
},
_getValueField:function(){
return this.searchAttr;
},
// FIXME:
// this is public so we can't remove until 2.0, but the name
// SHOULD be "compositionEnd"
compositionend: function(/*Event*/ evt){
// summary:
// When inputting characters using an input method, such as
// Asian languages, it will generate this event instead of
// onKeyDown event Note: this event is only triggered in FF
// (not in IE)
this._onKeyPress({charCode:-1});
},
//////////// INITIALIZATION METHODS ///////////////////////////////////////
constructor: function(){
this.query={};
this.fetchProperties={};
},
postMixInProperties: function(){
this.store = this.entityStore;
if(!this.hasDownArrow){
this.baseClass = "dijitTextBox";
}
if(!this.store){
var srcNodeRef = this.srcNodeRef;
// if user didn't specify store, then assume there are option tags
this.store = new dijit.form._ComboBoxDataStore(srcNodeRef);
// if there is no value set and there is an option list, set
// the value to the first value to be consistent with native
// Select
// Firefox and Safari set value
// IE6 and Opera set selectedIndex, which is automatically set
// by the selected attribute of an option tag
// IE6 does not set value, Opera sets value = selectedIndex
if( !this.value || (
(typeof srcNodeRef.selectedIndex == "number") &&
srcNodeRef.selectedIndex.toString() === this.value)
){
var item = this.store.fetchSelectedItem();
if(item){
this.value = this.store.getValue(item, this._getValueField());
}
}
}
},
uninitialize:function(){
if(this._popupWidget){
this._hideResultList();
this._popupWidget.destroy();
}
},
_getMenuLabelFromItem:function(/*Item*/ item){
var label = this.store.getValue(item, this.labelAttr || this.searchAttr);
var labelType = this.labelType;
// If labelType is not "text" we don't want to screw any markup ot whatever.
if (this.highlightMatch!="none" && this.labelType=="text" && this._lastInput){
label = this.doHighlight(label, this._escapeHtml(this._lastInput));
labelType = "html";
}
return {html: labelType=="html", label: label};
},
doHighlight:function(/*String*/label, /*String*/find){
// summary:
// Highlights the string entered by the user in the menu, by default this
// highlights the first occurence found. Override this method
// to implement your custom highlighing.
// Add greedy when this.highlightMatch=="all"
var modifiers = "i"+(this.highlightMatch=="all"?"g":"");
var escapedLabel = this._escapeHtml(label);
var ret = escapedLabel.replace(new RegExp("^("+ find +")", modifiers),
'<span class="dijitComboBoxHighlightMatch">$1</span>');
if (escapedLabel==ret){ // Nothing replaced, try to replace at word boundaries.
ret = escapedLabel.replace(new RegExp(" ("+ find +")", modifiers),
' <span class="dijitComboBoxHighlightMatch">$1</span>');
}
return ret;// returns String, (almost) valid HTML (entities encoded)
},
_escapeHtml:function(/*string*/str){
// TODO Should become dojo.html.entities(), when exists use instead
// summary:
// Adds escape sequences for special characters in XML: &<>"'
str = String(str).replace(/&/gm, "&").replace(/</gm, "<")
.replace(/>/gm, ">").replace(/"/gm, """);
return str; // string
},
open:function(){
this._isShowingNow=true;
return dijit.popup.open({
popup: this._popupWidget,
around: this.domNode,
parent: this
});
},
reset:function(){
// summary:
// Additionally reset the .item (to clean up).
this.item = null;
this.inherited(arguments);
},
getSearchKey: function()
{
var pos = this._getCaretPos(this.focusNode);
var key = this.focusNode[dojo.isIE?'innerText':'textContent'].substring(0, pos);
var index = this.getWordBeginIndex(key);
key = dojo.trim(key.substring(index + 1, key.length));
return key;
},
setTextValue: function()
{
var pos = this._getCaretPos(this.focusNode);
this.leftTextValue = this.focusNode[dojo.isIE?'innerText':'textContent'].substring(0, pos);
this.leftTextValue = this.leftTextValue.substr(0,this.getWordBeginIndex(this.leftTextValue) + 1);
this.rigthtTextValue = this.focusNode[dojo.isIE?'innerText':'textContent'].substring(pos, this.focusNode[dojo.isIE?'innerText':'textContent'].length);
},
clearTextValue: function()
{
this.leftTextValue = '';
this.rigthtTextValue = '';
},
getWordBeginIndex: function(text)
{
var lastIndexArray = [];
if (!text) text = this.focusNode[dojo.isIE?'innerText':'textContent'].substring(0, this._getCaretPos(this.focusNode));
dojo.forEach(this.keyWords.concat(this.noliterWords), function(x)
{
lastIndexArray.push(text.toLowerCase().lastIndexOf(x));
});
lastIndexArray.sort(function(a,b)
{
return b - a;
});
return lastIndexArray[0];
},
highlightKeyWord: function()
{
//Only for IE
var textContent = this.focusNode[dojo.isIE?'innerText':'textContent'];
var begin = 0, oneword;
for (var i = 0; i < textContent.length; i++)
{
if (dojo.indexOf(this.noliterWords, textContent.substring(i,i + 1)) > -1)
{
oneword = textContent.substring(begin, i + 1);
if (dojo.indexOf(this.keyWords, dojo.trim(oneword)) > -1)
{
this._boldWord(this.focusNode, begin, i + 1);
}
begin = i;
}
}
oneword = textContent.substring(begin,i + 1);
if (dojo.indexOf(this.keyWords, dojo.trim(oneword)) > -1)
{
this._boldWord(this.focusNode, begin, i + 1);
}
},
_boldWord: function(element, begin, end)
{
if (dojo.doc.body.createTextRange) {
var range = dojo.doc.body.createTextRange();
range.moveToElementText(element);
var line1 = element.innerText.substring(0, begin).split('/n').length - 1;
var line2 = element.innerText.substring(end, element.innerText.length).split('/n').length - 1;
range.moveEnd('character', end - element.innerText.length + line2);
range.moveStart('character', begin - line1);
if (!range.queryCommandState('bold')) {
range.execCommand('bold', false, null);
range.execCommand('forecolor', false, this.highlightColor);
}
}
},
getValue: function()
{
return this.focusNode[dojo.isIE?'innerText':'textContent'];
}
}
);
这个JS放置于dojo/dijit/form目录下
测试HTML为,读者使用的时候注意修改相对路径
<mce:script type="text/javascript"><!--
dojo.require("dijit.form.AutoCompleteEditor");
dojo.require("dojo.data.ItemFileReadStore");
var entityData =
{
identifier: "type",
label: "name",
items: [{
name: "Customer",
label: "Customer",
type: "0"
}, {
name: "Event",
label: "Event",
type: "1"
},
{
name: "GetString",
label: "GetString",
type: "2"
}]
}
entityDataStore = new dojo.data.ItemFileReadStore({data: entityData});
entityDataStoreMap = [];
entityData =
{
identifier: "type",
label: "name",
items: [{
name: "BirthDay",
label: "BirthDay",
type: "0"
}, {
name: "Gender",
label: "Gender",
type: "1"
}]
}
entityDataStoreMap['Customer'] = new dojo.data.ItemFileReadStore({data: entityData});
entityData =
{
identifier: "type",
label: "name",
items: [{
name: "Voice",
label: "Voice",
type: "0"
}, {
name: "GPRS",
label: "GPRS",
type: "1"
}]
}
entityDataStoreMap['Event'] = new dojo.data.ItemFileReadStore({data: entityData});
entityData =
{
identifier: "type",
label: "name",
items: [{
name: "GetString",
label: "GetString",
type: "0"
}]
}
entityDataStoreMap['Function'] = new dojo.data.ItemFileReadStore({data: entityData});
keyWords = ['and', 'or', 'if', 'else'];
dojo.addOnLoad(function(){dijit.byId('test').keyWords = keyWords;});
// --></mce:script>
</head>
<body class="tundra">
<div style="padding:10px 10px 0 10px" mce_style="padding:10px 10px 0 10px" >
</div>
<h1 id="towDialog">Test dijit.form.AutoCompleteEditor</h1>
<div id="test" dojoType="dijit.form.AutoCompleteEditor" entityStore="entityDataStore" subStoreMap="entityDataStoreMap"></div>
<INPUT type=button onclick="alert(dijit.byId('test').getValue())" value="getValue"/>
</body>
</html>
其中一级对象为Customer,Event和GetString,Customer包含BirthDay和Gender两个属性 Event包含Voice和GPRS两个属性
关键字为[if, else,and,or]
效果图如下