
I left my job at Basecamp today.
Hello, Javan Makhmali here
…and there ↝
Twitter,
GitHub,
javan@javan.us

I left my job at Basecamp today.
Created attachment 422090 Test case When input and media elements are adopted with document.adoptNode() from a foreign document (via DOMParser, for example) they lose their native shadow DOM roots and are left in a mostly broken state: <input>s lose their native UI, and <video> and <audio> elements lose their native controls. To reproduce, open the attached adopt-node-bug.html file in Safari and compare the correct middle column (imported node) to the incorrect right column (adopted node). Tested in Safari v14.0.3 (16610.4.3.1.4) on macOS v11.2.2, and mobile Safari on iOS v14.4.



Created attachment 398848 Minimal test case To reproduce: Open the attached template-img-srcset.html file in Safari on a 2x DPR or higher display. Expected: Both images load the same URL. Actual: The first image loads the correct URL. The second image (cloned from a <template>) incorrectly loads the first URL from its srcset.
Here is another example that demonstrates the issue. It works by canceling the "beforeinput" event and applying the target range to the selection to illustrate it. 1. Open the attached "select-target-range.html" file in Chrome 2. Place the cursor at the end of each line and press Cmd+Delete 3. Observe the selection, which represents the target range 4. Not that it doesn't match the actual range of text to be removed
Created attachment 381204 Minimal test case to reproduce <datalist> crash To reproduce: 1. Open System Preferences → Keyboard → Text and add entry to replace "cat" with "Cat" 2. Open the attached datalist.html file in Safari 3. Ensure that the menu option for Edit → Substitutions → Text Replacement is selected 3. Type "cat" in the text field and wait for the "Cat" replacement overlay to appear 4. Click any option from the expanded <datalist> menu and Safari will crash Tested on macOS 10.14.6 (18G103) with Safari 13.0.2 (14608.2.40.1.3) and Safari TP Release 94 (Safari 13.1, WebKit 14609.1.6.1). I believe this issue occurs in Safari 12.1 as well.

Dealing with incoming email, composing rich-text content, connecting to multiple databases, parallelizing test runs, integrating JavaScript with love, and rewriting the code loader. These are fundamental improvements to the fundamentals of working with the web and building fast and fresh applications. This is the kind of work we’ve been doing for the past fifteen years, and we’re still at it. THIS IS RAILS SIX!
UserAgent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/76.0.3809.100 Safari/537.36 Steps to reproduce the problem: 1. Open the attached cut.html file and click "Cut All" (or, use the Edit → Cut menu) What is the expected behavior? The cut operation completes in a reasonable amount of time. What went wrong? The cut operation takes several seconds to complete. The Performance inspector reveals a very slow Layout task. See the attached .gif for a comparison by browser. Chrome: 5,177ms Firefox: 19ms Safari: 147ms Did this work before? N/A Does this work in other browsers? N/A Chrome version: 76.0.3809.100 Channel: stable OS Version: OS X 10.14.6 Flash Version:

Today’s guest is Javan Makhmali, who works for Basecamp and helped develop Trix. Trix is a rich text editor for the web, made purposefully simple for everyday use instead of a full layout tool. Trix is not the same as Tiny MCE, and Javan discusses some of the differences. He talks about the benefits of using Trix over other native browser features for text editing. He talks about how Trix has simplified the work at Basecamp, especially when it came to crossing platforms. Javan talks more about how Trix differs from other text editors like Google Docs and contenteditable, how to tell if Trix is functioning correctly, and how it works with Markdown.

<p>The paragraph element is surprisingly complicated.</p>


My favorite JavaScript is the code I delete and replace with CSS.

Buckle up! In this episode, Javan Makhmali and Sam Stephenson join Jason and Chris. Sam and Javan are employees at Basecamp and also part of the mastermind behind many JavaScript libraries to come out of Basecamp: Turbolinks, Trix, and Stimulus. They each share how they got started programming, a brief insight into how they work at Basecamp, the transition from CoffeeScript to ES6, Typescript, as well as (wait for it...) upcoming features in Stimulus (🙌) and considerations for Turbolinks 6 (no feature promises made 😉). It was an absolute joy to chat with Javan and Sam. We very much enjoyed this episode and hope you will enjoy it, too.



