@grafit/era-dependencies 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +7 -0
- package/vendor/fonts/FontAwesome.otf +0 -0
- package/vendor/fonts/fontawesome-webfont.eot +0 -0
- package/vendor/fonts/fontawesome-webfont.svg +685 -0
- package/vendor/fonts/fontawesome-webfont.ttf +0 -0
- package/vendor/fonts/fontawesome-webfont.woff +0 -0
- package/vendor/fonts/fontawesome-webfont.woff2 +0 -0
- package/vendor/fonts/glyphicons-halflings-regular.eot +0 -0
- package/vendor/fonts/glyphicons-halflings-regular.svg +288 -0
- package/vendor/fonts/glyphicons-halflings-regular.ttf +0 -0
- package/vendor/fonts/glyphicons-halflings-regular.woff +0 -0
- package/vendor/fonts/glyphicons-halflings-regular.woff2 +0 -0
- package/vendor/scripts/angular/angular-cookies.js +322 -0
- package/vendor/scripts/angular/angular-file-upload.js +2087 -0
- package/vendor/scripts/angular/angular-filter.js +2287 -0
- package/vendor/scripts/angular/angular-locale_ru-ru.js +143 -0
- package/vendor/scripts/angular/angular-route.js +1069 -0
- package/vendor/scripts/angular/angular-sanitize.js +738 -0
- package/vendor/scripts/angular/angular-ui-router-0.2.18.js +4539 -0
- package/vendor/scripts/angular/angular.js +31768 -0
- package/vendor/scripts/angular/datetimepicker.js +578 -0
- package/vendor/scripts/angular/datetimepicker.templates.js +30 -0
- package/vendor/scripts/angular/mask.min.js +7 -0
- package/vendor/scripts/angular/ng-table.js +1518 -0
- package/vendor/scripts/angular/select.js +2356 -0
- package/vendor/scripts/angular/ui-bootstrap-tpls-2.1.3.js +7536 -0
- package/vendor/scripts/angular/uploader.js +3 -0
- package/vendor/scripts/bootbox.js +985 -0
- package/vendor/scripts/bootstrap.js +2377 -0
- package/vendor/scripts/es6-shim.js +3837 -0
- package/vendor/scripts/highchart/highcharts-more.src.js +3165 -0
- package/vendor/scripts/highchart/highstock.src.js +32008 -0
- package/vendor/scripts/highchart/modules/boost.src.js +2721 -0
- package/vendor/scripts/highchart/modules/exporting.src.js +951 -0
- package/vendor/scripts/jquery/jquery.js +11008 -0
- package/vendor/scripts/jquery.datetimepicker.full.js +2911 -0
- package/vendor/scripts/keycloak.js +2382 -0
- package/vendor/scripts/lodash.js +16733 -0
- package/vendor/scripts/moment-with-locales.js +12251 -0
- package/vendor/scripts/moment.js +4234 -0
- package/vendor/scripts/old/datepicker-ru.js +38 -0
- package/vendor/scripts/old/jquery-ui-1.11.1.js +16375 -0
- package/vendor/scripts/old/jquery.form.js +1278 -0
- package/vendor/scripts/perfect-scrollbar.js +1549 -0
- package/vendor/scripts/pickmeup/pickmeup-locales.js +11 -0
- package/vendor/scripts/pickmeup/pickmeup.js +1383 -0
- package/vendor/scripts/quill.js +9676 -0
- package/vendor/scripts/socket.io.min.js +3 -0
- package/vendor/scripts/textAngular/angular-spectrum-colorpicker.min.js +2 -0
- package/vendor/scripts/textAngular/spectrum.min.js +1 -0
- package/vendor/scripts/textAngular/textAngular-dropdownToggle.js +38 -0
- package/vendor/scripts/textAngular/textAngular-rangy.min.js +478 -0
- package/vendor/scripts/textAngular/textAngular-sanitize.min.js +322 -0
- package/vendor/scripts/textAngular/textAngular.min.js +1481 -0
- package/vendor/scripts/textAngular/textAngularSetup.js +1013 -0
- package/vendor/styles/bootstrap-theme.css +587 -0
- package/vendor/styles/bootstrap-theme.css.map +1 -0
- package/vendor/styles/bootstrap-theme.min.css +6 -0
- package/vendor/styles/bootstrap-theme.min.css.map +1 -0
- package/vendor/styles/bootstrap.css +6757 -0
- package/vendor/styles/bootstrap.css.map +1 -0
- package/vendor/styles/bootstrap.min.css +6 -0
- package/vendor/styles/bootstrap.min.css.map +1 -0
- package/vendor/styles/datetimepicker.css +115 -0
- package/vendor/styles/font-awesome.css +2199 -0
- package/vendor/styles/jquery.datetimepicker.min.css +1 -0
- package/vendor/styles/ng-table.css +136 -0
- package/vendor/styles/normalize.css +424 -0
- package/vendor/styles/perfect-scrollbar.css +165 -0
- package/vendor/styles/pickmeup.css +137 -0
- package/vendor/styles/spectrum.min.css +1 -0
- package/vendor/styles/textAngular.css +193 -0
|
@@ -0,0 +1,1013 @@
|
|
|
1
|
+
|
|
2
|
+
// tests against the current jqLite/jquery implementation if this can be an element
|
|
3
|
+
function validElementString(string){
|
|
4
|
+
try{
|
|
5
|
+
return angular.element(string).length !== 0;
|
|
6
|
+
}catch(any){
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
// setup the global contstant functions for setting up the toolbar
|
|
11
|
+
|
|
12
|
+
// all tool definitions
|
|
13
|
+
var taTools = {};
|
|
14
|
+
/*
|
|
15
|
+
A tool definition is an object with the following key/value parameters:
|
|
16
|
+
action: [function(deferred, restoreSelection)]
|
|
17
|
+
a function that is executed on clicking on the button - this will allways be executed using ng-click and will
|
|
18
|
+
overwrite any ng-click value in the display attribute.
|
|
19
|
+
The function is passed a deferred object ($q.defer()), if this is wanted to be used `return false;` from the action and
|
|
20
|
+
manually call `deferred.resolve();` elsewhere to notify the editor that the action has finished.
|
|
21
|
+
restoreSelection is only defined if the rangy library is included and it can be called as `restoreSelection()` to restore the users
|
|
22
|
+
selection in the WYSIWYG editor.
|
|
23
|
+
display: [string]?
|
|
24
|
+
Optional, an HTML element to be displayed as the button. The `scope` of the button is the tool definition object with some additional functions
|
|
25
|
+
If set this will cause buttontext and iconclass to be ignored
|
|
26
|
+
class: [string]?
|
|
27
|
+
Optional, if set will override the taOptions.classes.toolbarButton class.
|
|
28
|
+
buttontext: [string]?
|
|
29
|
+
if this is defined it will replace the contents of the element contained in the `display` element
|
|
30
|
+
iconclass: [string]?
|
|
31
|
+
if this is defined an icon (<i>) will be appended to the `display` element with this string as it's class
|
|
32
|
+
tooltiptext: [string]?
|
|
33
|
+
Optional, a plain text description of the action, used for the title attribute of the action button in the toolbar by default.
|
|
34
|
+
activestate: [function(commonElement)]?
|
|
35
|
+
this function is called on every caret movement, if it returns true then the class taOptions.classes.toolbarButtonActive
|
|
36
|
+
will be applied to the `display` element, else the class will be removed
|
|
37
|
+
disabled: [function()]?
|
|
38
|
+
if this function returns true then the tool will have the class taOptions.classes.disabled applied to it, else it will be removed
|
|
39
|
+
Other functions available on the scope are:
|
|
40
|
+
name: [string]
|
|
41
|
+
the name of the tool, this is the first parameter passed into taRegisterTool
|
|
42
|
+
isDisabled: [function()]
|
|
43
|
+
returns true if the tool is disabled, false if it isn't
|
|
44
|
+
displayActiveToolClass: [function(boolean)]
|
|
45
|
+
returns true if the tool is 'active' in the currently focussed toolbar
|
|
46
|
+
onElementSelect: [Object]
|
|
47
|
+
This object contains the following key/value pairs and is used to trigger the ta-element-select event
|
|
48
|
+
element: [String]
|
|
49
|
+
an element name, will only trigger the onElementSelect action if the tagName of the element matches this string
|
|
50
|
+
filter: [function(element)]?
|
|
51
|
+
an optional filter that returns a boolean, if true it will trigger the onElementSelect.
|
|
52
|
+
action: [function(event, element, editorScope)]
|
|
53
|
+
the action that should be executed if the onElementSelect function runs
|
|
54
|
+
*/
|
|
55
|
+
// name and toolDefinition to add into the tools available to be added on the toolbar
|
|
56
|
+
function registerTextAngularTool(name, toolDefinition){
|
|
57
|
+
if(!name || name === '' || taTools.hasOwnProperty(name)) throw('textAngular Error: A unique name is required for a Tool Definition');
|
|
58
|
+
if(
|
|
59
|
+
(toolDefinition.display && (toolDefinition.display === '' || !validElementString(toolDefinition.display))) ||
|
|
60
|
+
(!toolDefinition.display && !toolDefinition.buttontext && !toolDefinition.iconclass)
|
|
61
|
+
)
|
|
62
|
+
throw('textAngular Error: Tool Definition for "' + name + '" does not have a valid display/iconclass/buttontext value');
|
|
63
|
+
taTools[name] = toolDefinition;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
angular.module('textAngularSetup', [])
|
|
67
|
+
.constant('taRegisterTool', registerTextAngularTool)
|
|
68
|
+
.value('taTools', taTools)
|
|
69
|
+
// Here we set up the global display defaults, to set your own use a angular $provider#decorator.
|
|
70
|
+
.value('taOptions', {
|
|
71
|
+
//////////////////////////////////////////////////////////////////////////////////////
|
|
72
|
+
// forceTextAngularSanitize
|
|
73
|
+
// set false to allow the textAngular-sanitize provider to be replaced
|
|
74
|
+
// with angular-sanitize or a custom provider.
|
|
75
|
+
forceTextAngularSanitize: true,
|
|
76
|
+
///////////////////////////////////////////////////////////////////////////////////////
|
|
77
|
+
// keyMappings
|
|
78
|
+
// allow customizable keyMappings for specialized key boards or languages
|
|
79
|
+
//
|
|
80
|
+
// keyMappings provides key mappings that are attached to a given commandKeyCode.
|
|
81
|
+
// To modify a specific keyboard binding, simply provide function which returns true
|
|
82
|
+
// for the event you wish to map to.
|
|
83
|
+
// Or to disable a specific keyboard binding, provide a function which returns false.
|
|
84
|
+
// Note: 'RedoKey' and 'UndoKey' are internally bound to the redo and undo functionality.
|
|
85
|
+
// At present, the following commandKeyCodes are in use:
|
|
86
|
+
// 98, 'TabKey', 'ShiftTabKey', 105, 117, 'UndoKey', 'RedoKey'
|
|
87
|
+
//
|
|
88
|
+
// To map to an new commandKeyCode, add a new key mapping such as:
|
|
89
|
+
// {commandKeyCode: 'CustomKey', testForKey: function (event) {
|
|
90
|
+
// if (event.keyCode=57 && event.ctrlKey && !event.shiftKey && !event.altKey) return true;
|
|
91
|
+
// } }
|
|
92
|
+
// to the keyMappings. This example maps ctrl+9 to 'CustomKey'
|
|
93
|
+
// Then where taRegisterTool(...) is called, add a commandKeyCode: 'CustomKey' and your
|
|
94
|
+
// tool will be bound to ctrl+9.
|
|
95
|
+
//
|
|
96
|
+
// To disble one of the already bound commandKeyCodes such as 'RedoKey' or 'UndoKey' add:
|
|
97
|
+
// {commandKeyCode: 'RedoKey', testForKey: function (event) { return false; } },
|
|
98
|
+
// {commandKeyCode: 'UndoKey', testForKey: function (event) { return false; } },
|
|
99
|
+
// to disable them.
|
|
100
|
+
//
|
|
101
|
+
keyMappings : [],
|
|
102
|
+
toolbar: [
|
|
103
|
+
['h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'p', 'pre', 'quote'],
|
|
104
|
+
['bold', 'italics', 'underline', 'strikeThrough', 'ul', 'ol', 'redo', 'undo', 'clear'],
|
|
105
|
+
['justifyLeft','justifyCenter','justifyRight','justifyFull','indent','outdent'],
|
|
106
|
+
['html', 'insertImage', 'insertLink', 'insertVideo', 'wordcount', 'charcount']
|
|
107
|
+
],
|
|
108
|
+
classes: {
|
|
109
|
+
focussed: "focussed",
|
|
110
|
+
toolbar: "btn-toolbar",
|
|
111
|
+
toolbarGroup: "btn-group",
|
|
112
|
+
toolbarButton: "btn btn-default",
|
|
113
|
+
toolbarButtonActive: "active",
|
|
114
|
+
disabled: "disabled",
|
|
115
|
+
textEditor: 'form-control',
|
|
116
|
+
htmlEditor: 'form-control'
|
|
117
|
+
},
|
|
118
|
+
defaultTagAttributes : {
|
|
119
|
+
a: {target:""}
|
|
120
|
+
},
|
|
121
|
+
setup: {
|
|
122
|
+
// wysiwyg mode
|
|
123
|
+
textEditorSetup: function($element){ /* Do some processing here */ },
|
|
124
|
+
// raw html
|
|
125
|
+
htmlEditorSetup: function($element){ /* Do some processing here */ }
|
|
126
|
+
},
|
|
127
|
+
defaultFileDropHandler:
|
|
128
|
+
/* istanbul ignore next: untestable image processing */
|
|
129
|
+
function(file, insertAction){
|
|
130
|
+
var reader = new FileReader();
|
|
131
|
+
if(file.type.substring(0, 5) === 'image'){
|
|
132
|
+
reader.onload = function() {
|
|
133
|
+
if(reader.result !== '') insertAction('insertImage', reader.result, true);
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
reader.readAsDataURL(file);
|
|
137
|
+
// NOTE: For async procedures return a promise and resolve it when the editor should update the model.
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
return false;
|
|
141
|
+
}
|
|
142
|
+
})
|
|
143
|
+
|
|
144
|
+
// This is the element selector string that is used to catch click events within a taBind, prevents the default and $emits a 'ta-element-select' event
|
|
145
|
+
// these are individually used in an angular.element().find() call. What can go here depends on whether you have full jQuery loaded or just jQLite with angularjs.
|
|
146
|
+
// div is only used as div.ta-insert-video caught in filter.
|
|
147
|
+
.value('taSelectableElements', ['a','img'])
|
|
148
|
+
|
|
149
|
+
// This is an array of objects with the following options:
|
|
150
|
+
// selector: <string> a jqLite or jQuery selector string
|
|
151
|
+
// customAttribute: <string> an attribute to search for
|
|
152
|
+
// renderLogic: <function(element)>
|
|
153
|
+
// Both or one of selector and customAttribute must be defined.
|
|
154
|
+
.value('taCustomRenderers', [
|
|
155
|
+
{
|
|
156
|
+
// Parse back out: '<div class="ta-insert-video" ta-insert-video src="' + urlLink + '" allowfullscreen="true" width="300" frameborder="0" height="250"></div>'
|
|
157
|
+
// To correct video element. For now only support youtube
|
|
158
|
+
selector: 'img',
|
|
159
|
+
customAttribute: 'ta-insert-video',
|
|
160
|
+
renderLogic: function(element){
|
|
161
|
+
var iframe = angular.element('<iframe></iframe>');
|
|
162
|
+
var attributes = element.prop("attributes");
|
|
163
|
+
// loop through element attributes and apply them on iframe
|
|
164
|
+
angular.forEach(attributes, function(attr) {
|
|
165
|
+
iframe.attr(attr.name, attr.value);
|
|
166
|
+
});
|
|
167
|
+
iframe.attr('src', iframe.attr('ta-insert-video'));
|
|
168
|
+
element.replaceWith(iframe);
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
])
|
|
172
|
+
|
|
173
|
+
.value('taTranslations', {
|
|
174
|
+
// moved to sub-elements
|
|
175
|
+
//toggleHTML: "Toggle HTML",
|
|
176
|
+
//insertImage: "Please enter a image URL to insert",
|
|
177
|
+
//insertLink: "Please enter a URL to insert",
|
|
178
|
+
//insertVideo: "Please enter a youtube URL to embed",
|
|
179
|
+
html: {
|
|
180
|
+
tooltip: 'Toggle html / Rich Text'
|
|
181
|
+
},
|
|
182
|
+
// tooltip for heading - might be worth splitting
|
|
183
|
+
heading: {
|
|
184
|
+
tooltip: 'Heading '
|
|
185
|
+
},
|
|
186
|
+
p: {
|
|
187
|
+
tooltip: 'Paragraph'
|
|
188
|
+
},
|
|
189
|
+
pre: {
|
|
190
|
+
tooltip: 'Preformatted text'
|
|
191
|
+
},
|
|
192
|
+
ul: {
|
|
193
|
+
tooltip: 'Unordered List'
|
|
194
|
+
},
|
|
195
|
+
ol: {
|
|
196
|
+
tooltip: 'Ordered List'
|
|
197
|
+
},
|
|
198
|
+
quote: {
|
|
199
|
+
tooltip: 'Quote/unquote selection or paragraph'
|
|
200
|
+
},
|
|
201
|
+
undo: {
|
|
202
|
+
tooltip: 'Undo'
|
|
203
|
+
},
|
|
204
|
+
redo: {
|
|
205
|
+
tooltip: 'Redo'
|
|
206
|
+
},
|
|
207
|
+
bold: {
|
|
208
|
+
tooltip: 'Bold'
|
|
209
|
+
},
|
|
210
|
+
italic: {
|
|
211
|
+
tooltip: 'Italic'
|
|
212
|
+
},
|
|
213
|
+
underline: {
|
|
214
|
+
tooltip: 'Underline'
|
|
215
|
+
},
|
|
216
|
+
strikeThrough:{
|
|
217
|
+
tooltip: 'Strikethrough'
|
|
218
|
+
},
|
|
219
|
+
justifyLeft: {
|
|
220
|
+
tooltip: 'Align text left'
|
|
221
|
+
},
|
|
222
|
+
justifyRight: {
|
|
223
|
+
tooltip: 'Align text right'
|
|
224
|
+
},
|
|
225
|
+
justifyFull: {
|
|
226
|
+
tooltip: 'Justify text'
|
|
227
|
+
},
|
|
228
|
+
justifyCenter: {
|
|
229
|
+
tooltip: 'Center'
|
|
230
|
+
},
|
|
231
|
+
indent: {
|
|
232
|
+
tooltip: 'Increase indent'
|
|
233
|
+
},
|
|
234
|
+
outdent: {
|
|
235
|
+
tooltip: 'Decrease indent'
|
|
236
|
+
},
|
|
237
|
+
clear: {
|
|
238
|
+
tooltip: 'Clear formatting'
|
|
239
|
+
},
|
|
240
|
+
insertImage: {
|
|
241
|
+
dialogPrompt: 'Please enter an image URL to insert',
|
|
242
|
+
tooltip: 'Insert image',
|
|
243
|
+
hotkey: 'the - possibly language dependent hotkey ... for some future implementation'
|
|
244
|
+
},
|
|
245
|
+
insertVideo: {
|
|
246
|
+
tooltip: 'Insert video',
|
|
247
|
+
dialogPrompt: 'Please enter a youtube URL to embed'
|
|
248
|
+
},
|
|
249
|
+
insertLink: {
|
|
250
|
+
tooltip: 'Insert / edit link',
|
|
251
|
+
dialogPrompt: "Please enter a URL to insert"
|
|
252
|
+
},
|
|
253
|
+
editLink: {
|
|
254
|
+
reLinkButton: {
|
|
255
|
+
tooltip: "Relink"
|
|
256
|
+
},
|
|
257
|
+
unLinkButton: {
|
|
258
|
+
tooltip: "Unlink"
|
|
259
|
+
},
|
|
260
|
+
targetToggle: {
|
|
261
|
+
buttontext: "Open in New Window"
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
wordcount: {
|
|
265
|
+
tooltip: 'Display words Count'
|
|
266
|
+
},
|
|
267
|
+
charcount: {
|
|
268
|
+
tooltip: 'Display characters Count'
|
|
269
|
+
}
|
|
270
|
+
})
|
|
271
|
+
.factory('taToolFunctions', ['$window','taTranslations', function($window, taTranslations) {
|
|
272
|
+
return {
|
|
273
|
+
imgOnSelectAction: function(event, $element, editorScope){
|
|
274
|
+
// setup the editor toolbar
|
|
275
|
+
// Credit to the work at http://hackerwins.github.io/summernote/ for this editbar logic/display
|
|
276
|
+
var finishEdit = function(){
|
|
277
|
+
editorScope.updateTaBindtaTextElement();
|
|
278
|
+
editorScope.hidePopover();
|
|
279
|
+
};
|
|
280
|
+
event.preventDefault();
|
|
281
|
+
editorScope.displayElements.popover.css('width', '375px');
|
|
282
|
+
var container = editorScope.displayElements.popoverContainer;
|
|
283
|
+
container.empty();
|
|
284
|
+
var buttonGroup = angular.element('<div class="btn-group" style="padding-right: 6px;">');
|
|
285
|
+
var fullButton = angular.element('<button type="button" class="btn btn-default btn-sm btn-small" unselectable="on" tabindex="-1">100% </button>');
|
|
286
|
+
fullButton.on('click', function(event){
|
|
287
|
+
event.preventDefault();
|
|
288
|
+
$element.css({
|
|
289
|
+
'width': '100%',
|
|
290
|
+
'height': ''
|
|
291
|
+
});
|
|
292
|
+
finishEdit();
|
|
293
|
+
});
|
|
294
|
+
var halfButton = angular.element('<button type="button" class="btn btn-default btn-sm btn-small" unselectable="on" tabindex="-1">50% </button>');
|
|
295
|
+
halfButton.on('click', function(event){
|
|
296
|
+
event.preventDefault();
|
|
297
|
+
$element.css({
|
|
298
|
+
'width': '50%',
|
|
299
|
+
'height': ''
|
|
300
|
+
});
|
|
301
|
+
finishEdit();
|
|
302
|
+
});
|
|
303
|
+
var quartButton = angular.element('<button type="button" class="btn btn-default btn-sm btn-small" unselectable="on" tabindex="-1">25% </button>');
|
|
304
|
+
quartButton.on('click', function(event){
|
|
305
|
+
event.preventDefault();
|
|
306
|
+
$element.css({
|
|
307
|
+
'width': '25%',
|
|
308
|
+
'height': ''
|
|
309
|
+
});
|
|
310
|
+
finishEdit();
|
|
311
|
+
});
|
|
312
|
+
var resetButton = angular.element('<button type="button" class="btn btn-default btn-sm btn-small" unselectable="on" tabindex="-1">Reset</button>');
|
|
313
|
+
resetButton.on('click', function(event){
|
|
314
|
+
event.preventDefault();
|
|
315
|
+
$element.css({
|
|
316
|
+
width: '',
|
|
317
|
+
height: ''
|
|
318
|
+
});
|
|
319
|
+
finishEdit();
|
|
320
|
+
});
|
|
321
|
+
buttonGroup.append(fullButton);
|
|
322
|
+
buttonGroup.append(halfButton);
|
|
323
|
+
buttonGroup.append(quartButton);
|
|
324
|
+
buttonGroup.append(resetButton);
|
|
325
|
+
container.append(buttonGroup);
|
|
326
|
+
|
|
327
|
+
buttonGroup = angular.element('<div class="btn-group" style="padding-right: 6px;">');
|
|
328
|
+
var floatLeft = angular.element('<button type="button" class="btn btn-default btn-sm btn-small" unselectable="on" tabindex="-1"><i class="fa fa-align-left"></i></button>');
|
|
329
|
+
floatLeft.on('click', function(event){
|
|
330
|
+
event.preventDefault();
|
|
331
|
+
// webkit
|
|
332
|
+
$element.css('float', 'left');
|
|
333
|
+
// firefox
|
|
334
|
+
$element.css('cssFloat', 'left');
|
|
335
|
+
// IE < 8
|
|
336
|
+
$element.css('styleFloat', 'left');
|
|
337
|
+
finishEdit();
|
|
338
|
+
});
|
|
339
|
+
var floatRight = angular.element('<button type="button" class="btn btn-default btn-sm btn-small" unselectable="on" tabindex="-1"><i class="fa fa-align-right"></i></button>');
|
|
340
|
+
floatRight.on('click', function(event){
|
|
341
|
+
event.preventDefault();
|
|
342
|
+
// webkit
|
|
343
|
+
$element.css('float', 'right');
|
|
344
|
+
// firefox
|
|
345
|
+
$element.css('cssFloat', 'right');
|
|
346
|
+
// IE < 8
|
|
347
|
+
$element.css('styleFloat', 'right');
|
|
348
|
+
finishEdit();
|
|
349
|
+
});
|
|
350
|
+
var floatNone = angular.element('<button type="button" class="btn btn-default btn-sm btn-small" unselectable="on" tabindex="-1"><i class="fa fa-align-justify"></i></button>');
|
|
351
|
+
floatNone.on('click', function(event){
|
|
352
|
+
event.preventDefault();
|
|
353
|
+
// webkit
|
|
354
|
+
$element.css('float', '');
|
|
355
|
+
// firefox
|
|
356
|
+
$element.css('cssFloat', '');
|
|
357
|
+
// IE < 8
|
|
358
|
+
$element.css('styleFloat', '');
|
|
359
|
+
finishEdit();
|
|
360
|
+
});
|
|
361
|
+
buttonGroup.append(floatLeft);
|
|
362
|
+
buttonGroup.append(floatNone);
|
|
363
|
+
buttonGroup.append(floatRight);
|
|
364
|
+
container.append(buttonGroup);
|
|
365
|
+
|
|
366
|
+
buttonGroup = angular.element('<div class="btn-group">');
|
|
367
|
+
var remove = angular.element('<button type="button" class="btn btn-default btn-sm btn-small" unselectable="on" tabindex="-1"><i class="fa fa-trash-o"></i></button>');
|
|
368
|
+
remove.on('click', function(event){
|
|
369
|
+
event.preventDefault();
|
|
370
|
+
$element.remove();
|
|
371
|
+
finishEdit();
|
|
372
|
+
});
|
|
373
|
+
buttonGroup.append(remove);
|
|
374
|
+
container.append(buttonGroup);
|
|
375
|
+
|
|
376
|
+
editorScope.showPopover($element);
|
|
377
|
+
editorScope.showResizeOverlay($element);
|
|
378
|
+
},
|
|
379
|
+
aOnSelectAction: function(event, $element, editorScope){
|
|
380
|
+
// setup the editor toolbar
|
|
381
|
+
// Credit to the work at http://hackerwins.github.io/summernote/ for this editbar logic
|
|
382
|
+
event.preventDefault();
|
|
383
|
+
editorScope.displayElements.popover.css('width', '436px');
|
|
384
|
+
var container = editorScope.displayElements.popoverContainer;
|
|
385
|
+
container.empty();
|
|
386
|
+
container.css('line-height', '28px');
|
|
387
|
+
var link = angular.element('<a href="' + $element.attr('href') + '" target="_blank">' + $element.attr('href') + '</a>');
|
|
388
|
+
link.css({
|
|
389
|
+
'display': 'inline-block',
|
|
390
|
+
'max-width': '200px',
|
|
391
|
+
'overflow': 'hidden',
|
|
392
|
+
'text-overflow': 'ellipsis',
|
|
393
|
+
'white-space': 'nowrap',
|
|
394
|
+
'vertical-align': 'middle'
|
|
395
|
+
});
|
|
396
|
+
container.append(link);
|
|
397
|
+
var buttonGroup = angular.element('<div class="btn-group pull-right">');
|
|
398
|
+
var reLinkButton = angular.element('<button type="button" class="btn btn-default btn-sm btn-small" tabindex="-1" unselectable="on" title="' + taTranslations.editLink.reLinkButton.tooltip + '"><i class="fa fa-edit icon-edit"></i></button>');
|
|
399
|
+
reLinkButton.on('click', function(event){
|
|
400
|
+
event.preventDefault();
|
|
401
|
+
var urlLink = $window.prompt(taTranslations.insertLink.dialogPrompt, $element.attr('href'));
|
|
402
|
+
if(urlLink && urlLink !== '' && urlLink !== 'http://'){
|
|
403
|
+
$element.attr('href', urlLink);
|
|
404
|
+
editorScope.updateTaBindtaTextElement();
|
|
405
|
+
}
|
|
406
|
+
editorScope.hidePopover();
|
|
407
|
+
});
|
|
408
|
+
buttonGroup.append(reLinkButton);
|
|
409
|
+
var unLinkButton = angular.element('<button type="button" class="btn btn-default btn-sm btn-small" tabindex="-1" unselectable="on" title="' + taTranslations.editLink.unLinkButton.tooltip + '"><i class="fa fa-unlink icon-unlink"></i></button>');
|
|
410
|
+
// directly before this click event is fired a digest is fired off whereby the reference to $element is orphaned off
|
|
411
|
+
unLinkButton.on('click', function(event){
|
|
412
|
+
event.preventDefault();
|
|
413
|
+
$element.replaceWith($element.contents());
|
|
414
|
+
editorScope.updateTaBindtaTextElement();
|
|
415
|
+
editorScope.hidePopover();
|
|
416
|
+
});
|
|
417
|
+
buttonGroup.append(unLinkButton);
|
|
418
|
+
var targetToggle = angular.element('<button type="button" class="btn btn-default btn-sm btn-small" tabindex="-1" unselectable="on">' + taTranslations.editLink.targetToggle.buttontext + '</button>');
|
|
419
|
+
if($element.attr('target') === '_blank'){
|
|
420
|
+
targetToggle.addClass('active');
|
|
421
|
+
}
|
|
422
|
+
targetToggle.on('click', function(event){
|
|
423
|
+
event.preventDefault();
|
|
424
|
+
$element.attr('target', ($element.attr('target') === '_blank') ? '' : '_blank');
|
|
425
|
+
targetToggle.toggleClass('active');
|
|
426
|
+
editorScope.updateTaBindtaTextElement();
|
|
427
|
+
});
|
|
428
|
+
buttonGroup.append(targetToggle);
|
|
429
|
+
container.append(buttonGroup);
|
|
430
|
+
editorScope.showPopover($element);
|
|
431
|
+
},
|
|
432
|
+
extractYoutubeVideoId: function(url) {
|
|
433
|
+
var re = /(?:youtube(?:-nocookie)?\.com\/(?:[^\/\n\s]+\/\S+\/|(?:v|e(?:mbed)?)\/|\S*?[?&]v=)|youtu\.be\/)([a-zA-Z0-9_-]{11})/i;
|
|
434
|
+
var match = url.match(re);
|
|
435
|
+
return (match && match[1]) || null;
|
|
436
|
+
}
|
|
437
|
+
};
|
|
438
|
+
}])
|
|
439
|
+
.run(['taRegisterTool', '$window', 'taTranslations', 'taSelection', 'taToolFunctions', '$sanitize', 'taOptions', '$log',
|
|
440
|
+
function(taRegisterTool, $window, taTranslations, taSelection, taToolFunctions, $sanitize, taOptions, $log){
|
|
441
|
+
// test for the version of $sanitize that is in use
|
|
442
|
+
// You can disable this check by setting taOptions.textAngularSanitize == false
|
|
443
|
+
var gv = {}; $sanitize('', gv);
|
|
444
|
+
/* istanbul ignore next, throws error */
|
|
445
|
+
if ((taOptions.forceTextAngularSanitize===true) && (gv.version !== 'taSanitize')) {
|
|
446
|
+
throw angular.$$minErr('textAngular')("textAngularSetup", "The textAngular-sanitize provider has been replaced by another -- have you included angular-sanitize by mistake?");
|
|
447
|
+
}
|
|
448
|
+
taRegisterTool("html", {
|
|
449
|
+
iconclass: 'fa fa-code',
|
|
450
|
+
tooltiptext: taTranslations.html.tooltip,
|
|
451
|
+
action: function(){
|
|
452
|
+
this.$editor().switchView();
|
|
453
|
+
},
|
|
454
|
+
activeState: function(){
|
|
455
|
+
return this.$editor().showHtml;
|
|
456
|
+
}
|
|
457
|
+
});
|
|
458
|
+
// add the Header tools
|
|
459
|
+
// convenience functions so that the loop works correctly
|
|
460
|
+
var _retActiveStateFunction = function(q){
|
|
461
|
+
return function(){ return this.$editor().queryFormatBlockState(q); };
|
|
462
|
+
};
|
|
463
|
+
var headerAction = function(){
|
|
464
|
+
return this.$editor().wrapSelection("formatBlock", "<" + this.name.toUpperCase() +">");
|
|
465
|
+
};
|
|
466
|
+
angular.forEach(['h1','h2','h3','h4','h5','h6'], function(h){
|
|
467
|
+
taRegisterTool(h.toLowerCase(), {
|
|
468
|
+
buttontext: h.toUpperCase(),
|
|
469
|
+
tooltiptext: taTranslations.heading.tooltip + h.charAt(1),
|
|
470
|
+
action: headerAction,
|
|
471
|
+
activeState: _retActiveStateFunction(h.toLowerCase())
|
|
472
|
+
});
|
|
473
|
+
});
|
|
474
|
+
taRegisterTool('p', {
|
|
475
|
+
buttontext: 'P',
|
|
476
|
+
tooltiptext: taTranslations.p.tooltip,
|
|
477
|
+
action: function(){
|
|
478
|
+
return this.$editor().wrapSelection("formatBlock", "<P>");
|
|
479
|
+
},
|
|
480
|
+
activeState: function(){ return this.$editor().queryFormatBlockState('p'); }
|
|
481
|
+
});
|
|
482
|
+
// key: pre -> taTranslations[key].tooltip, taTranslations[key].buttontext
|
|
483
|
+
taRegisterTool('pre', {
|
|
484
|
+
buttontext: 'pre',
|
|
485
|
+
tooltiptext: taTranslations.pre.tooltip,
|
|
486
|
+
action: function(){
|
|
487
|
+
return this.$editor().wrapSelection("formatBlock", "<PRE>");
|
|
488
|
+
},
|
|
489
|
+
activeState: function(){ return this.$editor().queryFormatBlockState('pre'); }
|
|
490
|
+
});
|
|
491
|
+
taRegisterTool('ul', {
|
|
492
|
+
iconclass: 'fa fa-list-ul',
|
|
493
|
+
tooltiptext: taTranslations.ul.tooltip,
|
|
494
|
+
action: function(){
|
|
495
|
+
return this.$editor().wrapSelection("insertUnorderedList", null);
|
|
496
|
+
},
|
|
497
|
+
activeState: function(){ return this.$editor().queryCommandState('insertUnorderedList'); }
|
|
498
|
+
});
|
|
499
|
+
taRegisterTool('ol', {
|
|
500
|
+
iconclass: 'fa fa-list-ol',
|
|
501
|
+
tooltiptext: taTranslations.ol.tooltip,
|
|
502
|
+
action: function(){
|
|
503
|
+
return this.$editor().wrapSelection("insertOrderedList", null);
|
|
504
|
+
},
|
|
505
|
+
activeState: function(){ return this.$editor().queryCommandState('insertOrderedList'); }
|
|
506
|
+
});
|
|
507
|
+
taRegisterTool('quote', {
|
|
508
|
+
iconclass: 'fa fa-quote-right',
|
|
509
|
+
tooltiptext: taTranslations.quote.tooltip,
|
|
510
|
+
action: function(){
|
|
511
|
+
return this.$editor().wrapSelection("formatBlock", "<BLOCKQUOTE>");
|
|
512
|
+
},
|
|
513
|
+
activeState: function(){ return this.$editor().queryFormatBlockState('blockquote'); }
|
|
514
|
+
});
|
|
515
|
+
taRegisterTool('undo', {
|
|
516
|
+
iconclass: 'fa fa-undo',
|
|
517
|
+
tooltiptext: taTranslations.undo.tooltip,
|
|
518
|
+
action: function(){
|
|
519
|
+
return this.$editor().wrapSelection("undo", null);
|
|
520
|
+
}
|
|
521
|
+
});
|
|
522
|
+
taRegisterTool('redo', {
|
|
523
|
+
iconclass: 'fa fa-repeat',
|
|
524
|
+
tooltiptext: taTranslations.redo.tooltip,
|
|
525
|
+
action: function(){
|
|
526
|
+
return this.$editor().wrapSelection("redo", null);
|
|
527
|
+
}
|
|
528
|
+
});
|
|
529
|
+
taRegisterTool('bold', {
|
|
530
|
+
iconclass: 'fa fa-bold',
|
|
531
|
+
tooltiptext: taTranslations.bold.tooltip,
|
|
532
|
+
action: function(){
|
|
533
|
+
return this.$editor().wrapSelection("bold", null);
|
|
534
|
+
},
|
|
535
|
+
activeState: function(){
|
|
536
|
+
return this.$editor().queryCommandState('bold');
|
|
537
|
+
},
|
|
538
|
+
commandKeyCode: 98
|
|
539
|
+
});
|
|
540
|
+
taRegisterTool('justifyLeft', {
|
|
541
|
+
iconclass: 'fa fa-align-left',
|
|
542
|
+
tooltiptext: taTranslations.justifyLeft.tooltip,
|
|
543
|
+
action: function(){
|
|
544
|
+
return this.$editor().wrapSelection("justifyLeft", null);
|
|
545
|
+
},
|
|
546
|
+
activeState: function(commonElement){
|
|
547
|
+
/* istanbul ignore next: */
|
|
548
|
+
if (commonElement && commonElement.nodeName === '#document') return false;
|
|
549
|
+
var result = false;
|
|
550
|
+
if (commonElement) {
|
|
551
|
+
// commonELement.css('text-align') can throw an error 'Cannot read property 'defaultView' of null' in rare conditions
|
|
552
|
+
// so we do try catch here...
|
|
553
|
+
try {
|
|
554
|
+
result =
|
|
555
|
+
commonElement.css('text-align') === 'left' ||
|
|
556
|
+
commonElement.attr('align') === 'left' ||
|
|
557
|
+
(
|
|
558
|
+
commonElement.css('text-align') !== 'right' &&
|
|
559
|
+
commonElement.css('text-align') !== 'center' &&
|
|
560
|
+
commonElement.css('text-align') !== 'justify' && !this.$editor().queryCommandState('justifyRight') && !this.$editor().queryCommandState('justifyCenter')
|
|
561
|
+
) && !this.$editor().queryCommandState('justifyFull');
|
|
562
|
+
} catch(e) {
|
|
563
|
+
/* istanbul ignore next: error handler */
|
|
564
|
+
//console.log(e);
|
|
565
|
+
result = false;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
result = result || this.$editor().queryCommandState('justifyLeft');
|
|
569
|
+
return result;
|
|
570
|
+
}
|
|
571
|
+
});
|
|
572
|
+
taRegisterTool('justifyRight', {
|
|
573
|
+
iconclass: 'fa fa-align-right',
|
|
574
|
+
tooltiptext: taTranslations.justifyRight.tooltip,
|
|
575
|
+
action: function(){
|
|
576
|
+
return this.$editor().wrapSelection("justifyRight", null);
|
|
577
|
+
},
|
|
578
|
+
activeState: function(commonElement){
|
|
579
|
+
/* istanbul ignore next: */
|
|
580
|
+
if (commonElement && commonElement.nodeName === '#document') return false;
|
|
581
|
+
var result = false;
|
|
582
|
+
if(commonElement) {
|
|
583
|
+
// commonELement.css('text-align') can throw an error 'Cannot read property 'defaultView' of null' in rare conditions
|
|
584
|
+
// so we do try catch here...
|
|
585
|
+
try {
|
|
586
|
+
result = commonElement.css('text-align') === 'right';
|
|
587
|
+
} catch(e) {
|
|
588
|
+
/* istanbul ignore next: error handler */
|
|
589
|
+
//console.log(e);
|
|
590
|
+
result = false;
|
|
591
|
+
}
|
|
592
|
+
}
|
|
593
|
+
result = result || this.$editor().queryCommandState('justifyRight');
|
|
594
|
+
return result;
|
|
595
|
+
}
|
|
596
|
+
});
|
|
597
|
+
taRegisterTool('justifyFull', {
|
|
598
|
+
iconclass: 'fa fa-align-justify',
|
|
599
|
+
tooltiptext: taTranslations.justifyFull.tooltip,
|
|
600
|
+
action: function(){
|
|
601
|
+
return this.$editor().wrapSelection("justifyFull", null);
|
|
602
|
+
},
|
|
603
|
+
activeState: function(commonElement){
|
|
604
|
+
var result = false;
|
|
605
|
+
if(commonElement) {
|
|
606
|
+
// commonELement.css('text-align') can throw an error 'Cannot read property 'defaultView' of null' in rare conditions
|
|
607
|
+
// so we do try catch here...
|
|
608
|
+
try {
|
|
609
|
+
result = commonElement.css('text-align') === 'justify';
|
|
610
|
+
} catch(e) {
|
|
611
|
+
/* istanbul ignore next: error handler */
|
|
612
|
+
//console.log(e);
|
|
613
|
+
result = false;
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
result = result || this.$editor().queryCommandState('justifyFull');
|
|
617
|
+
return result;
|
|
618
|
+
}
|
|
619
|
+
});
|
|
620
|
+
taRegisterTool('justifyCenter', {
|
|
621
|
+
iconclass: 'fa fa-align-center',
|
|
622
|
+
tooltiptext: taTranslations.justifyCenter.tooltip,
|
|
623
|
+
action: function(){
|
|
624
|
+
return this.$editor().wrapSelection("justifyCenter", null);
|
|
625
|
+
},
|
|
626
|
+
activeState: function(commonElement){
|
|
627
|
+
/* istanbul ignore next: */
|
|
628
|
+
if (commonElement && commonElement.nodeName === '#document') return false;
|
|
629
|
+
var result = false;
|
|
630
|
+
if(commonElement) {
|
|
631
|
+
// commonELement.css('text-align') can throw an error 'Cannot read property 'defaultView' of null' in rare conditions
|
|
632
|
+
// so we do try catch here...
|
|
633
|
+
try {
|
|
634
|
+
result = commonElement.css('text-align') === 'center';
|
|
635
|
+
} catch(e) {
|
|
636
|
+
/* istanbul ignore next: error handler */
|
|
637
|
+
//console.log(e);
|
|
638
|
+
result = false;
|
|
639
|
+
}
|
|
640
|
+
|
|
641
|
+
}
|
|
642
|
+
result = result || this.$editor().queryCommandState('justifyCenter');
|
|
643
|
+
return result;
|
|
644
|
+
}
|
|
645
|
+
});
|
|
646
|
+
taRegisterTool('indent', {
|
|
647
|
+
iconclass: 'fa fa-indent',
|
|
648
|
+
tooltiptext: taTranslations.indent.tooltip,
|
|
649
|
+
action: function(){
|
|
650
|
+
return this.$editor().wrapSelection("indent", null);
|
|
651
|
+
},
|
|
652
|
+
activeState: function(){
|
|
653
|
+
return this.$editor().queryFormatBlockState('blockquote');
|
|
654
|
+
},
|
|
655
|
+
commandKeyCode: 'TabKey'
|
|
656
|
+
});
|
|
657
|
+
taRegisterTool('outdent', {
|
|
658
|
+
iconclass: 'fa fa-outdent',
|
|
659
|
+
tooltiptext: taTranslations.outdent.tooltip,
|
|
660
|
+
action: function(){
|
|
661
|
+
return this.$editor().wrapSelection("outdent", null);
|
|
662
|
+
},
|
|
663
|
+
activeState: function(){
|
|
664
|
+
return false;
|
|
665
|
+
},
|
|
666
|
+
commandKeyCode: 'ShiftTabKey'
|
|
667
|
+
});
|
|
668
|
+
taRegisterTool('italics', {
|
|
669
|
+
iconclass: 'fa fa-italic',
|
|
670
|
+
tooltiptext: taTranslations.italic.tooltip,
|
|
671
|
+
action: function(){
|
|
672
|
+
return this.$editor().wrapSelection("italic", null);
|
|
673
|
+
},
|
|
674
|
+
activeState: function(){
|
|
675
|
+
return this.$editor().queryCommandState('italic');
|
|
676
|
+
},
|
|
677
|
+
commandKeyCode: 105
|
|
678
|
+
});
|
|
679
|
+
taRegisterTool('underline', {
|
|
680
|
+
iconclass: 'fa fa-underline',
|
|
681
|
+
tooltiptext: taTranslations.underline.tooltip,
|
|
682
|
+
action: function(){
|
|
683
|
+
return this.$editor().wrapSelection("underline", null);
|
|
684
|
+
},
|
|
685
|
+
activeState: function(){
|
|
686
|
+
return this.$editor().queryCommandState('underline');
|
|
687
|
+
},
|
|
688
|
+
commandKeyCode: 117
|
|
689
|
+
});
|
|
690
|
+
taRegisterTool('strikeThrough', {
|
|
691
|
+
iconclass: 'fa fa-strikethrough',
|
|
692
|
+
tooltiptext: taTranslations.strikeThrough.tooltip,
|
|
693
|
+
action: function(){
|
|
694
|
+
return this.$editor().wrapSelection("strikeThrough", null);
|
|
695
|
+
},
|
|
696
|
+
activeState: function(){
|
|
697
|
+
return document.queryCommandState('strikeThrough');
|
|
698
|
+
}
|
|
699
|
+
});
|
|
700
|
+
taRegisterTool('clear', {
|
|
701
|
+
iconclass: 'fa fa-ban',
|
|
702
|
+
tooltiptext: taTranslations.clear.tooltip,
|
|
703
|
+
action: function(deferred, restoreSelection){
|
|
704
|
+
var i, selectedElements, elementsSeen;
|
|
705
|
+
|
|
706
|
+
this.$editor().wrapSelection("removeFormat", null);
|
|
707
|
+
var possibleNodes = angular.element(taSelection.getSelectionElement());
|
|
708
|
+
selectedElements = taSelection.getAllSelectedElements();
|
|
709
|
+
//$log.log('selectedElements:', selectedElements);
|
|
710
|
+
// remove lists
|
|
711
|
+
var removeListElements = function(list, pe){
|
|
712
|
+
list = angular.element(list);
|
|
713
|
+
var prevElement = pe;
|
|
714
|
+
if (!pe) {
|
|
715
|
+
prevElement = list;
|
|
716
|
+
}
|
|
717
|
+
angular.forEach(list.children(), function(liElem){
|
|
718
|
+
if (liElem.tagName.toLowerCase() === 'ul' ||
|
|
719
|
+
liElem.tagName.toLowerCase() === 'ol') {
|
|
720
|
+
prevElement = removeListElements(liElem, prevElement);
|
|
721
|
+
} else {
|
|
722
|
+
var newElem = angular.element('<p></p>');
|
|
723
|
+
newElem.html(angular.element(liElem).html());
|
|
724
|
+
prevElement.after(newElem);
|
|
725
|
+
prevElement = newElem;
|
|
726
|
+
}
|
|
727
|
+
});
|
|
728
|
+
list.remove();
|
|
729
|
+
return prevElement;
|
|
730
|
+
};
|
|
731
|
+
|
|
732
|
+
angular.forEach(selectedElements, function(element) {
|
|
733
|
+
if (element.nodeName.toLowerCase() === 'ul' ||
|
|
734
|
+
element.nodeName.toLowerCase() === 'ol') {
|
|
735
|
+
//console.log('removeListElements', element);
|
|
736
|
+
removeListElements(element);
|
|
737
|
+
}
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
angular.forEach(possibleNodes.find("ul"), removeListElements);
|
|
741
|
+
angular.forEach(possibleNodes.find("ol"), removeListElements);
|
|
742
|
+
|
|
743
|
+
// clear out all class attributes. These do not seem to be cleared via removeFormat
|
|
744
|
+
var $editor = this.$editor();
|
|
745
|
+
var recursiveRemoveClass = function(node){
|
|
746
|
+
node = angular.element(node);
|
|
747
|
+
/* istanbul ignore next: this is not triggered in tests any longer since we now never select the whole displayELement */
|
|
748
|
+
if(node[0] !== $editor.displayElements.text[0]) {
|
|
749
|
+
node.removeAttr('class');
|
|
750
|
+
}
|
|
751
|
+
angular.forEach(node.children(), recursiveRemoveClass);
|
|
752
|
+
};
|
|
753
|
+
angular.forEach(possibleNodes, recursiveRemoveClass);
|
|
754
|
+
// check if in list. If not in list then use formatBlock option
|
|
755
|
+
if(possibleNodes[0] && possibleNodes[0].tagName.toLowerCase() !== 'li' &&
|
|
756
|
+
possibleNodes[0].tagName.toLowerCase() !== 'ol' &&
|
|
757
|
+
possibleNodes[0].tagName.toLowerCase() !== 'ul' &&
|
|
758
|
+
possibleNodes[0].getAttribute("contenteditable") !== "true") {
|
|
759
|
+
this.$editor().wrapSelection("formatBlock", "default");
|
|
760
|
+
}
|
|
761
|
+
restoreSelection();
|
|
762
|
+
}
|
|
763
|
+
});
|
|
764
|
+
|
|
765
|
+
/* jshint -W099 */
|
|
766
|
+
/****************************
|
|
767
|
+
// we don't use this code - since the previous way CLEAR is expected to work does not clear partially selected <li>
|
|
768
|
+
|
|
769
|
+
var removeListElement = function(listE){
|
|
770
|
+
console.log(listE);
|
|
771
|
+
var _list = listE.parentNode.childNodes;
|
|
772
|
+
console.log('_list', _list);
|
|
773
|
+
var _preLis = [], _postLis = [], _found = false;
|
|
774
|
+
for (i = 0; i < _list.length; i++) {
|
|
775
|
+
if (_list[i] === listE) {
|
|
776
|
+
_found = true;
|
|
777
|
+
} else if (!_found) _preLis.push(_list[i]);
|
|
778
|
+
else _postLis.push(_list[i]);
|
|
779
|
+
}
|
|
780
|
+
var _parent = angular.element(listE.parentNode);
|
|
781
|
+
var newElem = angular.element('<p></p>');
|
|
782
|
+
newElem.html(angular.element(listE).html());
|
|
783
|
+
if (_preLis.length === 0 || _postLis.length === 0) {
|
|
784
|
+
if (_postLis.length === 0) _parent.after(newElem);
|
|
785
|
+
else _parent[0].parentNode.insertBefore(newElem[0], _parent[0]);
|
|
786
|
+
|
|
787
|
+
if (_preLis.length === 0 && _postLis.length === 0) _parent.remove();
|
|
788
|
+
else angular.element(listE).remove();
|
|
789
|
+
} else {
|
|
790
|
+
var _firstList = angular.element('<' + _parent[0].tagName + '></' + _parent[0].tagName + '>');
|
|
791
|
+
var _secondList = angular.element('<' + _parent[0].tagName + '></' + _parent[0].tagName + '>');
|
|
792
|
+
for (i = 0; i < _preLis.length; i++) _firstList.append(angular.element(_preLis[i]));
|
|
793
|
+
for (i = 0; i < _postLis.length; i++) _secondList.append(angular.element(_postLis[i]));
|
|
794
|
+
_parent.after(_secondList);
|
|
795
|
+
_parent.after(newElem);
|
|
796
|
+
_parent.after(_firstList);
|
|
797
|
+
_parent.remove();
|
|
798
|
+
}
|
|
799
|
+
taSelection.setSelectionToElementEnd(newElem[0]);
|
|
800
|
+
};
|
|
801
|
+
|
|
802
|
+
elementsSeen = [];
|
|
803
|
+
if (selectedElements.length !==0) console.log(selectedElements);
|
|
804
|
+
angular.forEach(selectedElements, function (element) {
|
|
805
|
+
if (elementsSeen.indexOf(element) !== -1 || elementsSeen.indexOf(element.parentElement) !== -1) {
|
|
806
|
+
return;
|
|
807
|
+
}
|
|
808
|
+
elementsSeen.push(element);
|
|
809
|
+
if (element.nodeName.toLowerCase() === 'li') {
|
|
810
|
+
console.log('removeListElement', element);
|
|
811
|
+
removeListElement(element);
|
|
812
|
+
}
|
|
813
|
+
else if (element.parentElement && element.parentElement.nodeName.toLowerCase() === 'li') {
|
|
814
|
+
console.log('removeListElement', element.parentElement);
|
|
815
|
+
elementsSeen.push(element.parentElement);
|
|
816
|
+
removeListElement(element.parentElement);
|
|
817
|
+
}
|
|
818
|
+
});
|
|
819
|
+
**********************/
|
|
820
|
+
|
|
821
|
+
/**********************
|
|
822
|
+
if(possibleNodes[0].tagName.toLowerCase() === 'li'){
|
|
823
|
+
var _list = possibleNodes[0].parentNode.childNodes;
|
|
824
|
+
var _preLis = [], _postLis = [], _found = false;
|
|
825
|
+
for(i = 0; i < _list.length; i++){
|
|
826
|
+
if(_list[i] === possibleNodes[0]){
|
|
827
|
+
_found = true;
|
|
828
|
+
}else if(!_found) _preLis.push(_list[i]);
|
|
829
|
+
else _postLis.push(_list[i]);
|
|
830
|
+
}
|
|
831
|
+
var _parent = angular.element(possibleNodes[0].parentNode);
|
|
832
|
+
var newElem = angular.element('<p></p>');
|
|
833
|
+
newElem.html(angular.element(possibleNodes[0]).html());
|
|
834
|
+
if(_preLis.length === 0 || _postLis.length === 0){
|
|
835
|
+
if(_postLis.length === 0) _parent.after(newElem);
|
|
836
|
+
else _parent[0].parentNode.insertBefore(newElem[0], _parent[0]);
|
|
837
|
+
|
|
838
|
+
if(_preLis.length === 0 && _postLis.length === 0) _parent.remove();
|
|
839
|
+
else angular.element(possibleNodes[0]).remove();
|
|
840
|
+
}else{
|
|
841
|
+
var _firstList = angular.element('<'+_parent[0].tagName+'></'+_parent[0].tagName+'>');
|
|
842
|
+
var _secondList = angular.element('<'+_parent[0].tagName+'></'+_parent[0].tagName+'>');
|
|
843
|
+
for(i = 0; i < _preLis.length; i++) _firstList.append(angular.element(_preLis[i]));
|
|
844
|
+
for(i = 0; i < _postLis.length; i++) _secondList.append(angular.element(_postLis[i]));
|
|
845
|
+
_parent.after(_secondList);
|
|
846
|
+
_parent.after(newElem);
|
|
847
|
+
_parent.after(_firstList);
|
|
848
|
+
_parent.remove();
|
|
849
|
+
}
|
|
850
|
+
taSelection.setSelectionToElementEnd(newElem[0]);
|
|
851
|
+
}
|
|
852
|
+
*******************/
|
|
853
|
+
|
|
854
|
+
|
|
855
|
+
/* istanbul ignore next: if it's javascript don't worry - though probably should show some kind of error message */
|
|
856
|
+
var blockJavascript = function (link) {
|
|
857
|
+
if (link.toLowerCase().indexOf('javascript')!==-1) {
|
|
858
|
+
return true;
|
|
859
|
+
}
|
|
860
|
+
return false;
|
|
861
|
+
};
|
|
862
|
+
|
|
863
|
+
taRegisterTool('insertImage', {
|
|
864
|
+
iconclass: 'fa fa-picture-o',
|
|
865
|
+
tooltiptext: taTranslations.insertImage.tooltip,
|
|
866
|
+
action: function(){
|
|
867
|
+
var imageLink;
|
|
868
|
+
imageLink = $window.prompt(taTranslations.insertImage.dialogPrompt, 'http://');
|
|
869
|
+
if(imageLink && imageLink !== '' && imageLink !== 'http://'){
|
|
870
|
+
/* istanbul ignore next: don't know how to test this... since it needs a dialogPrompt */
|
|
871
|
+
// block javascript here
|
|
872
|
+
if (!blockJavascript(imageLink)) {
|
|
873
|
+
if (taSelection.getSelectionElement().tagName && taSelection.getSelectionElement().tagName.toLowerCase() === 'a') {
|
|
874
|
+
// due to differences in implementation between FireFox and Chrome, we must move the
|
|
875
|
+
// insertion point past the <a> element, otherwise FireFox inserts inside the <a>
|
|
876
|
+
// With this change, both FireFox and Chrome behave the same way!
|
|
877
|
+
taSelection.setSelectionAfterElement(taSelection.getSelectionElement());
|
|
878
|
+
}
|
|
879
|
+
// In the past we used the simple statement:
|
|
880
|
+
//return this.$editor().wrapSelection('insertImage', imageLink, true);
|
|
881
|
+
//
|
|
882
|
+
// However on Firefox only, when the content is empty this is a problem
|
|
883
|
+
// See Issue #1201
|
|
884
|
+
// Investigation reveals that Firefox only inserts a <p> only!!!!
|
|
885
|
+
// So now we use insertHTML here and all is fine.
|
|
886
|
+
// NOTE: this is what 'insertImage' is supposed to do anyway!
|
|
887
|
+
var embed = '<img src="' + imageLink + '">';
|
|
888
|
+
return this.$editor().wrapSelection('insertHTML', embed, true);
|
|
889
|
+
}
|
|
890
|
+
}
|
|
891
|
+
},
|
|
892
|
+
onElementSelect: {
|
|
893
|
+
element: 'img',
|
|
894
|
+
action: taToolFunctions.imgOnSelectAction
|
|
895
|
+
}
|
|
896
|
+
});
|
|
897
|
+
taRegisterTool('insertVideo', {
|
|
898
|
+
iconclass: 'fa fa-youtube-play',
|
|
899
|
+
tooltiptext: taTranslations.insertVideo.tooltip,
|
|
900
|
+
action: function(){
|
|
901
|
+
var urlPrompt;
|
|
902
|
+
urlPrompt = $window.prompt(taTranslations.insertVideo.dialogPrompt, 'https://');
|
|
903
|
+
// block javascript here
|
|
904
|
+
/* istanbul ignore else: if it's javascript don't worry - though probably should show some kind of error message */
|
|
905
|
+
if (!blockJavascript(urlPrompt)) {
|
|
906
|
+
|
|
907
|
+
if (urlPrompt && urlPrompt !== '' && urlPrompt !== 'https://') {
|
|
908
|
+
|
|
909
|
+
videoId = taToolFunctions.extractYoutubeVideoId(urlPrompt);
|
|
910
|
+
|
|
911
|
+
/* istanbul ignore else: if it's invalid don't worry - though probably should show some kind of error message */
|
|
912
|
+
if (videoId) {
|
|
913
|
+
// create the embed link
|
|
914
|
+
var urlLink = "https://www.youtube.com/embed/" + videoId;
|
|
915
|
+
// create the HTML
|
|
916
|
+
// for all options see: http://stackoverflow.com/questions/2068344/how-do-i-get-a-youtube-video-thumbnail-from-the-youtube-api
|
|
917
|
+
// maxresdefault.jpg seems to be undefined on some.
|
|
918
|
+
var embed = '<img class="ta-insert-video" src="https://img.youtube.com/vi/' + videoId + '/hqdefault.jpg" ta-insert-video="' + urlLink + '" contenteditable="false" allowfullscreen="true" frameborder="0" />';
|
|
919
|
+
/* istanbul ignore next: don't know how to test this... since it needs a dialogPrompt */
|
|
920
|
+
if (taSelection.getSelectionElement().tagName && taSelection.getSelectionElement().tagName.toLowerCase() === 'a') {
|
|
921
|
+
// due to differences in implementation between FireFox and Chrome, we must move the
|
|
922
|
+
// insertion point past the <a> element, otherwise FireFox inserts inside the <a>
|
|
923
|
+
// With this change, both FireFox and Chrome behave the same way!
|
|
924
|
+
taSelection.setSelectionAfterElement(taSelection.getSelectionElement());
|
|
925
|
+
}
|
|
926
|
+
// insert
|
|
927
|
+
return this.$editor().wrapSelection('insertHTML', embed, true);
|
|
928
|
+
}
|
|
929
|
+
}
|
|
930
|
+
}
|
|
931
|
+
},
|
|
932
|
+
onElementSelect: {
|
|
933
|
+
element: 'img',
|
|
934
|
+
onlyWithAttrs: ['ta-insert-video'],
|
|
935
|
+
action: taToolFunctions.imgOnSelectAction
|
|
936
|
+
}
|
|
937
|
+
});
|
|
938
|
+
taRegisterTool('insertLink', {
|
|
939
|
+
tooltiptext: taTranslations.insertLink.tooltip,
|
|
940
|
+
iconclass: 'fa fa-link',
|
|
941
|
+
action: function(){
|
|
942
|
+
var urlLink;
|
|
943
|
+
// if this link has already been set, we need to just edit the existing link
|
|
944
|
+
/* istanbul ignore if: we do not test this */
|
|
945
|
+
if (taSelection.getSelectionElement().tagName && taSelection.getSelectionElement().tagName.toLowerCase() === 'a') {
|
|
946
|
+
urlLink = $window.prompt(taTranslations.insertLink.dialogPrompt, taSelection.getSelectionElement().href);
|
|
947
|
+
} else {
|
|
948
|
+
urlLink = $window.prompt(taTranslations.insertLink.dialogPrompt, 'http://');
|
|
949
|
+
}
|
|
950
|
+
if(urlLink && urlLink !== '' && urlLink !== 'http://'){
|
|
951
|
+
// block javascript here
|
|
952
|
+
/* istanbul ignore else: if it's javascript don't worry - though probably should show some kind of error message */
|
|
953
|
+
if (!blockJavascript(urlLink)) {
|
|
954
|
+
return this.$editor().wrapSelection('createLink', urlLink, true);
|
|
955
|
+
}
|
|
956
|
+
}
|
|
957
|
+
},
|
|
958
|
+
activeState: function(commonElement){
|
|
959
|
+
if(commonElement) return commonElement[0].tagName === 'A';
|
|
960
|
+
return false;
|
|
961
|
+
},
|
|
962
|
+
onElementSelect: {
|
|
963
|
+
element: 'a',
|
|
964
|
+
action: taToolFunctions.aOnSelectAction
|
|
965
|
+
}
|
|
966
|
+
});
|
|
967
|
+
taRegisterTool('wordcount', {
|
|
968
|
+
display: '<div id="toolbarWC" style="display:block; min-width:100px;">Words: <span ng-bind="wordcount"></span></div>',
|
|
969
|
+
disabled: true,
|
|
970
|
+
wordcount: 0,
|
|
971
|
+
activeState: function(){ // this fires on keyup
|
|
972
|
+
var textElement = this.$editor().displayElements.text;
|
|
973
|
+
/* istanbul ignore next: will default to '' when undefined */
|
|
974
|
+
var workingHTML = textElement[0].innerHTML || '';
|
|
975
|
+
var noOfWords = 0;
|
|
976
|
+
|
|
977
|
+
/* istanbul ignore if: will default to '' when undefined */
|
|
978
|
+
if (workingHTML.replace(/\s*<[^>]*?>\s*/g, '') !== '') {
|
|
979
|
+
if (workingHTML.trim() !== '') {
|
|
980
|
+
noOfWords = workingHTML.replace(/<\/?(b|i|em|strong|span|u|strikethrough|a|img|small|sub|sup|label)( [^>*?])?>/gi, '') // remove inline tags without adding spaces
|
|
981
|
+
.replace(/(<[^>]*?>\s*<[^>]*?>)/ig, ' ') // replace adjacent tags with possible space between with a space
|
|
982
|
+
.replace(/(<[^>]*?>)/ig, '') // remove any singular tags
|
|
983
|
+
.replace(/\s+/ig, ' ') // condense spacing
|
|
984
|
+
.match(/\S+/g).length; // count remaining non-space strings
|
|
985
|
+
}
|
|
986
|
+
}
|
|
987
|
+
|
|
988
|
+
//Set current scope
|
|
989
|
+
this.wordcount = noOfWords;
|
|
990
|
+
//Set editor scope
|
|
991
|
+
this.$editor().wordcount = noOfWords;
|
|
992
|
+
|
|
993
|
+
return false;
|
|
994
|
+
}
|
|
995
|
+
});
|
|
996
|
+
taRegisterTool('charcount', {
|
|
997
|
+
display: '<div id="toolbarCC" style="display:block; min-width:120px;">Characters: <span ng-bind="charcount"></span></div>',
|
|
998
|
+
disabled: true,
|
|
999
|
+
charcount: 0,
|
|
1000
|
+
activeState: function(){ // this fires on keyup
|
|
1001
|
+
var textElement = this.$editor().displayElements.text;
|
|
1002
|
+
var sourceText = textElement[0].innerText || textElement[0].textContent; // to cover the non-jquery use case.
|
|
1003
|
+
|
|
1004
|
+
// Caculate number of chars
|
|
1005
|
+
var noOfChars = sourceText.replace(/(\r\n|\n|\r)/gm,"").replace(/^\s+/g,' ').replace(/\s+$/g, ' ').length;
|
|
1006
|
+
//Set current scope
|
|
1007
|
+
this.charcount = noOfChars;
|
|
1008
|
+
//Set editor scope
|
|
1009
|
+
this.$editor().charcount = noOfChars;
|
|
1010
|
+
return false;
|
|
1011
|
+
}
|
|
1012
|
+
});
|
|
1013
|
+
}]);
|