GT2/Ejectable/node_modules/react-native-web/dist/exports/StyleSheet/createOrderedCSSStyleSheet.js

170 lines
5.0 KiB
JavaScript

/**
* Copyright (c) Nicolas Gallagher.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
*/
var slice = Array.prototype.slice;
/**
* Order-based insertion of CSS.
*
* Each rule is associated with a numerically defined group.
* Groups are ordered within the style sheet according to their number, with the
* lowest first.
*
* Groups are implemented using marker rules. The selector of the first rule of
* each group is used only to encode the group number for hydration. An
* alternative implementation could rely on CSSMediaRule, allowing groups to be
* treated as a sub-sheet, but the Edge implementation of CSSMediaRule is
* broken.
* https://developer.mozilla.org/en-US/docs/Web/API/CSSMediaRule
* https://gist.github.com/necolas/aa0c37846ad6bd3b05b727b959e82674
*/
export default function createOrderedCSSStyleSheet(sheet) {
var groups = {};
var selectors = {};
/**
* Hydrate approximate record from any existing rules in the sheet.
*/
if (sheet != null) {
var group;
slice.call(sheet.cssRules).forEach(function (cssRule, i) {
var cssText = cssRule.cssText; // Create record of existing selectors and rules
if (cssText.indexOf('stylesheet-group') > -1) {
group = decodeGroupRule(cssRule);
groups[group] = {
start: i,
rules: [cssText]
};
} else {
var selectorText = getSelectorText(cssText);
if (selectorText != null) {
selectors[selectorText] = true;
groups[group].rules.push(cssText);
}
}
});
}
function sheetInsert(sheet, group, text) {
var orderedGroups = getOrderedGroups(groups);
var groupIndex = orderedGroups.indexOf(group);
var nextGroupIndex = groupIndex + 1;
var nextGroup = orderedGroups[nextGroupIndex]; // Insert rule before the next group, or at the end of the stylesheet
var position = nextGroup != null && groups[nextGroup].start != null ? groups[nextGroup].start : sheet.cssRules.length;
var isInserted = insertRuleAt(sheet, text, position);
if (isInserted) {
// Set the starting index of the new group
if (groups[group].start == null) {
groups[group].start = position;
} // Increment the starting index of all subsequent groups
for (var i = nextGroupIndex; i < orderedGroups.length; i += 1) {
var groupNumber = orderedGroups[i];
var previousStart = groups[groupNumber].start;
groups[groupNumber].start = previousStart + 1;
}
}
return isInserted;
}
var OrderedCSSStyleSheet = {
/**
* The textContent of the style sheet.
*/
getTextContent: function getTextContent() {
return getOrderedGroups(groups).map(function (group) {
var rules = groups[group].rules;
return rules.join('\n');
}).join('\n');
},
/**
* Insert a rule into the style sheet
*/
insert: function insert(cssText, groupValue) {
var group = Number(groupValue); // Create a new group.
if (groups[group] == null) {
var markerRule = encodeGroupRule(group); // Create the internal record.
groups[group] = {
start: null,
rules: [markerRule]
}; // Update CSSOM.
if (sheet != null) {
sheetInsert(sheet, group, markerRule);
}
} // selectorText is more reliable than cssText for insertion checks. The
// browser excludes vendor-prefixed properties and rewrites certain values
// making cssText more likely to be different from what was inserted.
var selectorText = getSelectorText(cssText);
if (selectorText != null && selectors[selectorText] == null) {
// Update the internal records.
selectors[selectorText] = true;
groups[group].rules.push(cssText); // Update CSSOM.
if (sheet != null) {
var isInserted = sheetInsert(sheet, group, cssText);
if (!isInserted) {
// Revert internal record change if a rule was rejected (e.g.,
// unrecognized pseudo-selector)
groups[group].rules.pop();
}
}
}
}
};
return OrderedCSSStyleSheet;
}
/**
* Helper functions
*/
function encodeGroupRule(group) {
return "[stylesheet-group=\"" + group + "\"]{}";
}
function decodeGroupRule(cssRule) {
return Number(cssRule.selectorText.split(/["']/)[1]);
}
function getOrderedGroups(obj) {
return Object.keys(obj).map(Number).sort(function (a, b) {
return a > b ? 1 : -1;
});
}
var pattern = /\s*([,])\s*/g;
function getSelectorText(cssText) {
var selector = cssText.split('{')[0].trim();
return selector !== '' ? selector.replace(pattern, '$1') : null;
}
function insertRuleAt(root, cssText, position) {
try {
// $FlowFixMe: Flow is missing CSSOM types needed to type 'root'.
root.insertRule(cssText, position);
return true;
} catch (e) {
// JSDOM doesn't support `CSSSMediaRule#insertRule`.
// Also ignore errors that occur from attempting to insert vendor-prefixed selectors.
return false;
}
}