(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports) : typeof define === 'function' && define.amd ? define(['exports'], factory) : (global = global || self, factory(global.cssVendor = {})); }(this, (function (exports) { 'use strict'; var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; }; var isBrowser = (typeof window === "undefined" ? "undefined" : _typeof(window)) === "object" && (typeof document === "undefined" ? "undefined" : _typeof(document)) === 'object' && document.nodeType === 9; // Export javascript style and css style vendor prefixes. var js = ''; var css = ''; var vendor = ''; var browser = ''; var isTouch = isBrowser && 'ontouchstart' in document.documentElement; // We should not do anything if required serverside. if (isBrowser) { // Order matters. We need to check Webkit the last one because // other vendors use to add Webkit prefixes to some properties var jsCssMap = { Moz: '-moz-', ms: '-ms-', O: '-o-', Webkit: '-webkit-' }; var _document$createEleme = document.createElement('p'), style = _document$createEleme.style; var testProp = 'Transform'; for (var key in jsCssMap) { if (key + testProp in style) { js = key; css = jsCssMap[key]; break; } } // Correctly detect the Edge browser. if (js === 'Webkit' && 'msHyphens' in style) { js = 'ms'; css = jsCssMap.ms; browser = 'edge'; } // Correctly detect the Safari browser. if (js === 'Webkit' && '-apple-trailing-word' in style) { vendor = 'apple'; } } /** * Vendor prefix string for the current browser. * * @type {{js: String, css: String, vendor: String, browser: String}} * @api public */ var prefix = { js: js, css: css, vendor: vendor, browser: browser, isTouch: isTouch }; /** * Test if a keyframe at-rule should be prefixed or not * * @param {String} vendor prefix string for the current browser. * @return {String} * @api public */ function supportedKeyframes(key) { // Keyframes is already prefixed. e.g. key = '@-webkit-keyframes a' if (key[1] === '-') return key; // No need to prefix IE/Edge. Older browsers will ignore unsupported rules. // https://caniuse.com/#search=keyframes if (prefix.js === 'ms') return key; return "@" + prefix.css + "keyframes" + key.substr(10); } function _arrayWithoutHoles(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = new Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } } function _iterableToArray(iter) { if (Symbol.iterator in Object(iter) || Object.prototype.toString.call(iter) === "[object Arguments]") return Array.from(iter); } function _nonIterableSpread() { throw new TypeError("Invalid attempt to spread non-iterable instance"); } function _toConsumableArray(arr) { return _arrayWithoutHoles(arr) || _iterableToArray(arr) || _nonIterableSpread(); } // https://caniuse.com/#search=appearance var appearence = { noPrefill: ['appearance'], supportedProperty: function supportedProperty(prop) { if (prop !== 'appearance') return false; if (prefix.js === 'ms') return "-webkit-" + prop; return prefix.css + prop; } }; // https://caniuse.com/#search=color-adjust var colorAdjust = { noPrefill: ['color-adjust'], supportedProperty: function supportedProperty(prop) { if (prop !== 'color-adjust') return false; if (prefix.js === 'Webkit') return prefix.css + "print-" + prop; return prop; } }; var regExp = /[-\s]+(.)?/g; /** * Replaces the letter with the capital letter * * @param {String} match * @param {String} c * @return {String} * @api private */ function toUpper(match, c) { return c ? c.toUpperCase() : ''; } /** * Convert dash separated strings to camel-cased. * * @param {String} str * @return {String} * @api private */ function camelize(str) { return str.replace(regExp, toUpper); } /** * Convert dash separated strings to pascal cased. * * @param {String} str * @return {String} * @api private */ function pascalize(str) { return camelize("-" + str); } // but we can use a longhand property instead. // https://caniuse.com/#search=mask var mask = { noPrefill: ['mask'], supportedProperty: function supportedProperty(prop, style) { if (!/^mask/.test(prop)) return false; if (prefix.js === 'Webkit') { var longhand = 'mask-image'; if (camelize(longhand) in style) { return prop; } if (prefix.js + pascalize(longhand) in style) { return prefix.css + prop; } } return prop; } }; // https://caniuse.com/#search=text-orientation var textOrientation = { noPrefill: ['text-orientation'], supportedProperty: function supportedProperty(prop) { if (prop !== 'text-orientation') return false; if (prefix.vendor === 'apple' && !prefix.isTouch) { return prefix.css + prop; } return prop; } }; // https://caniuse.com/#search=transform var transform = { noPrefill: ['transform'], supportedProperty: function supportedProperty(prop, style, options) { if (prop !== 'transform') return false; if (options.transform) { return prop; } return prefix.css + prop; } }; // https://caniuse.com/#search=transition var transition = { noPrefill: ['transition'], supportedProperty: function supportedProperty(prop, style, options) { if (prop !== 'transition') return false; if (options.transition) { return prop; } return prefix.css + prop; } }; // https://caniuse.com/#search=writing-mode var writingMode = { noPrefill: ['writing-mode'], supportedProperty: function supportedProperty(prop) { if (prop !== 'writing-mode') return false; if (prefix.js === 'Webkit' || prefix.js === 'ms' && prefix.browser !== 'edge') { return prefix.css + prop; } return prop; } }; // https://caniuse.com/#search=user-select var userSelect = { noPrefill: ['user-select'], supportedProperty: function supportedProperty(prop) { if (prop !== 'user-select') return false; if (prefix.js === 'Moz' || prefix.js === 'ms' || prefix.vendor === 'apple') { return prefix.css + prop; } return prop; } }; // https://caniuse.com/#search=multicolumn // https://github.com/postcss/autoprefixer/issues/491 // https://github.com/postcss/autoprefixer/issues/177 var breakPropsOld = { supportedProperty: function supportedProperty(prop, style) { if (!/^break-/.test(prop)) return false; if (prefix.js === 'Webkit') { var jsProp = "WebkitColumn" + pascalize(prop); return jsProp in style ? prefix.css + "column-" + prop : false; } if (prefix.js === 'Moz') { var _jsProp = "page" + pascalize(prop); return _jsProp in style ? "page-" + prop : false; } return false; } }; // See https://github.com/postcss/autoprefixer/issues/324. var inlineLogicalOld = { supportedProperty: function supportedProperty(prop, style) { if (!/^(border|margin|padding)-inline/.test(prop)) return false; if (prefix.js === 'Moz') return prop; var newProp = prop.replace('-inline', ''); return prefix.js + pascalize(newProp) in style ? prefix.css + newProp : false; } }; // Camelization is required because we can't test using. // CSS syntax for e.g. in FF. var unprefixed = { supportedProperty: function supportedProperty(prop, style) { return camelize(prop) in style ? prop : false; } }; var prefixed = { supportedProperty: function supportedProperty(prop, style) { var pascalized = pascalize(prop); // Return custom CSS variable without prefixing. if (prop[0] === '-') return prop; // Return already prefixed value without prefixing. if (prop[0] === '-' && prop[1] === '-') return prop; if (prefix.js + pascalized in style) return prefix.css + prop; // Try webkit fallback. if (prefix.js !== 'Webkit' && "Webkit" + pascalized in style) return "-webkit-" + prop; return false; } }; // https://caniuse.com/#search=scroll-snap var scrollSnap = { supportedProperty: function supportedProperty(prop) { if (prop.substring(0, 11) !== 'scroll-snap') return false; if (prefix.js === 'ms') { return "" + prefix.css + prop; } return prop; } }; // https://caniuse.com/#search=overscroll-behavior var overscrollBehavior = { supportedProperty: function supportedProperty(prop) { if (prop !== 'overscroll-behavior') return false; if (prefix.js === 'ms') { return prefix.css + "scroll-chaining"; } return prop; } }; var propMap = { 'flex-grow': 'flex-positive', 'flex-shrink': 'flex-negative', 'flex-basis': 'flex-preferred-size', 'justify-content': 'flex-pack', order: 'flex-order', 'align-items': 'flex-align', 'align-content': 'flex-line-pack' // 'align-self' is handled by 'align-self' plugin. }; // Support old flex spec from 2012. var flex2012 = { supportedProperty: function supportedProperty(prop, style) { var newProp = propMap[prop]; if (!newProp) return false; return prefix.js + pascalize(newProp) in style ? prefix.css + newProp : false; } }; var propMap$1 = { flex: 'box-flex', 'flex-grow': 'box-flex', 'flex-direction': ['box-orient', 'box-direction'], order: 'box-ordinal-group', 'align-items': 'box-align', 'flex-flow': ['box-orient', 'box-direction'], 'justify-content': 'box-pack' }; var propKeys = Object.keys(propMap$1); var prefixCss = function prefixCss(p) { return prefix.css + p; }; // Support old flex spec from 2009. var flex2009 = { supportedProperty: function supportedProperty(prop, style, _ref) { var multiple = _ref.multiple; if (propKeys.indexOf(prop) > -1) { var newProp = propMap$1[prop]; if (!Array.isArray(newProp)) { return prefix.js + pascalize(newProp) in style ? prefix.css + newProp : false; } if (!multiple) return false; for (var i = 0; i < newProp.length; i++) { if (!(prefix.js + pascalize(newProp[0]) in style)) { return false; } } return newProp.map(prefixCss); } return false; } }; // plugins = [ // ...plugins, // breakPropsOld, // inlineLogicalOld, // unprefixed, // prefixed, // scrollSnap, // flex2012, // flex2009 // ] // Plugins without 'noPrefill' value, going last. // 'flex-*' plugins should be at the bottom. // 'flex2009' going after 'flex2012'. // 'prefixed' going after 'unprefixed' var plugins = [appearence, colorAdjust, mask, textOrientation, transform, transition, writingMode, userSelect, breakPropsOld, inlineLogicalOld, unprefixed, prefixed, scrollSnap, overscrollBehavior, flex2012, flex2009]; var propertyDetectors = plugins.filter(function (p) { return p.supportedProperty; }).map(function (p) { return p.supportedProperty; }); var noPrefill = plugins.filter(function (p) { return p.noPrefill; }).reduce(function (a, p) { a.push.apply(a, _toConsumableArray(p.noPrefill)); return a; }, []); var el; var cache = {}; if (isBrowser) { el = document.createElement('p'); // We test every property on vendor prefix requirement. // Once tested, result is cached. It gives us up to 70% perf boost. // http://jsperf.com/element-style-object-access-vs-plain-object // // Prefill cache with known css properties to reduce amount of // properties we need to feature test at runtime. // http://davidwalsh.name/vendor-prefix var computed = window.getComputedStyle(document.documentElement, ''); for (var key$1 in computed) { // eslint-disable-next-line no-restricted-globals if (!isNaN(key$1)) cache[computed[key$1]] = computed[key$1]; } // Properties that cannot be correctly detected using the // cache prefill method. noPrefill.forEach(function (x) { return delete cache[x]; }); } /** * Test if a property is supported, returns supported property with vendor * prefix if required. Returns `false` if not supported. * * @param {String} prop dash separated * @param {Object} [options] * @return {String|Boolean} * @api public */ function supportedProperty(prop, options) { if (options === void 0) { options = {}; } // For server-side rendering. if (!el) return prop; // Remove cache for benchmark tests or return property from the cache. if ( cache[prop] != null) { return cache[prop]; } // Check if 'transition' or 'transform' natively supported in browser. if (prop === 'transition' || prop === 'transform') { options[prop] = prop in el.style; } // Find a plugin for current prefix property. for (var i = 0; i < propertyDetectors.length; i++) { cache[prop] = propertyDetectors[i](prop, el.style, options); // Break loop, if value found. if (cache[prop]) break; } // Reset styles for current property. // Firefox can even throw an error for invalid properties, e.g., "0". try { el.style[prop] = ''; } catch (err) { return false; } return cache[prop]; } var cache$1 = {}; var transitionProperties = { transition: 1, 'transition-property': 1, '-webkit-transition': 1, '-webkit-transition-property': 1 }; var transPropsRegExp = /(^\s*[\w-]+)|, (\s*[\w-]+)(?![^()]*\))/g; var el$1; /** * Returns prefixed value transition/transform if needed. * * @param {String} match * @param {String} p1 * @param {String} p2 * @return {String} * @api private */ function prefixTransitionCallback(match, p1, p2) { if (p1 === 'var') return 'var'; if (p1 === 'all') return 'all'; if (p2 === 'all') return ', all'; var prefixedValue = p1 ? supportedProperty(p1) : ", " + supportedProperty(p2); if (!prefixedValue) return p1 || p2; return prefixedValue; } if (isBrowser) el$1 = document.createElement('p'); /** * Returns prefixed value if needed. Returns `false` if value is not supported. * * @param {String} property * @param {String} value * @return {String|Boolean} * @api public */ function supportedValue(property, value) { // For server-side rendering. var prefixedValue = value; if (!el$1 || property === 'content') return value; // It is a string or a number as a string like '1'. // We want only prefixable values here. // eslint-disable-next-line no-restricted-globals if (typeof prefixedValue !== 'string' || !isNaN(parseInt(prefixedValue, 10))) { return prefixedValue; } // Create cache key for current value. var cacheKey = property + prefixedValue; // Remove cache for benchmark tests or return value from cache. if ( cache$1[cacheKey] != null) { return cache$1[cacheKey]; } // IE can even throw an error in some cases, for e.g. style.content = 'bar'. try { // Test value as it is. el$1.style[property] = prefixedValue; } catch (err) { // Return false if value not supported. cache$1[cacheKey] = false; return false; } // If 'transition' or 'transition-property' property. if (transitionProperties[property]) { prefixedValue = prefixedValue.replace(transPropsRegExp, prefixTransitionCallback); } else if (el$1.style[property] === '') { // Value with a vendor prefix. prefixedValue = prefix.css + prefixedValue; // Hardcode test to convert "flex" to "-ms-flexbox" for IE10. if (prefixedValue === '-ms-flex') el$1.style[property] = '-ms-flexbox'; // Test prefixed value. el$1.style[property] = prefixedValue; // Return false if value not supported. if (el$1.style[property] === '') { cache$1[cacheKey] = false; return false; } } // Reset styles for current property. el$1.style[property] = ''; // Write current value to cache. cache$1[cacheKey] = prefixedValue; return cache$1[cacheKey]; } exports.prefix = prefix; exports.supportedKeyframes = supportedKeyframes; exports.supportedProperty = supportedProperty; exports.supportedValue = supportedValue; Object.defineProperty(exports, '__esModule', { value: true }); })));