なっから 

.oO なっから備忘録 Oo.

ホームページを作成したときの備忘録です。ここの落書き人のメモリーが揮発性ですのでメモっておきます。

 ※当該落書き人の備忘録ですのでHTML,CSS,JavaScriptの基礎的な説明は省いてあります。

逐次型プログラマが嵌まる非同期という落とし穴

次型プログラミングが染み付いたプログラマがハマるJavaScriptの非同期という落とし穴!!

これ、落書き人本人の話です。プログラム・ソース・コードの記述順に実行されるという潜入概念を捨てないとJavaScript記述の非同期の動作が理解できずに不具合を招きます。

特にハマるのがコールバックで記述する無名関数で記述した場合です。ここで記述した実行コードはコールバック時点で実行されるもので、コールバック記述の次に記述したコードが次に実行される訳でもなく、thisも引き継がれていません。この感覚が逐次型しか経験のないプログラマには理解しずらい部分です。非同期という感覚が身につかないとJavaScriptの理解ができません。

ES2015でますます記号のお化けみたいな記述がでてきて頭の切り替えに苦労しています。

なお非同期の素晴らしさはnode.jsで実感できます。逐次型プログラミングでより多くのトランザクション処理を行うためにはスレッド(又はタスク)で多重化して処理性能を上げるのが常識でした。非同期のnode.jsはシングルスレッドで有りながら処理能力が素晴らしい。今はnode.jsで非同期プログラミングに嵌っております。

SPA(single-page application)とCMS(Content Management System)手法

SPAですが「なっからHP」は文書としては単一なHTMLページです。厳密にはiframeを埋め込んでいますがそれは機能的に別物なページです。

SPAにした理由はHTMLページを増やすと整理整頓が苦手な落書き人にとってはトッチカらってしまうのが理由です。

なお、単一ページでコンテンツが増えると一文書として大きくなり見通しが悪くなります。そこで同種のコンテンツであるYouTubeはJSON(JavaScript Object Notation)で整理しています。その整理方法ですが、文書作成が楽な某グループウェア・ソフトを利用しています。そのソフトからJSONデータとして書き出して、ウェブ・サーバーにアップロードしています。

JSONでデータ化しているコンテンツは以下の通りです。

・なっからHPの手ぶちコンテンツ部分

・ちゅんラヂの選局データ

メッセージ通信(HTML5 Web Messaging)

window間で相互にオブジェクト(*1)を送受信する事ができます。この通信はクロス・ドメイン間でも可能です。

信頼出来る別ドメインのウェブ・アプリ間でデータの授受が必用な場合に用います。

なお、信頼できるドメインの確認ロジックを組み込む必用があります。これを慎重に組み込み、不用意にデータ詐取されことを防止する必用があります。

以下に、ちゅんラヂとそのエリア選択との間で、エリア選択結果の授受を行っいる事例を上げておきます。この事例では信頼できるoriginを確認しなくても、不正利用されて困るようなデータ授受はありません。しかし不正な利用は気持ち悪いことから、信頼できるoriginの確認を行っています。

1.送信側:エリア選択画面(tunein2area.html内)

