1414 lines
42 KiB
JavaScript
1414 lines
42 KiB
JavaScript
//>>built
|
|
// wrapped by build app
|
|
define("dojox/editor/plugins/SpellCheck", ["dijit","dojo","dojox","dojo/i18n!dojox/editor/plugins/nls/SpellCheck","dojo/require!dijit/_base/popup,dijit/_Widget,dijit/_Templated,dijit/form/TextBox,dijit/form/DropDownButton,dijit/TooltipDialog,dijit/form/MultiSelect,dojo/io/script,dijit/Menu"], function(dijit,dojo,dojox){
|
|
dojo.provide("dojox.editor.plugins.SpellCheck");
|
|
|
|
dojo.require("dijit._base.popup");
|
|
dojo.require("dijit._Widget");
|
|
dojo.require("dijit._Templated");
|
|
dojo.require("dijit.form.TextBox");
|
|
dojo.require("dijit.form.DropDownButton");
|
|
dojo.require("dijit.TooltipDialog");
|
|
dojo.require("dijit.form.MultiSelect");
|
|
dojo.require("dojo.io.script");
|
|
dojo.require("dijit.Menu");
|
|
|
|
dojo.requireLocalization("dojox.editor.plugins", "SpellCheck");
|
|
|
|
dojo.experimental("dojox.editor.plugins.SpellCheck");
|
|
|
|
dojo.declare("dojox.editor.plugins._spellCheckControl", [dijit._Widget, dijit._Templated], {
|
|
// summary:
|
|
// The widget that is used for the UI of the batch spelling check
|
|
|
|
widgetsInTemplate: true,
|
|
|
|
templateString:
|
|
"<table class='dijitEditorSpellCheckTable'>" +
|
|
"<tr><td colspan='3' class='alignBottom'><label for='${textId}' id='${textId}_label'>${unfound}</label>" +
|
|
"<div class='dijitEditorSpellCheckBusyIcon' id='${id}_progressIcon'></div></td></tr>" +
|
|
"<tr>" +
|
|
"<td class='dijitEditorSpellCheckBox'><input dojoType='dijit.form.TextBox' required='false' intermediateChanges='true' " +
|
|
"class='dijitEditorSpellCheckBox' dojoAttachPoint='unfoundTextBox' id='${textId}'/></td>" +
|
|
"<td><button dojoType='dijit.form.Button' class='blockButton' dojoAttachPoint='skipButton'>${skip}</button></td>" +
|
|
"<td><button dojoType='dijit.form.Button' class='blockButton' dojoAttachPoint='skipAllButton'>${skipAll}</button></td>" +
|
|
"</tr>" +
|
|
"<tr>" +
|
|
"<td class='alignBottom'><label for='${selectId}'>${suggestions}</td></label>" +
|
|
"<td colspan='2'><button dojoType='dijit.form.Button' class='blockButton' dojoAttachPoint='toDicButton'>${toDic}</button></td>" +
|
|
"</tr>" +
|
|
"<tr>" +
|
|
"<td>" +
|
|
"<select dojoType='dijit.form.MultiSelect' id='${selectId}' " +
|
|
"class='dijitEditorSpellCheckBox listHeight' dojoAttachPoint='suggestionSelect'></select>" +
|
|
"</td>" +
|
|
"<td colspan='2'>" +
|
|
"<button dojoType='dijit.form.Button' class='blockButton' dojoAttachPoint='replaceButton'>${replace}</button>" +
|
|
"<div class='topMargin'><button dojoType='dijit.form.Button' class='blockButton' " +
|
|
"dojoAttachPoint='replaceAllButton'>${replaceAll}</button><div>" +
|
|
"</td>" +
|
|
"</tr>" +
|
|
"<tr>" +
|
|
"<td><div class='topMargin'><button dojoType='dijit.form.Button' dojoAttachPoint='cancelButton'>${cancel}</button></div></td>" +
|
|
"<td></td>" +
|
|
"<td></td>" +
|
|
"</tr>" +
|
|
"</table>",
|
|
|
|
/*************************************************************************/
|
|
/** Framework Methods **/
|
|
/*************************************************************************/
|
|
constructor: function(){
|
|
// Indicate if the textbox ignores the text change event of the textbox
|
|
this.ignoreChange = false;
|
|
// Indicate if the text of the textbox is changed or not
|
|
this.isChanged = false;
|
|
// Indicate if the dialog is open or not
|
|
this.isOpen = false;
|
|
// Indicate if the dialog can be closed
|
|
this.closable = true;
|
|
},
|
|
|
|
postMixInProperties: function(){
|
|
this.id = dijit.getUniqueId(this.declaredClass.replace(/\./g,"_"));
|
|
this.textId = this.id + "_textBox";
|
|
this.selectId = this.id + "_select";
|
|
},
|
|
|
|
postCreate: function(){
|
|
var select = this.suggestionSelect;
|
|
|
|
// Customize multi-select to single select
|
|
dojo.removeAttr(select.domNode, "multiple");
|
|
select.addItems = function(/*Array*/ items){
|
|
// summary:
|
|
// Add items to the select widget
|
|
// items:
|
|
// An array of items be added to the select
|
|
// tags:
|
|
// public
|
|
var _this = this;
|
|
var o = null;
|
|
if(items && items.length > 0){
|
|
dojo.forEach(items, function(item, i){
|
|
o = dojo.create("option", {innerHTML: item, value: item}, _this.domNode);
|
|
if(i == 0){
|
|
o.selected = true;
|
|
}
|
|
});
|
|
}
|
|
};
|
|
select.removeItems = function(){
|
|
// summary:
|
|
// Remove all the items within the select widget
|
|
// tags:
|
|
// public
|
|
dojo.empty(this.domNode);
|
|
};
|
|
|
|
select.deselectAll = function(){
|
|
// summary:
|
|
// De-select all the selected items
|
|
// tags:
|
|
// public
|
|
this.containerNode.selectedIndex = -1;
|
|
};
|
|
|
|
// Connect up all the controls with their event handler
|
|
this.connect(this, "onKeyPress", "_cancel");
|
|
this.connect(this.unfoundTextBox, "onKeyPress", "_enter");
|
|
this.connect(this.unfoundTextBox, "onChange", "_unfoundTextBoxChange");
|
|
this.connect(this.suggestionSelect, "onKeyPress", "_enter");
|
|
this.connect(this.skipButton, "onClick", "onSkip");
|
|
this.connect(this.skipAllButton, "onClick", "onSkipAll");
|
|
this.connect(this.toDicButton, "onClick", "onAddToDic");
|
|
this.connect(this.replaceButton, "onClick", "onReplace");
|
|
this.connect(this.replaceAllButton, "onClick", "onReplaceAll");
|
|
this.connect(this.cancelButton, "onClick", "onCancel");
|
|
},
|
|
|
|
/*************************************************************************/
|
|
/** Public Methods **/
|
|
/*************************************************************************/
|
|
|
|
onSkip: function(){
|
|
// Stub for the click event of the skip button.
|
|
},
|
|
|
|
onSkipAll: function(){
|
|
// Stub for the click event of the skipAll button.
|
|
},
|
|
|
|
onAddToDic: function(){
|
|
// Stub for the click event of the toDic button.
|
|
},
|
|
|
|
onReplace: function(){
|
|
// Stub for the click event of the replace button.
|
|
},
|
|
|
|
onReplaceAll: function(){
|
|
// Stub for the click event of the replaceAll button.
|
|
},
|
|
|
|
onCancel: function(){
|
|
// Stub for the click event of the cancel button.
|
|
},
|
|
|
|
onEnter: function(){
|
|
// Stub for the enter event of the unFound textbox.
|
|
},
|
|
|
|
focus: function(){
|
|
// summary:
|
|
// Set the focus of the control
|
|
// tags:
|
|
// public
|
|
this.unfoundTextBox.focus();
|
|
},
|
|
|
|
/*************************************************************************/
|
|
/** Private Methods **/
|
|
/*************************************************************************/
|
|
|
|
_cancel: function(/*Event*/ evt){
|
|
// summary:
|
|
// Handle the cancel event
|
|
// evt:
|
|
// The event object
|
|
// tags:
|
|
// private
|
|
if(evt.keyCode == dojo.keys.ESCAPE){
|
|
this.onCancel();
|
|
dojo.stopEvent(evt);
|
|
}
|
|
},
|
|
|
|
_enter: function(/*Event*/ evt){
|
|
// summary:
|
|
// Handle the enter event
|
|
// evt:
|
|
// The event object
|
|
// tags:
|
|
// private
|
|
if(evt.keyCode == dojo.keys.ENTER){
|
|
this.onEnter();
|
|
dojo.stopEvent(evt);
|
|
}
|
|
},
|
|
|
|
_unfoundTextBoxChange: function(){
|
|
// summary:
|
|
// Indicate that the Not Found textbox is changed or not
|
|
// tags:
|
|
// private
|
|
var id = this.textId + "_label";
|
|
if(!this.ignoreChange){
|
|
dojo.byId(id).innerHTML = this["replaceWith"];
|
|
this.isChanged = true;
|
|
this.suggestionSelect.deselectAll();
|
|
}else{
|
|
dojo.byId(id).innerHTML = this["unfound"];
|
|
}
|
|
},
|
|
|
|
_setUnfoundWordAttr: function(/*String*/ value){
|
|
// summary:
|
|
// Set the value of the Not Found textbox
|
|
// value:
|
|
// The value of the Not Found textbox
|
|
// tags:
|
|
// private
|
|
value = value || "";
|
|
this.unfoundTextBox.set("value", value);
|
|
},
|
|
|
|
_getUnfoundWordAttr: function(){
|
|
// summary:
|
|
// Get the value of the Not Found textbox
|
|
// tags:
|
|
// private
|
|
return this.unfoundTextBox.get("value");
|
|
},
|
|
|
|
_setSuggestionListAttr: function(/*Array*/ values){
|
|
// summary:
|
|
// Set the items of the suggestion list
|
|
// values:
|
|
// The list of the suggestion items
|
|
// tags:
|
|
// private
|
|
var select = this.suggestionSelect;
|
|
values = values || [];
|
|
select.removeItems();
|
|
select.addItems(values);
|
|
},
|
|
|
|
_getSelectedWordAttr: function(){
|
|
// summary:
|
|
// Get the suggested word.
|
|
// If the select box is selected, the value is the selected item's value,
|
|
// else the value the the textbox's value
|
|
// tags:
|
|
// private
|
|
var selected = this.suggestionSelect.getSelected();
|
|
if(selected && selected.length > 0){
|
|
return selected[0].value;
|
|
}else{
|
|
return this.unfoundTextBox.get("value");
|
|
}
|
|
},
|
|
|
|
_setDisabledAttr: function(/*Boolean*/ disabled){
|
|
// summary:
|
|
// Enable/disable the control
|
|
// tags:
|
|
// private
|
|
this.skipButton.set("disabled", disabled);
|
|
this.skipAllButton.set("disabled", disabled);
|
|
this.toDicButton.set("disabled", disabled);
|
|
this.replaceButton.set("disabled", disabled);
|
|
this.replaceAllButton.set("disabled", disabled);
|
|
},
|
|
|
|
_setInProgressAttr: function(/*Boolean*/ show){
|
|
// summary:
|
|
// Set the visibility of the progress icon
|
|
// tags:
|
|
// private
|
|
var id = this.id + "_progressIcon",
|
|
cmd = show ? "removeClass" : "addClass";
|
|
dojo[cmd](id, "hidden");
|
|
}
|
|
});
|
|
|
|
dojo.declare("dojox.editor.plugins._SpellCheckScriptMultiPart", null, {
|
|
// summary:
|
|
// It is a base network service component. It transfers text to a remote service port
|
|
// with cross domain ability enabled. It can split text into specified pieces and send
|
|
// them out one by one so that it can handle the case when the service has a limitation of
|
|
// the capability.
|
|
// The encoding is UTF-8.
|
|
|
|
// ACTION [public const] String
|
|
// Actions for the server-side piece to take
|
|
ACTION_QUERY: "query",
|
|
ACTION_UPDATE: "update",
|
|
|
|
// callbackHandle [public] String
|
|
// The callback name of JSONP
|
|
callbackHandle: "callback",
|
|
|
|
// maxBufferLength [public] Number
|
|
// The max number of charactors that send to the service at one time.
|
|
maxBufferLength: 100,
|
|
|
|
// delimiter [public] String
|
|
// A token that is used to identify the end of a word (a complete unit). It prevents the service from
|
|
// cutting a single word into two parts. For example:
|
|
// "Dojo toolkit is a ajax framework. It helps the developers buid their web applications."
|
|
// Without the delimiter, the sentence might be split into the follow pieces which is absolutely
|
|
// not the result we want.
|
|
// "Dojo toolkit is a ajax fram", "ework It helps the developers bu", "id their web applications"
|
|
// Having " " as the delimiter, we get the following correct pieces.
|
|
// "Dojo toolkit is a ajax framework", " It helps the developers buid", " their web applications"
|
|
delimiter: " ",
|
|
|
|
// label [public] String
|
|
// The leading label of the JSON response. The service will return the result like this:
|
|
// {response: [
|
|
// {
|
|
// text: "teest",
|
|
// suggestion: ["test","treat"]
|
|
// }
|
|
// ]}
|
|
label: "response",
|
|
|
|
// _timeout [private] Number
|
|
// Set JSONP timeout period
|
|
_timeout: 30000,
|
|
SEC: 1000,
|
|
|
|
constructor: function(){
|
|
// The URL of the target service
|
|
this.serviceEndPoint = "";
|
|
// The queue that holds all the xhr request
|
|
this._queue = [];
|
|
// Indicate if the component is still working. For example, waiting for collecting all
|
|
// the responses from the service
|
|
this.isWorking = false;
|
|
// The extra command passed to the service
|
|
this.exArgs = null;
|
|
// The counter that indicate if all the responses are collected to
|
|
// assemble the final result.
|
|
this._counter = 0;
|
|
},
|
|
|
|
send: function(/*String*/ content, /*String?*/ action){
|
|
// summary:
|
|
// Send the content to the service port with the specified action
|
|
// content:
|
|
// The text to be sent
|
|
// action:
|
|
// The action the service should take. Current support actions are
|
|
// ACTION_QUERY and ACTION_UPDATE
|
|
// tags:
|
|
// public
|
|
var _this = this,
|
|
dt = this.delimiter,
|
|
mbl = this.maxBufferLength,
|
|
label = this.label,
|
|
serviceEndPoint = this.serviceEndPoint,
|
|
callbackParamName = this.callbackHandle,
|
|
comms = this.exArgs,
|
|
timeout = this._timeout,
|
|
l = 0, r = 0;
|
|
|
|
// Temparary list that holds the result returns from the service, which will be
|
|
// assembled into a completed one.
|
|
if(!this._result) {
|
|
this._result = [];
|
|
}
|
|
|
|
action = action || this.ACTION_QUERY;
|
|
|
|
var batchSend = function(){
|
|
var plan = [];
|
|
var plannedSize = 0;
|
|
if(content && content.length > 0){
|
|
_this.isWorking = true;
|
|
var len = content.length;
|
|
do{
|
|
l = r + 1;
|
|
if((r += mbl) > len){
|
|
r = len;
|
|
}else{
|
|
// If there is no delimiter (emplty string), leave the right boundary where it is.
|
|
// Else extend the right boundary to the first occurance of the delimiter if
|
|
// it doesn't meet the end of the content.
|
|
while(dt && content.charAt(r) != dt && r <= len){
|
|
r++;
|
|
}
|
|
}
|
|
// Record the information of the text slices
|
|
plan.push({l: l, r: r});
|
|
plannedSize++;
|
|
}while(r < len);
|
|
|
|
dojo.forEach(plan, function(item, index){
|
|
var jsonpArgs = {
|
|
url: serviceEndPoint,
|
|
action: action,
|
|
timeout: timeout,
|
|
callbackParamName: callbackParamName,
|
|
handle: function(response, ioArgs){
|
|
if(++_this._counter <= this.size && !(response instanceof Error) &&
|
|
response[label] && dojo.isArray(response[label])){
|
|
// Collect the results
|
|
var offset = this.offset;
|
|
dojo.forEach(response[label], function(item){
|
|
item.offset += offset;
|
|
});
|
|
// Put the packages in order
|
|
_this._result[this.number]= response[label];
|
|
}
|
|
if(_this._counter == this.size){
|
|
_this._finalizeCollection(this.action);
|
|
_this.isWorking = false;
|
|
if(_this._queue.length > 0){
|
|
// Call the next request waiting in queue
|
|
(_this._queue.shift())();
|
|
}
|
|
}
|
|
}
|
|
};
|
|
jsonpArgs.content = comms ? dojo.mixin(comms, {action: action, content: content.substring(item.l - 1, item.r)}):
|
|
{action: action, content: content.substring(item.l - 1, item.r)};
|
|
jsonpArgs.size = plannedSize;
|
|
jsonpArgs.number = index; // The index of the current package
|
|
jsonpArgs.offset = item.l - 1;
|
|
dojo.io.script.get(jsonpArgs);
|
|
});
|
|
}
|
|
};
|
|
|
|
if(!_this.isWorking){
|
|
batchSend();
|
|
}else{
|
|
_this._queue.push(batchSend);
|
|
}
|
|
},
|
|
|
|
_finalizeCollection: function(action){
|
|
// summary:
|
|
// Assemble the responses into one result.
|
|
// action:
|
|
// The action token
|
|
// tags:
|
|
// private
|
|
var result = this._result,
|
|
len = result.length;
|
|
// Turn the result into a one-dimensional array
|
|
for(var i = 0; i < len; i++){
|
|
var temp = result.shift();
|
|
result = result.concat(temp);
|
|
}
|
|
if(action == this.ACTION_QUERY){
|
|
this.onLoad(result);
|
|
}
|
|
this._counter = 0;
|
|
this._result = [];
|
|
},
|
|
|
|
onLoad: function(/*String*/ data){
|
|
// Stub method for a sucessful call
|
|
},
|
|
|
|
setWaitingTime: function(/*Number*/ seconds){
|
|
this._timeout = seconds * this.SEC;
|
|
}
|
|
});
|
|
|
|
dojo.declare("dojox.editor.plugins.SpellCheck", [dijit._editor._Plugin], {
|
|
// summary:
|
|
// This plugin provides a spelling check cabability for the editor.
|
|
|
|
// url [public] String
|
|
// The url of the spelling check service
|
|
url: "",
|
|
|
|
// bufferLength [public] Number
|
|
// The max length of each XHR request. It is used to divide the large
|
|
// text into pieces so that the server-side piece can hold.
|
|
bufferLength: 100,
|
|
|
|
// interactive [public] Boolean
|
|
// Indicate if the interactive spelling check is enabled
|
|
interactive: false,
|
|
|
|
// timeout [public] Number
|
|
// The minutes to waiting for the response. The default value is 30 seconds.
|
|
timeout: 30,
|
|
|
|
// button [protected] dijit.form.DropDownButton
|
|
// The button displayed on the editor's toolbar
|
|
button: null,
|
|
|
|
// _editor [private] dijit.Editor
|
|
// The reference to the editor the plug-in belongs to.
|
|
_editor: null,
|
|
|
|
// exArgs [private] Object
|
|
// The object that holds all the parametes passed into the constructor
|
|
exArgs: null,
|
|
|
|
// _cursorSpan [private] String
|
|
// The span that holds the current position of the cursor
|
|
_cursorSpan:
|
|
"<span class=\"cursorPlaceHolder\"></span>",
|
|
|
|
// _cursorSelector [private] String
|
|
// The CSS selector of the cursor span
|
|
_cursorSelector:
|
|
"cursorPlaceHolder",
|
|
|
|
// _incorrectWordsSpan [private] String
|
|
// The wrapper that marks the incorrect words
|
|
_incorrectWordsSpan:
|
|
"<span class='incorrectWordPlaceHolder'>${text}</span>",
|
|
|
|
// _ignoredIncorrectStyle [private] Object
|
|
// The style of the ignored incorrect words
|
|
_ignoredIncorrectStyle:
|
|
{"cursor": "inherit", "borderBottom": "none", "backgroundColor": "transparent"},
|
|
|
|
// _normalIncorrectStyle [private] Object
|
|
// The style of the marked incorrect words.
|
|
_normalIncorrectStyle:
|
|
{"cursor": "pointer", "borderBottom": "1px dotted red", "backgroundColor": "yellow"},
|
|
|
|
// _highlightedIncorrectStyle [private] Object
|
|
// The style of the highlighted incorrect words
|
|
_highlightedIncorrectStyle:
|
|
{"borderBottom": "1px dotted red", "backgroundColor": "#b3b3ff"},
|
|
|
|
// _selector [private] String
|
|
// An empty CSS class that identifies the incorrect words
|
|
_selector: "incorrectWordPlaceHolder",
|
|
|
|
// _maxItemNumber [private] Number
|
|
// The max number of the suggestion list items
|
|
_maxItemNumber: 3,
|
|
|
|
/*************************************************************************/
|
|
/** Framework Methods **/
|
|
/*************************************************************************/
|
|
|
|
constructor: function(){
|
|
// A list that holds all the spans that contains the incorrect words
|
|
// It is used to select/replace the specified word.
|
|
this._spanList = [];
|
|
// The cache that stores all the words. It looks like the following
|
|
// {
|
|
// "word": [],
|
|
// "wrd": ["word", "world"]
|
|
// }
|
|
this._cache = {};
|
|
// Indicate if this plugin is enabled or not
|
|
this._enabled = true;
|
|
// The index of the _spanList
|
|
this._iterator = 0;
|
|
},
|
|
|
|
setEditor: function(/*dijit.Editor*/ editor){
|
|
this._editor = editor;
|
|
this._initButton();
|
|
this._setNetwork();
|
|
this._connectUp();
|
|
},
|
|
|
|
/*************************************************************************/
|
|
/** Private Methods **/
|
|
/*************************************************************************/
|
|
|
|
_initButton: function(){
|
|
// summary:
|
|
// Initialize the button displayed on the editor's toolbar
|
|
// tags:
|
|
// private
|
|
var _this = this,
|
|
strings = (this._strings = dojo.i18n.getLocalization("dojox.editor.plugins", "SpellCheck")),
|
|
dialogPane = (this._dialog = new dijit.TooltipDialog());
|
|
|
|
dialogPane.set("content", (this._dialogContent = new dojox.editor.plugins._spellCheckControl({
|
|
unfound: strings["unfound"],
|
|
skip: strings["skip"],
|
|
skipAll: strings["skipAll"],
|
|
toDic: strings["toDic"],
|
|
suggestions: strings["suggestions"],
|
|
replaceWith: strings["replaceWith"],
|
|
replace: strings["replace"],
|
|
replaceAll: strings["replaceAll"],
|
|
cancel: strings["cancel"]
|
|
})));
|
|
|
|
this.button = new dijit.form.DropDownButton({
|
|
label: strings["widgetLabel"],
|
|
showLabel: false,
|
|
iconClass: "dijitEditorSpellCheckIcon",
|
|
dropDown: dialogPane,
|
|
id: dijit.getUniqueId(this.declaredClass.replace(/\./g,"_")) + "_dialogPane",
|
|
closeDropDown: function(focus){
|
|
// Determine if the dialog can be closed
|
|
if(_this._dialogContent.closable){
|
|
_this._dialogContent.isOpen = false;
|
|
if(dojo.isIE){
|
|
var pos = _this._iterator,
|
|
list = _this._spanList;
|
|
if(pos < list.length && pos >=0 ){
|
|
dojo.style(list[pos], _this._normalIncorrectStyle);
|
|
}
|
|
}
|
|
if(this._opened){
|
|
dijit.popup.close(this.dropDown);
|
|
if(focus){ this.focus(); }
|
|
this._opened = false;
|
|
this.state = "";
|
|
}
|
|
}
|
|
}
|
|
});
|
|
_this._dialogContent.isOpen = false;
|
|
|
|
dialogPane.domNode.setAttribute("aria-label", this._strings["widgetLabel"]);
|
|
},
|
|
|
|
_setNetwork: function(){
|
|
// summary:
|
|
// Set up the underlying network service
|
|
// tags:
|
|
// private
|
|
var comms = this.exArgs;
|
|
|
|
if(!this._service){
|
|
var service = (this._service = new dojox.editor.plugins._SpellCheckScriptMultiPart());
|
|
service.serviceEndPoint = this.url;
|
|
service.maxBufferLength = this.bufferLength;
|
|
service.setWaitingTime(this.timeout);
|
|
// Pass the other arguments directly to the service
|
|
if(comms){
|
|
delete comms.name;
|
|
delete comms.url;
|
|
delete comms.interactive;
|
|
delete comms.timeout;
|
|
service.exArgs = comms;
|
|
}
|
|
}
|
|
},
|
|
|
|
_connectUp: function(){
|
|
// summary:
|
|
// Connect up all the events with their event handlers
|
|
// tags:
|
|
// private
|
|
var editor = this._editor,
|
|
cont = this._dialogContent;
|
|
|
|
this.connect(this.button, "set", "_disabled");
|
|
this.connect(this._service, "onLoad", "_loadData");
|
|
this.connect(this._dialog, "onOpen", "_openDialog");
|
|
this.connect(editor, "onKeyPress", "_keyPress");
|
|
this.connect(editor, "onLoad", "_submitContent");
|
|
this.connect(cont, "onSkip", "_skip");
|
|
this.connect(cont, "onSkipAll", "_skipAll");
|
|
this.connect(cont, "onAddToDic", "_add");
|
|
this.connect(cont, "onReplace", "_replace");
|
|
this.connect(cont, "onReplaceAll", "_replaceAll");
|
|
this.connect(cont, "onCancel", "_cancel");
|
|
this.connect(cont, "onEnter", "_enter");
|
|
|
|
editor.contentPostFilters.push(this._spellCheckFilter); // Register the filter
|
|
dojo.publish(dijit._scopeName + ".Editor.plugin.SpellCheck.getParser", [this]); // Get the language parser
|
|
if(!this.parser){
|
|
console.error("Can not get the word parser!");
|
|
}
|
|
},
|
|
|
|
/*************************************************************************/
|
|
/** Event Handlers **/
|
|
/*************************************************************************/
|
|
|
|
_disabled: function(name, disabled){
|
|
// summary:
|
|
// When the plugin is disabled (the button is disabled), reset all to their initial status.
|
|
// If the interactive mode is on, check the content once it is enabled.
|
|
// name:
|
|
// Command name
|
|
// disabled:
|
|
// Command argument
|
|
// tags:
|
|
// private
|
|
if(name == "disabled"){
|
|
if(disabled){
|
|
this._iterator = 0;
|
|
this._spanList = [];
|
|
}else if(this.interactive && !disabled && this._service){
|
|
this._submitContent(true);
|
|
}
|
|
this._enabled = !disabled;
|
|
}
|
|
},
|
|
|
|
_keyPress: function(evt){
|
|
// summary:
|
|
// The handler of the onKeyPress event of the editor
|
|
// tags:
|
|
// private
|
|
if(this.interactive){
|
|
var v = 118, V = 86,
|
|
cc = evt.charCode;
|
|
if(!evt.altKey && cc == dojo.keys.SPACE){
|
|
this._submitContent();
|
|
}else if((evt.ctrlKey && (cc == v || cc == V)) || (!evt.ctrlKey && evt.charCode)){
|
|
this._submitContent(true);
|
|
}
|
|
}
|
|
},
|
|
|
|
_loadData: function(/*Array*/ data){
|
|
// summary:
|
|
// Apply the query result to the content
|
|
// data:
|
|
// The result of the query
|
|
// tags:
|
|
// private
|
|
var cache = this._cache,
|
|
html = this._editor.get("value"),
|
|
cont = this._dialogContent;
|
|
|
|
this._iterator = 0;
|
|
|
|
// Update the local cache
|
|
dojo.forEach(data, function(d){
|
|
cache[d.text] = d.suggestion;
|
|
cache[d.text].correct = false;
|
|
});
|
|
|
|
if(this._enabled){
|
|
// Mark incorrect words
|
|
cont.closable = false;
|
|
this._markIncorrectWords(html, cache);
|
|
cont.closable = true;
|
|
|
|
if(this._dialogContent.isOpen){
|
|
this._iterator = -1;
|
|
this._skip();
|
|
}
|
|
}
|
|
},
|
|
|
|
_openDialog: function(){
|
|
// summary:
|
|
// The handler of the onOpen event
|
|
var cont = this._dialogContent;
|
|
|
|
// Clear dialog content and disable it first
|
|
cont.ignoreChange = true;
|
|
cont.set("unfoundWord", "");
|
|
cont.set("suggestionList", null);
|
|
cont.set("disabled", true);
|
|
cont.set("inProgress", true);
|
|
|
|
cont.isOpen = true; // Indicate that the dialog is open
|
|
cont.closable = false;
|
|
|
|
this._submitContent();
|
|
|
|
cont.closable = true;
|
|
},
|
|
|
|
_skip: function(/*Event?*/ evt, /*Boolean?*/ noUpdate){
|
|
// summary:
|
|
// Ignore this word and move to the next unignored one.
|
|
// evt:
|
|
// The event object
|
|
// noUpdate:
|
|
// Indicate whether to update the status of the span list or not
|
|
// tags:
|
|
// private
|
|
var cont = this._dialogContent,
|
|
list = this._spanList || [],
|
|
len = list.length,
|
|
iter = this._iterator;
|
|
|
|
cont.closable = false;
|
|
cont.isChanged = false;
|
|
cont.ignoreChange = true;
|
|
|
|
// Skip the current word
|
|
if(!noUpdate && iter >= 0 && iter < len){
|
|
this._skipWord(iter);
|
|
}
|
|
|
|
// Move to the next
|
|
while(++iter < len && list[iter].edited == true){ /* do nothing */}
|
|
if(iter < len){
|
|
this._iterator = iter;
|
|
this._populateDialog(iter);
|
|
this._selectWord(iter);
|
|
}else{
|
|
// Reaches the end of the list
|
|
this._iterator = -1;
|
|
cont.set("unfoundWord", this._strings["msg"]);
|
|
cont.set("suggestionList", null);
|
|
cont.set("disabled", true);
|
|
cont.set("inProgress", false);
|
|
}
|
|
|
|
setTimeout(function(){
|
|
// When moving the focus out of the iframe in WebKit browsers, we
|
|
// need to focus something else first. So the textbox
|
|
// can be focused correctly.
|
|
if(dojo.isWebKit) { cont.skipButton.focus(); }
|
|
cont.focus();
|
|
cont.ignoreChange = false;
|
|
cont.closable = true;
|
|
}, 0);
|
|
},
|
|
|
|
_skipAll: function(){
|
|
// summary:
|
|
// Ignore all the same words
|
|
// tags:
|
|
// private
|
|
this._dialogContent.closable = false;
|
|
this._skipWordAll(this._iterator);
|
|
this._skip();
|
|
},
|
|
|
|
_add: function(){
|
|
// summary:
|
|
// Add the unrecognized word into the dictionary
|
|
// tags:
|
|
// private
|
|
var cont = this._dialogContent;
|
|
|
|
cont.closable = false;
|
|
cont.isOpen = true;
|
|
this._addWord(this._iterator, cont.get("unfoundWord"));
|
|
this._skip();
|
|
},
|
|
|
|
_replace: function(){
|
|
// summary:
|
|
// Replace the incorrect word with the selected one,
|
|
// or the one the user types in the textbox
|
|
// tags:
|
|
// private
|
|
var cont = this._dialogContent,
|
|
iter = this._iterator,
|
|
targetWord = cont.get("selectedWord");
|
|
|
|
cont.closable = false;
|
|
this._replaceWord(iter, targetWord);
|
|
this._skip(null, true);
|
|
},
|
|
|
|
_replaceAll: function(){
|
|
// summary:
|
|
// Replace all the words with the same text
|
|
// tags:
|
|
// private
|
|
var cont = this._dialogContent,
|
|
list = this._spanList,
|
|
len = list.length,
|
|
word = list[this._iterator].innerHTML.toLowerCase(),
|
|
targetWord = cont.get("selectedWord");
|
|
|
|
cont.closable = false;
|
|
for(var iter = 0; iter < len; iter++){
|
|
// If this word is not ignored and is the same as the source word,
|
|
// replace it.
|
|
if(list[iter].innerHTML.toLowerCase() == word){
|
|
this._replaceWord(iter, targetWord);
|
|
}
|
|
}
|
|
|
|
this._skip(null, true);
|
|
},
|
|
|
|
_cancel: function(){
|
|
// summary:
|
|
// Cancel this check action
|
|
// tags:
|
|
// private
|
|
this._dialogContent.closable = true;
|
|
this._editor.focus();
|
|
},
|
|
|
|
_enter: function(){
|
|
// summary:
|
|
// Handle the ENTER event
|
|
// tags:
|
|
// private
|
|
if(this._dialogContent.isChanged){
|
|
this._replace();
|
|
}else{
|
|
this._skip();
|
|
}
|
|
},
|
|
|
|
/*************************************************************************/
|
|
/** Utils **/
|
|
/*************************************************************************/
|
|
|
|
_query: function(/*String*/ html){
|
|
// summary:
|
|
// Send the query text to the service. The query text is a string of words
|
|
// separated by space.
|
|
// html:
|
|
// The html value of the editor
|
|
// tags:
|
|
// private
|
|
var service = this._service,
|
|
cache = this._cache,
|
|
words = this.parser.parseIntoWords(this._html2Text(html)) || [];
|
|
var content = [];
|
|
dojo.forEach(words, function(word){
|
|
word = word.toLowerCase();
|
|
if(!cache[word]){
|
|
// New word that need to be send to the server side for check
|
|
cache[word] = [];
|
|
cache[word].correct = true;
|
|
content.push(word);
|
|
}
|
|
});
|
|
if(content.length > 0){
|
|
service.send(content.join(" "));
|
|
}else if(!service.isWorking){
|
|
this._loadData([]);
|
|
}
|
|
},
|
|
|
|
_html2Text: function(html){
|
|
// summary:
|
|
// Substitute the tag with white charactors so that the server
|
|
// can easily process the text. For example:
|
|
// "<a src="sample.html">Hello, world!</a>" ==>
|
|
// " Hello, world! "
|
|
// html:
|
|
// The html code
|
|
// tags:
|
|
// private
|
|
var text = [],
|
|
isTag = false,
|
|
len = html ? html.length : 0;
|
|
|
|
for(var i = 0; i < len; i++){
|
|
if(html.charAt(i) == "<"){ isTag = true; }
|
|
if(isTag == true){
|
|
text.push(" ");
|
|
}else{
|
|
text.push(html.charAt(i));
|
|
}
|
|
if(html.charAt(i) == ">"){ isTag = false; }
|
|
|
|
}
|
|
return text.join("");
|
|
},
|
|
|
|
_getBookmark: function(/*String*/ eValue){
|
|
// summary:
|
|
// Get the cursor position. It is the index of the characters
|
|
// where the cursor is.
|
|
// eValue:
|
|
// The html value of the editor
|
|
// tags:
|
|
// private
|
|
var ed = this._editor,
|
|
cp = this._cursorSpan;
|
|
ed.execCommand("inserthtml", cp);
|
|
var nv = ed.get("value"),
|
|
index = nv.indexOf(cp),
|
|
i = -1;
|
|
while(++i < index && eValue.charAt(i) == nv.charAt(i)){ /* do nothing */}
|
|
return i;
|
|
},
|
|
|
|
_moveToBookmark: function(){
|
|
// summary:
|
|
// Move to the position when the cursor was.
|
|
// tags:
|
|
// private
|
|
var ed = this._editor,
|
|
cps = dojo.withGlobal(ed.window, "query", dojo, ["." + this._cursorSelector]),
|
|
cursorSpan = cps && cps[0];
|
|
// Find the cursor place holder
|
|
if(cursorSpan){
|
|
ed._sCall("selectElement", [cursorSpan]);
|
|
ed._sCall("collapse", [true]);
|
|
var parent = cursorSpan.parentNode;
|
|
if(parent){ parent.removeChild(cursorSpan); }
|
|
}
|
|
},
|
|
|
|
_submitContent: function(/*Boolean?*/ delay){
|
|
// summary:
|
|
// Functions to submit the content of the editor
|
|
// delay:
|
|
// Indicate if the action is taken immediately or not
|
|
// tags:
|
|
// private
|
|
if(delay){
|
|
var _this = this,
|
|
interval = 3000;
|
|
if(this._delayHandler){
|
|
clearTimeout(this._delayHandler);
|
|
this._delayHandler = null;
|
|
}
|
|
setTimeout(function(){ _this._query(_this._editor.get("value")); }, interval);
|
|
}else{
|
|
this._query(this._editor.get("value"));
|
|
}
|
|
},
|
|
|
|
_populateDialog: function(index){
|
|
// summary:
|
|
// Populate the content of the dailog
|
|
// index:
|
|
// The idex of the span list
|
|
// tags:
|
|
// private
|
|
var list = this._spanList,
|
|
cache = this._cache,
|
|
cont = this._dialogContent;
|
|
|
|
cont.set("disabled", false);
|
|
if(index < list.length && list.length > 0){
|
|
var word = list[index].innerHTML;
|
|
cont.set("unfoundWord", word);
|
|
cont.set("suggestionList", cache[word.toLowerCase()]);
|
|
cont.set("inProgress", false);
|
|
}
|
|
},
|
|
|
|
_markIncorrectWords: function(/*String*/ html, /*Object*/ cache){
|
|
// summary:
|
|
// Mark the incorrect words and set up menus if available
|
|
// html:
|
|
// The html value of the editor
|
|
// cache:
|
|
// The local word cache
|
|
// tags:
|
|
// private
|
|
var _this = this,
|
|
parser = this.parser,
|
|
editor = this._editor,
|
|
spanString = this._incorrectWordsSpan,
|
|
nstyle = this._normalIncorrectStyle,
|
|
selector = this._selector,
|
|
words = parser.parseIntoWords(this._html2Text(html).toLowerCase()),
|
|
indices = parser.getIndices(),
|
|
bookmark = this._cursorSpan,
|
|
bmpos = this._getBookmark(html),
|
|
spanOffset = "<span class='incorrectWordPlaceHolder'>".length,
|
|
bmMarked = false,
|
|
cArray = html.split(""),
|
|
spanList = null;
|
|
|
|
// Mark the incorrect words and cursor position
|
|
for(var i = words.length - 1; i >= 0; i--){
|
|
var word = words[i];
|
|
if(cache[word] && !cache[word].correct){
|
|
var offset = indices[i],
|
|
len = words[i].length,
|
|
end = offset + len;
|
|
if(end <= bmpos && !bmMarked){
|
|
cArray.splice(bmpos, 0, bookmark);
|
|
bmMarked = true;
|
|
}
|
|
cArray.splice(offset, len, dojo.string.substitute(spanString, {text: html.substring(offset, end)}));
|
|
if(offset < bmpos && bmpos < end && !bmMarked){
|
|
var tmp = cArray[offset].split("");
|
|
tmp.splice(spanOffset + bmpos - offset, 0, bookmark);
|
|
cArray[offset] = tmp.join("");
|
|
bmMarked = true;
|
|
}
|
|
}
|
|
}
|
|
if(!bmMarked){
|
|
cArray.splice(bmpos, 0, bookmark);
|
|
bmMarked = true;
|
|
}
|
|
|
|
editor.set("value", cArray.join(""));
|
|
editor._cursorToStart = false; // HACK! But really necessary here.
|
|
|
|
this._moveToBookmark();
|
|
|
|
// Get the incorrect words <span>
|
|
spanList = this._spanList = dojo.withGlobal(editor.window, "query", dojo, ["." + this._selector]);
|
|
dojo.forEach(spanList, function(span, i){ span.id = selector + i; });
|
|
|
|
// Set them to the incorrect word style
|
|
if(!this.interactive){ delete nstyle.cursor; }
|
|
spanList.style(nstyle);
|
|
|
|
if(this.interactive){
|
|
// Build the context menu
|
|
if(_this._contextMenu){
|
|
_this._contextMenu.uninitialize();
|
|
_this._contextMenu = null;
|
|
}
|
|
_this._contextMenu = new dijit.Menu({
|
|
targetNodeIds: [editor.iframe],
|
|
|
|
bindDomNode: function(/*String|DomNode*/ node){
|
|
// summary:
|
|
// Attach menu to given node
|
|
node = dojo.byId(node);
|
|
|
|
var cn; // Connect node
|
|
|
|
// Support context menus on iframes. Rather than binding to the iframe itself we need
|
|
// to bind to the <body> node inside the iframe.
|
|
var iframe, win;
|
|
if(node.tagName.toLowerCase() == "iframe"){
|
|
iframe = node;
|
|
win = this._iframeContentWindow(iframe);
|
|
cn = dojo.withGlobal(win, dojo.body);
|
|
}else{
|
|
|
|
// To capture these events at the top level, attach to <html>, not <body>.
|
|
// Otherwise right-click context menu just doesn't work.
|
|
cn = (node == dojo.body() ? dojo.doc.documentElement : node);
|
|
}
|
|
|
|
|
|
// "binding" is the object to track our connection to the node (ie, the parameter to bindDomNode())
|
|
var binding = {
|
|
node: node,
|
|
iframe: iframe
|
|
};
|
|
|
|
// Save info about binding in _bindings[], and make node itself record index(+1) into
|
|
// _bindings[] array. Prefix w/_dijitMenu to avoid setting an attribute that may
|
|
// start with a number, which fails on FF/safari.
|
|
dojo.attr(node, "_dijitMenu" + this.id, this._bindings.push(binding));
|
|
|
|
// Setup the connections to monitor click etc., unless we are connecting to an iframe which hasn't finished
|
|
// loading yet, in which case we need to wait for the onload event first, and then connect
|
|
// On linux Shift-F10 produces the oncontextmenu event, but on Windows it doesn't, so
|
|
// we need to monitor keyboard events in addition to the oncontextmenu event.
|
|
var doConnects = dojo.hitch(this, function(cn){
|
|
return [
|
|
// TODO: when leftClickToOpen is true then shouldn't space/enter key trigger the menu,
|
|
// rather than shift-F10?
|
|
dojo.connect(cn, this.leftClickToOpen ? "onclick" : "oncontextmenu", this, function(evt){
|
|
var target = evt.target,
|
|
strings = _this._strings;
|
|
// Schedule context menu to be opened unless it's already been scheduled from onkeydown handler
|
|
if(dojo.hasClass(target, selector) && !target.edited){ // Click on the incorrect word
|
|
dojo.stopEvent(evt);
|
|
|
|
// Build the on-demand menu items
|
|
var maxNumber = _this._maxItemNumber,
|
|
id = target.id,
|
|
index = id.substring(selector.length),
|
|
suggestions = cache[target.innerHTML.toLowerCase()],
|
|
slen = suggestions.length;
|
|
|
|
// Add the suggested words menu items
|
|
this.destroyDescendants();
|
|
if(slen == 0){
|
|
this.addChild(new dijit.MenuItem({
|
|
label: strings["iMsg"],
|
|
disabled: true
|
|
}));
|
|
}else{
|
|
for(var i = 0 ; i < maxNumber && i < slen; i++){
|
|
this.addChild(new dijit.MenuItem({
|
|
label: suggestions[i],
|
|
onClick: (function(){
|
|
var idx = index, txt = suggestions[i];
|
|
return function(){
|
|
_this._replaceWord(idx, txt);
|
|
editor.focus();
|
|
};
|
|
})()
|
|
}));
|
|
}
|
|
}
|
|
|
|
//Add the other action menu items
|
|
this.addChild(new dijit.MenuSeparator());
|
|
this.addChild(new dijit.MenuItem({
|
|
label: strings["iSkip"],
|
|
onClick: function(){
|
|
_this._skipWord(index);
|
|
editor.focus();
|
|
}
|
|
}));
|
|
this.addChild(new dijit.MenuItem({
|
|
label: strings["iSkipAll"],
|
|
onClick: function(){
|
|
_this._skipWordAll(index);
|
|
editor.focus();
|
|
}
|
|
}));
|
|
this.addChild(new dijit.MenuSeparator());
|
|
this.addChild(new dijit.MenuItem({
|
|
label: strings["toDic"],
|
|
onClick: function(){
|
|
_this._addWord(index);
|
|
editor.focus();
|
|
}
|
|
}));
|
|
|
|
this._scheduleOpen(target, iframe, {x: evt.pageX, y: evt.pageY});
|
|
}
|
|
}),
|
|
dojo.connect(cn, "onkeydown", this, function(evt){
|
|
if(evt.shiftKey && evt.keyCode == dojo.keys.F10){
|
|
dojo.stopEvent(evt);
|
|
this._scheduleOpen(evt.target, iframe); // no coords - open near target node
|
|
}
|
|
})
|
|
];
|
|
});
|
|
binding.connects = cn ? doConnects(cn) : [];
|
|
|
|
if(iframe){
|
|
// Setup handler to [re]bind to the iframe when the contents are initially loaded,
|
|
// and every time the contents change.
|
|
// Need to do this b/c we are actually binding to the iframe's <body> node.
|
|
// Note: can't use dojo.connect(), see #9609.
|
|
|
|
binding.onloadHandler = dojo.hitch(this, function(){
|
|
// want to remove old connections, but IE throws exceptions when trying to
|
|
// access the <body> node because it's already gone, or at least in a state of limbo
|
|
|
|
var win = this._iframeContentWindow(iframe);
|
|
cn = dojo.withGlobal(win, dojo.body);
|
|
binding.connects = doConnects(cn);
|
|
});
|
|
if(iframe.addEventListener){
|
|
iframe.addEventListener("load", binding.onloadHandler, false);
|
|
}else{
|
|
iframe.attachEvent("onload", binding.onloadHandler);
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
},
|
|
|
|
_selectWord: function(index){
|
|
// summary:
|
|
// Select the incorrect word. Move to it and highlight it
|
|
// index:
|
|
// The index of the span list
|
|
// tags:
|
|
// private
|
|
var list = this._spanList,
|
|
win = this._editor.window;
|
|
|
|
if(index < list.length && list.length > 0){
|
|
dojo.withGlobal(win, "selectElement", dijit._editor.selection, [list[index]]);
|
|
dojo.withGlobal(win, "collapse", dijit._editor.selection, [true]);
|
|
this._findText(list[index].innerHTML, false, false);
|
|
if(dojo.isIE){
|
|
// Because the selection in the iframe will be lost when the outer window get the
|
|
// focus, we need to mimic the highlight ourselves.
|
|
dojo.style(list[index], this._highlightedIncorrectStyle);
|
|
}
|
|
}
|
|
},
|
|
|
|
_replaceWord: function(index, text){
|
|
// summary:
|
|
// Replace the word at the given index with the text
|
|
// index:
|
|
// The index of the span list
|
|
// text:
|
|
// The text to be replaced with
|
|
// tags:
|
|
// private
|
|
var list = this._spanList;
|
|
|
|
list[index].innerHTML = text;
|
|
dojo.style(list[index], this._ignoredIncorrectStyle);
|
|
list[index].edited = true;
|
|
},
|
|
|
|
_skipWord: function(index){
|
|
// summary:
|
|
// Skip the word at the index
|
|
// index:
|
|
// The index of the span list
|
|
// tags:
|
|
// private
|
|
var list = this._spanList;
|
|
|
|
dojo.style(list[index], this._ignoredIncorrectStyle);
|
|
this._cache[list[index].innerHTML.toLowerCase()].correct = true;
|
|
list[index].edited = true;
|
|
},
|
|
|
|
_skipWordAll: function(index, /*String?*/word){
|
|
// summary:
|
|
// Skip the all the word that have the same text as the word at the index
|
|
// or the given word
|
|
// index:
|
|
// The index of the span list
|
|
// word:
|
|
// If this argument is given, skip all the words that have the same text
|
|
// as the word
|
|
// tags:
|
|
// private
|
|
var list = this._spanList,
|
|
len = list.length;
|
|
word = word || list[index].innerHTML.toLowerCase();
|
|
|
|
for(var i = 0; i < len; i++){
|
|
if(!list[i].edited && list[i].innerHTML.toLowerCase() == word){
|
|
this._skipWord(i);
|
|
}
|
|
}
|
|
},
|
|
|
|
_addWord: function(index, /*String?*/word){
|
|
// summary:
|
|
// Add the word at the index to the dictionary
|
|
// index:
|
|
// The index of the span list
|
|
// word:
|
|
// If this argument is given, add the word to the dictionary and
|
|
// skip all the words like it
|
|
// tags:
|
|
// private
|
|
var service = this._service;
|
|
service.send(word || this._spanList[index].innerHTML.toLowerCase(), service.ACTION_UPDATE);
|
|
this._skipWordAll(index, word);
|
|
},
|
|
|
|
_findText: function(/*String*/ txt, /*Boolean*/ caseSensitive, /*Boolean*/ backwards){
|
|
// summary:
|
|
// This function invokes a find with specific options
|
|
// txt: String
|
|
// The text to locate in the document.
|
|
// caseSensitive: Boolean
|
|
// Whether or ot to search case-sensitively.
|
|
// backwards: Boolean
|
|
// Whether or not to search backwards in the document.
|
|
// tags:
|
|
// private.
|
|
// returns:
|
|
// Boolean indicating if the content was found or not.
|
|
var ed = this._editor,
|
|
win = ed.window,
|
|
found = false;
|
|
if(txt){
|
|
if(win.find){
|
|
found = win.find(txt, caseSensitive, backwards, false, false, false, false);
|
|
}else{
|
|
var doc = ed.document;
|
|
if(doc.selection){
|
|
/* IE */
|
|
// Focus to restore position/selection,
|
|
// then shift to search from current position.
|
|
this._editor.focus();
|
|
var txtRg = doc.body.createTextRange();
|
|
var curPos = doc.selection?doc.selection.createRange():null;
|
|
if(curPos){
|
|
if(backwards){
|
|
txtRg.setEndPoint("EndToStart", curPos);
|
|
}else{
|
|
txtRg.setEndPoint("StartToEnd", curPos);
|
|
}
|
|
}
|
|
var flags = caseSensitive?4:0;
|
|
if(backwards){
|
|
flags = flags | 1;
|
|
}
|
|
//flags = flags |
|
|
found = txtRg.findText(txt,txtRg.text.length,flags);
|
|
if(found){
|
|
txtRg.select();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return found;
|
|
},
|
|
|
|
_spellCheckFilter: function(/*String*/ value){
|
|
// summary:
|
|
// Filter out the incorrect word style so that the value of the edtior
|
|
// won't include the spans that wrap around the incorrect words
|
|
// value:
|
|
// The html value of the editor
|
|
// tags:
|
|
// private
|
|
var regText = /<span class=["']incorrectWordPlaceHolder["'].*?>(.*?)<\/span>/g;
|
|
return value.replace(regText, "$1");
|
|
}
|
|
});
|
|
|
|
// Register this plugin.
|
|
dojo.subscribe(dijit._scopeName + ".Editor.getPlugin",null,function(o){
|
|
if(o.plugin){ return; }
|
|
var name = o.args.name.toLowerCase();
|
|
if(name === "spellcheck"){
|
|
o.plugin = new dojox.editor.plugins.SpellCheck({
|
|
url: ("url" in o.args) ? o.args.url : "",
|
|
interactive: ("interactive" in o.args) ? o.args.interactive : false,
|
|
bufferLength: ("bufferLength" in o.args) ? o.args.bufferLength: 100,
|
|
timeout: ("timeout" in o.args) ? o.args.timeout : 30,
|
|
exArgs: o.args
|
|
});
|
|
}
|
|
});
|
|
|
|
});
|