(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]); // Create setImmediate function. var setImmediate = window && window.setImmediate ? window.setImmediate : function (fn) { setTimeout(fn, 0); }; /** * CKEditor directive. * * @example *
*/ function ckeditorDirective($parse) { return { restrict: 'A', require: ['ckeditor', 'ngModel'], controller: [ '$scope', '$element', '$attrs', '$parse', '$q', ckeditorController ], link: function (scope, element, attrs, ctrls) { var ckeditor = ctrls[0]; var ngModel = ctrls[1]; // Initialize the editor when it is ready. ckeditor.ready().then(function initialize() { // Sync view on specific events. ['dataReady', 'change', 'saveSnapshot'].forEach(function (event) { ckeditor.$on(event, function syncView() { ngModel.$setViewValue(ckeditor.instance.getData() || ''); }); }); // Put editor out of readonly. ckeditor.instance.setReadOnly(false); // 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. ngModel.$render = function syncEditor() { ckeditor.ready().then(function () { ckeditor.instance.setData(ngModel.$viewValue || ''); }); }; } }; } /** * CKEditor controller. */ function ckeditorController($scope, $element, $attrs, $parse, $q) { // Create editor instance. var config = $parse($attrs.ckeditor)($scope) || {}; var editorElement = $element[0]; var 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 asynchrone and wrapped in $scope.$apply. * * @param {String} event * @param {Function} listener * @returns {Function} Deregistration function for this listener. */ this.$on = function $on(event, listener) { // Wrap primus event with $rootScope.$apply. 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); }; }; /** * Check if the editor if ready. * * @returns {Promise} */ this.ready = function ready() { if (this.readyDefer) return this.readyDefer.promise; var readyDefer = this.readyDefer = $q.defer(); if (this.instance.status === 'ready') readyDefer.resolve(); else this.$on('instanceReady', readyDefer.resolve); return readyDefer.promise; }; // Destroy editor when the scope is destroyed. $scope.$on('$destroy', function onDestroy() { //instance.destroy(false); }); } }));