own page load microopt: reduce blocking on i18n
* localStorage cache is faster than chrome.i18n.get * TreeWalker is faster than tHTML for removing extraneous whitespace * simple for() is faster than for-of with [...iterable]
This commit is contained in:
		
							parent
							
								
									4eae87e606
								
							
						
					
					
						commit
						b3b1d4a628
					
				|  | @ -37,6 +37,18 @@ function webNavigationListener(method, data) { | ||||||
|   }); |   }); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | // reset i18n cache on language change
 | ||||||
|  | 
 | ||||||
|  | setTimeout(() => { | ||||||
|  |   const {browserUIlanguage} = tryJSONparse(localStorage.L10N) || {}; | ||||||
|  |   const UIlang = chrome.i18n.getUILanguage(); | ||||||
|  |   if (browserUIlanguage != UIlang) { | ||||||
|  |     localStorage.L10N = JSON.stringify({ | ||||||
|  |       browserUIlanguage: UIlang, | ||||||
|  |     }); | ||||||
|  |   } | ||||||
|  | }); | ||||||
|  | 
 | ||||||
| // messaging
 | // messaging
 | ||||||
| 
 | 
 | ||||||
| chrome.runtime.onMessage.addListener(onRuntimeMessage); | chrome.runtime.onMessage.addListener(onRuntimeMessage); | ||||||
|  |  | ||||||
|  | @ -5,10 +5,14 @@ tDocLoader(); | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| function t(key, params) { | function t(key, params) { | ||||||
|   const s = chrome.i18n.getMessage(key, params); |   const cache = !params && t.cache[key]; | ||||||
|  |   const s = cache || chrome.i18n.getMessage(key, params); | ||||||
|   if (s == '') { |   if (s == '') { | ||||||
|     throw `Missing string "${key}"`; |     throw `Missing string "${key}"`; | ||||||
|   } |   } | ||||||
|  |   if (!params && !cache) { | ||||||
|  |     t.cache[key] = s; | ||||||
|  |   } | ||||||
|   return s; |   return s; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -35,24 +39,38 @@ function tHTML(html) { | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| function tNodeList(nodes) { | function tNodeList(nodes) { | ||||||
|   for (const node of [...nodes]) { |   const PREFIX = 'i18n-'; | ||||||
|  |   for (let n = nodes.length; --n >= 0;) { | ||||||
|  |     const node = nodes[n]; | ||||||
|     // skip non-ELEMENT_NODE
 |     // skip non-ELEMENT_NODE
 | ||||||
|     if (node.nodeType != 1) { |     if (node.nodeType != 1) { | ||||||
|       continue; |       continue; | ||||||
|     } |     } | ||||||
|     if (node.localName == 'template') { |     if (node.localName == 'template') { | ||||||
|  |       const elements = node.content.querySelectorAll('*'); | ||||||
|  |       tNodeList(elements); | ||||||
|  |       template[node.dataset.id] = elements[0]; | ||||||
|       // compress inter-tag whitespace to reduce number of DOM nodes by 25%
 |       // compress inter-tag whitespace to reduce number of DOM nodes by 25%
 | ||||||
|       template[node.dataset.id] = tHTML(node.innerHTML); |       const walker = document.createTreeWalker(elements[0], NodeFilter.SHOW_TEXT); | ||||||
|  |       const toRemove = []; | ||||||
|  |       while (walker.nextNode()) { | ||||||
|  |         const textNode = walker.currentNode; | ||||||
|  |         if (!textNode.nodeValue.trim()) { | ||||||
|  |           toRemove.push(textNode); | ||||||
|  |         } | ||||||
|  |       } | ||||||
|  |       toRemove.forEach(el => el.remove()); | ||||||
|       continue; |       continue; | ||||||
|     } |     } | ||||||
|     for (const attr of [...node.attributes]) { |     for (let a = node.attributes.length; --a >= 0;) { | ||||||
|       let name = attr.nodeName; |       const attr = node.attributes[a]; | ||||||
|       if (name.indexOf('i18n-') != 0) { |       const name = attr.nodeName; | ||||||
|  |       if (!name.startsWith(PREFIX)) { | ||||||
|         continue; |         continue; | ||||||
|       } |       } | ||||||
|       name = name.substr(5); // 'i18n-'.length
 |       const type = name.substr(PREFIX.length); | ||||||
|       const value = t(attr.value); |       const value = t(attr.value); | ||||||
|       switch (name) { |       switch (type) { | ||||||
|         case 'text': |         case 'text': | ||||||
|           node.insertBefore(document.createTextNode(value), node.firstChild); |           node.insertBefore(document.createTextNode(value), node.firstChild); | ||||||
|           break; |           break; | ||||||
|  | @ -63,15 +81,17 @@ function tNodeList(nodes) { | ||||||
|           node.insertAdjacentHTML('afterbegin', value); |           node.insertAdjacentHTML('afterbegin', value); | ||||||
|           break; |           break; | ||||||
|         default: |         default: | ||||||
|           node.setAttribute(name, value); |           node.setAttribute(type, value); | ||||||
|       } |       } | ||||||
|       node.removeAttribute(attr.nodeName); |       node.removeAttribute(name); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| function tDocLoader() { | function tDocLoader() { | ||||||
|  |   t.cache = tryJSONparse(localStorage.L10N) || {}; | ||||||
|  |   const cacheLength = Object.keys(t.cache).length; | ||||||
|   // localize HEAD
 |   // localize HEAD
 | ||||||
|   tNodeList(document.getElementsByTagName('*')); |   tNodeList(document.getElementsByTagName('*')); | ||||||
| 
 | 
 | ||||||
|  | @ -85,6 +105,9 @@ function tDocLoader() { | ||||||
|   const onLoad = () => { |   const onLoad = () => { | ||||||
|     tDocLoader.stop(); |     tDocLoader.stop(); | ||||||
|     process(observer.takeRecords()); |     process(observer.takeRecords()); | ||||||
|  |     if (cacheLength != Object.keys(t.cache).length) { | ||||||
|  |       localStorage.L10N = JSON.stringify(t.cache); | ||||||
|  |     } | ||||||
|   }; |   }; | ||||||
|   tDocLoader.start = () => { |   tDocLoader.start = () => { | ||||||
|     observer.observe(document, {subtree: true, childList: true}); |     observer.observe(document, {subtree: true, childList: true}); | ||||||
|  |  | ||||||
|  | @ -4,8 +4,8 @@ | ||||||
|   <title i18n-text-append="optionsHeading">Stylus </title> |   <title i18n-text-append="optionsHeading">Stylus </title> | ||||||
|   <link rel="stylesheet" href="index.css"> |   <link rel="stylesheet" href="index.css"> | ||||||
|   <script src="/dom.js"></script> |   <script src="/dom.js"></script> | ||||||
|   <script src="/localization.js"></script> |  | ||||||
|   <script src="/messaging.js"></script> |   <script src="/messaging.js"></script> | ||||||
|  |   <script src="/localization.js"></script> | ||||||
|   <script src="/prefs.js"></script> |   <script src="/prefs.js"></script> | ||||||
|   <script src="/apply.js"></script> |   <script src="/apply.js"></script> | ||||||
| </head> | </head> | ||||||
|  |  | ||||||
|  | @ -56,8 +56,8 @@ | ||||||
|   </template> |   </template> | ||||||
| 
 | 
 | ||||||
|   <script src="dom.js"></script> |   <script src="dom.js"></script> | ||||||
|   <script src="localization.js"></script> |  | ||||||
|   <script src="messaging.js"></script> |   <script src="messaging.js"></script> | ||||||
|  |   <script src="localization.js"></script> | ||||||
|   <script src="prefs.js"></script> |   <script src="prefs.js"></script> | ||||||
|   <script src="apply.js"></script> |   <script src="apply.js"></script> | ||||||
|   <script src="popup.js"></script> |   <script src="popup.js"></script> | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue
	
	Block a user