doubledepth

危ない値に対処した

#TITLE IEEE 754 dangerous values
#BPM 130

// Falsy
#RANDOM 0

// Infinity
#IF 1000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000
#00111:11
#ENDIF

// copde point?
#IF 弐
#00113:33
#ENDIF

// NaN
#IF 
#00115:55
#ENDIF

#ENDRANDOM

#00119:99

雑に全部1とみなすようにした。

Breakpointの使い方を今朝初めて知った

そんな私ですが書きかけscriptのBMS分岐関連機能を実装完了できました〜

Windows 11以降ではInternet Explorerが削除されるらしい。EPUB fileに引き続きCHM fileまでもOS標準機能で閲覧できなくなるんだろうか。

エモいでぽろぽろ

https://twitter.com/AOiRO_Manbow/status/1406067131960094720

という話がありました。(昔のbmsをひたすらBGAEncAdvかけまくってたら偶然見つけた

貴重な作例を教えていただきありがとうございます! 後付けされた動画BGAの誤った記述例もあり、こういった仕様違反はhand-coded BMSなら避け得ないものと思われます。「寛容なBMS parser」が有用な状況が少なくとも二例は確実に存在する、という情報は実装者にとって価値があると思います。

図表著者に責任がある誤記はBMS側で修正するのが筋だろうと個人的には思いますが、さすがに前世紀のBMS作品にまで修正を期待できませんし、現行のBMS Internet Ranking制度は結果的にBMSの修正を阻む要因にもなっています。BMS fileを修正してもらうのが難しいなら、仕様違反を「事実上の標準」として追認し実装してしまうのが一番あちこちに波風を立てずに済む現実的な方法……という結論になりました(当時の私の中では)。今は意見がちょっと変わりました。

どすこい生きてる

先週の妙に寒い夜から無限に眠かった。なるべく眠気に身を委ねたら一週間で復調した。

BMS SEARCHが装いを新たにした。個人的にplaylist機能がとても興味深い。BemuseなどのWeb BMS playersと連携して軽率に初心者向けpackとか投げ合う未来が思い浮かぶ。

Qwilight 1.12.2がreleaseされた。Web Camera非搭載環境でも起動可能になった模様。現時点ではinstallerのpathが不思議なので、手動更新は1.12.3まで控えたほうが良さそう。

今月末にFirefox 90が公開され、主要browsersすべてで#privateが使えるようになる。WeakMap風Classの糖衣構文? Prototype-based programmingにつきあう気がない方々には便利そう。

[追記] Firefox 90の公開日は変更されていた(に公開される予定)。Mozilla、Firefox 89のリリースを2週間先送り

先日比25倍の高速化に成功

“オートメーション工場”の各blockを分解してZIP化する処理、先日は25分かかっていたところを改修して1分で完了できるようにした。処理速度はnavigator.hardwareConcurrencyの値に依存する。ここまで高速化できれば重複内容を剪定する必要もなさそうなので、参考情報を同梱するにとどめた。

(“ゆめにっき” EX図表の重複内容を示すtext例:)
{
    "pattern_001": [
        "Line_00004-00223.bms"
    ],
    "pattern_002": [
        "Line_00229-01107.bms",
        "Line_01111-01989.bms",
        "Line_01993-02871.bms",
        "Line_02875-03753.bms",
        "Line_03757-04635.bms",
        "Line_04639-05517.bms",
        "Line_05521-06399.bms",
        "Line_06403-07281.bms",
        "Line_07285-08163.bms",
        "Line_08167-09045.bms",
        "Line_09049-09927.bms",
        "Line_09931-10809.bms",
        "Line_10813-11691.bms",
        "Line_11695-12573.bms",
        "Line_12577-13455.bms",
        "Line_13459-14337.bms",
        "Line_14341-15219.bms",
        "Line_15223-16101.bms",
        "Line_16105-16983.bms",
        "Line_16987-17865.bms",
        "Line_17869-18747.bms",
        "Line_18751-19629.bms",
        "Line_19633-20511.bms",
        "Line_20515-21393.bms",
        "Line_21397-22275.bms",
        "Line_22279-23157.bms",
        "Line_23161-24039.bms",
        "Line_24043-24921.bms",
        "Line_24925-25803.bms",
        "Line_25807-26685.bms",
        "Line_26689-27567.bms",
        "Line_27571-28449.bms",
        "Line_28453-29331.bms",
        "Line_29335-30213.bms",
        "Line_30217-31095.bms",
        "Line_31099-31977.bms",
        "Line_31981-32859.bms",
        "Line_32863-33741.bms",
        "Line_33745-34623.bms",
        "Line_34627-35505.bms",
        "Line_35509-36387.bms",
        "Line_36391-37269.bms",
        "Line_37273-38151.bms",
        "Line_38155-39033.bms",
        "Line_39037-39915.bms",
        "Line_39919-40797.bms",
        "Line_40801-41679.bms",
        "Line_41683-42561.bms",
        "Line_42565-43443.bms",
        "Line_43447-44325.bms",
        "Line_44329-45207.bms",
        "Line_45211-46089.bms",
        "Line_46093-46971.bms",
        "Line_46975-47853.bms",
        "Line_47857-48735.bms",
        "Line_48739-49617.bms",
        "Line_49621-50499.bms",
        "Line_50503-51381.bms",
        "Line_51385-52263.bms",
        "Line_52267-53145.bms",
        "Line_53149-54027.bms",
        "Line_54031-54909.bms",
        "Line_54913-55791.bms"
    ],
    "pattern_003": [
        "Line_55795-56716.bms"
    ]
}
等価な内容?
block 1block 2
#IF 1

#00111:11
#ENDIF
#IF 2
#00111:22
#00111:11
#ENDIF

LR2で後者にS-RANDOMを適用すると可視notesが増殖するらしいので、拙作scriptはこれらを等価な内容とはみなさないことにした。つまり、blockが文字列として一致するかどうかを大雑把に見ているだけであって、わざわざblockごとにBMS parserを走らせて比較するようなことはしていない。


今回の並列処理は、Layer画像群をBMSON用に変換して出力する際にも応用できそう。

わからないけい

BMSの変換結果をJSZipでdownloadableにした。ZIP化に25分かかった例147734 filesに分割される“オートメーション工場”が特別おかしいのか、それとも私のやり方がまずいのか。Colorful Channel収録作“ゆめにっき”のEX図表とかは一瞬でZIP化できたので、filesizeよりも数量の多寡のほうが影響が大きそう。もしそうなら本来の目的を達成しづらい。“ゆめにっき”で思い出したが、重複する分岐内容を雑にまとめられればfile数を減らせるかな……

わからい刑事デカ

以下の動画では“オートメーション工場”がscript内にdropされ、専用のtabが作られる。このtabが閉じられた瞬間に、“オートメーション工場”に割り当てられていたmemoryが一挙に解放される。(画面右側に増えていくbarsの青い部分が現在掴んでいるmemory、灰色部分が解放済み領域らしい。)

しかし“WindowsのTask Manager”“Chromium系browsersのTask Manager”Shift+Escで開く)のMemory Usageを眺めると、memoryが解放されているようには見えない。私はscript内のtabを消して5分間Task Managersを凝視したが、私が見張っている間memoryの値は微動だにしなかった。

