浏览代码

第二版修改+动画

outaozhen 3 年之前
父节点
当前提交
011b57de15
共有 14 个文件被更改,包括 64048 次插入40 次删除
  1. 50 0
      banner.html
  2. 143 0
      css/banner.css
  3. 82 6
      css/styles.css
  4. 1517 0
      css/video.css
  5. 84 33
      index.html
  6. 10 1
      js/global.js
  7. 197 0
      js/videoList.js
  8. 二进制
      js/videojs-flash/video-js.swf
  9. 1455 0
      js/videojs-flash/videojs-flash.js
  10. 508 0
      js/videojs-playlist/videojs-playlist-ui.js
  11. 909 0
      js/videojs-playlist/videojs-playlist.js
  12. 58924 0
      js/videojs/video.js
  13. 86 0
      lang/zh-CN.js
  14. 83 0
      lang/zh-CN.json

+ 50 - 0
banner.html

@@ -0,0 +1,50 @@
+<!DOCTYPE html>
+<html lang="en">
+<head>
+  <meta charset="utf-8">
+  <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
+  <meta http-equiv="x-ua-compatible" content="ie=edge">
+  <title>大司空云计价 - 远程办公,用免费正版市政计价软件</title>
+  <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css">
+  <link rel="stylesheet" href="css/banner.css">
+  <link rel="stylesheet" href="css/video.css">
+</head>
+<body>
+  <div class="bannerBox">
+    <div class="width1200">
+      <div class="banner">
+        <div class="bannerTitleBox">
+          <div class="bannerTitle bannerTitle1">云上协同</div>
+          <div class="bannerTitle bannerTitle2">积累造价数据资产</div>
+          <div class="bannerTitle3 mt-4">市政建筑计价免费版 永久免费</div>
+          <div class="mt-4"><a href="" class="btn btn-primary btnHand">立即体验</a></div>
+        </div>
+      </div>
+    </div>
+  </div>
+  <!-- JS. -->
+  <script src="js/jquery/jquery-3.2.1.min.js"></script>
+  <script src="js/bootstrap/bootstrap.min.js"></script>
+  <script src="js/global.js"></script>
+  <script src="js/videojs/video.js"></script>
+  <script src="js/videojs-playlist/videojs-playlist.js"></script>
+  <script src="js/videojs-playlist/videojs-playlist-ui.js" ></script>
+  <script src="js/videojs-flash/videojs-flash.js" ></script>
+  <!-- <script src="lang/zh-CN.js"></script> -->
+  <script src="js/videoList.js"></script>
+  <script type="text/javascript">
+    function topFunction() {
+      document.body.scrollTop = 0;
+      document.documentElement.scrollTop = 0;
+    }
+    // 滚动条到顶部的垂直高度
+    console.log($(window).scrollTop());
+    // 浏览器显示区域(可视区域)的高度
+    console.log($(window).height());
+    // 获取页面的文档高度
+    console.log($('.productTwo').offset().top);
+    console.log();
+  </script>
+</body>
+
+</html>

+ 143 - 0
css/banner.css

@@ -0,0 +1,143 @@
+@charset "utf-8";
+body{
+	padding: 0;
+  margin: 0;
+  font-weight: normal;
+  font-style: normal;
+  font-size: 12px;
+  font-family:"微软雅黑","Tahoma";
+  vertical-align: baseline;
+  text-align:justify;
+  word-wrap:break-word;
+  text-justify:inter-ideograph;
+  color: #000000cc;
+}
+.navBody{
+  background: rgba(33, 33, 41, 1);
+  height: 36px;
+  color: #fff;
+}
+.globalNav a{
+  float:left;
+  height:36px;
+  line-height:36px;
+  padding:0 10px;
+  color:#eee;
+  position: relative;
+  cursor: pointer
+}
+.globalNav a:hover{
+  background:#666E70;
+  color:#fff
+}
+.globalNav a.now{
+  background:#008FFF;
+  color:#fff;
+  font-weight:600
+}
+.globalNav .zhzd{
+  float:left;
+  height:36px;
+  position: relative;
+  z-index:999
+}
+.globalNav .zhzd a b{
+  border-color: #FFDDC9 transparent transparent;
+  border-right: 3px dashed transparent;
+  border-style: solid dashed dashed;
+  border-width: 3px 3px 0;
+  display: inline-block;
+  font-size: 0;
+  height: 0;
+  line-height: 0;
+  width: 0;
+  position: relative;
+  top:-1px;
+  left:4px
+}
+.globalNav .zhzd .zhzdMenu{
+  position: absolute;
+  left:0;
+  top:29px;
+  background:#fff;
+  border:1px solid #ccc;
+  border-top:none;
+  box-shadow:0 2px 4px rgba(0, 0, 0, 0.2);
+  z-index: 0;
+  display:none
+}
+.globalNav .zhzd .zhzdMenu a{
+  width:100%;
+  padding:0;
+  text-align:center;
+  color:#3366cc
+}
+.globalNav .zhzd .zhzdMenu a:hover{
+  background:#f2f2f2
+}
+.globalNav .zhzd a.now{
+  background:#fff;
+  border:1px solid #ccc;
+  border-bottom:none;
+  border-top:none;
+  color:#3366cc;
+  z-index:10
+}
+.width1200{
+  width: 1200px;
+  margin: 0 auto;
+}
+.table-cell{
+	display: table-cell;
+	vertical-align: middle;
+}
+.btnText{
+	padding-right: 10px;
+	font-size: 16px;
+	font-weight: 500;
+}
+.bannerBox{
+	height: 950px;
+	background: rgba(245, 247, 253, 1);
+}
+.banner{
+	position: relative;
+	height: 950px;
+	background: url(img/image_banner_01.png) no-repeat right -110px;
+}
+.bannerTitleBox{
+	position: absolute;
+	top: 300px;
+}
+.bannerTitle{
+	font-size: 64px;
+	font-weight: 600;
+}
+.bannerTitle1{
+	color: rgba(47, 84, 235, 1);
+}
+.bannerTitle2{
+	color: rgba(0, 0, 0, 0.8);
+}
+.bannerTitle3{
+	color: rgba(0, 0, 0, 0.5);
+	font-size: 24px;
+}
+.btnHand{
+	font-size: 24px;
+	font-weight: 500;
+	padding: 16px 32px;
+}
+.moreBox{
+	position: absolute;
+	bottom: 30px;
+}
+.moreBox h4{
+	font-size: 14px;
+	color: rgba(47, 84, 235, 1);
+}
+.moreBox .iconMore{
+	width: 32px;
+	height: 54px;
+	background: url(img/css_sprites.png) no-repeat -80px -1078px;
+}

+ 82 - 6
css/styles.css

@@ -112,9 +112,50 @@ body{
 	font-size: 16px;
 	font-weight: 500;
 }
+.bannerBox{
+	height: 950px;
+	background: rgba(245, 247, 253, 1);
+}
 .banner{
-	background: #F5F7FD;
-	height: 800px;
+	position: relative;
+	height: 950px;
+	background: url(img/image_banner_01.png) no-repeat right -110px;
+}
+.bannerTitleBox{
+	position: absolute;
+	top: 300px;
+}
+.bannerTitle{
+	font-size: 64px;
+	font-weight: 600;
+}
+.bannerTitle1{
+	color: rgba(47, 84, 235, 1);
+}
+.bannerTitle2{
+	color: rgba(0, 0, 0, 0.8);
+}
+.bannerTitle3{
+	color: rgba(0, 0, 0, 0.5);
+	font-size: 24px;
+}
+.btnHand{
+	font-size: 24px;
+	font-weight: 500;
+	padding: 16px 32px;
+}
+.moreBox{
+	position: absolute;
+	bottom: 30px;
+}
+.moreBox h4{
+	font-size: 14px;
+	color: rgba(47, 84, 235, 1);
+}
+.moreBox .iconMore{
+	width: 32px;
+	height: 54px;
+	background: url(img/css_sprites.png) no-repeat -80px -1078px;
 }
 .slogan{
 	height: 320px;
@@ -144,13 +185,13 @@ body{
 .videoLeft{
 	width: 843px;
 	height: 474px;
-	background: #000 url(img/image_video_cover_07.png) no-repeat;
+	/*background: #000 url(img/image_video_cover_07.png) no-repeat;*/
 }
 .videoRight{
 	width: 357px;
 	height: 474px;
 }
-.videoRight ul{
+.videoRight .vjs-playlist{
 	height: 474px;
 	overflow-y: auto;
 }
@@ -409,6 +450,7 @@ body{
 	list-style-type: none;
 	width: 275px;
 	height: 275px;
+	border-radius: 24px;
 	text-align: center;
 	background: #fff;	
 }
@@ -504,16 +546,19 @@ body{
 	height: 75px;
 	font-size: 16px;
 }
+/*.versionTableItemW, .versionTableBodyW{
+	width: 280px;
+}*/
 .versionTableTdBig{
 	font-size: 18px;
 	font-weight: 600;
 }
 .versionTableBodyBox{
 	border-radius: 0 0 24px 24px;
+	padding: 0 0 30px 0;
 	box-shadow: 0px 8px 16px rgba(0, 0, 0, 0.1);
 }
 .iconWidth{
-	margin-right: 10px;
 	display: inline-block;
 	width: 24px;
 	height: 24px;
@@ -560,7 +605,10 @@ body{
 .bottomList li{
 	font-size: 16px;
 	list-style-type: none;
-	line-height: 20px;
+	display: flex;
+	/*justify-content: center;*/
+	align-items: center;
+	line-height: 24px;
 	padding: 10px 0;
 	color: rgba(255, 255, 255, 0.6);
 }
@@ -569,4 +617,32 @@ body{
 }
 .bottomList li a:hover{
 	color: rgba(255, 255, 255, 1);
+}
+.backTop{
+	display: none;
+	position: fixed;
+	bottom: 150px;
+	right: 20px;
+	z-index: 99;
+	background: rgba(47, 84, 235, 1);
+	color: #fff;
+	width: 76px;
+	height: 76px;
+	cursor: pointer;
+}
+.backTopBox{
+	width: 76px;
+	height: 76px;
+	display: flex;
+	justify-content: center;
+	align-items: center;
+}
+.backTop:hover{
+	background: rgba(30, 67, 222, 1);
+}
+.iconImgBox{
+	text-align: center;
+}
+.iconTop{
+	background: url(img/css_sprites.png) no-repeat -208px -1072px;
 }

文件差异内容过多而无法显示
+ 1517 - 0
css/video.css


+ 84 - 33
index.html

@@ -7,6 +7,7 @@
   <title>大司空云计价 - 远程办公,用免费正版市政计价软件</title>
   <link rel="stylesheet" href="css/bootstrap/bootstrap.min.css">
   <link rel="stylesheet" href="css/styles.css">
+  <link rel="stylesheet" href="css/video.css">
 </head>
 <body>
   <div class="navBody">
@@ -40,7 +41,22 @@
       </div>
     </div>
   </div>
-  <div class="banner"></div>
+  <div class="bannerBox">
+    <div class="width1200">
+      <div class="banner">
+        <div class="bannerTitleBox">
+          <div class="bannerTitle bannerTitle1">云上协同</div>
+          <div class="bannerTitle bannerTitle2">积累造价数据资产</div>
+          <div class="bannerTitle3 mt-4">市政建筑计价免费版 永久免费</div>
+          <div class="mt-4"><a href="" class="btn btn-primary btnHand">立即体验</a></div>
+        </div>
+        <div class="w-100 text-center moreBox">
+          <h4>查看更多</h4>
+          <div><i class="iconWidth iconMore"></i></div>
+        </div>
+      </div>
+    </div>
+  </div>
   <div class="slogan">
     <div class="width1200">
       <div class="sloganSmall">大司空,古代官名,掌土木、营建之事</div>
@@ -50,9 +66,12 @@
   <div class="videoListCon">
     <div class="width1200">
       <div class="d-flex justify-content-center">
-        <div class="videoLeft"></div>
-        <div class="videoRight">
-          <ul>
+        <div class="videoLeft">
+          <video id="example" class="video-js vjs-default-skin vjs-big-play-centered vjs-v7 vjs-user-active" playsinline webkit-playsinline autoplay controls preload="auto" width="845px" height="475px" data-setup='{"techOrder": ["html5","flash"]}'></video>
+        </div>
+        <div class="videoRight ml-4">
+          <div class="vjs-playlist playlist-container"></div>
+          <!-- <ul>
             <li class="videoList">
               <div class="row">
                 <div class="col-6"><div class="videoImg"><a href="#"><img src="images/image_video_cover_01.png"></a></div></div>
@@ -89,7 +108,7 @@
                 <div class="col-6"><div class=""><div class="videoTitle">视频教程-模板克隆</div><span class="textRrey">23:03</span></div></div>
               </div>
             </li>
-          </ul>
+          </ul> -->
         </div>
       </div>
     </div>
@@ -259,64 +278,79 @@
       </div>
       <div class="versionTable">
         <div class="row">
-          <div class="col-4">
+          <div class="col-4 pr-0">
             <div class="versionTableItem"></div>
           </div>
-          <div class="col-4">
-            <div class="versionTableItem versionBgYellow">
+          <div class="col-3 px-0">
+            <div class="versionTableItem versionTableItemW versionBgYellow">
               <div class="d-flex justify-content-center versionBigtitle">个人版</div>
               <div class="d-flex justify-content-center versionMeta">协作入门</div>
             </div>
           </div>
-          <div class="col-4">
-             <div class="versionTableItem versionBgBlue">
+          <div class="col-1 px-0"></div>
+          <div class="col-3 px-0">
+             <div class="versionTableItem versionTableItemW versionBgBlue">
               <div class="d-flex justify-content-center versionBigtitle">企业版</div>
               <div class="d-flex justify-content-center versionMeta">企业专属</div>
             </div>
           </div>
+          <div class="col-1 px-0"></div>
         </div>
         <div class="row">
-          <div class="col-4">
+          <div class="col-4 pr-0">
             <div class="versionTableBody">
-              <div class="versionTableTd versionTableTdBig"><i class="iconWidth iconVersion"></i>工程信息</div>
-              <div class="versionTableTd"><i class="iconWidth"></i>工程特征</div>
+              <div class="versionTableTd versionTableTdBig"><i class="iconWidth iconVersion mr-2"></i>工程信息</div>
+              <div class="versionTableTd bg-light"><i class="iconWidth"></i>工程特征</div>
               <div class="versionTableTd"><i class="iconWidth"></i>造价分析</div>
-              <div class="versionTableTd"><i class="iconWidth"></i>呈现选项</div>
+              <div class="versionTableTd bg-light"><i class="iconWidth"></i>呈现选项</div>
               <div class="versionTableTd"><i class="iconWidth"></i>编制说明</div>
-              <div class="versionTableTd versionTableTdBig"><i class="iconWidth iconVersion"></i>工程信息</div>
+              <div class="versionTableTd versionTableTdBig bg-light"><i class="iconWidth iconVersion mr-2"></i>工程信息</div>
               <div class="versionTableTd"><i class="iconWidth"></i>工程特征</div>
-              <div class="versionTableTd"><i class="iconWidth"></i>造价分析</div>
+              <div class="versionTableTd bg-light"><i class="iconWidth"></i>造价分析</div>
               <div class="versionTableTd"><i class="iconWidth"></i>呈现选项</div>
-              <div class="versionTableTd"><i class="iconWidth"></i>编制说明</div>
+              <div class="versionTableTd bg-light"><i class="iconWidth"></i>编制说明</div>
             </div>
           </div>
-          <div class="col-4">
-            <div class="versionTableBody versionTableBodyBox">
-              <div class="versionTableTd"><i class="iconWidth iconCorrect"></i></div>
+          <div class="col-3 px-0">
+            <div class="versionTableBody versionTableBodyW versionTableBodyBox">
               <div class="versionTableTd"><i class="iconWidth iconCorrect"></i></div>
+              <div class="versionTableTd bg-light"><i class="iconWidth iconCorrect"></i></div>
               <div class="versionTableTd"><i class="iconWidth iconCorrect"></i></div>
+              <div class="versionTableTd bg-light"><i class="iconWidth iconCorrect"></i></div>
               <div class="versionTableTd"><i class="iconWidth iconCorrect"></i></div>
-              <div class="versionTableTd"><i class="iconWidth iconCorrect"></i></div>
-              <div class="versionTableTd"><i class="iconWidth iconWrong"></i></div>
-              <div class="versionTableTd"><i class="iconWidth iconWrong"></i></div>
-              <div class="versionTableTd"><i class="iconWidth iconWrong"></i></div>
+              <div class="versionTableTd bg-light"><i class="iconWidth iconWrong"></i></div>
               <div class="versionTableTd"><i class="iconWidth iconWrong"></i></div>
+              <div class="versionTableTd bg-light"><i class="iconWidth iconWrong"></i></div>
               <div class="versionTableTd"><i class="iconWidth iconWrong"></i></div>
+              <div class="versionTableTd bg-light"><i class="iconWidth iconWrong"></i></div>
             </div>
           </div>
-          <div class="col-4">
-            <div class="versionTableBody versionTableBodyBox">
-              <div class="versionTableTd"><i class="iconWidth iconCorrect"></i></div>
-              <div class="versionTableTd"><i class="iconWidth iconCorrect"></i></div>
-              <div class="versionTableTd"><i class="iconWidth iconCorrect"></i></div>
-              <div class="versionTableTd"><i class="iconWidth iconCorrect"></i></div>
-              <div class="versionTableTd"><i class="iconWidth iconCorrect"></i></div>
+          <div class="col-1 px-0">
+            <div class="versionTableTd"></div>
+            <div class="versionTableTd bg-light"></div>
+            <div class="versionTableTd"></div>
+            <div class="versionTableTd bg-light"></div>
+            <div class="versionTableTd"></div>
+            <div class="versionTableTd bg-light"></div>
+            <div class="versionTableTd"></div>
+            <div class="versionTableTd bg-light"></div>
+            <div class="versionTableTd"></div>
+            <div class="versionTableTd bg-light"></div>
+          </div>
+          <div class="col-3 px-0">
+            <div class="versionTableBody versionTableBodyW versionTableBodyBox">
               <div class="versionTableTd"><i class="iconWidth iconCorrect"></i></div>
+              <div class="versionTableTd bg-light"><i class="iconWidth iconCorrect"></i></div>
               <div class="versionTableTd"><i class="iconWidth iconCorrect"></i></div>
+              <div class="versionTableTd bg-light"><i class="iconWidth iconCorrect"></i></div>
               <div class="versionTableTd"><i class="iconWidth iconCorrect"></i></div>
+              <div class="versionTableTd bg-light"><i class="iconWidth iconCorrect"></i></div>
               <div class="versionTableTd"><i class="iconWidth iconCorrect"></i></div>
+              <div class="versionTableTd bg-light"><i class="iconWidth iconCorrect"></i></div>
               <div class="versionTableTd"><i class="iconWidth iconCorrect"></i></div>
+              <div class="versionTableTd bg-light"><i class="iconWidth iconCorrect"></i></div>
             </div>
+            <div class="col-1 px-0"></div>
           </div>
         </div>
       </div>
@@ -378,8 +412,8 @@
           <div class="bottomItem">
             <h4>联系我们</h4>
             <ul class="bottomList mt-4">
-              <li><i class="iconWidth iconPhone"></i>全国服务热线:<span class="phone">0756-3850888</span></li>
-              <li><i class="iconWidth iconQQ"></i>企业QQ</li>
+              <li><i class="iconWidth iconPhone mr-2"></i>全国服务热线:<span class="phone">0756-3850888</span></li>
+              <li><i class="iconWidth iconQQ mr-2"></i>企业QQ</li>
               <li class="erwein"><img src="images/erweima.jpg"></li>
             </ul>
           </div>
@@ -388,11 +422,28 @@
       <div class="mt-4">Copyright@珠海纵横创新软件有限公司 all rights reserved</div>
     </div>
   </div>
+  <div class="backTop" id="backTop" onclick="topFunction()">
+    <div class="d-flex flex-column backTopBox">
+      <div class="iconImgBox"><i class="iconWidth iconTop"></i></div>
+      <div>返回顶部</div>
+    </div>
+    </i>
+  </div>
   <!-- JS. -->
   <script src="js/jquery/jquery-3.2.1.min.js"></script>
   <script src="js/bootstrap/bootstrap.min.js"></script>
   <script src="js/global.js"></script>
+  <script src="js/videojs/video.js"></script>
+  <script src="js/videojs-playlist/videojs-playlist.js"></script>
+  <script src="js/videojs-playlist/videojs-playlist-ui.js" ></script>
+  <script src="js/videojs-flash/videojs-flash.js" ></script>
+  <!-- <script src="lang/zh-CN.js"></script> -->
+  <script src="js/videoList.js"></script>
   <script type="text/javascript">
+    function topFunction() {
+      document.body.scrollTop = 0;
+      document.documentElement.scrollTop = 0;
+    }
     // 滚动条到顶部的垂直高度
     console.log($(window).scrollTop());
     // 浏览器显示区域(可视区域)的高度

+ 10 - 1
js/global.js

@@ -19,4 +19,13 @@ $(window).bind("scroll",function() {
   }else{
   	$('.productThreeImg,.productThreeRImg1,.productThreeRImg2').removeClass('show');
   }
-})
+})
+
+window.onscroll = function() {scrollFunction()};
+function scrollFunction() {
+  if (document.body.scrollTop > 20 || document.documentElement.scrollTop > 1080) {
+      document.getElementById("backTop").style.display = "block";
+  } else {
+      document.getElementById("backTop").style.display = "none";
+  }
+}

+ 197 - 0
js/videoList.js

@@ -0,0 +1,197 @@
+var player = videojs('example');
+ 
+	player.playlist([
+	{
+	  name:'视频教程-调价',
+	  // description:'Explore the depths of our planet\'s oceans.',
+	  duration:596,
+	  sources: [{
+	    src: 'https://d4.smartcost.com.cn/%E5%91%86%E7%93%9C%E5%AD%A6%E9%80%A0%E4%BB%B7-%E5%85%AC%E8%B7%AF%E7%AF%87-%E6%9C%89logo.mp4',
+	    type: 'video/mp4'
+	  }],
+	  poster:'http://media.w3.org/2010/05/bunny/poster.png',
+	  thumbnail: [
+	  	{
+          srcset: 'test/example/oceans.jpg',
+          type: 'image/jpeg',
+          media: '(min-width: 400px;)'
+        },
+	  	{
+	  		src:'http://media.w3.org/2010/05/bunny/poster.png'
+	  	}
+  	  ]
+	},
+	{
+	  name:'视频教程-共用单价文件',//视频标题
+	  description:'Explore the depths of our planet\'s oceans.',//视频描述
+	  duration:52,//视频总时长,单位秒(s)
+	  sources: [{//视频资源地址以及视频的MIME类型
+	    src: 'http://d1video.smartcost.com.cn/video/play/2018/step1.mp4',
+	    type: 'video/mp4'
+	  }],
+	  //视频封面地址
+	  // poster:'http://media.w3.org/2010/05/sintel/poster.png',
+	  //右侧视频播放列表的图片
+	  thumbnail: [
+	  	{//默认图片
+          srcset: 'test/example/oceans.jpg',
+          type: 'image/jpeg',
+          media: '(max-width: 130px;)'
+        },
+	  	{//实际视频缩略图图片
+	  		src:'http://media.w3.org/2010/05/sintel/poster.png'
+	  	}
+  	  ]
+	}, {
+	  name:'视频教程-清单精灵',//视频标题
+	  description:'Explore the depths of our planet\'s oceans.',//视频描述
+	  duration:734,//视频总时长,单位秒(s)
+	  sources: [{//视频资源地址以及视频的MIME类型
+	    src: 'http://d2zihajmogu5jn.cloudfront.net/tears-of-steel/playlist.m3u8',
+	    type: 'application/x-mpegurl'
+	  }],
+	  //视频封面地址
+	  poster:'http://d2zihajmogu5jn.cloudfront.net/tears-of-steel/tears_of_steel.jpg',
+	  
+	  thumbnail: [
+	  	{//默认图片
+          srcset: 'img/jinjihu.jpg',
+          type: 'image/jpeg',
+          media: '(min-width: 400px;)'
+        },
+	  	{//实际视频缩略图图片
+	  		src:'http://d2zihajmogu5jn.cloudfront.net/tears-of-steel/tears_of_steel.jpg'
+	  	}
+  	  ]
+	},
+	{
+	  name:'视频教程-调价',
+	  description:'Explore the depths of our planet\'s oceans.',
+	  duration:596,
+	  sources: [{
+	    src: 'https://d4.smartcost.com.cn/%E5%91%86%E7%93%9C%E5%AD%A6%E9%80%A0%E4%BB%B7-%E5%85%AC%E8%B7%AF%E7%AF%87-%E6%9C%89logo.mp4',
+	    type: 'video/mp4'
+	  }],
+	  poster:'http://media.w3.org/2010/05/bunny/poster.png',
+	  thumbnail: [
+	  	{
+          srcset: 'test/example/oceans.jpg',
+          type: 'image/jpeg',
+          media: '(min-width: 400px;)'
+        },
+	  	{
+	  		src:'http://media.w3.org/2010/05/bunny/poster.png'
+	  	}
+  	  ]
+	},
+	{
+	  name:'视频教程-共用单价文件',//视频标题
+	  description:'Explore the depths of our planet\'s oceans.',//视频描述
+	  duration:52,//视频总时长,单位秒(s)
+	  sources: [{//视频资源地址以及视频的MIME类型
+	    src: 'http://d1video.smartcost.com.cn/video/play/2018/step1.mp4',
+	    type: 'video/mp4'
+	  }],
+	  //视频封面地址
+	  // poster:'http://media.w3.org/2010/05/sintel/poster.png',
+	  //右侧视频播放列表的图片
+	  thumbnail: [
+	  	{//默认图片
+          srcset: 'test/example/oceans.jpg',
+          type: 'image/jpeg',
+          media: '(max-width: 130px;)'
+        },
+	  	{//实际视频缩略图图片
+	  		src:'http://media.w3.org/2010/05/sintel/poster.png'
+	  	}
+  	  ]
+	}, {
+	  name:'视频教程-清单精灵',//视频标题
+	  description:'Explore the depths of our planet\'s oceans.',//视频描述
+	  duration:734,//视频总时长,单位秒(s)
+	  sources: [{//视频资源地址以及视频的MIME类型
+	    src: 'http://d2zihajmogu5jn.cloudfront.net/tears-of-steel/playlist.m3u8',
+	    type: 'application/x-mpegurl'
+	  }],
+	  //视频封面地址
+	  poster:'http://d2zihajmogu5jn.cloudfront.net/tears-of-steel/tears_of_steel.jpg',
+	  
+	  thumbnail: [
+	  	{//默认图片
+          srcset: 'img/jinjihu.jpg',
+          type: 'image/jpeg',
+          media: '(min-width: 400px;)'
+        },
+	  	{//实际视频缩略图图片
+	  		src:'http://d2zihajmogu5jn.cloudfront.net/tears-of-steel/tears_of_steel.jpg'
+	  	}
+  	  ]
+	},
+	{
+	  name:'视频教程-调价',
+	  description:'Explore the depths of our planet\'s oceans.',
+	  duration:596,
+	  sources: [{
+	    src: 'https://d4.smartcost.com.cn/%E5%91%86%E7%93%9C%E5%AD%A6%E9%80%A0%E4%BB%B7-%E5%85%AC%E8%B7%AF%E7%AF%87-%E6%9C%89logo.mp4',
+	    type: 'video/mp4'
+	  }],
+	  poster:'http://media.w3.org/2010/05/bunny/poster.png',
+	  thumbnail: [
+	  	{
+          srcset: 'test/example/oceans.jpg',
+          type: 'image/jpeg',
+          media: '(min-width: 400px;)'
+        },
+	  	{
+	  		src:'http://media.w3.org/2010/05/bunny/poster.png'
+	  	}
+  	  ]
+	},
+	{
+	  name:'视频教程-共用单价文件',//视频标题
+	  description:'Explore the depths of our planet\'s oceans.',//视频描述
+	  duration:52,//视频总时长,单位秒(s)
+	  sources: [{//视频资源地址以及视频的MIME类型
+	    src: 'http://d1video.smartcost.com.cn/video/play/2018/step1.mp4',
+	    type: 'video/mp4'
+	  }],
+	  //视频封面地址
+	  // poster:'http://media.w3.org/2010/05/sintel/poster.png',
+	  //右侧视频播放列表的图片
+	  thumbnail: [
+	  	{//默认图片
+          srcset: 'test/example/oceans.jpg',
+          type: 'image/jpeg',
+          media: '(max-width: 130px;)'
+        },
+	  	{//实际视频缩略图图片
+	  		src:'http://media.w3.org/2010/05/sintel/poster.png'
+	  	}
+  	  ]
+	}, {
+	  name:'视频教程-清单精灵',//视频标题
+	  description:'Explore the depths of our planet\'s oceans.',//视频描述
+	  duration:734,//视频总时长,单位秒(s)
+	  sources: [{//视频资源地址以及视频的MIME类型
+	    src: 'http://d2zihajmogu5jn.cloudfront.net/tears-of-steel/playlist.m3u8',
+	    type: 'application/x-mpegurl'
+	  }],
+	  //视频封面地址
+	  poster:'http://d2zihajmogu5jn.cloudfront.net/tears-of-steel/tears_of_steel.jpg',
+	  
+	  thumbnail: [
+	  	{//默认图片
+          srcset: 'img/jinjihu.jpg',
+          type: 'image/jpeg',
+          media: '(min-width: 400px;)'
+        },
+	  	{//实际视频缩略图图片
+	  		src:'http://d2zihajmogu5jn.cloudfront.net/tears-of-steel/tears_of_steel.jpg'
+	  	}
+  	  ]
+	}
+	]);
+	 
+	player.playlistUi();
+	player.playlist.autoadvance(0); // 自动播放, 0秒延时
+	player.playlist.repeat(true); //循环播放

二进制
js/videojs-flash/video-js.swf


文件差异内容过多而无法显示
+ 1455 - 0
js/videojs-flash/videojs-flash.js


+ 508 - 0
js/videojs-playlist/videojs-playlist-ui.js

@@ -0,0 +1,508 @@
+/*! @name videojs-playlist-ui @version 3.5.2 @license Apache-2.0 */
+(function (global, factory) {
+  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('global/document'), require('video.js')) :
+  typeof define === 'function' && define.amd ? define(['global/document', 'video.js'], factory) :
+  (global.videojsPlaylistUi = factory(global.document,global.videojs));
+}(this, (function (document,videojs) { 'use strict';
+
+  document = document && document.hasOwnProperty('default') ? document['default'] : document;
+  videojs = videojs && videojs.hasOwnProperty('default') ? videojs['default'] : videojs;
+
+  function _inheritsLoose(subClass, superClass) {
+    subClass.prototype = Object.create(superClass.prototype);
+    subClass.prototype.constructor = subClass;
+    subClass.__proto__ = superClass;
+  }
+
+  var version = "3.5.2";
+
+  var dom = videojs.dom || videojs;
+  var registerPlugin = videojs.registerPlugin || videojs.plugin; // Array#indexOf analog for IE8
+
+  var indexOf = function indexOf(array, target) {
+    for (var i = 0, length = array.length; i < length; i++) {
+      if (array[i] === target) {
+        return i;
+      }
+    }
+
+    return -1;
+  }; // see https://github.com/Modernizr/Modernizr/blob/master/feature-detects/css/pointerevents.js
+
+
+  var supportsCssPointerEvents = function () {
+    var element = document.createElement('x');
+    element.style.cssText = 'pointer-events:auto';
+    return element.style.pointerEvents === 'auto';
+  }();
+
+  var defaults = {
+    className: 'vjs-playlist',
+    playOnSelect: false,
+    supportsCssPointerEvents: supportsCssPointerEvents
+  }; // we don't add `vjs-playlist-now-playing` in addSelectedClass
+  // so it won't conflict with `vjs-icon-play
+  // since it'll get added when we mouse out
+
+  var addSelectedClass = function addSelectedClass(el) {
+    el.addClass('vjs-selected');
+  };
+
+  var removeSelectedClass = function removeSelectedClass(el) {
+    el.removeClass('vjs-selected');
+
+    if (el.thumbnail) {
+      dom.removeClass(el.thumbnail, 'vjs-playlist-now-playing');
+    }
+  };
+
+  var upNext = function upNext(el) {
+    el.addClass('vjs-up-next');
+  };
+
+  var notUpNext = function notUpNext(el) {
+    el.removeClass('vjs-up-next');
+  };
+
+  var createThumbnail = function createThumbnail(thumbnail) {
+    if (!thumbnail) {
+      var placeholder = document.createElement('div');
+      placeholder.className = 'vjs-playlist-thumbnail vjs-playlist-thumbnail-placeholder';
+      return placeholder;
+    }
+
+    var picture = document.createElement('picture');
+    picture.className = 'vjs-playlist-thumbnail';
+
+    if (typeof thumbnail === 'string') {
+      // simple thumbnails
+      var img = document.createElement('img');
+      img.src = thumbnail;
+      img.alt = '';
+      picture.appendChild(img);
+    } else {
+      // responsive thumbnails
+      // additional variations of a <picture> are specified as
+      // <source> elements
+      for (var i = 0; i < thumbnail.length - 1; i++) {
+        var _variant = thumbnail[i];
+        var source = document.createElement('source'); // transfer the properties of each variant onto a <source>
+
+        for (var prop in _variant) {
+          source[prop] = _variant[prop];
+        }
+
+        picture.appendChild(source);
+      } // the default version of a <picture> is specified by an <img>
+
+
+      var variant = thumbnail[thumbnail.length - 1];
+
+      var _img = document.createElement('img');
+
+      _img.alt = '';
+
+      for (var _prop in variant) {
+        _img[_prop] = variant[_prop];
+      }
+
+      picture.appendChild(_img);
+    }
+
+    return picture;
+  };
+
+  var Component = videojs.getComponent('Component');
+
+  var PlaylistMenuItem =
+  /*#__PURE__*/
+  function (_Component) {
+    _inheritsLoose(PlaylistMenuItem, _Component);
+
+    function PlaylistMenuItem(player, playlistItem, settings) {
+      var _this;
+
+      if (!playlistItem.item) {
+        throw new Error('Cannot construct a PlaylistMenuItem without an item option');
+      }
+
+      _this = _Component.call(this, player, playlistItem) || this;
+      _this.item = playlistItem.item;
+      _this.playOnSelect = settings.playOnSelect;
+
+      _this.emitTapEvents();
+
+      _this.on(['click', 'tap'], _this.switchPlaylistItem_);
+
+      _this.on('keydown', _this.handleKeyDown_);
+
+      return _this;
+    }
+
+    var _proto = PlaylistMenuItem.prototype;
+
+    _proto.handleKeyDown_ = function handleKeyDown_(event) {
+      // keycode 13 is <Enter>
+      // keycode 32 is <Space>
+      if (event.which === 13 || event.which === 32) {
+        this.switchPlaylistItem_();
+      }
+    };
+
+    _proto.switchPlaylistItem_ = function switchPlaylistItem_(event) {
+      this.player_.playlist.currentItem(indexOf(this.player_.playlist(), this.item));
+
+      if (this.playOnSelect) {
+        this.player_.play();
+      }
+    };
+
+    _proto.createEl = function createEl() {
+      var li = document.createElement('li');
+      var item = this.options_.item;
+
+      if (typeof item.data === 'object') {
+        var dataKeys = Object.keys(item.data);
+        dataKeys.forEach(function (key) {
+          var value = item.data[key];
+          li.dataset[key] = value;
+        });
+      }
+
+      li.className = 'vjs-playlist-item';
+      li.setAttribute('tabIndex', 0); // Thumbnail image
+
+      this.thumbnail = createThumbnail(item.thumbnail);
+      li.appendChild(this.thumbnail); // Duration
+
+      if (item.duration) {
+        var duration = document.createElement('time');
+        var time = videojs.formatTime(item.duration);
+        duration.className = 'vjs-playlist-duration';
+        duration.setAttribute('datetime', 'PT0H0M' + item.duration + 'S');
+        duration.appendChild(document.createTextNode(time));
+        li.appendChild(duration);
+      } // Now playing
+
+
+      var nowPlayingEl = document.createElement('span');
+      var nowPlayingText = this.localize('');
+      nowPlayingEl.className = 'vjs-playlist-now-playing-text';
+      nowPlayingEl.appendChild(document.createTextNode(nowPlayingText));
+      nowPlayingEl.setAttribute('title', nowPlayingText);
+      this.thumbnail.appendChild(nowPlayingEl); // Title container contains title and "up next"
+
+      var titleContainerEl = document.createElement('div');
+      titleContainerEl.className = 'vjs-playlist-title-container';
+      this.thumbnail.appendChild(titleContainerEl); // Up next
+
+      var upNextEl = document.createElement('span');
+      var upNextText = this.localize('Up Next');
+      upNextEl.className = 'vjs-up-next-text';
+      upNextEl.appendChild(document.createTextNode(upNextText));
+      upNextEl.setAttribute('title', upNextText);
+      titleContainerEl.appendChild(upNextEl); // Video title
+
+      var titleEl = document.createElement('cite');
+      var titleText = item.name || this.localize('Untitled Video');
+      titleEl.className = 'vjs-playlist-name';
+      titleEl.appendChild(document.createTextNode(titleText));
+      titleEl.setAttribute('title', titleText);
+      titleContainerEl.appendChild(titleEl);
+      return li;
+    };
+
+    return PlaylistMenuItem;
+  }(Component);
+
+  var PlaylistMenu =
+  /*#__PURE__*/
+  function (_Component2) {
+    _inheritsLoose(PlaylistMenu, _Component2);
+
+    function PlaylistMenu(player, options) {
+      var _this2;
+
+      if (!player.playlist) {
+        throw new Error('videojs-playlist is required for the playlist component');
+      }
+
+      _this2 = _Component2.call(this, player, options) || this;
+      _this2.items = [];
+
+      if (options.horizontal) {
+        _this2.addClass('vjs-playlist-horizontal');
+      } else {
+        _this2.addClass('vjs-playlist-vertical');
+      } // If CSS pointer events aren't supported, we have to prevent
+      // clicking on playlist items during ads with slightly more
+      // invasive techniques. Details in the stylesheet.
+
+
+      if (options.supportsCssPointerEvents) {
+        _this2.addClass('vjs-csspointerevents');
+      }
+
+      _this2.createPlaylist_();
+
+      if (!videojs.browser.TOUCH_ENABLED) {
+        _this2.addClass('vjs-mouse');
+      }
+
+      _this2.on(player, ['loadstart', 'playlistchange', 'playlistsorted'], function (event) {
+        _this2.update();
+      }); // Keep track of whether an ad is playing so that the menu
+      // appearance can be adapted appropriately
+
+
+      _this2.on(player, 'adstart', function () {
+        _this2.addClass('vjs-ad-playing');
+      });
+
+      _this2.on(player, 'adend', function () {
+        _this2.removeClass('vjs-ad-playing');
+      });
+
+      _this2.on('dispose', function () {
+        _this2.empty_();
+
+        player.playlistMenu = null;
+      });
+
+      _this2.on(player, 'dispose', function () {
+        _this2.dispose();
+      });
+
+      return _this2;
+    }
+
+    var _proto2 = PlaylistMenu.prototype;
+
+    _proto2.createEl = function createEl() {
+      return dom.createEl('div', {
+        className: this.options_.className
+      });
+    };
+
+    _proto2.empty_ = function empty_() {
+      if (this.items && this.items.length) {
+        this.items.forEach(function (i) {
+          return i.dispose();
+        });
+        this.items.length = 0;
+      }
+    };
+
+    _proto2.createPlaylist_ = function createPlaylist_() {
+      var playlist = this.player_.playlist() || [];
+      var list = this.el_.querySelector('.vjs-playlist-item-list');
+      var overlay = this.el_.querySelector('.vjs-playlist-ad-overlay');
+
+      if (!list) {
+        list = document.createElement('ol');
+        list.className = 'vjs-playlist-item-list';
+        this.el_.appendChild(list);
+      }
+
+      this.empty_(); // create new items
+
+      for (var i = 0; i < playlist.length; i++) {
+        var item = new PlaylistMenuItem(this.player_, {
+          item: playlist[i]
+        }, this.options_);
+        this.items.push(item);
+        list.appendChild(item.el_);
+      } // Inject the ad overlay. IE<11 doesn't support "pointer-events:
+      // none" so we use this element to block clicks during ad
+      // playback.
+
+
+      if (!overlay) {
+        overlay = document.createElement('li');
+        overlay.className = 'vjs-playlist-ad-overlay';
+        list.appendChild(overlay);
+      } else {
+        // Move overlay to end of list
+        list.appendChild(overlay);
+      } // select the current playlist item
+
+
+      var selectedIndex = this.player_.playlist.currentItem();
+
+      if (this.items.length && selectedIndex >= 0) {
+        addSelectedClass(this.items[selectedIndex]);
+        var thumbnail = this.items[selectedIndex].$('.vjs-playlist-thumbnail');
+
+        if (thumbnail) {
+          dom.addClass(thumbnail, 'vjs-playlist-now-playing');
+        }
+      }
+    };
+
+    _proto2.update = function update() {
+      // replace the playlist items being displayed, if necessary
+      var playlist = this.player_.playlist();
+
+      if (this.items.length !== playlist.length) {
+        // if the menu is currently empty or the state is obviously out
+        // of date, rebuild everything.
+        this.createPlaylist_();
+        return;
+      }
+
+      for (var i = 0; i < this.items.length; i++) {
+        if (this.items[i].item !== playlist[i]) {
+          // if any of the playlist items have changed, rebuild the
+          // entire playlist
+          this.createPlaylist_();
+          return;
+        }
+      } // the playlist itself is unchanged so just update the selection
+
+
+      var currentItem = this.player_.playlist.currentItem();
+
+      for (var _i = 0; _i < this.items.length; _i++) {
+        var item = this.items[_i];
+
+        if (_i === currentItem) {
+          addSelectedClass(item);
+
+          if (document.activeElement !== item.el()) {
+            dom.addClass(item.thumbnail, 'vjs-playlist-now-playing');
+          }
+
+          notUpNext(item);
+        } else if (_i === currentItem + 1) {
+          removeSelectedClass(item);
+          upNext(item);
+        } else {
+          removeSelectedClass(item);
+          notUpNext(item);
+        }
+      }
+    };
+
+    return PlaylistMenu;
+  }(Component);
+  /**
+   * Returns a boolean indicating whether an element has child elements.
+   *
+   * Note that this is distinct from whether it has child _nodes_.
+   *
+   * @param  {HTMLElement} el
+   *         A DOM element.
+   *
+   * @return {boolean}
+   *         Whether the element has child elements.
+   */
+
+
+  var hasChildEls = function hasChildEls(el) {
+    for (var i = 0; i < el.childNodes.length; i++) {
+      if (dom.isEl(el.childNodes[i])) {
+        return true;
+      }
+    }
+
+    return false;
+  };
+  /**
+   * Finds the first empty root element.
+   *
+   * @param  {string} className
+   *         An HTML class name to search for.
+   *
+   * @return {HTMLElement}
+   *         A DOM element to use as the root for a playlist.
+   */
+
+
+  var findRoot = function findRoot(className) {
+    var all = document.querySelectorAll('.' + className);
+    var el;
+
+    for (var i = 0; i < all.length; i++) {
+      if (!hasChildEls(all[i])) {
+        el = all[i];
+        break;
+      }
+    }
+
+    return el;
+  };
+  /**
+   * Initialize the plugin on a player.
+   *
+   * @param  {Object} [options]
+   *         An options object.
+   *
+   * @param  {HTMLElement} [options.el]
+   *         A DOM element to use as a root node for the playlist.
+   *
+   * @param  {string} [options.className]
+   *         An HTML class name to use to find a root node for the playlist.
+   *
+   * @param  {boolean} [options.playOnSelect = false]
+   *         If true, will attempt to begin playback upon selecting a new
+   *         playlist item in the UI.
+   */
+
+
+  var playlistUi = function playlistUi(options) {
+    var player = this;
+
+    if (!player.playlist) {
+      throw new Error('videojs-playlist plugin is required by the videojs-playlist-ui plugin');
+    }
+
+    if (dom.isEl(options)) {
+      videojs.log.warn('videojs-playlist-ui: Passing an element directly to playlistUi() is deprecated, use the "el" option instead!');
+      options = {
+        el: options
+      };
+    }
+
+    options = videojs.mergeOptions(defaults, options); // If the player is already using this plugin, remove the pre-existing
+    // PlaylistMenu, but retain the element and its location in the DOM because
+    // it will be re-used.
+
+    if (player.playlistMenu) {
+      var el = player.playlistMenu.el(); // Catch cases where the menu may have been disposed elsewhere or the
+      // element removed from the DOM.
+
+      if (el) {
+        var parentNode = el.parentNode;
+        var nextSibling = el.nextSibling; // Disposing the menu will remove `el` from the DOM, but we need to
+        // empty it ourselves to be sure.
+
+        player.playlistMenu.dispose();
+        dom.emptyEl(el); // Put the element back in its place.
+
+        if (nextSibling) {
+          parentNode.insertBefore(el, nextSibling);
+        } else {
+          parentNode.appendChild(el);
+        }
+
+        options.el = el;
+      }
+    }
+
+    if (!dom.isEl(options.el)) {
+      options.el = findRoot(options.className);
+    }
+
+    player.playlistMenu = new PlaylistMenu(player, options);
+  }; // register components
+
+
+  videojs.registerComponent('PlaylistMenu', PlaylistMenu);
+  videojs.registerComponent('PlaylistMenuItem', PlaylistMenuItem); // register the plugin
+
+  registerPlugin('playlistUi', playlistUi);
+  playlistUi.VERSION = version;
+
+  return playlistUi;
+
+})));

+ 909 - 0
js/videojs-playlist/videojs-playlist.js

@@ -0,0 +1,909 @@
+/*! @name videojs-playlist @version 4.3.1 @license Apache-2.0 */
+(function (global, factory) {
+  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('video.js')) :
+  typeof define === 'function' && define.amd ? define(['video.js'], factory) :
+  (global = global || self, global.videojsPlaylist = factory(global.videojs));
+}(this, function (videojs) { 'use strict';
+
+  videojs = videojs && videojs.hasOwnProperty('default') ? videojs['default'] : videojs;
+
+  /**
+   * Validates a number of seconds to use as the auto-advance delay.
+   *
+   * @private
+   * @param   {number} s
+   *          The number to check
+   *
+   * @return  {boolean}
+   *          Whether this is a valid second or not
+   */
+  var validSeconds = function validSeconds(s) {
+    return typeof s === 'number' && !isNaN(s) && s >= 0 && s < Infinity;
+  };
+  /**
+   * Resets the auto-advance behavior of a player.
+   *
+   * @param {Player} player
+   *        The player to reset the behavior on
+   */
+
+
+  var reset = function reset(player) {
+    var aa = player.playlist.autoadvance_;
+
+    if (aa.timeout) {
+      player.clearTimeout(aa.timeout);
+    }
+
+    if (aa.trigger) {
+      player.off('ended', aa.trigger);
+    }
+
+    aa.timeout = null;
+    aa.trigger = null;
+  };
+  /**
+   * Sets up auto-advance behavior on a player.
+   *
+   * @param  {Player} player
+   *         the current player
+   *
+   * @param  {number} delay
+   *         The number of seconds to wait before each auto-advance.
+   *
+   * @return {undefined}
+   *         Used to short circuit function logic
+   */
+
+
+  var setup = function setup(player, delay) {
+    reset(player); // Before queuing up new auto-advance behavior, check if `seconds` was
+    // called with a valid value.
+
+    if (!validSeconds(delay)) {
+      player.playlist.autoadvance_.delay = null;
+      return;
+    }
+
+    player.playlist.autoadvance_.delay = delay;
+
+    player.playlist.autoadvance_.trigger = function () {
+      // This calls setup again, which will reset the existing auto-advance and
+      // set up another auto-advance for the next "ended" event.
+      var cancelOnPlay = function cancelOnPlay() {
+        return setup(player, delay);
+      }; // If there is a "play" event while we're waiting for an auto-advance,
+      // we need to cancel the auto-advance. This could mean the user seeked
+      // back into the content or restarted the content. This is reproducible
+      // with an auto-advance > 0.
+
+
+      player.one('play', cancelOnPlay);
+      player.playlist.autoadvance_.timeout = player.setTimeout(function () {
+        reset(player);
+        player.off('play', cancelOnPlay);
+        player.playlist.next();
+      }, delay * 1000);
+    };
+
+    player.one('ended', player.playlist.autoadvance_.trigger);
+  };
+
+  /**
+   * Removes all remote text tracks from a player.
+   *
+   * @param  {Player} player
+   *         The player to clear tracks on
+   */
+
+  var clearTracks = function clearTracks(player) {
+    var tracks = player.remoteTextTracks();
+    var i = tracks && tracks.length || 0; // This uses a `while` loop rather than `forEach` because the
+    // `TextTrackList` object is a live DOM list (not an array).
+
+    while (i--) {
+      player.removeRemoteTextTrack(tracks[i]);
+    }
+  };
+  /**
+   * Plays an item on a player's playlist.
+   *
+   * @param  {Player} player
+   *         The player to play the item on
+   *
+   * @param  {Object} item
+   *         A source from the playlist.
+   *
+   * @return {Player}
+   *         The player that is now playing the item
+   */
+
+
+  var playItem = function playItem(player, item) {
+    var replay = !player.paused() || player.ended();
+    player.trigger('beforeplaylistitem', item.originalValue || item);
+
+    if (item.playlistItemId_) {
+      player.playlist.currentPlaylistItemId_ = item.playlistItemId_;
+    }
+
+    player.poster(item.poster || '');
+    player.src(item.sources);
+    clearTracks(player);
+    player.ready(function () {
+      (item.textTracks || []).forEach(player.addRemoteTextTrack.bind(player));
+      player.trigger('playlistitem', item.originalValue || item);
+
+      if (replay) {
+        var playPromise = player.play(); // silence error when a pause interrupts a play request
+        // on browsers which return a promise
+
+        if (typeof playPromise !== 'undefined' && typeof playPromise.then === 'function') {
+          playPromise.then(null, function (e) {});
+        }
+      }
+
+      setup(player, player.playlist.autoadvance_.delay);
+    });
+    return player;
+  };
+
+  /**
+   * Returns whether a playlist item is an object of any kind, excluding null.
+   *
+   * @private
+   *
+   * @param {Object}
+   *         value to be checked
+   *
+   * @return {boolean}
+   *          The result
+   */
+
+  var isItemObject = function isItemObject(value) {
+    return !!value && typeof value === 'object';
+  };
+  /**
+   * Look through an array of playlist items and transform any primitive
+   * as well as null values to objects. This method also adds a property
+   * to the transformed item containing original value passed in an input list.
+   *
+   * @private
+   *
+   * @param  {Array} arr
+   *         An array of playlist items
+   *
+   * @return {Array}
+   *         A new array with transformed items
+   */
+
+
+  var transformPrimitiveItems = function transformPrimitiveItems(arr) {
+    var list = [];
+    var tempItem;
+    arr.forEach(function (item) {
+      if (!isItemObject(item)) {
+        tempItem = Object(item);
+        tempItem.originalValue = item;
+      } else {
+        tempItem = item;
+      }
+
+      list.push(tempItem);
+    });
+    return list;
+  };
+  /**
+   * Generate a unique id for each playlist item object. This id will be used to determine
+   * index of an item in the playlist array for cases where there are multiple items with
+   * the same source set.
+   *
+   * @private
+   *
+   * @param  {Array} arr
+   *         An array of playlist items
+   */
+
+
+  var generatePlaylistItemId = function generatePlaylistItemId(arr) {
+    var guid = 1;
+    arr.forEach(function (item) {
+      item.playlistItemId_ = guid++;
+    });
+  };
+  /**
+   * Look through an array of playlist items for a specific playlist item id.
+   *
+   * @private
+   * @param   {Array} list
+   *          An array of playlist items to look through
+   *
+   * @param   {number} currentItemId
+   *          The current item ID.
+   *
+   * @return  {number}
+   *          The index of the playlist item or -1 if not found
+   */
+
+
+  var indexInPlaylistItemIds = function indexInPlaylistItemIds(list, currentItemId) {
+    for (var i = 0; i < list.length; i++) {
+      if (list[i].playlistItemId_ === currentItemId) {
+        return i;
+      }
+    }
+
+    return -1;
+  };
+  /**
+   * Given two sources, check to see whether the two sources are equal.
+   * If both source urls have a protocol, the protocols must match, otherwise, protocols
+   * are ignored.
+   *
+   * @private
+   * @param {string|Object} source1
+   *        The first source
+   *
+   * @param {string|Object} source2
+   *        The second source
+   *
+   * @return {boolean}
+   *         The result
+   */
+
+
+  var sourceEquals = function sourceEquals(source1, source2) {
+    var src1 = source1;
+    var src2 = source2;
+
+    if (typeof source1 === 'object') {
+      src1 = source1.src;
+    }
+
+    if (typeof source2 === 'object') {
+      src2 = source2.src;
+    }
+
+    if (/^\/\//.test(src1)) {
+      src2 = src2.slice(src2.indexOf('//'));
+    }
+
+    if (/^\/\//.test(src2)) {
+      src1 = src1.slice(src1.indexOf('//'));
+    }
+
+    return src1 === src2;
+  };
+  /**
+   * Look through an array of playlist items for a specific `source`;
+   * checking both the value of elements and the value of their `src`
+   * property.
+   *
+   * @private
+   * @param   {Array} arr
+   *          An array of playlist items to look through
+   *
+   * @param   {string} src
+   *          The source to look for
+   *
+   * @return  {number}
+   *          The index of that source or -1
+   */
+
+
+  var indexInSources = function indexInSources(arr, src) {
+    for (var i = 0; i < arr.length; i++) {
+      var sources = arr[i].sources;
+
+      if (Array.isArray(sources)) {
+        for (var j = 0; j < sources.length; j++) {
+          var source = sources[j];
+
+          if (source && sourceEquals(source, src)) {
+            return i;
+          }
+        }
+      }
+    }
+
+    return -1;
+  };
+  /**
+   * Randomize the contents of an array.
+   *
+   * @private
+   * @param  {Array} arr
+   *         An array.
+   *
+   * @return {Array}
+   *         The same array that was passed in.
+   */
+
+
+  var randomize = function randomize(arr) {
+    var index = -1;
+    var lastIndex = arr.length - 1;
+
+    while (++index < arr.length) {
+      var rand = index + Math.floor(Math.random() * (lastIndex - index + 1));
+      var value = arr[rand];
+      arr[rand] = arr[index];
+      arr[index] = value;
+    }
+
+    return arr;
+  };
+  /**
+   * Factory function for creating new playlist implementation on the given player.
+   *
+   * API summary:
+   *
+   * playlist(['a', 'b', 'c']) // setter
+   * playlist() // getter
+   * playlist.currentItem() // getter, 0
+   * playlist.currentItem(1) // setter, 1
+   * playlist.next() // 'c'
+   * playlist.previous() // 'b'
+   * playlist.first() // 'a'
+   * playlist.last() // 'c'
+   * playlist.autoadvance(5) // 5 second delay
+   * playlist.autoadvance() // cancel autoadvance
+   *
+   * @param  {Player} player
+   *         The current player
+   *
+   * @param  {Array=} initialList
+   *         If given, an initial list of sources with which to populate
+   *         the playlist.
+   *
+   * @param  {number=}  initialIndex
+   *         If given, the index of the item in the list that should
+   *         be loaded first. If -1, no video is loaded. If omitted, The
+   *         the first video is loaded.
+   *
+   * @return {Function}
+   *         Returns the playlist function specific to the given player.
+   */
+
+
+  function factory(player, initialList, initialIndex) {
+    if (initialIndex === void 0) {
+      initialIndex = 0;
+    }
+
+    var list = null;
+    var changing = false;
+    /**
+     * Get/set the playlist for a player.
+     *
+     * This function is added as an own property of the player and has its
+     * own methods which can be called to manipulate the internal state.
+     *
+     * @param  {Array} [newList]
+     *         If given, a new list of sources with which to populate the
+     *         playlist. Without this, the function acts as a getter.
+     *
+     * @param  {number}  [newIndex]
+     *         If given, the index of the item in the list that should
+     *         be loaded first. If -1, no video is loaded. If omitted, The
+     *         the first video is loaded.
+     *
+     * @return {Array}
+     *         The playlist
+     */
+
+    var playlist = player.playlist = function (newList, newIndex) {
+      if (newIndex === void 0) {
+        newIndex = 0;
+      }
+
+      if (changing) {
+        throw new Error('do not call playlist() during a playlist change');
+      }
+
+      if (Array.isArray(newList)) {
+        // @todo - Simplify this to `list.slice()` for v5.
+        var previousPlaylist = Array.isArray(list) ? list.slice() : null;
+        var nextPlaylist = newList.slice();
+        list = nextPlaylist.slice(); // Transform any primitive and null values in an input list to objects
+
+        if (list.filter(function (item) {
+          return isItemObject(item);
+        }).length !== list.length) {
+          list = transformPrimitiveItems(list);
+        } // Add unique id to each playlist item. This id will be used
+        // to determine index in cases where there are more than one
+        // identical sources in the playlist.
+
+
+        generatePlaylistItemId(list); // Mark the playlist as changing during the duringplaylistchange lifecycle.
+
+        changing = true;
+        player.trigger({
+          type: 'duringplaylistchange',
+          nextIndex: newIndex,
+          nextPlaylist: nextPlaylist,
+          previousIndex: playlist.currentIndex_,
+          // @todo - Simplify this to simply pass along `previousPlaylist` for v5.
+          previousPlaylist: previousPlaylist || []
+        });
+        changing = false;
+
+        if (newIndex !== -1) {
+          playlist.currentItem(newIndex);
+        } // The only time the previous playlist is null is the first call to this
+        // function. This allows us to fire the `duringplaylistchange` event
+        // every time the playlist is populated and to maintain backward
+        // compatibility by not firing the `playlistchange` event on the initial
+        // population of the list.
+        //
+        // @todo - Remove this condition in preparation for v5.
+
+
+        if (previousPlaylist) {
+          player.setTimeout(function () {
+            player.trigger('playlistchange');
+          }, 0);
+        }
+      } // Always return a shallow clone of the playlist list.
+      //  We also want to return originalValue if any item in the list has it.
+
+
+      return list.map(function (item) {
+        return item.originalValue || item;
+      }).slice();
+    }; // On a new source, if there is no current item, disable auto-advance.
+
+
+    player.on('loadstart', function () {
+      if (playlist.currentItem() === -1) {
+        reset(player);
+      }
+    });
+    playlist.currentIndex_ = -1;
+    playlist.player_ = player;
+    playlist.autoadvance_ = {};
+    playlist.repeat_ = false;
+    playlist.currentPlaylistItemId_ = null;
+    /**
+     * Get or set the current item in the playlist.
+     *
+     * During the duringplaylistchange event, acts only as a getter.
+     *
+     * @param  {number} [index]
+     *         If given as a valid value, plays the playlist item at that index.
+     *
+     * @return {number}
+     *         The current item index.
+     */
+
+    playlist.currentItem = function (index) {
+      // If the playlist is changing, only act as a getter.
+      if (changing) {
+        return playlist.currentIndex_;
+      } // Act as a setter when the index is given and is a valid number.
+
+
+      if (typeof index === 'number' && playlist.currentIndex_ !== index && index >= 0 && index < list.length) {
+        playlist.currentIndex_ = index;
+        playItem(playlist.player_, list[playlist.currentIndex_]);
+        return playlist.currentIndex_;
+      }
+
+      var src = playlist.player_.currentSrc() || ''; // If there is a currentPlaylistItemId_, validate that it matches the
+      // current source URL returned by the player. This is sufficient evidence
+      // to suggest that the source was set by the playlist plugin. This code
+      // exists primarily to deal with playlists where multiple items have the
+      // same source.
+
+      if (playlist.currentPlaylistItemId_) {
+        var indexInItemIds = indexInPlaylistItemIds(list, playlist.currentPlaylistItemId_);
+        var item = list[indexInItemIds]; // Found a match, this is our current index!
+
+        if (item && Array.isArray(item.sources) && indexInSources([item], src) > -1) {
+          playlist.currentIndex_ = indexInItemIds;
+          return playlist.currentIndex_;
+        } // If this does not match the current source, null it out so subsequent
+        // calls can skip this step.
+
+
+        playlist.currentPlaylistItemId_ = null;
+      } // Finally, if we don't have a valid, current playlist item ID, we can
+      // auto-detect it based on the player's current source URL.
+
+
+      playlist.currentIndex_ = playlist.indexOf(src);
+      return playlist.currentIndex_;
+    };
+    /**
+     * Checks if the playlist contains a value.
+     *
+     * @param  {string|Object|Array} value
+     *         The value to check
+     *
+     * @return {boolean}
+     *         The result
+     */
+
+
+    playlist.contains = function (value) {
+      return playlist.indexOf(value) !== -1;
+    };
+    /**
+     * Gets the index of a value in the playlist or -1 if not found.
+     *
+     * @param  {string|Object|Array} value
+     *         The value to find the index of
+     *
+     * @return {number}
+     *         The index or -1
+     */
+
+
+    playlist.indexOf = function (value) {
+      if (typeof value === 'string') {
+        return indexInSources(list, value);
+      }
+
+      var sources = Array.isArray(value) ? value : value.sources;
+
+      for (var i = 0; i < sources.length; i++) {
+        var source = sources[i];
+
+        if (typeof source === 'string') {
+          return indexInSources(list, source);
+        } else if (source.src) {
+          return indexInSources(list, source.src);
+        }
+      }
+
+      return -1;
+    };
+    /**
+     * Get the index of the current item in the playlist. This is identical to
+     * calling `currentItem()` with no arguments.
+     *
+     * @return {number}
+     *         The current item index.
+     */
+
+
+    playlist.currentIndex = function () {
+      return playlist.currentItem();
+    };
+    /**
+     * Get the index of the last item in the playlist.
+     *
+     * @return {number}
+     *         The index of the last item in the playlist or -1 if there are no
+     *         items.
+     */
+
+
+    playlist.lastIndex = function () {
+      return list.length - 1;
+    };
+    /**
+     * Get the index of the next item in the playlist.
+     *
+     * @return {number}
+     *         The index of the next item in the playlist or -1 if there is no
+     *         current item.
+     */
+
+
+    playlist.nextIndex = function () {
+      var current = playlist.currentItem();
+
+      if (current === -1) {
+        return -1;
+      }
+
+      var lastIndex = playlist.lastIndex(); // When repeating, loop back to the beginning on the last item.
+
+      if (playlist.repeat_ && current === lastIndex) {
+        return 0;
+      } // Don't go past the end of the playlist.
+
+
+      return Math.min(current + 1, lastIndex);
+    };
+    /**
+     * Get the index of the previous item in the playlist.
+     *
+     * @return {number}
+     *         The index of the previous item in the playlist or -1 if there is
+     *         no current item.
+     */
+
+
+    playlist.previousIndex = function () {
+      var current = playlist.currentItem();
+
+      if (current === -1) {
+        return -1;
+      } // When repeating, loop back to the end of the playlist.
+
+
+      if (playlist.repeat_ && current === 0) {
+        return playlist.lastIndex();
+      } // Don't go past the beginning of the playlist.
+
+
+      return Math.max(current - 1, 0);
+    };
+    /**
+     * Plays the first item in the playlist.
+     *
+     * @return {Object|undefined}
+     *         Returns undefined and has no side effects if the list is empty.
+     */
+
+
+    playlist.first = function () {
+      if (changing) {
+        return;
+      }
+
+      var newItem = playlist.currentItem(0);
+
+      if (list.length) {
+        return list[newItem].originalValue || list[newItem];
+      }
+
+      playlist.currentIndex_ = -1;
+    };
+    /**
+     * Plays the last item in the playlist.
+     *
+     * @return {Object|undefined}
+     *         Returns undefined and has no side effects if the list is empty.
+     */
+
+
+    playlist.last = function () {
+      if (changing) {
+        return;
+      }
+
+      var newItem = playlist.currentItem(playlist.lastIndex());
+
+      if (list.length) {
+        return list[newItem].originalValue || list[newItem];
+      }
+
+      playlist.currentIndex_ = -1;
+    };
+    /**
+     * Plays the next item in the playlist.
+     *
+     * @return {Object|undefined}
+     *         Returns undefined and has no side effects if on last item.
+     */
+
+
+    playlist.next = function () {
+      if (changing) {
+        return;
+      }
+
+      var index = playlist.nextIndex();
+
+      if (index !== playlist.currentIndex_) {
+        var newItem = playlist.currentItem(index);
+        return list[newItem].originalValue || list[newItem];
+      }
+    };
+    /**
+     * Plays the previous item in the playlist.
+     *
+     * @return {Object|undefined}
+     *         Returns undefined and has no side effects if on first item.
+     */
+
+
+    playlist.previous = function () {
+      if (changing) {
+        return;
+      }
+
+      var index = playlist.previousIndex();
+
+      if (index !== playlist.currentIndex_) {
+        var newItem = playlist.currentItem(index);
+        return list[newItem].originalValue || list[newItem];
+      }
+    };
+    /**
+     * Set up auto-advance on the playlist.
+     *
+     * @param  {number} [delay]
+     *         The number of seconds to wait before each auto-advance.
+     */
+
+
+    playlist.autoadvance = function (delay) {
+      setup(playlist.player_, delay);
+    };
+    /**
+     * Sets `repeat` option, which makes the "next" video of the last video in
+     * the playlist be the first video in the playlist.
+     *
+     * @param  {boolean} [val]
+     *         The value to set repeat to
+     *
+     * @return {boolean}
+     *         The current value of repeat
+     */
+
+
+    playlist.repeat = function (val) {
+      if (val === undefined) {
+        return playlist.repeat_;
+      }
+
+      if (typeof val !== 'boolean') {
+        videojs.log.error('videojs-playlist: Invalid value for repeat', val);
+        return;
+      }
+
+      playlist.repeat_ = !!val;
+      return playlist.repeat_;
+    };
+    /**
+     * Sorts the playlist array.
+     *
+     * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort}
+     * @fires playlistsorted
+     *
+     * @param {Function} compare
+     *        A comparator function as per the native Array method.
+     */
+
+
+    playlist.sort = function (compare) {
+      // Bail if the array is empty.
+      if (!list.length) {
+        return;
+      }
+
+      list.sort(compare); // If the playlist is changing, don't trigger events.
+
+      if (changing) {
+        return;
+      }
+      /**
+       * Triggered after the playlist is sorted internally.
+       *
+       * @event playlistsorted
+       * @type {Object}
+       */
+
+
+      player.trigger('playlistsorted');
+    };
+    /**
+     * Reverses the playlist array.
+     *
+     * @see {@link https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reverse}
+     * @fires playlistsorted
+     */
+
+
+    playlist.reverse = function () {
+      // Bail if the array is empty.
+      if (!list.length) {
+        return;
+      }
+
+      list.reverse(); // If the playlist is changing, don't trigger events.
+
+      if (changing) {
+        return;
+      }
+      /**
+       * Triggered after the playlist is sorted internally.
+       *
+       * @event playlistsorted
+       * @type {Object}
+       */
+
+
+      player.trigger('playlistsorted');
+    };
+    /**
+     * Shuffle the contents of the list randomly.
+     *
+     * @see   {@link https://github.com/lodash/lodash/blob/40e096b6d5291a025e365a0f4c010d9a0efb9a69/shuffle.js}
+     * @fires playlistsorted
+     * @todo  Make the `rest` option default to `true` in v5.0.0.
+     * @param {Object} [options]
+     *        An object containing shuffle options.
+     *
+     * @param {boolean} [options.rest = false]
+     *        By default, the entire playlist is randomized. However, this may
+     *        not be desirable in all cases, such as when a user is already
+     *        watching a video.
+     *
+     *        When `true` is passed for this option, it will only shuffle
+     *        playlist items after the current item. For example, when on the
+     *        first item, will shuffle the second item and beyond.
+     */
+
+
+    playlist.shuffle = function (_temp) {
+      var _ref = _temp === void 0 ? {} : _temp,
+          rest = _ref.rest;
+
+      var index = 0;
+      var arr = list; // When options.rest is true, start randomization at the item after the
+      // current item.
+
+      if (rest) {
+        index = playlist.currentIndex_ + 1;
+        arr = list.slice(index);
+      } // Bail if the array is empty or too short to shuffle.
+
+
+      if (arr.length <= 1) {
+        return;
+      }
+
+      randomize(arr); // When options.rest is true, splice the randomized sub-array back into
+      // the original array.
+
+      if (rest) {
+        var _list;
+
+        (_list = list).splice.apply(_list, [index, arr.length].concat(arr));
+      } // If the playlist is changing, don't trigger events.
+
+
+      if (changing) {
+        return;
+      }
+      /**
+       * Triggered after the playlist is sorted internally.
+       *
+       * @event playlistsorted
+       * @type {Object}
+       */
+
+
+      player.trigger('playlistsorted');
+    }; // If an initial list was given, populate the playlist with it.
+
+
+    if (Array.isArray(initialList)) {
+      playlist(initialList.slice(), initialIndex); // If there is no initial list given, silently set an empty array.
+    } else {
+      list = [];
+    }
+
+    return playlist;
+  }
+
+  var version = "4.3.1";
+
+  var registerPlugin = videojs.registerPlugin || videojs.plugin;
+  /**
+   * The video.js playlist plugin. Invokes the playlist-maker to create a
+   * playlist function on the specific player.
+   *
+   * @param {Array} list
+   *        a list of sources
+   *
+   * @param {number} item
+   *        The index to start at
+   */
+
+  var plugin = function plugin(list, item) {
+    factory(this, list, item);
+  };
+
+  registerPlugin('playlist', plugin);
+  plugin.VERSION = version;
+
+  return plugin;
+
+}));

