Skip to content

Instantly share code, notes, and snippets.

@Pathologic
Created July 10, 2018 15:42
Show Gist options
  • Save Pathologic/e29ea9fffacf25f63ac4f12f3023b03b to your computer and use it in GitHub Desktop.
Save Pathologic/e29ea9fffacf25f63ac4f12f3023b03b to your computer and use it in GitHub Desktop.
update of tinymce modxlink plugin
/**
* modxlink.js
*
* Based on link.js but with resource search added.
*
* By default both pagetitle and alias are searched.
*
* Author: markwillis82
* Date: 7/7/15
* update 64j 8/11/17
*/
// Handles selection from Modx-Ressource-Tree
if (parent.modx.tree) {
var modxOldRessourceId = parent.modx.tree.itemToChange;
var modxLinkTitle = '';
var checkModxTreeUpdateInterval = undefined;
var checkModxTreeUpdate = function() {
checkModxTreeUpdateInterval = setTimeout(checkModxTreeUpdate, 100);
if (parent.modx.tree.itemToChange != modxOldRessourceId) {
modxOldRessourceId = parent.modx.tree.itemToChange;
modxLinkTitle = parent.modx.tree.selectedObjectName;
document.getElementById('link-href-inp').value = '[~' + modxOldRessourceId + '~]';
if (!document.getElementById('text-to-display').value) {
document.getElementById('text-to-display').value = modxLinkTitle;
}
}
};
}
/*global tinymce:true */
var autoComplt = (function() {
/* Properties:
[ Private ]
<OBJ> _CONST = the constants collection
<OBJ> _ui = the obj to build UI
<CLS> _AutoCompltList = the class dealing with the autocomplete operations
[ Public ]
> Refer to the Public APIs above
Methods:
[ Private ]
> _getIEVersion : Get the IE version
> _getAppropriateMode : Get the mode appropriate for the current user scenario
> _getWindowSize : Get the client window width and height
> _normalizeEvt : Normalize the event obj
> _addEvt : Add an event to one elem, used for cross-browser mitigation
> _rmEvent : remove an event to one elem, used for cross-browser mitigation
> _getComputedStyle : Get the computed style value, used for cross-browser mitigation
[ Public ]
> enable : Refer to the Public APIs above
*/
'use strict';
var _DBG = 0; // A little debug flag
if (!Array.prototype.indexOf) {
Array.prototype.indexOf = function(searchElement, fromIndex) {
if (this === undefined || this === null) {throw new TypeError('"this" is null or not defined');}
var length = this.length >>> 0;
fromIndex = +fromIndex || 0;
if (Math.abs(fromIndex) === Infinity) {fromIndex = 0;}
if (fromIndex < 0) {
fromIndex += length;
if (fromIndex < 0) {fromIndex = 0;}
}
for (; fromIndex < length; fromIndex++) {if (this[fromIndex] === searchElement) {return fromIndex;}}
return -1;
};
}
var
/* Return:
@ Is IE: <NUM> the version of IE
@ Not IE: NaN
*/
_getIEVersion = function() {
var rv = -1; // Return value assumes failure.
if (navigator.appName == 'Microsoft Internet Explorer') {
var ua = navigator.userAgent;
var re = new RegExp('MSIE ([0-9]{1,}[\.0-9]{0,})');
if (re.exec(ua) != null) {
rv = +(RegExp.$1);
}
}
return (rv === -1) ? NaN : rv;
},
/* Return:
> _CONST.modePC or _CONST.modeMobile
*/
_getAppropriateMode = function() {
// Judge by the userAgent string
var ua = window.navigator.userAgent.toLowerCase();
if (ua.search(/mobile|windows phone/) >= 0) return _CONST.modeMobile;
// Bye the legacy IEs
if (_getIEVersion() <= 9) return _CONST.modePC;
// Judge by the window width
return (_getWindowSize().windowWidth > _CONST.modeMobileW) ? _CONST.modePC : _CONST.modeMobile;
},
/* Return: {
windowWidth : the width of the client window in px. If unable to find, then -1.
windowHeight : the height of the client window in px. If unable to find, then -1.
}
*/
_getWindowSize = function() {
if (window.innerWidth) {
return {
windowWidth: window.innerWidth,
windowHeight: window.innerHeight
};
} else if (document.documentElement.offsetHeight) {
return {
windowWidth: document.documentElement.offsetWidth,
windowHeight: document.documentElement.offsetHeight
};
} else if (document.body.offsetHeight) {
return {
windowWidth: document.body.offsetWidth,
windowHeight: document.body.offsetHeight
};
} else if (document.documentElement.clientHeight) {
return {
windowWidth: document.documentElement.clientWidth,
windowHeight: document.documentElement.clientHeight
};
} else if (document.body.clientHeight) {
return {
windowWidth: document.body.clientWidth,
windowHeight: document.body.clientHeight
};
}
return {
windowWidth: -1,
windowHeight: -1
};
},
_CONST = (function(c) {
_CONST = c;
c.modePC = 'modePC'; // Represent the PC mode
c.modeMobile = 'modeMobile'; // Represent the mobile mode
c.modeMobileW = 768; // in px. The width used to seperate the PC & mobile mode
c.autoCompltListClass = 'autoComplt-list';
c.autoCompltHintClass = 'autoComplt-hint';
c.autoCompltHintSelectedClass = 'autoComplt-hint-selected';
c.maxHintNum = (_getAppropriateMode() === _CONST.modePC) ? 10 : 5; // For limited mobile screen, not too many
c.autoCompltDelay = 250; // in ms
c.hiddenArg_close_list_n_make_final_selection = 'hiddenArg_close_list_n_make_final_selection';
c.listStatus = {
attr: 'data-listStatus',
open: 'open'
};
c.keyCode = {
up: 38,
down: 40,
esc: 27,
enter: 13
};
c.defaultStyles = {
autoCompltList: {
maxHeight: 'none',
border: '1px solid #aaa',
padding: '0',
margin: '0',
zIndex: 99999,
overflowX: 'hidden',
overflowY: 'auto',
display: 'none',
position: 'absolute',
backgroundColor: '#fff'
},
autoCompltHint: {
height: '1.5em',
padding: (_getAppropriateMode() === _CONST.modePC) ? '2px 6px 2px 10px' : '6px 6px 6px 10px', // For good touch ux, enlarge for the mobile mode
margin: '6px 0',
overflow: 'hidden',
listStyleType: 'none',
color: '#000',
backgroundColor: '#fff',
cursor: 'default',
fontSize: '1em'
},
autoCompltHintSelected: {
color: '#fff',
backgroundColor: '#3399ff'
}
};
c.adjStyleAttrs = {
autoCompltList: ['border', 'maxHeight', 'backgroundColor'],
autoCompltHint: ['height', 'padding', 'margin', 'color', 'backgroundColor', 'fontSize'],
autoCompltHintSelected: ['color', 'backgroundColor']
};
// names of listeners supported
c.listenersSupported = [
'select' // Called when the user's final hint selection is decided
];
return _CONST;
})({}),
/* Arg:
<OBJ> e = the event obj
Return:
<OBJ> the normalized event obj
*/
_normalizeEvt = function(e) {
if (!e) e = window.event;
if (!e.target) e.target = e.srcElement;
e.stopBubble = function() {
this.cancelBubble = true;
if (this.stopPropagation) { this.stopPropagation(); }
};
e.stopDefault = function() {
if (this.preventDefault) { this.preventDefault(); }
this.returnValue = false;
return false;
};
return e;
},
/* Arg:
<ELM> elem = the DOM elem
<STR> evt = the event name
<FN> eHandle = the event handle
*/
_addEvt = function(elem, evt, eHandle) {
if (elem.addEventListener) {
elem.addEventListener(evt, eHandle);
} else if (elem.attachEvent) { // The IE 8 case
elem.attachEvent('on' + evt, eHandle);
}
},
/* Arg: Refer to _addEvt
*/
_rmEvent = function(elem, evt, eHandle) {
if (elem.removeEventListener) {
elem.removeEventListener(evt, eHandle);
} else if (elem.detachEvent) { // The IE 8 case
elem.detachEvent('on' + evt, eHandle);
}
},
/* Arg:
<ELM> elem = the DOM elem
<STR> name = the style name
*/
_getComputedStyle = function(elem, name) {
var v = null;
if (window.getComputedStyle) {
v = window.getComputedStyle(elem)[name] || null;
} else if (elem.currentStyle) { // Hack for IE...Reference from the jQuery
v = elem.currentStyle && elem.currentStyle[name];
var left,
rsLeft,
style = elem.style;
// Avoid setting v to empty string here
// so we don't default to auto
if (v == null && style && style[name]) {
v = style[name];
}
// From the awesome hack by Dean Edwards
// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
// If we're not dealing with a regular pixel number
// but a number that has a weird ending, we need to convert it to pixels
// but not position css attributes, as those are proportional to the parent element instead
// and we can't measure the parent instead because it might trigger a "stacking dolls" problem
// Remember the original values
left = style.left;
rsLeft = elem.runtimeStyle && elem.runtimeStyle.left;
// Put in the new values to get a computed value out
if (rsLeft) {
elem.runtimeStyle.left = elem.currentStyle.left;
}
style.left = name === 'fontSize' ? '1em' : v;
v = style.pixelLeft + 'px';
// Revert the changed values
style.left = left;
if (rsLeft) {
elem.runtimeStyle.left = rsLeft;
}
}
return v;
},
/* Methods:
[ Public ]
> buildElem : Build on elem from the HTML text
> buildHint : Build one autocomplete hint elem
> buildList : Build one autocomplete list
*/
_ui = {
/* Arg:
<STR> html = the HTMl text
Return:
<ELM> the elem built from the input HTML
*/
buildElem: function(html) {
var div = document.createElement('DIV');
div.innerHTML = html;
return div.firstChild.cloneNode(true);
},
/* Arg:
<STR> hint = the hint text
<OBJ> styles = the obk holding the styles to set. Refer to _CONST.defaultStyles.autoCompltHint for the required styles
Return:
@ OK: <ELM> the hint ui elem
@ NG: null
*/
buildHint: function(hint, styles) {
if (typeof hint == 'string' && hint) {
hint = this.buildElem('<li class="' + _CONST.autoCompltHintClass + '">' + hint + '</li>');
hint.style.height = hint.style.lineHeight = styles.autoCompltHint.height; // line-height shall always be equal to the height
hint.style.padding = styles.autoCompltHint.padding;
hint.style.margin = styles.autoCompltHint.margin;
hint.style.overflow = styles.autoCompltHint.overflow;
hint.style.listStyleType = styles.autoCompltHint.listStyleType;
hint.style.color = styles.autoCompltHint.color;
hint.style.backgroundColor = styles.autoCompltHint.backgroundColor;
hint.style.cursor = styles.autoCompltHint.cursor;
hint.style.fontSize = styles.autoCompltHint.fontSize;
return hint;
}
return null;
},
/* Arg:
<OBJ> styles = the obk holding the styles to set. Refer to _CONST.defaultStyles.autoCompltList for the required styles
Return:
@ OK: <ELM> the list ui elem
@ NG: null
*/
buildList: function(styles) {
var list = this.buildElem('<ul class="' + _CONST.autoCompltListClass + '"></ul>');
list.style.maxHeight = styles.autoCompltList.maxHeight;
list.style.border = styles.autoCompltList.border;
list.style.padding = styles.autoCompltList.padding;
list.style.margin = styles.autoCompltList.margin;
list.style.zIndex = styles.autoCompltList.zIndex;
list.style.overflowX = styles.autoCompltList.overflowX;
list.style.overflowY = styles.autoCompltList.overflowY;
list.style.display = styles.autoCompltList.display;
list.style.position = styles.autoCompltList.position;
list.style.backgroundColor = styles.autoCompltList.backgroundColor;
return list;
}
},
/* Properties:
[ Public ]
<ELM> uiElem = the autocomplete list current being displayed and associated with.
<ELM> assocInput = the input elem associated with
<BOO> mouseOnList = A little flag marking the moused is on the top of the autocomplete list
<NUM> maxHintNum = The max number of hints displayed
<OBJ> styles = the obj holding the style setting of the list and hints. Refer to _CONST.defaultStyles for the required styles.
<FN> onMouseSelectionListener = Called when user explicitly selects one hint by mouse clicking. No args passed.
Methods:
[ Public ]
> genList : Build and setup one autocomplete list
> isHint : Check if it is a autocomplete hint elem or not
> putHints : Put hints into the autocomplete list
> clearHints : Clear all hints
> isOpen : Tell if the auotcomplete list is open or not
> open : Open the autocomplete list. NOTICE: before opening, there must at one hint in the list so please call this.putHints first then open.
> close : Close the autocomplete list
> autoScroll : Auto scroll the list according the position and offset of the current selected hint so the current selected hint could show up
> pick : Pick up one hint. NOTICE: this action is to pick up one hint but not to select that hint so it will not change this.assocInput's value. Please use this.getPicked to get the picked hint and extract the hint text and assign this.assocInput's value the hint text
> unpick : Unpick all hints
> getPicked : Get the hint elem picked
*/
_AutoCompltList = function(assocInput) {
this.uiElem = null;
this.assocInput = assocInput;
this.mouseOnList = false;
this.onMouseSelectionListener = null;
this.maxHintNum = _CONST.maxHintNum;
this.styles = JSON.parse(JSON.stringify(_CONST.defaultStyles)); // Copy the default first
};
{
/*
*/
_AutoCompltList.prototype.genList = function() {
if (!this.uiElem) {
var that = this;
this.uiElem = _ui.buildList(this.styles);
// Make hint selected onmouseover
_addEvt(this.uiElem, 'mouseover', function(e) {
e = _normalizeEvt(e);
if (that.isHint(e.target)) {
that.pick(e.target);
that.autoScroll();
}
});
// Make hint not selected onmouseout
_addEvt(this.uiElem, 'mouseout', function(e) {
that.unpick();
});
// Prepare for the hint selection by clicking
_addEvt(this.uiElem, 'mousedown', function(e) {
that.mouseOnList = true;
// One hack for FF.
// Even call focus methos on the input's onblur event, however, still the input losese its focus.
// As a result we have to set a timeout here
setTimeout(function() {
that.assocInput.focus();
}, 50);
});
// pick hint by clicking
_addEvt(this.uiElem, 'mouseup', function(e) {
e = _normalizeEvt(e);
if (that.isHint(e.target)) {
that.pick(e.target);
if (typeof that.onMouseSelectionListener == 'function') that.onMouseSelectionListener();
}
});
document.body.appendChild(this.uiElem);
}
};
/* Arg:
<ELM> el = the elem to check
Return:
@ Ok: true
@ NG: false
*/
_AutoCompltList.prototype.isHint = function(el) {
if (el && typeof el == 'object' && el.nodeType === 1) {
var cls = ' ' + el.className + ' ';
return (cls.indexOf(' ' + _CONST.autoCompltHintClass + ' ') >= 0);
}
return false;
};
/* Arg:
<ARR> hints = the array of hint texts
Return:
<NUM> the number of hints put
*/
_AutoCompltList.prototype.putHints = function(hints) {
var count = 0;
if (hints instanceof Array) {
var i,
j,
hs = [];
j = Math.min(hints.length, this.maxHintNum);
for (i = 0; i < j; i++) {
hs.push(_ui.buildHint(hints[i], this.styles));
if (!hs[hs.length - 1]) {
hs.pop();
}
}
if (hs.length > 0) {
var buf = document.createDocumentFragment();
for (i = 0, count = hs.length; i < count; i++) {
buf.appendChild(hs[i]);
}
this.clearHints();
this.genList(); // Geneate the list in case there is none
this.uiElem.appendChild(buf);
}
}
return count;
};
/*
*/
_AutoCompltList.prototype.clearHints = function() {
if (this.uiElem) {
this.uiElem.innerHTML = '';
}
};
/*
Return:
@ Ok: true
@ NG: false
*/
_AutoCompltList.prototype.isOpen = function() {
if (this.uiElem) {
return (this.uiElem.getAttribute(_CONST.listStatus.attr) == _CONST.listStatus.open);
}
return false;
};
/*
*/
_AutoCompltList.prototype.open = function() {
var hints;
if (this.uiElem
&& (hints = this.uiElem.querySelectorAll('.' + _CONST.autoCompltHintClass))
&& hints.length // At lease one hint exists, we would open...
) {
var i,
buf;
// Position the list
buf = this.assocInput.getBoundingClientRect();
this.uiElem.style.top = (document.documentElement && document.documentElement.scrollTop ? document.documentElement.scrollTop : document.body.scrollTop)
+ buf.bottom + 'px';
this.uiElem.style.left = buf.left + 'px';
// Calculate the list's width
buf = buf.right - buf.left - parseFloat(_getComputedStyle(this.uiElem, 'borderLeftWidth')) - parseFloat(_getComputedStyle(this.uiElem, 'borderRightWidth'));
this.uiElem.style.width = buf + 'px';
// Calculate the list's height
for (i = 0, buf = 0; i < hints.length; i++) {
buf += parseFloat(_getComputedStyle(hints[i], 'height'))
+ parseFloat(_getComputedStyle(hints[i], 'paddingTop'))
+ parseFloat(_getComputedStyle(hints[i], 'paddingBottom'));
if (hints[i + 1]) { // Compute the margin between the hints
buf += Math.max(
parseFloat(_getComputedStyle(hints[i], 'marginBottom')), parseFloat(_getComputedStyle(hints[i + 1], 'marginTop'))
);
}
}
buf += parseFloat(_getComputedStyle(hints[0], 'marginTop'))
+ parseFloat(_getComputedStyle(hints[hints.length - 1], 'marginBottom'));
this.uiElem.style.height = (buf + 1) + 'px'; // Plus one for a little buffer
// Open
this.uiElem.setAttribute(_CONST.listStatus.attr, _CONST.listStatus.open);
this.uiElem.style.display = 'block';
}
};
/*
*/
_AutoCompltList.prototype.close = function() {
if (this.uiElem) {
this.mouseOnList = false;
this.uiElem.parentNode.removeChild(this.uiElem);
this.uiElem = null;
}
};
/*
*/
_AutoCompltList.prototype.autoScroll = function() {
var hint = this.getPicked();
if (hint) {
var currHint,
offset = 0,
minDisplayH = 0,
hintH = hint.clientHeight,
hintMT = parseFloat(_getComputedStyle(hint, 'marginTop')),
hintMB = parseFloat(_getComputedStyle(hint, 'marginBottom'));
currHint = hint.previousSibling;
minDisplayH = hintH + (currHint ? Math.max(hintMT, hintMB) : hintMT); // The min height to display one hint
while (currHint) {
offset += hintH; // Add the current hint' hintH
currHint = currHint.previousSibling;
if (currHint) {
// There is one hint before the current hint so calculate based on the collapsed model
offset += Math.max(hintMT, hintMB);
} else {
// No more previous hint, this is the 1st hint so just take the marign top
offset += hintMT;
}
}
if (this.uiElem.clientHeight + this.uiElem.scrollTop - offset < minDisplayH
|| offset - this.uiElem.scrollTop < minDisplayH
) {
// Ther is no enough room displaying the current selected hint so adjust the scroll
this.uiElem.scrollTop = offset;
}
}
};
/* Arg:
<ELM|NUM> candidate = could be
1) the hint elem or
2) the index of the hint in the list. Passing in -1 would pick the last hint. Passing in 0 would pick the 1st hint.
*/
_AutoCompltList.prototype.pick = function(candidate) {
if (this.uiElem) {
var hint = null;
if (this.isHint(candidate)) {
hint = candidate;
} else if (typeof candidate == 'number' && (candidate >= 0 || candidate === -1)) {
var hints = this.uiElem.querySelectorAll('.' + _CONST.autoCompltHintClass);
if (hints.length > 0) {
hint = +candidate;
hint = (hint === -1 || hint > hints.length - 1) ? hints.length - 1 : hint;
hint = hints[hint];
}
}
if (hint !== null) {
this.unpick();
hint.className += ' ' + _CONST.autoCompltHintSelectedClass;
hint.style.color = this.styles.autoCompltHintSelected.color;
hint.style.backgroundColor = this.styles.autoCompltHintSelected.backgroundColor;
}
}
};
/*
*/
_AutoCompltList.prototype.unpick = function() {
if (this.uiElem) {
var slct = this.getPicked();
if (slct) {
slct.className = _CONST.autoCompltHintClass;
slct.style.color = this.styles.autoCompltHint.color;
slct.style.backgroundColor = this.styles.autoCompltHint.backgroundColor;
}
}
};
/* Return:
@ OK: <ELM> the selected hint element
@ NG: null
*/
_AutoCompltList.prototype.getPicked = function() {
return !this.uiElem ? null : this.uiElem.querySelector('.' + _CONST.autoCompltHintSelectedClass) || null;
};
}
var
publicProps = {
enable: function(input, params) {
if (input
&& typeof input == 'object'
&& typeof input.tagName == 'string'
&& input.tagName.toLowerCase() == 'input'
&& input.type == 'text'
&& input.nodeType === 1
&& !input.autoComplt
) {
/* Propertise:
[ Private ]
<NUM> input_autoComplt_delay = the ms delays the work of fetching the autocomplete hints based on the user's input
<BOO> input_autoComplt_enabled = true to perform the autocomplete function; false not to perform.
<STR> input_autoComplt_currentTarget = the current user's input for which the autocomplete is target
<OBJ> input_autoComplt_listenerMap = the map of listeners
<OBJ> input_autoComplt_list = the instance of _AutoCompltList
Methods:
[ Private ]
> input_autoComplt_hintsFetcher : The function fetching the autocomplete hints
> input_autoComplt_startFetcher : Setup and call input_autoComplt_hintsFetcher to fetch the hints
> input_autoComplt_compltInput : Autocomplete the <input> according to the hint selection state
> input_autoComplt_blurEvtHandle, input_autoComplt_keyEvtHandle : The event handle
> input_autoComplt_inputEvtHandleMobile : The event handle for the mobile mode
[ Public ]
> setHintsFetcher, setListener, config, setStyles, close, enable, disable, destroy : Refe to the Public APIs above
*/
input.autoComplt = {};
var
input_autoComplt_delay = _CONST.autoCompltDelay,
input_autoComplt_enabled = true,
input_autoComplt_currentTarget = '',
input_autoComplt_hintsFetcher = null,
input_autoComplt_listenerMap = null,
input_autoComplt_list = new _AutoCompltList(input),
/*
*/
input_autoComplt_startFetcher = function() {
if (input.value.length > 0
&& input_autoComplt_enabled
&& typeof input_autoComplt_hintsFetcher == 'function'
&& input_autoComplt_currentTarget !== input.value // If equals, it means we've already been searching for the hints for the same value
) {
var fetcherCaller = {
that: input,
// Record the autocomplete target for this fetching job
compltTarget: (input_autoComplt_currentTarget = input.value),
compltTargetMatchCurrentTarget: function() {
// If the user's input has changed during the fetching, this fetching job is useless.
// So only when the user's input doesn't change, we will return true to indicate proceeding further.
return (fetcherCaller.compltTarget === input_autoComplt_currentTarget);
},
call: function() {
if (fetcherCaller.compltTargetMatchCurrentTarget()) {
input_autoComplt_hintsFetcher.call(
fetcherCaller.that,
fetcherCaller.compltTarget,
fetcherCaller.openHint
);
}
},
openHint: function(hints) {
if (fetcherCaller.compltTargetMatchCurrentTarget()) {
if (input_autoComplt_list.putHints(hints)) {
input_autoComplt_list.open();
} else {
fetcherCaller.that.autoComplt.close();
}
}
}
};
setTimeout(fetcherCaller.call, input_autoComplt_delay);
}
},
/*
*/
input_autoComplt_compltInput = function() {
if (input_autoComplt_enabled) {
var hint = input_autoComplt_list.getPicked();
if (hint) {
input.value = hint.innerHTML;
} else {
// If no hint is selected, just use the original user input to autocomplete
input.value = input_autoComplt_currentTarget;
}
}
},
/*
*/
input_autoComplt_blurEvtHandle = function(e) {
if (input_autoComplt_list.mouseOnList) {
// If the mouse is on the autocomplete list, do not close the list
// and still need to focus on the input.
input.focus();
input_autoComplt_list.mouseOnList = false; // Assign false for the next detection
} else {
if (input_autoComplt_list.isOpen()) {
input.autoComplt.close(_CONST.hiddenArg_close_list_n_make_final_selection);
}
}
},
/*
*/
input_autoComplt_keyEvtHandle = function(e) {
if (_getAppropriateMode() === _CONST.modeMobile) return; // Let this::input_autoComplt_inputEvtHandleMobile handle
e = _normalizeEvt(e);
if (input_autoComplt_enabled) {
if (e.type == 'keydown'
&& input_autoComplt_list.isOpen()
&& (e.keyCode === _CONST.keyCode.up || e.keyCode === _CONST.keyCode.down)
) {
// At the case that the hint list is open ans user is walking thru the hints.
// Let's try to autocomplete the input by the selected input.
var hint = input_autoComplt_list.getPicked();
if (e.keyCode === _CONST.keyCode.up) {
if (!hint) {
// If none is selected, then pick the last hint
input_autoComplt_list.pick(-1);
} else if (hint.previousSibling) {
// If some hint is selected and the previous hint exists, then pick the previous hint
input_autoComplt_list.pick(hint.previousSibling);
} else {
// If some hint is selected but the previous hint doesn't exists, then unpick all
input_autoComplt_list.unpick();
}
} else if (e.keyCode === _CONST.keyCode.down) {
if (!hint) {
// If none is selected, then pick the first hint
input_autoComplt_list.pick(0);
} else if (hint.nextSibling) {
// If some hint is selected and the next hint exists, then pick the next hint
input_autoComplt_list.pick(hint.nextSibling);
} else {
// If some hint is selected but the next hint doesn't exists, then unpick all
input_autoComplt_list.unpick();
}
}
input_autoComplt_list.autoScroll();
input_autoComplt_compltInput();
}
else if (e.type == 'keyup') {
var startFetching = false;
switch (e.keyCode) {
case _CONST.keyCode.up:
case _CONST.keyCode.down:
if (input_autoComplt_list.isOpen()) {
// We have handled this 2 key codes onkeydown, so must do nothing here
} else {
startFetching = true;
}
break;
case _CONST.keyCode.esc:
if (input_autoComplt_list.isOpen()) {
// When pressing the ESC key, let's resume back to the original user input
input.value = input_autoComplt_currentTarget;
input.autoComplt.close(_CONST.hiddenArg_close_list_n_make_final_selection);
}
break;
case _CONST.keyCode.enter:
if (input_autoComplt_list.isOpen()) {
// When pressing the enter key, let's try autocomplete
input_autoComplt_compltInput();
input.autoComplt.close(_CONST.hiddenArg_close_list_n_make_final_selection);
}
break;
default:
startFetching = true;
break;
}
if (startFetching) {
if (input.value.length > 0) {
input_autoComplt_startFetcher();
} else {
input.autoComplt.close();
}
}
}
}
},
/*
*/
input_autoComplt_inputEvtHandleMobile = function(e) {
if (_getAppropriateMode() === _CONST.modePC) return; // Let this::input_autoComplt_keyEvtHandle handle
if (input.value.length > 0) {
input_autoComplt_startFetcher();
} else {
input.autoComplt.close();
}
},
/* Arg:
<STR> name = Refer to _CONST.listenersSupported
*/
input_autoComplt_invokeListener = function(name) {
if (input_autoComplt_listenerMap != null && typeof input_autoComplt_listenerMap[name] == 'function') {
input_autoComplt_listenerMap[name].call(input);
}
};
input.autoComplt.setHintsFetcher = function(hintsFetcher) {
if (hintsFetcher === null || typeof hintsFetcher == 'function') {
input_autoComplt_hintsFetcher = hintsFetcher;
return true;
}
return false;
};
input.autoComplt.setListener = function(name, listener) {
if ((listener === null || typeof listener == 'function') && _CONST.listenersSupported.indexOf(name) >= 0) {
if (input_autoComplt_listenerMap == null) input_autoComplt_listenerMap = {};
input_autoComplt_listenerMap[name] = listener;
return true;
}
return false;
};
input.autoComplt.setStyles = function(targetClass, styles) {
var tStyles,
adjStyleAttrs,
newStyles = false;
// Let's find out which the target UI part is being set
switch (targetClass) {
case _CONST.autoCompltListClass:
tStyles = input_autoComplt_list.styles.autoCompltList;
adjStyleAttrs = _CONST.adjStyleAttrs.autoCompltList;
break;
case _CONST.autoCompltHintClass:
tStyles = input_autoComplt_list.styles.autoCompltHint;
adjStyleAttrs = _CONST.adjStyleAttrs.autoCompltHint;
break;
case _CONST.autoCompltHintSelectedClass:
tStyles = input_autoComplt_list.styles.autoCompltHintSelected;
adjStyleAttrs = _CONST.adjStyleAttrs.autoCompltHintSelected;
break;
}
if (styles instanceof Object && tStyles && adjStyleAttrs) {
for (var i = 0; i < adjStyleAttrs.length; i++) {
if (typeof styles[adjStyleAttrs[i]] == 'string' || typeof styles[adjStyleAttrs[i]] == 'number') { // A simple type checking
if (!newStyles) {
newStyles = {};
}
newStyles[adjStyleAttrs[i]] = tStyles[adjStyleAttrs[i]] = styles[adjStyleAttrs[i]];
}
}
}
return newStyles;
};
input.autoComplt.config = function(params) {
var pms = false;
if (params instanceof Object) {
var buf;
// Config the fetching delay timing
//
if (params.delay !== undefined && (buf = Math.floor(params.delay)) > 0) {
if (!pms) pms = {};
input_autoComplt_delay = pms.delay = buf;
}
// Config the max number of displayed hints
//
if (params.maxHintNum !== undefined && (buf = Math.floor(params.maxHintNum)) > 0) {
if (!pms) pms = {};
input_autoComplt_list.maxHintNum = pms.maxHintNum = buf;
}
}
return pms;
};
input.autoComplt.close = function() {
input_autoComplt_currentTarget = ''; // Closing means no need for autocomplete hint so no autocomplete target either
input_autoComplt_list.close();
if (input_autoComplt_enabled
&& input.value !== ''
&& arguments[0] === _CONST.hiddenArg_close_list_n_make_final_selection
) {
input_autoComplt_invokeListener('select');
}
};
input.autoComplt.enable = function() {
input_autoComplt_enabled = true;
};
input.autoComplt.disable = function() {
input_autoComplt_enabled = false;
this.close();
};
input.autoComplt.destroy = function() {
_rmEvent(input, 'blur', input_autoComplt_blurEvtHandle);
_rmEvent(input, 'keyup', input_autoComplt_keyEvtHandle);
_rmEvent(input, 'keydown', input_autoComplt_keyEvtHandle);
this.disable();
delete input.autoComplt;
};
input_autoComplt_list.onMouseSelectionListener = function() {
input_autoComplt_compltInput();
input.autoComplt.close(_CONST.hiddenArg_close_list_n_make_final_selection);
};
_addEvt(input, 'blur', input_autoComplt_blurEvtHandle);
_addEvt(input, 'keyup', input_autoComplt_keyEvtHandle);
_addEvt(input, 'keydown', input_autoComplt_keyEvtHandle);
_addEvt(input, 'input', input_autoComplt_inputEvtHandleMobile);
if (params instanceof Object) {
input.autoComplt.config(params);
input.autoComplt.setHintsFetcher(params.hintsFetcher);
}
return input;
}
return null;
}
};
return publicProps;
}());
tinymce.PluginManager.add('modxlink', function(editor) {
function createLinkList(callback)
{
return function() {
var linkList = editor.settings.link_list;
if (typeof linkList == 'string') {
tinymce.util.XHR.send({
url: linkList,
success: function(text) {
callback(tinymce.util.JSON.parse(text));
}
});
} else if (typeof linkList == 'function') {
linkList(callback);
} else {
callback(linkList);
}
};
}
function buildListItems(inputList, itemCallback, startItems)
{
function appendItems(values, output)
{
output = output || [];
tinymce.each(values, function(item) {
var menuItem = {text: item.text || item.title};
if (item.menu) {
menuItem.menu = appendItems(item.menu);
} else {
menuItem.value = item.value;
if (itemCallback) {
itemCallback(menuItem);
}
}
output.push(menuItem);
});
return output;
}
return appendItems(inputList, startItems || []);
}
function showDialog(linkList)
{
if (parent.tree) {
parent.tree.ca = 'disabled'; // Disable Modx-TreeAction, resetted on onOk or OnCancel
checkModxTreeUpdate.call(this); // Watch Modx-Tree for changes and update URL-field
}
;
var data = {}, selection = editor.selection, dom = editor.dom, selectedElm, anchorElm, initialText;
var win, onlyText, textListCtrl, linkListCtrl, relListCtrl, targetListCtrl, classListCtrl, linkTitleCtrl, value;
function linkListChangeHandler(e)
{
var textCtrl = win.find('#text');
if (!textCtrl.value() || (e.lastControl && textCtrl.value() == e.lastControl.text())) {
textCtrl.value(e.control.text());
}
win.find('#href').value(e.control.value());
}
function buildAnchorListControl(url)
{
var anchorList = [];
tinymce.each(editor.dom.select('a:not([href])'), function(anchor) {
var id = anchor.name || anchor.id;
if (id) {
anchorList.push({
text: id,
value: '#' + id,
selected: url.indexOf('#' + id) != -1
});
}
});
if (anchorList.length) {
anchorList.unshift({text: 'None', value: ''});
return {
name: 'anchor',
type: 'listbox',
label: 'Anchors',
values: anchorList,
onselect: linkListChangeHandler
};
}
}
function updateText()
{
if (!initialText && data.text.length === 0 && onlyText) {
this.parent().parent().find('#text')[0].value(this.value());
}
}
function urlChange(e)
{
var meta = e.meta || {};
if (linkListCtrl) {
linkListCtrl.value(editor.convertURL(this.value(), 'href'));
}
tinymce.each(e.meta, function(value, key) {
win.find('#' + key).value(value);
});
if (!meta.text) {
updateText.call(this);
}
}
function isOnlyTextSelected(anchorElm)
{
var html = selection.getContent();
// Partial html and not a fully selected anchor element
if (/</.test(html) && (!/^<a [^>]+>[^<]+<\/a>$/.test(html) || html.indexOf('href=') == -1)) {
return false;
}
if (anchorElm) {
var nodes = anchorElm.childNodes, i;
if (nodes.length === 0) {
return false;
}
for (i = nodes.length - 1; i >= 0; i--) {
if (nodes[i].nodeType != 3) {
return false;
}
}
}
return true;
}
selectedElm = selection.getNode();
anchorElm = dom.getParent(selectedElm, 'a[href]');
onlyText = isOnlyTextSelected();
data.text = initialText = anchorElm ? (anchorElm.innerText || anchorElm.textContent) : selection.getContent({format: 'text'});
data.href = anchorElm ? dom.getAttrib(anchorElm, 'href') : '';
if (anchorElm) {
data.target = dom.getAttrib(anchorElm, 'target');
} else if (editor.settings.default_link_target) {
data.target = editor.settings.default_link_target;
}
if ((value = dom.getAttrib(anchorElm, 'rel'))) {
data.rel = value;
}
if ((value = dom.getAttrib(anchorElm, 'class'))) {
data['class'] = value;
}
if ((value = dom.getAttrib(anchorElm, 'title'))) {
data.title = value;
}
if (onlyText) {
textListCtrl = {
name: 'text',
type: 'textbox',
size: 40,
label: 'Text to display',
id: 'text-to-display',
onchange: function() {
data.text = this.value();
}
};
}
if (linkList) {
linkListCtrl = {
type: 'listbox',
label: 'Link list',
values: buildListItems(
linkList,
function(item) {
item.value = editor.convertURL(item.value || item.url, 'href');
},
[{text: 'None', value: ''}]
),
onselect: linkListChangeHandler,
value: editor.convertURL(data.href, 'href'),
onPostRender: function() {
linkListCtrl = this;
}
};
}
if (editor.settings.target_list !== false) {
if (!editor.settings.target_list) {
editor.settings.target_list = [
{text: 'None', value: ''},
{text: 'New window', value: '_blank'}
];
}
targetListCtrl = {
name: 'target',
type: 'listbox',
label: 'Target',
values: buildListItems(editor.settings.target_list)
};
}
if (editor.settings.rel_list) {
relListCtrl = {
name: 'rel',
type: 'listbox',
label: 'Rel',
values: buildListItems(editor.settings.rel_list)
};
}
if (editor.settings.link_class_list) {
classListCtrl = {
name: 'class',
type: 'listbox',
label: 'Class',
values: buildListItems(
editor.settings.link_class_list,
function(item) {
if (item.value) {
item.textStyle = function() {
return editor.formatter.getCssText({inline: 'a', classes: [item.value]});
};
}
}
)
};
}
if (editor.settings.link_title !== false) {
linkTitleCtrl = {
name: 'title',
type: 'textbox',
label: 'Title',
value: data.title
};
}
win = editor.windowManager.open({
title: 'Insert link',
data: data,
body: [
{
name: 'href',
type: 'FileImagePicker',
filetype: 'file',
size: 40,
autofocus: true,
label: 'Url',
id: 'link-href',
onchange: urlChange,
onkeyup: updateText
},
{
name: 'search',
type: 'textbox',
label: 'Search in EVO',
id: 'link-search'
},
textListCtrl,
linkTitleCtrl,
buildAnchorListControl(data.href),
linkListCtrl,
relListCtrl,
targetListCtrl,
classListCtrl
],
onSubmit: function(e) {
/*eslint dot-notation: 0*/
var href;
data = tinymce.extend(data, e.data);
href = data.href;
// Delay confirm since onSubmit will move focus
function delayedConfirm(message, callback)
{
var rng = editor.selection.getRng();
window.setTimeout(function() {
editor.windowManager.confirm(message, function(state) {
editor.selection.setRng(rng);
callback(state);
});
}, 0);
}
function insertLink()
{
// Reset Modx-TreeAction
if (parent.tree) {
parent.tree.ca = '';
clearTimeout(checkModxTreeUpdateInterval);
}
;
var linkAttrs = {
href: href,
target: data.target ? data.target : null,
rel: data.rel ? data.rel : null,
'class': data['class'] ? data['class'] : null,
title: data.title ? data.title : null
};
if (anchorElm) {
editor.focus();
if (onlyText && data.text != initialText) {
if ('innerText' in anchorElm) {
anchorElm.innerText = data.text;
} else {
anchorElm.textContent = data.text;
}
}
dom.setAttribs(anchorElm, linkAttrs);
selection.select(anchorElm);
editor.undoManager.add();
} else {
if (onlyText) {
editor.insertContent(dom.createHTML('a', linkAttrs, dom.encode(data.text)));
} else {
editor.execCommand('mceInsertLink', false, linkAttrs);
}
}
}
if (!href) {
editor.execCommand('unlink');
return;
}
// Is email and not //user@domain.com
if (href.indexOf('@') > 0 && href.indexOf('//') == -1 && href.indexOf('mailto:') == -1) {
delayedConfirm(
'The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?',
function(state) {
if (state) {
href = 'mailto:' + href;
}
insertLink();
}
);
return;
}
// Is not protocol prefixed
if ((editor.settings.link_assume_external_targets && !/^\w+:/i.test(href)) ||
(!editor.settings.link_assume_external_targets && /^\s*www\./i.test(href))) {
delayedConfirm(
'The URL you entered seems to be an external link. Do you want to add the required http:// prefix?',
function(state) {
if (state) {
href = 'http://' + href;
}
insertLink();
}
);
return;
}
insertLink();
},
onClose: function(e) {
// Reset Modx-TreeAction
if (parent.tree) {
parent.tree.ca = '';
clearTimeout(checkModxTreeUpdateInterval);
}
;
}
});
var input = document.querySelector('#link-search'), _resultDataset = {};
autoComplt.enable(input, {
// the hintsFetcher is your customized function which searchs the proper autocomplete hints based on the user's input value.
hintsFetcher: function(v, openList) {
_resultDataset = {};
var xhr = new XMLHttpRequest(), _link = encodeURI(tinymce4_baseUrl + 'tinymce/plugins/modxlink/search.php' + '?q=' + v);
xhr.open('GET', _link);
xhr.onload = function() {
if (xhr.status === 200) {
//alert(xhr.responseText);
var _results = JSON.parse(xhr.responseText), _e = [];
for (var i = 0; i < Object.keys(_results).length; i++) {
_resultDataset[_results[i].pagetitle] = _results[i];
_e.push(_results[i].pagetitle);
}
openList(_e);
} else {
console.error('ajax error', _link);
}
};
xhr.send();
}
});
input.autoComplt.setListener('select', function(e, r) {
var _a = document.querySelector('#link-href input'), _title = document.querySelector('#text-to-display');
_a.value = '[~' + _resultDataset[this.value].id + '~]';
if (_title.value == '') _title.value = _resultDataset[this.value].title;
});
}
editor.addButton('link', {
icon: 'link',
tooltip: 'Insert/edit link',
shortcut: 'Meta+K',
onclick: createLinkList(showDialog),
stateSelector: 'a[href]'
});
editor.addButton('unlink', {
icon: 'unlink',
tooltip: 'Remove link',
cmd: 'unlink',
stateSelector: 'a[href]'
});
editor.addShortcut('Meta+K', '', createLinkList(showDialog));
editor.addCommand('mceLink', createLinkList(showDialog));
this.showDialog = showDialog;
editor.addMenuItem('link', {
icon: 'link',
text: 'Insert/edit link',
shortcut: 'Meta+K',
onclick: createLinkList(showDialog),
stateSelector: 'a[href]',
context: 'insert',
prependToContext: true
});
tinymce.ui.Factory.add('FileImagePicker',tinymce.ui.ComboBox.extend({
init: function(settings) {
var self = this, editor = tinymce.activeEditor, editorSettings = editor.settings;
var actionCallback, fileBrowserCallback, fileBrowserCallbackTypes;
settings.spellcheck = false;
fileBrowserCallbackTypes = editorSettings.file_picker_types || editorSettings.file_browser_callback_types;
if (fileBrowserCallbackTypes) {
fileBrowserCallbackTypes = Tools.makeMap(fileBrowserCallbackTypes, /[, ]/);
}
if (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[settings.filetype]) {
fileBrowserCallback = editorSettings.file_picker_callback;
if (fileBrowserCallback && (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[settings.filetype])) {
actionCallback = function() {
var meta = self.fire('beforecall').meta;
meta = Tools.extend({filetype: settings.filetype}, meta);
// file_picker_callback(callback, currentValue, metaData)
fileBrowserCallback.call(
editor,
function(value, meta) {
self.value(value).fire('change', {meta: meta});
},
self.value(),
meta
);
};
} else {
// Legacy callback: file_picker_callback(id, currentValue, filetype, window)
fileBrowserCallback = editorSettings.file_browser_callback;
if (fileBrowserCallback && (!fileBrowserCallbackTypes || fileBrowserCallbackTypes[settings.filetype])) {
actionCallback = function() {
fileBrowserCallback(
self.getEl('inp').id,
self.value(),
self.settings.filetype,
window
);
};
}
}
}
if (actionCallback) {
settings.icon = 'browse';
settings.onaction = actionCallback;
}
self._super(settings);
self.off('click').on('click', function(e){
var elm = e.target, root = self.getEl();
if (!self.$.contains(root, elm) && elm != root) {
return;
}
while (elm && elm != root) {
if (elm.id && elm.id.indexOf('-action') != -1) {
if (elm.id === self._id + '-image-action') {
self.settings.filetype = 'image';
} else {
self.settings.filetype = 'file';
}
self.fire('action');
}
elm = elm.parentNode;
}
});
},
renderHtml: function() {
var self = this, id = self._id, settings = self.settings, prefix = self.classPrefix;
var value = self.state.get('value') || '';
var icon, text, openBtnHtml = '', extraAttrs = '';
if ("spellcheck" in settings) {
extraAttrs += ' spellcheck="' + settings.spellcheck + '"';
}
if (settings.maxLength) {
extraAttrs += ' maxlength="' + settings.maxLength + '"';
}
if (settings.size) {
extraAttrs += ' size="' + settings.size + '"';
}
if (settings.subtype) {
extraAttrs += ' type="' + settings.subtype + '"';
}
if (self.disabled()) {
extraAttrs += ' disabled="disabled"';
}
openBtnHtml = (
'<div id="' + id + '-open" class="' + prefix + 'btn ' + prefix + 'open" tabIndex="-1" role="button">' +
'<button id="' + id + '-action" type="button" hidefocus="1" tabindex="-1">' +
'<i class="mce-ico mce-i-browse"></i>' +
'</button>' +
'<button id="' + id + '-image-action" type="button" hidefocus="1" tabindex="-1">' +
'<i class="mce-ico mce-i-image"></i>' +
'</button>' +
'</div>'
);
self.classes.add('has-open');
return (
'<div id="' + id + '" class="' + self.classes + '">' +
'<input id="' + id + '-inp" class="' + prefix + 'textbox" value="' +
self.encode(value, false) + '" hidefocus="1"' + extraAttrs + ' placeholder="' +
self.encode(settings.placeholder) + '" />' +
openBtnHtml +
'</div>'
);
},
}));
});
if(parent.modx.tree)var modxOldRessourceId=parent.modx.tree.itemToChange,modxLinkTitle="",checkModxTreeUpdateInterval=void 0,checkModxTreeUpdate=function(){checkModxTreeUpdateInterval=setTimeout(checkModxTreeUpdate,100),parent.modx.tree.itemToChange!=modxOldRessourceId&&(modxOldRessourceId=parent.modx.tree.itemToChange,modxLinkTitle=parent.modx.tree.selectedObjectName,document.getElementById("link-href-inp").value="[~"+modxOldRessourceId+"~]",document.getElementById("text-to-display").value||(document.getElementById("text-to-display").value=modxLinkTitle))};var autoComplt=function(){"use strict";Array.prototype.indexOf||(Array.prototype.indexOf=function(t,e){if(void 0===this||null===this)throw new TypeError('"this" is null or not defined');var n=this.length>>>0;for(e=+e||0,Math.abs(e)===1/0&&(e=0),e<0&&(e+=n)<0&&(e=0);e<n;e++)if(this[e]===t)return e;return-1});var t,e=function(){return window.navigator.userAgent.toLowerCase().search(/mobile|windows phone/)>=0?o.modeMobile:function(){var t=-1;if("Microsoft Internet Explorer"==navigator.appName){var e=navigator.userAgent;null!=new RegExp("MSIE ([0-9]{1,}[.0-9]{0,})").exec(e)&&(t=+RegExp.$1)}return-1===t?NaN:t}()<=9?o.modePC:n().windowWidth>o.modeMobileW?o.modePC:o.modeMobile},n=function(){return window.innerWidth?{windowWidth:window.innerWidth,windowHeight:window.innerHeight}:document.documentElement.offsetHeight?{windowWidth:document.documentElement.offsetWidth,windowHeight:document.documentElement.offsetHeight}:document.body.offsetHeight?{windowWidth:document.body.offsetWidth,windowHeight:document.body.offsetHeight}:document.documentElement.clientHeight?{windowWidth:document.documentElement.clientWidth,windowHeight:document.documentElement.clientHeight}:document.body.clientHeight?{windowWidth:document.body.clientWidth,windowHeight:document.body.clientHeight}:{windowWidth:-1,windowHeight:-1}},o=(o=t={},t.modePC="modePC",t.modeMobile="modeMobile",t.modeMobileW=768,t.autoCompltListClass="autoComplt-list",t.autoCompltHintClass="autoComplt-hint",t.autoCompltHintSelectedClass="autoComplt-hint-selected",t.maxHintNum=e()===o.modePC?10:5,t.autoCompltDelay=250,t.hiddenArg_close_list_n_make_final_selection="hiddenArg_close_list_n_make_final_selection",t.listStatus={attr:"data-listStatus",open:"open"},t.keyCode={up:38,down:40,esc:27,enter:13},t.defaultStyles={autoCompltList:{maxHeight:"none",border:"1px solid #aaa",padding:"0",margin:"0",zIndex:99999,overflowX:"hidden",overflowY:"auto",display:"none",position:"absolute",backgroundColor:"#fff"},autoCompltHint:{height:"1.5em",padding:e()===o.modePC?"2px 6px 2px 10px":"6px 6px 6px 10px",margin:"6px 0",overflow:"hidden",listStyleType:"none",color:"#000",backgroundColor:"#fff",cursor:"default",fontSize:"1em"},autoCompltHintSelected:{color:"#fff",backgroundColor:"#3399ff"}},t.adjStyleAttrs={autoCompltList:["border","maxHeight","backgroundColor"],autoCompltHint:["height","padding","margin","color","backgroundColor","fontSize"],autoCompltHintSelected:["color","backgroundColor"]},t.listenersSupported=["select"],o),i=function(t){return t||(t=window.event),t.target||(t.target=t.srcElement),t.stopBubble=function(){this.cancelBubble=!0,this.stopPropagation&&this.stopPropagation()},t.stopDefault=function(){return this.preventDefault&&this.preventDefault(),this.returnValue=!1,!1},t},l=function(t,e,n){t.addEventListener?t.addEventListener(e,n):t.attachEvent&&t.attachEvent("on"+e,n)},a=function(t,e,n){t.removeEventListener?t.removeEventListener(e,n):t.detachEvent&&t.detachEvent("on"+e,n)},s=function(t,e){var n=null;if(window.getComputedStyle)n=window.getComputedStyle(t)[e]||null;else if(t.currentStyle){n=t.currentStyle&&t.currentStyle[e];var o,i,l=t.style;null==n&&l&&l[e]&&(n=l[e]),o=l.left,(i=t.runtimeStyle&&t.runtimeStyle.left)&&(t.runtimeStyle.left=t.currentStyle.left),l.left="fontSize"===e?"1em":n,n=l.pixelLeft+"px",l.left=o,i&&(t.runtimeStyle.left=i)}return n},r={buildElem:function(t){var e=document.createElement("DIV");return e.innerHTML=t,e.firstChild.cloneNode(!0)},buildHint:function(t,e){return"string"==typeof t&&t?((t=this.buildElem('<li class="'+o.autoCompltHintClass+'">'+t+"</li>")).style.height=t.style.lineHeight=e.autoCompltHint.height,t.style.padding=e.autoCompltHint.padding,t.style.margin=e.autoCompltHint.margin,t.style.overflow=e.autoCompltHint.overflow,t.style.listStyleType=e.autoCompltHint.listStyleType,t.style.color=e.autoCompltHint.color,t.style.backgroundColor=e.autoCompltHint.backgroundColor,t.style.cursor=e.autoCompltHint.cursor,t.style.fontSize=e.autoCompltHint.fontSize,t):null},buildList:function(t){var e=this.buildElem('<ul class="'+o.autoCompltListClass+'"></ul>');return e.style.maxHeight=t.autoCompltList.maxHeight,e.style.border=t.autoCompltList.border,e.style.padding=t.autoCompltList.padding,e.style.margin=t.autoCompltList.margin,e.style.zIndex=t.autoCompltList.zIndex,e.style.overflowX=t.autoCompltList.overflowX,e.style.overflowY=t.autoCompltList.overflowY,e.style.display=t.autoCompltList.display,e.style.position=t.autoCompltList.position,e.style.backgroundColor=t.autoCompltList.backgroundColor,e}},u=function(t){this.uiElem=null,this.assocInput=t,this.mouseOnList=!1,this.onMouseSelectionListener=null,this.maxHintNum=o.maxHintNum,this.styles=JSON.parse(JSON.stringify(o.defaultStyles))};return u.prototype.genList=function(){if(!this.uiElem){var t=this;this.uiElem=r.buildList(this.styles),l(this.uiElem,"mouseover",function(e){e=i(e),t.isHint(e.target)&&(t.pick(e.target),t.autoScroll())}),l(this.uiElem,"mouseout",function(e){t.unpick()}),l(this.uiElem,"mousedown",function(e){t.mouseOnList=!0,setTimeout(function(){t.assocInput.focus()},50)}),l(this.uiElem,"mouseup",function(e){e=i(e),t.isHint(e.target)&&(t.pick(e.target),"function"==typeof t.onMouseSelectionListener&&t.onMouseSelectionListener())}),document.body.appendChild(this.uiElem)}},u.prototype.isHint=function(t){return!(!t||"object"!=typeof t||1!==t.nodeType)&&(" "+t.className+" ").indexOf(" "+o.autoCompltHintClass+" ")>=0},u.prototype.putHints=function(t){var e=0;if(t instanceof Array){var n,o,i=[];for(o=Math.min(t.length,this.maxHintNum),n=0;n<o;n++)i.push(r.buildHint(t[n],this.styles)),i[i.length-1]||i.pop();if(i.length>0){var l=document.createDocumentFragment();for(n=0,e=i.length;n<e;n++)l.appendChild(i[n]);this.clearHints(),this.genList(),this.uiElem.appendChild(l)}}return e},u.prototype.clearHints=function(){this.uiElem&&(this.uiElem.innerHTML="")},u.prototype.isOpen=function(){return!!this.uiElem&&this.uiElem.getAttribute(o.listStatus.attr)==o.listStatus.open},u.prototype.open=function(){var t;if(this.uiElem&&(t=this.uiElem.querySelectorAll("."+o.autoCompltHintClass))&&t.length){var e,n;for(n=this.assocInput.getBoundingClientRect(),this.uiElem.style.top=(document.documentElement&&document.documentElement.scrollTop?document.documentElement.scrollTop:document.body.scrollTop)+n.bottom+"px",this.uiElem.style.left=n.left+"px",n=n.right-n.left-parseFloat(s(this.uiElem,"borderLeftWidth"))-parseFloat(s(this.uiElem,"borderRightWidth")),this.uiElem.style.width=n+"px",e=0,n=0;e<t.length;e++)n+=parseFloat(s(t[e],"height"))+parseFloat(s(t[e],"paddingTop"))+parseFloat(s(t[e],"paddingBottom")),t[e+1]&&(n+=Math.max(parseFloat(s(t[e],"marginBottom")),parseFloat(s(t[e+1],"marginTop"))));n+=parseFloat(s(t[0],"marginTop"))+parseFloat(s(t[t.length-1],"marginBottom")),this.uiElem.style.height=n+1+"px",this.uiElem.setAttribute(o.listStatus.attr,o.listStatus.open),this.uiElem.style.display="block"}},u.prototype.close=function(){this.uiElem&&(this.mouseOnList=!1,this.uiElem.parentNode.removeChild(this.uiElem),this.uiElem=null)},u.prototype.autoScroll=function(){var t=this.getPicked();if(t){var e,n,o=0,i=t.clientHeight,l=parseFloat(s(t,"marginTop")),a=parseFloat(s(t,"marginBottom"));for(n=i+((e=t.previousSibling)?Math.max(l,a):l);e;)o+=i,o+=(e=e.previousSibling)?Math.max(l,a):l;(this.uiElem.clientHeight+this.uiElem.scrollTop-o<n||o-this.uiElem.scrollTop<n)&&(this.uiElem.scrollTop=o)}},u.prototype.pick=function(t){if(this.uiElem){var e=null;if(this.isHint(t))e=t;else if("number"==typeof t&&(t>=0||-1===t)){var n=this.uiElem.querySelectorAll("."+o.autoCompltHintClass);n.length>0&&(e=n[e=-1===(e=+t)||e>n.length-1?n.length-1:e])}null!==e&&(this.unpick(),e.className+=" "+o.autoCompltHintSelectedClass,e.style.color=this.styles.autoCompltHintSelected.color,e.style.backgroundColor=this.styles.autoCompltHintSelected.backgroundColor)}},u.prototype.unpick=function(){if(this.uiElem){var t=this.getPicked();t&&(t.className=o.autoCompltHintClass,t.style.color=this.styles.autoCompltHint.color,t.style.backgroundColor=this.styles.autoCompltHint.backgroundColor)}},u.prototype.getPicked=function(){return this.uiElem&&this.uiElem.querySelector("."+o.autoCompltHintSelectedClass)||null},{enable:function(t,n){if(t&&"object"==typeof t&&"string"==typeof t.tagName&&"input"==t.tagName.toLowerCase()&&"text"==t.type&&1===t.nodeType&&!t.autoComplt){t.autoComplt={};var s=o.autoCompltDelay,r=!0,c="",d=null,m=null,p=new u(t),f=function(){if(t.value.length>0&&r&&"function"==typeof d&&c!==t.value){var e={that:t,compltTarget:c=t.value,compltTargetMatchCurrentTarget:function(){return e.compltTarget===c},call:function(){e.compltTargetMatchCurrentTarget()&&d.call(e.that,e.compltTarget,e.openHint)},openHint:function(t){e.compltTargetMatchCurrentTarget()&&(p.putHints(t)?p.open():e.that.autoComplt.close())}};setTimeout(e.call,s)}},h=function(){if(r){var e=p.getPicked();t.value=e?e.innerHTML:c}},g=function(e){p.mouseOnList?(t.focus(),p.mouseOnList=!1):p.isOpen()&&t.autoComplt.close(o.hiddenArg_close_list_n_make_final_selection)},y=function(n){if(e()!==o.modeMobile&&(n=i(n),r))if("keydown"!=n.type||!p.isOpen()||n.keyCode!==o.keyCode.up&&n.keyCode!==o.keyCode.down){if("keyup"==n.type){var l=!1;switch(n.keyCode){case o.keyCode.up:case o.keyCode.down:p.isOpen()||(l=!0);break;case o.keyCode.esc:p.isOpen()&&(t.value=c,t.autoComplt.close(o.hiddenArg_close_list_n_make_final_selection));break;case o.keyCode.enter:p.isOpen()&&(h(),t.autoComplt.close(o.hiddenArg_close_list_n_make_final_selection));break;default:l=!0}l&&(t.value.length>0?f():t.autoComplt.close())}}else{var a=p.getPicked();n.keyCode===o.keyCode.up?a?a.previousSibling?p.pick(a.previousSibling):p.unpick():p.pick(-1):n.keyCode===o.keyCode.down&&(a?a.nextSibling?p.pick(a.nextSibling):p.unpick():p.pick(0)),p.autoScroll(),h()}};return t.autoComplt.setHintsFetcher=function(t){return(null===t||"function"==typeof t)&&(d=t,!0)},t.autoComplt.setListener=function(t,e){return(null===e||"function"==typeof e)&&o.listenersSupported.indexOf(t)>=0&&(null==m&&(m={}),m[t]=e,!0)},t.autoComplt.setStyles=function(t,e){var n,i,l=!1;switch(t){case o.autoCompltListClass:n=p.styles.autoCompltList,i=o.adjStyleAttrs.autoCompltList;break;case o.autoCompltHintClass:n=p.styles.autoCompltHint,i=o.adjStyleAttrs.autoCompltHint;break;case o.autoCompltHintSelectedClass:n=p.styles.autoCompltHintSelected,i=o.adjStyleAttrs.autoCompltHintSelected}if(e instanceof Object&&n&&i)for(var a=0;a<i.length;a++)"string"!=typeof e[i[a]]&&"number"!=typeof e[i[a]]||(l||(l={}),l[i[a]]=n[i[a]]=e[i[a]]);return l},t.autoComplt.config=function(t){var e,n=!1;t instanceof Object&&(void 0!==t.delay&&(e=Math.floor(t.delay))>0&&(n||(n={}),s=n.delay=e),void 0!==t.maxHintNum&&(e=Math.floor(t.maxHintNum))>0&&(n||(n={}),p.maxHintNum=n.maxHintNum=e));return n},t.autoComplt.close=function(){var e;c="",p.close(),r&&""!==t.value&&arguments[0]===o.hiddenArg_close_list_n_make_final_selection&&(e="select",null!=m&&"function"==typeof m[e]&&m[e].call(t))},t.autoComplt.enable=function(){r=!0},t.autoComplt.disable=function(){r=!1,this.close()},t.autoComplt.destroy=function(){a(t,"blur",g),a(t,"keyup",y),a(t,"keydown",y),this.disable(),delete t.autoComplt},p.onMouseSelectionListener=function(){h(),t.autoComplt.close(o.hiddenArg_close_list_n_make_final_selection)},l(t,"blur",g),l(t,"keyup",y),l(t,"keydown",y),l(t,"input",function(n){e()!==o.modePC&&(t.value.length>0?f():t.autoComplt.close())}),n instanceof Object&&(t.autoComplt.config(n),t.autoComplt.setHintsFetcher(n.hintsFetcher)),t}return null}}}();tinymce.PluginManager.add("modxlink",function(t){function e(e){return function(){var n=t.settings.link_list;"string"==typeof n?tinymce.util.XHR.send({url:n,success:function(t){e(tinymce.util.JSON.parse(t))}}):"function"==typeof n?n(e):e(n)}}function n(t,e,n){return function t(n,o){return o=o||[],tinymce.each(n,function(n){var i={text:n.text||n.title};n.menu?i.menu=t(n.menu):(i.value=n.value,e&&e(i)),o.push(i)}),o}(t,n||[])}function o(e){parent.tree&&(parent.tree.ca="disabled",checkModxTreeUpdate.call(this));var o,i,l,a,s,r,u,c,d,m,p,f,h={},g=t.selection,y=t.dom;function C(t){var e=a.find("#text");(!e.value()||t.lastControl&&e.value()==t.lastControl.text())&&e.value(t.control.text()),a.find("#href").value(t.control.value())}function v(){!l&&0===h.text.length&&s&&this.parent().parent().find("#text")[0].value(this.value())}o=g.getNode(),i=y.getParent(o,"a[href]"),s=function(t){var e=g.getContent();if(/</.test(e)&&(!/^<a [^>]+>[^<]+<\/a>$/.test(e)||-1==e.indexOf("href=")))return!1;if(t){var n,o=t.childNodes;if(0===o.length)return!1;for(n=o.length-1;n>=0;n--)if(3!=o[n].nodeType)return!1}return!0}(),h.text=l=i?i.innerText||i.textContent:g.getContent({format:"text"}),h.href=i?y.getAttrib(i,"href"):"",i?h.target=y.getAttrib(i,"target"):t.settings.default_link_target&&(h.target=t.settings.default_link_target),(f=y.getAttrib(i,"rel"))&&(h.rel=f),(f=y.getAttrib(i,"class"))&&(h.class=f),(f=y.getAttrib(i,"title"))&&(h.title=f),s&&(r={name:"text",type:"textbox",size:40,label:"Text to display",id:"text-to-display",onchange:function(){h.text=this.value()}}),e&&(u={type:"listbox",label:"Link list",values:n(e,function(e){e.value=t.convertURL(e.value||e.url,"href")},[{text:"None",value:""}]),onselect:C,value:t.convertURL(h.href,"href"),onPostRender:function(){u=this}}),!1!==t.settings.target_list&&(t.settings.target_list||(t.settings.target_list=[{text:"None",value:""},{text:"New window",value:"_blank"}]),d={name:"target",type:"listbox",label:"Target",values:n(t.settings.target_list)}),t.settings.rel_list&&(c={name:"rel",type:"listbox",label:"Rel",values:n(t.settings.rel_list)}),t.settings.link_class_list&&(m={name:"class",type:"listbox",label:"Class",values:n(t.settings.link_class_list,function(e){e.value&&(e.textStyle=function(){return t.formatter.getCssText({inline:"a",classes:[e.value]})})})}),!1!==t.settings.link_title&&(p={name:"title",type:"textbox",label:"Title",value:h.title}),a=t.windowManager.open({title:"Insert link",data:h,body:[{name:"href",type:"FileImagePicker",filetype:"file",size:40,autofocus:!0,label:"Url",id:"link-href",onchange:function(e){var n=e.meta||{};u&&u.value(t.convertURL(this.value(),"href")),tinymce.each(e.meta,function(t,e){a.find("#"+e).value(t)}),n.text||v.call(this)},onkeyup:v},{name:"search",type:"textbox",label:"Search in EVO",id:"link-search"},r,p,function(e){var n=[];if(tinymce.each(t.dom.select("a:not([href])"),function(t){var o=t.name||t.id;o&&n.push({text:o,value:"#"+o,selected:-1!=e.indexOf("#"+o)})}),n.length)return n.unshift({text:"None",value:""}),{name:"anchor",type:"listbox",label:"Anchors",values:n,onselect:C}}(h.href),u,c,d,m],onSubmit:function(e){var n;function o(e,n){var o=t.selection.getRng();window.setTimeout(function(){t.windowManager.confirm(e,function(e){t.selection.setRng(o),n(e)})},0)}function a(){parent.tree&&(parent.tree.ca="",clearTimeout(checkModxTreeUpdateInterval));var e={href:n,target:h.target?h.target:null,rel:h.rel?h.rel:null,class:h.class?h.class:null,title:h.title?h.title:null};i?(t.focus(),s&&h.text!=l&&("innerText"in i?i.innerText=h.text:i.textContent=h.text),y.setAttribs(i,e),g.select(i),t.undoManager.add()):s?t.insertContent(y.createHTML("a",e,y.encode(h.text))):t.execCommand("mceInsertLink",!1,e)}h=tinymce.extend(h,e.data),(n=h.href)?n.indexOf("@")>0&&-1==n.indexOf("//")&&-1==n.indexOf("mailto:")?o("The URL you entered seems to be an email address. Do you want to add the required mailto: prefix?",function(t){t&&(n="mailto:"+n),a()}):t.settings.link_assume_external_targets&&!/^\w+:/i.test(n)||!t.settings.link_assume_external_targets&&/^\s*www\./i.test(n)?o("The URL you entered seems to be an external link. Do you want to add the required http:// prefix?",function(t){t&&(n="http://"+n),a()}):a():t.execCommand("unlink")},onClose:function(t){parent.tree&&(parent.tree.ca="",clearTimeout(checkModxTreeUpdateInterval))}});var x=document.querySelector("#link-search"),b={};autoComplt.enable(x,{hintsFetcher:function(t,e){b={};var n=new XMLHttpRequest,o=encodeURI(tinymce4_baseUrl+"tinymce/plugins/modxlink/search.php?q="+t);n.open("GET",o),n.onload=function(){if(200===n.status){for(var t=JSON.parse(n.responseText),i=[],l=0;l<Object.keys(t).length;l++)b[t[l].pagetitle]=t[l],i.push(t[l].pagetitle);e(i)}else console.error("ajax error",o)},n.send()}}),x.autoComplt.setListener("select",function(t,e){var n=document.querySelector("#link-href input"),o=document.querySelector("#text-to-display");n.value="[~"+b[this.value].id+"~]",""==o.value&&(o.value=b[this.value].title)})}t.addButton("link",{icon:"link",tooltip:"Insert/edit link",shortcut:"Meta+K",onclick:e(o),stateSelector:"a[href]"}),t.addButton("unlink",{icon:"unlink",tooltip:"Remove link",cmd:"unlink",stateSelector:"a[href]"}),t.addShortcut("Meta+K","",e(o)),t.addCommand("mceLink",e(o)),this.showDialog=o,t.addMenuItem("link",{icon:"link",text:"Insert/edit link",shortcut:"Meta+K",onclick:e(o),stateSelector:"a[href]",context:"insert",prependToContext:!0}),tinymce.ui.Factory.add("FileImagePicker",tinymce.ui.ComboBox.extend({init:function(t){var e,n,o,i=this,l=tinymce.activeEditor,a=l.settings;t.spellcheck=!1,(o=a.file_picker_types||a.file_browser_callback_types)&&(o=Tools.makeMap(o,/[, ]/)),o&&!o[t.filetype]||(!(n=a.file_picker_callback)||o&&!o[t.filetype]?!(n=a.file_browser_callback)||o&&!o[t.filetype]||(e=function(){n(i.getEl("inp").id,i.value(),i.settings.filetype,window)}):e=function(){var e=i.fire("beforecall").meta;e=Tools.extend({filetype:t.filetype},e),n.call(l,function(t,e){i.value(t).fire("change",{meta:e})},i.value(),e)}),e&&(t.icon="browse",t.onaction=e),i._super(t),i.off("click").on("click",function(t){var e=t.target,n=i.getEl();if(i.$.contains(n,e)||e==n)for(;e&&e!=n;)e.id&&-1!=e.id.indexOf("-action")&&(e.id===i._id+"-image-action"?i.settings.filetype="image":i.settings.filetype="file",i.fire("action")),e=e.parentNode})},renderHtml:function(){var t,e=this,n=e._id,o=e.settings,i=e.classPrefix,l=e.state.get("value")||"",a="";return"spellcheck"in o&&(a+=' spellcheck="'+o.spellcheck+'"'),o.maxLength&&(a+=' maxlength="'+o.maxLength+'"'),o.size&&(a+=' size="'+o.size+'"'),o.subtype&&(a+=' type="'+o.subtype+'"'),e.disabled()&&(a+=' disabled="disabled"'),t='<div id="'+n+'-open" class="'+i+"btn "+i+'open" tabIndex="-1" role="button"><button id="'+n+'-action" type="button" hidefocus="1" tabindex="-1"><i class="mce-ico mce-i-browse"></i></button><button id="'+n+'-image-action" type="button" hidefocus="1" tabindex="-1"><i class="mce-ico mce-i-image"></i></button></div>',e.classes.add("has-open"),'<div id="'+n+'" class="'+e.classes+'"><input id="'+n+'-inp" class="'+i+'textbox" value="'+e.encode(l,!1)+'" hidefocus="1"'+a+' placeholder="'+e.encode(o.placeholder)+'" />'+t+"</div>"}}))});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment