Index: lib/src/prism/plugins/previewer-base/prism-previewer-base.js |
diff --git a/lib/src/prism/plugins/previewer-base/prism-previewer-base.js b/lib/src/prism/plugins/previewer-base/prism-previewer-base.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..a5eb59adc6fd6626b86f00bf5a3ccb64f2c2e5e6 |
--- /dev/null |
+++ b/lib/src/prism/plugins/previewer-base/prism-previewer-base.js |
@@ -0,0 +1,201 @@ |
+(function() { |
+ |
+ if (typeof self === 'undefined' || !self.Prism || !self.document) { |
+ 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); |
+ } |
+ }); |
+ |
+}()); |