Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
/**
2
 * TinyMCE version 6.8.3 (2024-02-08)
3
 */
4
 
5
(function () {
6
    'use strict';
7
 
8
    var global$1 = tinymce.util.Tools.resolve('tinymce.PluginManager');
9
 
10
    const eq = t => a => t === a;
11
    const isNull = eq(null);
12
    const isUndefined = eq(undefined);
13
    const isNullable = a => a === null || a === undefined;
14
    const isNonNullable = a => !isNullable(a);
15
 
16
    const noop = () => {
17
    };
18
    const constant = value => {
19
      return () => {
20
        return value;
21
      };
22
    };
23
    const never = constant(false);
24
 
25
    class Optional {
26
      constructor(tag, value) {
27
        this.tag = tag;
28
        this.value = value;
29
      }
30
      static some(value) {
31
        return new Optional(true, value);
32
      }
33
      static none() {
34
        return Optional.singletonNone;
35
      }
36
      fold(onNone, onSome) {
37
        if (this.tag) {
38
          return onSome(this.value);
39
        } else {
40
          return onNone();
41
        }
42
      }
43
      isSome() {
44
        return this.tag;
45
      }
46
      isNone() {
47
        return !this.tag;
48
      }
49
      map(mapper) {
50
        if (this.tag) {
51
          return Optional.some(mapper(this.value));
52
        } else {
53
          return Optional.none();
54
        }
55
      }
56
      bind(binder) {
57
        if (this.tag) {
58
          return binder(this.value);
59
        } else {
60
          return Optional.none();
61
        }
62
      }
63
      exists(predicate) {
64
        return this.tag && predicate(this.value);
65
      }
66
      forall(predicate) {
67
        return !this.tag || predicate(this.value);
68
      }
69
      filter(predicate) {
70
        if (!this.tag || predicate(this.value)) {
71
          return this;
72
        } else {
73
          return Optional.none();
74
        }
75
      }
76
      getOr(replacement) {
77
        return this.tag ? this.value : replacement;
78
      }
79
      or(replacement) {
80
        return this.tag ? this : replacement;
81
      }
82
      getOrThunk(thunk) {
83
        return this.tag ? this.value : thunk();
84
      }
85
      orThunk(thunk) {
86
        return this.tag ? this : thunk();
87
      }
88
      getOrDie(message) {
89
        if (!this.tag) {
90
          throw new Error(message !== null && message !== void 0 ? message : 'Called getOrDie on None');
91
        } else {
92
          return this.value;
93
        }
94
      }
95
      static from(value) {
96
        return isNonNullable(value) ? Optional.some(value) : Optional.none();
97
      }
98
      getOrNull() {
99
        return this.tag ? this.value : null;
100
      }
101
      getOrUndefined() {
102
        return this.value;
103
      }
104
      each(worker) {
105
        if (this.tag) {
106
          worker(this.value);
107
        }
108
      }
109
      toArray() {
110
        return this.tag ? [this.value] : [];
111
      }
112
      toString() {
113
        return this.tag ? `some(${ this.value })` : 'none()';
114
      }
115
    }
116
    Optional.singletonNone = new Optional(false);
117
 
118
    const exists = (xs, pred) => {
119
      for (let i = 0, len = xs.length; i < len; i++) {
120
        const x = xs[i];
121
        if (pred(x, i)) {
122
          return true;
123
        }
124
      }
125
      return false;
126
    };
127
    const map$1 = (xs, f) => {
128
      const len = xs.length;
129
      const r = new Array(len);
130
      for (let i = 0; i < len; i++) {
131
        const x = xs[i];
132
        r[i] = f(x, i);
133
      }
134
      return r;
135
    };
136
    const each$1 = (xs, f) => {
137
      for (let i = 0, len = xs.length; i < len; i++) {
138
        const x = xs[i];
139
        f(x, i);
140
      }
141
    };
142
 
143
    const Cell = initial => {
144
      let value = initial;
145
      const get = () => {
146
        return value;
147
      };
148
      const set = v => {
149
        value = v;
150
      };
151
      return {
152
        get,
153
        set
154
      };
155
    };
156
 
157
    const last = (fn, rate) => {
158
      let timer = null;
159
      const cancel = () => {
160
        if (!isNull(timer)) {
161
          clearTimeout(timer);
162
          timer = null;
163
        }
164
      };
165
      const throttle = (...args) => {
166
        cancel();
167
        timer = setTimeout(() => {
168
          timer = null;
169
          fn.apply(null, args);
170
        }, rate);
171
      };
172
      return {
173
        cancel,
174
        throttle
175
      };
176
    };
177
 
178
    const insertEmoticon = (editor, ch) => {
179
      editor.insertContent(ch);
180
    };
181
 
182
    const keys = Object.keys;
183
    const hasOwnProperty = Object.hasOwnProperty;
184
    const each = (obj, f) => {
185
      const props = keys(obj);
186
      for (let k = 0, len = props.length; k < len; k++) {
187
        const i = props[k];
188
        const x = obj[i];
189
        f(x, i);
190
      }
191
    };
192
    const map = (obj, f) => {
193
      return tupleMap(obj, (x, i) => ({
194
        k: i,
195
        v: f(x, i)
196
      }));
197
    };
198
    const tupleMap = (obj, f) => {
199
      const r = {};
200
      each(obj, (x, i) => {
201
        const tuple = f(x, i);
202
        r[tuple.k] = tuple.v;
203
      });
204
      return r;
205
    };
206
    const has = (obj, key) => hasOwnProperty.call(obj, key);
207
 
208
    const shallow = (old, nu) => {
209
      return nu;
210
    };
211
    const baseMerge = merger => {
212
      return (...objects) => {
213
        if (objects.length === 0) {
214
          throw new Error(`Can't merge zero objects`);
215
        }
216
        const ret = {};
217
        for (let j = 0; j < objects.length; j++) {
218
          const curObject = objects[j];
219
          for (const key in curObject) {
220
            if (has(curObject, key)) {
221
              ret[key] = merger(ret[key], curObject[key]);
222
            }
223
          }
224
        }
225
        return ret;
226
      };
227
    };
228
    const merge = baseMerge(shallow);
229
 
230
    const singleton = doRevoke => {
231
      const subject = Cell(Optional.none());
232
      const revoke = () => subject.get().each(doRevoke);
233
      const clear = () => {
234
        revoke();
235
        subject.set(Optional.none());
236
      };
237
      const isSet = () => subject.get().isSome();
238
      const get = () => subject.get();
239
      const set = s => {
240
        revoke();
241
        subject.set(Optional.some(s));
242
      };
243
      return {
244
        clear,
245
        isSet,
246
        get,
247
        set
248
      };
249
    };
250
    const value = () => {
251
      const subject = singleton(noop);
252
      const on = f => subject.get().each(f);
253
      return {
254
        ...subject,
255
        on
256
      };
257
    };
258
 
259
    const checkRange = (str, substr, start) => substr === '' || str.length >= substr.length && str.substr(start, start + substr.length) === substr;
260
    const contains = (str, substr, start = 0, end) => {
261
      const idx = str.indexOf(substr, start);
262
      if (idx !== -1) {
263
        return isUndefined(end) ? true : idx + substr.length <= end;
264
      } else {
265
        return false;
266
      }
267
    };
268
    const startsWith = (str, prefix) => {
269
      return checkRange(str, prefix, 0);
270
    };
271
 
272
    var global = tinymce.util.Tools.resolve('tinymce.Resource');
273
 
274
    const DEFAULT_ID = 'tinymce.plugins.emoticons';
275
    const option = name => editor => editor.options.get(name);
276
    const register$2 = (editor, pluginUrl) => {
277
      const registerOption = editor.options.register;
278
      registerOption('emoticons_database', {
279
        processor: 'string',
280
        default: 'emojis'
281
      });
282
      registerOption('emoticons_database_url', {
283
        processor: 'string',
284
        default: `${ pluginUrl }/js/${ getEmojiDatabase(editor) }${ editor.suffix }.js`
285
      });
286
      registerOption('emoticons_database_id', {
287
        processor: 'string',
288
        default: DEFAULT_ID
289
      });
290
      registerOption('emoticons_append', {
291
        processor: 'object',
292
        default: {}
293
      });
294
      registerOption('emoticons_images_url', {
295
        processor: 'string',
296
        default: 'https://twemoji.maxcdn.com/v/13.0.1/72x72/'
297
      });
298
    };
299
    const getEmojiDatabase = option('emoticons_database');
300
    const getEmojiDatabaseUrl = option('emoticons_database_url');
301
    const getEmojiDatabaseId = option('emoticons_database_id');
302
    const getAppendedEmoji = option('emoticons_append');
303
    const getEmojiImageUrl = option('emoticons_images_url');
304
 
305
    const ALL_CATEGORY = 'All';
306
    const categoryNameMap = {
307
      symbols: 'Symbols',
308
      people: 'People',
309
      animals_and_nature: 'Animals and Nature',
310
      food_and_drink: 'Food and Drink',
311
      activity: 'Activity',
312
      travel_and_places: 'Travel and Places',
313
      objects: 'Objects',
314
      flags: 'Flags',
315
      user: 'User Defined'
316
    };
317
    const translateCategory = (categories, name) => has(categories, name) ? categories[name] : name;
318
    const getUserDefinedEmoji = editor => {
319
      const userDefinedEmoticons = getAppendedEmoji(editor);
320
      return map(userDefinedEmoticons, value => ({
321
        keywords: [],
322
        category: 'user',
323
        ...value
324
      }));
325
    };
326
    const initDatabase = (editor, databaseUrl, databaseId) => {
327
      const categories = value();
328
      const all = value();
329
      const emojiImagesUrl = getEmojiImageUrl(editor);
330
      const getEmoji = lib => {
331
        if (startsWith(lib.char, '<img')) {
332
          return lib.char.replace(/src="([^"]+)"/, (match, url) => `src="${ emojiImagesUrl }${ url }"`);
333
        } else {
334
          return lib.char;
335
        }
336
      };
337
      const processEmojis = emojis => {
338
        const cats = {};
339
        const everything = [];
340
        each(emojis, (lib, title) => {
341
          const entry = {
342
            title,
343
            keywords: lib.keywords,
344
            char: getEmoji(lib),
345
            category: translateCategory(categoryNameMap, lib.category)
346
          };
347
          const current = cats[entry.category] !== undefined ? cats[entry.category] : [];
348
          cats[entry.category] = current.concat([entry]);
349
          everything.push(entry);
350
        });
351
        categories.set(cats);
352
        all.set(everything);
353
      };
354
      editor.on('init', () => {
355
        global.load(databaseId, databaseUrl).then(emojis => {
356
          const userEmojis = getUserDefinedEmoji(editor);
357
          processEmojis(merge(emojis, userEmojis));
358
        }, err => {
359
          console.log(`Failed to load emojis: ${ err }`);
360
          categories.set({});
361
          all.set([]);
362
        });
363
      });
364
      const listCategory = category => {
365
        if (category === ALL_CATEGORY) {
366
          return listAll();
367
        }
368
        return categories.get().bind(cats => Optional.from(cats[category])).getOr([]);
369
      };
370
      const listAll = () => all.get().getOr([]);
371
      const listCategories = () => [ALL_CATEGORY].concat(keys(categories.get().getOr({})));
372
      const waitForLoad = () => {
373
        if (hasLoaded()) {
374
          return Promise.resolve(true);
375
        } else {
376
          return new Promise((resolve, reject) => {
377
            let numRetries = 15;
378
            const interval = setInterval(() => {
379
              if (hasLoaded()) {
380
                clearInterval(interval);
381
                resolve(true);
382
              } else {
383
                numRetries--;
384
                if (numRetries < 0) {
385
                  console.log('Could not load emojis from url: ' + databaseUrl);
386
                  clearInterval(interval);
387
                  reject(false);
388
                }
389
              }
390
            }, 100);
391
          });
392
        }
393
      };
394
      const hasLoaded = () => categories.isSet() && all.isSet();
395
      return {
396
        listCategories,
397
        hasLoaded,
398
        waitForLoad,
399
        listAll,
400
        listCategory
401
      };
402
    };
403
 
404
    const emojiMatches = (emoji, lowerCasePattern) => contains(emoji.title.toLowerCase(), lowerCasePattern) || exists(emoji.keywords, k => contains(k.toLowerCase(), lowerCasePattern));
405
    const emojisFrom = (list, pattern, maxResults) => {
406
      const matches = [];
407
      const lowerCasePattern = pattern.toLowerCase();
408
      const reachedLimit = maxResults.fold(() => never, max => size => size >= max);
409
      for (let i = 0; i < list.length; i++) {
410
        if (pattern.length === 0 || emojiMatches(list[i], lowerCasePattern)) {
411
          matches.push({
412
            value: list[i].char,
413
            text: list[i].title,
414
            icon: list[i].char
415
          });
416
          if (reachedLimit(matches.length)) {
417
            break;
418
          }
419
        }
420
      }
421
      return matches;
422
    };
423
 
424
    const patternName = 'pattern';
425
    const open = (editor, database) => {
426
      const initialState = {
427
        pattern: '',
428
        results: emojisFrom(database.listAll(), '', Optional.some(300))
429
      };
430
      const currentTab = Cell(ALL_CATEGORY);
431
      const scan = dialogApi => {
432
        const dialogData = dialogApi.getData();
433
        const category = currentTab.get();
434
        const candidates = database.listCategory(category);
435
        const results = emojisFrom(candidates, dialogData[patternName], category === ALL_CATEGORY ? Optional.some(300) : Optional.none());
436
        dialogApi.setData({ results });
437
      };
438
      const updateFilter = last(dialogApi => {
439
        scan(dialogApi);
440
      }, 200);
441
      const searchField = {
442
        label: 'Search',
443
        type: 'input',
444
        name: patternName
445
      };
446
      const resultsField = {
447
        type: 'collection',
448
        name: 'results'
449
      };
450
      const getInitialState = () => {
451
        const body = {
452
          type: 'tabpanel',
453
          tabs: map$1(database.listCategories(), cat => ({
454
            title: cat,
455
            name: cat,
456
            items: [
457
              searchField,
458
              resultsField
459
            ]
460
          }))
461
        };
462
        return {
463
          title: 'Emojis',
464
          size: 'normal',
465
          body,
466
          initialData: initialState,
467
          onTabChange: (dialogApi, details) => {
468
            currentTab.set(details.newTabName);
469
            updateFilter.throttle(dialogApi);
470
          },
471
          onChange: updateFilter.throttle,
472
          onAction: (dialogApi, actionData) => {
473
            if (actionData.name === 'results') {
474
              insertEmoticon(editor, actionData.value);
475
              dialogApi.close();
476
            }
477
          },
478
          buttons: [{
479
              type: 'cancel',
480
              text: 'Close',
481
              primary: true
482
            }]
483
        };
484
      };
485
      const dialogApi = editor.windowManager.open(getInitialState());
486
      dialogApi.focus(patternName);
487
      if (!database.hasLoaded()) {
488
        dialogApi.block('Loading emojis...');
489
        database.waitForLoad().then(() => {
490
          dialogApi.redial(getInitialState());
491
          updateFilter.throttle(dialogApi);
492
          dialogApi.focus(patternName);
493
          dialogApi.unblock();
494
        }).catch(_err => {
495
          dialogApi.redial({
496
            title: 'Emojis',
497
            body: {
498
              type: 'panel',
499
              items: [{
500
                  type: 'alertbanner',
501
                  level: 'error',
502
                  icon: 'warning',
503
                  text: 'Could not load emojis'
504
                }]
505
            },
506
            buttons: [{
507
                type: 'cancel',
508
                text: 'Close',
509
                primary: true
510
              }],
511
            initialData: {
512
              pattern: '',
513
              results: []
514
            }
515
          });
516
          dialogApi.focus(patternName);
517
          dialogApi.unblock();
518
        });
519
      }
520
    };
521
 
522
    const register$1 = (editor, database) => {
523
      editor.addCommand('mceEmoticons', () => open(editor, database));
524
    };
525
 
526
    const setup = editor => {
527
      editor.on('PreInit', () => {
528
        editor.parser.addAttributeFilter('data-emoticon', nodes => {
529
          each$1(nodes, node => {
530
            node.attr('data-mce-resize', 'false');
531
            node.attr('data-mce-placeholder', '1');
532
          });
533
        });
534
      });
535
    };
536
 
537
    const init = (editor, database) => {
538
      editor.ui.registry.addAutocompleter('emoticons', {
539
        trigger: ':',
540
        columns: 'auto',
541
        minChars: 2,
542
        fetch: (pattern, maxResults) => database.waitForLoad().then(() => {
543
          const candidates = database.listAll();
544
          return emojisFrom(candidates, pattern, Optional.some(maxResults));
545
        }),
546
        onAction: (autocompleteApi, rng, value) => {
547
          editor.selection.setRng(rng);
548
          editor.insertContent(value);
549
          autocompleteApi.hide();
550
        }
551
      });
552
    };
553
 
554
    const onSetupEditable = editor => api => {
555
      const nodeChanged = () => {
556
        api.setEnabled(editor.selection.isEditable());
557
      };
558
      editor.on('NodeChange', nodeChanged);
559
      nodeChanged();
560
      return () => {
561
        editor.off('NodeChange', nodeChanged);
562
      };
563
    };
564
    const register = editor => {
565
      const onAction = () => editor.execCommand('mceEmoticons');
566
      editor.ui.registry.addButton('emoticons', {
567
        tooltip: 'Emojis',
568
        icon: 'emoji',
569
        onAction,
570
        onSetup: onSetupEditable(editor)
571
      });
572
      editor.ui.registry.addMenuItem('emoticons', {
573
        text: 'Emojis...',
574
        icon: 'emoji',
575
        onAction,
576
        onSetup: onSetupEditable(editor)
577
      });
578
    };
579
 
580
    var Plugin = () => {
581
      global$1.add('emoticons', (editor, pluginUrl) => {
582
        register$2(editor, pluginUrl);
583
        const databaseUrl = getEmojiDatabaseUrl(editor);
584
        const databaseId = getEmojiDatabaseId(editor);
585
        const database = initDatabase(editor, databaseUrl, databaseId);
586
        register$1(editor, database);
587
        register(editor);
588
        init(editor, database);
589
        setup(editor);
590
      });
591
    };
592
 
593
    Plugin();
594
 
595
})();