-
Notifications
You must be signed in to change notification settings - Fork 133
/
angular-ckeditor.js
153 lines (134 loc) · 4.5 KB
/
angular-ckeditor.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
(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);
});
});
}
}));