Created
June 15, 2018 06:06
-
-
Save rogerblanton/89f5bf6515a75850f8202dcc393dadca to your computer and use it in GitHub Desktop.
A plugin that allows you to author modals based on Bootstrap 3 in AEM RTE.
This file contains 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
(function ($) { | |
"use strict"; | |
// Setup Constants | |
var modalPlugin = { | |
GROUP: "modalPlugin", | |
DEBUG: true, | |
FEATURE: "modal", | |
MODAL_DIALOG: "modaldialog", | |
MODAL_UI_SETTING: "modalPlugin#modal" | |
}; | |
var logger = function(msg, callback) { | |
if (!modalPlugin.DEBUG || !console) { | |
return; | |
} | |
if (msg && (typeof msg ==='string')) { | |
console.log(msg); | |
} | |
if (callback) { | |
callback(); | |
} | |
}; | |
//returns the picker dialog html | |
//Handlebars doesn't do anything useful here, but the framework expects a template | |
var modalTemplate = function () { | |
CUI.rte.templates["dlg-" + modalPlugin.MODAL_DIALOG] = | |
Handlebars.compile( | |
'<div class="rte-dialog-columnContainer">' | |
+ '<div class="rte-dialog-column">' | |
+ '<label>' | |
+ '<input type="text" id="modalPath" placeholder="Modal Target" class="coral-Form-field coral-Textfield">' | |
+ '</label>' | |
+ '</div>' | |
+ '<div class="rte-dialog-column">' | |
+ '<button data-type="apply" class="coral-RichText-dialogButton coral-Button coral-Button--square coral-Button--primary" title="Apply">' | |
+ '<i class="coral-Icon coral-Icon--check coral-Icon--sizeS"></i>' | |
+ '</button>' | |
+ '</div>' | |
+ '<div class="rte-dialog-column">' | |
+ '<button data-type="cancel" class="coral-RichText-dialogButton coral-Button coral-Button--square" title="Cancel">' | |
+ '<i class="coral-Icon coral-Icon--close coral-Icon--sizeS"></i>' | |
+ '</button>' | |
+ '</div>' | |
+ '</div>' | |
); | |
CUI.rte.Templates = CUI.rte.templates || {}; | |
}; | |
modalTemplate(); | |
/** | |
* | |
* @see /libs/clientlibs/granite/richtext/js/rte/ui/CuiToolbarBuilder.js for Super Class | |
* | |
* @type {Class} | |
* @extends CUI.rte.ui.cui.CuiToolbarBuilder | |
*/ | |
modalPlugin.CuiToolbarBuilder = new Class({ | |
toString: "ModalToolbarBuilder", | |
extend: CUI.rte.ui.cui.CuiToolbarBuilder, | |
_getUISettings: function (options) { | |
logger("in modalPlugin.CuiToolbarBuilder._getUISettings", function() { | |
console.log(options); | |
}); | |
if (!this._getClassesForCommand(modalPlugin.MODAL_UI_SETTING)) { | |
this.registerAdditionalClasses(modalPlugin.MODAL_UI_SETTING, "coral-Icon touchui-modal coral-RichText--trigger"); | |
} | |
return this.superClass._getUISettings(options); | |
} | |
}); | |
/** | |
* | |
* @see /libs/clientlibs/granite/richtext/js/rte/ui/dialogs/AbstractBaseDialog.js | |
* | |
* @type {Class} | |
* @extends CUI.rte.ui.cui.AbstractBaseDialog | |
*/ | |
modalPlugin.ModalDialog = new Class({ | |
extend: CUI.rte.ui.cui.AbstractDialog, | |
toString: "ModalDialog", | |
getDataType: function () { | |
return modalPlugin.MODAL_DIALOG; | |
}, | |
// method called everytime the dialog is closed, this is useful because sometimes the dialog can be closed | |
// without clicking the 'cancel' button. | |
onHide: function () { | |
//reset the popover values to empty when closed | |
this.$linkTitle.val(""); | |
}, | |
initialize: function (config) { | |
logger("In modalPlugin.ModalDialog.initialize", function() { | |
console.log(config); | |
}); | |
this.exec = config.execute; | |
var context = this.editorKernel.editContext, | |
selection = CUI.rte.Selection.createProcessingSelection(context), | |
tag = CUI.rte.Common.getTagInPath(context, selection.startNode, "span"); | |
this.$linkTitle = $('#modalPath'); | |
}, | |
attach: function (config, $container, editorKernel, enforceCreation) { | |
this.superClass.attach.call(this, config, $container, editorKernel); | |
}, | |
// this method is called when the dialog is closed via the 'checkbox' e.g. to save the input | |
apply: function () { | |
// Getting the values of from the popover and passing them to the Command | |
var options, | |
modalPathValue = $('#modalPath').val(); | |
if (modalPathValue !== "") { | |
options = options || {}; | |
options.linkTitle = modalPathValue; | |
} | |
// this calls the execute method in teh dialogConfig object below, which in turns, calls the relayCommand to | |
// the command class. | |
this.exec(options); | |
// hide the dialog | |
this.hide(); | |
}, | |
// this method is called when the 'x' is clicked in the dialog, e.g. user interaction is cancelled. | |
cancel: function () { | |
// hide the dialog | |
this.hide(); | |
} | |
}); | |
/** | |
* | |
* @see /libs/clientlibs/granite/richtext/js/rte/ui/CuiDialogManager.js for Super class | |
* | |
* @type {Class} | |
* @extends CUI.rte.ui.cui.CuiDialogManager | |
*/ | |
modalPlugin.DialogManager = new Class({ | |
extend: CUI.rte.ui.cui.CuiDialogManager, | |
toString: "ModalDialogManager", | |
create: function (dialogId, config) { | |
logger("modalPlugin.DialogManager.create", function() { | |
console.log(dialogId, config); | |
}); | |
if (dialogId !== modalPlugin.MODAL_DIALOG) { | |
return this.superClass.create.call(this, dialogId, config); | |
} | |
var context = this.editorKernel.getEditContext(), | |
$container = CUI.rte.UIUtils.getUIContainer($(context.root)), | |
dialog = new modalPlugin.ModalDialog(); | |
dialog.attach(config, $container, this.editorKernel, true); | |
return dialog; | |
} | |
}); | |
/** | |
* | |
* extend CUI toolkit impl to create instances of extended toolbar builder and dialog manager | |
* | |
* @type {Class} | |
* @extends CUI.rte.ui.cui.ToolkitImpl | |
*/ | |
modalPlugin.ToolkitImpl = new Class({ | |
toString: "ModalToolkitImpl", | |
extend: CUI.rte.ui.cui.ToolkitImpl, | |
createToolbarBuilder: function () { | |
logger("in modalPlugin.ToolkitImpl,createToolbarBuilder"); | |
return new modalPlugin.CuiToolbarBuilder(); | |
}, | |
createDialogManager: function (editorKernel) { | |
logger("in modalPlugin.ToolkitImpl,createDialogManager"); | |
return new modalPlugin.DialogManager(editorKernel); | |
} | |
}); | |
// see /etc/clientlibs/granite/coralui2/optional/rte/js/core/ui/ToolkitRegistry.js | |
CUI.rte.ui.ToolkitRegistry.register("modal", modalPlugin.ToolkitImpl); | |
CUI.rte.ui.ToolkitRegistry.initialize("modal"); | |
/** | |
* | |
* @type {Class} | |
* @extends CUI.rte.plugins.Plugin | |
*/ | |
modalPlugin.ModalPlugin = new Class({ | |
toString: "ModalPlugin", | |
extend: CUI.rte.plugins.Plugin, | |
modalUI: null, | |
getFeatures: function () { | |
return [modalPlugin.FEATURE]; | |
}, | |
initializeUI: function (toolbarGenerator) { | |
var plg = CUI.rte.plugins; | |
// Create the Toolbar Element | |
if (this.isFeatureEnabled(modalPlugin.FEATURE)) { | |
var tooltip = {}; | |
tooltip.title="Modal"; | |
this.modalUI = toolbarGenerator.createElement(modalPlugin.FEATURE, this, true, tooltip); | |
// Add the toolbar element to the toolbar | |
toolbarGenerator.addElement("demo", plg.Plugin.SORT_FORMAT, this.modalUI, 140); | |
} | |
}, | |
//this method is called to determine whether or not event handlers should be applied | |
//if true, it assumes the plugin button clicked is an action and not a popover and applies | |
//no events, so setting it to false applies the events like we want to the dialog can be reused | |
//in the same dialog session. | |
isHeadless: function(cmd, value) { | |
return false; | |
}, | |
execute: function (id, value, envOptions) { | |
logger("modalPlugin.ModalPlugin.execute", function() { | |
console.log(id, value, envOptions); | |
}); | |
var editorKernel = this.editorKernel, | |
dialogManager = editorKernel.getDialogManager(), | |
context = envOptions.editContext, | |
selection = CUI.rte.Selection.createProcessingSelection(context), | |
startNode = selection.startNode; | |
if ((selection.startOffset === startNode.length) && (startNode != selection.endNode)) { | |
startNode = startNode.nextSibling; | |
} | |
if (dialogManager.isShown(this.dialog)) { | |
dialogManager.hide(this.dialog); | |
return; | |
} | |
var dialogConfig = { | |
execute: function (options) { | |
editorKernel.relayCmd(id, options); | |
}, | |
parameters: { | |
"command": modalPlugin.MODAL_UI_SETTING | |
} | |
}, | |
dh = CUI.rte.ui.DialogHelper; | |
if (!this.dialog) { | |
this.dialog = dialogManager.create(modalPlugin.MODAL_DIALOG, dialogConfig); | |
} | |
var tag = CUI.rte.Common.getTagInPath(context, selection.startNode, "span"); | |
if (tag) { | |
this.dialog.$linkTitle.val(tag.dataset.target ? tag.dataset.target.replace("#","") : ""); | |
} | |
dialogManager.prepareShow(this.dialog); | |
dialogManager.show(this.dialog); | |
}, | |
updateState: function (selDef) { | |
if (this.modalUI != null && (selDef.isSelection || selDef.nodeList.commonAncestor.classList.contains("modal-plugin"))) { | |
this.modalUI.setDisabled(false); | |
this.modalUI.setSelected(false); | |
} else { | |
this.modalUI.setSelected(false); | |
this.modalUI.setDisabled(true); | |
} | |
} | |
}); | |
CUI.rte.plugins.PluginRegistry.register(modalPlugin.GROUP, modalPlugin.ModalPlugin); | |
/** | |
* This Class is invoked by the dialog via the `apply` method, e.g. when the User saves the Dialog. This is not invoked | |
* when the user cancels input or closes the dialog without saving it. | |
* | |
* @see /libs/clientlibs/granite/richtext/core/js/commands/Command.js | |
* | |
* @type {Class} | |
* @extends CUI.rte.commands.Command | |
*/ | |
modalPlugin.ModalCmd = new Class({ | |
toString: "ModalCmd", | |
extend: CUI.rte.commands.Command, | |
// a command can have one or multiple commands, the `isCommand` method checks whether this command is the one accountable for action. | |
isCommand: function (cmdStr) { | |
return (cmdStr.toLowerCase() == modalPlugin.FEATURE); | |
}, | |
getProcessingOptions: function () { | |
var cmd = CUI.rte.commands.Command; | |
return cmd.PO_SELECTION | cmd.PO_BOOKMARK | cmd.PO_NODELIST; | |
}, | |
_getTagObject: function (options) { | |
var returnObj = {}; | |
returnObj.attributes = {}; | |
returnObj.tag = "span"; | |
if (options) { | |
if (options.linkTitle) { | |
returnObj.attributes["data-target"] = "#"+options.linkTitle; | |
} | |
returnObj.attributes["data-toggle"] = "modal"; | |
returnObj.attributes["class"] = "modal-plugin"; | |
} | |
return returnObj; | |
}, | |
execute: function (executionDefinition) { | |
var selection = executionDefinition.selection, | |
nodeList = executionDefinition.nodeList; | |
if (!selection || !nodeList) { | |
return; | |
} | |
//if no modal value is passed, assume delete and remove the span | |
if (!executionDefinition.value) { | |
var dpr = CUI.rte.DomProcessor; | |
for (var i = 0; i < nodeList.nodes.length; i++) { | |
dpr.removeWithoutChildren(nodeList.nodes[i].dom.parentNode); | |
} | |
return; | |
} | |
var common = CUI.rte.Common, | |
context = executionDefinition.editContext, | |
tagObj = this._getTagObject(executionDefinition.value); | |
// method to check whether or not the selected nodes have any existing modals in them, returns an Array[]. | |
var tags = common.getTagInPath(context, selection.startNode, tagObj.tag); | |
//remove existing span before surrounding with new span | |
if (tags != null) { | |
nodeList.removeNodesByTag(executionDefinition.editContext, tagObj.tag, undefined, true); | |
nodeList.commonAncestor = nodeList.nodes[0].dom.parentElement; | |
} | |
//apply the markup to the selection | |
nodeList.surround(executionDefinition.editContext, tagObj.tag, tagObj.attributes); | |
} | |
}); | |
// Assigning the Command Class `modalPlugin.ModalCmd` to The Feature. If your Plugin has multiple features, you will have to register multiple commands | |
CUI.rte.commands.CommandRegistry.register(modalPlugin.FEATURE, modalPlugin.ModalCmd); | |
}(jQuery)); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Can you please explain how to use it? It's really urgent.