Scriptを修正しても改善される様子がなく困り果てていたが、browser windowを最小化した瞬間に数百MiBの領域が空いた。なるほど、tabが非active状態になるまでgarbage collectionを控える戦略か。賢い。でもこの事実に偶然気が付かなかったら私はまたscriptingを中断していたな〜

確認したところ、同じBlink engineで動いているはずのたとえばMicrosoft Edgeなどであっても、memoryを解放するtimingや量はbrowsersによってまちまちだった。ただ、またもや私の勘違いかもしれないが、WeakMap内部の参照を明示的に切っておくとどのbrowsersでも効果があった。

let tab = document.getElementById("TAB");
let set = new Set([0, 1, 2, 3, 4]);
let bms = {
    lines: [
        "#TITLE bar",
        "#ARTIST baz",
        "#BPM 130",
        "#WAV11 qux.wav",
        "#00111:11"
    ],
    name: "foo.bms",
    numToRead: set
};
let globalWeakMap = new WeakMap();
globalWeakMap.set(tab, bms);

こんな感じのとき、いきなりglobalWeakMap.delete(tab)を実行するよりは、以下のようにちょっと小細工しておくほうが、巨大なWeakMapもすっきり解放されるようだ。

set = null;
bms.lines.splice(0);
bms.lines = null;
bms.numToRead.clear();
bms.numToRead = null;
bms = null;
globalWeakMap.delete(tab);
tab.parentNode.removeChild(tab);
tab = null;

