LinkDialog.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603
  1. define("dijit/_editor/plugins/LinkDialog", [
  2. "require",
  3. "dojo/_base/declare", // declare
  4. "dojo/dom-attr", // domAttr.get
  5. "dojo/keys", // keys.ENTER
  6. "dojo/_base/lang", // lang.delegate lang.hitch lang.trim
  7. "dojo/_base/sniff", // has("ie")
  8. "dojo/_base/query", // query
  9. "dojo/string", // string.substitute
  10. "dojo/_base/window", // win.withGlobal
  11. "../../_Widget",
  12. "../_Plugin",
  13. "../../form/DropDownButton",
  14. "../range",
  15. "../selection"
  16. ], function(require, declare, domAttr, keys, lang, has, query, string, win,
  17. _Widget, _Plugin, DropDownButton, rangeapi, selectionapi){
  18. /*=====
  19. var _Plugin = dijit._editor._Plugin;
  20. =====*/
  21. // module:
  22. // dijit/_editor/plugins/LinkDialog
  23. // summary:
  24. // Editor plugins: LinkDialog (for inserting links) and ImgLinkDialog (for inserting images)
  25. var LinkDialog = declare("dijit._editor.plugins.LinkDialog", _Plugin, {
  26. // summary:
  27. // This plugin provides the basis for an 'anchor' (link) dialog and an extension of it
  28. // provides the image link dialog.
  29. //
  30. // description:
  31. // The command provided by this plugin is:
  32. // * createLink
  33. // Override _Plugin.buttonClass. This plugin is controlled by a DropDownButton
  34. // (which triggers a TooltipDialog).
  35. buttonClass: DropDownButton,
  36. // Override _Plugin.useDefaultCommand... processing is handled by this plugin, not by dijit.Editor.
  37. useDefaultCommand: false,
  38. // urlRegExp: [protected] String
  39. // Used for validating input as correct URL. While file:// urls are not terribly
  40. // useful, they are technically valid.
  41. urlRegExp: "((https?|ftps?|file)\\://|\./|/|)(/[a-zA-Z]{1,1}:/|)(((?:(?:[\\da-zA-Z](?:[-\\da-zA-Z]{0,61}[\\da-zA-Z])?)\\.)*(?:[a-zA-Z](?:[-\\da-zA-Z]{0,80}[\\da-zA-Z])?)\\.?)|(((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])|(0[xX]0*[\\da-fA-F]?[\\da-fA-F]\\.){3}0[xX]0*[\\da-fA-F]?[\\da-fA-F]|(0+[0-3][0-7][0-7]\\.){3}0+[0-3][0-7][0-7]|(0|[1-9]\\d{0,8}|[1-3]\\d{9}|4[01]\\d{8}|42[0-8]\\d{7}|429[0-3]\\d{6}|4294[0-8]\\d{5}|42949[0-5]\\d{4}|429496[0-6]\\d{3}|4294967[01]\\d{2}|42949672[0-8]\\d|429496729[0-5])|0[xX]0*[\\da-fA-F]{1,8}|([\\da-fA-F]{1,4}\\:){7}[\\da-fA-F]{1,4}|([\\da-fA-F]{1,4}\\:){6}((\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])\\.){3}(\\d|[1-9]\\d|1\\d\\d|2[0-4]\\d|25[0-5])))(\\:\\d+)?(/(?:[^?#\\s/]+/)*(?:[^?#\\s/]{0,}(?:\\?[^?#\\s/]*)?(?:#.*)?)?)?",
  42. // emailRegExp: [protected] String
  43. // Used for validating input as correct email address. Taken from dojox.validate
  44. emailRegExp: "<?(mailto\\:)([!#-'*+\\-\\/-9=?A-Z^-~]+[.])*[!#-'*+\\-\\/-9=?A-Z^-~]+" /*username*/ + "@" +
  45. "((?:(?:[\\da-zA-Z](?:[-\\da-zA-Z]{0,61}[\\da-zA-Z])?)\\.)+(?:[a-zA-Z](?:[-\\da-zA-Z]{0,6}[\\da-zA-Z])?)\\.?)|localhost|^[^-][a-zA-Z0-9_-]*>?", // host.
  46. // htmlTemplate: [protected] String
  47. // String used for templating the HTML to insert at the desired point.
  48. htmlTemplate: "<a href=\"${urlInput}\" _djrealurl=\"${urlInput}\"" +
  49. " target=\"${targetSelect}\"" +
  50. ">${textInput}</a>",
  51. // tag: [protected] String
  52. // Tag used for the link type.
  53. tag: "a",
  54. // _hostRxp [private] RegExp
  55. // Regular expression used to validate url fragments (ip address, hostname, etc)
  56. _hostRxp: /^((([^\[:]+):)?([^@]+)@)?(\[([^\]]+)\]|([^\[:]*))(:([0-9]+))?$/,
  57. // _userAtRxp [private] RegExp
  58. // Regular expression used to validate e-mail address fragment.
  59. _userAtRxp: /^([!#-'*+\-\/-9=?A-Z^-~]+[.])*[!#-'*+\-\/-9=?A-Z^-~]+@/i,
  60. // linkDialogTemplate: [protected] String
  61. // Template for contents of TooltipDialog to pick URL
  62. linkDialogTemplate: [
  63. "<table><tr><td>",
  64. "<label for='${id}_urlInput'>${url}</label>",
  65. "</td><td>",
  66. "<input data-dojo-type='dijit.form.ValidationTextBox' required='true' " +
  67. "id='${id}_urlInput' name='urlInput' data-dojo-props='intermediateChanges:true'/>",
  68. "</td></tr><tr><td>",
  69. "<label for='${id}_textInput'>${text}</label>",
  70. "</td><td>",
  71. "<input data-dojo-type='dijit.form.ValidationTextBox' required='true' id='${id}_textInput' " +
  72. "name='textInput' data-dojo-props='intermediateChanges:true'/>",
  73. "</td></tr><tr><td>",
  74. "<label for='${id}_targetSelect'>${target}</label>",
  75. "</td><td>",
  76. "<select id='${id}_targetSelect' name='targetSelect' data-dojo-type='dijit.form.Select'>",
  77. "<option selected='selected' value='_self'>${currentWindow}</option>",
  78. "<option value='_blank'>${newWindow}</option>",
  79. "<option value='_top'>${topWindow}</option>",
  80. "<option value='_parent'>${parentWindow}</option>",
  81. "</select>",
  82. "</td></tr><tr><td colspan='2'>",
  83. "<button data-dojo-type='dijit.form.Button' type='submit' id='${id}_setButton'>${set}</button>",
  84. "<button data-dojo-type='dijit.form.Button' type='button' id='${id}_cancelButton'>${buttonCancel}</button>",
  85. "</td></tr></table>"
  86. ].join(""),
  87. _initButton: function(){
  88. this.inherited(arguments);
  89. // Setup to lazy create TooltipDialog first time the button is clicked
  90. this.button.loadDropDown = lang.hitch(this, "_loadDropDown");
  91. this._connectTagEvents();
  92. },
  93. _loadDropDown: function(callback){
  94. // Called the first time the button is pressed. Initialize TooltipDialog.
  95. require([
  96. "dojo/i18n", // i18n.getLocalization
  97. "../../TooltipDialog",
  98. "../../registry", // registry.byId, registry.getUniqueId
  99. "../../form/Button", // used by template
  100. "../../form/Select", // used by template
  101. "../../form/ValidationTextBox", // used by template
  102. "dojo/i18n!../../nls/common",
  103. "dojo/i18n!../nls/LinkDialog"
  104. ], lang.hitch(this, function(i18n, TooltipDialog, registry){
  105. var _this = this;
  106. this.tag = this.command == 'insertImage' ? 'img' : 'a';
  107. var messages = lang.delegate(i18n.getLocalization("dijit", "common", this.lang),
  108. i18n.getLocalization("dijit._editor", "LinkDialog", this.lang));
  109. var dropDown = (this.dropDown = this.button.dropDown = new TooltipDialog({
  110. title: messages[this.command + "Title"],
  111. execute: lang.hitch(this, "setValue"),
  112. onOpen: function(){
  113. _this._onOpenDialog();
  114. TooltipDialog.prototype.onOpen.apply(this, arguments);
  115. },
  116. onCancel: function(){
  117. setTimeout(lang.hitch(_this, "_onCloseDialog"),0);
  118. }
  119. }));
  120. messages.urlRegExp = this.urlRegExp;
  121. messages.id = registry.getUniqueId(this.editor.id);
  122. this._uniqueId = messages.id;
  123. this._setContent(dropDown.title +
  124. "<div style='border-bottom: 1px black solid;padding-bottom:2pt;margin-bottom:4pt'></div>" +
  125. string.substitute(this.linkDialogTemplate, messages));
  126. dropDown.startup();
  127. this._urlInput = registry.byId(this._uniqueId + "_urlInput");
  128. this._textInput = registry.byId(this._uniqueId + "_textInput");
  129. this._setButton = registry.byId(this._uniqueId + "_setButton");
  130. this.connect(registry.byId(this._uniqueId + "_cancelButton"), "onClick", function(){
  131. this.dropDown.onCancel();
  132. });
  133. if(this._urlInput){
  134. this.connect(this._urlInput, "onChange", "_checkAndFixInput");
  135. }
  136. if(this._textInput){
  137. this.connect(this._textInput, "onChange", "_checkAndFixInput");
  138. }
  139. // Build up the dual check for http/https/file:, and mailto formats.
  140. this._urlRegExp = new RegExp("^" + this.urlRegExp + "$", "i");
  141. this._emailRegExp = new RegExp("^" + this.emailRegExp + "$", "i");
  142. this._urlInput.isValid = lang.hitch(this, function(){
  143. // Function over-ride of isValid to test if the input matches a url or a mailto style link.
  144. var value = this._urlInput.get("value");
  145. return this._urlRegExp.test(value) || this._emailRegExp.test(value);
  146. });
  147. // Listen for enter and execute if valid.
  148. this.connect(dropDown.domNode, "onkeypress", function(e){
  149. if(e && e.charOrCode == keys.ENTER &&
  150. !e.shiftKey && !e.metaKey && !e.ctrlKey && !e.altKey){
  151. if(!this._setButton.get("disabled")){
  152. dropDown.onExecute();
  153. dropDown.execute(dropDown.get('value'));
  154. }
  155. }
  156. });
  157. callback();
  158. }));
  159. },
  160. _checkAndFixInput: function(){
  161. // summary:
  162. // A function to listen for onChange events and test the input contents
  163. // for valid information, such as valid urls with http/https/ftp and if
  164. // not present, try and guess if the input url is relative or not, and if
  165. // not, append http:// to it. Also validates other fields as determined by
  166. // the internal _isValid function.
  167. var self = this;
  168. var url = this._urlInput.get("value");
  169. var fixupUrl = function(url){
  170. var appendHttp = false;
  171. var appendMailto = false;
  172. if(url && url.length > 1){
  173. url = lang.trim(url);
  174. if(url.indexOf("mailto:") !== 0){
  175. if(url.indexOf("/") > 0){
  176. if(url.indexOf("://") === -1){
  177. // Check that it doesn't start with / or ./, which would
  178. // imply 'target server relativeness'
  179. if(url.charAt(0) !== '/' && url.indexOf("./") !== 0){
  180. if(self._hostRxp.test(url)){
  181. appendHttp = true;
  182. }
  183. }
  184. }
  185. }else if(self._userAtRxp.test(url)){
  186. // If it looks like a foo@, append a mailto.
  187. appendMailto = true;
  188. }
  189. }
  190. }
  191. if(appendHttp){
  192. self._urlInput.set("value", "http://" + url);
  193. }
  194. if(appendMailto){
  195. self._urlInput.set("value", "mailto:" + url);
  196. }
  197. self._setButton.set("disabled", !self._isValid());
  198. };
  199. if(this._delayedCheck){
  200. clearTimeout(this._delayedCheck);
  201. this._delayedCheck = null;
  202. }
  203. this._delayedCheck = setTimeout(function(){
  204. fixupUrl(url);
  205. }, 250);
  206. },
  207. _connectTagEvents: function(){
  208. // summary:
  209. // Over-ridable function that connects tag specific events.
  210. this.editor.onLoadDeferred.addCallback(lang.hitch(this, function(){
  211. this.connect(this.editor.editNode, "ondblclick", this._onDblClick);
  212. }));
  213. },
  214. _isValid: function(){
  215. // summary:
  216. // Internal function to allow validating of the inputs
  217. // for a link to determine if set should be disabled or not
  218. // tags:
  219. // protected
  220. return this._urlInput.isValid() && this._textInput.isValid();
  221. },
  222. _setContent: function(staticPanel){
  223. // summary:
  224. // Helper for _initButton above. Not sure why it's a separate method.
  225. this.dropDown.set({
  226. parserScope: "dojo", // make parser search for dojoType/data-dojo-type even if page is multi-version
  227. content: staticPanel
  228. });
  229. },
  230. _checkValues: function(args){
  231. // summary:
  232. // Function to check the values in args and 'fix' them up as needed.
  233. // args: Object
  234. // Content being set.
  235. // tags:
  236. // protected
  237. if(args && args.urlInput){
  238. args.urlInput = args.urlInput.replace(/"/g, "&quot;");
  239. }
  240. return args;
  241. },
  242. setValue: function(args){
  243. // summary:
  244. // Callback from the dialog when user presses "set" button.
  245. // tags:
  246. // private
  247. //TODO: prevent closing popup if the text is empty
  248. this._onCloseDialog();
  249. if(has("ie") < 9){ //see #4151
  250. var sel = rangeapi.getSelection(this.editor.window);
  251. var range = sel.getRangeAt(0);
  252. var a = range.endContainer;
  253. if(a.nodeType === 3){
  254. // Text node, may be the link contents, so check parent.
  255. // This plugin doesn't really support nested HTML elements
  256. // in the link, it assumes all link content is text.
  257. a = a.parentNode;
  258. }
  259. if(a && (a.nodeName && a.nodeName.toLowerCase() !== this.tag)){
  260. // Still nothing, one last thing to try on IE, as it might be 'img'
  261. // and thus considered a control.
  262. a = win.withGlobal(this.editor.window,
  263. "getSelectedElement", selectionapi, [this.tag]);
  264. }
  265. if(a && (a.nodeName && a.nodeName.toLowerCase() === this.tag)){
  266. // Okay, we do have a match. IE, for some reason, sometimes pastes before
  267. // instead of removing the targeted paste-over element, so we unlink the
  268. // old one first. If we do not the <a> tag remains, but it has no content,
  269. // so isn't readily visible (but is wrong for the action).
  270. if(this.editor.queryCommandEnabled("unlink")){
  271. // Select all the link children, then unlink. The following insert will
  272. // then replace the selected text.
  273. win.withGlobal(this.editor.window,
  274. "selectElementChildren", selectionapi, [a]);
  275. this.editor.execCommand("unlink");
  276. }
  277. }
  278. }
  279. // make sure values are properly escaped, etc.
  280. args = this._checkValues(args);
  281. this.editor.execCommand('inserthtml',
  282. string.substitute(this.htmlTemplate, args));
  283. // IE sometimes leaves a blank link, so we need to fix it up.
  284. // Go ahead and do this for everyone just to avoid blank links
  285. // in the page.
  286. query("a", this.editor.document).forEach(function(a){
  287. if(!a.innerHTML && !domAttr.has(a, "name")){
  288. // Remove empty anchors that do not have "name" set.
  289. // Empty ones with a name set could be a hidden hash
  290. // anchor.
  291. a.parentNode.removeChild(a);
  292. }
  293. }, this);
  294. },
  295. _onCloseDialog: function(){
  296. // summary:
  297. // Handler for close event on the dialog
  298. if(this.editor.focused){
  299. // put focus back in the edit area, unless the dialog closed because the user clicked somewhere else
  300. this.editor.focus();
  301. }
  302. },
  303. _getCurrentValues: function(a){
  304. // summary:
  305. // Over-ride for getting the values to set in the dropdown.
  306. // a:
  307. // The anchor/link to process for data for the dropdown.
  308. // tags:
  309. // protected
  310. var url, text, target;
  311. if(a && a.tagName.toLowerCase() === this.tag){
  312. url = a.getAttribute('_djrealurl') || a.getAttribute('href');
  313. target = a.getAttribute('target') || "_self";
  314. text = a.textContent || a.innerText;
  315. win.withGlobal(this.editor.window, "selectElement", selectionapi, [a, true]);
  316. }else{
  317. text = win.withGlobal(this.editor.window, selectionapi.getSelectedText);
  318. }
  319. return {urlInput: url || '', textInput: text || '', targetSelect: target || ''}; //Object;
  320. },
  321. _onOpenDialog: function(){
  322. // summary:
  323. // Handler for when the dialog is opened.
  324. // If the caret is currently in a URL then populate the URL's info into the dialog.
  325. var a;
  326. if(has("ie") < 9){
  327. // IE is difficult to select the element in, using the range unified
  328. // API seems to work reasonably well.
  329. var sel = rangeapi.getSelection(this.editor.window);
  330. var range = sel.getRangeAt(0);
  331. a = range.endContainer;
  332. if(a.nodeType === 3){
  333. // Text node, may be the link contents, so check parent.
  334. // This plugin doesn't really support nested HTML elements
  335. // in the link, it assumes all link content is text.
  336. a = a.parentNode;
  337. }
  338. if(a && (a.nodeName && a.nodeName.toLowerCase() !== this.tag)){
  339. // Still nothing, one last thing to try on IE, as it might be 'img'
  340. // and thus considered a control.
  341. a = win.withGlobal(this.editor.window,
  342. "getSelectedElement", selectionapi, [this.tag]);
  343. }
  344. }else{
  345. a = win.withGlobal(this.editor.window,
  346. "getAncestorElement", selectionapi, [this.tag]);
  347. }
  348. this.dropDown.reset();
  349. this._setButton.set("disabled", true);
  350. this.dropDown.set("value", this._getCurrentValues(a));
  351. },
  352. _onDblClick: function(e){
  353. // summary:
  354. // Function to define a behavior on double clicks on the element
  355. // type this dialog edits to select it and pop up the editor
  356. // dialog.
  357. // e: Object
  358. // The double-click event.
  359. // tags:
  360. // protected.
  361. if(e && e.target){
  362. var t = e.target;
  363. var tg = t.tagName? t.tagName.toLowerCase() : "";
  364. if(tg === this.tag && domAttr.get(t,"href")){
  365. var editor = this.editor;
  366. win.withGlobal(editor.window,
  367. "selectElement",
  368. selectionapi, [t]);
  369. editor.onDisplayChanged();
  370. // Call onNormalizedDisplayChange() now, rather than on timer.
  371. // On IE, when focus goes to the first <input> in the TooltipDialog, the editor loses it's selection.
  372. // Later if onNormalizedDisplayChange() gets called via the timer it will disable the LinkDialog button
  373. // (actually, all the toolbar buttons), at which point clicking the <input> will close the dialog,
  374. // since (for unknown reasons) focus.js ignores disabled controls.
  375. if(editor._updateTimer){
  376. clearTimeout(editor._updateTimer);
  377. delete editor._updateTimer;
  378. }
  379. editor.onNormalizedDisplayChanged();
  380. var button = this.button;
  381. setTimeout(function(){
  382. // Focus shift outside the event handler.
  383. // IE doesn't like focus changes in event handles.
  384. button.set("disabled", false);
  385. button.loadAndOpenDropDown().then(function(){
  386. if(button.dropDown.focus){
  387. button.dropDown.focus();
  388. }
  389. });
  390. }, 10);
  391. }
  392. }
  393. }
  394. });
  395. var ImgLinkDialog = declare("dijit._editor.plugins.ImgLinkDialog", [LinkDialog], {
  396. // summary:
  397. // This plugin extends LinkDialog and adds in a plugin for handling image links.
  398. // provides the image link dialog.
  399. //
  400. // description:
  401. // The command provided by this plugin is:
  402. // * insertImage
  403. // linkDialogTemplate: [protected] String
  404. // Over-ride for template since img dialog doesn't need target that anchor tags may.
  405. linkDialogTemplate: [
  406. "<table><tr><td>",
  407. "<label for='${id}_urlInput'>${url}</label>",
  408. "</td><td>",
  409. "<input dojoType='dijit.form.ValidationTextBox' regExp='${urlRegExp}' " +
  410. "required='true' id='${id}_urlInput' name='urlInput' data-dojo-props='intermediateChanges:true'/>",
  411. "</td></tr><tr><td>",
  412. "<label for='${id}_textInput'>${text}</label>",
  413. "</td><td>",
  414. "<input data-dojo-type='dijit.form.ValidationTextBox' required='false' id='${id}_textInput' " +
  415. "name='textInput' data-dojo-props='intermediateChanges:true'/>",
  416. "</td></tr><tr><td>",
  417. "</td><td>",
  418. "</td></tr><tr><td colspan='2'>",
  419. "<button data-dojo-type='dijit.form.Button' type='submit' id='${id}_setButton'>${set}</button>",
  420. "<button data-dojo-type='dijit.form.Button' type='button' id='${id}_cancelButton'>${buttonCancel}</button>",
  421. "</td></tr></table>"
  422. ].join(""),
  423. // htmlTemplate: [protected] String
  424. // String used for templating the <img> HTML to insert at the desired point.
  425. htmlTemplate: "<img src=\"${urlInput}\" _djrealurl=\"${urlInput}\" alt=\"${textInput}\" />",
  426. // tag: [protected] String
  427. // Tag used for the link type (img).
  428. tag: "img",
  429. _getCurrentValues: function(img){
  430. // summary:
  431. // Over-ride for getting the values to set in the dropdown.
  432. // a:
  433. // The anchor/link to process for data for the dropdown.
  434. // tags:
  435. // protected
  436. var url, text;
  437. if(img && img.tagName.toLowerCase() === this.tag){
  438. url = img.getAttribute('_djrealurl') || img.getAttribute('src');
  439. text = img.getAttribute('alt');
  440. win.withGlobal(this.editor.window,
  441. "selectElement", selectionapi, [img, true]);
  442. }else{
  443. text = win.withGlobal(this.editor.window, selectionapi.getSelectedText);
  444. }
  445. return {urlInput: url || '', textInput: text || ''}; //Object;
  446. },
  447. _isValid: function(){
  448. // summary:
  449. // Over-ride for images. You can have alt text of blank, it is valid.
  450. // tags:
  451. // protected
  452. return this._urlInput.isValid();
  453. },
  454. _connectTagEvents: function(){
  455. // summary:
  456. // Over-ridable function that connects tag specific events.
  457. this.inherited(arguments);
  458. this.editor.onLoadDeferred.addCallback(lang.hitch(this, function(){
  459. // Use onmousedown instead of onclick. Seems that IE eats the first onclick
  460. // to wrap it in a selector box, then the second one acts as onclick. See #10420
  461. this.connect(this.editor.editNode, "onmousedown", this._selectTag);
  462. }));
  463. },
  464. _selectTag: function(e){
  465. // summary:
  466. // A simple event handler that lets me select an image if it is clicked on.
  467. // makes it easier to select images in a standard way across browsers. Otherwise
  468. // selecting an image for edit becomes difficult.
  469. // e: Event
  470. // The mousedown event.
  471. // tags:
  472. // private
  473. if(e && e.target){
  474. var t = e.target;
  475. var tg = t.tagName? t.tagName.toLowerCase() : "";
  476. if(tg === this.tag){
  477. win.withGlobal(this.editor.window,
  478. "selectElement",
  479. selectionapi, [t]);
  480. }
  481. }
  482. },
  483. _checkValues: function(args){
  484. // summary:
  485. // Function to check the values in args and 'fix' them up as needed
  486. // (special characters in the url or alt text)
  487. // args: Object
  488. // Content being set.
  489. // tags:
  490. // protected
  491. if(args && args.urlInput){
  492. args.urlInput = args.urlInput.replace(/"/g, "&quot;");
  493. }
  494. if(args && args.textInput){
  495. args.textInput = args.textInput.replace(/"/g, "&quot;");
  496. }
  497. return args;
  498. },
  499. _onDblClick: function(e){
  500. // summary:
  501. // Function to define a behavior on double clicks on the element
  502. // type this dialog edits to select it and pop up the editor
  503. // dialog.
  504. // e: Object
  505. // The double-click event.
  506. // tags:
  507. // protected.
  508. if(e && e.target){
  509. var t = e.target;
  510. var tg = t.tagName ? t.tagName.toLowerCase() : "";
  511. if(tg === this.tag && domAttr.get(t,"src")){
  512. var editor = this.editor;
  513. win.withGlobal(editor.window,
  514. "selectElement",
  515. selectionapi, [t]);
  516. editor.onDisplayChanged();
  517. // Call onNormalizedDisplayChange() now, rather than on timer.
  518. // On IE, when focus goes to the first <input> in the TooltipDialog, the editor loses it's selection.
  519. // Later if onNormalizedDisplayChange() gets called via the timer it will disable the LinkDialog button
  520. // (actually, all the toolbar buttons), at which point clicking the <input> will close the dialog,
  521. // since (for unknown reasons) focus.js ignores disabled controls.
  522. if(editor._updateTimer){
  523. clearTimeout(editor._updateTimer);
  524. delete editor._updateTimer;
  525. }
  526. editor.onNormalizedDisplayChanged();
  527. var button = this.button;
  528. setTimeout(function(){
  529. // Focus shift outside the event handler.
  530. // IE doesn't like focus changes in event handles.
  531. button.set("disabled", false);
  532. button.loadAndOpenDropDown().then(function(){
  533. if(button.dropDown.focus){
  534. button.dropDown.focus();
  535. }
  536. });
  537. }, 10);
  538. }
  539. }
  540. }
  541. });
  542. // Register these plugins
  543. _Plugin.registry["createLink"] = function(){
  544. return new LinkDialog({command: "createLink"});
  545. };
  546. _Plugin.registry["insertImage"] = function(){
  547. return new ImgLinkDialog({command: "insertImage"});
  548. };
  549. // Export both LinkDialog and ImgLinkDialog
  550. LinkDialog.ImgLinkDialog = ImgLinkDialog;
  551. return LinkDialog;
  552. });