function sendAreaCode(aArea){ var winTarget = null; // 親Window オブジェクト if (window.opener == null && window.parent == window.self) { console.log("tunein2area.htmlの単独利用では選択エリアを戻すwindowがありません。"); return false; } if(window.parent !== window.self) { winTarget = window.parent; } else if (window.opener !== window.self) { winTarget = window.opener; } else { console.log("tunein2area.htmlは親windowへ送信する要件のみです。それ以外のwindowへ選択エリアを戻す事は出来ません。"); return false; } //親windowのみに選択されたエリア情報を送信 if (window.location.origin == "null" ) { winTarget.postMessage( aArea , "*" ); } else { winTarget.postMessage( aArea , window.location.origin ); } console.log("tunein2areaで選択されたエリア:" + aArea); return false; //falseはクリック元のアンカーの動きをキャンセル }

2.受信側:ちゅんラヂ画面(tunein2radio.html)

/* --------------------------------------------------------------------------------------------  tunein2area.htmlから選択されたエリア情報のpostMasseageを受けてエリアを切り替える */ // postMasseageの着信を監視 if(window.addEventListener){ window.addEventListener("message" , receiveArea); }else if(window.attachEvent){ window.attachEvent("onmessage" , receiveArea); } //受信イベント処理 var receiveArea = function (e) { // 信頼するorignからのデータであることを確認 if (e.source.location.origin == "null" //local実行 ※Firefoxのみ動作 || e.source.location.origin == "https://www.zdesign.info" || e.source.location.origin == "http://www.zdesign.info" || e.source.location.origin == "https://zdesign.info" || e.source.location.origin == "http://zdesign.info") { if(e.source.location.origin != location.origin) { // 同一orign以外からの受信で、tunein2area.htmlが信頼できるorigin以外に設置されている console.log("送信元のoriginは信頼できるが当該受信originと不一致(warning): 送信元origin:「" + e.source.location.origin + "」 受信元origin:「" + location.origin + "」"); } else { // 送受信双方とも同一orignで信頼できる console.log("送信元のoriginが一致(OK): 送信元origin:「" + e.source.location.origin + "」 受信側origin:「" + location.origin + "」"); } } else { // 送信元のwindow.location.orignが信頼できない(成りすまし) console.log("送信元のoriginが信頼できない(reject) 送信元origin:「" + e.source.location.origin + "」"); return false; } // 送信されてきたエリアに切り替える ・・・エリアを切り替える処理記述 }

iframeで機能単位に分割

ちゅんラヂはiframeを多様しています。iframeを多様することは、文書としてのHTMLでは好ましくありません。しかし、ちゅんラヂの様に機能(ウェブ・アプリ)の場合は、機能毎に分割管理することで、一つのHTMLが複雑化・肥大化することを防ぎ、メンテナンス性の悪化が防止できます。

なお、各機能のHTML(window)間のデータ授受はメッセージ通信を利用しています。

また「allow="autoplay; encrypted-media"」属性を指定することでautoplay blockingの回避が容易にできます。ただし親Windowで何らかのユーザー操作が無い状態でのautoplayは出来なくなります。これは各ブラウザに於いてautoplay poricyの実装が進んでいる為です。

audio要素とWeb Audio APIでの「Access-Control-Allow-Origin」の扱いに違いが

ちゅんラヂの様にクロスドメインで音声配信を再生する時の話です。

audio要素で再生している音声のVisualizerを表示してみました。Visualizer自体はymgd-aさんの「Web Audio APIを使ってDAW(っぽいもの)を作ってみる」と使わせて頂きました。

本題ですがaudio要素にVisualizerと表示する為にAudioContextにaudio要素を渡すと音声出力がzeroになってしまします。

それはaudio要素のcrosプロパティに'anonymous'をセットすることで対応できました。しかしこれでも駄目な配信がありました。

そこでテストを行うと配信サーバーから送られてくるHTTP HeaderでAccess-Control-Allow-Originの設定有無で、双方の解釈に違いが有ることが判りました。

以下の表はちゅんラヂの手法から見た場合の事象です。

┌────────────────┬────────┬────────┐
│Access-Control-Allow-Origin設定 │    audio要素   │ Web Audio API  │
┝━━━━━━━━━━━━━━━━┿━━━━━━━━┿━━━━━━━━┥
│具体的なorigin設定あり *2       │ 接続出来ない   │ 接続出来ない   │
├────────────────┼────────┼────────┤
│* 又はリクエストoriginが戻る    │ 接続出来る     │ 接続出来る     │
├────────────────┼────────┼────────┤
│当該Header無し *1               │ 接続出来る     │ 接続出来ない   │
└────────────────┴────────┴────────┘
 主なサイマル配信の状況
┌───────┬──────────────┬─┬─┬────────────┐
│     配信元   │Access-Control-Allow-Origin:│*1│*2│          備考          │
┝━━━━━━━┿━━━━━━━━━━━━━━┿━┿━┿━━━━━━━━━━━━┥
│らじる★らじる│リクエストoriginが戻る      │○│○│HLS(HE-AAC)             │
├───────┼──────────────┼─┼─┼────────────┤
│radiko.jp     │radiko.jp                   │×│○│HLS,IE向けはRTMPE       │
├───────┼──────────────┼─┼─┼────────────┤
│JCBA          │*                           │○│○│HLS(HE-AACv2)とicy(MP3) │
├───────┼──────────────┼─┼─┼────────────┤
│ListenRadio   │リクエストoriginを戻る      │○│○│HLS(HE-AAC)             │
├───────┼──────────────┼─┼─┼────────────┤
│FM++          │fmplapla.com                │×│×│Websocket独自配信(OPUS) │
├───────┼──────────────┼─┼─┼────────────┤
│AFN           │リクエストoriginを戻る      │○│○│icy(MP3)                │
└───────┴──────────────┴─┴─┴────────────┘

参考:Security with MediaElementAudioSourceNode and Cross-Origin Resources

混在コンテンツ制限で使えないWeb APIサービス

ちゅんラヂではIPアドレスからエリアを自動判定する為に「http://ip-api.com/json/」サービスを利用しています。

ここでip-api.comがTLSに対応していないために、ちゅんラヂに「https://~」でアクセスすると混在コンテンツになり利用できません。そこでWeb Serverに中継させることで混在コンテンツを回避しました。

それを回避する為の中継プログラムはPHPで記述しました。コードは下記の2行です。


<?php
echo file_get_contents("http://ip-api.com/json/" . $_SERVER["REMOTE_ADDR"]);

たった2行のプログラムを解説するのは野暮というものですので、解説は割愛します。

スムース・スクロール

ベタですが、スムース・スクロールをこの備忘録で利用しています。私はページ内リンクがスパッスパッと切り替わるのが好みです。しかし、それはページ構成がわかっている場合の話です。スムース・スクロールさせることで、感覚的にページ内を上下方向のどちらに動いたのかがわかり、ページ内で迷子にならないのが利点です。

なおスクロールでジャンプさせるアンカーですが、今どきはページ内アンカー「<a name="アンカー名"> </a>」を使わずに、id属性がページ内アンカーの役割をします。

コードは下記の通りで、スムース・スクロールはjQueryのanimateで行っています。“jquery.easing.1.3.js”はjQueryのアニメート・エフェクトに変化を加えるjQuery Easing Pluginです。

<script src="../libs/jquery.easing.1.3.js"></script> <script> $(function () { //スムース・スクロール $('a[href^="#"]').click(function() { var speed = 700; //ms単位でスクロール速度を指定 var href = $(this).attr("href"); var target = $(href == "#" || href == "" ? 'html' : href); var position = target.offset().top; $('body,html').animate({ scrollTop: position },{ duration: speed, easing: 'easeInCubic' }); return false; }); }); </script>

タブでコンテンツを切り替え(同一ページ内)

なっからHPのコンテンツはタブで切り替えています。そしてHTML的にはiframeを除き1ページ構成で、同一ページ内のブロックの表示・非表示を切り替えています。利点はコンテンツ切替時のページ読み込みがありませんので、切り替えは瞬時に切り替わります。

この手法はなっからHPの様に、コンテンツ量が少ないウェブページの場合に利点が活かせます。大きなコンテンツの場合は初期ロードに時間がかかりますので、この手法は短所になります。

ソースコードの要点です

<nav id="tab_menu"> <ul class="tabul"> <li id="tab1" class="tabPresent"> コンテンツ1 </li> <li id="tab2"> コンテンツ2 </li> <li id="tab3"> コンテンツ3 </li> <li id="tab4"> コンテンツ4 </li> </ul> </nav> <section id="sec1"> : コンテンツ1 : </section> <section id="sec2" style="display: none;"> : コンテンツ2 : </section> <section id="sec3" style="display: none;"> : コンテンツ3 : </section> <section id="sec4" style="display: none;"> : コンテンツ4 : </section> /* タブメニュー */ #tab_menu { position: absolute; bottom: -1px; clear:both; overflow: hidden; width:880px; height: 32px; } #tab_menu ul{ list-style:none; margin: 0; position: absolute; bottom: 0px; } #tab_menu li { float: left; display: block; font-size:14px; line-height: 100%; text-align:center; vertical-align:middle; text-decoration: none; height: 22px; margin: 0px 1px 0 1px; padding: 8px 0 0 0; border-top: 1px solid gray; border-left: 1px solid gray; border-right: 1px solid gray; border-bottom: 1px solid navy; border-radius: 6px 6px 0 0 / 6px 6px 0 0; background-color: lightsteelblue; } #tab_menu li:hover { background-color: blue; color: white; cursor: pointer; } #tab_menu li:hover.tabPresent { background-color: white; color: navy; cursor: auto; } #tab_menu li.tabPresent { display: block; line-height: 100%; text-align: center; vertical-align: middle; border-top: 1px solid navy; border-left: 1px solid navy; border-right: 1px solid navy; border-bottom: 1px solid white; background-color: white; } #tab_menu li.tabPresent a { background-color: white; color: navy; cursor: default; } var gTab = { init : function () { var pTabs = this.setup.tabs; var pPages = this.setup.pages; var i; for(i=0; i<pPages.length; i++) { if(i !== 0) pPages[i].style.display = "none"; pTabs[i].onclick = function(){ gTab.showpage(this); return false;} } }, showpage : function (aTab) { for (var i = 0; i < this.setup.tabs.length; i++) { if (this.setup.tabs[i].id == aTab.id) { this.setup.pages[i].style.display = "block"; //クリックされたタブのブロクを表示 this.setup.tabs[i].className = "tabPresent" //クリックされたタブのclassを変更してカレント状態表示にする presentIx = i; } else { this.setup.pages[i].style.display = "none"; //クリックされたタブの以外のブロクを非表示 this.setup.tabs[i].className = null; //クリックされたタブの以外のクラス名をクリアーして選択されていない表示状態にする } } } }; window.onload = function () { //タブメニューのセットアップ gTab.setup = { tabs : docid$("tab_menu").getElementsByTagName("li"), pages : [docid$("sec1"), docid$("sec2"), docid$("sec3"), docid$("sec4")] } gTab.init(); };

フォント種類とフォントサイズ&ウェイトの指定方法

基準となるフォントのサイズと相対的なフォントサイズの指定で、全体的な文字サイズのバランスをそのままで拡大・縮小ができる様にする。

つまり全体的な文字サイズを変えたい時には、html要素のcss指定を変えるだけで、配下の各要素のフォントサイズを変更する必要がなくなる。

基準となるフォントサイズ(em指定)はhtml要素に指定する。

以下の例ではGoogleが提供するウェブ・フォント「Noto Sans Japanese」を利用して、基準をなるサイズをウェイトを指定している。

ウェブ・フォントを読み込むことで表示までの時間が掛かるが、プラットフォームに依存しない文字表示が可能になる。

「arial」指定は、何らかの理由でGoogleからフォントが読み込めなかった時の代替えフォント指定。

html要素は以下の相対フォントサイズ(rem指定)は指定する。

基準フォントに対す相対的なサイズを指定することで、全体のフォントサイズ変更時の変更作業を最小限にする考慮を行う。

@import url(https://fonts.googleapis.com/earlyaccess/notosansjapanese.css); html{ width: 100%; font-size: 1.0em; font-weight: 200; font-family: 'Noto Sans Japanese',arial; } body{ font-size: 1.0rem; line-height: 1.5rem; }

埋め込みフォントを止めました。

Google Fontを埋め込んだ時は、ウェブ・フォントの読み込み時間が1秒程度で問題ないと判断したのですが、最近は読み込みに5秒以上かかる様になってしましました。私は待ち時間5秒を超えたら実用性に疑問を感じますので一旦、ウェブ・フォント埋め込みを止めました。

読み込みが遅くなった原因に心当たりがあります。私が利用しているプロバイダに問題が有るようです。春頃から夕方以降にダウンロード速度が30Kbps程度まで悪化しています。このプロバイダは老舗でパソコン通信時代から使っているメールアドレスがあり止めるに止められない状況です。

私の環境に問題が有るのは明白ですが、他にも私のような環境の方が居られると思い、しばらく様子見で埋め込みフォントは見合わせます。

複数のYouTube動画を埋め込む時はクリック後に動的再生で

「何いってんだ!」と言わないでください。YouTubeの埋め込みはiframeで読み込まれるコードが結構重い。これを複数設置するとローディングに時間がかかります。ということで複数埋め込むべきでは無いというお話です。

そこで、なっからHPでは直接埋め込まずに、一覧はイメージを並べて、そのイメージがクリックされた時に動的に埋め込みコードを生成しています。イメージをクリックした時にpopup風のブロックを動的に作り出して、そこにYouTube動画埋め込みコードを書き出し、再生しています。

loadingやsending待ちの時には不用意な操作を防止させる

なっからHPではローディングが終わるまで、待っている事を知らせるアイコンが表示しウィンドウ全体を覆うことで、不用意な操作を防止しています。

静的なコンテンツだけではなく、動的にJSONデータを読み込んでコンテンツを表示していますので、その処理が完了する前にメニューをクリックされた場合の動作が不定になる可能性があります。これを防止する為です。

入力フォームに入力されたデータをサーバーに送信した場合にも、この手法でサーバーからの応答が戻るまで不用意な操作を抑止することで、せっかちは方が送信ボタンを何度もクリックして重複送信になってしまう事を防げます。

ソースコードの要点です

<div id="loading"> <div id="loading_circle"> <img src="./img/loading.gif" alt="loading"> </div> </div> /* Loading Mask ロードされるまで操作されたくない */ #loading { position: fixed; top: 0; left: 0; width: 100vw; height: 100vh; background-color: white; opacity: 0.7; z-index: 1000; } #loading_circle { position: absolute; top: 0; right: 0; bottom: 0; left: 0; margin: auto; width: 48px; height: 48px; z-index: 1001; } $('#loading_circle').hide(); $('#loading').hide();

bxSlider:スライダーが簡単に設置できる

jQueryプラグインのbxSliderは高性能なコンテンツ・スライダーでレスポンシヴ・デザインにも対応しています。

まずはbxSliderを入手しましょう。 bxslider-4

こちらの利用方法を参照してください。設定項目が盛りだくさんです。

インターネットラジオを自動再生させる

Google ChromeはAutoplay Policy ChangesでChrome 66から自動再生が出来なくなりました。これはサイトを開いたら音が出てビックリを無くす処置です。

これにより、ちゅんラヂは「~URL~?recieve=放送局ID」で開くと同時に自動再生をさせる機能がChromeでは使えなくなってしまいました。

しかしYouTubeは相変わらず自動再生が出来ています。これ謎です。YouTubeが自動再生できるのだから、ちゅんラヂだって出来る筈と思い色々と試してみました。

行ったことはaudioタグの属性に「muted」を指定しておき、JavaScriptにて再生時にそのミュートを解除する。更にちゅんラジ・プレイヤーを埋め込んであるiframeにautoplay属性を与える。

しかし駄目でした。YouTubeの動画が表示される部分を解析してみましたが謎のままです。GoogleのYouTubeだけ特例で認めるのってズルい気がします。

ところで、何度か手動再生を行ったサイトからの自動再生が可能になります。これはMedia Engagement Indexで管理され「chrome://media-engagement/」で確認できます。このscoreが高くなると自動再生を認めるようです。

Chromeの設定を以下の手順で変更すると以前の状態に戻り自動再生が有効になります。

なお、Firefoxの「media.autoplay.esabled」はfalseにすると自動再生ができなくなります。Chromeの様にiframeを介してた自動再生も出来なくなります。

利用しているHTML Editor (Sublime Text 3)

HTML構文を入力してページレイアウトを作成するにはSublime Text 3を利用しています。ページレイアウトが整った後に文字コンテツを入力する段階では秀丸を利用しています。完全に手打ち派です。

Sublime Text 3はHTML補完機能に優れています。HTMLページを整える作業が効率よくできます。なお日本語入力が使いづらいです。日本語をインライン入力にするにはIMESupportをインストール(*1)しても日本産エディターには遠く及びませしShift-Jisも扱えません。

レイアウトが整った後の文字コンテンツの打ち込みは秀丸を利用しています。私の場合はコンテンツを文字で表現することが主ですので、日本語入力で使いやすい秀丸という選択になります。なお最初から秀丸を利用しない理由は、秀丸のHTML補完機能が中途半端でストレスを感じからです。

といことでHTMLページ作成の初期段階はSublime Text 3を利用して、その後は秀丸で編集を行っています。理想的にはez-HTMLがHTML5対応してくれれば良いのにと思っています。なおMSのVisual StudioのHTML Editorも使ってみましたが鈍足でHTML補完も文字入力もかなり不満でした。

因みにWysiwyg系のHTML Editorは手打ち派の私には有り得ないようなソースコードを吐出す事があり不満です。ソースなんて見ないから気にしない派の人ならWysiwyg系のHTML Editorが良いと思います。

Firefoxの不可解な2回リクエスト

ちゅんラヂあいる(HTTPサーバー)という、ローカルで受信したネットラジオのストリームをちゅんラヂ(ブラウザ)に向けて流し込むプログラムを作成しています。簡単に言えばHTMLとWebサーバーの組み合わせでネットラジオを受信しようという魂胆です。ちゅんラヂあいるを公開できる日が来るかは未定です。Web形態に拘っていますので、こんなややこしい構成にしています。目標は国内のサイマルラジオを一堂に全部を集めてザッピング出来うようにすることです。

閑話休題:

ちゅんラヂあいる作成中にFirefoxの不具合に悩まされました。間違いなく1回しかリクエストしていないのに、サーバー側には2回もリクエストが届くのです。(・_・) 確認しているFirefoxのバージョンは61.0 64bit版です。

Webアプリにとっては同じリクエストが2回も同時に届くなんて致命的です。セッション管理して、ほぼ同時に届いた処理をぐちゃぐちゃにならないようにすれば済む話です。しかしラジオを受信するだけのお遊びソフトでそこまでやってもしょうがない、それにFirefoxの不具合を根本的に解消できることにはなりません。それにサーバーはnode.jsを素のまま使っているだけで、至って簡素なプログラムです。自分用でms単位で同じ処理のリクエストなんて想定してもスペックオーバーになるだけです。

検索してみるとhrefやsrcを空欄にした場合や、レンダリングが二回さるるようなHTMLページで2回リクエストが発生している様です。しかし、今回のケースはHTMLページが存在するわけではなく、URLでサーバーにリクエストするだけの物です。ヒットしたケースとは別物です。

色々試していると同じ種類のリクエストでも正常に1回だけの場合もあります。リクエスト方法を色々が変えてもみました。表が駄目ならXMLHttpRequestにて裏側でリクエストも行ってみました。しかし何をやっても症状の改善がみられません。もう2日も弄り回して回避策を探りました。しかしさっぱり判りませんでした。

そこで、上手くゆくリクエストと駄目なリクエストのURLをじっくり比較してみました。そして発見しました。語尾に「.m3u8」がある場合に、Firefoxが勝手に2回リクエストを行っていました。

 http://~hoge.html?url=~.m3u8 ※このリクエストを行うと2回も送信されてしまう

 http://~hoge.html?url=~_m3u8 ※ドットの部分をアンダースコアに補正すると問題なし

そこでサーバー側の処理にて「_m3u8」→「.m3u8」変換を行うことで、Firefoxの不可解な動作が回避しました。

レアなケースではありますが、これは厄介な不具合です。Firefoxはm3u8に対して何をしようとしているのでしょうか。判ったからといって自力でFirefoxのこの問題を改修できるはずもありません。とりあえずMozillaにフィードバックで送信しておきました。(^^ゞ

それに、ちゅんラヂあいる環境は自分専用で公開の目処が立っていないですし(^○^)

しかし、こんな事があってもFirefoxはメインで使うブラウザです。シンプルな考え方が好きなブラウザです。私はFirefoxを中心に、Chromeでも動作確認を行うようにしています。Edgeは気が向いたら時々。IEに至ってはWebアプリはほとんど動かないことが多く敢えて動作確認対象から除外しています。ビジネス用でIEでの動作が求められる場合以外は無視です。

Firefox+jQueryの$.ajaxで不可解な動作 (サーバーはnode.js)

FirefoxがXMLHttpRequestで「クロスオリジン要求をブロックしました」と不可解な動作のお話です。

エラー内容

「クロスオリジン要求をブロックしました: 同一生成元ポリシーにより、http://127.0.0.1:8071/play?url=http%3A%2F%2Fedge-ads-02-gos1.sharp-stream.com%2Fjazzfmmobile.aac%3Fdevice%3Drpweb にあるリモートリソースの読み込みは拒否されます (理由: CORS 要求が成功しなかった)。」

現象

Firefoxは上記のURLのをサーバーに送信すらせずにエラーを吐きます。そりゃないよ!!。因みにリクエスト先はクロスドメインではありません。

Google Chromeは同じ条件でサーバーにリクエストを送信します。Firefoxでも上記URLの引数「url=」の後ろが上記以外ならリクエストを送信します。

動作環境

ウェブサーバーはnode.jsです。

ブラウザ側のXMLHttpRequestはjQueryの「$.ajax」を利用しています。

ブラウザのアドレス入力欄に直接入力してもエラーになります。

対策方法

GETリクエストをやめて、POSTリクエストで「url=」の引数を渡すようにすることで、Firefoxの不可解な現象を回避できました。

結果

GETからPOSTに変更したことでFirefoxの不可解な現象が解消しました。

なお、以前悩んだFirefoxの「.m3u8」の不可解な動作もこれで回避できることがわかりました。

教訓ですが、GETリクエストの長々としたURL引数で情報を送らずに、素直にPOSTで情報を送ったほうがベターだということです。その時の送る情報はJSON書式のプレーンテキストで送るのが無難です。

本当にクロスドメインPOSTだった時にはどうする!!

実はテスト段階では同一ドメインですが、公開段階ではクロスドメインになりますので、同一オリジンポリシーに引っかかり動作しません。そこでサーバー側で対応する必要があります。

クロスドメインでのPOSTはブラウザが先にOPTIONSメソッドでプリフライトリクエストを送信します。node.jsにはこれに自動的に応答する機能がありません。そこでnode.jsのJavaScriptでOPTIONSリクエストが来たら、以下の様に許可するレスポンスを返さないと、POSTリクエストがブラウザから送信されません。

if (req.method === 'OPTIONS') { var headers = {}; headers["Access-Control-Allow-Origin"] = "*"; headers["Access-Control-Allow-Methods"] = "POST, GET, PUT, DELETE, OPTIONS"; headers["Access-Control-Allow-Credentials"] = false; headers["Access-Control-Max-Age"] = '86400'; // 24 hours headers["Access-Control-Allow-Headers"] = "X-Requested-With, X-HTTP-Method-Override, Content-Type, Accept"; res.writeHead(200, headers); res.end(); } else { //...other requests }

上記で返すヘッダーの内容は必要最低限の許可を返します。この情報はここで得られました。有難う御座いました。

なおContent-Typeを許可してもFirefoxはクロスドメインで本来許可されているタイプ以外は拒絶(CSRF対策?)します。詳しくはオリジン間リソース共有を参照して下さい。そこでデータはJSONオブジェクトではなく、JSON書式のプレーンテキストで送ることで対処する必要があります。

ところでCSRF対策は、ここで作成しているサーバー(node.js)はlocalhost限定物で割愛しています。公開する時には当然ですがCSRF対策しないと駄目です。

音量調整スライダーの可変量と耳で感じる音量変化の感じを近づける

ちゅんラヂ・プレイヤーの音量調整スライダーのお話です。Web Audio APIの音量値を直線的な値で変化させると耳で感じる音量変化に対して不自然でした。耳で感じる音量変化ですがスライダー最小からの音量変化が大きく、スライダーの半分くらいから上で緩やかな音量変化に耳では感じます。耳で感じる変化量が対数グラフっぽい感じです。これでは音量が小さい方の音量調整に不満を感じます。

そこでスライダーの値が小さい方での音量値の変化が小さく、スライダーの値の大きな方で音量値の変化が大きくすることで、耳で感じる変化量が自然になるのではないかと思いました。

まずスライダーの変化範囲を0~30で1ステップ毎の変化(WALKMANと同じ)としました。これを音量値0~1(*1)に間に収まる様に指数計算(=2^G3/2^30)してみるとスライダー値29まで0.5まで緩やかに上がり、30でいきなり1.0となりました。なんじゃこりゃです。

そこで以下の計算式に落ち着きました。同じ整数同士を累乗すると尻上がりな値になり、その値を0~1以下の実数にするという単純なものです。こんな単純な計算でも耳で感じる音量変化がかなり自然になりました。

後で知ったのですが、私が求めた指数っぽい変化をAカーブと云うのだそうです。y=xで信号的に直線的に変化するのがBカーブ、対数っぽいのがCカーブだそうです。オーディオのボリューム調整はAカーブだそうです。こんな感じ。

もっとAカーブを細かく調整したい場合はここが参考になると思われますが、なんか難しくて高等数学が苦手な私には理解不能です。

Google Chromeの頑固なキャッシュを回避

ウェブページを変更し時にhtmlファイルは直ぐに反映されましたがCSSがキャッシュされたままで表示が崩れる現象に遭遇しました。Cstl+Shift+RでリロードさせてもCSSのキャッシュが無効になりませんでした。設定の中の閲覧履歴を消去でやっとCSSが削除されて表示が最新になりました。しかしウェブページを訪れるユーザーはそんな事はしません。

そこでCCSの読み込みURLの引数に変更時日時を指定する事でキャッシュ回避を行いました。仕組み的には引数の値が変わればキャッシュが別もになり読み込みが行われます。

例:<link rel="stylesheet" href="./css/index.css?time=1539181239" type="text/css">

1539181239」の部分には私は更新時点のUNIXタイムをセットしました。