Created
August 18, 2013 13:40
-
-
Save miracle2k/6261717 to your computer and use it in GitHub Desktop.
CKEditor 3/4 plugin that adds a new "internal link" option to the Link dialog window. This is an adaption of Henri Medot's Drupal integration plugin (https://drupal.org/project/ckeditor_link) to a custom CMS system. I'm publishing it here because the original code includes no comments, which meant I had to figure out the CKEditor API and what th…
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/** | |
* Add an "CMS internal link" option to the Link plugin. | |
* | |
* This was adapted from a similar plugin for Drupal (version 7.x-2.3): | |
* Written by Henri MEDOT <henri.medot[AT]absyx[DOT]fr> | |
* http://www.absyx.fr | |
* https://drupal.org/project/ckeditor_link | |
* | |
* Portions of code: | |
* Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. | |
* For licensing, see LICENSE.html or http://ckeditor.com/license | |
*/ | |
(function($) { | |
// Get a CKEDITOR.dialog.contentDefinition object by its ID. | |
var getById = function(array, id, recurse) { | |
for (var i = 0, item; (item = array[i]); i++) { | |
if (item.id == id) return item; | |
if (recurse && item[recurse]) { | |
var retval = getById(item[recurse], id, recurse); | |
if (retval) return retval; | |
} | |
} | |
return null; | |
}; | |
var resetInitValues = function(dialog) { | |
dialog.foreach(function(contentObj) { | |
contentObj.setInitValue && contentObj.setInitValue(); | |
}); | |
}; | |
CKEDITOR.plugins.add('aitdlink', { | |
init: function(editor, pluginPath) { | |
// Hook us into the creation process of the link dialog | |
CKEDITOR.on('dialogDefinition', function(e) { | |
if ((e.editor != editor) || (e.data.name != 'link')) | |
return; | |
var definition = e.data.definition; | |
// When OK is pressed in the dialog. In some cases we need to | |
// post-process the link we are inserting. | |
definition.onOk = CKEDITOR.tools.override(definition.onOk, function(original) { | |
return function() { | |
var isNewLinkWithText = false; | |
// Test if there is no selection in the editor (that is, just | |
// a simple blinking cursor - a "collapsed" range), and we are | |
// not editing an existing link below the cursor (selectedElement), | |
// but are inserting a new one. | |
if ((this.getValueOf('info', 'linkType') == 'aitd') && !this._.selectedElement) { | |
var ranges = editor.getSelection().getRanges(true); | |
if ((ranges.length == 1) && ranges[0].collapsed) { | |
// This means CKEditor will not just add a href to an | |
// existing piece of text, but will also insert the link | |
// text itself, which will be the url. | |
isNewLinkWithText = true; | |
} | |
} | |
// Let the original link dialog insert the link into the text. | |
// We can't really customize this code, so we need to make our | |
// changes afterwards | |
original.call(this); | |
// If CKEditor must created a new link with "aitd://1234" as | |
// the text, which is tremendously unuseful, then we will | |
// replace that text with the title of the page linked to. | |
if (isNewLinkWithText) { | |
var value = getPageSpan(dialog).getAttribute('pageTitle'); | |
if (value) { | |
CKEDITOR.plugins.link.getSelectedLink(editor).setText(value); | |
} | |
} | |
}; | |
}); | |
// Overrides linkType definition. | |
var infoTab = definition.getContents('info'); | |
// Add a "internal link" option to the link type select box. | |
var linkTypeSelect = getById(infoTab.elements, 'linkType'); | |
linkTypeSelect.items.unshift(['Interner Link', 'aitd']); | |
/* Get the DOM element to which we attach our runtime data (page | |
target of the link) as data attributes. Helper because it is | |
such a long call. | |
*/ | |
var getPageSpan = function(dialog) { | |
var pageIdDisplay = dialog.getContentElement('info', 'aitdPage'); | |
return pageIdDisplay.getElement().$; | |
}; | |
/* Helper to set the currently selected target page (internal id | |
and visible link description). | |
*/ | |
var setTargetPage = function(dialog, page, version) { | |
var span = getPageSpan(dialog); | |
if (page) { | |
span.setAttribute('pageId', page.getId()); | |
if (version) | |
span.setAttribute('versionId', version.getId()); | |
else | |
span.removeAttribute('versionId'); | |
span.setAttribute('pageTitle', page.getLinktext()); | |
var label = page.getPathString(); | |
if (version) | |
label += ' ('+ version.getModified() +')'; | |
jQuery(span).text(label); | |
} | |
else { | |
span.removeAttribute('pageId'); | |
span.removeAttribute('versionId'); | |
jQuery(span).text(''); | |
} | |
}; | |
// Add the UI that is shown when the user selects our new link type | |
// option from the select box. | |
infoTab.elements.push({ | |
type: 'vbox', | |
id: 'aitdOptions', | |
children: [{ | |
type: 'button', | |
label: 'Zielseite auswählen', | |
onClick: function() { | |
// Showing a Qooxdoo dialog here is problematic, because | |
// the CKEditor UI system is working on a completely different | |
// z-index plane than Qooxdoo; thus, a Qooxdoo window that | |
// we open here will be behind the CkEditor link dialog (AND | |
// its blocker). | |
// | |
// There are only two options solving this: | |
// 1) Trying to make CKEditor insert its dialog within | |
// the Qooxdoo hierarchy. This is probably hard. | |
// 2) The workaround we are using, which is hiding the | |
// CKEditor link dialog while we are showing the Qooxdoo | |
// link chooser. | |
jQuery('.cke_dialog_background_cover').hide(); | |
var dialog = this.getDialog(); | |
dialog._.element.setStyle('display', 'none'); | |
editor.qooxdooDialog.showBrowsePageDialog( | |
// init values | |
getPageSpan(dialog).getAttribute('pageId'), | |
getPageSpan(dialog).getAttribute('versionId'), | |
// on page select | |
function(page, version) { | |
setTargetPage(dialog, page, version); | |
}, | |
// on window close | |
function() { | |
// Show the CkEditor dialog again once Qooxdoo window is gone. | |
jQuery('.cke_dialog_background_cover').show(); | |
dialog._.element.setStyle('display', 'block'); | |
}); | |
return true; | |
} | |
}, | |
// This label indicates the currently selected page to the user. | |
// Since I am not aware of a better way "proper" way to attach | |
// such data to a CKEditor dialog instance, we also use this label | |
// to store the correctly selected target page as data attributes. | |
{ | |
type: 'html', | |
id: 'aitdPage', | |
html: '<span style="white-space: normal"></span>', // disable nowrap | |
validate: function() { | |
var pageId = this.getElement().$.getAttribute('pageId'); | |
if (!pageId) { | |
alert('Sie müssen eine Zielseite auswählen.'); | |
return false; | |
} | |
} | |
}] | |
}); | |
// When the user picks a new type in the select box: | |
// Show or hide our controls. | |
linkTypeSelect.onChange = CKEDITOR.tools.override(linkTypeSelect.onChange, function(original) { | |
return function() { | |
// Run the default logic (handles all other select items) | |
original.call(this); | |
// Fetch our UI that we've added to the dialog. | |
var dialog = this.getDialog(); | |
var ourUIControls = dialog.getContentElement('info', 'aitdOptions') | |
.getElement().getParent().getParent(); | |
// Handle our own link type option | |
if (this.getValue() == 'aitd') { | |
ourUIControls.show(); | |
// Set the visible states for CkEditor's linkTarget and upload | |
// tabs for our option. | |
if (editor.config.linkShowTargetTab) | |
dialog.showPage('target'); | |
var uploadTab = dialog.definition.getContents('upload'); | |
if (uploadTab && !uploadTab.hidden) | |
dialog.hidePage('upload'); | |
} | |
else { | |
ourUIControls.hide(); | |
} | |
}; | |
}); | |
// When the type select box is initialized with a value. The | |
// original is a "setValue(data.type)" one-liner, so we can | |
// replace it in full. | |
linkTypeSelect.setup = function(data) { | |
// Make our option the default for all isn't an existing url. | |
if (!data.type || (data.type == 'url') && !data.url) { | |
data.type = 'aitd'; | |
// Dialog instance is re-used, so reset selection | |
setTargetPage(this.getDialog(), null); | |
} | |
// If there is a url, but no protocol, that indicates to us we | |
// are dealing with an internal aitd:// url (the link dialog | |
// will only parse the protocols it supports, thus the protocol | |
// is empty, and the full url contained in data.url). | |
else if (data.url && !data.url.protocol && data.url.url) { | |
// Initialize dialog as internal link | |
data.type = 'aitd'; | |
// Store the selected page, show it's title. | |
var parsed = data.url.url.match(/(?:aitd|internal):\/\/(\d+)(?:#(\d+))?/); | |
var pageId = parsed[1], versionId = parsed[2]; | |
var page = editor.qooxdooDialog.getPage(pageId); | |
var version = editor.qooxdooDialog.getPageVersion(page, versionId); | |
setTargetPage(this.getDialog(), page, version); | |
// Do not make the "url link type" processing to jump into action | |
delete data.url; | |
} | |
this.setValue(data.type); | |
}; | |
// When the type select box is supposed to save its value | |
linkTypeSelect.commit = function(data) { | |
data.type = this.getValue(); | |
if (data.type == 'aitd') { | |
// We have a problem here. The the code where the CKEditor | |
// dialog constructs the link to be inserted cannot reasonably | |
// be patched by us. That code does a switch() on data.type, | |
// and will not be able to handle our custom value of 'aitd'. | |
// | |
// Our only option is to use the default 'url' type to have | |
// our link being properly created. | |
data.type = 'url'; | |
// The "url" link type stores it's values in "data.url.url" | |
// and "data.url.protocol". However, we cannot set these | |
// fields directly, because this commit() is running before | |
// before the commit()s of the respective input fields the | |
// "url" link type uses - which would override our values. | |
// | |
// We thus need to set the text field values directly. | |
// | |
// The problem with THAT is that the "protocol" select field | |
// will not accept invalid values, we cannot set it to aitd://. | |
// We set it to empty instead, and include aitd:// in the url. | |
var dialog = this.getDialog(); | |
var link = "aitd://" + getPageSpan(dialog).getAttribute('pageId'); | |
var versionId = getPageSpan(dialog).getAttribute('versionId'); | |
if (versionId) | |
link += '#' + versionId; | |
dialog.setValueOf('info', 'protocol', ''); | |
dialog.setValueOf('info', 'url', link); | |
} | |
}; | |
}); | |
} | |
}); | |
})(jQuery); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment