Browse Source

make demo page great again

DIYgod 8 years ago
parent
commit
41578d58cf
11 changed files with 1711 additions and 258 deletions
  1. 78 0
      demo/demo.css
  2. 14 0
      demo/demo.js
  3. 288 254
      demo/index.html
  4. 1321 0
      demo/modernizr.js
  5. 0 0
      dist/DPlayer.min.css
  6. 0 0
      dist/DPlayer.min.js
  7. 0 0
      dist/DPlayer.min.js.map
  8. 2 2
      package.json
  9. 1 1
      src/DPlayer.js
  10. 6 0
      src/DPlayer.scss
  11. 1 1
      src/option.js

+ 78 - 0
demo/demo.css

@@ -0,0 +1,78 @@
+body {
+    max-width: 700px;
+    margin: 0 auto;
+    padding: 45px;
+}
+
+.example {
+    position: relative;
+    margin: 15px 0 0;
+    padding: 39px 19px 14px;
+    background-color: #fff;
+    border-radius: 4px 4px 0 0;
+    border: 1px solid #ddd;
+}
+
+.example:after {
+    content: "Example";
+    position: absolute;
+    top: 0;
+    left: 0;
+    padding: 2px 8px;
+    font-size: 12px;
+    font-weight: bold;
+    background-color: #f5f5f5;
+    color: #9da0a4;
+    border-radius: 4px 0 4px 0;
+}
+
+.highlight {
+    border-radius: 0 0 4px 4px;
+    border: 1px solid #ddd;
+    border-top: none;
+}
+
+.btn {
+    background: #fff;
+    border: 1px solid #ddd;
+    border-radius: 4px;
+    width: 110px;
+    height: 40px;
+    font-size: 14px;
+    margin: 0 0 10px;
+    cursor: pointer;
+    outline: none;
+}
+
+/* github-corner */
+.github-corner:hover .octo-arm {
+    animation: octocat-wave 560ms ease-in-out
+}
+
+@keyframes octocat-wave {
+    0%,
+    100% {
+        transform: rotate(0)
+    }
+    20%,
+    60% {
+        transform: rotate(-25deg)
+    }
+    40%,
+    80% {
+        transform: rotate(10deg)
+    }
+}
+
+@media (max-width:500px) {
+    .github-corner:hover .octo-arm {
+        animation: none
+    }
+    .github-corner .octo-arm {
+        animation: octocat-wave 560ms ease-in-out
+    }
+}
+
+/* modernizr */
+.adownload li.adownload{ color: green; }.canvas li.canvas{ color: green; }.cssanimations li.cssanimations{ color: green; }.csstransforms li.csstransforms{ color: green; }.documentfragment li.documentfragment{ color: green; }.fullscreen li.fullscreen{ color: green; }.localstorage li.localstorage{ color: green; }.svg li.svg{ color: green; }.texttrackapi li.texttrackapi{ color: green; }.track li.track{ color: green; }.todataurljpeg li.todataurljpeg{ color: green; }.todataurlpng li.todataurlpng{ color: green; }.todataurlwebp li.todataurlwebp{ color: green; }.video li.video{ color: green; }.websockets li.websockets{ color: green; }.setclasses li.setclasses{ color: green; }
+.no-adownload li.adownload{ color: red; }.no-canvas li.canvas{ color: red; }.no-cssanimations li.cssanimations{ color: red; }.no-csstransforms li.csstransforms{ color: red; }.no-documentfragment li.documentfragment{ color: red; }.no-fullscreen li.fullscreen{ color: red; }.no-localstorage li.localstorage{ color: red; }.no-svg li.svg{ color: red; }.no-texttrackapi li.texttrackapi{ color: red; }.no-track li.track{ color: red; }.no-todataurljpeg li.todataurljpeg{ color: red; }.no-todataurlpng li.todataurlpng{ color: red; }.no-todataurlwebp li.todataurlwebp{ color: red; }.no-video li.video{ color: red; }.no-websockets li.websockets{ color: red; }.no-setclasses li.setclasses{ color: red; }

+ 14 - 0
demo/demo.js

@@ -0,0 +1,14 @@
+// stats.js: JavaScript Performance Monitor
+const stats = new Stats();
+stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom
+document.body.appendChild(stats.dom);
+function animate () {
+    stats.begin();
+    // monitored code goes here
+    stats.end();
+
+    requestAnimationFrame(animate);
+}
+requestAnimationFrame(animate);
+
+hljs.initHighlightingOnLoad();

+ 288 - 254
demo/index.html

@@ -4,274 +4,308 @@
     <meta charset="UTF-8">
     <title>DPlayer Demo</title>
     <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
-    <style>
-        body {
-            font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
-        }
-        .container {
-            max-width: 40rem;
-            margin-left: auto;
-            margin-right: auto;
-            margin-bottom: 50px;
-        }
-        h1 {
-            font-size: 54px;
-            color: #333;
-            margin: 30px 0 10px;
-        }
-        h2 {
-            font-size: 22px;
-            color: #555;
-        }
-        h3 {
-            font-size: 24px;
-            color: #555;
-        }
-        hr {
-            display: block;
-            width: 7rem;
-            height: 1px;
-            margin: 2.5rem 0;
-            background-color: #eee;
-            border: 0;
-        }
-        a {
-            color: #08c;
-            text-decoration: none;
-        }
-        p {
-            font-size: 18px;
-        }
-    </style>
+    <link rel="stylesheet" href="https://unpkg.com/github-markdown-css">
+    <link rel="stylesheet" href="https://cdn.bootcss.com/highlight.js/9.12.0/styles/github-gist.min.css">
+    <link rel="stylesheet" href="demo.css">
     <link rel="stylesheet" href="../dist/DPlayer.min.css">
+    <script src="../plugin/flv.min.js"></script>
+    <script src="../plugin/hls.min.js"></script>
+    <script src="../dist/DPlayer.min.js"></script>
 </head>
