@javan@mastodon.social
Apparently this technique is so foreign now that someone had to write this in a blog about a web browser. 😔
Apparently this technique is so foreign now that someone had to write this in a blog about a web browser. 😔
Static PRs episode 3, putting React in its place.
I’m resigning from the Committer team.
CSS tip: Use `font-variant-numeric: tabular-nums;` to align numbers neatly in a tables, progress counters, etc. Demo: codepen.io/javan/full/WNX…
Happy to announce that I’m joining @StatusHero as VP (haha.business) of engineering. Kicking things off in the new year! 🎉
Update: I’m no longer looking for a new role. More soon, but for now a huge thank you to everyone who reached out and helped spread the word. I’m humbled and grateful.💞
I left my job at Basecamp today.
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.
This shit is totally unacceptable, @canary. I’ve wasted enough time completing your dark pattern maze. Cancel my account.
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
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!
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.
node_modules/.bin/electron --version
: v4.0.1Expected Behavior
The top of the window should continue responding to clicks after exiting from fullscreen.
Actual behavior
The top ~40px stops responding to clicks after exiting from fullscreen.
To Reproduce
$ git clone https://github.com/javan/electron-quick-start.git -b hidden-inset-mojave
$ npm install
$ npm start
Additional Information
This issue is quite similar to #14529.
This PR improves the way Trix records input—particularly on mobile—by adding support for Level 2 InputEvent
s, which are currently implemented in Safari and Chrome, and in their companion webview implementations on iOS and Android. In all other browsers, Trix will continue to work exactly as it does today. Here's how:
First, Trix tests for InputEvent
support:
trix/src/trix/index.coffee.erb
Lines 21 to 24 in 650ee9a
Then, when a <trix-editor>
initializes, its editor controller installs one of two input controllers:
trix/src/trix/controllers/editor_controller.coffee
Lines 24 to 26 in 650ee9a
Trix.Level2InputController
if input events are supportedTrix.Level0InputController
, which is equivalent to the current Trix.InputController
, if input events are not supportedFixes #575
Fixes #574
Fixes #520
Fixes #494
Fixes #396
Fixes #315
Fixes #178
Closes #564
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
Expected Behavior
Clicking any region of the window should activate the element under the cursor.
Actual behavior
A small region in the top-center of the window isn't clickable.
To Reproduce
$ git clone https://github.com/javan/electron-quick-start.git -b hidden-inset-mojave
$ npm install
$ npm start
Screenshots
macOS Mojave 10.14 Beta (18A384a)
👎
Additional Information
This issue impacts any application with critical navigation elements on the top of the page. For us (Basecamp 3), the "Hey!" menu is very difficult to click:
It's likely that Mojave will be released this week during the Apple Special Event on September 12.
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. 💥
This pull request adds support for displaying groups of images in a grid, AKA image galleries. It works by automatically grouping adjacent image attachments into blocks, and styling the block's attachment elements to form a grid. The layout is 100% CSS-based so it can easily be customized or adjusted for different viewport sizes using media queries. Out of the box, Trix supports two and three column grids controlled by just a few styles in trix.css
:
trix/assets/trix/stylesheets/content.scss Lines 96 to 116 in 23ea7a0 |
→ → → |
Additional changes
Attachment toolbars now include file metadata, and their markup and has been revised to align more closely with <trix-toolbar>
's.
Text formatting can no longer be applied to attachments because, in part, the resulting HTML would break image gallery layouts. It also had weird/inconsistent results before and could easily happen accidentally (dropping images into bold text, for example).
Attachments with href
attributes now render <a>
s inside their <figure>
s.
<!-- BEFORE -->
<a href="…">
<figure>
<img src="…">
<figcaption>…</figcaption>
</figure>
</a>
<!-- AFTER -->
<figure>
<a href="…">
<img src="…">
<figcaption>…</figcaption>
</a>
</figure>
This pull requests adds support for Custom Elements v1, the official standardized spec recognized by all major platforms. v1 has already landed in Chrome, Safari, and Opera, and development is underway in Firefox and Microsoft Edge.
Custom Elements v1 and v0 are conceptually identical, although they don't share much in common API-wise. For better or worse, those API differences make v1 more difficult to adopt (unless you're only targeting modern browsers and delivering ES2015+ JavaScript code).
Caveat: an unpolyfillable upgrade
There are things that current V1 makes impossible to reproduce via plain JS and the most obvious is the JS instance upgrade. In older engines, extending any DOM native class is not enough to have a proper DOM instance.
— https://www.webreflection.co.uk/blog/2016/08/21/custom-elements-v1#caveat-an-unpolyfillable-upgrade
ES5 vs ES2015
The custom elements v1 spec is not compatible with ES5 style classes. This means ES2015 code compiled to ES5 will not work with a native implementation of Custom Elements.[0] While it's possible to force the custom elements polyfill to be used to workaround this issue (by setting (customElements.forcePolyfill = true;
before loading the polyfill), you will not be using the UA's native implementation in that case.Since this is not ideal, we've provided an alternative: native-shim.js. Loading this shim minimally augments the native implementation to be compatible with ES5 code. We are also working on some future refinements to this approach that will improve the implementation and automatically detect if it's needed.
[0] The spec requires that an element call the
HTMLElement
constructor. Typically an ES5 style class would do something likeHTMLElement.call(this)
to emulatesuper()
. However,HTMLElement
must be called as a constructor and not as a plain function, i.e. withReflect.construct(HTMLElement, [], MyCEConstructor)
, or it will throw.
— https://github.com/webcomponents/custom-elements/blob/master/README.md#es5-vs-es2015
In my testing, none of the v1 polyfills worked reliably in older versions of Safari or on older Android devices. They're also far more invasive, attempting to patch every possible means of content creation / insertion. Miss one (like Element#insertAdjacentHTML
) and custom elements slip through without upgrading.
v0 on the other hand, even though it's only officially supported in Chrome, has far better support thanks to several battle hardened, MutationObserver
-based polyfills. And so, this pull requests preserves support for v0 too.
To smooth over the v1/v0 API differences, this change introduces its own distinct naming convention for lifecycle callbacks: initialize()
, connect()
, disconnect()
(thanks, Stimulus!), and automatically maps them to the supported custom element API.
v1:
initialize() → connectedCallback() (once)
connect() → connectedCallback()
disconnect() → disconnectedCallback()
v0 (polyfilled if necessary):
initialize() → createdCallback()
connect() → attachedCallback()
disconnect() → detachedCallback()
Fixes #495
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!
PNG is not an ideal format for storing video previews for the same reason it’s not ideal for storing photos. We don’t need lossless compression or the associated extra bytes. Switching to JPG results in files that are ~90% smaller and have a negligible difference in image quality.
$ ffmpeg -i beach.m4v -y -vcodec png -vf thumbnail -vframes 1 -f image2 beach.png $ mediainfo beach.png Format : PNG File size : 1.78 MiB Width : 1 920 pixels Height : 1 080 pixels Bit depth : 24 bits Compression : Lossless
$ ffmpeg -i beach.m4v -y -vframes 1 -f image2 beach.jpg $ mediainfo beach.jpg Format : JPEG File size : 82.2 KiB Width : 1 920 pixels Height : 1 080 pixels Bit depth : 8 bits Compression : Lossy
$ mediainfo beach.m4v Format : MPEG-4 Codec ID : M4V (M4V /M4A /mp42/isom) File size : 37.4 MiB Duration : 20 s 860 ms Width : 1 920 pixels Height : 1 080 pixels
I made a some improvements to the emoji converter, and broke it up into several commits so you could see each change clearly. Hopefully you find this useful and not annoying. Thanks so much sharing your Stimulus tutorials! ✌️
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!
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.
Define a controller's target names and Stimulus automatically creates properties for accessing them:
import { Controller } from "stimulus"
export default class extends Controller {
static targets = [ "source", "slide" ]
initialize() {
this.sourceTarget // this.targets.find("source")
this.sourceTargets // this.targets.findAll("source")
this.slideTarget // this.targets.find("slide")
this.slideTargets // this.targets.findAll("slide")
}
}
We’re happy to announce three new members of the Rails committers team: George, Javan, and Ryuta 🎉!
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
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.
has_many :through
relations that return the same number of records and one common record with the max updated_at
will generate the same cache_key
. For example:
class Access < ActiveRecord::Base; end
class Person < ActiveRecord::Base; end
class Bucket < ActiveRecord::Base
has_many :people, through: :accesses
end
>> bucket.people.map(&:name)
=> ["Victor Cooper", "Jared Davis"]
>> bucket.people.cache_key
=> "people/query-2343a4c885c823c01ef9c2eb358b73eb-2-20161229150259317823"
Above, Victor Cooper has the max updated_at
(20161229150259317823
). If we remove Jared Davis and add a new person who's updated_at
is less than Victor's, we'll get the same cache_key
:
>> bucket.reload.people.map(&:name)
=> ["Victor Cooper", "Amy Rivera"]
>> bucket.people.cache_key
=> "people/query-2343a4c885c823c01ef9c2eb358b73eb-2-20161229150259317823"
PathSet#exists?
and PathSet#find
both do the same thing (PathSet#find_all
). The exists?
avoids MissingTemplate
errors when no template is found. This change avoids it by using find_all.first
instead. No behavior change, just one less template lookup.
Noticed this while working on #25411.
Extends the fix from #25546 to namespaced controllers and their templates.
New test failing without this change:
$ bundle exec rake test TEST=test/controller/render_test.rb
Run options: --seed 59446
# Running:
.....S...............................F.......................
Finished in 0.498034s, 122.4816 runs/s, 614.4159 assertions/s.
1) Failure:
NamespacedEtagRenderTest#test_etag_reflects_template_digest [.../actionpack/test/controller/render_test.rb:611]:
Expected response to be a <200: ok>, but was a <304: Not Modified>.
Expected: 200
Actual: 304
61 runs, 306 assertions, 1 failures, 0 errors, 1 skips
/cc @jeremy
This fixes a pathological-sounding-but-it-happened-to-us scenario that caused incorrect template digests for */*
requests that render non-HTML (JSON, in our case) templates. The scene looks something like this:
append_view_path(Rails.root.join("app/views/api"))
app/views
, but for different formats: app/views/api/todos/_todo.json.jbuilder
and app/views/todos/_todo.html.erb
, both "logically" known as todos/todo
. Both of these partials have cache
fragments.schedules#show
) with a corresponding JSON template (show.json.jbuilder
) that in turn renders a collection of partials: json.todos @todos, partial: 'todos/todo', as: :todo
.fetch
requests to this action that doesn't set an Accept
header so it defaults to */*
.The JSON response is correctly returned, but the template digest it uses for caching is computed using the HTML templates. Altering _todo.json.jbuilder
does not invalidate its cache, but altering _todo.html.erb
does.
We worked around the issue by adding an Accept: application/json
header. Adding a .json
format to the URL would have worked around it too.
Here are the new tests failing without the changes to ActionView::Digestor
:
$ rake test TEST=test/template/digestor_test.rb
Run options: --seed 2948
# Running:
........F.............F...............
Finished in 5.274653s, 7.2043 runs/s, 10.4272 assertions/s.
1) Failure:
TemplateDigestorTest#test_template_formats_of_nested_deps_with_non_default_rendered_format [/Users/javan/Projects/rails/actionview/test/template/digestor_test.rb:165]:
Expected: [:json]
Actual: [:json, :html]
2) Failure:
TemplateDigestorTest#test_different_formats_with_same_logical_template_names_results_in_different_digests [/Users/javan/Projects/rails/actionview/test/template/digestor_test.rb:287]:
Expected "18824a8cf013bb976bc39b5809a262c4" to not be equal to "18824a8cf013bb976bc39b5809a262c4".
38 runs, 55 assertions, 2 failures, 0 errors, 0 skips
All green with the changes.
Jbuilder currently does not account for an app's controller-wide fragment_cache_key
s when computing its cache keys.
This change adds support, and also aligns Jbuilder's caching setup with ActionView's cache helpers and Abstract Controller's fragment caching and instrumentation.
To test, I made a fresh app with a fragment_cache_key
call that should invalidate the template's cache with every request.
# config/environments/development.rb
Rails.application.configure do
config.x.counter = 0
config.action_controller.perform_caching = true
config.cache_store = :mem_cache_store
end
# app/controllers/widgets_controller.rb
class WidgetsController < ApplicationController
before_action { Rails.application.config.x.counter += 1 }
fragment_cache_key { Rails.application.config.x.counter }
def index
end
end
# app/views/widgets/index.json.jbuilder
json.cache! "unwavering-key" do
json.config_x_counter Rails.application.config.x.counter
end
Requests using Jbuilder 2.5.0
$ curl http://0.0.0.0:3000/widgets.json
{"config_x_counter":1}
$ curl http://0.0.0.0:3000/widgets.json
{"config_x_counter":1}
Started GET "/widgets.json" for 127.0.0.1 at 2016-06-13 18:42:18 -0400
Processing by WidgetsController#index as JSON
Rendering widgets/index.json.jbuilder
Rendered widgets/index.json.jbuilder (5.7ms)
Completed 200 OK in 18ms (Views: 10.4ms | ActiveRecord: 0.0ms)
Started GET "/widgets.json" for 127.0.0.1 at 2016-06-13 18:42:20 -0400
Processing by WidgetsController#index as JSON
Rendering widgets/index.json.jbuilder
Rendered widgets/index.json.jbuilder (5.7ms)
Completed 200 OK in 10ms (Views: 8.7ms | ActiveRecord: 0.0ms)
Requests using this branch
$ curl http://0.0.0.0:3000/widgets.json
{"config_x_counter":1}
$ curl http://0.0.0.0:3000/widgets.json
{"config_x_counter":2}
Processing by WidgetsController#index as JSON
Rendering widgets/index.json.jbuilder
Read fragment jbuilder/views/1/unwavering-key/c9b8ffbaac0ab06acdab1ba36772a5dc (2.7ms)
Write fragment jbuilder/views/1/unwavering-key/c9b8ffbaac0ab06acdab1ba36772a5dc (0.5ms)
Rendered widgets/index.json.jbuilder (6.5ms)
Completed 200 OK in 19ms (Views: 11.1ms | ActiveRecord: 0.0ms)
Started GET "/widgets.json" for 127.0.0.1 at 2016-06-13 18:39:53 -0400
Processing by WidgetsController#index as JSON
Rendering widgets/index.json.jbuilder
Read fragment jbuilder/views/2/unwavering-key/c9b8ffbaac0ab06acdab1ba36772a5dc (0.5ms)
Write fragment jbuilder/views/2/unwavering-key/c9b8ffbaac0ab06acdab1ba36772a5dc (0.7ms)
Rendered widgets/index.json.jbuilder (6.5ms)
Completed 200 OK in 11ms (Views: 9.5ms | ActiveRecord: 0.0ms)
Note that the cache was correctly invalidated and the presence logging like you'd see when using cache
in an .erb template.
/cc @jeremy
This isn't an issue with your polyfill per se so feel free to close it any time. We recently noticed a number of exceptions in our app stemming from Fetch-intiated PUT
requests in MS Edge that lacked a request body.
I did some digging and found that Edge's PUT
requests had Content-Length: 0
so the JSON.stringify
'd data we send as the body wasn't making it to the server. I also found that PATCH
requests fail entirely and are never transmitted.
These requests succeed in every browser I could find that has native Fetch support. They also succeed in browsers that don't when using this polyfill.
I put together small demo Rack app here: https://github.com/javan/fetch-put-patch-demo
I'm curious if you think MS Edge is doing it right and most closely following the spec. And if not, should this polyfill step in and smooth over the inconsistency?
Thanks for your time! ❤️
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.
I set out to find an effective way of extracting emoji images from Apple's "Apple Color Emoji.ttf" font and found that the data necessary data is in the sbix table.
This table provides access to bitmap data in a standard graphics format (such as PNG, JPEG, TIFF).
emoji_font = Pathname.new("/System/Library/Fonts/Apple Color Emoji.ttf")
ttf = TTFunk::File.open(emoji_font)
char_code = "\u{1f4a9}".codepoints.first
glyph_id = ttf.cmap.unicode.first.code_map[char_code]
ttf.sbix.all_bitmap_data_for(glyph_id).each do |bitmap|
filename = "glyph-#{glyph_id}-#{bitmap.ppem}.#{bitmap.type}"
File.write(filename, bitmap.data.read)
end
Currently, Rails is not compatible with Rack::ContentLength
because ActionDispatch::Response::RackBody
instances don't respond to to_ary
and, even though it's not part of the Rack spec, it's how Rack decides if a body has a knowable length. There's some discussion in rack/rack@2b3abc7, which was later reverted leading to #1160. And a little more in rack/rack@9495207.
IMO, Rack should remove the to_ary
check. This change provides immediate support and should continue to work if / when Rack does make the change.
Example:
class User<ActiveRecord:: Base
to_param : name
end
user = User.find_by(name: 'Fancy Pants')
user.id # => 123
user.to_param # => "123-fancy-pants"
I ran into an exception today when sending an email that contained "1+1=?" in the subject.
Here's a test (not included in the commit) to simulate that failure:
it "should allow setting the value of a field to a string that contains '?='" do
mail = Mail.new
mail.charset = 'utf-8'
mail.subject = "1+1=?"
mail[:subject].encoded.should eq "1+1=?"
end
Failures:
1) mail encoding using default encoding should allow setting the value of a field to a string that contains '?='
Failure/Error: mail[:subject].encoded.should eq "1+1=?"
NoMethodError:
undefined method `+' for nil:NilClass
# ./lib/mail/encodings.rb:295:in `block in collapse_adjacent_encodings'
# ./lib/mail/encodings.rb:291:in `each'
# ./lib/mail/encodings.rb:291:in `collapse_adjacent_encodings'
# ./lib/mail/encodings.rb:120:in `value_decode'
# ./lib/mail/encodings.rb:101:in `decode_encode'
# ./lib/mail/fields/unstructured_field.rb:73:in `do_decode'
# ./lib/mail/fields/unstructured_field.rb:55:in `decoded'
# ./lib/mail/fields/unstructured_field.rb:122:in `fold'
# ./lib/mail/fields/unstructured_field.rb:103:in `wrapped_value'
# ./lib/mail/fields/unstructured_field.rb:69:in `do_encode'
# ./lib/mail/fields/unstructured_field.rb:51:in `encoded'
# ./lib/mail/field.rb:167:in `method_missing'
# ./spec/mail/encoding_spec.rb:25:in `block (3 levels) in <top (required)>'
This fixes the problem by checking more closely (with a regex) to see if the string actually is encoded before proceeding.
The end! 👋