File "plugin-20241115132540.js"
Full Path: /home/krishnamexports/public_html/panel/vendors/tinymce/plugins/textpattern/plugin-20241115132540.js
File size: 28.22 KB
MIME-type: text/plain; charset=us-ascii
Charset: utf-8
(function () {
var defs = {}; // id -> {dependencies, definition, instance (possibly undefined)}
// Used when there is no 'main' module.
// The name is probably (hopefully) unique so minification removes for releases.
var register_3795 = function (id) {
var module = dem(id);
var fragments = id.split('.');
var target = Function('return this;')();
for (var i = 0; i < fragments.length - 1; ++i) {
if (target[fragments[i]] === undefined)
target[fragments[i]] = {};
target = target[fragments[i]];
}
target[fragments[fragments.length - 1]] = module;
};
var instantiate = function (id) {
var actual = defs[id];
var dependencies = actual.deps;
var definition = actual.defn;
var len = dependencies.length;
var instances = new Array(len);
for (var i = 0; i < len; ++i)
instances[i] = dem(dependencies[i]);
var defResult = definition.apply(null, instances);
if (defResult === undefined)
throw 'module [' + id + '] returned undefined';
actual.instance = defResult;
};
var def = function (id, dependencies, definition) {
if (typeof id !== 'string')
throw 'module id must be a string';
else if (dependencies === undefined)
throw 'no dependencies for ' + id;
else if (definition === undefined)
throw 'no definition function for ' + id;
defs[id] = {
deps: dependencies,
defn: definition,
instance: undefined
};
};
var dem = function (id) {
var actual = defs[id];
if (actual === undefined)
throw 'module [' + id + '] was undefined';
else if (actual.instance === undefined)
instantiate(id);
return actual.instance;
};
var req = function (ids, callback) {
var len = ids.length;
var instances = new Array(len);
for (var i = 0; i < len; ++i)
instances.push(dem(ids[i]));
callback.apply(null, callback);
};
var ephox = {};
ephox.bolt = {
module: {
api: {
define: def,
require: req,
demand: dem
}
}
};
var define = def;
var require = req;
var demand = dem;
// this helps with minificiation when using a lot of global references
var defineGlobal = function (id, ref) {
define(id, [], function () { return ref; });
};
/*jsc
["tinymce.plugins.visualchars.Plugin","tinymce.core.PluginManager","tinymce.core.util.Delay","ephox.katamari.api.Arr","ephox.sugar.api.node.Element","tinymce.plugins.visualchars.core.VisualChars","global!tinymce.util.Tools.resolve","ephox.katamari.api.Option","global!Array","global!Error","global!String","ephox.katamari.api.Fun","global!console","global!document","tinymce.plugins.visualchars.core.Data","tinymce.plugins.visualchars.core.Nodes","ephox.sugar.api.node.Node","global!Object","ephox.sugar.api.node.NodeTypes","tinymce.plugins.visualchars.core.Html"]
jsc*/
defineGlobal("global!tinymce.util.Tools.resolve", tinymce.util.Tools.resolve);
/**
* ResolveGlobal.js
*
* Released under LGPL License.
* Copyright (c) 1999-2017 Ephox Corp. All rights reserved
*
* License: http://www.tinymce.com/license
* Contributing: http://www.tinymce.com/contributing
*/
define(
'tinymce.core.PluginManager',
[
'global!tinymce.util.Tools.resolve'
],
function (resolve) {
return resolve('tinymce.PluginManager');
}
);
/**
* ResolveGlobal.js
*
* Released under LGPL License.
* Copyright (c) 1999-2017 Ephox Corp. All rights reserved
*
* License: http://www.tinymce.com/license
* Contributing: http://www.tinymce.com/contributing
*/
define(
'tinymce.core.util.Delay',
[
'global!tinymce.util.Tools.resolve'
],
function (resolve) {
return resolve('tinymce.util.Delay');
}
);
defineGlobal("global!Array", Array);
defineGlobal("global!Error", Error);
define(
'ephox.katamari.api.Fun',
[
'global!Array',
'global!Error'
],
function (Array, Error) {
var noop = function () { };
var compose = function (fa, fb) {
return function () {
return fa(fb.apply(null, arguments));
};
};
var constant = function (value) {
return function () {
return value;
};
};
var identity = function (x) {
return x;
};
var tripleEquals = function(a, b) {
return a === b;
};
// Don't use array slice(arguments), makes the whole function unoptimisable on Chrome
var curry = function (f) {
// equivalent to arguments.slice(1)
// starting at 1 because 0 is the f, makes things tricky.
// Pay attention to what variable is where, and the -1 magic.
// thankfully, we have tests for this.
var args = new Array(arguments.length - 1);
for (var i = 1; i < arguments.length; i++) args[i-1] = arguments[i];
return function () {
var newArgs = new Array(arguments.length);
for (var j = 0; j < newArgs.length; j++) newArgs[j] = arguments[j];
var all = args.concat(newArgs);
return f.apply(null, all);
};
};
var not = function (f) {
return function () {
return !f.apply(null, arguments);
};
};
var die = function (msg) {
return function () {
throw new Error(msg);
};
};
var apply = function (f) {
return f();
};
var call = function(f) {
f();
};
var never = constant(false);
var always = constant(true);
return {
noop: noop,
compose: compose,
constant: constant,
identity: identity,
tripleEquals: tripleEquals,
curry: curry,
not: not,
die: die,
apply: apply,
call: call,
never: never,
always: always
};
}
);
defineGlobal("global!Object", Object);
define(
'ephox.katamari.api.Option',
[
'ephox.katamari.api.Fun',
'global!Object'
],
function (Fun, Object) {
var never = Fun.never;
var always = Fun.always;
/**
Option objects support the following methods:
fold :: this Option a -> ((() -> b, a -> b)) -> Option b
is :: this Option a -> a -> Boolean
isSome :: this Option a -> () -> Boolean
isNone :: this Option a -> () -> Boolean
getOr :: this Option a -> a -> a
getOrThunk :: this Option a -> (() -> a) -> a
getOrDie :: this Option a -> String -> a
or :: this Option a -> Option a -> Option a
- if some: return self
- if none: return opt
orThunk :: this Option a -> (() -> Option a) -> Option a
- Same as "or", but uses a thunk instead of a value
map :: this Option a -> (a -> b) -> Option b
- "fmap" operation on the Option Functor.
- same as 'each'
ap :: this Option a -> Option (a -> b) -> Option b
- "apply" operation on the Option Apply/Applicative.
- Equivalent to <*> in Haskell/PureScript.
each :: this Option a -> (a -> b) -> Option b
- same as 'map'
bind :: this Option a -> (a -> Option b) -> Option b
- "bind"/"flatMap" operation on the Option Bind/Monad.
- Equivalent to >>= in Haskell/PureScript; flatMap in Scala.
flatten :: {this Option (Option a))} -> () -> Option a
- "flatten"/"join" operation on the Option Monad.
exists :: this Option a -> (a -> Boolean) -> Boolean
forall :: this Option a -> (a -> Boolean) -> Boolean
filter :: this Option a -> (a -> Boolean) -> Option a
equals :: this Option a -> Option a -> Boolean
equals_ :: this Option a -> (Option a, a -> Boolean) -> Boolean
toArray :: this Option a -> () -> [a]
*/
var none = function () { return NONE; };
var NONE = (function () {
var eq = function (o) {
return o.isNone();
};
// inlined from peanut, maybe a micro-optimisation?
var call = function (thunk) { return thunk(); };
var id = function (n) { return n; };
var noop = function () { };
var me = {
fold: function (n, s) { return n(); },
is: never,
isSome: never,
isNone: always,
getOr: id,
getOrThunk: call,
getOrDie: function (msg) {
throw new Error(msg || 'error: getOrDie called on none.');
},
or: id,
orThunk: call,
map: none,
ap: none,
each: noop,
bind: none,
flatten: none,
exists: never,
forall: always,
filter: none,
equals: eq,
equals_: eq,
toArray: function () { return []; },
toString: Fun.constant("none()")
};
if (Object.freeze) Object.freeze(me);
return me;
})();
/** some :: a -> Option a */
var some = function (a) {
// inlined from peanut, maybe a micro-optimisation?
var constant_a = function () { return a; };
var self = function () {
// can't Fun.constant this one
return me;
};
var map = function (f) {
return some(f(a));
};
var bind = function (f) {
return f(a);
};
var me = {
fold: function (n, s) { return s(a); },
is: function (v) { return a === v; },
isSome: always,
isNone: never,
getOr: constant_a,
getOrThunk: constant_a,
getOrDie: constant_a,
or: self,
orThunk: self,
map: map,
ap: function (optfab) {
return optfab.fold(none, function(fab) {
return some(fab(a));
});
},
each: function (f) {
f(a);
},
bind: bind,
flatten: constant_a,
exists: bind,
forall: bind,
filter: function (f) {
return f(a) ? me : NONE;
},
equals: function (o) {
return o.is(a);
},
equals_: function (o, elementEq) {
return o.fold(
never,
function (b) { return elementEq(a, b); }
);
},
toArray: function () {
return [a];
},
toString: function () {
return 'some(' + a + ')';
}
};
return me;
};
/** from :: undefined|null|a -> Option a */
var from = function (value) {
return value === null || value === undefined ? NONE : some(value);
};
return {
some: some,
none: none,
from: from
};
}
);
defineGlobal("global!String", String);
define(
'ephox.katamari.api.Arr',
[
'ephox.katamari.api.Option',
'global!Array',
'global!Error',
'global!String'
],
function (Option, Array, Error, String) {
// Use the native Array.indexOf if it is available (IE9+) otherwise fall back to manual iteration
// https://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Array/indexOf
var rawIndexOf = (function () {
var pIndexOf = Array.prototype.indexOf;
var fastIndex = function (xs, x) { return pIndexOf.call(xs, x); };
var slowIndex = function(xs, x) { return slowIndexOf(xs, x); };
return pIndexOf === undefined ? slowIndex : fastIndex;
})();
var indexOf = function (xs, x) {
// The rawIndexOf method does not wrap up in an option. This is for performance reasons.
var r = rawIndexOf(xs, x);
return r === -1 ? Option.none() : Option.some(r);
};
var contains = function (xs, x) {
return rawIndexOf(xs, x) > -1;
};
// Using findIndex is likely less optimal in Chrome (dynamic return type instead of bool)
// but if we need that micro-optimisation we can inline it later.
var exists = function (xs, pred) {
return findIndex(xs, pred).isSome();
};
var range = function (num, f) {
var r = [];
for (var i = 0; i < num; i++) {
r.push(f(i));
}
return r;
};
// It's a total micro optimisation, but these do make some difference.
// Particularly for browsers other than Chrome.
// - length caching
// http://jsperf.com/browser-diet-jquery-each-vs-for-loop/69
// - not using push
// http://jsperf.com/array-direct-assignment-vs-push/2
var chunk = function (array, size) {
var r = [];
for (var i = 0; i < array.length; i += size) {
var s = array.slice(i, i + size);
r.push(s);
}
return r;
};
var map = function(xs, f) {
// pre-allocating array size when it's guaranteed to be known
// http://jsperf.com/push-allocated-vs-dynamic/22
var len = xs.length;
var r = new Array(len);
for (var i = 0; i < len; i++) {
var x = xs[i];
r[i] = f(x, i, xs);
}
return r;
};
// Unwound implementing other functions in terms of each.
// The code size is roughly the same, and it should allow for better optimisation.
var each = function(xs, f) {
for (var i = 0, len = xs.length; i < len; i++) {
var x = xs[i];
f(x, i, xs);
}
};
var eachr = function (xs, f) {
for (var i = xs.length - 1; i >= 0; i--) {
var x = xs[i];
f(x, i, xs);
}
};
var partition = function(xs, pred) {
var pass = [];
var fail = [];
for (var i = 0, len = xs.length; i < len; i++) {
var x = xs[i];
var arr = pred(x, i, xs) ? pass : fail;
arr.push(x);
}
return { pass: pass, fail: fail };
};
var filter = function(xs, pred) {
var r = [];
for (var i = 0, len = xs.length; i < len; i++) {
var x = xs[i];
if (pred(x, i, xs)) {
r.push(x);
}
}
return r;
};
/*
* Groups an array into contiguous arrays of like elements. Whether an element is like or not depends on f.
*
* f is a function that derives a value from an element - e.g. true or false, or a string.
* Elements are like if this function generates the same value for them (according to ===).
*
*
* Order of the elements is preserved. Arr.flatten() on the result will return the original list, as with Haskell groupBy function.
* For a good explanation, see the group function (which is a special case of groupBy)
* http://hackage.haskell.org/package/base-4.7.0.0/docs/Data-List.html#v:group
*/
var groupBy = function (xs, f) {
if (xs.length === 0) {
return [];
} else {
var wasType = f(xs[0]); // initial case for matching
var r = [];
var group = [];
for (var i = 0, len = xs.length; i < len; i++) {
var x = xs[i];
var type = f(x);
if (type !== wasType) {
r.push(group);
group = [];
}
wasType = type;
group.push(x);
}
if (group.length !== 0) {
r.push(group);
}
return r;
}
};
var foldr = function (xs, f, acc) {
eachr(xs, function (x) {
acc = f(acc, x);
});
return acc;
};
var foldl = function (xs, f, acc) {
each(xs, function (x) {
acc = f(acc, x);
});
return acc;
};
var find = function (xs, pred) {
for (var i = 0, len = xs.length; i < len; i++) {
var x = xs[i];
if (pred(x, i, xs)) {
return Option.some(x);
}
}
return Option.none();
};
var findIndex = function (xs, pred) {
for (var i = 0, len = xs.length; i < len; i++) {
var x = xs[i];
if (pred(x, i, xs)) {
return Option.some(i);
}
}
return Option.none();
};
var slowIndexOf = function (xs, x) {
for (var i = 0, len = xs.length; i < len; ++i) {
if (xs[i] === x) {
return i;
}
}
return -1;
};
var push = Array.prototype.push;
var flatten = function (xs) {
// Note, this is possible because push supports multiple arguments:
// http://jsperf.com/concat-push/6
// Note that in the past, concat() would silently work (very slowly) for array-like objects.
// With this change it will throw an error.
var r = [];
for (var i = 0, len = xs.length; i < len; ++i) {
// Ensure that each value is an array itself
if (! Array.prototype.isPrototypeOf(xs[i])) throw new Error('Arr.flatten item ' + i + ' was not an array, input: ' + xs);
push.apply(r, xs[i]);
}
return r;
};
var bind = function (xs, f) {
var output = map(xs, f);
return flatten(output);
};
var forall = function (xs, pred) {
for (var i = 0, len = xs.length; i < len; ++i) {
var x = xs[i];
if (pred(x, i, xs) !== true) {
return false;
}
}
return true;
};
var equal = function (a1, a2) {
return a1.length === a2.length && forall(a1, function (x, i) {
return x === a2[i];
});
};
var slice = Array.prototype.slice;
var reverse = function (xs) {
var r = slice.call(xs, 0);
r.reverse();
return r;
};
var difference = function (a1, a2) {
return filter(a1, function (x) {
return !contains(a2, x);
});
};
var mapToObject = function(xs, f) {
var r = {};
for (var i = 0, len = xs.length; i < len; i++) {
var x = xs[i];
r[String(x)] = f(x, i);
}
return r;
};
var pure = function(x) {
return [x];
};
var sort = function (xs, comparator) {
var copy = slice.call(xs, 0);
copy.sort(comparator);
return copy;
};
return {
map: map,
each: each,
eachr: eachr,
partition: partition,
filter: filter,
groupBy: groupBy,
indexOf: indexOf,
foldr: foldr,
foldl: foldl,
find: find,
findIndex: findIndex,
flatten: flatten,
bind: bind,
forall: forall,
exists: exists,
contains: contains,
equal: equal,
reverse: reverse,
chunk: chunk,
difference: difference,
mapToObject: mapToObject,
pure: pure,
sort: sort,
range: range
};
}
);
define("global!console", [], function () { if (typeof console === "undefined") console = { log: function () {} }; return console; });
defineGlobal("global!document", document);
define(
'ephox.sugar.api.node.Element',
[
'ephox.katamari.api.Fun',
'global!Error',
'global!console',
'global!document'
],
function (Fun, Error, console, document) {
var fromHtml = function (html, scope) {
var doc = scope || document;
var div = doc.createElement('div');
div.innerHTML = html;
if (!div.hasChildNodes() || div.childNodes.length > 1) {
console.error('HTML does not have a single root node', html);
throw 'HTML must have a single root node';
}
return fromDom(div.childNodes[0]);
};
var fromTag = function (tag, scope) {
var doc = scope || document;
var node = doc.createElement(tag);
return fromDom(node);
};
var fromText = function (text, scope) {
var doc = scope || document;
var node = doc.createTextNode(text);
return fromDom(node);
};
var fromDom = function (node) {
if (node === null || node === undefined) throw new Error('Node cannot be null or undefined');
return {
dom: Fun.constant(node)
};
};
return {
fromHtml: fromHtml,
fromTag: fromTag,
fromText: fromText,
fromDom: fromDom
};
}
);
/**
* Plugin.js
*
* Released under LGPL License.
* Copyright (c) 1999-2017 Ephox Corp. All rights reserved
*
* License: http://www.tinymce.com/license
* Contributing: http://www.tinymce.com/contributing
*/
define(
'tinymce.plugins.visualchars.core.Data',
[
],
function () {
var charMap = {
'\u00a0': 'nbsp',
'\u00ad': 'shy'
};
var charMapToRegExp = function (charMap, global) {
var key, regExp = '';
for (key in charMap) {
regExp += key;
}
return new RegExp('[' + regExp + ']', global ? 'g' : '');
};
var charMapToSelector = function (charMap) {
var key, selector = '';
for (key in charMap) {
if (selector) {
selector += ',';
}
selector += 'span.mce-' + charMap[key];
}
return selector;
};
return {
charMap: charMap,
regExp: charMapToRegExp(charMap),
regExpGlobal: charMapToRegExp(charMap, true),
selector: charMapToSelector(charMap),
charMapToRegExp: charMapToRegExp,
charMapToSelector: charMapToSelector
};
}
);
define(
'ephox.sugar.api.node.NodeTypes',
[
],
function () {
return {
ATTRIBUTE: 2,
CDATA_SECTION: 4,
COMMENT: 8,
DOCUMENT: 9,
DOCUMENT_TYPE: 10,
DOCUMENT_FRAGMENT: 11,
ELEMENT: 1,
TEXT: 3,
PROCESSING_INSTRUCTION: 7,
ENTITY_REFERENCE: 5,
ENTITY: 6,
NOTATION: 12
};
}
);
define(
'ephox.sugar.api.node.Node',
[
'ephox.sugar.api.node.NodeTypes'
],
function (NodeTypes) {
var name = function (element) {
var r = element.dom().nodeName;
return r.toLowerCase();
};
var type = function (element) {
return element.dom().nodeType;
};
var value = function (element) {
return element.dom().nodeValue;
};
var isType = function (t) {
return function (element) {
return type(element) === t;
};
};
var isComment = function (element) {
return type(element) === NodeTypes.COMMENT || name(element) === '#comment';
};
var isElement = isType(NodeTypes.ELEMENT);
var isText = isType(NodeTypes.TEXT);
var isDocument = isType(NodeTypes.DOCUMENT);
return {
name: name,
type: type,
value: value,
isElement: isElement,
isText: isText,
isDocument: isDocument,
isComment: isComment
};
}
);
define(
'tinymce.plugins.visualchars.core.Html',
[
'tinymce.plugins.visualchars.core.Data'
],
function (Data) {
var wrapCharWithSpan = function (value) {
return '<span data-mce-bogus="1" class="mce-' + Data.charMap[value] + '">' + value + '</span>';
};
return {
wrapCharWithSpan: wrapCharWithSpan
};
}
);
/**
* Plugin.js
*
* Released under LGPL License.
* Copyright (c) 1999-2017 Ephox Corp. All rights reserved
*
* License: http://www.tinymce.com/license
* Contributing: http://www.tinymce.com/contributing
*/
define(
'tinymce.plugins.visualchars.core.Nodes',
[
'ephox.katamari.api.Arr',
'ephox.sugar.api.node.Element',
'ephox.sugar.api.node.Node',
'tinymce.plugins.visualchars.core.Data',
'tinymce.plugins.visualchars.core.Html'
],
function (Arr, Element, Node, Data, Html) {
var isMatch = function (n) {
return Node.isText(n) &&
Node.value(n) !== undefined &&
Data.regExp.test(Node.value(n));
};
// inlined sugars PredicateFilter.descendants for file size
var filterDescendants = function (scope, predicate) {
var result = [];
var dom = scope.dom();
var children = Arr.map(dom.childNodes, Element.fromDom);
Arr.each(children, function (x) {
if (predicate(x)) {
result = result.concat([ x ]);
}
result = result.concat(filterDescendants(x, predicate));
});
return result;
};
var findParentElm = function (elm, rootElm) {
while (elm.parentNode) {
if (elm.parentNode === rootElm) {
return elm;
}
elm = elm.parentNode;
}
};
var replaceWithSpans = function (html) {
return html.replace(Data.regExpGlobal, Html.wrapCharWithSpan);
};
return {
isMatch: isMatch,
filterDescendants: filterDescendants,
findParentElm: findParentElm,
replaceWithSpans: replaceWithSpans
};
}
);
/**
* Plugin.js
*
* Released under LGPL License.
* Copyright (c) 1999-2017 Ephox Corp. All rights reserved
*
* License: http://www.tinymce.com/license
* Contributing: http://www.tinymce.com/contributing
*/
define(
'tinymce.plugins.visualchars.core.VisualChars',
[
'tinymce.plugins.visualchars.core.Data',
'tinymce.plugins.visualchars.core.Nodes',
'ephox.katamari.api.Arr',
'ephox.sugar.api.node.Element',
'ephox.sugar.api.node.Node'
],
function (Data, Nodes, Arr, Element, Node) {
var show = function (editor, rootElm) {
var node, div;
var nodeList = Nodes.filterDescendants(Element.fromDom(rootElm), Nodes.isMatch);
Arr.each(nodeList, function (n) {
var withSpans = Nodes.replaceWithSpans(Node.value(n));
div = editor.dom.create('div', null, withSpans);
while ((node = div.lastChild)) {
editor.dom.insertAfter(node, n.dom());
}
editor.dom.remove(n.dom());
});
};
var hide = function (editor, body) {
var nodeList = editor.dom.select(Data.selector, body);
Arr.each(nodeList, function (node) {
editor.dom.remove(node, 1);
});
};
var toggle = function (editor) {
var body = editor.getBody();
var bookmark = editor.selection.getBookmark();
var parentNode = Nodes.findParentElm(editor.selection.getNode(), body);
// if user does select all the parentNode will be undefined
parentNode = parentNode !== undefined ? parentNode : body;
hide(editor, parentNode);
show(editor, parentNode);
editor.selection.moveToBookmark(bookmark);
};
return {
show: show,
hide: hide,
toggle: toggle
};
}
);
/**
* Plugin.js
*
* Released under LGPL License.
* Copyright (c) 1999-2017 Ephox Corp. All rights reserved
*
* License: http://www.tinymce.com/license
* Contributing: http://www.tinymce.com/contributing
*/
define(
'tinymce.plugins.visualchars.Plugin',
[
'tinymce.core.PluginManager',
'tinymce.core.util.Delay',
'ephox.katamari.api.Arr',
'ephox.sugar.api.node.Element',
'tinymce.plugins.visualchars.core.VisualChars'
],
function (PluginManager, Delay, Arr, Element, VisualChars) {
PluginManager.add('visualchars', function (editor) {
var self = this, state;
var toggleActiveState = function () {
var self = this;
editor.on('VisualChars', function (e) {
self.active(e.state);
});
};
var debouncedToggle = Delay.debounce(function () {
VisualChars.toggle(editor);
}, 300);
if (editor.settings.forced_root_block !== false) {
editor.on('keydown', function (e) {
if (self.state === true) {
e.keyCode === 13 ? VisualChars.toggle(editor) : debouncedToggle();
}
});
}
editor.addCommand('mceVisualChars', function () {
var body = editor.getBody(), selection = editor.selection, bookmark;
state = !state;
self.state = state;
editor.fire('VisualChars', { state: state });
bookmark = selection.getBookmark();
if (state === true) {
VisualChars.show(editor, body);
} else {
VisualChars.hide(editor, body);
}
selection.moveToBookmark(bookmark);
});
editor.addButton('visualchars', {
title: 'Show invisible characters',
cmd: 'mceVisualChars',
onPostRender: toggleActiveState
});
editor.addMenuItem('visualchars', {
text: 'Show invisible characters',
cmd: 'mceVisualChars',
onPostRender: toggleActiveState,
selectable: true,
context: 'view',
prependToContext: true
});
});
return function () {};
}
);
dem('tinymce.plugins.visualchars.Plugin')();
})();