Index: lib/src/prism/plugins/previewer-gradient/prism-previewer-gradient.js |
diff --git a/lib/src/prism/plugins/previewer-gradient/prism-previewer-gradient.js b/lib/src/prism/plugins/previewer-gradient/prism-previewer-gradient.js |
new file mode 100644 |
index 0000000000000000000000000000000000000000..11c2d0619e4ade3bde93ce094c81a8b4a0372f78 |
--- /dev/null |
+++ b/lib/src/prism/plugins/previewer-gradient/prism-previewer-gradient.js |
@@ -0,0 +1,216 @@ |
+(function() { |
+ |
+ if ( |
+ typeof self !== 'undefined' && !self.Prism || |
+ typeof global !== 'undefined' && !global.Prism |
+ ) { |
+ return; |
+ } |
+ |
+ var languages = { |
+ 'css': true, |
+ 'less': true, |
+ 'sass': [ |
+ { |
+ lang: 'sass', |
+ before: 'punctuation', |
+ inside: 'inside', |
+ root: Prism.languages.sass && Prism.languages.sass['variable-line'] |
+ }, |
+ { |
+ lang: 'sass', |
+ before: 'punctuation', |
+ inside: 'inside', |
+ root: Prism.languages.sass && Prism.languages.sass['property-line'] |
+ } |
+ ], |
+ 'scss': true, |
+ 'stylus': [ |
+ { |
+ lang: 'stylus', |
+ before: 'func', |
+ inside: 'rest', |
+ root: Prism.languages.stylus && Prism.languages.stylus['property-declaration'].inside |
+ }, |
+ { |
+ lang: 'stylus', |
+ before: 'func', |
+ inside: 'rest', |
+ root: Prism.languages.stylus && Prism.languages.stylus['variable-declaration'].inside |
+ } |
+ ] |
+ }; |
+ |
+ Prism.hooks.add('before-highlight', function (env) { |
+ if (env.language && languages[env.language] && !languages[env.language].initialized) { |
+ var lang = languages[env.language]; |
+ if (Prism.util.type(lang) !== 'Array') { |
+ lang = [lang]; |
+ } |
+ lang.forEach(function(lang) { |
+ var before, inside, root, skip; |
+ if (lang === true) { |
+ // Insert before color previewer if it exists |
+ before = Prism.plugins.Previewer && Prism.plugins.Previewer.byType['color'] ? 'color' : 'important'; |
+ inside = env.language; |
+ lang = env.language; |
+ } else { |
+ before = lang.before || 'important'; |
+ inside = lang.inside || lang.lang; |
+ root = lang.root || Prism.languages; |
+ skip = lang.skip; |
+ lang = env.language; |
+ } |
+ |
+ if (!skip && Prism.languages[lang]) { |
+ Prism.languages.insertBefore(inside, before, { |
+ 'gradient': { |
+ pattern: /(?:\b|\B-[a-z]{1,10}-)(?:repeating-)?(?:linear|radial)-gradient\((?:(?:rgb|hsl)a?\(.+?\)|[^\)])+\)/gi, |
+ inside: { |
+ 'function': /[\w-]+(?=\()/, |
+ 'punctuation': /[(),]/ |
+ } |
+ } |
+ }, root); |
+ env.grammar = Prism.languages[lang]; |
+ |
+ languages[env.language] = {initialized: true}; |
+ } |
+ }); |
+ } |
+ }); |
+ |
+ // Stores already processed gradients so that we don't |
+ // make the conversion every time the previewer is shown |
+ var cache = {}; |
+ |
+ /** |
+ * Returns a W3C-valid linear gradient |
+ * @param {string} prefix Vendor prefix if any ("-moz-", "-webkit-", etc.) |
+ * @param {string} func Gradient function name ("linear-gradient") |
+ * @param {string[]} values Array of the gradient function parameters (["0deg", "red 0%", "blue 100%"]) |
+ */ |
+ var convertToW3CLinearGradient = function(prefix, func, values) { |
+ // Default value for angle |
+ var angle = '180deg'; |
+ |
+ if (/^(?:-?\d*\.?\d+(?:deg|rad)|to\b|top|right|bottom|left)/.test(values[0])) { |
+ angle = values.shift(); |
+ if (angle.indexOf('to ') < 0) { |
+ // Angle uses old keywords |
+ // W3C syntax uses "to" + opposite keywords |
+ if (angle.indexOf('top') >= 0) { |
+ if (angle.indexOf('left') >= 0) { |
+ angle = 'to bottom right'; |
+ } else if (angle.indexOf('right') >= 0) { |
+ angle = 'to bottom left'; |
+ } else { |
+ angle = 'to bottom'; |
+ } |
+ } else if (angle.indexOf('bottom') >= 0) { |
+ if (angle.indexOf('left') >= 0) { |
+ angle = 'to top right'; |
+ } else if (angle.indexOf('right') >= 0) { |
+ angle = 'to top left'; |
+ } else { |
+ angle = 'to top'; |
+ } |
+ } else if (angle.indexOf('left') >= 0) { |
+ angle = 'to right'; |
+ } else if (angle.indexOf('right') >= 0) { |
+ angle = 'to left'; |
+ } else if (prefix) { |
+ // Angle is shifted by 90deg in prefixed gradients |
+ if (angle.indexOf('deg') >= 0) { |
+ angle = (90 - parseFloat(angle)) + 'deg'; |
+ } else if (angle.indexOf('rad') >= 0) { |
+ angle = (Math.PI / 2 - parseFloat(angle)) + 'rad'; |
+ } |
+ } |
+ } |
+ } |
+ |
+ return func + '(' + angle + ',' + values.join(',') + ')'; |
+ }; |
+ |
+ /** |
+ * Returns a W3C-valid radial gradient |
+ * @param {string} prefix Vendor prefix if any ("-moz-", "-webkit-", etc.) |
+ * @param {string} func Gradient function name ("linear-gradient") |
+ * @param {string[]} values Array of the gradient function parameters (["0deg", "red 0%", "blue 100%"]) |
+ */ |
+ var convertToW3CRadialGradient = function(prefix, func, values) { |
+ if (values[0].indexOf('at') < 0) { |
+ // Looks like old syntax |
+ |
+ // Default values |
+ var position = 'center'; |
+ var shape = 'ellipse'; |
+ var size = 'farthest-corner'; |
+ |
+ if (/\bcenter|top|right|bottom|left\b|^\d+/.test(values[0])) { |
+ // Found a position |
+ // Remove angle value, if any |
+ position = values.shift().replace(/\s*-?\d+(?:rad|deg)\s*/, ''); |
+ } |
+ if (/\bcircle|ellipse|closest|farthest|contain|cover\b/.test(values[0])) { |
+ // Found a shape and/or size |
+ var shapeSizeParts = values.shift().split(/\s+/); |
+ if (shapeSizeParts[0] && (shapeSizeParts[0] === 'circle' || shapeSizeParts[0] === 'ellipse')) { |
+ shape = shapeSizeParts.shift(); |
+ } |
+ if (shapeSizeParts[0]) { |
+ size = shapeSizeParts.shift(); |
+ } |
+ |
+ // Old keywords are converted to their synonyms |
+ if (size === 'cover') { |
+ size = 'farthest-corner'; |
+ } else if (size === 'contain') { |
+ size = 'clothest-side'; |
+ } |
+ } |
+ |
+ return func + '(' + shape + ' ' + size + ' at ' + position + ',' + values.join(',') + ')'; |
+ } |
+ return func + '(' + values.join(',') + ')'; |
+ }; |
+ |
+ /** |
+ * Converts a gradient to a W3C-valid one |
+ * Does not support old webkit syntax (-webkit-gradient(linear...) and -webkit-gradient(radial...)) |
+ * @param {string} gradient The CSS gradient |
+ */ |
+ var convertToW3CGradient = function(gradient) { |
+ if (cache[gradient]) { |
+ return cache[gradient]; |
+ } |
+ var parts = gradient.match(/^(\b|\B-[a-z]{1,10}-)((?:repeating-)?(?:linear|radial)-gradient)/); |
+ // "", "-moz-", etc. |
+ var prefix = parts && parts[1]; |
+ // "linear-gradient", "radial-gradient", etc. |
+ var func = parts && parts[2]; |
+ |
+ var values = gradient.replace(/^(?:\b|\B-[a-z]{1,10}-)(?:repeating-)?(?:linear|radial)-gradient\(|\)$/g, '').split(/\s*,\s*/); |
+ |
+ if (func.indexOf('linear') >= 0) { |
+ return cache[gradient] = convertToW3CLinearGradient(prefix, func, values); |
+ } else if (func.indexOf('radial') >= 0) { |
+ return cache[gradient] = convertToW3CRadialGradient(prefix, func, values); |
+ } |
+ return cache[gradient] = func + '(' + values.join(',') + ')'; |
+ }; |
+ |
+ |
+ |
+ if (Prism.plugins.Previewer) { |
+ new Prism.plugins.Previewer('gradient', function(value) { |
+ this.firstChild.style.backgroundImage = ''; |
+ this.firstChild.style.backgroundImage = convertToW3CGradient(value); |
+ return !!this.firstChild.style.backgroundImage; |
+ }, '*', function () { |
+ this._elt.innerHTML = '<div></div>'; |
+ }); |
+ } |
+ |
+}()); |