vendor: 21,831 bytes, lines 1-647
1/*! ie11CustomProperties.js v4.1.0 | MIT License | https://git.io/fjXMN */ 2!function () { 3 'use strict'; 4 5 // check for support 6 var testEl = document.createElement('i'); 7 testEl.style.setProperty('--x', 'y'); 8 if (testEl.style.getPropertyValue('--x') === 'y' || !testEl.msMatchesSelector) return; 9 10 if (!Element.prototype.matches) Element.prototype.matches = Element.prototype.msMatchesSelector; 11 12 var listeners = [], 13 root = document, 14 Observer; 15 16 function qsa(el, selector){ 17 try { 18 return el.querySelectorAll(selector); 19 } catch(e) { 20 // console.warn('the Selector '+selector+' can not be parsed'); 21 return []; 22 } 23 } 24 function onElement (selector, callback) { 25 var listener = { 26 selector: selector, 27 callback: callback, 28 elements: new WeakMap(), 29 }; 30 var els = qsa(root, listener.selector), i=0, el; 31 while (el = els[i++]) { 32 listener.elements.set(el, true); 33 listener.callback.call(el, el); 34 } 35 listeners.push(listener); 36 if (!Observer) { 37 Observer = new MutationObserver(checkMutations); 38 Observer.observe(root, { 39 childList: true, 40 subtree: true 41 }); 42 } 43 checkListener(listener); 44 }; 45 function checkListener(listener, target) { 46 var i = 0, el, els = []; 47 try { 48 target && target.matches(listener.selector) && els.push(target); 49 } catch(e) {} 50 if (loaded) { // ok? check inside node on innerHTML - only when loaded 51 Array.prototype.push.apply(els, qsa(target || root, listener.selector)); 52 } 53 while (el = els[i++]) { 54 if (listener.elements.has(el)) continue; 55 listener.elements.set(el,true); 56 listener.callback.call(el, el); 57 } 58 } 59 function checkListeners(inside) { 60 var i = 0, listener; 61 while (listener = listeners[i++]) checkListener(listener, inside); 62 } 63 function checkMutations(mutations) { 64 var j = 0, i, mutation, nodes, target; 65 while (mutation = mutations[j++]) { 66 nodes = mutation.addedNodes, i = 0; 67 while (target = nodes[i++]) target.nodeType === 1 && checkListeners(target); 68 } 69 } 70 71 var loaded = false; 72 document.addEventListener('DOMContentLoaded', function () { 73 loaded = true; 74 }); 75 76 // svg polyfills 77 function copyProperty(prop, from, to){ 78 var desc = Object.getOwnPropertyDescriptor(from, prop); 79 Object.defineProperty(to, prop, desc); 80 } 81 if (!('classList' in Element.prototype)) { 82 copyProperty('classList', HTMLElement.prototype, Element.prototype); 83 } 84 if (!('innerHTML' in Element.prototype)) { 85 copyProperty('innerHTML', HTMLElement.prototype, Element.prototype); 86 } 87 if (!('runtimeStyle' in Element.prototype)) { // new 88 copyProperty('runtimeStyle', HTMLElement.prototype, Element.prototype); 89 } 90 if (!('sheet' in SVGStyleElement.prototype)) { 91 Object.defineProperty(SVGStyleElement.prototype, 'sheet', { 92 get:function(){ 93 var all = document.styleSheets, i=0, sheet; 94 while (sheet=all[i++]) { 95 if (sheet.ownerNode === this) return sheet; 96 } 97 98 } 99 }); 100 } 101 102 // main logic 103 var styles_of_getter_properties = {}; 104 var drawQueue = new Set(); 105 var collecting = false; 106 var drawing = false; 107 108 // cached regexps, better performance 109 var regFindSetters = /([\s{;])(--([A-Za-z0-9-_]*)\s*:([^;!}{]+)(!important)?)(?=\s*([;}]|$))/g; 110 var regFindGetters = /([{;]\s*)([A-Za-z0-9-_]+\s*:[^;}{]*var\([^!;}{]+)(!important)?(?=\s*([;}$]|$))/g; 111 var regRuleIEGetters = /-ieVar-([^:]+):/g 112 var regRuleIESetters = /-ie-([^};]+)/g 113 //const regHasVar = /var\(/; 114 var regPseudos = /:(hover|active|focus|target|visited|link|:before|:after|:first-letter|:first-line)/; 115 116 function foundStyle(el){ 117 if (el.ieCP_polyfilled) return; 118 if (el.ieCP_elementSheet) return; 119 if (!el.sheet) return; 120 if (el.href) { 121 return fetchCss(el.href, function (css) { 122 var newCss = rewriteCss(css); 123 if (css === newCss) return; 124 activateStyleElement(el, newCss); 125 }); 126 } 127 128 var css = el.innerHTML; 129 var newCss = rewriteCss(css); 130 if (css === newCss) return; 131 activateStyleElement(el, newCss); 132 } 133 onElement('style', foundStyle); 134 onElement('link[rel="stylesheet"]', foundStyle); 135 // immediate, to pass w3c-tests, bud its a bad idea 136 // addEventListener('DOMNodeInserted',function(e){ e.target.tagName === 'STYLE' && foundStyle(e.target); }); 137 138 139 140 onElement('[ie-style]', function (el) { 141 var newCss = rewriteCss('{'+el.getAttribute('ie-style')).substr(1); 142 el.style.cssText += ';'+ newCss; 143 var found = parseRewrittenStyle(el.style); 144 if (found.getters) addGetterElement(el, found.getters, '%styleAttr'); 145 if (found.setters) addSetterElement(el, found.setters); 146 }); 147 148 // ie has a bug, where unknown properties at pseudo-selectors are computed at the element 149 // #el::after { -content:'x'; } => getComputedStyle(el)['-content'] == 'x' 150 // should we add something like -ieVar-pseudo_after-content:'x'? 151 function rewriteCss(css) { 152 153 /* uncomment if spec finished and needed by someone 154 css = css.replace(/@property ([^{]+){([^}]+)}/, function($0, prop, body){ 155 prop = prop.trim(); 156 const declaration = {name:prop}; 157 body.split(';').forEach(function(pair){ 158 const x = pair.split(':'); 159 if (x[1]) declaration[ x[0].trim() ] = x[1]; 160 }); 161 declaration['inherits'] = declaration['inherits'].trim()==='true' ? true : false; 162 declaration['initialValue'] = declaration['initial-value']; 163 CSS.registerProperty(declaration) 164 return '/*\n @property ... removed \n*'+'/'; 165 }); 166 */ 167 return css.replace(regFindSetters, function($0, $1, $2, $3, $4, important){ 168 return $1+'-ie-'+(important?'â':'')+$3+':'+encodeValue($4); 169 }).replace(regFindGetters, function($0, $1, $2, important){ 170 return $1+'-ieVar-'+(important?'â':'')+$2+'; '+$2; // keep the original, so chaining works "--x:var(--y)" 171 }); 172 } 173 function encodeValue(value){ 174 return value; 175 return value.replace(/ /g,'â£'); 176 } 177 var keywords = {initial:1,inherit:1,revert:1,unset:1}; 178 function decodeValue(value){ 179 return value; 180 if (value===undefined) return; 181 value = value.replace(/â£/g,' '); 182 const trimmed = value.trim(); 183 if (keywords[trimmed]) return trimmed; 184 return value; 185 } 186 187 function parseRewrittenStyle(style) { // less memory then parameter cssText? 188 189 // ie11 can access unknown properties in stylesheets only if accessed a dashed known property 190 style['z-index'] === style && x(); // do something (compare and call) just for minifiers 191 192 const cssText = style.cssText; 193 var matchesGetters = cssText.match(regRuleIEGetters), j, match; 194 if (matchesGetters) { 195 var getters = []; // eg. [border,color] 196 for (j = 0; match = matchesGetters[j++];) { 197 let propName = match.slice(7, -1); 198 if (propName[0] === 'â') propName = propName.substr(1); 199 getters.push(propName); 200 201 if (!styles_of_getter_properties[propName]) styles_of_getter_properties[propName] = []; 202 styles_of_getter_properties[propName].push(style); 203 } 204 } 205 var matchesSetters = cssText.match(regRuleIESetters); 206 if (matchesSetters) { 207 var setters = {}; // eg. [--color:#fff, --padding:10px]; 208 for (j = 0; match = matchesSetters[j++];) { 209 let x = match.substr(4).split(':'); 210 let propName = x[0]; 211 let propValue = x[1]; 212 if (propName[0] === 'â') propName = propName.substr(1); 213 setters[propName] = propValue; 214 } 215 } 216 return {getters:getters, setters:setters}; 217 } 218 function activateStyleElement(style, css) { 219 style.sheet.cssText = css; 220 style.ieCP_polyfilled = true; 221 var rules = style.sheet.rules, i=0, rule; // cssRules = CSSRuleList, rules = MSCSSRuleList 222 while (rule = rules[i++]) { 223 const found = parseRewrittenStyle(rule.style); 224 if (found.getters) addGettersSelector(rule.selectorText, found.getters); 225 if (found.setters) addSettersSelector(rule.selectorText, found.setters); 226 227 // mediaQueries: redraw the hole document 228 // better add events for each element? 229 const media = rule.parentRule && rule.parentRule.media && rule.parentRule.media.mediaText; 230 if (media && (found.getters || found.setters)) { 231 matchMedia(media).addListener(function(){ 232 drawTree(document.documentElement) 233 }) 234 } 235 } 236 redrawStyleSheets(); 237 } 238 239 function addGettersSelector(selector, properties) { 240 selectorAddPseudoListeners(selector); 241 onElement(unPseudo(selector), function (el) { 242 addGetterElement(el, properties, selector); 243 drawElement(el); 244 }); 245 } 246 function addGetterElement(el, properties, selector) { 247 var i=0, prop, j; 248 const selectors = selector.split(','); // split grouped selectors 249 el.setAttribute('iecp-needed', true); 250 if (!el.ieCPSelectors) el.ieCPSelectors = {}; 251 while (prop = properties[i++]) { 252 for (j = 0; selector = selectors[j++];) { 253 const parts = selector.trim().split('::'); 254 if (!el.ieCPSelectors[prop]) el.ieCPSelectors[prop] = []; 255 el.ieCPSelectors[prop].push({ 256 selector: parts[0], 257 pseudo: parts[1] ? '::' + parts[1] : '' 258 }); 259 } 260 } 261 } 262 function addSettersSelector(selector, propVals) { 263 selectorAddPseudoListeners(selector); 264 onElement(unPseudo(selector), function (el) { 265 addSetterElement(el, propVals); 266 }); 267 } 268 function addSetterElement(el, propVals) { 269 if (!el.ieCP_setters) el.ieCP_setters = {}; 270 for (var prop in propVals) { // eg. {foo:#fff, bar:baz} 271 el.ieCP_setters['--' + prop] = 1; 272 } 273 drawTree(el); 274 } 275 276 function redrawStyleSheets() { 277 for (var prop in styles_of_getter_properties) { 278 let styles = styles_of_getter_properties[prop]; 279 for (var i=0, style; style=styles[i++];) { 280 if (style.owningElement) continue; 281 var value = style['-ieVar-'+prop]; 282 if (!value) continue; 283 value = styleComputeValueWidthVars(getComputedStyle(document.documentElement), value); 284 if (value === '') continue; 285 try { 286 style[prop] = value; 287 } catch(e) {} 288 } 289 } 290 } 291 292 293 var pseudos = { 294 hover:{ 295 on:'mouseenter', 296 off:'mouseleave' 297 }, 298 focus:{ 299 on:'focusin', 300 off:'focusout' 301 }, 302 active:{ 303 on:'CSSActivate', 304 off:'CSSDeactivate' 305 }, 306 }; 307 function selectorAddPseudoListeners(selector){ 308 // ie11 has the strange behavoir, that groups of selectors are individual rules, but starting with the full selector: 309 // td, th, button { color:red } results in this rules: 310 // "td, th, button" | "th, th" | "th" 311 selector = selector.split(',')[0]; 312 for (var pseudo in pseudos) { 313 var parts = selector.split(':'+pseudo); 314 if (parts.length > 1) { 315 var ending = parts[1].match(/^[^\s]*/); // ending elementpart of selector (used for not(:active)) 316 let sel = unPseudo(parts[0]+ending); 317 const listeners = pseudos[pseudo]; 318 onElement(sel, function (el) { 319 el.addEventListener(listeners.on, drawTreeEvent); 320 el.addEventListener(listeners.off, drawTreeEvent); 321 }); 322 } 323 } 324 } 325 326 var CSSActive = null; 327 document.addEventListener('mousedown',function(e){ 328 setTimeout(function(){ 329 if (e.target === document.activeElement) { 330 var evt = document.createEvent('Event'); 331 evt.initEvent('CSSActivate', true, true); 332 CSSActive = e.target; 333 CSSActive.dispatchEvent(evt); 334 } 335 }) 336 }); 337 document.addEventListener('mouseup',function(){ 338 if (CSSActive) { 339 var evt = document.createEvent('Event'); 340 evt.initEvent('CSSDeactivate', true, true); 341 CSSActive.dispatchEvent(evt); 342 CSSActive = null; 343 } 344 }); 345 346 function unPseudo(selector){ 347 return selector.replace(regPseudos,'').replace(':not()',''); 348 } 349 350 // draw queue 351 function drawElement(el){ 352 drawQueue.add(el); 353 if (collecting) return; 354 collecting = true; 355 requestAnimationFrame(function(){ 356 //setImmediate(function(){ 357 collecting = false; 358 drawing = true; 359 drawQueue.forEach(_drawElement); 360 drawQueue.clear(); 361 setTimeout(function(){ // mutationObserver will trigger delayed, requestAnimationFrame will miss some changes 362 drawing = false; 363 }) 364 }) 365 } 366 367 var uniqueCounter = 0; 368 function _drawElement(el) { 369 if (!el.ieCP_unique) { // use el.uniqueNumber? but needs class for the css-selector => test performance 370 el.ieCP_unique = ++uniqueCounter; 371 el.classList.add('iecp-u' + el.ieCP_unique); 372 } 373 var style = getComputedStyle(el); 374 let css = ''; 375 376 el.runtimeStyle.cssText = ''; // new 377 378 for (var prop in el.ieCPSelectors) { 379 var important = style['-ieVar-â' + prop]; 380 let valueWithVar = important || style['-ieVar-' + prop]; 381 if (!valueWithVar) continue; // todo, what if '0' 382 var details = {}; 383 var value = styleComputeValueWidthVars(style, valueWithVar, details); 384 //if (value==='initial') value = initials[prop]; 385 if (important) value += ' !important'; 386 for (var i=0, item; item=el.ieCPSelectors[prop][i++];) { // todo: split and use requestAnimationFrame? 387 if (item.selector === '%styleAttr') el.style[prop] = value; // i dont know why but i initial have to set style also (seen in demo) 388 if (!important && details.allByRoot !== false) continue; // dont have to draw root-properties 389 if (item.pseudo) { 390 css += item.selector + '.iecp-u' + el.ieCP_unique + item.pseudo + '{' + prop + ':' + value + '}\n'; 391 } else { 392 el.runtimeStyle[prop] = value; // new 393 } 394 } 395 } 396 elementSetCss(el, css); 397 } 398 function elementSetCss(el, css){ 399 if (!el.ieCP_styleEl && css) { 400 const styleEl = document.createElement('style'); 401 styleEl.ieCP_elementSheet = 1; 402 //el.appendChild(styleEl); // yes! self-closing tags can have style as children, but - if i set innerHTML, the stylesheet is lost 403 document.head.appendChild(styleEl); 404 el.ieCP_styleEl = styleEl; 405 } 406 if (el.ieCP_styleEl) el.ieCP_styleEl.innerHTML = css; 407 } 408 /* */ 409 410 function drawTree(target) { 411 if (!target) return; 412 413 target === document.documentElement && redrawStyleSheets(); // new 414 415 var els = target.querySelectorAll('[iecp-needed]'); 416 if (target.hasAttribute && target.hasAttribute('iecp-needed')) drawElement(target); // self 417 for (var i = 0, el; el = els[i++];) drawElement(el); // tree 418 } 419 function drawTreeEvent(e) { 420 drawTree(e.target) 421 } 422 423 function findVars(str, cb){ // css value parser 424 let level=0, openedLevel=null, lastPoint=0, newStr = '', i=0, char, insideCalc; 425 while (char=str[i++]) { 426 if (char === '(') { 427 ++level; 428 if (openedLevel === null && str[i-4]+str[i-3]+str[i-2] === 'var') { 429 openedLevel = level; 430 newStr += str.substring(lastPoint, i-4); 431 lastPoint = i; 432 } 433 if (str[i-5]+str[i-4]+str[i-3]+str[i-2] === 'calc') { 434 insideCalc = level; 435 } 436 } 437 if (char === ')' && openedLevel === level) { 438 let variable = str.substring(lastPoint, i-1).trim(), fallback; 439 let x = variable.indexOf(','); 440 if (x!==-1) { 441 fallback = variable.slice(x+1); 442 variable = variable.slice(0,x); 443 } 444 newStr += cb(variable, fallback, insideCalc); 445 lastPoint = i; 446 openedLevel = null; 447 } 448 if (char === ')') { 449 --level; 450 if (insideCalc === level) insideCalc = null; 451 } 452 } 453 newStr += str.substring(lastPoint); 454 return newStr; 455 } 456 function styleComputeValueWidthVars(style, valueWithVars, details){ 457 return findVars(valueWithVars, function(variable, fallback, insideCalc){ 458 var value = style.getPropertyValue(variable); 459 if (insideCalc) value = value.replace(/^calc\(/, '('); // prevent nested calc 460 if (details && style.lastPropertyServedBy !== document.documentElement) details.allByRoot = false; 461 if (value==='' && fallback) value = styleComputeValueWidthVars(style, fallback, details); 462 return value; 463 }); 464 } 465 466 // mutation listener 467 var observer = new MutationObserver(function(mutations) { 468 if (drawing) return; 469 for (var i=0, mutation; mutation=mutations[i++];) { 470 if (mutation.attributeName === 'iecp-needed') continue; // why? 471 // recheck all selectors if it targets new elements? 472 drawTree(mutation.target); 473 } 474 }); 475 setTimeout(function(){ 476 observer.observe(document,{attributes: true, subtree: true }); 477 }) 478 479 // :target listener 480 var oldHash = location.hash 481 addEventListener('hashchange',function(e){ 482 var newEl = document.getElementById(location.hash.substr(1)); 483 if (newEl) { 484 var oldEl = document.getElementById(oldHash.substr(1)); 485 drawTree(newEl); 486 drawTree(oldEl); 487 } else { 488 drawTree(document); 489 } 490 oldHash = location.hash; 491 }); 492 493 // add owningElement to Element.style 494 var descriptor = Object.getOwnPropertyDescriptor(HTMLElement.prototype, 'style'); 495 var styleGetter = descriptor.get; 496 descriptor.get = function () { 497 const style = styleGetter.call(this); 498 style.owningElement = this; 499 return style; 500 } 501 Object.defineProperty(HTMLElement.prototype, 'style', descriptor); 502 503 // add computedFor to computed style-objects 504 var originalGetComputed = getComputedStyle; 505 window.getComputedStyle = function (el) { 506 var style = originalGetComputed.apply(this, arguments); 507 style.computedFor = el; 508 //style.pseudoElt = pseudoElt; //not needed at the moment 509 return style; 510 } 511 512 // getPropertyValue / setProperty hooks 513 var StyleProto = CSSStyleDeclaration.prototype; 514 515 var oldGetP = StyleProto.getPropertyValue; 516 StyleProto.getPropertyValue = function (property) { 517 this.lastPropertyServedBy = false; 518 property = property.trim(); 519 520 /* * 521 if (this.owningElement) { 522 const ieProperty = '-ieVar-'+property; 523 const iePropertyImportant = '-ieVar-â'+property; 524 let value = this[iePropertyImportant] || this[ieProperty]; 525 if (value !== undefined) { 526 // todo, test if syntax valid 527 return value; 528 } 529 } 530 /* */ 531 532 if (property[0] !== '-' || property[1] !== '-') return oldGetP.apply(this, arguments); 533 const undashed = property.substr(2); 534 const ieProperty = '-ie-'+undashed; 535 const iePropertyImportant = '-ie-â'+undashed; 536 let value = decodeValue(this[iePropertyImportant] || this[ieProperty]); 537 538 if (this.computedFor) { // computedStyle 539 if (value !== undefined && !inheritingKeywords[value]) { 540 //if (regHasVar.test(value)) // todo: to i need this check?!!! i think its faster without 541 value = styleComputeValueWidthVars(this, value); 542 this.lastPropertyServedBy = this.computedFor; 543 } else { // inherited 544 if (inheritingKeywords[value] || !register[property] || register[property].inherits) { 545 //let el = this.pseudoElt ? this.computedFor : this.computedFor.parentNode; 546 let el = this.computedFor.parentNode; 547 while (el && el.nodeType === 1) { 548 // without, it affects performance: 1000 els inside 100 parents: 1000ms (instead of 600ms) is it acceptable? 549 // would also remove complexity, because i can remove the ieCP_setters property 550 //if (el.ieCP_setters && el.ieCP_setters[property]) { 551 // i could make 552 // value = el.nodeType ? getComputedStyle(this.computedFor.parentNode).getPropertyValue(property) 553 // but i fear performance, stupid? 554 var style = getComputedStyle(el); 555 var tmpVal = decodeValue(style[iePropertyImportant] || style[ieProperty]); 556 if (tmpVal !== undefined) { 557 // calculated style from current element not from the element the value was inherited from! (style, value) 558 //value = tmpVal; if (regHasVar.test(tmpVal)) // todo: to i need this check?!!! i think its faster without 559 value = styleComputeValueWidthVars(this, tmpVal); 560 this.lastPropertyServedBy = el; 561 break; 562 } 563 //} 564 el = el.parentNode; 565 } 566 } 567 } 568 if (value==='initial') return ''; 569 } 570 //if ((value === undefined || value === 'initial') && register[property]) value = register[property].initialValue; // todo? 571 if (value === undefined && register[property]) value = register[property].initialValue; 572 if (value === undefined) return ''; 573 return value; 574 }; 575 var inheritingKeywords = {inherit:1,revert:1,unset:1}; 576 577 var oldSetP = StyleProto.setProperty; 578 StyleProto.setProperty = function (property, value, prio) { 579 if (property[0] !== '-' || property[1] !== '-') return oldSetP.apply(this, arguments); 580 const el = this.owningElement; 581 if (el) { 582 if (!el.ieCP_setters) el.ieCP_setters = {}; 583 el.ieCP_setters[property] = 1; 584 } 585 property = '-ie-'+(prio==='important'?'â':'') + property.substr(2); 586 this.cssText += '; ' + property + ':' + encodeValue(value) + ';'; 587 //this[property] = value; 588// el === document.documentElement && redrawStyleSheets(); new in drawTree 589 el && drawTree(el); // its delayed internal 590 }; 591 592 593 /* 594 var descriptor = Object.getOwnPropertyDescriptor(StyleProto, 'cssText'); 595 var cssTextGetter = descriptor.get; 596 var cssTextSetter = descriptor.set; 597 // descriptor.get = function () { 598 // const style = styleGetter.call(this); 599 // style.owningElement = this; 600 // return style; 601 // } 602 descriptor.set = function (css) { 603 var el = this.owningElement; 604 if (el) { 605 css = rewriteCss('{'+css).substr(1); 606 cssTextSetter.call(this, css); 607 var found = parseRewrittenStyle(this); 608 if (found.getters) addGetterElement(el, found.getters, '%styleAttr'); 609 if (found.setters) addSetterElement(el, found.setters); 610 return; 611 } 612 return cssTextSetter.call(this, css); 613 } 614 Object.defineProperty(StyleProto, 'cssText', descriptor); 615 */ 616 617 618 if (!window.CSS) window.CSS = {}; 619 var register = {} 620 CSS.registerProperty = function(options){ 621 register[options.name] = options; 622 } 623 624 // fix "initial" keyword with generated custom properties, this is not supported ad all by ie, should i make a separate "inherit"-polyfill? 625 /* 626 const computed = getComputedStyle(document.documentElement) 627 const initials = {}; 628 for (let i in computed) { 629 initials[i.replace(/([A-Z])/, function(x){ return '-'+x.toLowerCase(x) })] = computed[i]; 630 } 631 initials['display'] = 'inline'; 632 */ 633 634 // utils 635 function fetchCss(url, callback) { 636 var request = new XMLHttpRequest(); 637 request.open('GET', url); 638 request.overrideMimeType('text/css'); 639 request.onload = function () { 640 if (request.status >= 200 && request.status < 400) { 641 callback(request.responseText); 642 } 643 }; 644 request.send(); 645 } 646 647}();
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.