You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
153 lines
4.5 KiB
153 lines
4.5 KiB
(function (root, factory) {
|
|
// AMD
|
|
if (typeof define === 'function' && define.amd) define(['angular'], factory);
|
|
// Global
|
|
else factory(angular);
|
|
}(this, function (angular) {
|
|
|
|
angular
|
|
.module('ckeditor', [])
|
|
.directive('ckeditor', ['$parse', ckeditorDirective]);
|
|
|
|
// Polyfill setImmediate function.
|
|
var setImmediate = window && window.setImmediate ? window.setImmediate : function (fn) {
|
|
setTimeout(fn, 0);
|
|
};
|
|
|
|
/**
|
|
* CKEditor directive.
|
|
*
|
|
* @example
|
|
* <div ckeditor="options" ng-model="content" ready="onReady()"></div>
|
|
*/
|
|
|
|
function ckeditorDirective($parse) {
|
|
return {
|
|
restrict: 'A',
|
|
require: ['ckeditor', 'ngModel'],
|
|
controller: [
|
|
'$scope',
|
|
'$element',
|
|
'$attrs',
|
|
'$parse',
|
|
'$q',
|
|
ckeditorController
|
|
],
|
|
link: function (scope, element, attrs, ctrls) {
|
|
// get needed controllers
|
|
var controller = ctrls[0]; // our own, see below
|
|
var ngModelController = ctrls[1];
|
|
|
|
// Initialize the editor content when it is ready.
|
|
controller.ready().then(function initialize() {
|
|
// Sync view on specific events.
|
|
['dataReady', 'change', 'blur', 'saveSnapshot'].forEach(function (event) {
|
|
controller.onCKEvent(event, function syncView() {
|
|
ngModelController.$setViewValue(controller.instance.getData() || '');
|
|
});
|
|
});
|
|
|
|
controller.instance.setReadOnly(!! attrs.readonly);
|
|
attrs.$observe('readonly', function (readonly) {
|
|
controller.instance.setReadOnly(!! readonly);
|
|
});
|
|
|
|
// Defer the ready handler calling to ensure that the editor is
|
|
// completely ready and populated with data.
|
|
setImmediate(function () {
|
|
$parse(attrs.ready)(scope);
|
|
});
|
|
});
|
|
|
|
// Set editor data when view data change.
|
|
ngModelController.$render = function syncEditor() {
|
|
controller.ready().then(function () {
|
|
// "noSnapshot" prevent recording an undo snapshot
|
|
controller.instance.setData(ngModelController.$viewValue || '', {
|
|
noSnapshot: true,
|
|
callback: function () {
|
|
// Amends the top of the undo stack with the current DOM changes
|
|
// ie: merge snapshot with the first empty one
|
|
// http://docs.ckeditor.com/#!/api/CKEDITOR.editor-event-updateSnapshot
|
|
controller.instance.fire('updateSnapshot');
|
|
}
|
|
});
|
|
});
|
|
};
|
|
}
|
|
};
|
|
}
|
|
|
|
/**
|
|
* CKEditor controller.
|
|
*/
|
|
|
|
function ckeditorController($scope, $element, $attrs, $parse, $q) {
|
|
var config = $parse($attrs.ckeditor)($scope) || {};
|
|
var editorElement = $element[0];
|
|
var instance;
|
|
var readyDeferred = $q.defer(); // a deferred to be resolved when the editor is ready
|
|
|
|
// Create editor instance.
|
|
if (editorElement.hasAttribute('contenteditable') &&
|
|
editorElement.getAttribute('contenteditable').toLowerCase() == 'true') {
|
|
instance = this.instance = CKEDITOR.inline(editorElement, config);
|
|
}
|
|
else {
|
|
instance = this.instance = CKEDITOR.replace(editorElement, config);
|
|
}
|
|
|
|
/**
|
|
* Listen on events of a given type.
|
|
* This make all event asynchronous and wrapped in $scope.$apply.
|
|
*
|
|
* @param {String} event
|
|
* @param {Function} listener
|
|
* @returns {Function} Deregistration function for this listener.
|
|
*/
|
|
|
|
this.onCKEvent = function (event, listener) {
|
|
instance.on(event, asyncListener);
|
|
|
|
function asyncListener() {
|
|
var args = arguments;
|
|
setImmediate(function () {
|
|
applyListener.apply(null, args);
|
|
});
|
|
}
|
|
|
|
function applyListener() {
|
|
var args = arguments;
|
|
$scope.$apply(function () {
|
|
listener.apply(null, args);
|
|
});
|
|
}
|
|
|
|
// Return the deregistration function
|
|
return function $off() {
|
|
instance.removeListener(event, applyListener);
|
|
};
|
|
};
|
|
|
|
this.onCKEvent('instanceReady', function() {
|
|
readyDeferred.resolve(true);
|
|
});
|
|
|
|
/**
|
|
* Check if the editor if ready.
|
|
*
|
|
* @returns {Promise}
|
|
*/
|
|
this.ready = function ready() {
|
|
return readyDeferred.promise;
|
|
};
|
|
|
|
// Destroy editor when the scope is destroyed.
|
|
$scope.$on('$destroy', function onDestroy() {
|
|
// do not delete too fast or pending events will throw errors
|
|
readyDeferred.promise.then(function() {
|
|
instance.destroy(false);
|
|
});
|
|
});
|
|
}
|
|
}));
|
|
|