MIMEタイプがaudio/xxxやvideo/xxxなときにやをどうにかこうにかするスクリプトを改造した
以前書いたのは<a>
の直後にエレメントを追加して直接ページ上で再生するやつだったけど、今回のはwindow.open
を使って子ウィンドウを作るタイプにした。
こっちの方がスタイルシートの影響範囲をいちいち考えなくて済むから楽かもしれない。
audio/xxx
やvideo/xxx
なリンクをクリックする度に、子ウィンドウを開いてそこに<audio>
や<video>
な要素を配置します。
既に子ウィンドウが出ている場合は、同じ子ウィンドウの末尾に要素を追加していきます。
それぞれのメディア要素は再生が終わると自身を殺します。
他のすべてのメディア要素が死んでいる状態で、最後のメディア要素が終了すると子ウィンドウが閉じる仕組みです。
(function(){ var checked = []; // 同じURLに複数回リクエストを送るのを防ぐためのリストを格納 var timeout = 30 * 1000; // タイムアウトになるまでの時間(単位:ms) var allowList = ["audio", "video"]; if (!HTMLAnchorElement.prototype.addPopup) { // addPopupメソッドをA要素に追加 Object.defineProperty(HTMLAnchorElement.prototype, "addPopup", { value: function(url){ var self = this; var xhr = new XMLHttpRequest(); // HEADリクエストを送る xhr.open("HEAD", url, true); xhr.onreadystatechange = function(){ if (xhr.readyState === 4) { console.log(xhr.status + " : " + url); if (xhr.status !== 200) return; var header = xhr.getResponseHeader("Content-Type"); var type = header.split("/")[0]; if (allowList.some(function(v){ return v === type })) { // いずれかにヒットすれば true console.log(header + " - OK"); addPopupItem(self, type); } } }; xhr.send(null); window.setTimeout(function(){ // 一定時間でタイムアウト xhr.abort(); }, timeout); } }); } else { return false; } var elems = document.getElementsByTagName("a"); elems = Array.prototype.slice.call(elems); // A要素を全て抽出 for (var i in elems) { var url = elems[i].href; if (url.indexOf("http") === 0) { url = url.indexOf("#") + 1 ? url.split("#")[0] : url; // URLに # を含む場合は除去 if (checked.every(function(li){ return url !== li })) { // 全てとアンマッチなら true checked.push(url); elems[i].addPopup(url); } } } function addPopupItem(target, mediaType) { // ポップアップを生成 target.onclick = function(evt){ if (evt.ctrlKey && evt.altKey) { return true; // Ctrl+Alt を押しながらクリックすれば普段のクリックと同じ効果 } else { openPopup(); return false; } }; function openPopup() { var popup = window.open("", "media-popup", "menubar=no,toolbar=no,location=no,status=no,scrollbars=yes"); var doc = popup.document; var style = doc.createElement("style"); doc.querySelector("head").appendChild(style); var s = style.sheet, i = 0; [ "body {" + "margin: 0;" + "padding: 0;" + "}", "audio {" + "display: block;" + "margin: auto;" + "}" ].forEach(function(value){ s.insertRule(value, i++); }); var element; // 殻の中身を定義 element = doc.createElement(mediaType); element.setAttribute("src", target.href); element.setAttribute("autoplay", "autoplay"); element.setAttribute("controls", "controls"); element.addEventListener("ended", function(){ // 終端まで移動したら自らを削除する this.parentElement.removeChild(this); close(); }, false); doc.body.appendChild(element); var rect = element.getBoundingClientRect(); popup.resizeTo(rect.width + 80, rect.height + 200); function close() { // 再生中のメディアがゼロのとき、子ウィンドウを閉じる doc.querySelectorAll( allowList.join(",") ).length === 0 ? popup.close() : 0; } } } })();
ブックマークレット版
javascript:(function(d,A,c,l,P,E,i,u){l=["audio","video"],A=function(E,t,P){P=function(p,d,S,e,r){p=open("","media-popup","menubar=no,toolbar=no,location=no,status=no,scrollbars=yes"),d=p.document,S=d.createElement("style"),i=0;d.querySelector("head").appendChild(S);["body{margin:0;padding:0}","audio{display:block;margin:auto}"].forEach(function(s){S.sheet.insertRule(s,i++)});e=d.createElement(t);e.src=E.href,e.autoplay="autoplay",e.controls="controls";e.addEventListener("ended",function(){this.parentElement.removeChild(this);d.querySelectorAll(l.join(",")).length===0?p.close():0},false);d.body.appendChild(e);r=e.getBoundingClientRect();p.resizeTo(r.width+80,r.height+200)},E.onclick=function(e){if(e.ctrlKey&&e.altKey)return true;else{P();return false}}},c=[],P=HTMLAnchorElement.prototype;if(P.addPopup)return false;else{Object.defineProperty(P,"addPopup",{value:function(u,S,X){S=this,X=new XMLHttpRequest();X.open("HEAD",u);X.onreadystatechange=function(h,t){if(X.readyState===4){console.log(X.status+" : "+u);if(X.status!==200)return;h=X.getResponseHeader("Content-Type"),t=h.split("/")[0];if(l.some(function(v){return v===t})){console.log(h+" - OK");A(S,t)}}};X.send();setTimeout(function(){X.abort()},3e4)}})}E=d.querySelectorAll("a");for(i=E.length;i;){u=E[--i].href.split("#")[0];if(/^http/.test(u))if(c.every(function(i){return u!==i}))E[i].addPopup(c[c.length]=u)}})(document)
目立たないけど、ブックマークレット版の方はコード量を減らすためにかなりの工夫を凝らしてるつもりです。
可能な限りセミコロンやブレースを削ったり、効率的なビルドイン関数を使ったり、ちまちまやってます。
読んでもらえると楽しいです。たぶん。可読性最悪ですが。