| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603 | define("dijit/_editor/plugins/LinkDialog", [	"require",	"dojo/_base/declare", // declare	"dojo/dom-attr", // domAttr.get	"dojo/keys", // keys.ENTER	"dojo/_base/lang", // lang.delegate lang.hitch lang.trim	"dojo/_base/sniff", // has("ie")	"dojo/_base/query", // query	"dojo/string", // string.substitute	"dojo/_base/window", // win.withGlobal	"../../_Widget",	"../_Plugin",	"../../form/DropDownButton",	"../range",	"../selection"], function(require, declare, domAttr, keys, lang, has, query, string, win,	_Widget, _Plugin, DropDownButton, rangeapi, selectionapi){/*=====	var _Plugin = dijit._editor._Plugin;=====*/// module://		dijit/_editor/plugins/LinkDialog// summary://		Editor plugins: LinkDialog (for inserting links) and ImgLinkDialog (for inserting images)var LinkDialog = declare("dijit._editor.plugins.LinkDialog", _Plugin, {	// summary:	//		This plugin provides the basis for an 'anchor' (link) dialog and an extension of it	//		provides the image link dialog.	//	// description:	//		The command provided by this plugin is:	//		* createLink	// Override _Plugin.buttonClass.   This plugin is controlled by a DropDownButton	// (which triggers a TooltipDialog).	buttonClass: DropDownButton,	// Override _Plugin.useDefaultCommand... processing is handled by this plugin, not by dijit.Editor.	useDefaultCommand: false,	// urlRegExp: [protected] String	//		Used for validating input as correct URL.  While file:// urls are not terribly	//		useful, they are technically valid.	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/]*)?(?:#.*)?)?)?",	// emailRegExp: [protected] String	//		Used for validating input as correct email address.  Taken from dojox.validate	emailRegExp:  "<?(mailto\\:)([!#-'*+\\-\\/-9=?A-Z^-~]+[.])*[!#-'*+\\-\\/-9=?A-Z^-~]+" /*username*/ + "@" +        "((?:(?:[\\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.	// htmlTemplate: [protected] String	//		String used for templating the HTML to insert at the desired point.	htmlTemplate: "<a href=\"${urlInput}\" _djrealurl=\"${urlInput}\"" +		" target=\"${targetSelect}\"" +		">${textInput}</a>",	// tag: [protected] String	//		Tag used for the link type.	tag: "a",	// _hostRxp [private] RegExp	//		Regular expression used to validate url fragments (ip address, hostname, etc)	_hostRxp: /^((([^\[:]+):)?([^@]+)@)?(\[([^\]]+)\]|([^\[:]*))(:([0-9]+))?$/,	// _userAtRxp [private] RegExp	//		Regular expression used to validate e-mail address fragment.	_userAtRxp: /^([!#-'*+\-\/-9=?A-Z^-~]+[.])*[!#-'*+\-\/-9=?A-Z^-~]+@/i,	// linkDialogTemplate: [protected] String	//		Template for contents of TooltipDialog to pick URL	linkDialogTemplate: [		"<table><tr><td>",		"<label for='${id}_urlInput'>${url}</label>",		"</td><td>",		"<input data-dojo-type='dijit.form.ValidationTextBox' required='true' " +		"id='${id}_urlInput' name='urlInput' data-dojo-props='intermediateChanges:true'/>",		"</td></tr><tr><td>",		"<label for='${id}_textInput'>${text}</label>",		"</td><td>",		"<input data-dojo-type='dijit.form.ValidationTextBox' required='true' id='${id}_textInput' " +		"name='textInput' data-dojo-props='intermediateChanges:true'/>",		"</td></tr><tr><td>",		"<label for='${id}_targetSelect'>${target}</label>",		"</td><td>",		"<select id='${id}_targetSelect' name='targetSelect' data-dojo-type='dijit.form.Select'>",		"<option selected='selected' value='_self'>${currentWindow}</option>",		"<option value='_blank'>${newWindow}</option>",		"<option value='_top'>${topWindow}</option>",		"<option value='_parent'>${parentWindow}</option>",		"</select>",		"</td></tr><tr><td colspan='2'>",		"<button data-dojo-type='dijit.form.Button' type='submit' id='${id}_setButton'>${set}</button>",		"<button data-dojo-type='dijit.form.Button' type='button' id='${id}_cancelButton'>${buttonCancel}</button>",		"</td></tr></table>"	].join(""),	_initButton: function(){		this.inherited(arguments);		// Setup to lazy create TooltipDialog first time the button is clicked		this.button.loadDropDown = lang.hitch(this, "_loadDropDown");		this._connectTagEvents();	},	_loadDropDown: function(callback){		// Called the first time the button is pressed.  Initialize TooltipDialog.		require([			"dojo/i18n", // i18n.getLocalization			"../../TooltipDialog",			"../../registry", // registry.byId, registry.getUniqueId			"../../form/Button",	// used by template			"../../form/Select",	// used by template			"../../form/ValidationTextBox",	// used by template			"dojo/i18n!../../nls/common",			"dojo/i18n!../nls/LinkDialog"		], lang.hitch(this, function(i18n, TooltipDialog, registry){			var _this = this;			this.tag = this.command == 'insertImage' ? 'img' : 'a';			var messages = lang.delegate(i18n.getLocalization("dijit", "common", this.lang),				i18n.getLocalization("dijit._editor", "LinkDialog", this.lang));			var dropDown = (this.dropDown = this.button.dropDown = new TooltipDialog({				title: messages[this.command + "Title"],				execute: lang.hitch(this, "setValue"),				onOpen: function(){					_this._onOpenDialog();					TooltipDialog.prototype.onOpen.apply(this, arguments);				},				onCancel: function(){					setTimeout(lang.hitch(_this, "_onCloseDialog"),0);				}			}));			messages.urlRegExp = this.urlRegExp;			messages.id = registry.getUniqueId(this.editor.id);			this._uniqueId = messages.id;			this._setContent(dropDown.title +				"<div style='border-bottom: 1px black solid;padding-bottom:2pt;margin-bottom:4pt'></div>" +				string.substitute(this.linkDialogTemplate, messages));			dropDown.startup();			this._urlInput = registry.byId(this._uniqueId + "_urlInput");			this._textInput = registry.byId(this._uniqueId + "_textInput");			this._setButton = registry.byId(this._uniqueId + "_setButton");			this.connect(registry.byId(this._uniqueId + "_cancelButton"), "onClick", function(){				this.dropDown.onCancel();			});			if(this._urlInput){				this.connect(this._urlInput, "onChange", "_checkAndFixInput");			}			if(this._textInput){				this.connect(this._textInput, "onChange", "_checkAndFixInput");			}			// Build up the dual check for http/https/file:, and mailto formats.			this._urlRegExp = new RegExp("^" + this.urlRegExp + "$", "i");			this._emailRegExp = new RegExp("^" + this.emailRegExp + "$", "i");			this._urlInput.isValid = lang.hitch(this, function(){				// Function over-ride of isValid to test if the input matches a url or a mailto style link.				var value = this._urlInput.get("value");				return this._urlRegExp.test(value) || this._emailRegExp.test(value);			});			// Listen for enter and execute if valid.			this.connect(dropDown.domNode, "onkeypress", function(e){				if(e && e.charOrCode == keys.ENTER &&					!e.shiftKey && !e.metaKey && !e.ctrlKey && !e.altKey){					if(!this._setButton.get("disabled")){						dropDown.onExecute();						dropDown.execute(dropDown.get('value'));					}				}			});			callback();		}));	},	_checkAndFixInput: function(){		// summary:		//		A function to listen for onChange events and test the input contents		//		for valid information, such as valid urls with http/https/ftp and if		//		not present, try and guess if the input url is relative or not, and if		//		not, append http:// to it.  Also validates other fields as determined by		//		the internal _isValid function.		var self = this;		var url = this._urlInput.get("value");		var fixupUrl = function(url){			var appendHttp = false;			var appendMailto = false;			if(url && url.length > 1){				url = lang.trim(url);				if(url.indexOf("mailto:") !== 0){					if(url.indexOf("/") > 0){						if(url.indexOf("://") === -1){							// Check that it doesn't start with / or ./, which would							// imply 'target server relativeness'							if(url.charAt(0) !== '/' && url.indexOf("./") !== 0){								if(self._hostRxp.test(url)){									appendHttp = true;								}							}						}					}else if(self._userAtRxp.test(url)){						// If it looks like a foo@, append a mailto.						appendMailto = true;					}				}			}			if(appendHttp){				self._urlInput.set("value", "http://" + url);			}			if(appendMailto){				self._urlInput.set("value", "mailto:" + url);			}			self._setButton.set("disabled", !self._isValid());		};		if(this._delayedCheck){			clearTimeout(this._delayedCheck);			this._delayedCheck = null;		}		this._delayedCheck = setTimeout(function(){			fixupUrl(url);		}, 250);	},	_connectTagEvents: function(){		// summary:		//		Over-ridable function that connects tag specific events.		this.editor.onLoadDeferred.addCallback(lang.hitch(this, function(){			this.connect(this.editor.editNode, "ondblclick", this._onDblClick);		}));	},	_isValid: function(){		// summary:		//		Internal function to allow validating of the inputs		//		for a link to determine if set should be disabled or not		// tags:		//		protected		return this._urlInput.isValid() && this._textInput.isValid();	},	_setContent: function(staticPanel){		// summary:		//		Helper for _initButton above.   Not sure why it's a separate method.		this.dropDown.set({			parserScope: "dojo",		// make parser search for dojoType/data-dojo-type even if page is multi-version			content: staticPanel		});	},	_checkValues: function(args){		// summary:		//		Function to check the values in args and 'fix' them up as needed.		// args: Object		//		Content being set.		// tags:		//		protected		if(args && args.urlInput){			args.urlInput = args.urlInput.replace(/"/g, """);		}		return args;	},	setValue: function(args){		// summary:		//		Callback from the dialog when user presses "set" button.		// tags:		//		private		//TODO: prevent closing popup if the text is empty		this._onCloseDialog();		if(has("ie") < 9){ //see #4151			var sel = rangeapi.getSelection(this.editor.window);			var range = sel.getRangeAt(0);			var a = range.endContainer;			if(a.nodeType === 3){				// Text node, may be the link contents, so check parent.				// This plugin doesn't really support nested HTML elements				// in the link, it assumes all link content is text.				a = a.parentNode;			}			if(a && (a.nodeName && a.nodeName.toLowerCase() !== this.tag)){				// Still nothing, one last thing to try on IE, as it might be 'img'				// and thus considered a control.				a = win.withGlobal(this.editor.window,					"getSelectedElement", selectionapi, [this.tag]);			}			if(a && (a.nodeName && a.nodeName.toLowerCase() === this.tag)){				// Okay, we do have a match.  IE, for some reason, sometimes pastes before				// instead of removing the targeted paste-over element, so we unlink the				// old one first.  If we do not the <a> tag remains, but it has no content,				// so isn't readily visible (but is wrong for the action).				if(this.editor.queryCommandEnabled("unlink")){					// Select all the link children, then unlink.  The following insert will					// then replace the selected text.					win.withGlobal(this.editor.window,						"selectElementChildren", selectionapi, [a]);					this.editor.execCommand("unlink");				}			}		}		// make sure values are properly escaped, etc.		args = this._checkValues(args);		this.editor.execCommand('inserthtml',			string.substitute(this.htmlTemplate, args));		// IE sometimes leaves a blank link, so we need to fix it up.		// Go ahead and do this for everyone just to avoid blank links		// in the page.		query("a", this.editor.document).forEach(function(a){			if(!a.innerHTML && !domAttr.has(a, "name")){				// Remove empty anchors that do not have "name" set.				// Empty ones with a name set could be a hidden hash				// anchor.				a.parentNode.removeChild(a);			}		}, this);	},	_onCloseDialog: function(){		// summary:		//		Handler for close event on the dialog		if(this.editor.focused){			// put focus back in the edit area, unless the dialog closed because the user clicked somewhere else			this.editor.focus();		}	},	_getCurrentValues: function(a){		// summary:		//		Over-ride for getting the values to set in the dropdown.		// a:		//		The anchor/link to process for data for the dropdown.		// tags:		//		protected		var url, text, target;		if(a && a.tagName.toLowerCase() === this.tag){			url = a.getAttribute('_djrealurl') || a.getAttribute('href');			target = a.getAttribute('target') || "_self";			text = a.textContent || a.innerText;			win.withGlobal(this.editor.window, "selectElement", selectionapi, [a, true]);		}else{			text = win.withGlobal(this.editor.window, selectionapi.getSelectedText);		}		return {urlInput: url || '', textInput: text || '', targetSelect: target || ''}; //Object;	},	_onOpenDialog: function(){		// summary:		//		Handler for when the dialog is opened.		//		If the caret is currently in a URL then populate the URL's info into the dialog.		var a;		if(has("ie") < 9){			// IE is difficult to select the element in, using the range unified			// API seems to work reasonably well.			var sel = rangeapi.getSelection(this.editor.window);			var range = sel.getRangeAt(0);			a = range.endContainer;			if(a.nodeType === 3){				// Text node, may be the link contents, so check parent.				// This plugin doesn't really support nested HTML elements				// in the link, it assumes all link content is text.				a = a.parentNode;			}			if(a && (a.nodeName && a.nodeName.toLowerCase() !== this.tag)){				// Still nothing, one last thing to try on IE, as it might be 'img'				// and thus considered a control.				a = win.withGlobal(this.editor.window,					"getSelectedElement", selectionapi, [this.tag]);			}		}else{			a = win.withGlobal(this.editor.window,				"getAncestorElement", selectionapi, [this.tag]);		}		this.dropDown.reset();		this._setButton.set("disabled", true);		this.dropDown.set("value", this._getCurrentValues(a));	},	_onDblClick: function(e){		// summary:		// 		Function to define a behavior on double clicks on the element		//		type this dialog edits to select it and pop up the editor		//		dialog.		// e: Object		//		The double-click event.		// tags:		//		protected.		if(e && e.target){			var t = e.target;			var tg = t.tagName? t.tagName.toLowerCase() : "";			if(tg === this.tag && domAttr.get(t,"href")){				var editor = this.editor;				win.withGlobal(editor.window,					 "selectElement",					 selectionapi, [t]);				editor.onDisplayChanged();				// Call onNormalizedDisplayChange() now, rather than on timer.				// On IE, when focus goes to the first <input> in the TooltipDialog, the editor loses it's selection.				// Later if onNormalizedDisplayChange() gets called via the timer it will disable the LinkDialog button				// (actually, all the toolbar buttons), at which point clicking the <input> will close the dialog,				// since (for unknown reasons) focus.js ignores disabled controls.				if(editor._updateTimer){					clearTimeout(editor._updateTimer);					delete editor._updateTimer;				}				editor.onNormalizedDisplayChanged();				var button = this.button;				setTimeout(function(){					// Focus shift outside the event handler.					// IE doesn't like focus changes in event handles.					button.set("disabled", false);					button.loadAndOpenDropDown().then(function(){						if(button.dropDown.focus){							button.dropDown.focus();						}					});				}, 10);			}		}	}});var ImgLinkDialog = declare("dijit._editor.plugins.ImgLinkDialog", [LinkDialog], {	// summary:	//		This plugin extends LinkDialog and adds in a plugin for handling image links.	//		provides the image link dialog.	//	// description:	//		The command provided by this plugin is:	//		* insertImage	// linkDialogTemplate: [protected] String	//		Over-ride for template since img dialog doesn't need target that anchor tags may.	linkDialogTemplate: [		"<table><tr><td>",		"<label for='${id}_urlInput'>${url}</label>",		"</td><td>",		"<input dojoType='dijit.form.ValidationTextBox' regExp='${urlRegExp}' " +		"required='true' id='${id}_urlInput' name='urlInput' data-dojo-props='intermediateChanges:true'/>",		"</td></tr><tr><td>",		"<label for='${id}_textInput'>${text}</label>",		"</td><td>",		"<input data-dojo-type='dijit.form.ValidationTextBox' required='false' id='${id}_textInput' " +		"name='textInput' data-dojo-props='intermediateChanges:true'/>",		"</td></tr><tr><td>",		"</td><td>",		"</td></tr><tr><td colspan='2'>",		"<button data-dojo-type='dijit.form.Button' type='submit' id='${id}_setButton'>${set}</button>",		"<button data-dojo-type='dijit.form.Button' type='button' id='${id}_cancelButton'>${buttonCancel}</button>",		"</td></tr></table>"	].join(""),	// htmlTemplate: [protected] String	//		String used for templating the <img> HTML to insert at the desired point.	htmlTemplate: "<img src=\"${urlInput}\" _djrealurl=\"${urlInput}\" alt=\"${textInput}\" />",	// tag: [protected] String	//		Tag used for the link type (img).	tag: "img",	_getCurrentValues: function(img){		// summary:		//		Over-ride for getting the values to set in the dropdown.		// a:		//		The anchor/link to process for data for the dropdown.		// tags:		//		protected		var url, text;		if(img && img.tagName.toLowerCase() === this.tag){			url = img.getAttribute('_djrealurl') || img.getAttribute('src');			text = img.getAttribute('alt');			win.withGlobal(this.editor.window,				"selectElement", selectionapi, [img, true]);		}else{			text = win.withGlobal(this.editor.window, selectionapi.getSelectedText);		}		return {urlInput: url || '', textInput: text || ''}; //Object;	},	_isValid: function(){		// summary:		//		Over-ride for images.  You can have alt text of blank, it is valid.		// tags:		//		protected		return this._urlInput.isValid();	},	_connectTagEvents: function(){		// summary:		//		Over-ridable function that connects tag specific events.		this.inherited(arguments);		this.editor.onLoadDeferred.addCallback(lang.hitch(this, function(){			// Use onmousedown instead of onclick.  Seems that IE eats the first onclick			// to wrap it in a selector box, then the second one acts as onclick.  See #10420			this.connect(this.editor.editNode, "onmousedown", this._selectTag);		}));	},	_selectTag: function(e){		// summary:		//		A simple event handler that lets me select an image if it is clicked on.		//		makes it easier to select images in a standard way across browsers.  Otherwise		//		selecting an image for edit becomes difficult.		// e: Event		//		The mousedown event.		// tags:		//		private		if(e && e.target){			var t = e.target;			var tg = t.tagName? t.tagName.toLowerCase() : "";			if(tg === this.tag){				win.withGlobal(this.editor.window,					"selectElement",					selectionapi, [t]);			}		}	},	_checkValues: function(args){		// summary:		//		Function to check the values in args and 'fix' them up as needed		//		(special characters in the url or alt text)		// args: Object		//		Content being set.		// tags:		//		protected		if(args && args.urlInput){			args.urlInput = args.urlInput.replace(/"/g, """);		}		if(args && args.textInput){			args.textInput = args.textInput.replace(/"/g, """);		}		return args;	},	_onDblClick: function(e){		// summary:		// 		Function to define a behavior on double clicks on the element		//		type this dialog edits to select it and pop up the editor		//		dialog.		// e: Object		//		The double-click event.		// tags:		//		protected.		if(e && e.target){			var t = e.target;			var tg = t.tagName ? t.tagName.toLowerCase() : "";			if(tg === this.tag && domAttr.get(t,"src")){				var editor = this.editor;				win.withGlobal(editor.window,					 "selectElement",					 selectionapi, [t]);				editor.onDisplayChanged();				// Call onNormalizedDisplayChange() now, rather than on timer.				// On IE, when focus goes to the first <input> in the TooltipDialog, the editor loses it's selection.				// Later if onNormalizedDisplayChange() gets called via the timer it will disable the LinkDialog button				// (actually, all the toolbar buttons), at which point clicking the <input> will close the dialog,				// since (for unknown reasons) focus.js ignores disabled controls.				if(editor._updateTimer){					clearTimeout(editor._updateTimer);					delete editor._updateTimer;				}				editor.onNormalizedDisplayChanged();				var button = this.button;				setTimeout(function(){					// Focus shift outside the event handler.					// IE doesn't like focus changes in event handles.					button.set("disabled", false);					button.loadAndOpenDropDown().then(function(){						if(button.dropDown.focus){							button.dropDown.focus();						}					});				}, 10);			}		}	}});// Register these plugins_Plugin.registry["createLink"] = function(){	return new LinkDialog({command: "createLink"});};_Plugin.registry["insertImage"] = function(){	return new ImgLinkDialog({command: "insertImage"});};// Export both LinkDialog and ImgLinkDialogLinkDialog.ImgLinkDialog = ImgLinkDialog;return LinkDialog;});
 |