');return this._content=s.Node.create(e({showFilepicker:t,component:n,CSS:i})),this._content.one(o).on("keyup",this._updateTextToDisplay,this),this._content.one(r).on("keyup",this._setTextToDisplayState,this),this._content.one(l).on("click",this._setLink,this),t&&this._content.one(c).on("click",function(t){t.preventDefault(),this.get("host").showFilepicker("link",this._filepickerCallback,this)},this),this._content},_unlink:function(){var e=this.get("host"),t=e.getSelection();t&&t.length&&(t[0].startOffset===t[0].endOffset?(t=e.getSelectedNodes())&&(t.each(function(t){t=t.ancestor("a",!0);t&&(e.setSelection(e.getSelectionFromNode(t)),document.execCommand("unlink",!1,null))},this),this.markUpdated()):(document.execCommand("unlink",!1,null),this.markUpdated()))},_setTextToDisplayState:function(){var t=this._content.one(r).get("value");this._hasTextToDisplay=""!==t},_updateTextToDisplay:function(){var t=this._content.one(o),e=this._content.one(r),t=t.get("value");this._hasTextToDisplay||e.set("value",t)}})},"@VERSION@",{requires:["moodle-editor_atto-plugin"]});
\ No newline at end of file
+YUI.add("moodle-atto_link-button",function(s,t){var n="atto_link",i={NEWWINDOW:"atto_link_openinnewwindow",URLINPUT:"atto_link_urlentry",URLTEXT:"atto_link_urltext"},a=".atto_link_openinnewwindow",o=".atto_link_urlentry",c=".atto_link_urltext",l=".submit",r=".openlinkbrowser";s.namespace("M.atto_link").Button=s.Base.create("button",s.M.editor_atto.EditorPlugin,[],{_currentSelection:null,_content:null,_hasTextToDisplay:!1,_hasPlainTextSelected:!1,initializer:function(){this.addButton({icon:"e/insert_edit_link",keys:"75",callback:this._displayDialogue,tags:"a",tagMatchRequiresAll:!1}),this.addButton({buttonName:"unlink",callback:this._unlink,icon:"e/remove_link",title:"unlink",tags:"a",tagMatchRequiresAll:!1})},_displayDialogue:function(){var t;this._currentSelection=this.get("host").getSelection(),!1!==this._currentSelection&&((t=this.getDialogue({headerContent:M.util.get_string("createlink",n),width:"auto",focusAfterHide:!0,focusOnShowSelector:o})).set("bodyContent",this._getDialogueContent()),this._resolveAnchors(),t.show())},_resolveAnchors:function(){var t,e,n,i=this.get("host").getSelectionParentNode();i&&(0<(i=this._findSelectedAnchors(s.one(i))).length?(i=i[0],this._currentSelection=this.get("host").getSelectionFromNode(i),t=i.getAttribute("href"),e=i.getAttribute("target"),n=i.get("innerText"),i=i.getAttribute("title"),""!==t&&this._content.one(o).setAttribute("value",t),""!==n?this._content.one(c).set("value",n):""!==i&&this._content.one(c).set("value",i),"_blank"===e?this._content.one(a).setAttribute("checked","checked"):this._content.one(a).removeAttribute("checked")):""!==(n=this._getTextSelection())&&(this._hasTextToDisplay=!0,this._hasPlainTextSelected=!0,this._content.one(c).set("value",n)))},_filepickerCallback:function(t){this.getDialogue().set("focusAfterHide",null).hide(),""!==t.url&&(this._setLinkOnSelection(t.url),this.markUpdated())},_setLink:function(t){t.preventDefault(),this.getDialogue({focusAfterHide:null}).hide(),""!==(t=this._content.one(o).get("value"))&&(t=t.trim(),new RegExp(/^[a-zA-Z]*\.*\/|^#|^[a-zA-Z]*:/).test(t)||(t="http://"+t),this._setLinkOnSelection(t),this.markUpdated())},_setLinkOnSelection:function(t){var e,n,i,o,l=this.get("host");if(this.editor.focus(),l.setSelection(this._currentSelection),n=!this._currentSelection[0].collapsed,i=this._content.one(c),""===(o=i.get("value").replace(/(<([^>]+)>)/gi,"").trim())&&(o=t),n?(document.execCommand("unlink",!1,null),document.execCommand("createLink",!1,t),e=l.getSelectionParentNode()):((i=s.Node.create(""+o+"")).setAttribute("href",t),e=l.insertContentAtFocusPoint(i.get("outerHTML")),l.setSelection(l.getSelectionFromNode(e))),e)return t=this._findSelectedAnchors(s.one(e)),s.Array.each(t,function(t){this._content.one(a).get("checked")?t.setAttribute("target","_blank"):t.removeAttribute("target"),n&&o&&(this._hasPlainTextSelected?t.set("innerText",o):t.setAttribute("title",o))},this),e},_findSelectedAnchors:function(t){var e,n,i=t.get("tagName");return i&&"a"===i.toLowerCase()?[t]:(n=[],t.all("a").each(function(t){!e&&this.get("host").selectionContainsNode(t)&&n.push(t)},this),0{{#if showFilepicker}}
{{else}}{{/if}}
');return this._content=s.Node.create(e({showFilepicker:t,component:n,CSS:i})),this._content.one(o).on("keyup",this._updateTextToDisplay,this),this._content.one(o).on("change",this._updateTextToDisplay,this),this._content.one(c).on("keyup",this._setTextToDisplayState,this),this._content.one(l).on("click",this._setLink,this),t&&this._content.one(r).on("click",function(t){t.preventDefault(),this.get("host").showFilepicker("link",this._filepickerCallback,this)},this),this._content},_unlink:function(){var e=this.get("host"),t=e.getSelection();t&&t.length&&(t[0].startOffset===t[0].endOffset?(t=e.getSelectedNodes())&&(t.each(function(t){t=t.ancestor("a",!0);t&&(e.setSelection(e.getSelectionFromNode(t)),document.execCommand("unlink",!1,null))},this),this.markUpdated()):(document.execCommand("unlink",!1,null),this.markUpdated()))},_setTextToDisplayState:function(){var t=this._content.one(c).get("value");this._hasTextToDisplay=""!==t},_updateTextToDisplay:function(){var t=this._content.one(o),e=this._content.one(c),t=t.get("value");this._hasTextToDisplay||e.set("value",t)},_getTextSelection:function(){var t,e,n="",i=window.getSelection(),o=i.rangeCount;if(o){for(t=[],e=0;eSome text here
+ * window.getSelection() will return XYZSome text here
+ *
+ * @returns {string} Selected text
+ * @private
+ */
+ _getTextSelection: function() {
+ var selText = '';
+ var sel = window.getSelection();
+ var rangeCount = sel.rangeCount;
+ if (rangeCount) {
+ var rangeTexts = [];
+ for (var i = 0; i < rangeCount; ++i) {
+ rangeTexts.push('' + sel.getRangeAt(i));
+ }
+ selText = rangeTexts.join('');
+ }
+ return selText;
}
});
diff --git a/lib/editor/atto/plugins/link/yui/src/button/js/button.js b/lib/editor/atto/plugins/link/yui/src/button/js/button.js
index e4b698bca3c..db182c031b7 100644
--- a/lib/editor/atto/plugins/link/yui/src/button/js/button.js
+++ b/lib/editor/atto/plugins/link/yui/src/button/js/button.js
@@ -109,6 +109,14 @@ Y.namespace('M.atto_link').Button = Y.Base.create('button', Y.M.editor_atto.Edit
*/
_hasTextToDisplay: false,
+ /**
+ * User has selected plain text or not.
+ * @property _hasPlainTextSelected
+ * @type Boolean
+ * @private
+ */
+ _hasPlainTextSelected: false,
+
initializer: function() {
// Add the link button first.
this.addButton({
@@ -174,7 +182,8 @@ Y.namespace('M.atto_link').Button = Y.Base.create('button', Y.M.editor_atto.Edit
anchornode,
url,
target,
- textToDisplay;
+ textToDisplay,
+ title;
// Note this is a document fragment and YUI doesn't like them.
if (!selectednode) {
@@ -188,11 +197,14 @@ Y.namespace('M.atto_link').Button = Y.Base.create('button', Y.M.editor_atto.Edit
url = anchornode.getAttribute('href');
target = anchornode.getAttribute('target');
textToDisplay = anchornode.get('innerText');
+ title = anchornode.getAttribute('title');
if (url !== '') {
this._content.one(SELECTORS.URLINPUT).setAttribute('value', url);
}
if (textToDisplay !== '') {
this._content.one(SELECTORS.URLTEXT).set('value', textToDisplay);
+ } else if (title !== '') {
+ this._content.one(SELECTORS.URLTEXT).set('value', title);
}
if (target === '_blank') {
this._content.one(SELECTORS.NEWWINDOW).setAttribute('checked', 'checked');
@@ -201,9 +213,10 @@ Y.namespace('M.atto_link').Button = Y.Base.create('button', Y.M.editor_atto.Edit
}
} else {
// User is selecting some text before clicking on the Link button.
- textToDisplay = window.getSelection().toString();
+ textToDisplay = this._getTextSelection();
if (textToDisplay !== '') {
this._hasTextToDisplay = true;
+ this._hasPlainTextSelected = true;
this._content.one(SELECTORS.URLTEXT).set('value', textToDisplay);
}
}
@@ -323,9 +336,16 @@ Y.namespace('M.atto_link').Button = Y.Base.create('button', Y.M.editor_atto.Edit
} else {
anchornode.removeAttribute('target');
}
- if (isUpdating) {
+ if (isUpdating && textToDisplay) {
// The 'createLink' command do not allow to set the custom text to display. So we need to do it here.
- anchornode.set('innerText', textToDisplay);
+ if (this._hasPlainTextSelected) {
+ // Only replace the innerText if the user has not selected any element or just the plain text.
+ anchornode.set('innerText', textToDisplay);
+ } else {
+ // The user has selected another element to add the hyperlink, like an image.
+ // We should add the title attribute instead of replacing the innerText of the hyperlink.
+ anchornode.setAttribute('title', textToDisplay);
+ }
}
}, this);
@@ -385,6 +405,7 @@ Y.namespace('M.atto_link').Button = Y.Base.create('button', Y.M.editor_atto.Edit
}));
this._content.one(SELECTORS.URLINPUT).on('keyup', this._updateTextToDisplay, this);
+ this._content.one(SELECTORS.URLINPUT).on('change', this._updateTextToDisplay, this);
this._content.one(SELECTORS.URLTEXT).on('keyup', this._setTextToDisplayState, this);
this._content.one(SELECTORS.SUBMIT).on('click', this._setLink, this);
if (canShowFilepicker) {
@@ -477,5 +498,28 @@ Y.namespace('M.atto_link').Button = Y.Base.create('button', Y.M.editor_atto.Edit
if (!this._hasTextToDisplay) {
urlText.set('value', urlEntryVal);
}
+ },
+
+ /**
+ * Get only the selected text.
+ * In some cases, window.getSelection() is not run as expected. We should only get the text value
+ * For ex: Some text here
+ * window.getSelection() will return XYZSome text here
+ *
+ * @returns {string} Selected text
+ * @private
+ */
+ _getTextSelection: function() {
+ var selText = '';
+ var sel = window.getSelection();
+ var rangeCount = sel.rangeCount;
+ if (rangeCount) {
+ var rangeTexts = [];
+ for (var i = 0; i < rangeCount; ++i) {
+ rangeTexts.push('' + sel.getRangeAt(i));
+ }
+ selText = rangeTexts.join('');
+ }
+ return selText;
}
});