Subversion Repositories oidplus

Rev

Rev 1042 | Go to most recent revision | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
597 daniel-mar 1
/**
2
 * Copyright (c) Tiny Technologies, Inc. All rights reserved.
3
 * Licensed under the LGPL or a commercial license.
4
 * For LGPL see License.txt in the project root for license information.
5
 * For commercial licenses see https://www.tiny.cloud/
6
 *
1422 daniel-mar 7
 * Version: 5.10.8 (2023-10-19)
597 daniel-mar 8
 */
9
(function () {
10
    'use strict';
11
 
637 daniel-mar 12
    var global$2 = tinymce.util.Tools.resolve('tinymce.PluginManager');
597 daniel-mar 13
 
14
    var global$1 = tinymce.util.Tools.resolve('tinymce.dom.RangeUtils');
15
 
637 daniel-mar 16
    var global = tinymce.util.Tools.resolve('tinymce.util.Tools');
597 daniel-mar 17
 
18
    var allowHtmlInNamedAnchor = function (editor) {
19
      return editor.getParam('allow_html_in_named_anchor', false, 'boolean');
20
    };
21
 
22
    var namedAnchorSelector = 'a:not([href])';
23
    var isEmptyString = function (str) {
24
      return !str;
25
    };
26
    var getIdFromAnchor = function (elm) {
27
      var id = elm.getAttribute('id') || elm.getAttribute('name');
28
      return id || '';
29
    };
30
    var isAnchor = function (elm) {
31
      return elm && elm.nodeName.toLowerCase() === 'a';
32
    };
33
    var isNamedAnchor = function (elm) {
34
      return isAnchor(elm) && !elm.getAttribute('href') && getIdFromAnchor(elm) !== '';
35
    };
36
    var isEmptyNamedAnchor = function (elm) {
37
      return isNamedAnchor(elm) && !elm.firstChild;
38
    };
39
 
40
    var removeEmptyNamedAnchorsInSelection = function (editor) {
41
      var dom = editor.dom;
42
      global$1(dom).walk(editor.selection.getRng(), function (nodes) {
637 daniel-mar 43
        global.each(nodes, function (node) {
597 daniel-mar 44
          if (isEmptyNamedAnchor(node)) {
45
            dom.remove(node, false);
46
          }
47
        });
48
      });
49
    };
50
    var isValidId = function (id) {
51
      return /^[A-Za-z][A-Za-z0-9\-:._]*$/.test(id);
52
    };
53
    var getNamedAnchor = function (editor) {
54
      return editor.dom.getParent(editor.selection.getStart(), namedAnchorSelector);
55
    };
56
    var getId = function (editor) {
57
      var anchor = getNamedAnchor(editor);
58
      if (anchor) {
59
        return getIdFromAnchor(anchor);
60
      } else {
61
        return '';
62
      }
63
    };
64
    var createAnchor = function (editor, id) {
65
      editor.undoManager.transact(function () {
66
        if (!allowHtmlInNamedAnchor(editor)) {
67
          editor.selection.collapse(true);
68
        }
69
        if (editor.selection.isCollapsed()) {
70
          editor.insertContent(editor.dom.createHTML('a', { id: id }));
71
        } else {
72
          removeEmptyNamedAnchorsInSelection(editor);
73
          editor.formatter.remove('namedAnchor', null, null, true);
74
          editor.formatter.apply('namedAnchor', { value: id });
75
          editor.addVisual();
76
        }
77
      });
78
    };
79
    var updateAnchor = function (editor, id, anchorElement) {
80
      anchorElement.removeAttribute('name');
81
      anchorElement.id = id;
82
      editor.addVisual();
83
      editor.undoManager.add();
84
    };
85
    var insert = function (editor, id) {
86
      var anchor = getNamedAnchor(editor);
87
      if (anchor) {
88
        updateAnchor(editor, id, anchor);
89
      } else {
90
        createAnchor(editor, id);
91
      }
92
      editor.focus();
93
    };
94
 
95
    var insertAnchor = function (editor, newId) {
96
      if (!isValidId(newId)) {
97
        editor.windowManager.alert('Id should start with a letter, followed only by letters, numbers, dashes, dots, colons or underscores.');
98
        return false;
99
      } else {
100
        insert(editor, newId);
101
        return true;
102
      }
103
    };
104
    var open = function (editor) {
105
      var currentId = getId(editor);
106
      editor.windowManager.open({
107
        title: 'Anchor',
108
        size: 'normal',
109
        body: {
110
          type: 'panel',
111
          items: [{
112
              name: 'id',
113
              type: 'input',
114
              label: 'ID',
115
              placeholder: 'example'
116
            }]
117
        },
118
        buttons: [
119
          {
120
            type: 'cancel',
121
            name: 'cancel',
122
            text: 'Cancel'
123
          },
124
          {
125
            type: 'submit',
126
            name: 'save',
127
            text: 'Save',
128
            primary: true
129
          }
130
        ],
131
        initialData: { id: currentId },
132
        onSubmit: function (api) {
133
          if (insertAnchor(editor, api.getData().id)) {
134
            api.close();
135
          }
136
        }
137
      });
138
    };
139
 
637 daniel-mar 140
    var register$1 = function (editor) {
597 daniel-mar 141
      editor.addCommand('mceAnchor', function () {
142
        open(editor);
143
      });
144
    };
145
 
146
    var isNamedAnchorNode = function (node) {
147
      return node && isEmptyString(node.attr('href')) && !isEmptyString(node.attr('id') || node.attr('name'));
148
    };
149
    var isEmptyNamedAnchorNode = function (node) {
150
      return isNamedAnchorNode(node) && !node.firstChild;
151
    };
152
    var setContentEditable = function (state) {
153
      return function (nodes) {
154
        for (var i = 0; i < nodes.length; i++) {
155
          var node = nodes[i];
156
          if (isEmptyNamedAnchorNode(node)) {
157
            node.attr('contenteditable', state);
158
          }
159
        }
160
      };
161
    };
162
    var setup = function (editor) {
163
      editor.on('PreInit', function () {
164
        editor.parser.addNodeFilter('a', setContentEditable('false'));
165
        editor.serializer.addNodeFilter('a', setContentEditable(null));
166
      });
167
    };
168
 
169
    var registerFormats = function (editor) {
170
      editor.formatter.register('namedAnchor', {
171
        inline: 'a',
172
        selector: namedAnchorSelector,
173
        remove: 'all',
174
        split: true,
175
        deep: true,
176
        attributes: { id: '%value' },
177
        onmatch: function (node, _fmt, _itemName) {
178
          return isNamedAnchor(node);
179
        }
180
      });
181
    };
182
 
637 daniel-mar 183
    var register = function (editor) {
597 daniel-mar 184
      editor.ui.registry.addToggleButton('anchor', {
185
        icon: 'bookmark',
186
        tooltip: 'Anchor',
187
        onAction: function () {
188
          return editor.execCommand('mceAnchor');
189
        },
190
        onSetup: function (buttonApi) {
191
          return editor.selection.selectorChangedWithUnbind('a:not([href])', buttonApi.setActive).unbind;
192
        }
193
      });
194
      editor.ui.registry.addMenuItem('anchor', {
195
        icon: 'bookmark',
196
        text: 'Anchor...',
197
        onAction: function () {
198
          return editor.execCommand('mceAnchor');
199
        }
200
      });
201
    };
202
 
203
    function Plugin () {
637 daniel-mar 204
      global$2.add('anchor', function (editor) {
597 daniel-mar 205
        setup(editor);
637 daniel-mar 206
        register$1(editor);
597 daniel-mar 207
        register(editor);
208
        editor.on('PreInit', function () {
209
          registerFormats(editor);
210
        });
211
      });
212
    }
213
 
214
    Plugin();
215
 
216
}());