aboutsummaryrefslogtreecommitdiffstats
path: root/node_modules/jade/bin
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/jade/bin')
0 files changed, 0 insertions, 0 deletions
color: #000000; background-color: #ffffc0; padding-left: 5px; padding-right: 5px; } .highlight .hll { background-color: #ffffcc } .highlight .c { color: #888 } /* Comment */ .highlight .err { color: #A61717; background-color: #E3D2D2 } /* Error */ .highlight .k { color: #080; font-weight: bold } /* Keyword */ .highlight .ch { color: #888 } /* Comment.Hashbang */ .highlight .cm { color: #888 } /* Comment.Multiline */ .highlight .cp { color: #C00; font-weight: bold } /* Comment.Preproc */ .highlight .cpf { color: #888 } /* Comment.PreprocFile */ .highlight .c1 { color: #888 } /* Comment.Single */ .highlight .cs { color: #C00; font-weight: bold; background-color: #FFF0F0 } /* Comment.Special */ .highlight .gd { color: #000; background-color: #FDD } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #A00 } /* Generic.Error */ .highlight .gh { color: #333 } /* Generic.Heading */ .highlight .gi { color: #000; background-color: #DFD } /* Generic.Inserted */ .highlight .go { color: #888 } /* Generic.Output */ .highlight .gp { color: #555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666 } /* Generic.Subheading */ .highlight .gt { color: #A00 } /* Generic.Traceback */ .highlight .kc { color: #080; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #080; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #080; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #080 } /* Keyword.Pseudo */ .highlight .kr { color: #080; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #00D; font-weight: bold } /* Literal.Number */ .highlight .s { color: #D20; background-color: #FFF0F0 } /* Literal.String */ .highlight .na { color: #369 } /* Name.Attribute */ .highlight .nb { color: #038 } /* Name.Builtin */ .highlight .nc { color: #B06; font-weight: bold } /* Name.Class */ .highlight .no { color: #036; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555 } /* Name.Decorator */ .highlight .ne { color: #B06; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #06B; font-weight: bold } /* Name.Function */ .highlight .nl { color: #369; font-style: italic } /* Name.Label */ .highlight .nn { color: #B06; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #369; font-weight: bold } /* Name.Property */ .highlight .nt { color: #B06; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #369 } /* Name.Variable */ .highlight .ow { color: #080 } /* Operator.Word */ .highlight .w { color: #BBB } /* Text.Whitespace */ .highlight .mb { color: #00D; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #00D; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #00D; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #00D; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #00D; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #D20; background-color: #FFF0F0 } /* Literal.String.Affix */ .highlight .sb { color: #D20; background-color: #FFF0F0 } /* Literal.String.Backtick */ .highlight .sc { color: #D20; background-color: #FFF0F0 } /* Literal.String.Char */ .highlight .dl { color: #D20; background-color: #FFF0F0 } /* Literal.String.Delimiter */ .highlight .sd { color: #D20; background-color: #FFF0F0 } /* Literal.String.Doc */ .highlight .s2 { color: #D20; background-color: #FFF0F0 } /* Literal.String.Double */ .highlight .se { color: #04D; background-color: #FFF0F0 } /* Literal.String.Escape */ .highlight .sh { color: #D20; background-color: #FFF0F0 } /* Literal.String.Heredoc */ .highlight .si { color: #33B; background-color: #FFF0F0 } /* Literal.String.Interpol */ .highlight .sx { color: #2B2; background-color: #F0FFF0 } /* Literal.String.Other */ .highlight .sr { color: #080; background-color: #FFF0FF } /* Literal.String.Regex */ .highlight .s1 { color: #D20; background-color: #FFF0F0 } /* Literal.String.Single */ .highlight .ss { color: #A60; background-color: #FFF0F0 } /* Literal.String.Symbol */ .highlight .bp { color: #038 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #06B; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #369 } /* Name.Variable.Class */ .highlight .vg { color: #D70 } /* Name.Variable.Global */ .highlight .vi { color: #33B } /* Name.Variable.Instance */ .highlight .vm { color: #369 } /* Name.Variable.Magic */ .highlight .il { color: #00D; font-weight: bold } /* Literal.Number.Integer.Long */
// Exercise the real input/coast handlers with controlled frame timing.
import assert from 'node:assert/strict';
import { runInNewContext } from 'node:vm';
const source = await Bun.file('src/app.js').text();
const start = source.indexOf('(() => {', source.indexOf('// The feed scrolls like a phone'));
const end = source.indexOf("\nwindow.addEventListener('load'", start);
assert(start > 0 && end > start);
function fixture(inside = '', { feedOnPage = true } = {}) {
  let now = 100, id = 0;
  const handlers = new Map(), frames = new Map(), steps = [];
  const window = { scrollY: 5000,
    addEventListener: (name, fn) => handlers.set(name, fn),
    scrollBy: ({ top }) => { steps.push(top); window.scrollY += top; },
  };
  // the page shows a feed (or, for a thread case, a thread) unless told otherwise
  const document = { body: {}, addEventListener() {}, removeEventListener() {},
    querySelector: (selector) => (feedOnPage && selector.split(/,\s*/).some((p) => p === (inside === '.thread-page' ? '.thread-page' : '.feed-page')) ? {} : null) };
  // the target sits inside the given element (over the posts unless it names something else)
  const target = { closest: (selector) => selector.split(/,\s*/).some((p) => p === inside) ? target : null,
    scrollHeight: 100, clientHeight: 100, parentElement: document.body };
  runInNewContext(source.slice(start, end), { window, document,
    matchMedia: () => ({ matches: false }), performance: { now: () => now },
    requestAnimationFrame: (fn) => { frames.set(++id, fn); return id; },
    cancelAnimationFrame: (id) => frames.delete(id), setTimeout, clearTimeout,
  });
  return {
    get y() { return window.scrollY; }, steps,
    wheel(dir) { handlers.get('wheel')({ deltaY: dir * 100, wheelDeltaY: -dir * 120,
      deltaMode: 0, deltaX: 0, target, timeStamp: now, preventDefault() {} }); },
    advance(ms) { now += ms; },
    frame(stamp = now) { const callbacks = [...frames.values()]; frames.clear(); for (const fn of callbacks) fn(stamp); },
  };
}
const settle = (f, n = 70) => { for (let i = 0; i < n; i++) { f.advance(17); f.frame(); } };
for (const dir of [1, -1]) {
  const f = fixture();
  f.wheel(dir); f.advance(17); f.frame();
  f.wheel(dir); f.advance(17); f.frame();
  // A busy main thread has missed several frames when the turn back arrives.
  f.advance(120);
  const before = f.y;
  // [flying: the first turn back is a brake]
  f.wheel(-dir);
  assert.equal(f.y, before, 'the brake notch moves nothing by itself');
  f.frame(0); // a queued animation frame can carry an older timestamp
  assert.equal(f.y, before, 'old frame time moves nothing');
  settle(f, 30);
  const over = (f.y - before) * dir;
  assert(over >= 0 && over < 120, 'the brake eases the flight out within a short way, never the other way: ' + over);
  // [the next notch back: the gentle start]
  const stopped = f.y;
  f.wheel(-dir);
  assert((stopped - f.y) * dir > 0 && (stopped - f.y) * dir < 20, 'the gentle start responds at once with a small step');
  settle(f);
  const back = (stopped - f.y) * dir;
  assert(back > 130 && back < 170, 'the notch after the brake creeps back half a notch: ' + back);
  // [then the wind-up]
  { const y = f.y; f.wheel(-dir); f.advance(60); f.frame(); f.wheel(-dir); settle(f);
    const on = (y - f.y) * dir; assert(on > 600, 'the notches after wind up: ' + on); }
  // The gentle start waits a while for its notch: a second and a half after the brake, still gentle.
  { f.advance(2500); f.wheel(dir); f.advance(17); f.frame(); f.wheel(dir); f.advance(120); f.frame();
    f.wheel(-dir); settle(f, 30); f.advance(1500); const y = f.y; f.wheel(-dir); settle(f);
    const s = (y - f.y) * dir; assert(s > 130 && s < 170, 'a notch back a while after the brake is still the gentle start: ' + s); }
  // ...but long after, the brake forgotten, a notch back is a plain push from rest.
  { f.advance(2500); f.wheel(dir); f.advance(17); f.frame(); f.wheel(dir); f.advance(120); f.frame();
    f.wheel(-dir); settle(f, 30); f.advance(3000); const y = f.y; f.wheel(-dir); settle(f);
    const s = (y - f.y) * dir; assert(s > 280 && s < 330, 'long after the brake, a notch back is a full push: ' + s); }
  // A stroll (one notch) turned back has nothing worth braking: the gentle start at once.
  { f.advance(2500); f.wheel(dir); f.advance(100); f.frame(); const y = f.y; f.wheel(-dir);
    assert((y - f.y) * dir > 0 && (y - f.y) * dir < 20, 'against a stroll the turn back starts at once, with a small step');
    settle(f); const s = (y - f.y) * dir; assert(s > 130 && s < 170, 'against a stroll the turn back is the gentle start: ' + s); }
  // From rest a notch either way is a full push.
  { f.advance(2500); const y = f.y; f.wheel(dir); settle(f); const s = (f.y - y) * dir; assert(s > 280 && s < 330, 'from rest a notch either way is a full push: ' + s); }
  // A turn back against a coast that only just died is the gentle start too.
  { f.wheel(dir); settle(f, 40); f.advance(100); const y = f.y; f.wheel(-dir); settle(f); const s = (y - f.y) * dir; assert(s > 130 && s < 170, 'reversing right after the coast dies is the gentle start: ' + s); }
  // Well after the coast died it is a plain notch from rest, whichever way.
  { f.advance(2500); const y = f.y; f.wheel(-dir); settle(f); const s = (y - f.y) * dir; assert(s > 280 && s < 330, 'a notch the other way from rest is a full push: ' + s); }
}
// A notch over a video (or an audio player) in the feed coasts like any
// other — the wheel does nothing to a player itself — and so does one on a
// thread page, and one with the pointer anywhere else on the page while a
// feed is up (the rail, the wallet column, the header).
for (const sel of ['video', 'audio', '.thread-page', '.wallet-column', '']) {
  const f = fixture(sel);
  const y = f.y; f.wheel(1); settle(f);
  assert(f.y - y > 280 && f.y - y < 330, 'a notch over ' + sel + ' coasts a full push: ' + (f.y - y));
}
// ...while a notch in a text box, or in the media pager, is left alone — and
// so is every notch on a page with no feed on it (the wallet).
for (const sel of ['textarea', '.media-pager']) {
  const f = fixture(sel);
  const y = f.y; f.wheel(1); settle(f);
  assert.equal(f.y, y, 'a notch in ' + sel + ' is not ours');
}
{ const f = fixture('', { feedOnPage: false }); const y = f.y; f.wheel(1); settle(f); assert.equal(f.y, y, 'no feed on the page: the wheel is the browser\'s'); }
console.log('✓ a turn back against a flight brakes, the next notch is the gentle start, then the wind-up; against a stroll or a dead coast the gentle start at once; stale frames; videos and threads coast');