Style shadowRoot

Allow stylus to target elements in shadow-root

目前为 2023-02-10 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name Style shadowRoot
  3. // @namespace https://github.com/Procyon-b
  4. // @version 0.1
  5. // @description Allow stylus to target elements in shadow-root
  6. // @author Achernar
  7. // @match http://NOTHING/
  8. // @run-at document-start
  9. // @grant none
  10. // ==/UserScript==
  11.  
  12. (function() {
  13. "use strict";
  14. var id=Date.now()
  15.  
  16. //console.info('%c shadow CSS ', 'background: lightblue', {document}, {'body':document ? document.body:'-'}, {dE:document ? document.documentElement : '-'})
  17.  
  18. // patch attachShadow()
  19. var AS=HTMLElement.prototype.attachShadow;
  20. //console.info('%c shadow CSS ', 'background: #6699FF', id,{AS},location.href);
  21.  
  22. HTMLElement.prototype.attachShadow=function(m){/*[native */
  23. var e=this;
  24. //console.info('%c shadow CSS ', 'background: #6699FF', id,{e, m})
  25. e.sr=AS.call(e,m);
  26. shadows.push(e.sr);
  27. inject(e.sr);
  28. shadowMod(e.sr);
  29. return e.sr;
  30. }
  31. // END patch
  32.  
  33. var sheets={}, order=[],
  34. shadows=[], sCSS={}, injCSS='', oldInjCSS='';
  35.  
  36. // catch stylus injections
  37. const obs=new MutationObserver(function(muts){
  38. for (let mut of muts) {
  39. //console.info('%c shadow CSS ', 'background: #6699FF', {mut});
  40. for (let n of mut.addedNodes) {
  41. if ( (n.tagName == 'STYLE') && (n.className == 'stylus') && n.id.startsWith('stylus-') ) {
  42. addCSS(n);
  43. }
  44. }
  45.  
  46. for (let n of mut.removedNodes) {
  47. if ( (n.tagName == 'STYLE') && (n.className == 'stylus') && n.id.startsWith('stylus-') ) {
  48. remCSS(n);
  49. }
  50. }
  51. }
  52. });
  53. // END catch
  54.  
  55. function init() {
  56. if (!document.documentElement) {
  57. setTimeout(init, 0);
  58. return;
  59. }
  60.  
  61. obs.observe(document.documentElement, {attributes: false, subtree: false, childList: true });
  62. // check for stylesheets
  63. document.documentElement.querySelectorAll('style[id^="stylus-"].stylus').forEach( (e) => addCSS(e,2) );
  64. }
  65.  
  66. init();
  67.  
  68. // get only shadow-specific css
  69. function parseSheet(s) {
  70. var R=s.sheet.cssRules, sels, sel, shCSS='';
  71. for (let i=0; i < R.length; i++) {
  72. sel=R[i].selectorText;
  73. //console.info('%c shadow CSS ', 'background: #6699FF', sel);
  74. if (!sel.includes(':host')) continue;
  75. //console.info('%c shadow CSS ', 'background: #6699FF', {sels, f:sels.filter(function(v){ return v.includes(':host') })} );
  76. sels=sel.split(',').filter(function(v){ return v.includes(':host') }).join(',');
  77. shCSS+=sels.trim()+ R[i].cssText.substr(sel.length)+'\n';
  78. }
  79.  
  80. if (shCSS) shCSS='/*'+s.id+'*/\n'+shCSS;
  81. //console.info('%c shadow CSS ', 'background: lightgreen', shCSS)
  82. return shCSS;
  83. }
  84.  
  85. // inject in this shadow
  86. function inject(e) {
  87. if (injCSS == '') {
  88. if (e._inj_) {
  89. e._inj_.remove();
  90. delete e._inj_;
  91. }
  92. return;
  93. }
  94. if (!e._inj_ || !e._inj_.parentNode ) {
  95. let s=e._inj_=document.createElement('style');
  96. s.className='sh-stylus';
  97. e.appendChild(s);
  98. }
  99. e._inj_.textContent=injCSS;
  100. }
  101.  
  102. // inject style
  103. function injectAll() {
  104. for (let i=0; i < shadows.length; i++) inject(shadows[i]);
  105. }
  106.  
  107. // create & inject style in shadows
  108. function injUpd() {
  109. oldInjCSS=injCSS;
  110. injCSS='';
  111. for (let i=0; i < order.length; i++) injCSS+=sCSS[order[i]];
  112. if (injCSS !== oldInjCSS) injectAll();
  113. }
  114.  
  115. // get stylus stylesheet order
  116. function getStylusOrder() {
  117. order=[];
  118. for (let i=0; i < document.styleSheets.length; i++) {
  119. let s=document.styleSheets[i].ownerNode;
  120. if (s.className == 'stylus') order.push(s.id);
  121. }
  122. }
  123.  
  124. // handle new stylesheet
  125. function addCSS(n, k=1) {
  126. if (n.id in sheets) return;
  127. sheets[n.id]=n;
  128. if (n.__k__) ;//console.info('%c shadow CSS ', 'background: #6699FF', 'already known', n.id, n.__k__, k);
  129. else {
  130. n.__k__=k;
  131.  
  132. // monitor modification
  133. const obs=new MutationObserver(function(muts){
  134. if (muts[0].addedNodes.length) {
  135. // === reparse stylesheet and reinject it
  136. sCSS[muts[0].target.id]=parseSheet(muts[0].target);
  137. injUpd();
  138. }
  139. });
  140. obs.observe(n, {attributes: false, subtree: false, childList: true });
  141. }
  142.  
  143. sCSS[n.id]=parseSheet(n);
  144. getStylusOrder();
  145. injUpd();
  146. }
  147.  
  148. // handle stylesheet removal
  149. function remCSS(n) {
  150. // is it only moved?
  151. if (n.parentNode == null) delete sheets[n.id];
  152. getStylusOrder();
  153. injUpd();
  154. }
  155.  
  156. // monitor shadow modification
  157. function shadowMod(e) {
  158. const obs=new MutationObserver(function(muts){
  159. if (e._inj_ && (!e._inj_.parentNode || e._inj_.nextElementSibling) ) e.appendChild(e._inj_);
  160. });
  161. obs.observe(e, {attributes: false, subtree: false, childList: true });
  162. }
  163.  
  164. })();