201 lines
5.0 KiB
JavaScript
201 lines
5.0 KiB
JavaScript
(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);
|
|
}
|
|
});
|
|
|
|
}()); |