文件差异内容过多而无法显示
+ 58924 - 0
js/videojs/video.js


+ 86 - 0
lang/zh-CN.js

@@ -0,0 +1,86 @@
+videojs.addLanguage('zh-CN', {
+  "Play": "播放",
+  "Pause": "暂停",
+  "Current Time": "当前时间",
+  "Duration": "时长",
+  "Remaining Time": "剩余时间",
+  "Stream Type": "媒体流类型",
+  "LIVE": "直播",
+  "Loaded": "加载完毕",
+  "Progress": "进度",
+  "Fullscreen": "全屏",
+  "Non-Fullscreen": "退出全屏",
+  "Mute": "静音",
+  "Unmute": "取消静音",
+  "Playback Rate": "播放速度",
+  "Subtitles": "字幕",
+  "subtitles off": "关闭字幕",
+  "Captions": "内嵌字幕",
+  "captions off": "关闭内嵌字幕",
+  "Chapters": "节目段落",
+  "Close Modal Dialog": "关闭弹窗",
+  "Descriptions": "描述",
+  "descriptions off": "关闭描述",
+  "Audio Track": "音轨",
+  "You aborted the media playback": "视频播放被终止",
+  "A network error caused the media download to fail part-way.": "网络错误导致视频下载中途失败。",
+  "The media could not be loaded, either because the server or network failed or because the format is not supported.": "视频因格式不支持或者服务器或网络的问题无法加载。",
+  "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "由于视频文件损坏或是该视频使用了你的浏览器不支持的功能,播放终止。",
+  "No compatible source was found for this media.": "无法找到此视频兼容的源。",
+  "The media is encrypted and we do not have the keys to decrypt it.": "视频已加密,无法解密。",
+  "Play Video": "播放视频",
+  "Close": "关闭",
+  "Modal Window": "弹窗",
+  "This is a modal window": "这是一个弹窗",
+  "This modal can be closed by pressing the Escape key or activating the close button.": "可以按ESC按键或启用关闭按钮来关闭此弹窗。",
+  ", opens captions settings dialog": ", 开启标题设置弹窗",
+  ", opens subtitles settings dialog": ", 开启字幕设置弹窗",
+  ", opens descriptions settings dialog": ", 开启描述设置弹窗",
+  ", selected": ", 选择",
+  "captions settings": "字幕设定",
+  "Audio Player": "音频播放器",
+  "Video Player": "视频播放器",
+  "Replay": "重播",
+  "Progress Bar": "进度条",
+  "Volume Level": "音量",
+  "subtitles settings": "字幕设定",
+  "descriptions settings": "描述设定",
+  "Text": "文字",
+  "White": "白",
+  "Black": "黑",
+  "Red": "红",
+  "Green": "绿",
+  "Blue": "蓝",
+  "Yellow": "黄",
+  "Magenta": "紫红",
+  "Cyan": "青",
+  "Background": "背景",
+  "Window": "视窗",
+  "Transparent": "透明",
+  "Semi-Transparent": "半透明",
+  "Opaque": "不透明",
+  "Font Size": "字体尺寸",
+  "Text Edge Style": "字体边缘样式",
+  "None": "无",
+  "Raised": "浮雕",
+  "Depressed": "压低",
+  "Uniform": "均匀",
+  "Dropshadow": "下阴影",
+  "Font Family": "字体库",
+  "Proportional Sans-Serif": "比例无细体",
+  "Monospace Sans-Serif": "单间隔无细体",
+  "Proportional Serif": "比例细体",
+  "Monospace Serif": "单间隔细体",
+  "Casual": "舒适",
+  "Script": "手写体",
+  "Small Caps": "小型大写字体",
+  "Reset": "重置",
+  "restore all settings to the default values": "恢复全部设定至预设值",
+  "Done": "完成",
+  "Caption Settings Dialog": "字幕设定视窗",
+  "Beginning of dialog window. Escape will cancel and close the window.": "开始对话视窗。离开会取消及关闭视窗",
+  "End of dialog window.": "结束对话视窗",
+  "Now Playing":"正在播放",
+  "Up Next":"下一个播放",
+  "Untitled Video":"无标题视频"
+});