でも手動で参照を切っていくならべつにWeakMap必要なくない……? うすうす気づいてたけど私WeakMapの使い方まちがえてない……? 間違っていたのは世界じゃない、俺のほうだ……?

[追記] あ、これちょっと間違えてるな、WeakMapにset()した時点でたぶん「変数bms内容」と「tabに関連付けられたobject内容」は関係なくなるから、後から変数bms側をいじくっても意味ないなこれ。tabに関連付けられたobject内容」のほうの内部の参照を切っていけばいいんだけど。[追記2] いや、最初のcodeと次のcodeのscopeが異なるならともかく、最初のcodeに次のcodeを続けて記述する場合ならべつに間違いではないな……。Programming、なんもわからん。

Queuewwwっうぇ

“オートメーション工場”#ENDIFを消すと約六千層の入れ子になる。これは私の書きかけscriptに限らず、uBMplay 1.5系でもmBMplayでも同様の結果になる。無限入れ子を実装していなければ即死だった……(六千層の入れ子の構文解析に3分半かかっているけれど、そこはもう諦める)

一方で入れ子を平坦化するLR2やbeatorajaは、347781行目からの入れ子を絶対に正しく解釈できないが、#ENDIFがない書式違反#IF」にも寛容であることがたやすい。BOFXVIIのrulesから抜粋:

BMS(bmson)作品はLunatic Rave 2QMS-playerbeatoraja(※ 解説記事)、BemuseのいずれかのBMSプレイヤーにおいて正常な演奏ができることを前提とします。

このruleが定めるappsはいずれも入れ子分岐をsupportしない。変換器としてはBMX2WAV v2のような「入れ子を尊重しつつ、入れ子を知らないようにも振る舞える」という形が現実的かな……と考え直し、私は書きかけのscriptに「LR2やbeatorajaのように入れ子を無視するoption」を追加した。

分岐の入れ子をsupportする実装の、通常版“オートメーション工場”解釈の例:

入れ子の範囲内、且つ入れ子にされた#IF blocksの範囲外に、行番号409541の行が含まれる。この画像は2021年2月19日の当日記から再掲。もう4箇月も経っていて笑ってしまった。進捗ダメです。というか、当時の私の書きかけscriptは当時のVivaldi browserで“オートメーション工場”を読み込んでこの画面を出すまでに2分半くらいかかっていたはずなんだが、さっき試したら26秒くらいで済んでしまった。そのくらいならscript全部を書き直す必要なんか無かったなあ……


分岐の入れ子をsupportしない実装の、通常版“オートメーション工場”解釈の例:

