(function() { if (typeof self === 'undefined' || !self.Prism || !self.document || !Function.prototype.bind) { return; } /** * Returns the absolute X, Y offsets for an element * @param {HTMLElement} element * @returns {{top: number, right: number, bottom: number, left: number}} */ var getOffset = function (element) { var left = 0, top = 0, el = element; if (el.parentNode) { do { left += el.offsetLeft; top += el.offsetTop; } while ((el = el.offsetParent) && el.nodeType < 9); el = element; do { left -= el.scrollLeft; top -= el.scrollTop; } while ((el = el.parentNode) && !/body/i.test(el.nodeName)); } return { top: top, right: innerWidth - left - element.offsetWidth, bottom: innerHeight - top - element.offsetHeight, left: left }; }; var tokenRegexp = /(?:^|\s)token(?=$|\s)/; var activeRegexp = /(?:^|\s)active(?=$|\s)/g; var flippedRegexp = /(?:^|\s)flipped(?=$|\s)/g; /** * Previewer constructor * @param {string} type Unique previewer type * @param {function} updater Function that will be called on mouseover. * @param {string[]|string=} supportedLanguages Aliases of the languages this previewer must be enabled for. Defaults to "*", all languages. * @constructor */ var Previewer = function (type, updater, supportedLanguages, initializer) { this._elt = null; this._type = type; this._clsRegexp = RegExp('(?:^|\\s)' + type + '(?=$|\\s)'); this._token = null; this.updater = updater; this._mouseout = this.mouseout.bind(this); this.initializer = initializer; var self = this; if (!supportedLanguages) { supportedLanguages = ['*']; } if (Prism.util.type(supportedLanguages) !== 'Array') { supportedLanguages = [supportedLanguages]; } supportedLanguages.forEach(function (lang) { if (typeof lang !== 'string') { lang = lang.lang; } if (!Previewer.byLanguages[lang]) { Previewer.byLanguages[lang] = []; } if (Previewer.byLanguages[lang].indexOf(self) < 0) { Previewer.byLanguages[lang].push(self); } }); Previewer.byType[type] = this; }; /** * Creates the HTML element for the previewer. */ Previewer.prototype.init = function () { if (this._elt) { return; } this._elt = document.createElement('div'); this._elt.className = 'prism-previewer prism-previewer-' + this._type; document.body.appendChild(this._elt); if(this.initializer) { this.initializer(); } }; /** * Checks the class name of each hovered element * @param token */ Previewer.prototype.check = function (token) { do { if (tokenRegexp.test(token.className) && this._clsRegexp.test(token.className)) { break; } } while(token = token.parentNode); if (token && token !== this._token) { this._token = token; this.show(); } }; /** * Called on mouseout */ Previewer.prototype.mouseout = function() { this._token.removeEventListener('mouseout', this._mouseout, false); this._token = null; this.hide(); }; /** * Shows the previewer positioned properly for the current token. */ Previewer.prototype.show = function () { if (!this._elt) { this.init(); } if (!this._token) { return; } if (this.updater.call(this._elt, this._token.textContent)) { this._token.addEventListener('mouseout', this._mouseout, false); var offset = getOffset(this._token); this._elt.className += ' active'; if (offset.top - this._elt.offsetHeight > 0) { this._elt.className = this._elt.className.replace(flippedRegexp, ''); this._elt.style.top = offset.top + 'px'; this._elt.style.bottom = ''; } else { this._elt.className += ' flipped'; this._elt.style.bottom = offset.bottom + 'px'; this._elt.style.top = ''; } this._elt.style.left = offset.left + Math.min(200, this._token.offsetWidth / 2) + 'px'; } else { this.hide(); } }; /** * Hides the previewer. */ Previewer.prototype.hide = function () { this._elt.className = this._elt.className.replace(activeRegexp, ''); }; /** * Map of all registered previewers by language * @type {{}} */ Previewer.byLanguages = {}; /** * Map of all registered previewers by type * @type {{}} */ Previewer.byType = {}; /** * Initializes the mouseover event on the code block. * @param {HTMLElement} elt The code block (env.element) * @param {string} lang The language (env.language) */ Previewer.initEvents = function (elt, lang) { var previewers = []; if (Previewer.byLanguages[lang]) { previewers = previewers.concat(Previewer.byLanguages[lang]); } if (Previewer.byLanguages['*']) { previewers = previewers.concat(Previewer.byLanguages['*']); } elt.addEventListener('mouseover', function (e) { var target = e.target; previewers.forEach(function (previewer) { previewer.check(target); }); }, false); }; Prism.plugins.Previewer = Previewer; // Initialize the previewers only when needed Prism.hooks.add('after-highlight', function (env) { if(Previewer.byLanguages['*'] || Previewer.byLanguages[env.language]) { Previewer.initEvents(env.element, env.language); } }); }());