Action Text is a new framework coming to Rails 6 to make it easier to create, edit, and display rich text content within an app. Brittany invited Javan Makhmali, programmer at Basecamp, on to the show to get the scoop.

I’m having so much fun working through @jamis Buck’s new book, The Ray Tracer Challenge (pragprog.com/book/jbtracer/…). Here’s my scene from Chapter 10: Patterns.

🗣✨ 𝗧𝗥𝗜𝗫 𝟭.𝟬 ↝ github.com/basecamp/trix/… Lovely new site by @AdamStddrd ↝ trix-editor.org


Kudos to @getsentry for revealing the culprit of a JavaScript error that would’ve been a serious head scratcher. jQuery was being injected by some browser extension(s), clobbering our own version and nuking the jQuery plugins we rely on. 💥



Inspired by @rpsthecoder’s preethisam.com/2018/06/25/how…, I made a thing: codepen.io/javan/full/Ker…
Hello, another Basecamp developer here with more details. We use `MutationObserver` to detect when images are added to the DOM and swap their "src" attribute with a tiny placeholder image. Then we restore the original "src" as images are scrolled into the viewport. This technique is commonly referred to as “lazy loading” and is intended to avoid unnecessary network requests for images that may never be viewed. Due to this WebKit issue, our approach doesn’t work in Safari because the original image is always loaded. Additionally (this may be a separate issue), cloning <img> elements causes them to load *again* even if the cloned node is detached from the DOM. For example, running `document.body.cloneNode(true)` reloads all of its images. This affects our Turbolinks (https://github.com/turbolinks/turbolinks) library, which stores “snapshots” of pages by cloning them. I made a video to help illustrate the problem: https://www.youtube.com/watch?v=p6bkcjoyP1M In Safari (left), every image on the page is loaded initially, and then loaded again when scrolled into view. Cloning <body> loads all of its images once more. In Chrome (right), only the first image loads initially and the rest are canceled. Cloning <body> makes no additional network requests. My example application and its source code are available here: https://glitch.com/~jealous-moon Thanks for your time!



Dear @github, It's hard to review large pull requests when you’re only interested in a subset of files so I made this Chrome extension to help: github.com/javan/pull-req…. Please steal my code and build it into your website. 💜

🙃 Keepin’ it simple. Server-rendered client-rendered apps using a server-side client. twitter.com/ebidel/status/…

"Headless Chrome: an answer to server-side rendering JS sites": developers.google.com/web/tools/pupp… "...Headless Chrome eats JS for breakfast..." 🥞 New post on using headless/Puppeteer on the server to prerender dynamic pages for 1st load perf 📊, crawlers 🕸, social widgets 🗣, & more.


Rich text editors have come a long way in recent years, and there are dozens of great libraries now. Sadly, they all suffer on one platform: Android. It’s notoriously difficult to support. Chrome team doesn’t seem to care, and they’re making matters worse: bugs.chromium.org/p/chromium/iss…
Hello, I’m one of the maintainers of Trix (https://github.com/basecamp/trix), a rich text editor for the web. Trix, like many modern editors, listens for events to record input, and then converts that input into an editing operation on its internal document model. This process has always been challenging on Android due to its lack of key codes on keyboard events (this ol’ issue https://bugs.chromium.org/p/chromium/issues/detail?id=118639), but we’ve managed well enough by handling composition events. The change that fixed this issue is a significant departure from the current behavior of composition events, and problematic in a number ways: 1. Composition events are dispatched every time you move the cursor but aren’t typing. This alone quite a stretch from from the spec’s definition: “Composition Events provide a means for inputing text in a supplementary or alternate manner than by Keyboard Events” (https://www.w3.org/TR/uievents/#events-compositionevents). These composition events while moving the cursor also have no associated keyboard or inputs events, another deviation from the spec: “During the composition session, keydown and keyup events MUST still be sent” (https://www.w3.org/TR/uievents/#events-composition-key-events). 2. The DOM selection during compositions now varies depending on the context. When moving the cursor, the selection during compositionupdate is always collapsed at the cursor’s position. When typing, the selection is the expanded range around the composed characters. This means we can’t reliably determine if the input is new or if it’s replacing a range of existing content. Description of the attached screenshots: cursor-movement.png: Shows the composition events dispatched after moving the cursor from left to right four times. Note how the compositionupdate data is identical, but the DOM selection is not. movement-then-typing.png: Shows the events dispatched when placing the cursor before "The", "quick", and "brown", and then typing "fox " at the end. This illustrates how the selection expands while typing, and how difficult it is to distinguish input from cursor movement generally. These screenshots come from http://jsfiddle.net/javan/mj13jj7b/embedded/result,js/ - a small test bed I made. Related issue: https://bugs.chromium.org/p/chromium/issues/detail?id=812674 Thanks for your time!
UserAgent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_3) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/64.0.3282.140 Safari/537.36 Steps to reproduce the problem: 1. Open the attached HTML file 2. Scroll to the bottom 3. Click each of the links to focus the form controls What is the expected behavior? The page does not scroll What went wrong? The page scrolls to the element's unstuck position when calling its focus() method. This happens for all form controls except for <input type="text"> so https://bugs.chromium.org/p/chromium/issues/detail?id=740417 could be partially related. Did this work before? No Does this work in other browsers? Yes Chrome version: 64.0.3282.140 Channel: stable OS Version: OS X 10.13.3 Flash Version: https://bugs.chromium.org/p/chromium/issues/detail?id=805800 may also be related.


We’re happy to announce three new members of the Rails committers team: George, Javan, and Ryuta 🎉!

Created attachment 314620 Paste event clipboard data inspector The ClipboardData for paste events are identical when performing Paste (⌘V) and Paste and Match Style (⌥⇧⌘V). Because they're indistinguishable, it's impossible to programmatically handle pastes correctly in a rich text editor (like https://github.com/basecamp/trix, which I help maintain). There's no way to infer the desired format and pick an appropriate type. To reproduce, open the attached paste-inspector.html file in Safari and paste as instructed. Note that the logged paste events are identical and both contain "text/html" and "text/plain" types. Chrome and Firefox only return a "text/plain" type when performing Paste and Match Style, which I assume is correct. This issue is also present in previous versions of Safari. Screenshots Safari: https://cl.ly/291S1I3L2B25 Chrome: https://cl.ly/3m3B1P0D0Q2E Firefox: https://cl.ly/3M3v2R2r2T2H



Created attachment 8829246 ff-element-duplicated.gif Steps to Reproduce: 1. Paste the following Data URI into your address bar: data:text/html;charset=utf-8,<x-foo contenteditable><div>ab</div><p>cd</p></x-foo> 2. Place the cursor before "b" and select through "c" so that both letters are selected 3. Press backspace Expected Results: <x-foo contenteditable> <div>ad</div> </x-foo> Actual Results: <x-foo contenteditable> <div>a</div> </x-foo> <x-foo contenteditable> <p>d</p> </x-foo> Additional details: Enabling dom.webcomponents.customelements.enabled and registering the element with document.registerElement has no affect. The same problem is reproducible using <span contenteditable> as the containing element (instead of <x-foo contenteditable>) so I suspect Firefox treats the contained elements as invalid block children of an inline parent. Styling the container with "display:block;" doesn't help, and in either case the contenteditable element should not be duplicated.







After six months of polish, four betas, and two release candidates, Rails 5.0 is finally done! It’s taken hundreds of contributors and thousands of commits to get here, but what a destination: Rails 5.0 is without a doubt the best, most complete version of Rails yet. It’s incredible that this community is still going so strong after so long. Thanks to everyone who helped get us here.






Add "Apple Color Emoji" to your font-family stack to fix broken emoji in Chrome. codepen.io/javan/pen/GomJ…


Here's a little preview of Trix, the rich text editor in @basecamp 3.