常に解釈される大域領域に、行番号409541の行が含まれる。行番号409534–409548は、実装側からはおりたたみやごころRemixtatami3.bmsと同じように見えているはず(大域領域を挟んで重複する#IF labels)。ただ、beatorajaはこういう構造も問題ないが、LR2はSMBプロトコルを用いたオンデマンドリソース配信型BMSの作成と運用みたいな構造を正しく解釈できなかったように記憶している。tatami3.bmsの108–296行目はLR2でも普通に解釈されているようなので、危ないのは#RANDOM nから「最初の#IF block」までの区間だけかもしれない。

あと、mBMplayはtatami3.bms#IF 5を引き当てると死ぬ


分岐の入れ子をsupportしない実装の、#ENDIF省略版“オートメーション工場”解釈の例:

分岐の入れ子構造が存在しないものと決めつけて構わない状況であれば、こういったinvalid BMSにはとても簡単に対処できる。


Firefox 89の新UI“Proton”がなかなか好み

私は集中を妨げるFaviconsを常に非表示にしていたので、pinned tabsが視認不可能になった点だけやや困った。既定のSystem themeは私には眩しすぎ、応急的にGrayishを導入した。

Non-active tabsの枠線が描画されなくなった。

BOFoonXVIIのimpression periodが一年ずれていた。私も同じ誤りを何度もやらかしており、わかる。

[追記] 日程公開後数時間後には修正されていたようだ。一方私は当site配下events情報頁のBOFoonXVII項目のHTML tagを打ち間違えたまま丸一日気付かなかった。すみませんでした……

Module scripts in XML documents are currently not supported.

Firefoxならapplication/xhtml+xmlでも<script type="module">を使えるが、Blinkでは無理らしかった。先週上がっていたWorkaroundを適用しても、私の状況は改善されなかった。私はHTMLとXHTMLの両方に対応できるようにJavaScriptを書いていたが、残念ながらこれは無駄な配慮だったようだ。まあFirefox上でならscriptが期待通りに動作することは確認できたので、私は満足した。寝る。

ジ・クソネミスト / ジェフ・ネルズ

先日挙げた作例には全部対応した。Maman, Godzilla est venu à mon marriage et m'a menacé avec un Bazookaだけはどう頑張ってもBMS会場に登録されたtitleにならなかったが、これはもしかすると、Windows-1252 textが何らかの形で強引にShift_JISに変換された結果、元のbyte sequenceが損なわれた状態かもしれない。もしそうなら、このBMSのtext encodingをどう変えたところで元の非ASCII文字列が復元されるはずがない。文字化けが#WAV#BMPでなかったのは幸運というしかない。

本当は一時間後にMUMEI Academy 2021関連のevent情報を更新したかったが限界。寝る。

読み込んだBMSのText Encoding変更機能を実装しようとしている

2HDBMS縺薙o繧後ち縺帙°繧、はShift_JIS(というかWindows-31J)としてもUTF-8としても矛盾なく解釈できるように文字列が選ばれている。非ASCII文字として半角読点と半角カタカナが含まれておりムォムォッツァレラポポ十周年。一般的なencoding検出器はこのtextをUTF-8とみなす可能性が高い。検出器のalgorithmを私は知らないが、仮定したencodingに対して「ありえそうな文字の出現頻度」を点数化して比較すれば、このBMSは普通にUTF-8文字列に見えるだろう。

KBP2012結界も同系統。#TITLE#ARTISTの内容はShift_JISとしてもEUC-KR(というかWindows-949)としても解釈できる。Shift_JISとみなすのが妥当なtitleだが、#WAV定義欄をShift_JISとして復号すると不正なbyte sequenceが出現するため、全体的にはEUC-KRとみなすしかない。

BOF2008茶道屋Summer~夏至~~N.E.W~はShift_JISとしてもGBKとしても解釈できる。それから十二年を経たBOFXVITsubasa Mai Kazeも初版はGBKだったが、こちらはShift_JISとして開こうとすると不正なbyte sequenceが出現するので、encodingを一意に定めることができるという意味ではむしろ安全だったかもしれない。

BOFU2017Is This Your Memory?のHYPER図表は全体的にはShift_JISらしきtextだが、UTF-8のbyte order markが先頭に付与されている。馬鹿正直にBOMを信頼すると文字化けする。JavaScriptのFileReader.readAsText()で内容を読み取る場合、余計なBOM部分を削って渡すなり他の方法を用いるなりしない限り、encodingとしてShift_JISを指定してもBOMが優先されUTF-8 textが返却される。しかしBOM部分が余計であることを事前に判定する方法はなさそうだし、開いた後でさえそれが余計なBOMでなくWindows-1252における必要な文字列þÿïEF,»BB,¿BFである可能性があり、何もわからない。

BOFU2017いたずらな夜はbyte order mark無しのUTF-8。当時は珍しかったが、Windows 10のnotepadの既定のencodingがBOM無しUTF-8に変更されたこともあってか、近年はちらほら見かける。

BOFU2017Maman, Godzilla est venu à mon marriage et m'a menacé avec un Bazookafrançaisであることまでは分かるが、text encodingがよくわからない。迷ったらWindows-1252にしておけばそこそこ無難かな〜とは思うが……

JSLintが最近のJavaScriptに対応していた

ES6 modulesやasync/awaitでerrorが発生しなくなっていた。ちょっとだけ試してみた感じではClass構文は駄目っぽかったが、私の試行が不徹底なだけかも。あとObjectのpropertiesをalphabetical orderで記述することを強いられるようになっていた。私には合理的とは思えないが、この規則に従うとなんかええことがあるんですかね。

書きかけのscript、横着して従来の形を変えずに小細工したら“オートメーション工場”の処理時間が改造前10秒→改造後36秒に。はい駄目〜

二重否定

nanasigroove ver.1.552は二重否定を解釈する(実際はindentを削る必要がある):

#TITLE Double Negation
#BPM 130
#RANDOM 5
#IF 1
    #00111:11
#ELSE
    #00113:33
#ELSE
    #00115:55
#ENDIF
#ENDRANDOM
#TITLE Result 1
#BPM 130
// setrandom 1

    #00111:11



    #00115:55


#TITLE Result 2
#BPM 130
// setrandom 2..5



    #00113:33




“IF statement”#IF節を持たない場合、#ELSE節は全て受理される:

#TITLE Double Negation
#BPM 130
#RANDOM 5
#ELSE
    #00113:33
#ELSE
    #00115:55
#ENDIF
#ENDRANDOM

#IF節を伴わない#ELSE節が全肯定を意味するなら、全肯定に対する#ELSEは全否定を意味するべきだろう」と考えた私は、書きかけのscriptのflowをそのように変更した。多重否定はnanasigroove仕様の範囲外なので、どのように実装しても構わないはずだ。真っ当なprogramming言語はELSE節の連続を構文違反とみなす。BMIIDXViewは多重化された#ELSEを拒絶する。mBMplayは多重化された#ELSE節のうち1個目だけを選択肢として扱うが、#IF節を伴わないstatementの#ELSE節は全て解釈する。

で、この方針を採るなら“IF statement”そのものの構造化が必須になるはず(#IF, #ELSEIF, #ELSEの各code blocksとは別に)という事実に私は今さら気がついた。そうしなければ「#ELSE#ELSE#ENDIF」と「#ELSE#ENDIF#ELSE(→#ENDIF)」を区別できないはずだ。しかし現状の書きかけscriptは#ENDIFを読み捨てており、statementsの視覚化も考慮していない。つまり、二箇月前からの書き直しと付随するtestsがようやく終わったばかりなのに、またもや書き直してtestしなきゃ……

分岐関連のcode blockが#IF#ENDIFしか存在しない状況では、code blockの開閉がそのままstatement blockの開閉を意味するので、現状のままの素朴な構造で対応できるはずだった。私は古い時代の思い込みを更新できていなかったということだろう。

ちなみに、#SWITCH statementにおける「#CASE節を伴わない複数の#DEFs」も、nanasigrooveではやや奇妙な結果になる。最初の#DEF節が#SKIPを持っていてもbreakすることなく、全ての#DEFsが解釈される。この#SKIP#CASE節が存在する場合のみ機能する。

先日教えていただいたwebpagesを眺めていた

英語の解説は私には一切わからなかったが、動画内で赤い矢印で注目を促してくれる箇所だけはなんとなく雰囲気を掴めたと思う。解説頁内動画3つめのTromboneとか、かなりへぇ〜って思った。私の理解が及んだのはそのあたりまで。

“Candide overture”、面白かった。変拍子botいわく「4/4、2/4、7/2、終盤に17/2(33335)と34/4(3×11+1)の共存」だそう。すなわちpolyrhythm、という解釈で合っているだろうか。終盤は私の雑な感性では普通に三拍子だなあ〜たまに気まぐれで崩してくる感じが良いなあ〜くらいにしか受け取れていない。このへんから口笛で追えなくなるので、私が終盤を理解するにはもうちょっと聴きこむ必要がありそう。終盤の追いかけっこ(同じようなphrasesを時間差で複数流すところ)は別の動画を観てようやくわかった。

PolyrhythmはDP譜面で左右別々の間隔で小節線を降らしたりできればもうちょっと視覚的に気持ちよくできるんじゃないかな〜と思うが、rhyrhm表現がに束縛されるBMS形式だと難しそう。

Comment返信

https://hitkey.nekokan.dyndns.info/diary2010.php#D201010

↑の記事でLeonard Bernstein『America』について触れられてましたが、

https://orchestrasounds.com/tag/candide-overture/page/2/

https://twitter.com/henbyoushi_bot/status/648621919302914048

https://www.youtube.com/watch?v=YaY2iAC4Y-w

で触れられているMass(1拍子でキメを作る)やCandide overture(7拍子と終盤のポリリズム)もリズム的に面白いので紹介させて頂きます。

非常に興味深いwebpagesを教えていただきとてもありがたいです! 1拍子で何をどうするのか全く想像できずワクワクします。今shower直後でheadphonesを装着できないので後日感想を書きます!

ところで教えていただいたYouTubeのURL末尾に見慣れないparameterが付与されていて、検索しても詳細が不明だったので、念のため当日記のhyperlinkからは件の情報を外しております。もし参照先の動画が正常に閲覧できないなどの不具合があった方は、各自でURL末尾(YaY2iAC4Y-wの直後)に&pbjreload=101を付け足してくださるようお願いいたします。

二箇月前からの埋伏bugを解決

#RANDOM#SWITCHの深すぎる入れ子に対処するために、一定層以降のnodesは疑似的に別の文書木として切り出し、最後にまとめて統合していた。この場合はの周辺にbugsが多く潜むことが予想され、実際にそれらの対処に追われた。その時は暫定的に対処できたつもりだったが、内部処理をまるごと書き直して再び境界値試験をやり直すことを考えると吐きそうになっていた。しかしついさっきものすごく雑な解決方法を思いつき、30秒くらいcodeを書いたら実際に解決できてしまった。ええ〜…… ちまちま付け足していた例外処理とか全部要らなくなったんだけど…… 既にそれらの内容の大半が頭から揮発した今となっては、不要な処理を迂闊に除去するのも怖い……

File読み込み処理の見直し

DataTransferItem.webkitGetAsEntry()は普通に走査したら100件が上限とか、dropEvent.dataTransfer.itemsはdrop event handlerが終了すると同時に空になるとか、今月頭に見つけた書きかけscriptに関するbugsを調べていたら初心者が踏みそうな地雷を全部踏み抜いていたことが発覚。File読み込み関数群がDomino Topplingのごとく連鎖的にああ^〜

なんとかなりそうだけどと吐き気が酷いので寝る。

日記

8秒かかる鈍重な処理も8分割して並列処理すれば1秒で終わるのでは? 気がすすまない案ばかり思いつく。Workers側の並列処理はおそらく私が狙った通りに書けた。非同期処理群をPromise.all()で束ねるところがうまく書けなかった。then()節の中に後続処理を全部つっこむだけなのに、なぜうまくいかないのかわからない。awaitしても解決されていない。カッとなってscriptを一週間前の版に差し戻した。まあこの版でも“オートメーション工場”の読み込み時間は10秒を切っているし、43万個以上のnodesのの10秒なら私にしては頑張ったほうだろう。

いまのところWeb WorkersはDOMさわれないが、innerHTMLはただの文字列なのでWorkers側で走査できる。innerHTML文字列は再帰下降構文解析器が通過する道順そのものなので、(妥当なHTMLなら)素朴なwhile loopだけで簡単にDOM walkができてしまうし、部分的な挿げ替えもできてしまう。いわゆる仮想DOMほど大仰な用途が必要でない場合は、これだけで事足りる感がある。

日記

BMS関連

拙作BMS
bubble / hitkey
二次配布BMS
ノイズの海と鯨 / moka
PARTY TIME IN MY DREAM / HAIJI
BMSE非公式ヘルプ
Lite
Lite-online
Full
Full-online
buglist
iBMSC
Web (Japanese version)
issues
BMS差分
a­nal­gam
boléro
Ketch­up
quovadis
SELF
yellows
Do not use non-ascii filenames
Brilliant Techno Square
雑多なメモ
bmsplayer data
bms benchmark
Secrets - Feeling Pomu 2nd
grid2sec
bmx2xxx
BMx Outliner
BMS command memo
BMS command memo (Japanese version)
BMS EVENT LITE
#RANDOM BMS list
BMS #OPTION command
BMS Bitmap test
Extended BPM
STOP Sequence
BMS Edge Cases
BMS extensions proposed by Sonorous (unofficial Japanese version)
BMS 2.0 (unofficial Japanese version)
BMS Editors
Do not use non-ascii filenames
BM98 Kikuchan Version 3.30 Revision #4.2
BMSON Checker
_wsh_bms2bmson.js

その他

HTML関連メモ
Dakuten on HTML
nest1000
EVS
Nervous Cascading
Source Han Sans test
User-Agent String
CSS Logical Properties