+ 83 - 0
lang/zh-CN.json

@@ -0,0 +1,83 @@
+{
+  "Play": "播放",
+  "Pause": "暂停",
+  "Current Time": "当前时间",
+  "Duration": "时长",
+  "Remaining Time": "剩余时间",
+  "Stream Type": "媒体流类型",
+  "LIVE": "直播",
+  "Loaded": "加载完毕",
+  "Progress": "进度",
+  "Fullscreen": "全屏",
+  "Non-Fullscreen": "退出全屏",
+  "Mute": "静音",
+  "Unmute": "取消静音",
+  "Playback Rate": "播放速度",
+  "Subtitles": "字幕",
+  "subtitles off": "关闭字幕",
+  "Captions": "内嵌字幕",
+  "captions off": "关闭内嵌字幕",
+  "Chapters": "节目段落",
+  "Close Modal Dialog": "关闭弹窗",
+  "Descriptions": "描述",
+  "descriptions off": "关闭描述",
+  "Audio Track": "音轨",
+  "You aborted the media playback": "视频播放被终止",
+  "A network error caused the media download to fail part-way.": "网络错误导致视频下载中途失败。",
+  "The media could not be loaded, either because the server or network failed or because the format is not supported.": "视频因格式不支持或者服务器或网络的问题无法加载。",
+  "The media playback was aborted due to a corruption problem or because the media used features your browser did not support.": "由于视频文件损坏或是该视频使用了你的浏览器不支持的功能,播放终止。",
+  "No compatible source was found for this media.": "无法找到此视频兼容的源。",
+  "The media is encrypted and we do not have the keys to decrypt it.": "视频已加密,无法解密。",
+  "Play Video": "播放视频",
+  "Close": "关闭",
+  "Modal Window": "弹窗",
+  "This is a modal window": "这是一个弹窗",
+  "This modal can be closed by pressing the Escape key or activating the close button.": "可以按ESC按键或启用关闭按钮来关闭此弹窗。",
+  ", opens captions settings dialog": ", 开启标题设置弹窗",
+  ", opens subtitles settings dialog": ", 开启字幕设置弹窗",
+  ", opens descriptions settings dialog": ", 开启描述设置弹窗",
+  ", selected": ", 选择",
+  "captions settings": "字幕设定",
+  "Audio Player": "音频播放器",
+  "Video Player": "视频播放器",
+  "Replay": "重播",
+  "Progress Bar": "进度条",
+  "Volume Level": "音量",
+  "subtitles settings": "字幕设定",
+  "descriptions settings": "描述设定",
+  "Text": "文字",
+  "White": "白",
+  "Black": "黑",
+  "Red": "红",
+  "Green": "绿",
+  "Blue": "蓝",
+  "Yellow": "黄",
+  "Magenta": "紫红",
+  "Cyan": "青",
+  "Background": "背景",
+  "Window": "视窗",
+  "Transparent": "透明",
+  "Semi-Transparent": "半透明",
+  "Opaque": "不透明",
+  "Font Size": "字体尺寸",
+  "Text Edge Style": "字体边缘样式",
+  "None": "无",
+  "Raised": "浮雕",
+  "Depressed": "压低",
+  "Uniform": "均匀",
+  "Dropshadow": "下阴影",
+  "Font Family": "字体库",
+  "Proportional Sans-Serif": "比例无细体",
+  "Monospace Sans-Serif": "单间隔无细体",
+  "Proportional Serif": "比例细体",
+  "Monospace Serif": "单间隔细体",
+  "Casual": "舒适",
+  "Script": "手写体",
+  "Small Caps": "小型大写字体",
+  "Reset": "重置",
+  "restore all settings to the default values": "恢复全部设定至预设值",
+  "Done": "完成",
+  "Caption Settings Dialog": "字幕设定视窗",
+  "Beginning of dialog window. Escape will cancel and close the window.": "开始对话视窗。离开会取消及关闭视窗",
+  "End of dialog window.": "结束对话视窗"
+}