-<body>
-<div class="container">
-    <h1>DPlayer</h1>
-    <h2>Wow, such a lovely HTML5 danmaku video player</h2>
-    <p>Made by <a href="https://www.anotherhome.net/" target="_blank">DIYgod</a>. Available on <a href="https://github.com/DIYgod/DPlayer" target="_blank">GitHub</a>. Licensed SATA.</p>
-    <hr>
-    <h3>Normal</h3>
-    <div id="dplayer1"></div>
-    <button onclick="switchDPlayer()">Switch Video</button>
-    <button onclick="dp1.notice('Notice演示')">Notice</button>
-    <h3>Quality switching</h3>
-    <div id="dplayer6"></div>
-    <button onclick="dp6.switchQuality(1)">Switch quality</button>
-    <h3>Live Video (HTTP Live Streaming, M3U8 format) support</h3>
-    <div id="dplayer3"></div>
-    <h3>FLV format support</h3>
-    <div id="dplayer4"></div>
-    <h3>Bilibili video and danmaku support</h3>
-    <div id="dplayer2"></div>
-    <!--<h3>Segmented videos</h3>
-    <div id="dplayer5"></div>-->
-</div>
-<script src="https://rawgit.com/mrdoob/stats.js/master/build/stats.min.js"></script>
-<script src="../plugin/flv.min.js"></script>
-<script src="../plugin/hls.min.js"></script>
-<script src="../dist/DPlayer.min.js"></script>
-<script>
-    // Normal
-    var dp1 = new DPlayer({
-        element: document.getElementById('dplayer1'),
-        autoplay: false,
-        theme: '#FADFA3',
-        loop: true,
-        screenshot: true,
-        hotkey: true,
-        logo: 'http://devtest.qiniudn.com/DPlayer.png',
+<body class="markdown-body">
+    <p align="center">
+        <img src="https://ws4.sinaimg.cn/large/006tKfTcgy1fhu01y9uy7j305k04s3yc.jpg" alt="ADPlayer" width="100">
+    </p>
+    <h1 align="center" style="margin-top: 16px;">DPlayer</h1>
+    <blockquote>
+        <p>Wow, such a lovely HTML5 danmaku video player</p>
+    </blockquote>
+    <p><a href="https://www.npmjs.com/package/dplayer"><img src="https://img.shields.io/npm/v/dplayer.svg?style=flat-square" alt="npm"></a>
+    <a href="https://github.com/DIYgod/DPlayer/blob/master/LICENSE"><img src="https://img.shields.io/npm/l/dplayer.svg?style=flat-square"></a>
+    <a href="https://david-dm.org/DIYgod/DPlayer#info=devDependencies"><img src="https://img.shields.io/david/dev/DIYgod/dplayer.svg?style=flat-square"></a>
+    <a href="https://www.npmjs.com/package/dplayer"><img src="https://img.shields.io/npm/dt/dplayer.svg?style=flat-square"></a>
+    <a href="https://travis-ci.org/DIYgod/DPlayer"><img src="https://img.shields.io/travis/DIYgod/DPlayer.svg?style=flat-square"></a>
+    <a href="https://github.com/DIYgod/DPlayer#donate"><img src="https://img.shields.io/badge/$-donate-ff69b4.svg?style=flat-square"></a></p>
+    <h2>Quick Start</h2>
+    <div class="example">
+        <div id="dplayer1"></div>
+        <script>
+            var dp1 = new DPlayer({
+                element: document.getElementById('dplayer1'),
+                video: {
+                    url: 'http://devtest.qiniudn.com/若能绽放光芒.mp4',
+                    pic: 'http://devtest.qiniudn.com/若能绽放光芒.png',
+                },
+                danmaku: {
+                    id: '9E2E3368B56CDBB4',
+                    api: 'https://api.prprpr.me/dplayer/'
+                }
+            });
+        </script>
+    </div>
+    <div class="highlight highlight-text-html-basic"><pre><code class="html">&lt;link rel=&quot;stylesheet&quot; href=&quot;dist/DPlayer.min.css&quot;&gt;
+&lt;div id=&quot;dplayer&quot;&gt;&lt;/div&gt;
+&lt;script src=&quot;dist/DPlayer.min.js&quot;&gt;&lt;/script&gt;
+&lt;script&gt;
+    var dp = new DPlayer({
+        element: document.getElementById(&#39;dplayer&#39;),
         video: {
-            url: 'http://devtest.qiniudn.com/若能绽放光芒.mp4',
-            pic: 'http://devtest.qiniudn.com/若能绽放光芒.png',
-            type: 'auto',
+            url: &#39;demo.mp4&#39;,
+            pic: &#39;demo.png&#39;
         },
         danmaku: {
-            id: '9E2E3368B56CDBB4',
-            api: 'https://api.prprpr.me/dplayer/',
-            token: 'tokendemo',
-            maximum: 3000,
-            user: 'DIYgod的女粉'
-        },
-        contextmenu: [
-            {
-                text: '关于作者',
-                link: 'http://diygod.me'
-            },
-            {
-                text: '播放器意见反馈',
-                link: 'https://github.com/DIYgod/DPlayer/issues'
-            },
-            {
-                text: '关于 DPlayer 播放器',
-                link: 'https://github.com/DIYgod/DPlayer'
-            },
-            {
-                text: '自定义右键菜单demo',
-                link: 'https://github.com/DIYgod/DPlayer'
-            },
-        ]
+            id: &#39;demo&#39;,
+            api: &#39;https://api.prprpr.me/dplayer/&#39;
+        }
     });
-    function switchDPlayer() {
-        if (dp1.option.danmaku.id !== '5rGf5Y2X55qu6Z2p') {
-            dp1.switchVideo({
-                url: 'http://devtest.qiniudn.com/微小微-江南皮革厂倒闭了.mp4',
-                pic: 'http://devtest.qiniudn.com/微小微-江南皮革厂倒闭了.jpg',
-                type: 'auto',
-            },
-                {
-                    id: '5rGf5Y2X55qu6Z2p',
+&lt;/script&gt;</code></pre></div>
+    <h2>Options</h2>
+    <div class="example">
+        <button class="btn" onclick="switchDPlayer()">Switch Video</button>
+        <button class="btn" onclick="dp2.notice('Notice演示')">Notice</button>
+        <div id="dplayer2"></div>
+        <script>
+            var dp2 = new DPlayer({
+                element: document.getElementById('dplayer2'),
+                autoplay: false,
+                theme: '#FADFA3',
+                loop: true,
+                screenshot: true,
+                hotkey: true,
+                logo: 'http://devtest.qiniudn.com/DPlayer.png',
+                video: {
+                    url: 'http://devtest.qiniudn.com/若能绽放光芒.mp4',
+                    pic: 'http://devtest.qiniudn.com/若能绽放光芒.png',
+                    type: 'auto',
+                },
+                danmaku: {
+                    id: '9E2E3368B56CDBB42',
                     api: 'https://api.prprpr.me/dplayer/',
                     token: 'tokendemo',
-                    maximum: 3000
-                });
-        }
-        else {
-            dp1.switchVideo({
-                url: 'http://devtest.qiniudn.com/若能绽放光芒.mp4',
+                    maximum: 3000,
+                    user: 'DIYgod'
+                },
+                contextmenu: [
+                    {
+                        text: 'custom contextmenu',
+                        link: 'https://github.com/DIYgod/DPlayer'
+                    },
+                ]
+            });
+            function switchDPlayer() {
+                if (dp2.option.danmaku.id !== '5rGf5Y2X55qu6Z2p') {
+                    dp2.switchVideo({
+                        url: 'http://devtest.qiniudn.com/微小微-江南皮革厂倒闭了.mp4',
+                        pic: 'http://devtest.qiniudn.com/微小微-江南皮革厂倒闭了.jpg',
+                        type: 'auto',
+                    },
+                        {
+                            id: '5rGf5Y2X55qu6Z2p',
+                            api: 'https://api.prprpr.me/dplayer/',
+                            maximum: 3000,
+                            user: 'DIYgod'
+                        });
+                }
+                else {
+                    dp2.switchVideo({
+                        url: 'http://devtest.qiniudn.com/若能绽放光芒.mp4',
+                        pic: 'http://devtest.qiniudn.com/若能绽放光芒.png',
+                        type: 'auto',
+                    },
+                        {
+                            id: '9E2E3368B56CDBB42',
+                            api: 'https://api.prprpr.me/dplayer/',
+                            maximum: 3000,
+                            user: 'DIYgod'
+                        });
+                }
+            }
+        </script>
+    </div>
+    <div class="highlight highlight-text-html-basic">
+        <pre><code class="js">{
+    element: document.getElementById(&#39;dplayer&#39;),
+    autoplay: false,
+    theme: &#39;#FADFA3&#39;,
+    loop: true,
+    screenshot: true,
+    hotkey: true,
+    logo: &#39;logo.png&#39;,
+    video: {
+        url: &#39;demo.mp4&#39;,
+        pic: &#39;demo.png&#39;,
+        type: &#39;auto&#39;,
+    },
+    danmaku: {
+        id: &#39;demo&#39;,
+        api: &#39;https://api.prprpr.me/dplayer/&#39;,
+        token: &#39;demo&#39;,
+        maximum: 3000,
+        user: &#39;DIYgod&#39;
+    },
+    contextmenu: [
+        {
+            text: &#39;custom contextmenu&#39;,
+            link: &#39;https://github.com/DIYgod/DPlayer&#39;
+        },
+    ]
+}</code></pre>
+    </div>
+    <h2>Quality switching</h2>
+    <div class="example">
+        <button class="btn" onclick="dp3.switchQuality(1)">Switch quality</button>
+        <div id="dplayer3"></div>
+        <script>
+        var dp3 = new DPlayer({
+            element: document.getElementById('dplayer3'),
+            video: {
+                quality: [{
+                    name: 'HD',
+                    url: 'http://devtest.qiniudn.com/若能绽放光芒.mp4?1'
+                }, {
+                    name: 'SD',
+                    url: 'http://devtest.qiniudn.com/若能绽放光芒.mp4?2'
+                }],
+                defaultQuality: 0,
                 pic: 'http://devtest.qiniudn.com/若能绽放光芒.png',
                 type: 'auto',
             },
-                {
-                    id: '9E2E3368B56CDBB4',
+            danmaku: {
+                id: '9E2E3368B56CDBB43',
+                api: 'https://api.prprpr.me/dplayer/'
+            }
+        });
+        </script>
+    </div>
+    <div class="highlight highlight-text-html-basic">
+        <pre><code class="js">{
+    video: {
+        quality: [{
+            name: &#39;HD&#39;,
+            url: &#39;demo1.mp4&#39;
+        }, {
+            name: &#39;SD&#39;,
+            url: &#39;demo2.mp4&#39;
+        }],
+        defaultQuality: 0
+    }
+}</code></pre>
+    </div>
+    <h2>Live Video (HTTP Live Streaming, M3U8 format) support</h2>
+    <div class="example">
+        <div id="dplayer4"></div>
+        <script>
+            var dp4 = new DPlayer({
+                element: document.getElementById('dplayer4'),
+                video: {
+                    url: 'http://devtest.qiniudn.com/若能绽放光芒5.m3u8',
+                    pic: 'http://devtest.qiniudn.com/若能绽放光芒.png',
+                    type: 'hls',
+                },
+                danmaku: {
+                    id: '9E2E3368B56CDBB44',
                     api: 'https://api.prprpr.me/dplayer/',
-                    token: 'tokendemo',
-                    maximum: 3000
-                });
-        }
+                }
+            });
+        </script>
+    </div>
+    <div class="highlight highlight-text-html-basic">
+        <pre><code class="js">{
+    video: {
+        url: &#39;demo.m3u8&#39;,
+        pic: &#39;demo.png&#39;,
+        type: &#39;hls&#39;,
     }
+}</code></pre>
+    </div>
+    <h2>FLV support</h2>
+    <div class="example">
+        <div id="dplayer5"></div>
+        <script>
+            var dp5 = new DPlayer({
+                element: document.getElementById('dplayer5'),
+                video: {
+                    url: 'http://devtest.qiniudn.com/【微小微】玖月奇迹-踩踩踩.flv',
+                    pic: 'http://devtest.qiniudn.com/【微小微】玖月奇迹-踩踩踩.jpg',
+                    type: 'flv',
+                },
+                danmaku: {
+                    id: '9E2E3368B56CDBB45',
+                    api: 'https://api.prprpr.me/dplayer/',
+                }
+            });
+        </script>
+    </div>
+    <div class="highlight highlight-text-html-basic">
+        <pre><code class="js">{
+    video: {
+        url: &#39;demo.flv&#39;,
+        pic: &#39;demo.png&#39;,
+        type: &#39;flv&#39;,
+    }
+}</code></pre>
+    </div>
+    <h2>Bilibili video and danmaku support</h2>
+    <div class="example">
+        <div id="dplayer6"></div>
+        <script>
+            var dp5 = new DPlayer({
+                element: document.getElementById('dplayer6'),
+                screenshot: false,
+                video: {
+                    url: 'https://api.prprpr.me/dplayer/video/bilibili?aid=4045652',
+                    pic: 'http://devtest.qiniudn.com/微小微-江南皮革厂倒闭了.jpg'
+                },
+                danmaku: {
+                    id: '9E2E3368B56CDBB46',
+                    api: 'https://api.prprpr.me/dplayer/',
+                    addition: ['https://api.prprpr.me/dplayer/bilibili?aid=4045652']
+                }
+            });
+        </script>
+    </div>
+    <div class="highlight highlight-text-html-basic">
+        <pre><code class="js">{
+    screenshot: false,
+    video: {
+        url: &#39;https://api.prprpr.me/dplayer/video/bilibili?aid=[aid]&#39;
+    },
+    danmaku: {
+        addition: [&#39;https://api.prprpr.me/dplayer/bilibili?aid=[aid]&#39;]
+    }
+}</code></pre>
+    </div>
+    <h2>HTML5 check</h2>
+    <ul>
+        <li class="adownload">a[download]</li>
+        <li class="canvas">Canvas</li>
+        <li class="cssanimations">CSS animations</li>
+        <li class="csstransforms">CSS transforms</li>
+        <li class="documentfragment">Document fragment</li>
+        <li class="fullscreen">Fullscreen</li>
+        <li class="localstorage">Localstorage</li>
+        <li class="svg">SVG</li>
+        <li class="track">Track</li>
+        <li class="todataurljpeg">toDataURL jpeg</li>
+        <li class="todataurlpng">toDataURL png</li>
+        <li class="todataurlwebp">toDataURL webp</li>
+        <li class="video">Video</li>
+        <li class="websockets">WebSockets</li>
+    </ul>
 
-    // Bilibili video and danmaku support
-    var dp2 = new DPlayer({
-        element: document.getElementById('dplayer2'),
-        autoplay: false,
-        theme: '#FADFA3',
-        loop: true,
-        screenshot: false,
-        logo: 'http://devtest.qiniudn.com/DPlayer.png',
-        video: {
-            url: 'https://api.prprpr.me/dplayer/video/bilibili?aid=4045652',
-            pic: 'http://devtest.qiniudn.com/微小微-江南皮革厂倒闭了.jpg',
-            type: 'auto',
-        },
-        danmaku: {
-            id: '5rGf5Y2X55qu6Z2p',
-            api: 'https://api.prprpr.me/dplayer/',
-            token: 'tokendemo',
-            maximum: 3000,
-            addition: ['https://api.prprpr.me/dplayer/bilibili?aid=4045652']
-        }
-    });
-
-    // Live Video (HTTP Live Streaming, M3U8 format) support
-    var dp3 = new DPlayer({
-        element: document.getElementById('dplayer3'),
-        autoplay: false,
-        theme: '#FADFA3',
-        loop: true,
-        screenshot: true,
-        hotkey: true,
-        logo: 'http://devtest.qiniudn.com/DPlayer.png',
-        video: {
-            url: 'http://devtest.qiniudn.com/若能绽放光芒5.m3u8',
-            pic: 'http://devtest.qiniudn.com/若能绽放光芒.png',
-            type: 'hls',
-        },
-        danmaku: {
-            id: '9E2E3368B56CDBB42',
-            api: 'https://api.prprpr.me/dplayer/',
-            token: 'tokendemo',
-            maximum: 3000
-        }
-    });
-
-    // FLV format support
-    var dp4 = new DPlayer({
-        element: document.getElementById('dplayer4'),
-        autoplay: false,
-        theme: '#FADFA3',
-        loop: true,
-        screenshot: true,
-        hotkey: true,
-        logo: 'http://devtest.qiniudn.com/DPlayer.png',
-        video: {
-            url: 'http://devtest.qiniudn.com/【微小微】玖月奇迹-踩踩踩.flv',
-            pic: 'http://devtest.qiniudn.com/【微小微】玖月奇迹-踩踩踩.jpg',
-            type: 'flv',
-        },
-        danmaku: {
-            id: '9E2E3368B56CDBB43',
-            api: 'https://api.prprpr.me/dplayer/',
-            token: 'tokendemo',
-            maximum: 3000
-        }
-    });
-
-    // Segmented videos
-    // var dp5 = new DPlayer({
-    //     element: document.getElementById('dplayer5'),
-    //     autoplay: false,
-    //     theme: '#FADFA3',
-    //     loop: true,
-    //     screenshot: true,
-    //     hotkey: true,
-    //     preload: 'metadata',
-    //     video: {
-    //         url: ['http://devtest.qiniudn.com/若能绽放光芒0.mp4', 'http://devtest.qiniudn.com/若能绽放光芒1.mp4', 'http://devtest.qiniudn.com/若能绽放光芒2.mp4', 'http://devtest.qiniudn.com/若能绽放光芒3.mp4', 'http://devtest.qiniudn.com/若能绽放光芒4.mp4', 'http://devtest.qiniudn.com/若能绽放光芒5.mp4', 'http://devtest.qiniudn.com/若能绽放光芒6.mp4', 'http://devtest.qiniudn.com/若能绽放光芒7.mp4', 'http://devtest.qiniudn.com/若能绽放光芒8.mp4'],
-    //         pic: 'http://devtest.qiniudn.com/若能绽放光芒.png',
-    //         type: 'normal',
-    //     },
-    //     danmaku: {
-    //         id: '9E2E3368B56CDBB40',
-    //         api: 'https://api.prprpr.me/dplayer/',
-    //         token: 'tokendemo',
-    //         maximum: 3000
-    //     }
-    // });
-
-    // Quality switching
-    var dp6 = new DPlayer({
-        element: document.getElementById('dplayer6'),
-        autoplay: false,
-        theme: '#FADFA3',
-        loop: true,
-        screenshot: true,
-        hotkey: true,
-        logo: 'http://devtest.qiniudn.com/DPlayer.png',
-        video: {
-            quality: [{
-                name: '高清',
-                url: 'http://devtest.qiniudn.com/若能绽放光芒.mp4?1'
-            }, {
-                name: '超清',
-                url: 'http://devtest.qiniudn.com/若能绽放光芒.mp4?2'
-            }],
-            defaultQuality: 0,
-            pic: 'http://devtest.qiniudn.com/若能绽放光芒.png',
-            type: 'auto',
-        },
-        danmaku: {
-            id: '9E2E3368B56CDBB4',
-            api: 'https://api.prprpr.me/dplayer/',
-            token: 'tokendemo',
-            maximum: 3000,
-            user: 'DIYgod的女粉'
-        }
-    });
-
-    // stats.js: JavaScript Performance Monitor
-    var stats = new Stats();
-    stats.showPanel(0); // 0: fps, 1: ms, 2: mb, 3+: custom
-    document.body.appendChild(stats.dom);
-    function animate() {
-        stats.begin();
-        // monitored code goes here
-        stats.end();
+    <a href="https://github.com/DIYgod/DPlayer" target="_blank" class="github-corner">
+        <svg width="80" height="80" viewBox="0 0 250 250" style="fill:#151513; color:#fff; position: absolute; top: 0; border: 0; right: 0;">
+            <path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path>
+            <path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
+                fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path>
+            <path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
+                fill="currentColor" class="octo-body"></path>
+        </svg>
+    </a>
 
-        requestAnimationFrame(animate);
-    }
-    requestAnimationFrame(animate);
-</script>
-<a href="https://github.com/DIYgod/DPlayer" target="_blank" class="github-corner"><svg width="80" height="80" viewBox="0 0 250 250" style="fill:#151513; color:#fff; position: absolute; top: 0; border: 0; right: 0;"><path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z"></path><path d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2" fill="currentColor" style="transform-origin: 130px 106px;" class="octo-arm"></path><path d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z" fill="currentColor" class="octo-body"></path></svg></a><style>.github-corner:hover .octo-arm{animation:octocat-wave 560ms ease-in-out}@keyframes octocat-wave{0%,100%{transform:rotate(0)}20%,60%{transform:rotate(-25deg)}40%,80%{transform:rotate(10deg)}}@media (max-width:500px){.github-corner:hover .octo-arm{animation:none}.github-corner .octo-arm{animation:octocat-wave 560ms ease-in-out}}</style>
+    <script src="https://rawgit.com/mrdoob/stats.js/master/build/stats.min.js"></script>
+    <script src="https://cdn.bootcss.com/highlight.js/9.12.0/highlight.min.js"></script>
+    <script src="demo.js"></script>
+    <script src="modernizr.js"></script>
 </body>
 </html>

+ 1321 - 0
demo/modernizr.js

@@ -0,0 +1,1321 @@
+/*!
+ * modernizr v3.5.0
+ * Build https://modernizr.com/download?-adownload-canvas-cssanimations-csstransforms-documentfragment-fullscreen-localstorage-svg-texttrackapi_track-todataurljpeg_todataurlpng_todataurlwebp-video-websockets-setclasses-dontmin
+ *
+ * Copyright (c)
+ *  Faruk Ates
+ *  Paul Irish
+ *  Alex Sexton
+ *  Ryan Seddon
+ *  Patrick Kettner
+ *  Stu Cox
+ *  Richard Herrera
+
+ * MIT License
+ */
+
+/*
+ * Modernizr tests which native CSS3 and HTML5 features are available in the
+ * current UA and makes the results available to you in two ways: as properties on
+ * a global `Modernizr` object, and as classes on the `<html>` element. This
+ * information allows you to progressively enhance your pages with a granular level
+ * of control over the experience.
+*/
+
+;(function(window, document, undefined){
+  var classes = [];
+  
+
+  var tests = [];
+  
+
+  /**
+   *
+   * ModernizrProto is the constructor for Modernizr
+   *
+   * @class
+   * @access public
+   */
+
+  var ModernizrProto = {
+    // The current version, dummy
+    _version: '3.5.0',
+
+    // Any settings that don't work as separate modules
+    // can go in here as configuration.
+    _config: {
+      'classPrefix': '',
+      'enableClasses': true,
+      'enableJSClass': true,
+      'usePrefixes': true
+    },
+
+    // Queue of tests
+    _q: [],
+
+    // Stub these for people who are listening
+    on: function(test, cb) {
+      // I don't really think people should do this, but we can
+      // safe guard it a bit.
+      // -- NOTE:: this gets WAY overridden in src/addTest for actual async tests.
+      // This is in case people listen to synchronous tests. I would leave it out,
+      // but the code to *disallow* sync tests in the real version of this
+      // function is actually larger than this.
+      var self = this;
+      setTimeout(function() {
+        cb(self[test]);
+      }, 0);
+    },
+
+    addTest: function(name, fn, options) {
+      tests.push({name: name, fn: fn, options: options});
+    },
+
+    addAsyncTest: function(fn) {
+      tests.push({name: null, fn: fn});
+    }
+  };
+
+  
+
+  // Fake some of Object.create so we can force non test results to be non "own" properties.
+  var Modernizr = function() {};
+  Modernizr.prototype = ModernizrProto;
+
+  // Leak modernizr globally when you `require` it rather than force it here.
+  // Overwrite name so constructor name is nicer :D
+  Modernizr = new Modernizr();
+
+  
+/*!
+{
+  "name": "SVG",
+  "property": "svg",
+  "caniuse": "svg",
+  "tags": ["svg"],
+  "authors": ["Erik Dahlstrom"],
+  "polyfills": [
+    "svgweb",
+    "raphael",
+    "amplesdk",
+    "canvg",
+    "svg-boilerplate",
+    "sie",
+    "dojogfx",
+    "fabricjs"
+  ]
+}
+!*/
+/* DOC
+Detects support for SVG in `<embed>` or `<object>` elements.
+*/
+
+  Modernizr.addTest('svg', !!document.createElementNS && !!document.createElementNS('http://www.w3.org/2000/svg', 'svg').createSVGRect);
+
+/*!
+{
+  "name": "WebSockets Support",
+  "property": "websockets",
+  "authors": ["Phread [fearphage]", "Mike Sherov [mikesherov]", "Burak Yigit Kaya [BYK]"],
+  "caniuse": "websockets",
+  "tags": ["html5"],
+  "warnings": [
+    "This test will reject any old version of WebSockets even if it is not prefixed such as in Safari 5.1"
+  ],
+  "notes": [{
+    "name": "CLOSING State and Spec",
+    "href": "https://www.w3.org/TR/websockets/#the-websocket-interface"
+  }],
+  "polyfills": [
+    "sockjs",
+    "socketio",
+    "kaazing-websocket-gateway",
+    "websocketjs",
+    "atmosphere",
+    "graceful-websocket",
+    "portal",
+    "datachannel"
+  ]
+}
+!*/
+
+  var supports = false;
+  try {
+    supports = 'WebSocket' in window && window.WebSocket.CLOSING === 2;
+  } catch (e) {}
+  Modernizr.addTest('websockets', supports);
+
+/*!
+{
+  "name": "Local Storage",
+  "property": "localstorage",
+  "caniuse": "namevalue-storage",
+  "tags": ["storage"],
+  "knownBugs": [],
+  "notes": [],
+  "warnings": [],
+  "polyfills": [
+    "joshuabell-polyfill",
+    "cupcake",
+    "storagepolyfill",
+    "amplifyjs",
+    "yui-cacheoffline"
+  ]
+}
+!*/
+
+  // In FF4, if disabled, window.localStorage should === null.
+
+  // Normally, we could not test that directly and need to do a
+  //   `('localStorage' in window)` test first because otherwise Firefox will
+  //   throw bugzil.la/365772 if cookies are disabled
+
+  // Similarly, in Chrome with "Block third-party cookies and site data" enabled,
+  // attempting to access `window.sessionStorage` will throw an exception. crbug.com/357625
+
+  // Also in iOS5 Private Browsing mode, attempting to use localStorage.setItem
+  // will throw the exception:
+  //   QUOTA_EXCEEDED_ERROR DOM Exception 22.
+  // Peculiarly, getItem and removeItem calls do not throw.
+
+  // Because we are forced to try/catch this, we'll go aggressive.
+
+  // Just FWIW: IE8 Compat mode supports these features completely:
+  //   www.quirksmode.org/dom/html5.html
+  // But IE8 doesn't support either with local files
+
+  Modernizr.addTest('localstorage', function() {
+    var mod = 'modernizr';
+    try {
+      localStorage.setItem(mod, mod);
+      localStorage.removeItem(mod);
+      return true;
+    } catch (e) {
+      return false;
+    }
+  });
+
+
+  /**
+   * is returns a boolean if the typeof an obj is exactly type.
+   *
+   * @access private
+   * @function is
+   * @param {*} obj - A thing we want to check the type of
+   * @param {string} type - A string to compare the typeof against
+   * @returns {boolean}
+   */
+
+  function is(obj, type) {
+    return typeof obj === type;
+  }
+  ;
+
+  /**
+   * Run through all tests and detect their support in the current UA.
+   *
+   * @access private
+   */
+
+  function testRunner() {
+    var featureNames;
+    var feature;
+    var aliasIdx;
+    var result;
+    var nameIdx;
+    var featureName;
+    var featureNameSplit;
+
+    for (var featureIdx in tests) {
+      if (tests.hasOwnProperty(featureIdx)) {
+        featureNames = [];
+        feature = tests[featureIdx];
+        // run the test, throw the return value into the Modernizr,
+        // then based on that boolean, define an appropriate className
+        // and push it into an array of classes we'll join later.
+        //
+        // If there is no name, it's an 'async' test that is run,
+        // but not directly added to the object. That should
+        // be done with a post-run addTest call.
+        if (feature.name) {
+          featureNames.push(feature.name.toLowerCase());
+
+          if (feature.options && feature.options.aliases && feature.options.aliases.length) {
+            // Add all the aliases into the names list
+            for (aliasIdx = 0; aliasIdx < feature.options.aliases.length; aliasIdx++) {
+              featureNames.push(feature.options.aliases[aliasIdx].toLowerCase());
+            }
+          }
+        }
+
+        // Run the test, or use the raw value if it's not a function
+        result = is(feature.fn, 'function') ? feature.fn() : feature.fn;
+
+
+        // Set each of the names on the Modernizr object
+        for (nameIdx = 0; nameIdx < featureNames.length; nameIdx++) {
+          featureName = featureNames[nameIdx];
+          // Support dot properties as sub tests. We don't do checking to make sure
+          // that the implied parent tests have been added. You must call them in
+          // order (either in the test, or make the parent test a dependency).
+          //
+          // Cap it to TWO to make the logic simple and because who needs that kind of subtesting
+          // hashtag famous last words
+          featureNameSplit = featureName.split('.');
+
+          if (featureNameSplit.length === 1) {
+            Modernizr[featureNameSplit[0]] = result;
+          } else {
+            // cast to a Boolean, if not one already
+            if (Modernizr[featureNameSplit[0]] && !(Modernizr[featureNameSplit[0]] instanceof Boolean)) {
+              Modernizr[featureNameSplit[0]] = new Boolean(Modernizr[featureNameSplit[0]]);
+            }
+
+            Modernizr[featureNameSplit[0]][featureNameSplit[1]] = result;
+          }
+
+          classes.push((result ? '' : 'no-') + featureNameSplit.join('-'));
+        }
+      }
+    }
+  }
+  ;
+
+  /**
+   * docElement is a convenience wrapper to grab the root element of the document
+   *
+   * @access private
+   * @returns {HTMLElement|SVGElement} The root element of the document
+   */
+
+  var docElement = document.documentElement;
+  
+/*!
+{
+  "name": "Document Fragment",
+  "property": "documentfragment",
+  "notes": [{
+    "name": "W3C DOM Level 1 Reference",
+    "href": "https://www.w3.org/TR/REC-DOM-Level-1/level-one-core.html#ID-B63ED1A3"
+  }, {
+    "name": "SitePoint Reference",
+    "href": "http://reference.sitepoint.com/javascript/DocumentFragment"
+  }, {
+    "name": "QuirksMode Compatibility Tables",
+    "href": "http://www.quirksmode.org/m/w3c_core.html#t112"
+  }],
+  "authors": ["Ron Waldon (@jokeyrhyme)"],
+  "knownBugs": ["false-positive on Blackberry 9500, see QuirksMode note"],
+  "tags": []
+}
+!*/
+/* DOC
+Append multiple elements to the DOM within a single insertion.
+*/
+
+  Modernizr.addTest('documentfragment', function() {
+    return 'createDocumentFragment' in document &&
+      'appendChild' in docElement;
+  });
+
+
+  /**
+   * A convenience helper to check if the document we are running in is an SVG document
+   *
+   * @access private
+   * @returns {boolean}
+   */
+
+  var isSVG = docElement.nodeName.toLowerCase() === 'svg';
+  
+
+  /**
+   * setClasses takes an array of class names and adds them to the root element
+   *
+   * @access private
+   * @function setClasses
+   * @param {string[]} classes - Array of class names
+   */
+
+  // Pass in an and array of class names, e.g.:
+  //  ['no-webp', 'borderradius', ...]
+  function setClasses(classes) {
+    var className = docElement.className;
+    var classPrefix = Modernizr._config.classPrefix || '';
+
+    if (isSVG) {
+      className = className.baseVal;
+    }
+
+    // Change `no-js` to `js` (independently of the `enableClasses` option)
+    // Handle classPrefix on this too
+    if (Modernizr._config.enableJSClass) {
+      var reJS = new RegExp('(^|\\s)' + classPrefix + 'no-js(\\s|$)');
+      className = className.replace(reJS, '$1' + classPrefix + 'js$2');
+    }
+
+    if (Modernizr._config.enableClasses) {
+      // Add the new classes
+      className += ' ' + classPrefix + classes.join(' ' + classPrefix);
+      if (isSVG) {
+        docElement.className.baseVal = className;
+      } else {
+        docElement.className = className;
+      }
+    }
+
+  }
+
+  ;
+
+  /**
+   * createElement is a convenience wrapper around document.createElement. Since we
+   * use createElement all over the place, this allows for (slightly) smaller code
+   * as well as abstracting away issues with creating elements in contexts other than
+   * HTML documents (e.g. SVG documents).
+   *
+   * @access private
+   * @function createElement
+   * @returns {HTMLElement|SVGElement} An HTML or SVG element
+   */
+
+  function createElement() {
+    if (typeof document.createElement !== 'function') {
+      // This is the case in IE7, where the type of createElement is "object".
+      // For this reason, we cannot call apply() as Object is not a Function.
+      return document.createElement(arguments[0]);
+    } else if (isSVG) {
+      return document.createElementNS.call(document, 'http://www.w3.org/2000/svg', arguments[0]);
+    } else {
+      return document.createElement.apply(document, arguments);
+    }
+  }
+
+  ;
+/*!
+{
+  "name": "Canvas",
+  "property": "canvas",
+  "caniuse": "canvas",
+  "tags": ["canvas", "graphics"],
+  "polyfills": ["flashcanvas", "excanvas", "slcanvas", "fxcanvas"]
+}
+!*/
+/* DOC
+Detects support for the `<canvas>` element for 2D drawing.
+*/
+
+  // On the S60 and BB Storm, getContext exists, but always returns undefined
+  // so we actually have to call getContext() to verify
+  // github.com/Modernizr/Modernizr/issues/issue/97/
+  Modernizr.addTest('canvas', function() {
+    var elem = createElement('canvas');
+    return !!(elem.getContext && elem.getContext('2d'));
+  });
+
+/*!
+{
+  "name": "HTML5 Video",
+  "property": "video",
+  "caniuse": "video",
+  "tags": ["html5"],
+  "knownBugs": [
+    "Without QuickTime, `Modernizr.video.h264` will be `undefined`; https://github.com/Modernizr/Modernizr/issues/546"
+  ],
+  "polyfills": [
+    "html5media",
+    "mediaelementjs",
+    "sublimevideo",
+    "videojs",
+    "leanbackplayer",
+    "videoforeverybody"
+  ]
+}
+!*/
+/* DOC
+Detects support for the video element, as well as testing what types of content it supports.
+
+Subproperties are provided to describe support for `ogg`, `h264` and `webm` formats, e.g.:
+
+```javascript
+Modernizr.video         // true
+Modernizr.video.ogg     // 'probably'
+```
+*/
+
+  // Codec values from : github.com/NielsLeenheer/html5test/blob/9106a8/index.html#L845
+  //                     thx to NielsLeenheer and zcorpan
+
+  // Note: in some older browsers, "no" was a return value instead of empty string.
+  //   It was live in FF3.5.0 and 3.5.1, but fixed in 3.5.2
+  //   It was also live in Safari 4.0.0 - 4.0.4, but fixed in 4.0.5
+
+  Modernizr.addTest('video', function() {
+    var elem = createElement('video');
+    var bool = false;
+
+    // IE9 Running on Windows Server SKU can cause an exception to be thrown, bug #224
+    try {
+      bool = !!elem.canPlayType
+      if (bool) {
+        bool = new Boolean(bool);
+        bool.ogg = elem.canPlayType('video/ogg; codecs="theora"').replace(/^no$/, '');
+
+        // Without QuickTime, this value will be `undefined`. github.com/Modernizr/Modernizr/issues/546
+        bool.h264 = elem.canPlayType('video/mp4; codecs="avc1.42E01E"').replace(/^no$/, '');
+
+        bool.webm = elem.canPlayType('video/webm; codecs="vp8, vorbis"').replace(/^no$/, '');
+
+        bool.vp9 = elem.canPlayType('video/webm; codecs="vp9"').replace(/^no$/, '');
+
+        bool.hls = elem.canPlayType('application/x-mpegURL; codecs="avc1.42E01E"').replace(/^no$/, '');
+      }
+    } catch (e) {}
+
+    return bool;
+  });
+
+/*!
+{
+  "name": "a[download] Attribute",
+  "property": "adownload",
+  "caniuse" : "download",
+  "tags": ["media", "attribute"],
+  "builderAliases": ["a_download"],
+  "notes": [{
+    "name": "WhatWG Reference",
+    "href": "https://developers.whatwg.org/links.html#downloading-resources"
+  }]
+}
+!*/
+/* DOC
+When used on an `<a>`, this attribute signifies that the resource it points to should be downloaded by the browser rather than navigating to it.
+*/
+
+  Modernizr.addTest('adownload', !window.externalHost && 'download' in createElement('a'));
+
+/*!
+{
+  "name": "canvas.toDataURL type support",
+  "property": ["todataurljpeg", "todataurlpng", "todataurlwebp"],
+  "tags": ["canvas"],
+  "builderAliases": ["canvas_todataurl_type"],
+  "async" : false,
+  "notes": [{
+    "name": "MDN article",
+    "href": "https://developer.mozilla.org/en-US/docs/Web/API/HTMLCanvasElement.toDataURL"
+  }]
+}
+!*/
+
+
+  var canvas = createElement('canvas');
+
+  Modernizr.addTest('todataurljpeg', function() {
+    return !!Modernizr.canvas && canvas.toDataURL('image/jpeg').indexOf('data:image/jpeg') === 0;
+  });
+  Modernizr.addTest('todataurlpng', function() {
+    return !!Modernizr.canvas && canvas.toDataURL('image/png').indexOf('data:image/png') === 0;
+  });
+  Modernizr.addTest('todataurlwebp', function() {
+    var supports = false;
+
+    // firefox 3 throws an error when you use an "invalid" toDataUrl
+    try {
+      supports = !!Modernizr.canvas && canvas.toDataURL('image/webp').indexOf('data:image/webp') === 0;
+    } catch (e) {}
+
+    return supports;
+  });
+
+
+/*!
+{
+  "name": "Track element and Timed Text Track",
+  "property": ["texttrackapi", "track"],
+  "tags": ["elem"],
+  "builderAliases": ["elem_track"],
+  "authors": ["Addy Osmani"],
+  "notes": [{
+    "name": "W3 track Element Spec",
+    "href": "http://www.w3.org/TR/html5/video.html#the-track-element"
+  },{
+    "name": "W3 track API Spec",
+    "href": "http://www.w3.org/TR/html5/media-elements.html#text-track-api"
+  }],
+  "warnings": ["While IE10 has implemented the track element, IE10 does not expose the underlying APIs to create timed text tracks by JS (really sad)"]
+}
+!*/
+
+  Modernizr.addTest('texttrackapi', typeof (createElement('video').addTextTrack) === 'function');
+
+  // a more strict test for track including UI support: document.createElement('track').kind === 'subtitles'
+  Modernizr.addTest('track', 'kind' in createElement('track'));
+
+
+  /**
+   * cssToDOM takes a kebab-case string and converts it to camelCase
+   * e.g. box-sizing -> boxSizing
+   *
+   * @access private
+   * @function cssToDOM
+   * @param {string} name - String name of kebab-case prop we want to convert
+   * @returns {string} The camelCase version of the supplied name
+   */
+
+  function cssToDOM(name) {
+    return name.replace(/([a-z])-([a-z])/g, function(str, m1, m2) {
+      return m1 + m2.toUpperCase();
+    }).replace(/^-/, '');
+  }
+  ;
+
+  /**
+   * If the browsers follow the spec, then they would expose vendor-specific styles as:
+   *   elem.style.WebkitBorderRadius
+   * instead of something like the following (which is technically incorrect):
+   *   elem.style.webkitBorderRadius
+
+   * WebKit ghosts their properties in lowercase but Opera & Moz do not.
+   * Microsoft uses a lowercase `ms` instead of the correct `Ms` in IE8+
+   *   erik.eae.net/archives/2008/03/10/21.48.10/
+
+   * More here: github.com/Modernizr/Modernizr/issues/issue/21
+   *
+   * @access private
+   * @returns {string} The string representing the vendor-specific style properties
+   */
+
+  var omPrefixes = 'Moz O ms Webkit';
+  
+
+  var cssomPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.split(' ') : []);
+  ModernizrProto._cssomPrefixes = cssomPrefixes;
+  
+
+  /**
+   * atRule returns a given CSS property at-rule (eg @keyframes), possibly in
+   * some prefixed form, or false, in the case of an unsupported rule
+   *
+   * @memberof Modernizr
+   * @name Modernizr.atRule
+   * @optionName Modernizr.atRule()
+   * @optionProp atRule
+   * @access public
+   * @function atRule
+   * @param {string} prop - String name of the @-rule to test for
+   * @returns {string|boolean} The string representing the (possibly prefixed)
+   * valid version of the @-rule, or `false` when it is unsupported.
+   * @example
+   * ```js
+   *  var keyframes = Modernizr.atRule('@keyframes');
+   *
+   *  if (keyframes) {
+   *    // keyframes are supported
+   *    // could be `@-webkit-keyframes` or `@keyframes`
+   *  } else {
+   *    // keyframes === `false`
+   *  }
+   * ```
+   *
+   */
+
+  var atRule = function(prop) {
+    var length = prefixes.length;
+    var cssrule = window.CSSRule;
+    var rule;
+
+    if (typeof cssrule === 'undefined') {
+      return undefined;
+    }
+
+    if (!prop) {
+      return false;
+    }
+
+    // remove literal @ from beginning of provided property
+    prop = prop.replace(/^@/, '');
+
+    // CSSRules use underscores instead of dashes
+    rule = prop.replace(/-/g, '_').toUpperCase() + '_RULE';
+
+    if (rule in cssrule) {
+      return '@' + prop;
+    }
+
+    for (var i = 0; i < length; i++) {
+      // prefixes gives us something like -o-, and we want O_
+      var prefix = prefixes[i];
+      var thisRule = prefix.toUpperCase() + '_' + rule;
+
+      if (thisRule in cssrule) {
+        return '@-' + prefix.toLowerCase() + '-' + prop;
+      }
+    }
+
+    return false;
+  };
+
+  ModernizrProto.atRule = atRule;
+
+  
+
+  /**
+   * List of JavaScript DOM values used for tests
+   *
+   * @memberof Modernizr
+   * @name Modernizr._domPrefixes
+   * @optionName Modernizr._domPrefixes
+   * @optionProp domPrefixes
+   * @access public
+   * @example
+   *
+   * Modernizr._domPrefixes is exactly the same as [_prefixes](#modernizr-_prefixes), but rather
+   * than kebab-case properties, all properties are their Capitalized variant
+   *
+   * ```js
+   * Modernizr._domPrefixes === [ "Moz", "O", "ms", "Webkit" ];
+   * ```
+   */
+
+  var domPrefixes = (ModernizrProto._config.usePrefixes ? omPrefixes.toLowerCase().split(' ') : []);
+  ModernizrProto._domPrefixes = domPrefixes;
+  
+
+
+  /**
+   * contains checks to see if a string contains another string
+   *
+   * @access private
+   * @function contains
+   * @param {string} str - The string we want to check for substrings
+   * @param {string} substr - The substring we want to search the first string for
+   * @returns {boolean}
+   */
+
+  function contains(str, substr) {
+    return !!~('' + str).indexOf(substr);
+  }
+
+  ;
+
+  /**
+   * fnBind is a super small [bind](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function/bind) polyfill.
+   *
+   * @access private
+   * @function fnBind
+   * @param {function} fn - a function you want to change `this` reference to
+   * @param {object} that - the `this` you want to call the function with
+   * @returns {function} The wrapped version of the supplied function
+   */
+
+  function fnBind(fn, that) {
+    return function() {
+      return fn.apply(that, arguments);
+    };
+  }
+
+  ;
+
+  /**
+   * testDOMProps is a generic DOM property test; if a browser supports
+   *   a certain property, it won't return undefined for it.
+   *
+   * @access private
+   * @function testDOMProps
+   * @param {array.<string>} props - An array of properties to test for
+   * @param {object} obj - An object or Element you want to use to test the parameters again
+   * @param {boolean|object} elem - An Element to bind the property lookup again. Use `false` to prevent the check
+   * @returns {false|*} returns false if the prop is unsupported, otherwise the value that is supported
+   */
+  function testDOMProps(props, obj, elem) {
+    var item;
+
+    for (var i in props) {
+      if (props[i] in obj) {
+
+        // return the property name as a string
+        if (elem === false) {
+          return props[i];
+        }
+
+        item = obj[props[i]];
+
+        // let's bind a function
+        if (is(item, 'function')) {
+          // bind to obj unless overriden
+          return fnBind(item, elem || obj);
+        }
+
+        // return the unbound function or obj or value
+        return item;
+      }
+    }
+    return false;
+  }
+
+  ;
+
+  /**
+   * Create our "modernizr" element that we do most feature tests on.
+   *
+   * @access private
+   */
+
+  var modElem = {
+    elem: createElement('modernizr')
+  };
+
+  // Clean up this element
+  Modernizr._q.push(function() {
+    delete modElem.elem;
+  });
+
+  
+
+  var mStyle = {
+    style: modElem.elem.style
+  };
+
+  // kill ref for gc, must happen before mod.elem is removed, so we unshift on to
+  // the front of the queue.
+  Modernizr._q.unshift(function() {
+    delete mStyle.style;
+  });
+
+  
+
+  /**
+   * domToCSS takes a camelCase string and converts it to kebab-case
+   * e.g. boxSizing -> box-sizing
+   *
+   * @access private
+   * @function domToCSS
+   * @param {string} name - String name of camelCase prop we want to convert
+   * @returns {string} The kebab-case version of the supplied name
+   */
+
+  function domToCSS(name) {
+    return name.replace(/([A-Z])/g, function(str, m1) {
+      return '-' + m1.toLowerCase();
+    }).replace(/^ms-/, '-ms-');
+  }
+  ;
+
+
+  /**
+   * wrapper around getComputedStyle, to fix issues with Firefox returning null when
+   * called inside of a hidden iframe
+   *
+   * @access private
+   * @function computedStyle
+   * @param {HTMLElement|SVGElement} - The element we want to find the computed styles of
+   * @param {string|null} [pseudoSelector]- An optional pseudo element selector (e.g. :before), of null if none
+   * @returns {CSSStyleDeclaration}
+   */
+
+  function computedStyle(elem, pseudo, prop) {
+    var result;
+
+    if ('getComputedStyle' in window) {
+      result = getComputedStyle.call(window, elem, pseudo);
+      var console = window.console;
+
+      if (result !== null) {
+        if (prop) {
+          result = result.getPropertyValue(prop);
+        }
+      } else {
+        if (console) {
+          var method = console.error ? 'error' : 'log';
+          console[method].call(console, 'getComputedStyle returning null, its possible modernizr test results are inaccurate');
+        }
+      }
+    } else {
+      result = !pseudo && elem.currentStyle && elem.currentStyle[prop];
+    }
+
+    return result;
+  }
+
+  ;
+
+  /**
+   * getBody returns the body of a document, or an element that can stand in for
+   * the body if a real body does not exist
+   *
+   * @access private
+   * @function getBody
+   * @returns {HTMLElement|SVGElement} Returns the real body of a document, or an
+   * artificially created element that stands in for the body
+   */
+
+  function getBody() {
+    // After page load injecting a fake body doesn't work so check if body exists
+    var body = document.body;
+
+    if (!body) {
+      // Can't use the real body create a fake one.
+      body = createElement(isSVG ? 'svg' : 'body');
+      body.fake = true;
+    }
+
+    return body;
+  }
+
+  ;
+
+  /**
+   * injectElementWithStyles injects an element with style element and some CSS rules
+   *
+   * @access private
+   * @function injectElementWithStyles
+   * @param {string} rule - String representing a css rule
+   * @param {function} callback - A function that is used to test the injected element
+   * @param {number} [nodes] - An integer representing the number of additional nodes you want injected
+   * @param {string[]} [testnames] - An array of strings that are used as ids for the additional nodes
+   * @returns {boolean}
+   */
+
+  function injectElementWithStyles(rule, callback, nodes, testnames) {
+    var mod = 'modernizr';
+    var style;
+    var ret;
+    var node;
+    var docOverflow;
+    var div = createElement('div');
+    var body = getBody();
+
+    if (parseInt(nodes, 10)) {
+      // In order not to give false positives we create a node for each test
+      // This also allows the method to scale for unspecified uses
+      while (nodes--) {
+        node = createElement('div');
+        node.id = testnames ? testnames[nodes] : mod + (nodes + 1);
+        div.appendChild(node);
+      }
+    }
+
+    style = createElement('style');
+    style.type = 'text/css';
+    style.id = 's' + mod;
+
+    // IE6 will false positive on some tests due to the style element inside the test div somehow interfering offsetHeight, so insert it into body or fakebody.
+    // Opera will act all quirky when injecting elements in documentElement when page is served as xml, needs fakebody too. #270
+    (!body.fake ? div : body).appendChild(style);
+    body.appendChild(div);
+
+    if (style.styleSheet) {
+      style.styleSheet.cssText = rule;
+    } else {
+      style.appendChild(document.createTextNode(rule));
+    }
+    div.id = mod;
+
+    if (body.fake) {
+      //avoid crashing IE8, if background image is used
+      body.style.background = '';
+      //Safari 5.13/5.1.4 OSX stops loading if ::-webkit-scrollbar is used and scrollbars are visible
+      body.style.overflow = 'hidden';
+      docOverflow = docElement.style.overflow;
+      docElement.style.overflow = 'hidden';
+      docElement.appendChild(body);
+    }
+
+    ret = callback(div, rule);
+    // If this is done after page load we don't want to remove the body so check if body exists
+    if (body.fake) {
+      body.parentNode.removeChild(body);
+      docElement.style.overflow = docOverflow;
+      // Trigger layout so kinetic scrolling isn't disabled in iOS6+
+      // eslint-disable-next-line
+      docElement.offsetHeight;
+    } else {
+      div.parentNode.removeChild(div);
+    }
+
+    return !!ret;
+
+  }
+
+  ;
+
+  /**
+   * nativeTestProps allows for us to use native feature detection functionality if available.
+   * some prefixed form, or false, in the case of an unsupported rule
+   *
+   * @access private
+   * @function nativeTestProps
+   * @param {array} props - An array of property names
+   * @param {string} value - A string representing the value we want to check via @supports
+   * @returns {boolean|undefined} A boolean when @supports exists, undefined otherwise
+   */
+
+  // Accepts a list of property names and a single value
+  // Returns `undefined` if native detection not available
+  function nativeTestProps(props, value) {
+    var i = props.length;
+    // Start with the JS API: http://www.w3.org/TR/css3-conditional/#the-css-interface
+    if ('CSS' in window && 'supports' in window.CSS) {
+      // Try every prefixed variant of the property
+      while (i--) {
+        if (window.CSS.supports(domToCSS(props[i]), value)) {
+          return true;
+        }
+      }
+      return false;
+    }
+    // Otherwise fall back to at-rule (for Opera 12.x)
+    else if ('CSSSupportsRule' in window) {
+      // Build a condition string for every prefixed variant
+      var conditionText = [];
+      while (i--) {
+        conditionText.push('(' + domToCSS(props[i]) + ':' + value + ')');
+      }
+      conditionText = conditionText.join(' or ');
+      return injectElementWithStyles('@supports (' + conditionText + ') { #modernizr { position: absolute; } }', function(node) {
+        return computedStyle(node, null, 'position') == 'absolute';
+      });
+    }
+    return undefined;
+  }
+  ;
+
+  // testProps is a generic CSS / DOM property test.
+
+  // In testing support for a given CSS property, it's legit to test:
+  //    `elem.style[styleName] !== undefined`
+  // If the property is supported it will return an empty string,
+  // if unsupported it will return undefined.
+
+  // We'll take advantage of this quick test and skip setting a style
+  // on our modernizr element, but instead just testing undefined vs
+  // empty string.
+
+  // Property names can be provided in either camelCase or kebab-case.
+
+  function testProps(props, prefixed, value, skipValueTest) {
+    skipValueTest = is(skipValueTest, 'undefined') ? false : skipValueTest;
+
+    // Try native detect first
+    if (!is(value, 'undefined')) {
+      var result = nativeTestProps(props, value);
+      if (!is(result, 'undefined')) {
+        return result;
+      }
+    }
+
+    // Otherwise do it properly
+    var afterInit, i, propsLength, prop, before;
+
+    // If we don't have a style element, that means we're running async or after
+    // the core tests, so we'll need to create our own elements to use
+
+    // inside of an SVG element, in certain browsers, the `style` element is only
+    // defined for valid tags. Therefore, if `modernizr` does not have one, we
+    // fall back to a less used element and hope for the best.
+    // for strict XHTML browsers the hardly used samp element is used
+    var elems = ['modernizr', 'tspan', 'samp'];
+    while (!mStyle.style && elems.length) {
+      afterInit = true;
+      mStyle.modElem = createElement(elems.shift());
+      mStyle.style = mStyle.modElem.style;
+    }
+
+    // Delete the objects if we created them.
+    function cleanElems() {
+      if (afterInit) {
+        delete mStyle.style;
+        delete mStyle.modElem;
+      }
+    }
+
+    propsLength = props.length;
+    for (i = 0; i < propsLength; i++) {
+      prop = props[i];
+      before = mStyle.style[prop];
+
+      if (contains(prop, '-')) {
+        prop = cssToDOM(prop);
+      }
+
+      if (mStyle.style[prop] !== undefined) {
+
+        // If value to test has been passed in, do a set-and-check test.
+        // 0 (integer) is a valid property value, so check that `value` isn't
+        // undefined, rather than just checking it's truthy.
+        if (!skipValueTest && !is(value, 'undefined')) {
+
+          // Needs a try catch block because of old IE. This is slow, but will
+          // be avoided in most cases because `skipValueTest` will be used.
+          try {
+            mStyle.style[prop] = value;
+          } catch (e) {}
+
+          // If the property value has changed, we assume the value used is
+          // supported. If `value` is empty string, it'll fail here (because
+          // it hasn't changed), which matches how browsers have implemented
+          // CSS.supports()
+          if (mStyle.style[prop] != before) {
+            cleanElems();
+            return prefixed == 'pfx' ? prop : true;
+          }
+        }
+        // Otherwise just return true, or the property name if this is a
+        // `prefixed()` call
+        else {
+          cleanElems();
+          return prefixed == 'pfx' ? prop : true;
+        }
+      }
+    }
+    cleanElems();
+    return false;
+  }
+
+  ;
+
+  /**
+   * testPropsAll tests a list of DOM properties we want to check against.
+   * We specify literally ALL possible (known and/or likely) properties on
+   * the element including the non-vendor prefixed one, for forward-
+   * compatibility.
+   *
+   * @access private
+   * @function testPropsAll
+   * @param {string} prop - A string of the property to test for
+   * @param {string|object} [prefixed] - An object to check the prefixed properties on. Use a string to skip
+   * @param {HTMLElement|SVGElement} [elem] - An element used to test the property and value against
+   * @param {string} [value] - A string of a css value
+   * @param {boolean} [skipValueTest] - An boolean representing if you want to test if value sticks when set
+   * @returns {false|string} returns the string version of the property, or false if it is unsupported
+   */
+  function testPropsAll(prop, prefixed, elem, value, skipValueTest) {
+
+    var ucProp = prop.charAt(0).toUpperCase() + prop.slice(1),
+      props = (prop + ' ' + cssomPrefixes.join(ucProp + ' ') + ucProp).split(' ');
+
+    // did they call .prefixed('boxSizing') or are we just testing a prop?
+    if (is(prefixed, 'string') || is(prefixed, 'undefined')) {
+      return testProps(props, prefixed, value, skipValueTest);
+
+      // otherwise, they called .prefixed('requestAnimationFrame', window[, elem])
+    } else {
+      props = (prop + ' ' + (domPrefixes).join(ucProp + ' ') + ucProp).split(' ');
+      return testDOMProps(props, prefixed, elem);
+    }
+  }
+
+  // Modernizr.testAllProps() investigates whether a given style property,
+  // or any of its vendor-prefixed variants, is recognized
+  //
+  // Note that the property names must be provided in the camelCase variant.
+  // Modernizr.testAllProps('boxSizing')
+  ModernizrProto.testAllProps = testPropsAll;
+
+  
+
+  /**
+   * prefixed returns the prefixed or nonprefixed property name variant of your input
+   *
+   * @memberof Modernizr
+   * @name Modernizr.prefixed
+   * @optionName Modernizr.prefixed()
+   * @optionProp prefixed
+   * @access public
+   * @function prefixed
+   * @param {string} prop - String name of the property to test for
+   * @param {object} [obj] - An object to test for the prefixed properties on
+   * @param {HTMLElement} [elem] - An element used to test specific properties against
+   * @returns {string|false} The string representing the (possibly prefixed) valid
+   * version of the property, or `false` when it is unsupported.
+   * @example
+   *
+   * Modernizr.prefixed takes a string css value in the DOM style camelCase (as
+   * opposed to the css style kebab-case) form and returns the (possibly prefixed)
+   * version of that property that the browser actually supports.
+   *
+   * For example, in older Firefox...
+   * ```js
+   * prefixed('boxSizing')
+   * ```
+   * returns 'MozBoxSizing'
+   *
+   * In newer Firefox, as well as any other browser that support the unprefixed
+   * version would simply return `boxSizing`. Any browser that does not support
+   * the property at all, it will return `false`.
+   *
+   * By default, prefixed is checked against a DOM element. If you want to check
+   * for a property on another object, just pass it as a second argument
+   *
+   * ```js
+   * var rAF = prefixed('requestAnimationFrame', window);
+   *
+   * raf(function() {
+   *  renderFunction();
+   * })
+   * ```
+   *
+   * Note that this will return _the actual function_ - not the name of the function.
+   * If you need the actual name of the property, pass in `false` as a third argument
+   *
+   * ```js
+   * var rAFProp = prefixed('requestAnimationFrame', window, false);
+   *
+   * rafProp === 'WebkitRequestAnimationFrame' // in older webkit
+   * ```
+   *
+   * One common use case for prefixed is if you're trying to determine which transition
+   * end event to bind to, you might do something like...
+   * ```js
+   * var transEndEventNames = {
+   *     'WebkitTransition' : 'webkitTransitionEnd', * Saf 6, Android Browser
+   *     'MozTransition'    : 'transitionend',       * only for FF < 15
+   *     'transition'       : 'transitionend'        * IE10, Opera, Chrome, FF 15+, Saf 7+
+   * };
+   *
+   * var transEndEventName = transEndEventNames[ Modernizr.prefixed('transition') ];
+   * ```
+   *
+   * If you want a similar lookup, but in kebab-case, you can use [prefixedCSS](#modernizr-prefixedcss).
+   */
+
+  var prefixed = ModernizrProto.prefixed = function(prop, obj, elem) {
+    if (prop.indexOf('@') === 0) {
+      return atRule(prop);
+    }
+
+    if (prop.indexOf('-') != -1) {
+      // Convert kebab-case to camelCase
+      prop = cssToDOM(prop);
+    }
+    if (!obj) {
+      return testPropsAll(prop, 'pfx');
+    } else {
+      // Testing DOM property e.g. Modernizr.prefixed('requestAnimationFrame', window) // 'mozRequestAnimationFrame'
+      return testPropsAll(prop, obj, elem);
+    }
+  };
+
+  
+/*!
+{
+  "name": "Fullscreen API",
+  "property": "fullscreen",
+  "caniuse": "fullscreen",
+  "notes": [{
+    "name": "MDN documentation",
+    "href": "https://developer.mozilla.org/en/API/Fullscreen"
+  }],
+  "polyfills": ["screenfulljs"],
+  "builderAliases": ["fullscreen_api"]
+}
+!*/
+/* DOC
+Detects support for the ability to make the current website take over the user's entire screen
+*/
+
+  // github.com/Modernizr/Modernizr/issues/739
+  Modernizr.addTest('fullscreen', !!(prefixed('exitFullscreen', document, false) || prefixed('cancelFullScreen', document, false)));
+
+
+  /**
+   * testAllProps determines whether a given CSS property is supported in the browser
+   *
+   * @memberof Modernizr
+   * @name Modernizr.testAllProps
+   * @optionName Modernizr.testAllProps()
+   * @optionProp testAllProps
+   * @access public
+   * @function testAllProps
+   * @param {string} prop - String naming the property to test (either camelCase or kebab-case)
+   * @param {string} [value] - String of the value to test
+   * @param {boolean} [skipValueTest=false] - Whether to skip testing that the value is supported when using non-native detection
+   * @example
+   *
+   * testAllProps determines whether a given CSS property, in some prefixed form,
+   * is supported by the browser.
+   *
+   * ```js
+   * testAllProps('boxSizing')  // true
+   * ```
+   *
+   * It can optionally be given a CSS value in string form to test if a property
+   * value is valid
+   *
+   * ```js
+   * testAllProps('display', 'block') // true
+   * testAllProps('display', 'penguin') // false
+   * ```
+   *
+   * A boolean can be passed as a third parameter to skip the value check when
+   * native detection (@supports) isn't available.
+   *
+   * ```js
+   * testAllProps('shapeOutside', 'content-box', true);
+   * ```
+   */
+
+  function testAllProps(prop, value, skipValueTest) {
+    return testPropsAll(prop, undefined, undefined, value, skipValueTest);
+  }
+  ModernizrProto.testAllProps = testAllProps;
+  
+/*!
+{
+  "name": "CSS Animations",
+  "property": "cssanimations",
+  "caniuse": "css-animation",
+  "polyfills": ["transformie", "csssandpaper"],
+  "tags": ["css"],
+  "warnings": ["Android < 4 will pass this test, but can only animate a single property at a time"],
+  "notes": [{
+    "name" : "Article: 'Dispelling the Android CSS animation myths'",
+    "href": "https://goo.gl/OGw5Gm"
+  }]
+}
+!*/
+/* DOC
+Detects whether or not elements can be animated using CSS
+*/
+
+  Modernizr.addTest('cssanimations', testAllProps('animationName', 'a', true));
+
+/*!
+{
+  "name": "CSS Transforms",
+  "property": "csstransforms",
+  "caniuse": "transforms2d",
+  "tags": ["css"]
+}
+!*/
+
+  Modernizr.addTest('csstransforms', function() {
+    // Android < 3.0 is buggy, so we sniff and blacklist
+    // http://git.io/hHzL7w
+    return navigator.userAgent.indexOf('Android 2.') === -1 &&
+           testAllProps('transform', 'scale(1)', true);
+  });
+
+
+  // Run each test
+  testRunner();
+
+  // Remove the "no-js" class if it exists
+  setClasses(classes);
+
+  delete ModernizrProto.addTest;
+  delete ModernizrProto.addAsyncTest;
+
+  // Run the things that are supposed to run after the tests
+  for (var i = 0; i < Modernizr._q.length; i++) {
+    Modernizr._q[i]();
+  }
+
+  // Leak Modernizr namespace
+  window.Modernizr = Modernizr;
+
+
+;
+
+})(window, document);

File diff suppressed because it is too large
+ 0 - 0
dist/DPlayer.min.css


File diff suppressed because it is too large
+ 0 - 0
dist/DPlayer.min.js


File diff suppressed because it is too large
+ 0 - 0
dist/DPlayer.min.js.map


+ 2 - 2
package.json

@@ -1,6 +1,6 @@
 {
   "name": "dplayer",
-  "version": "1.4.3",
+  "version": "1.5.0",
   "description": "Wow, such a lovely HTML5 danmaku video player",
   "main": "dist/DPlayer.min.js",
   "style": "dist/DPlayer.min.css",
@@ -23,7 +23,7 @@
     "html5"
   ],
   "author": "DIYgod",
-  "license": "SATA",
+  "license": "MIT",
   "bugs": {
     "url": "https://github.com/DIYgod/DPlayer/issues"
   },

+ 1 - 1
src/DPlayer.js

@@ -1,4 +1,4 @@
-console.log('\n %c DPlayer 1.4.3 %c http://dplayer.js.org \n\n', 'color: #fadfa3; background: #030307; padding:5px 0;', 'background: #fadfa3; padding:5px 0;');
+console.log('\n %c DPlayer 1.5.0 %c http://dplayer.js.org \n\n', 'color: #fadfa3; background: #030307; padding:5px 0;', 'background: #fadfa3; padding:5px 0;');
 
 require('./DPlayer.scss');
 const utils = require('./utils.js');

+ 6 - 0
src/DPlayer.scss

@@ -4,6 +4,10 @@
     user-select: none;
     line-height: 1;
 
+    * {
+        box-sizing: content-box;
+    }
+
     &:-webkit-full-screen {
         width: 100%;
         height: 100%;
@@ -386,6 +390,7 @@
                 text-align: center;
                 opacity: 1;
                 transition: opacity .1s ease-in-out;
+                word-wrap: normal;
             }
 
             .dplayer-bar {
@@ -1046,6 +1051,7 @@
         img {
             max-width: 100%;
             max-height: 100%;
+            background: none;
         }
     }
 

+ 1 - 1
src/option.js

@@ -14,7 +14,7 @@ module.exports = (option) => {
         theme: '#b7daff',
         loop: false,
         lang: navigator.language.indexOf('zh') !== -1 ? 'zh' : 'en',
-        screenshot: false,
+        screenshot: true,
         hotkey: true,
         preload: 'auto',
         apiBackend: defaultApiBackend,

Some files were not shown because too many files changed in this diff