├── .eslintrc ├── .gitignore ├── .stylelintrc ├── README.md ├── __test__ ├── e2e │ └── index.js ├── index.js └── play.js ├── build ├── rollup.config.base.js ├── rollup.config.common.js ├── rollup.config.es.js ├── rollup.config.min.js └── rollup.config.umd.js ├── coverage └── chrome │ ├── base.css │ ├── index.html │ ├── prettify.css │ ├── prettify.js │ ├── sort-arrow-sprite.png │ ├── sorter.js │ └── src │ ├── base.js.html │ ├── clarity.js.html │ ├── component.js.html │ ├── createchild.js.html │ ├── index.html │ ├── index.js.html │ ├── play.js.html │ ├── progressbar.js.html │ ├── progresstime.js.html │ ├── screen.js.html │ └── volume.js.html ├── demo ├── chimee.js ├── icon │ ├── pause.svg │ ├── play.svg │ ├── screen-full.svg │ ├── screen-small.svg │ ├── volume-high.svg │ ├── volume-low.svg │ └── volume-mute.svg └── index.html ├── dev.md ├── karma.conf.js ├── lib ├── index.browser.js ├── index.js ├── index.min.js └── index.mjs ├── package-lock.json ├── package.json └── src ├── .DS_Store ├── README.md ├── base.js ├── clarity.js ├── component.js ├── control.css ├── createchild.js ├── image ├── .DS_Store ├── danma-off.svg ├── danma-on.svg ├── enlarge.svg ├── loading.svg ├── pause.svg ├── play.svg ├── shrink.svg ├── spause.svg ├── splay.svg ├── vhigh.svg ├── vlow.svg └── vmute.svg ├── index.js ├── play.js ├── playbackrate.js ├── progressbar.js ├── progresstime.js ├── screen.js └── volume.js /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "globals": { 3 | "process": true, 4 | "test": true, 5 | "expect": true, 6 | "describe": true, 7 | "it": true 8 | }, 9 | "env": { 10 | "browser": true, 11 | "commonjs": true, 12 | "es6": true, 13 | "jasmine": true 14 | }, 15 | "extends": [ 16 | ], 17 | "plugins": [ 18 | ], 19 | "parser": "babel-eslint", 20 | "parserOptions": { 21 | "sourceType": "module" 22 | }, 23 | "rules": { 24 | // 强制getter/setter成对出现在对象中 25 | "accessor-pairs": 2, 26 | // 禁止在括号内使用空格 27 | "array-bracket-spacing": 0, 28 | // 把 var 语句看作是在块级作用域范围之内 29 | "block-scoped-var": 0, 30 | // 大括号风格 31 | "brace-style": [2, "1tbs", { "allowSingleLine": true }], 32 | // 不强制要求使用骆驼拼写法 33 | "camelcase": 0, 34 | // 强制在逗号周围使用空格 35 | "comma-spacing": [2, { "before": false, "after": true }], 36 | // 逗号风格(必须要放在末尾) 37 | "comma-style": [2, "last"], 38 | // 限制圈复杂度(条件数目,0为不作限制) 39 | "complexity": 0, 40 | // 禁止在计算属性内使用空格 41 | "computed-property-spacing": 0, 42 | // 允许返回不指定的值 43 | "consistent-return": 0, 44 | // 不要求一致的 This 45 | "consistent-this": 0, 46 | // 验证构造函数中 super() 的调用 47 | "constructor-super": 2, 48 | // 多行是必须遵循大括号约定 49 | "curly": [2, "multi-line"], 50 | // 不强制要求含有default分支 51 | "default-case": 0, 52 | // 强制在点号之前换行 53 | "dot-location": [2, "property"], 54 | // 不强制要求使用点号 55 | "dot-notation": 0, 56 | // 要求文件末尾保留一行空行 57 | "eol-last": 2, 58 | // 要求使用全等号 59 | "eqeqeq": [2, "allow-null"], 60 | // 可以使用匿名函数 61 | "func-names": 0, 62 | // 不强制 function 声明或表达式的一致性 63 | "func-style": 0, 64 | // 强制 generator 函数中 * 号周围有空格 65 | "generator-star-spacing": [2, { "before": true, "after": true }], 66 | // 不要求 for-in 语句中包含 if 67 | "guard-for-in": 0, 68 | // 强制回调错误处理,必须要使用err或error。不处理则会报错 69 | "handle-callback-err": [2, "^(err|error)$" ], 70 | // 强制使用一致的缩进 71 | //"indent": [2, 2, { "SwitchCase": 1 }], 72 | // 强制在对象字面量的键和值之间使用一致的空格 73 | "key-spacing": [2, { "beforeColon": false, "afterColon": true }], 74 | // 不强制使用一致的换行符风格 75 | "linebreak-style": 0, 76 | // 不强制注释周围有空行 77 | "lines-around-comment": 0, 78 | // 不强制回调函数最大嵌套深度 79 | "max-nested-callbacks": 0, 80 | // 要求构造函数首字母大写 81 | "new-cap": [2, { "newIsCap": true, "capIsNew": false }], 82 | // 要求调用无参构造函数时带括号 83 | "new-parens": 2, 84 | // 不要求变量声明语句后有一行空行 85 | "newline-after-var": 0, 86 | // 不禁用alert 87 | "no-alert": 0, 88 | // 禁止使用 Array 构造函数 89 | "no-array-constructor": 2, 90 | // 禁用 caller 或 callee 91 | "no-caller": 2, 92 | // 不禁用在 catch 语句中遮盖变量 93 | "no-catch-shadow": 0, 94 | // 禁止在条件语句中出现赋值操作符 95 | "no-cond-assign": 2, 96 | // 禁止使用console符 97 | "no-console": 0, 98 | // 禁止在条件中使用常量表达式 99 | "no-constant-condition": 2, 100 | // 不禁用 continue 101 | "no-continue": 0, 102 | // 禁止在正则表达式中使用控制字符 103 | "no-control-regex": 2, 104 | // 禁用debugger 105 | // "no-debugger": 2, 106 | // 禁止删除变量 107 | "no-delete-var": 2, 108 | // 不禁止使用看起来像除法的正则表达式 109 | "no-div-regex": 0, 110 | // 禁止 function 定义中出现重名参数 111 | "no-dupe-args": 2, 112 | // 禁止对象字面量中出现重复的 key 113 | "no-dupe-keys": 2, 114 | // 禁止出现重复的 case 标签 115 | "no-duplicate-case": 2, 116 | // 不禁止 if 语句中 return 语句之后有 else 块 117 | "no-else-return": 0, 118 | // 不禁止出现空语句块 119 | "no-empty": 0, 120 | // 禁止在正则表达式中使用空字符集 121 | "no-empty-character-class": 2, 122 | // 禁止与 null 进行比较 123 | "no-eq-null": 2, 124 | // 禁止使用eval 125 | "no-eval": 2, 126 | // 禁止对 catch 子句中的异常重新赋值 127 | "no-ex-assign": 2, 128 | // 禁止扩展原生类型 129 | "no-extend-native": 2, 130 | // 禁止不必要的 .bind() 调用 131 | "no-extra-bind": 2, 132 | // 禁止不必要的布尔转换 133 | "no-extra-boolean-cast": 2, 134 | // 不禁止冗余的括号 135 | "no-extra-parens": 0, 136 | // 不禁止不必要的分号 137 | "no-extra-semi": 0, 138 | // 禁止 case 语句落空 139 | "no-fallthrough": 2, 140 | // 禁止数字字面量中使用前导和末尾小数点 141 | "no-floating-decimal": 2, 142 | // 禁止对 function 声明重新赋值 143 | "no-func-assign": 2, 144 | // 禁用隐式的eval() 145 | "no-implied-eval": 2, 146 | // 允许在代码后使用内联注释 147 | "no-inline-comments": 0, 148 | // 禁止 function 声明出现在嵌套的语句块中 149 | "no-inner-declarations": [2, "functions"], 150 | // 禁止 RegExp 构造函数中存在无效的正则表达式字符串 151 | "no-invalid-regexp": 2, 152 | // 禁止在字符串和注释之外不规则的空白 153 | "no-irregular-whitespace": 2, 154 | // 禁用迭代器 155 | "no-iterator": 2, 156 | // 不允许标签与变量同名 157 | "no-label-var": 2, 158 | // 禁用不必要的嵌套块 159 | "no-lone-blocks": 2, 160 | // 不禁止 if 作为唯一的语句出现在 else 语句中 161 | "no-lonely-if": 0, 162 | // 不禁止循环中存在函数 163 | "no-loop-func": 0, 164 | // 不禁止 require 调用与普通变量声明混合使用 165 | "no-mixed-requires": 0, 166 | // 禁止空格和 tab 的混合缩进 167 | //"no-mixed-spaces-and-tabs": 2, 168 | // 禁止出现多个空格 169 | "no-multi-spaces": 2, 170 | // 禁止多行字符串 171 | "no-multi-str": 2, 172 | // 不允许多个空行 173 | "no-multiple-empty-lines": [2, { "max": 1 }], 174 | "no-native-reassign": 2, 175 | "no-negated-in-lhs": 2, 176 | // 不禁止使用嵌套的三元表达式,但是要换行!!! 177 | "no-nested-ternary": 0, 178 | // 禁止在非赋值或条件语句中使用 new 操作符 179 | "no-new": 2, 180 | // 不禁止对 Function 对象使用 new 操作符 181 | "no-new-func": 0, 182 | // 禁用 Object 的构造函数 183 | "no-new-object": 2, 184 | // 禁止调用 require 时使用 new 操作符 185 | "no-new-require": 2, 186 | // 禁止对 String,Number 和 Boolean 使用 new 操作符 187 | "no-new-wrappers": 2, 188 | // 禁止把全局对象作为函数调用 189 | "no-obj-calls": 2, 190 | // 禁用八进制字面量 191 | "no-octal": 2, 192 | // 禁止在字符串中使用八进制转义序列 193 | "no-octal-escape": 2, 194 | // 不禁止对 function 的参数进行重新赋值 195 | "no-param-reassign": 0, 196 | // 不禁止对 __dirname 和 __filename 进行字符串连接 197 | "no-path-concat": 0, 198 | // 不禁用 process.env 199 | "no-process-env": 0, 200 | // 不禁用 process.exit() 201 | "no-process-exit": 0, 202 | // 不禁用禁用 __proto__ 属性 203 | "no-proto": 0, 204 | // 禁止重新声明变量 205 | "no-redeclare": 2, 206 | // 禁止正则表达式字面量中出现多个空格 207 | "no-regex-spaces": 2, 208 | // 不禁用通过 require 加载的指定模块 209 | "no-restricted-modules": 0, 210 | // 禁止在返回语句中赋值 211 | "no-return-assign": 2, 212 | // 不禁止使用 javascript: url 213 | "no-script-url": 0, 214 | // 禁止自身比较 215 | "no-self-compare": 2, 216 | // 不允许使用逗号操作符 217 | "no-sequences": 2, 218 | // 不禁止变量声明与外层作用域的变量同名 219 | "no-shadow": 0, 220 | // 关键字不能被遮蔽 221 | "no-shadow-restricted-names": 2, 222 | "no-spaced-func": 2, 223 | // 禁用稀疏数组 224 | "no-sparse-arrays": 2, 225 | // 不禁止使用同步方法 226 | "no-sync": 0, 227 | // 不禁用三元操作符 228 | "no-ternary": 0, 229 | // 禁止在构造函数中,在调用 super() 之前使用 this 或 super 230 | "no-this-before-super": 2, 231 | // 禁止抛出异常字面量 232 | "no-throw-literal": 2, 233 | // 禁用行尾空格 234 | "no-trailing-spaces": 2, 235 | // 禁用未声明的变量,除非它们在 /*global */ 注释中被提到 236 | "no-undef": 2, 237 | // 禁止将变量初始化为 undefined 238 | "no-undef-init": 2, 239 | // 不禁止将 undefined 作为标识符 240 | "no-undefined": 0, 241 | // 允许标识符中有悬空下划线 242 | "no-underscore-dangle": 0, 243 | // 禁止出现令人困惑的多行表达式 244 | "no-unexpected-multiline": 2, 245 | // 禁止可以在有更简单的可替代的表达式时使用三元操作符 246 | "no-unneeded-ternary": 2, 247 | // 禁止在return、throw、continue 和 break 语句之后出现不可达代码 248 | "no-unreachable": 2, 249 | "no-unused-expressions": 0, 250 | // 禁止未使用过的变量 251 | "no-unused-vars": [2, { "vars": "all", "args": "none" }], 252 | // 不禁止定义前使用 253 | "no-use-before-define": 0, 254 | // 要求使用 let 或 const 而不是 var 255 | "no-var": 2, 256 | // 不禁用 void 操作符 257 | "no-void": 0, 258 | // 不禁用警告注释 259 | "no-warning-comments": 0, 260 | // 禁用with 261 | "no-with": 2, 262 | // 不强制在花括号中使用一致的空格 263 | "object-curly-spacing": 0, 264 | // 要求对象字面量简写语法 265 | "object-shorthand": 2, 266 | // 强制函数中的变量在一起声明 267 | "one-var": [2, { "initialized": "never" }], 268 | // 不要求或禁止尽可能地简化赋值操作 269 | "operator-assignment": 0, 270 | // 强制操作符使用一致的换行符 271 | "operator-linebreak": [2, "after", { "overrides": { "?": "before", ":": "before" } }], 272 | // 不要求或禁止块内填充 273 | "padded-blocks": 0, 274 | // 建议使用const 275 | "prefer-const": 2, 276 | // 不要求对象字面量属性名称使用引号 277 | "quote-props": 0, 278 | // 引号 279 | "quotes": [2, "single", "avoid-escape"], 280 | // parseint输入10防止八进制 281 | "radix": 0, 282 | // 用分号 283 | "semi": 1, 284 | // 不强制分号之前和之后使用一致的空格 285 | "semi-spacing": 0, 286 | // 无需变量排序 287 | "sort-vars": 0, 288 | // 要求语句块之前的空格 289 | "space-before-blocks": [2, "always"], 290 | // 强制在 function的左括号之前使用一致的空格 291 | "space-before-function-paren": [2, "always"], 292 | // 强制在圆括号内使用一致的空格 293 | "space-in-parens": [2, "never"], 294 | // 要求操作符周围有空格 295 | "space-infix-ops": 2, 296 | // 强制在一元操作符前后使用一致的空格 297 | "space-unary-ops": [2, { "words": true, "nonwords": false }], 298 | // 强制在注释中 // 或 /* 使用一致的空格 299 | "spaced-comment": [2, "always", { "markers": ["global", "globals", "eslint", "eslint-disable", "*package", "!"] }], 300 | // 不要求严格模式 301 | "strict": 0, 302 | // 要求使用 isNaN() 检查 NaN 303 | "use-isnan": 2, 304 | // 不强制使用有效的 JSDoc 注释 305 | "valid-jsdoc": 0, 306 | // 强制 typeof 表达式与有效的字符串进行比较 307 | "valid-typeof": 2, 308 | // 不要求所有的 var 声明出现在它们所在的作用域顶部 309 | "vars-on-top": 0, 310 | // 要求 IIFE 使用括号括起来 311 | "wrap-iife": [2, "any"], 312 | // 不要求正则表达式被括号括起来 313 | "wrap-regex": 0, 314 | // 比较时,变量值必须放在首位 315 | "yoda": [2, "never"] 316 | } 317 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ### OSX ### 2 | *.DS_Store 3 | .AppleDouble 4 | .LSOverride 5 | 6 | # Icon must end with two 7 | Icon 8 | # Thumbnails 9 | ._* 10 | # Files that might appear in the root of a volume 11 | .DocumentRevisions-V100 12 | .fseventsd 13 | .Spotlight-V100 14 | .TemporaryItems 15 | .Trashes 16 | .VolumeIcon.icns 17 | .com.apple.timemachine.donotpresent 18 | # Directories potentially created on remote AFP share 19 | .AppleDB 20 | .AppleDesktop 21 | Network Trash Folder 22 | Temporary Items 23 | .apdisk 24 | 25 | ### Node ### 26 | # Logs 27 | logs 28 | *.log 29 | npm-debug.log* 30 | 31 | # Runtime data 32 | pids 33 | *.pid 34 | *.seed 35 | *.pid.lock 36 | 37 | # Directory for instrumented libs generated by jscoverage/JSCover 38 | lib-cov 39 | 40 | # Coverage directory used by tools like istanbul 41 | coverage 42 | 43 | # nyc test coverage 44 | .nyc_output 45 | 46 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 47 | .grunt 48 | 49 | # node-waf configuration 50 | .lock-wscript 51 | 52 | # Compiled binary addons (http://nodejs.org/api/addons.html) 53 | build/Release 54 | 55 | # Dependency directories 56 | node_modules 57 | jspm_packages 58 | 59 | # Optional npm cache directory 60 | .npm 61 | 62 | # Optional eslint cache 63 | .eslintcache 64 | 65 | # Optional REPL history 66 | .node_repl_history 67 | 68 | # Output of 'npm pack' 69 | *.tgz 70 | 71 | # Yarn Integrity file 72 | .yarn-integrity 73 | 74 | # Nuxt build 75 | .nuxt 76 | 77 | # Nuxt generate 78 | dist -------------------------------------------------------------------------------- /.stylelintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "stylelint-config-standard", 3 | "rules": { 4 | "selector-type-no-unknown": [true, { 5 | "ignore": ["custom-elements", "default-namespace"] 6 | }] 7 | } 8 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # chimee-plugin-controlbar 2 | 3 | ## install 4 | 5 | 安装 6 | 7 | ```shell 8 | # 依赖于 chimee, 首先需要安装 chimee 9 | npm install chimee 10 | # 安装控制条组件 11 | npm install chimee-plugin-controlbar 12 | ``` 13 | 14 | 使用 15 | 16 | ```javascript 17 | import chimee from 'chimee'; 18 | import chimeePluginControlbar from 'chimee-plugin-controlbar'; 19 | 20 | // 安装插件 21 | chimee.install(chimeePluginControlbar); 22 | const player = new chimee({ 23 | // ... 24 | // 使用插件 25 | plugin: [ 26 | chimeePluginControlbar.name // 或者 'chimeeControl' 27 | ] 28 | }); 29 | ``` 30 | 31 | **也可以在页面中引用 `/lib/index.browser.js` 然后在页面中使用 chimeePluginControlbar** 32 | 33 | ## 配置 34 | 35 | 一个配置 🌰 更详细的配置例子, 可以[参考 /demo/index.html](https://github.com/Chimeejs/chimee-plugin-controlbar/blob/master/demo/index.html) 36 | 37 | ```javascript 38 | plugin: [{ 39 | name: chimeePluginControlbar.name, 40 | majorColor: '', 41 | hoverColor: '', 42 | children: { 43 | volume: { 44 | icon: { 45 | low: '', 46 | high: '' 47 | }, 48 | layout: 'vertical' 49 | } 50 | } 51 | }] 52 | ``` 53 | 54 | ### 具体的参数配置 55 | 56 | #### name 57 | * 类型: string 58 | * 含义: 该插件名字, 在 chimee 中使用需要名字,需要唯一对应 59 | * 值: 'chimeeControl' | chimeePluginControlbar.name 60 | * 必需 61 | 62 | #### majorColor 63 | * 类型: string 64 | * 作用范围: 65 | * 该插件中,所有的 svg 图 66 | * 播放进度条,进度颜色 67 | * 声音控制条,音量颜色 68 | * 可选值: 十六进制颜色('#fff') 69 | * 默认值: '#de698c' 70 | * 非必需 71 | 72 | #### hoverColor 73 | * 类型: string 74 | * 作用范围: 75 | * 该插件中,所有的 svg 图 76 | * 可选值: 十六进制颜色('#fff') 77 | * 默认值: '#4c4c4c' 78 | * 非必需 79 | #### barShowByMouse 80 | * 类型: string 81 | * 作用:控制条显示由 82 | * move 触发 播放器的 mousemove 显示 83 | * enter/levae 鼠标进入/出,来控制 控制条显示/隐藏 84 | * 可选值: 'move', 'enter' 85 | * 默认值: 'move' 86 | * 非必需 87 | #### hideBarTime 88 | * 类型: number 89 | * 作用:hidebar 延迟时间 90 | * 默认值: 2000 91 | * 注意:barShowByMouse 为 move 时有效,enter 时为0, 用户设置无效 92 | * 非必需 93 | #### children 94 | * 类型: Object 95 | * 含义: 配置子组件是否展示/展示方式,还可以自己扩展子组件 96 | * 非必需 97 | * 目前支持的组件: play, progressTime, progressBar, volume, screen, clarity, playbackrate 98 | 99 | ##### 目前支持的组件及配置 100 | 101 | * play 102 | * 类型: Object 103 | * 含义: 配置播放暂停键 icon 及事件 104 | * 默认: {} 105 | * 可配置属性: 106 | * 生命周期: 107 | * create: 插入 dom 节点, 完成事件注册 108 | * destroy 109 | * bitmap: true/ false 是否是位图,默认 false, 如果用户采用位图的话,则把当前的默认 svg 都清空掉, 用户通过 css background 来自己设置图片 110 | * icon: play / pause 图标, 支持 svg 图 111 | * animate: 当前是一个 svg path 动画,可以传 path 来实现你想要的动画 112 | * event: 绑定 dom 事件, this 指向这个插件, 通过 this.$dom 可以拿到 dom 节点 113 | * 注意: icon animate bitmap 都是配置图的。 bitmap 优先。其次 icon ,最后取 animate 中的值 114 | 115 | 配置 🌰 116 | 117 | ```javascirpt 118 | { 119 | // 可以传两个 icon 来切换播放暂停状态 120 | icon: { 121 | play: '', 122 | pause: '' 123 | }, 124 | // 当前是一个 svg path 动画,可以传 path 来实现你想要的动画 125 | animate: { 126 | path: { 127 | play: { 128 | left: '' 129 | }, 130 | pause: { 131 | left: '' 132 | } 133 | } 134 | }, 135 | // 可以指定 event 来绑定一些事件,默认 this 是该插件,而不是 dom 136 | event: { 137 | click () { 138 | console.log(''); 139 | } 140 | } 141 | } 142 | ``` 143 | 144 | * progressTime 145 | * 类型: Object 146 | * 含义: 时间展示组件,用来展示播放时间/开播时间/视频总时长 147 | * 默认: {} 148 | * 可配置属性: 149 | * 生命周期: 150 | * create: 插入 dom 节点, 完成事件注册 151 | * destroy 152 | * event: 绑定 dom 事件, this 指向这个插件, 通过 this.$dom 可以拿到 dom 节点 153 | 154 | 配置 🌰 155 | 156 | ```javascirpt 157 | { 158 | // 可以指定 event 来绑定一些事件,默认 this 是该插件,而不是 dom 159 | event: { 160 | click () { 161 | console.log(''); 162 | } 163 | } 164 | } 165 | ``` 166 | 167 | * progressBar 168 | * 类型: Object | false 169 | * 含义: 进度条控制组件 170 |    * 默认: {}, 属性值为 false 的时候,表示,他是一个占位符,不现实,可以区分左右,目前只有 progressbar 有这个功能 171 |    * 可配置属性: 172 | * 生命周期: 173 | * create: 插入 dom 节点, 完成事件注册 174 | * destroy 175 | * layout: 有两种位置, 一是,居中布局。二是,位于整个控制条顶部。 176 | * 可选值: 'top' / 'baseline'(默认) 177 | * event: 绑定 dom 事件, this 指向这个插件, 通过 this.$dom 可以拿到 dom 节点 178 | 179 | 配置 🌰 180 | 181 | ```javascirpt 182 | { 183 | layout: 'top', 184 | 185 | // 可以指定 event 来绑定一些事件,默认 this 是该插件,而不是 dom 186 | event: { 187 | click () { 188 | console.log(''); 189 | } 190 | } 191 | } 192 | ``` 193 | 194 | * volume 195 | * 类型: Object 196 | * 含义: 声音控制组件 197 | * 默认: {} 198 | * 可配置属性: 199 | * 生命周期: 200 | * create: 插入 dom 节点, 完成事件注册 201 | * destroy 202 | * layout: 有两种位置, 一是,垂直。二是,水平。 203 | * 可选值: 'vertical' / 'horizonal'(默认) 204 | * bitmap: true/ false 是否是位图,默认 false,如果用户采用位图的话,则把当前的默认 svg 都清空掉, 用户通过 css background 来自己设置图片 205 | * icon: 音量按钮的三个状态按钮,mute / low / high 最少写前两个 206 | * [暂时不支持]animate: 也可以配置,然后自己通过 css 来控制 207 | * event: 绑定 dom 事件, this 指向这个插件, 通过 this.$dom 可以拿到 dom 节点 208 | * 注意: icon bitmap 都是配置图的。 bitmap 优先。其次 icon 209 | 210 | 配置 🌰 211 | 212 | ```javascirpt 213 | volume: { 214 | icon: { 215 | low: ``, 216 | mute: ``, 217 | high: `` 218 | }, 219 | layout: 'vertical', 220 | 221 | // 可以指定 event 来绑定一些事件,默认 this 是该插件,而不是 dom 222 | event: { 223 | click () { 224 | console.log(''); 225 | } 226 | } 227 | }, 228 | ``` 229 | 230 | * screen 231 | * 类型: Object 232 | * 含义: 配置全屏/非全屏 icon 及事件 233 | * 默认: {} 234 | * 可配置属性: 235 | * 生命周期: 236 | * create: 插入 dom 节点, 完成事件注册 237 | * destroy 238 | * bitmap: true/ false 是否是位图,默认 false,如果用户采用位图的话,则把当前的默认 svg 都清空掉, 用户通过 css background 来自己设置图片 239 | * icon: full / small 图标, 支持 svg 图 240 | * event: 绑定 dom 事件, this 指向这个插件, 通过 this.$dom 可以拿到 dom 节点 241 | * 注意: icon bitmap 都是配置图的。 bitmap 优先。其次 icon 242 | 243 | 配置 🌰 244 | 245 | ```javascirpt 246 | { 247 | // 可以传两个 icon 来切换播放暂停状态 248 | icon: { 249 | full: '', 250 | small: '' 251 | }, 252 | 253 | // 可以指定 event 来绑定一些事件,默认 this 是该插件,而不是 dom 254 | event: { 255 | click () { 256 | console.log(''); 257 | } 258 | } 259 | } 260 | ``` 261 | 262 | * clarity 263 | * 类型: Object 264 | * 含义: 切换清晰度组件 265 | * 默认: {} 266 | * 可配置参数 267 | * 生命周期: 268 | * create: 插入 dom 节点, 完成事件注册 269 | * destroy 270 | * list: [] 271 | * duration 272 | * 类型:`number` 273 | * 默认:3 274 | * 单次视频加载的时长 275 | * 若在规定的时间段内加载不成功,则放弃此次任务。 276 | * 单位为秒,对应于主视频的播放时间,也就是说若主视频暂停播放,则时间停滞,但加载仍继续。 277 | * bias 278 | * 类型:`number` 279 | * 默认:0 280 | * 偏差区间,单位为秒 281 | * 若该值小于等于0,则在主视频播放到或超过约定时间点直接切换,若新视频加载失败,则放弃此次切换。 282 | * 若该值大于0,则当主视频播放到约定时间偏差区间里,一旦视频加载成功就切换。若超出偏差空间,则放弃此次切换。 283 | * repeatTimes 284 | * 类型:`number` 285 | * 默认:0 286 | * 重复次数 287 | * 若加载视频失败,则自动重新加载,直至重复次数耗尽。默认不重复加载。 288 | * increment 289 | * 类型:`number` 290 | * 默认:0 291 | * 每次重复时递增的时间,单位为秒 292 | * 一般而言加载失败都是因为超时加载失败,故每次重复的时候应相应延长加载时间。每次重复加载都会相应叠加该值对应的时间。 293 | * immediate 294 | * 类型:`boolean` 295 | * 默认:`false` 296 | * 新视频加载成功后是否立即切换无需等待到约定时间。 297 | * 注意空数组时不展示 298 | 299 | 配置 🌰 300 | 301 | ```javascirpt 302 | { 303 | list: [ 304 | {name: '标清', src:''}, 305 | {name: '高清', src: ''}, 306 | {name: '原画', src: ''} 307 | ], 308 | // 可以指定 event 来绑定一些事件,默认 this 是该插件,而不是 dom 309 | event: { 310 | click () { 311 | console.log(''); 312 | } 313 | } 314 | } 315 | ``` 316 | 317 | 直播切流测试: http://chimee.org/demo/live-clarity.html 318 | 319 | 点播切流测试: http://chimee.org/demo/clarity.html 320 | 321 | * playbackrate 322 | * 类型: Object 323 | * 含义: 切换播放倍速组件 324 | * 默认: {} 325 | * 可配置参数 326 | * 生命周期: 327 | * create: 插入 dom 节点, 完成事件注册 328 | * destroy 329 | * list: [] 330 | * defualt: 默认播放速率 boolean值 331 | * 注意空数组时不展示 332 | 333 | 配置 🌰 334 | 335 | ```javascirpt 336 | { 337 | // default 通过设置 default 来标明当前播放速率 338 | list: [ 339 | {name: '0.5倍速', value: 0.5}, 340 | {name: '1倍速', value: 1, default: true}, 341 | {name: '2倍速', value: 2} 342 | ], 343 | // 可以指定 event 来绑定一些事件,默认 this 是该插件,而不是 dom 344 | event: { 345 | click () { 346 | console.log(''); 347 | } 348 | } 349 | } 350 | ``` 351 | 352 | * 自定义组件 353 | * 类型: Object 354 | * 含义: 自定义组件 355 | * 可配置属性: 356 | * 生命周期: 357 | * create: 插入 dom 节点, 完成事件注册 358 | * destroy 359 | * tag: 自定义标签名 360 | * html: 自定义标签中的 html 内容 361 | * event: 绑定 dom 事件, this 指向这个插件, 通过 this.$dom 可以拿到 dom 节点 362 | * 注意: css 写在自己项目中就好了 363 | 364 | 配置 🌰 365 | 366 | ```javascirpt 367 | { 368 | tag: '', 369 | html: ``, 370 | // 可以指定 event 来绑定一些事件,默认 this 是该插件,而不是 dom 371 | event: { 372 | click () { 373 | console.log(''); 374 | } 375 | } 376 | } 377 | ``` 378 | 379 | ##### 组件相关问题 380 | 381 | * Q: 子组件的默认顺序是什么? 382 | 383 | A: 在 children 没有配置的情况下会采用下面的顺序 384 | 385 |    * 注意:根据 chimee 的参数 isLive 来判断是否是直播还是点播 386 | 387 |    * 直播: play, progressTime, volume, screen 388 | 389 |    * 点播: play, progressTime, progressTime, volume, screen 390 | 391 |    * 如果用户配置了, 则按照用户的配置走,不论是否是直播还是点播 392 | 393 | * Q: 我可以控制顺序吗? 394 | 395 | A: 在 children 对象中,属性的书写顺序就是渲染顺序 396 | 397 |    * 注意, progressbar 可以作为一个占位符存在 398 | 399 | * Q: 为什么我配置了一个组件后,其他默认组件就都不存在了? 400 | 401 | A: 假如 children 配置后, 会读 children 的属性,并渲染, 不会补充其他组件,所以,需要你把所有的组件都写. 402 | 403 | ## 方法 404 | 405 | * updateClarity 406 | * 含义: 更新清晰度列表 407 | * 参数:Array 清晰度列表 408 | 409 | 配置 🌰 410 | 411 | ```javascirpt 412 | function updateClarity() { 413 | player.load('http://yunxianchang.live.ujne7.com/vod-system-bj/103_368b70a5d4f-c5cc-42ff-b442-004168fc86a3.mp4'); 414 | player.on('load', function () { // 在 load 事件后 415 | player.$plugins.chimeeControl.updateClarity([ 416 | {name: '标清', src: 'http://yunxianchang.live.ujne7.com/vod-system-bj/103_368b70a5d4f-c5cc-42ff-b442-004168fc86a3.mp4'}, 417 | {name: '高清', src: 'http://yunxianchang.live.ujne7.com/vod-system-bj/103_369ed890f51-1c38-42a7-9ce2-828492660c60.mp4'}, 418 | {name: '超清', src: 'http://yunxianchang.live.ujne7.com/vod-system-bj/103_370cc2f40bd-a39f-472a-884f-f44fcd9c5ae0.mp4'}, 419 | {name: '原画', src: 'http://yunxianchang.live.ujne7.com/vod-system-bj/103_371ab0c0fda-143d-4755-8727-d3cd12dce02d.mp4'} 420 | ]); 421 | }); 422 | } 423 | ``` 424 | 425 | 426 | ## 最后 427 | 428 | 欢迎各位大佬使用。有什么问题/建议,随时提。 429 | -------------------------------------------------------------------------------- /__test__/e2e/index.js: -------------------------------------------------------------------------------- 1 | import Chimee from 'chimee'; 2 | import chimeePluginControlbar from '../../src/index'; 3 | 4 | document.body.innerHTML = ` 5 |
6 | `; 7 | Chimee.install(chimeePluginControlbar); 8 | 9 | const player = new Chimee({ 10 | wrapper: '#wrap', 11 | src: 'http://yunxianchang.live.ujne7.com/vod-system-bj/103_371ab0c0fda-143d-4755-8727-d3cd12dce02d.mp4', 12 | isLive: false, 13 | majorColor: '#f00', 14 | hoverColor: '#00f', 15 | plugin: [ 16 | { 17 | name: chimeePluginControlbar.name, 18 | majorColor: '#f00', 19 | hoverColor: '#fff', 20 | children: { 21 | play: {}, 22 | progressTime: true, 23 | progressBar: { 24 | layout: 'top' // baseline 25 | }, 26 | volume: { 27 | icon: { 28 | low: ` 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | `, 40 | mute: ` 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | `, 53 | }, 54 | layout: 'vertical' 55 | }, 56 | clarity: [ 57 | {name: '标清', src: 'http://yunxianchang.live.ujne7.com/vod-system-bj/103_368b70a5d4f-c5cc-42ff-b442-004168fc86a3.mp4'}, 58 | {name: '高清', src: 'http://yunxianchang.live.ujne7.com/vod-system-bj/103_369ed890f51-1c38-42a7-9ce2-828492660c60.mp4'}, 59 | {name: '原画', src: 'http://yunxianchang.live.ujne7.com/vod-system-bj/103_371ab0c0fda-143d-4755-8727-d3cd12dce02d.mp4'} 60 | ], 61 | screen: true 62 | } 63 | } 64 | ], 65 | controls: true, 66 | autoplay: true 67 | }); 68 | 69 | const controls = player.$plugins.chimeeControl; 70 | 71 | 72 | describe('controls inited', async () => { 73 | expect(controls).toBeDefined(); 74 | }); 75 | 76 | describe('screen inited', async () => { 77 | const screen = await controls.children.screen; 78 | expect(screen).toBeDefined(); 79 | }); 80 | -------------------------------------------------------------------------------- /__test__/index.js: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chimeejs/chimee-plugin-controlbar/122787e29c3f905a101c7bb722937a81abc4b600/__test__/index.js -------------------------------------------------------------------------------- /__test__/play.js: -------------------------------------------------------------------------------- 1 | import Play from '../src/play'; 2 | 3 | describe('init', function () { 4 | 5 | }); 6 | -------------------------------------------------------------------------------- /build/rollup.config.base.js: -------------------------------------------------------------------------------- 1 | const {version, name, author, license, dependencies} = require('../package.json'); 2 | const banner = ` 3 | /** 4 | * ${name} v${version} 5 | * (c) 2017 ${author} 6 | * Released under ${license} 7 | */ 8 | `; 9 | import babel from 'rollup-plugin-babel'; 10 | import resolve from 'rollup-plugin-node-resolve'; 11 | import commonjs from 'rollup-plugin-commonjs'; 12 | import postcss from 'rollup-plugin-postcss'; 13 | import uglify from 'rollup-plugin-uglify'; 14 | 15 | // PostCSS plugins 16 | import cssnano from 'cssnano'; 17 | import cssnext from 'postcss-cssnext'; 18 | import postcssReporter from 'postcss-reporter'; 19 | import stylelint from 'stylelint'; 20 | 21 | const babelConfig = { 22 | cjs: { 23 | presets: [ 24 | ['latest', {es2015: {modules: false}}], 25 | 'stage-0' 26 | ], 27 | plugins: [ 28 | 'transform-decorators-legacy', 29 | 'transform-runtime' 30 | ], 31 | exclude: 'node_modules/**', 32 | runtimeHelpers: true, 33 | babelrc: false 34 | }, 35 | es: { 36 | presets: [ 37 | ['latest', {es2015: {modules: false}}], 38 | 'stage-0' 39 | ], 40 | plugins: [ 41 | 'transform-decorators-legacy', 42 | 'transform-runtime' 43 | ], 44 | exclude: 'node_modules/**', 45 | runtimeHelpers: true, 46 | babelrc: false 47 | }, 48 | umd: { 49 | presets: ['es2015-rollup', 'stage-0'], 50 | plugins: [ 51 | 'transform-decorators-legacy', 52 | 'transform-runtime' 53 | ], 54 | exclude: 'node_modules/**', 55 | runtimeHelpers: true, 56 | babelrc: false 57 | }, 58 | iife: { 59 | presets: ['es2015-rollup', 'stage-0'], 60 | plugins: ['transform-decorators-legacy'], 61 | babelrc: false 62 | }, 63 | min: { 64 | presets: ['es2015-rollup', 'stage-0'], 65 | exclude: 'node_modules/**', 66 | plugins: [], 67 | babelrc: false 68 | } 69 | }; 70 | const externalRegExp = new RegExp(Object.keys(dependencies).join('|')); 71 | export default function (modeConf) { 72 | const mode = modeConf.output.format; 73 | const config = { 74 | input: 'src/index.js', 75 | output: { 76 | banner 77 | }, 78 | external (id) { 79 | return !/min|umd|iife/.test(mode) && externalRegExp.test(id); 80 | }, 81 | plugins: [ 82 | postcss({ 83 | plugins: [ 84 | stylelint(), 85 | postcssReporter(), 86 | cssnext(), 87 | cssnano() 88 | ], 89 | extensions: ['.css'] 90 | }), 91 | babel(babelConfig[mode]), 92 | resolve(), 93 | commonjs() 94 | ] 95 | }; 96 | modeConf.uglify && config.plugins.push(uglify()); 97 | delete modeConf.uglify; 98 | Object.assign(config, modeConf); 99 | if(mode === 'umd') { 100 | config.output.name = camelize(name); 101 | } 102 | return config; 103 | }; 104 | 105 | /** 106 | * camelize any string, e.g hello world -> helloWorld 107 | * @param {string} str only accept string! 108 | * @return {string} camelize string 109 | */ 110 | function camelize (str) { 111 | return str.replace(/(^|[^a-zA-Z]+)([a-zA-Z])/g, function (match, spilt, initials, index) { 112 | return (index === 0) ? initials.toLowerCase() : initials.toUpperCase(); 113 | }); 114 | } 115 | -------------------------------------------------------------------------------- /build/rollup.config.common.js: -------------------------------------------------------------------------------- 1 | import base from './rollup.config.base'; 2 | export default base({ 3 | output: { 4 | format: 'cjs', 5 | file: 'lib/index.js' 6 | } 7 | }); 8 | -------------------------------------------------------------------------------- /build/rollup.config.es.js: -------------------------------------------------------------------------------- 1 | import base from './rollup.config.base'; 2 | export default base({ 3 | output: { 4 | format: 'es', 5 | file: 'lib/index.mjs' 6 | } 7 | }); 8 | -------------------------------------------------------------------------------- /build/rollup.config.min.js: -------------------------------------------------------------------------------- 1 | import base from './rollup.config.base'; 2 | import replace from 'rollup-plugin-replace'; 3 | const config = base({ 4 | output: { 5 | format: 'umd', 6 | file: 'lib/index.min.js' 7 | }, 8 | uglify: true 9 | }); 10 | config.plugins.unshift(replace({ 11 | 'process.env.NODE_ENV': '"development"' 12 | })); 13 | 14 | export default config; 15 | 16 | -------------------------------------------------------------------------------- /build/rollup.config.umd.js: -------------------------------------------------------------------------------- 1 | import base from './rollup.config.base'; 2 | import replace from 'rollup-plugin-replace'; 3 | const config = base({ 4 | output: { 5 | format: 'umd', 6 | file: 'lib/index.browser.js' 7 | } 8 | }); 9 | config.plugins.unshift(replace({ 10 | 'process.env.NODE_ENV': '"development"' 11 | })); 12 | export default config; 13 | -------------------------------------------------------------------------------- /coverage/chrome/base.css: -------------------------------------------------------------------------------- 1 | body, html { 2 | margin:0; padding: 0; 3 | height: 100%; 4 | } 5 | body { 6 | font-family: Helvetica Neue, Helvetica, Arial; 7 | font-size: 14px; 8 | color:#333; 9 | } 10 | .small { font-size: 12px; } 11 | *, *:after, *:before { 12 | -webkit-box-sizing:border-box; 13 | -moz-box-sizing:border-box; 14 | box-sizing:border-box; 15 | } 16 | h1 { font-size: 20px; margin: 0;} 17 | h2 { font-size: 14px; } 18 | pre { 19 | font: 12px/1.4 Consolas, "Liberation Mono", Menlo, Courier, monospace; 20 | margin: 0; 21 | padding: 0; 22 | -moz-tab-size: 2; 23 | -o-tab-size: 2; 24 | tab-size: 2; 25 | } 26 | a { color:#0074D9; text-decoration:none; } 27 | a:hover { text-decoration:underline; } 28 | .strong { font-weight: bold; } 29 | .space-top1 { padding: 10px 0 0 0; } 30 | .pad2y { padding: 20px 0; } 31 | .pad1y { padding: 10px 0; } 32 | .pad2x { padding: 0 20px; } 33 | .pad2 { padding: 20px; } 34 | .pad1 { padding: 10px; } 35 | .space-left2 { padding-left:55px; } 36 | .space-right2 { padding-right:20px; } 37 | .center { text-align:center; } 38 | .clearfix { display:block; } 39 | .clearfix:after { 40 | content:''; 41 | display:block; 42 | height:0; 43 | clear:both; 44 | visibility:hidden; 45 | } 46 | .fl { float: left; } 47 | @media only screen and (max-width:640px) { 48 | .col3 { width:100%; max-width:100%; } 49 | .hide-mobile { display:none!important; } 50 | } 51 | 52 | .quiet { 53 | color: #7f7f7f; 54 | color: rgba(0,0,0,0.5); 55 | } 56 | .quiet a { opacity: 0.7; } 57 | 58 | .fraction { 59 | font-family: Consolas, 'Liberation Mono', Menlo, Courier, monospace; 60 | font-size: 10px; 61 | color: #555; 62 | background: #E8E8E8; 63 | padding: 4px 5px; 64 | border-radius: 3px; 65 | vertical-align: middle; 66 | } 67 | 68 | div.path a:link, div.path a:visited { color: #333; } 69 | table.coverage { 70 | border-collapse: collapse; 71 | margin: 10px 0 0 0; 72 | padding: 0; 73 | } 74 | 75 | table.coverage td { 76 | margin: 0; 77 | padding: 0; 78 | vertical-align: top; 79 | } 80 | table.coverage td.line-count { 81 | text-align: right; 82 | padding: 0 5px 0 20px; 83 | } 84 | table.coverage td.line-coverage { 85 | text-align: right; 86 | padding-right: 10px; 87 | min-width:20px; 88 | } 89 | 90 | table.coverage td span.cline-any { 91 | display: inline-block; 92 | padding: 0 5px; 93 | width: 100%; 94 | } 95 | .missing-if-branch { 96 | display: inline-block; 97 | margin-right: 5px; 98 | border-radius: 3px; 99 | position: relative; 100 | padding: 0 4px; 101 | background: #333; 102 | color: yellow; 103 | } 104 | 105 | .skip-if-branch { 106 | display: none; 107 | margin-right: 10px; 108 | position: relative; 109 | padding: 0 4px; 110 | background: #ccc; 111 | color: white; 112 | } 113 | .missing-if-branch .typ, .skip-if-branch .typ { 114 | color: inherit !important; 115 | } 116 | .coverage-summary { 117 | border-collapse: collapse; 118 | width: 100%; 119 | } 120 | .coverage-summary tr { border-bottom: 1px solid #bbb; } 121 | .keyline-all { border: 1px solid #ddd; } 122 | .coverage-summary td, .coverage-summary th { padding: 10px; } 123 | .coverage-summary tbody { border: 1px solid #bbb; } 124 | .coverage-summary td { border-right: 1px solid #bbb; } 125 | .coverage-summary td:last-child { border-right: none; } 126 | .coverage-summary th { 127 | text-align: left; 128 | font-weight: normal; 129 | white-space: nowrap; 130 | } 131 | .coverage-summary th.file { border-right: none !important; } 132 | .coverage-summary th.pct { } 133 | .coverage-summary th.pic, 134 | .coverage-summary th.abs, 135 | .coverage-summary td.pct, 136 | .coverage-summary td.abs { text-align: right; } 137 | .coverage-summary td.file { white-space: nowrap; } 138 | .coverage-summary td.pic { min-width: 120px !important; } 139 | .coverage-summary tfoot td { } 140 | 141 | .coverage-summary .sorter { 142 | height: 10px; 143 | width: 7px; 144 | display: inline-block; 145 | margin-left: 0.5em; 146 | background: url(sort-arrow-sprite.png) no-repeat scroll 0 0 transparent; 147 | } 148 | .coverage-summary .sorted .sorter { 149 | background-position: 0 -20px; 150 | } 151 | .coverage-summary .sorted-desc .sorter { 152 | background-position: 0 -10px; 153 | } 154 | .status-line { height: 10px; } 155 | /* dark red */ 156 | .red.solid, .status-line.low, .low .cover-fill { background:#C21F39 } 157 | .low .chart { border:1px solid #C21F39 } 158 | /* medium red */ 159 | .cstat-no, .fstat-no, .cbranch-no, .cbranch-no { background:#F6C6CE } 160 | /* light red */ 161 | .low, .cline-no { background:#FCE1E5 } 162 | /* light green */ 163 | .high, .cline-yes { background:rgb(230,245,208) } 164 | /* medium green */ 165 | .cstat-yes { background:rgb(161,215,106) } 166 | /* dark green */ 167 | .status-line.high, .high .cover-fill { background:rgb(77,146,33) } 168 | .high .chart { border:1px solid rgb(77,146,33) } 169 | /* dark yellow (gold) */ 170 | .medium .chart { border:1px solid #f9cd0b; } 171 | .status-line.medium, .medium .cover-fill { background: #f9cd0b; } 172 | /* light yellow */ 173 | .medium { background: #fff4c2; } 174 | /* light gray */ 175 | span.cline-neutral { background: #eaeaea; } 176 | 177 | .cbranch-no { background: yellow !important; color: #111; } 178 | 179 | .cstat-skip { background: #ddd; color: #111; } 180 | .fstat-skip { background: #ddd; color: #111 !important; } 181 | .cbranch-skip { background: #ddd !important; color: #111; } 182 | 183 | 184 | .cover-fill, .cover-empty { 185 | display:inline-block; 186 | height: 12px; 187 | } 188 | .chart { 189 | line-height: 0; 190 | } 191 | .cover-empty { 192 | background: white; 193 | } 194 | .cover-full { 195 | border-right: none !important; 196 | } 197 | pre.prettyprint { 198 | border: none !important; 199 | padding: 0 !important; 200 | margin: 0 !important; 201 | } 202 | .com { color: #999 !important; } 203 | .ignore-none { color: #999; font-weight: normal; } 204 | 205 | .wrapper { 206 | min-height: 100%; 207 | height: auto !important; 208 | height: 100%; 209 | margin: 0 auto -48px; 210 | } 211 | .footer, .push { 212 | height: 48px; 213 | } 214 | -------------------------------------------------------------------------------- /coverage/chrome/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for All files 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | / 20 |

21 |
22 |
23 | 34.77% 24 | Statements 25 | 7788/22400 26 |
27 |
28 | 17.65% 29 | Branches 30 | 2659/15064 31 |
32 |
33 | 24.41% 34 | Functions 35 | 1115/4568 36 |
37 |
38 | 36.51% 39 | Lines 40 | 7531/20628 41 |
42 |
43 |
44 |
45 |
46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 |
FileStatementsBranchesFunctionsLines
src/
34.77%7788/2240017.65%2659/1506424.41%1115/456836.51%7531/20628
76 |
77 |
78 | 82 | 83 | 84 | 91 | 92 | 93 | 94 | -------------------------------------------------------------------------------- /coverage/chrome/prettify.css: -------------------------------------------------------------------------------- 1 | .pln{color:#000}@media screen{.str{color:#080}.kwd{color:#008}.com{color:#800}.typ{color:#606}.lit{color:#066}.pun,.opn,.clo{color:#660}.tag{color:#008}.atn{color:#606}.atv{color:#080}.dec,.var{color:#606}.fun{color:red}}@media print,projection{.str{color:#060}.kwd{color:#006;font-weight:bold}.com{color:#600;font-style:italic}.typ{color:#404;font-weight:bold}.lit{color:#044}.pun,.opn,.clo{color:#440}.tag{color:#006;font-weight:bold}.atn{color:#404}.atv{color:#060}}pre.prettyprint{padding:2px;border:1px solid #888}ol.linenums{margin-top:0;margin-bottom:0}li.L0,li.L1,li.L2,li.L3,li.L5,li.L6,li.L7,li.L8{list-style-type:none}li.L1,li.L3,li.L5,li.L7,li.L9{background:#eee} 2 | -------------------------------------------------------------------------------- /coverage/chrome/prettify.js: -------------------------------------------------------------------------------- 1 | window.PR_SHOULD_USE_CONTINUATION=true;(function(){var h=["break,continue,do,else,for,if,return,while"];var u=[h,"auto,case,char,const,default,double,enum,extern,float,goto,int,long,register,short,signed,sizeof,static,struct,switch,typedef,union,unsigned,void,volatile"];var p=[u,"catch,class,delete,false,import,new,operator,private,protected,public,this,throw,true,try,typeof"];var l=[p,"alignof,align_union,asm,axiom,bool,concept,concept_map,const_cast,constexpr,decltype,dynamic_cast,explicit,export,friend,inline,late_check,mutable,namespace,nullptr,reinterpret_cast,static_assert,static_cast,template,typeid,typename,using,virtual,where"];var x=[p,"abstract,boolean,byte,extends,final,finally,implements,import,instanceof,null,native,package,strictfp,super,synchronized,throws,transient"];var R=[x,"as,base,by,checked,decimal,delegate,descending,dynamic,event,fixed,foreach,from,group,implicit,in,interface,internal,into,is,lock,object,out,override,orderby,params,partial,readonly,ref,sbyte,sealed,stackalloc,string,select,uint,ulong,unchecked,unsafe,ushort,var"];var r="all,and,by,catch,class,else,extends,false,finally,for,if,in,is,isnt,loop,new,no,not,null,of,off,on,or,return,super,then,true,try,unless,until,when,while,yes";var w=[p,"debugger,eval,export,function,get,null,set,undefined,var,with,Infinity,NaN"];var s="caller,delete,die,do,dump,elsif,eval,exit,foreach,for,goto,if,import,last,local,my,next,no,our,print,package,redo,require,sub,undef,unless,until,use,wantarray,while,BEGIN,END";var I=[h,"and,as,assert,class,def,del,elif,except,exec,finally,from,global,import,in,is,lambda,nonlocal,not,or,pass,print,raise,try,with,yield,False,True,None"];var f=[h,"alias,and,begin,case,class,def,defined,elsif,end,ensure,false,in,module,next,nil,not,or,redo,rescue,retry,self,super,then,true,undef,unless,until,when,yield,BEGIN,END"];var H=[h,"case,done,elif,esac,eval,fi,function,in,local,set,then,until"];var A=[l,R,w,s+I,f,H];var e=/^(DIR|FILE|vector|(de|priority_)?queue|list|stack|(const_)?iterator|(multi)?(set|map)|bitset|u?(int|float)\d*)/;var C="str";var z="kwd";var j="com";var O="typ";var G="lit";var L="pun";var F="pln";var m="tag";var E="dec";var J="src";var P="atn";var n="atv";var N="nocode";var M="(?:^^\\.?|[+-]|\\!|\\!=|\\!==|\\#|\\%|\\%=|&|&&|&&=|&=|\\(|\\*|\\*=|\\+=|\\,|\\-=|\\->|\\/|\\/=|:|::|\\;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\@|\\[|\\^|\\^=|\\^\\^|\\^\\^=|\\{|\\||\\|=|\\|\\||\\|\\|=|\\~|break|case|continue|delete|do|else|finally|instanceof|return|throw|try|typeof)\\s*";function k(Z){var ad=0;var S=false;var ac=false;for(var V=0,U=Z.length;V122)){if(!(al<65||ag>90)){af.push([Math.max(65,ag)|32,Math.min(al,90)|32])}if(!(al<97||ag>122)){af.push([Math.max(97,ag)&~32,Math.min(al,122)&~32])}}}}af.sort(function(av,au){return(av[0]-au[0])||(au[1]-av[1])});var ai=[];var ap=[NaN,NaN];for(var ar=0;arat[0]){if(at[1]+1>at[0]){an.push("-")}an.push(T(at[1]))}}an.push("]");return an.join("")}function W(al){var aj=al.source.match(new RegExp("(?:\\[(?:[^\\x5C\\x5D]|\\\\[\\s\\S])*\\]|\\\\u[A-Fa-f0-9]{4}|\\\\x[A-Fa-f0-9]{2}|\\\\[0-9]+|\\\\[^ux0-9]|\\(\\?[:!=]|[\\(\\)\\^]|[^\\x5B\\x5C\\(\\)\\^]+)","g"));var ah=aj.length;var an=[];for(var ak=0,am=0;ak=2&&ai==="["){aj[ak]=X(ag)}else{if(ai!=="\\"){aj[ak]=ag.replace(/[a-zA-Z]/g,function(ao){var ap=ao.charCodeAt(0);return"["+String.fromCharCode(ap&~32,ap|32)+"]"})}}}}return aj.join("")}var aa=[];for(var V=0,U=Z.length;V=0;){S[ac.charAt(ae)]=Y}}var af=Y[1];var aa=""+af;if(!ag.hasOwnProperty(aa)){ah.push(af);ag[aa]=null}}ah.push(/[\0-\uffff]/);V=k(ah)})();var X=T.length;var W=function(ah){var Z=ah.sourceCode,Y=ah.basePos;var ad=[Y,F];var af=0;var an=Z.match(V)||[];var aj={};for(var ae=0,aq=an.length;ae=5&&"lang-"===ap.substring(0,5);if(am&&!(ai&&typeof ai[1]==="string")){am=false;ap=J}if(!am){aj[ag]=ap}}var ab=af;af+=ag.length;if(!am){ad.push(Y+ab,ap)}else{var al=ai[1];var ak=ag.indexOf(al);var ac=ak+al.length;if(ai[2]){ac=ag.length-ai[2].length;ak=ac-al.length}var ar=ap.substring(5);B(Y+ab,ag.substring(0,ak),W,ad);B(Y+ab+ak,al,q(ar,al),ad);B(Y+ab+ac,ag.substring(ac),W,ad)}}ah.decorations=ad};return W}function i(T){var W=[],S=[];if(T.tripleQuotedStrings){W.push([C,/^(?:\'\'\'(?:[^\'\\]|\\[\s\S]|\'{1,2}(?=[^\']))*(?:\'\'\'|$)|\"\"\"(?:[^\"\\]|\\[\s\S]|\"{1,2}(?=[^\"]))*(?:\"\"\"|$)|\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$))/,null,"'\""])}else{if(T.multiLineStrings){W.push([C,/^(?:\'(?:[^\\\']|\\[\s\S])*(?:\'|$)|\"(?:[^\\\"]|\\[\s\S])*(?:\"|$)|\`(?:[^\\\`]|\\[\s\S])*(?:\`|$))/,null,"'\"`"])}else{W.push([C,/^(?:\'(?:[^\\\'\r\n]|\\.)*(?:\'|$)|\"(?:[^\\\"\r\n]|\\.)*(?:\"|$))/,null,"\"'"])}}if(T.verbatimStrings){S.push([C,/^@\"(?:[^\"]|\"\")*(?:\"|$)/,null])}var Y=T.hashComments;if(Y){if(T.cStyleComments){if(Y>1){W.push([j,/^#(?:##(?:[^#]|#(?!##))*(?:###|$)|.*)/,null,"#"])}else{W.push([j,/^#(?:(?:define|elif|else|endif|error|ifdef|include|ifndef|line|pragma|undef|warning)\b|[^\r\n]*)/,null,"#"])}S.push([C,/^<(?:(?:(?:\.\.\/)*|\/?)(?:[\w-]+(?:\/[\w-]+)+)?[\w-]+\.h|[a-z]\w*)>/,null])}else{W.push([j,/^#[^\r\n]*/,null,"#"])}}if(T.cStyleComments){S.push([j,/^\/\/[^\r\n]*/,null]);S.push([j,/^\/\*[\s\S]*?(?:\*\/|$)/,null])}if(T.regexLiterals){var X=("/(?=[^/*])(?:[^/\\x5B\\x5C]|\\x5C[\\s\\S]|\\x5B(?:[^\\x5C\\x5D]|\\x5C[\\s\\S])*(?:\\x5D|$))+/");S.push(["lang-regex",new RegExp("^"+M+"("+X+")")])}var V=T.types;if(V){S.push([O,V])}var U=(""+T.keywords).replace(/^ | $/g,"");if(U.length){S.push([z,new RegExp("^(?:"+U.replace(/[\s,]+/g,"|")+")\\b"),null])}W.push([F,/^\s+/,null," \r\n\t\xA0"]);S.push([G,/^@[a-z_$][a-z_$@0-9]*/i,null],[O,/^(?:[@_]?[A-Z]+[a-z][A-Za-z_$@0-9]*|\w+_t\b)/,null],[F,/^[a-z_$][a-z_$@0-9]*/i,null],[G,new RegExp("^(?:0x[a-f0-9]+|(?:\\d(?:_\\d+)*\\d*(?:\\.\\d*)?|\\.\\d\\+)(?:e[+\\-]?\\d+)?)[a-z]*","i"),null,"0123456789"],[F,/^\\[\s\S]?/,null],[L,/^.[^\s\w\.$@\'\"\`\/\#\\]*/,null]);return g(W,S)}var K=i({keywords:A,hashComments:true,cStyleComments:true,multiLineStrings:true,regexLiterals:true});function Q(V,ag){var U=/(?:^|\s)nocode(?:\s|$)/;var ab=/\r\n?|\n/;var ac=V.ownerDocument;var S;if(V.currentStyle){S=V.currentStyle.whiteSpace}else{if(window.getComputedStyle){S=ac.defaultView.getComputedStyle(V,null).getPropertyValue("white-space")}}var Z=S&&"pre"===S.substring(0,3);var af=ac.createElement("LI");while(V.firstChild){af.appendChild(V.firstChild)}var W=[af];function ae(al){switch(al.nodeType){case 1:if(U.test(al.className)){break}if("BR"===al.nodeName){ad(al);if(al.parentNode){al.parentNode.removeChild(al)}}else{for(var an=al.firstChild;an;an=an.nextSibling){ae(an)}}break;case 3:case 4:if(Z){var am=al.nodeValue;var aj=am.match(ab);if(aj){var ai=am.substring(0,aj.index);al.nodeValue=ai;var ah=am.substring(aj.index+aj[0].length);if(ah){var ak=al.parentNode;ak.insertBefore(ac.createTextNode(ah),al.nextSibling)}ad(al);if(!ai){al.parentNode.removeChild(al)}}}break}}function ad(ak){while(!ak.nextSibling){ak=ak.parentNode;if(!ak){return}}function ai(al,ar){var aq=ar?al.cloneNode(false):al;var ao=al.parentNode;if(ao){var ap=ai(ao,1);var an=al.nextSibling;ap.appendChild(aq);for(var am=an;am;am=an){an=am.nextSibling;ap.appendChild(am)}}return aq}var ah=ai(ak.nextSibling,0);for(var aj;(aj=ah.parentNode)&&aj.nodeType===1;){ah=aj}W.push(ah)}for(var Y=0;Y=S){ah+=2}if(V>=ap){Z+=2}}}var t={};function c(U,V){for(var S=V.length;--S>=0;){var T=V[S];if(!t.hasOwnProperty(T)){t[T]=U}else{if(window.console){console.warn("cannot override language handler %s",T)}}}}function q(T,S){if(!(T&&t.hasOwnProperty(T))){T=/^\s*]*(?:>|$)/],[j,/^<\!--[\s\S]*?(?:-\->|$)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],[L,/^(?:<[%?]|[%?]>)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i]]),["default-markup","htm","html","mxml","xhtml","xml","xsl"]);c(g([[F,/^[\s]+/,null," \t\r\n"],[n,/^(?:\"[^\"]*\"?|\'[^\']*\'?)/,null,"\"'"]],[[m,/^^<\/?[a-z](?:[\w.:-]*\w)?|\/?>$/i],[P,/^(?!style[\s=]|on)[a-z](?:[\w:-]*\w)?/i],["lang-uq.val",/^=\s*([^>\'\"\s]*(?:[^>\'\"\s\/]|\/(?=\s)))/],[L,/^[=<>\/]+/],["lang-js",/^on\w+\s*=\s*\"([^\"]+)\"/i],["lang-js",/^on\w+\s*=\s*\'([^\']+)\'/i],["lang-js",/^on\w+\s*=\s*([^\"\'>\s]+)/i],["lang-css",/^style\s*=\s*\"([^\"]+)\"/i],["lang-css",/^style\s*=\s*\'([^\']+)\'/i],["lang-css",/^style\s*=\s*([^\"\'>\s]+)/i]]),["in.tag"]);c(g([],[[n,/^[\s\S]+/]]),["uq.val"]);c(i({keywords:l,hashComments:true,cStyleComments:true,types:e}),["c","cc","cpp","cxx","cyc","m"]);c(i({keywords:"null,true,false"}),["json"]);c(i({keywords:R,hashComments:true,cStyleComments:true,verbatimStrings:true,types:e}),["cs"]);c(i({keywords:x,cStyleComments:true}),["java"]);c(i({keywords:H,hashComments:true,multiLineStrings:true}),["bsh","csh","sh"]);c(i({keywords:I,hashComments:true,multiLineStrings:true,tripleQuotedStrings:true}),["cv","py"]);c(i({keywords:s,hashComments:true,multiLineStrings:true,regexLiterals:true}),["perl","pl","pm"]);c(i({keywords:f,hashComments:true,multiLineStrings:true,regexLiterals:true}),["rb"]);c(i({keywords:w,cStyleComments:true,regexLiterals:true}),["js"]);c(i({keywords:r,hashComments:3,cStyleComments:true,multilineStrings:true,tripleQuotedStrings:true,regexLiterals:true}),["coffee"]);c(g([],[[C,/^[\s\S]+/]]),["regex"]);function d(V){var U=V.langExtension;try{var S=a(V.sourceNode);var T=S.sourceCode;V.sourceCode=T;V.spans=S.spans;V.basePos=0;q(U,T)(V);D(V)}catch(W){if("console" in window){console.log(W&&W.stack?W.stack:W)}}}function y(W,V,U){var S=document.createElement("PRE");S.innerHTML=W;if(U){Q(S,U)}var T={langExtension:V,numberLines:U,sourceNode:S};d(T);return S.innerHTML}function b(ad){function Y(af){return document.getElementsByTagName(af)}var ac=[Y("pre"),Y("code"),Y("xmp")];var T=[];for(var aa=0;aa=0){var ah=ai.match(ab);var am;if(!ah&&(am=o(aj))&&"CODE"===am.tagName){ah=am.className.match(ab)}if(ah){ah=ah[1]}var al=false;for(var ak=aj.parentNode;ak;ak=ak.parentNode){if((ak.tagName==="pre"||ak.tagName==="code"||ak.tagName==="xmp")&&ak.className&&ak.className.indexOf("prettyprint")>=0){al=true;break}}if(!al){var af=aj.className.match(/\blinenums\b(?::(\d+))?/);af=af?af[1]&&af[1].length?+af[1]:true:false;if(af){Q(aj,af)}S={langExtension:ah,sourceNode:aj,numberLines:af};d(S)}}}if(X]*(?:>|$)/],[PR.PR_COMMENT,/^<\!--[\s\S]*?(?:-\->|$)/],[PR.PR_PUNCTUATION,/^(?:<[%?]|[%?]>)/],["lang-",/^<\?([\s\S]+?)(?:\?>|$)/],["lang-",/^<%([\s\S]+?)(?:%>|$)/],["lang-",/^]*>([\s\S]+?)<\/xmp\b[^>]*>/i],["lang-handlebars",/^]*type\s*=\s*['"]?text\/x-handlebars-template['"]?\b[^>]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-js",/^]*>([\s\S]*?)(<\/script\b[^>]*>)/i],["lang-css",/^]*>([\s\S]*?)(<\/style\b[^>]*>)/i],["lang-in.tag",/^(<\/?[a-z][^<>]*>)/i],[PR.PR_DECLARATION,/^{{[#^>/]?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{&?\s*[\w.][^}]*}}/],[PR.PR_DECLARATION,/^{{{>?\s*[\w.][^}]*}}}/],[PR.PR_COMMENT,/^{{![^}]*}}/]]),["handlebars","hbs"]);PR.registerLangHandler(PR.createSimpleLexer([[PR.PR_PLAIN,/^[ \t\r\n\f]+/,null," \t\r\n\f"]],[[PR.PR_STRING,/^\"(?:[^\n\r\f\\\"]|\\(?:\r\n?|\n|\f)|\\[\s\S])*\"/,null],[PR.PR_STRING,/^\'(?:[^\n\r\f\\\']|\\(?:\r\n?|\n|\f)|\\[\s\S])*\'/,null],["lang-css-str",/^url\(([^\)\"\']*)\)/i],[PR.PR_KEYWORD,/^(?:url|rgb|\!important|@import|@page|@media|@charset|inherit)(?=[^\-\w]|$)/i,null],["lang-css-kw",/^(-?(?:[_a-z]|(?:\\[0-9a-f]+ ?))(?:[_a-z0-9\-]|\\(?:\\[0-9a-f]+ ?))*)\s*:/i],[PR.PR_COMMENT,/^\/\*[^*]*\*+(?:[^\/*][^*]*\*+)*\//],[PR.PR_COMMENT,/^(?:)/],[PR.PR_LITERAL,/^(?:\d+|\d*\.\d+)(?:%|[a-z]+)?/i],[PR.PR_LITERAL,/^#(?:[0-9a-f]{3}){1,2}/i],[PR.PR_PLAIN,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i],[PR.PR_PUNCTUATION,/^[^\s\w\'\"]+/]]),["css"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_KEYWORD,/^-?(?:[_a-z]|(?:\\[\da-f]+ ?))(?:[_a-z\d\-]|\\(?:\\[\da-f]+ ?))*/i]]),["css-kw"]);PR.registerLangHandler(PR.createSimpleLexer([],[[PR.PR_STRING,/^[^\)\"\']+/]]),["css-str"]); 2 | -------------------------------------------------------------------------------- /coverage/chrome/sort-arrow-sprite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chimeejs/chimee-plugin-controlbar/122787e29c3f905a101c7bb722937a81abc4b600/coverage/chrome/sort-arrow-sprite.png -------------------------------------------------------------------------------- /coverage/chrome/sorter.js: -------------------------------------------------------------------------------- 1 | var addSorting = (function () { 2 | "use strict"; 3 | var cols, 4 | currentSort = { 5 | index: 0, 6 | desc: false 7 | }; 8 | 9 | // returns the summary table element 10 | function getTable() { return document.querySelector('.coverage-summary'); } 11 | // returns the thead element of the summary table 12 | function getTableHeader() { return getTable().querySelector('thead tr'); } 13 | // returns the tbody element of the summary table 14 | function getTableBody() { return getTable().querySelector('tbody'); } 15 | // returns the th element for nth column 16 | function getNthColumn(n) { return getTableHeader().querySelectorAll('th')[n]; } 17 | 18 | // loads all columns 19 | function loadColumns() { 20 | var colNodes = getTableHeader().querySelectorAll('th'), 21 | colNode, 22 | cols = [], 23 | col, 24 | i; 25 | 26 | for (i = 0; i < colNodes.length; i += 1) { 27 | colNode = colNodes[i]; 28 | col = { 29 | key: colNode.getAttribute('data-col'), 30 | sortable: !colNode.getAttribute('data-nosort'), 31 | type: colNode.getAttribute('data-type') || 'string' 32 | }; 33 | cols.push(col); 34 | if (col.sortable) { 35 | col.defaultDescSort = col.type === 'number'; 36 | colNode.innerHTML = colNode.innerHTML + ''; 37 | } 38 | } 39 | return cols; 40 | } 41 | // attaches a data attribute to every tr element with an object 42 | // of data values keyed by column name 43 | function loadRowData(tableRow) { 44 | var tableCols = tableRow.querySelectorAll('td'), 45 | colNode, 46 | col, 47 | data = {}, 48 | i, 49 | val; 50 | for (i = 0; i < tableCols.length; i += 1) { 51 | colNode = tableCols[i]; 52 | col = cols[i]; 53 | val = colNode.getAttribute('data-value'); 54 | if (col.type === 'number') { 55 | val = Number(val); 56 | } 57 | data[col.key] = val; 58 | } 59 | return data; 60 | } 61 | // loads all row data 62 | function loadData() { 63 | var rows = getTableBody().querySelectorAll('tr'), 64 | i; 65 | 66 | for (i = 0; i < rows.length; i += 1) { 67 | rows[i].data = loadRowData(rows[i]); 68 | } 69 | } 70 | // sorts the table using the data for the ith column 71 | function sortByIndex(index, desc) { 72 | var key = cols[index].key, 73 | sorter = function (a, b) { 74 | a = a.data[key]; 75 | b = b.data[key]; 76 | return a < b ? -1 : a > b ? 1 : 0; 77 | }, 78 | finalSorter = sorter, 79 | tableBody = document.querySelector('.coverage-summary tbody'), 80 | rowNodes = tableBody.querySelectorAll('tr'), 81 | rows = [], 82 | i; 83 | 84 | if (desc) { 85 | finalSorter = function (a, b) { 86 | return -1 * sorter(a, b); 87 | }; 88 | } 89 | 90 | for (i = 0; i < rowNodes.length; i += 1) { 91 | rows.push(rowNodes[i]); 92 | tableBody.removeChild(rowNodes[i]); 93 | } 94 | 95 | rows.sort(finalSorter); 96 | 97 | for (i = 0; i < rows.length; i += 1) { 98 | tableBody.appendChild(rows[i]); 99 | } 100 | } 101 | // removes sort indicators for current column being sorted 102 | function removeSortIndicators() { 103 | var col = getNthColumn(currentSort.index), 104 | cls = col.className; 105 | 106 | cls = cls.replace(/ sorted$/, '').replace(/ sorted-desc$/, ''); 107 | col.className = cls; 108 | } 109 | // adds sort indicators for current column being sorted 110 | function addSortIndicators() { 111 | getNthColumn(currentSort.index).className += currentSort.desc ? ' sorted-desc' : ' sorted'; 112 | } 113 | // adds event listeners for all sorter widgets 114 | function enableUI() { 115 | var i, 116 | el, 117 | ithSorter = function ithSorter(i) { 118 | var col = cols[i]; 119 | 120 | return function () { 121 | var desc = col.defaultDescSort; 122 | 123 | if (currentSort.index === i) { 124 | desc = !currentSort.desc; 125 | } 126 | sortByIndex(i, desc); 127 | removeSortIndicators(); 128 | currentSort.index = i; 129 | currentSort.desc = desc; 130 | addSortIndicators(); 131 | }; 132 | }; 133 | for (i =0 ; i < cols.length; i += 1) { 134 | if (cols[i].sortable) { 135 | // add the click event handler on the th so users 136 | // dont have to click on those tiny arrows 137 | el = getNthColumn(i).querySelector('.sorter').parentElement; 138 | if (el.addEventListener) { 139 | el.addEventListener('click', ithSorter(i)); 140 | } else { 141 | el.attachEvent('onclick', ithSorter(i)); 142 | } 143 | } 144 | } 145 | } 146 | // adds sorting functionality to the UI 147 | return function () { 148 | if (!getTable()) { 149 | return; 150 | } 151 | cols = loadColumns(); 152 | loadData(cols); 153 | addSortIndicators(); 154 | enableUI(); 155 | }; 156 | })(); 157 | 158 | window.addEventListener('load', addSorting); 159 | -------------------------------------------------------------------------------- /coverage/chrome/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Code coverage report for src/ 5 | 6 | 7 | 8 | 9 | 14 | 15 | 16 |
17 |
18 |

19 | all files src/ 20 |

21 |
22 |
23 | 34.77% 24 | Statements 25 | 7788/22400 26 |
27 |
28 | 17.65% 29 | Branches 30 | 2659/15064 31 |
32 |
33 | 24.41% 34 | Functions 35 | 1115/4568 36 |
37 |
38 | 36.51% 39 | Lines 40 | 7531/20628 41 |
42 |
43 |
44 |
45 |
46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 |
FileStatementsBranchesFunctionsLines
base.js
35.55%600/168819.4%218/112422.16%76/34337.49%580/1547
clarity.js
35.76%664/185718.58%225/121123.38%90/38537.51%641/1709
component.js
36.58%653/178519.05%225/118124.32%89/36638.49%630/1637
createchild.js
33.22%949/285716.52%316/191325%143/57234.72%920/2650
index.js
32.08%966/301115.78%319/202123.72%144/60733.58%937/2790
play.js
35.97%663/184318.5%225/121623.81%90/37837.76%640/1695
progressbar.js
34.58%877/253617.37%302/173925.34%131/51736.36%848/2332
progresstime.js
36.24%665/183518.58%225/121123.75%90/37938.06%642/1687
screen.js
35.38%875/247317.7%302/170625.84%131/50737.27%846/2270
volume.js
34.83%876/251517.34%302/174225.49%131/51436.65%847/2311
193 |
194 |
195 | 199 | 200 | 201 | 208 | 209 | 210 | 211 | -------------------------------------------------------------------------------- /demo/icon/pause.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Created with Sketch. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /demo/icon/play.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Created with Sketch. 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /demo/icon/screen-full.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Sketch. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /demo/icon/screen-small.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Sketch. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /demo/icon/volume-high.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Sketch. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /demo/icon/volume-low.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Sketch. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /demo/icon/volume-mute.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Sketch. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /demo/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | test 6 | 22 | 23 | 24 |
25 |
26 | 27 | 28 | 29 | 30 | 430 | 431 | -------------------------------------------------------------------------------- /dev.md: -------------------------------------------------------------------------------- 1 | # 开发笔记 2 | 3 | ## 整理思路 4 | 5 | 1. js 来做通过控制类来调整布局的变化。 css 主要由用户去写, 而不是动态生成 6 | 7 | ## 记下来要做的事情 8 | 9 | 1. 文档补齐,测试用例补齐,代码整理 10 | * 整理 package.json, build 目录 11 | * eslint 12 | 1. 尝试使用 flex 布局(table 布局) 13 | 1. 思考用户 css 自定义 html event 14 | 15 | ## 总结的思路 16 | 17 | 1. 类似于 smll play 的类名, 会被覆盖掉,所以类名得复杂一点了 18 | 2. 组件的话, 用户可以替换 icon 但是不可以替换 html 19 | 20 | ## 测试点 21 | 22 | 1. 用户配置 -> init html event 23 | 2. 设定的几个事件的执行情况 -------------------------------------------------------------------------------- /karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration 2 | // Generated on Thu Aug 03 2017 16:41:10 GMT+0800 (CST) 3 | 4 | const babel = require('rollup-plugin-babel'); 5 | const resolve = require('rollup-plugin-node-resolve'); 6 | const commonjs = require('rollup-plugin-commonjs'); 7 | const postcss = require('rollup-plugin-postcss'); 8 | const replace = require('rollup-plugin-replace'); 9 | // PostCSS plugins 10 | const cssnext = require('postcss-cssnext'); 11 | 12 | module.exports = function (config) { 13 | config.set({ 14 | 15 | // base path that will be used to resolve all patterns (eg. files, exclude) 16 | basePath: '', 17 | 18 | // frameworks to use 19 | // available frameworks: https://npmjs.org/browse/keyword/karma-adapter 20 | frameworks: ['jasmine'], 21 | 22 | // list of files / patterns to load in the browser 23 | files: [ 24 | {pattern: 'src/**.js', watched: false}, 25 | {pattern: '__test__/**/**.js', watched: true}, 26 | ], 27 | 28 | // list of files to exclude 29 | exclude: [ 30 | 'node_modules/*' 31 | ], 32 | 33 | // preprocess matching files before serving them to the browser 34 | // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor 35 | preprocessors: { 36 | 'src/**.js': ['rollup', 'coverage'], 37 | '__test__/**/**.js': ['rollup'] 38 | }, 39 | rollupPreprocessor: { 40 | plugins: [ 41 | postcss({ 42 | plugins: [ 43 | cssnext() 44 | ], 45 | extensions: ['.css'] 46 | }), 47 | babel({ 48 | presets: ['es2015-rollup', 'stage-0'], 49 | plugins: [ 50 | 'transform-decorators-legacy', 51 | 'transform-runtime' 52 | ], 53 | exclude: 'node_modules/**', 54 | runtimeHelpers: true, 55 | babelrc: false 56 | }), 57 | resolve(), 58 | commonjs(), 59 | replace({ 60 | 'process.env.NODE_ENV': '"production"' 61 | }) 62 | ], 63 | format: 'iife', 64 | name: 'controlbar', 65 | sourcemap: 'inline' 66 | }, 67 | // test results reporter to use 68 | // possible values: 'dots', 'progress' 69 | // available reporters: https://npmjs.org/browse/keyword/karma-reporter 70 | reporters: ['progress', 'coverage'], 71 | 72 | coverageReporter: { 73 | type: 'html', 74 | dir: 'coverage/', 75 | subdir (browser) { 76 | return browser.toLowerCase().split(/[ /-]/)[0]; 77 | } 78 | }, 79 | 80 | // web server port 81 | port: 9876, 82 | 83 | // enable / disable colors in the output (reporters and logs) 84 | colors: true, 85 | 86 | // level of logging 87 | // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG 88 | logLevel: config.LOG_INFO, 89 | 90 | // enable / disable watching file and executing tests whenever any file changes 91 | autoWatch: true, 92 | 93 | // start these browsers 94 | // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher 95 | browsers: ['Chrome'], 96 | 97 | // Continuous Integration mode 98 | // if true, Karma captures browsers, runs the tests and exits 99 | singleRun: false, 100 | 101 | // Concurrency level 102 | // how many browser should be started simultaneous 103 | concurrency: Infinity, 104 | 105 | client: { 106 | captureConsole: true 107 | } 108 | }); 109 | }; 110 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "chimee-plugin-controlbar", 3 | "version": "0.4.11", 4 | "description": "controlbar for chimee", 5 | "main": "lib/index.js", 6 | "module": "lib/index.mjs", 7 | "jsnext:main": "lib/index.mjs", 8 | "browser": "lib/index.browser.js", 9 | "scripts": { 10 | "test": "karma start karma.conf.js", 11 | "lint": "eslint ./src --fix", 12 | "build": "npm run b-common && npm run b-es && npm run b-umd && npm run b-min", 13 | "b-common": "rollup -c build/rollup.config.common.js", 14 | "b-es": "rollup -c build/rollup.config.es.js", 15 | "b-umd": "rollup -c build/rollup.config.umd.js", 16 | "b-min": "rollup -c build/rollup.config.min.js" 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/Chimeejs/chimee-plugin-controlbar.git" 21 | }, 22 | "keywords": [ 23 | "plugin", 24 | "chimee", 25 | "controlbar", 26 | "video" 27 | ], 28 | "author": "yandeqiang", 29 | "license": "MIT", 30 | "bugs": { 31 | "url": "https://github.com/Chimeejs/chimee-plugin-controlbar/issues" 32 | }, 33 | "homepage": "https://github.com/Chimeejs/chimee-plugin-controlbar#readme", 34 | "dependencies": { 35 | "babel-eslint": "^9.0.0", 36 | "chimee-helper": "^0.2.11" 37 | }, 38 | "devDependencies": { 39 | "babel-core": "^6.26.0", 40 | "babel-plugin-external-helpers": "^6.22.0", 41 | "babel-plugin-transform-decorators-legacy": "^1.3.4", 42 | "babel-plugin-transform-runtime": "^6.23.0", 43 | "babel-preset-env": "^1.6.1", 44 | "babel-preset-es2015-rollup": "^3.0.0", 45 | "babel-preset-latest": "^6.24.1", 46 | "babel-preset-stage-0": "^6.24.1", 47 | "chimee": "^0.9.5", 48 | "cssnano": "^4.1.10", 49 | "postcss": "^6.0.14", 50 | "postcss-cssnext": "^3.0.2", 51 | "postcss-reporter": "^5.0.0", 52 | "rollup": "^0.50.0", 53 | "rollup-plugin-babel": "^3.0.2", 54 | "rollup-plugin-commonjs": "^8.2.6", 55 | "rollup-plugin-node-resolve": "^3.0.0", 56 | "rollup-plugin-postcss": "^0.5.5", 57 | "rollup-plugin-replace": "^2.0.0", 58 | "rollup-plugin-uglify": "^2.0.1", 59 | "stylelint": "^11.1.1", 60 | "stylelint-config-standard": "^17.0.0", 61 | "toxic-decorators": "^0.3.8" 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /src/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chimeejs/chimee-plugin-controlbar/122787e29c3f905a101c7bb722937a81abc4b600/src/.DS_Store -------------------------------------------------------------------------------- /src/README.md: -------------------------------------------------------------------------------- 1 | # control 2 | 3 | ## 介绍 4 | 5 | 1. 定义了组件基础类 6 | 7 | 1. 定义了官方组件 8 | 9 | 1. 定义了扩展自定义组件的方式 10 | 11 | ## 第一阶段优化 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 1. 尽量不要使用默认 css 值来做什么事情, 尽量计算, 防止用户复写 css 后产生的 js 还是拿自己默认的 css 去写, 因为不同的机器/浏览器, 获取到 css 值不同 40 | 41 | 1. 有些数据流还是双向的。 可以尽量做到单向, 42 | 43 | ## 注意 44 | 45 | 1. 采用 table 布局, 根据内容撑开 cell, 可以设置 cell 中, 子元素的宽度来决定父元素的宽度 46 | 47 | 1. 鉴于 html 假如用户自定义的话, 则之前写好的组件也就没什么意义了。还不如用户重新写一个方便, 所以把 html 从已有组件用户配置项中移除 (提示用户, html 属性只有在用户完全自定义时有效, 图标组件, 提供 icon 属性, 可以替换默认 html, 较为复杂逻辑建议自定义组件) 48 | 49 | 1. css 长度,有的需要用户写一份 css 样式表来覆盖, 有的是 js 运算出来的。 50 | 51 | ## 待完善组件列表 52 | 53 | 1. 刷新 54 | 55 | 1. 清晰度 -------------------------------------------------------------------------------- /src/base.js: -------------------------------------------------------------------------------- 1 | import {addEvent, removeEvent} from 'chimee-helper'; 2 | import {bind} from 'toxic-utils'; 3 | 4 | export default class Base { 5 | constructor (parent) { 6 | this.parent = parent; 7 | } 8 | 9 | create () { 10 | this.createEl(); 11 | this.addAllEvent(); 12 | this.option.create && this.option.create.apply(this); 13 | } 14 | 15 | destroy () { 16 | this.removeAllEvent(); 17 | this.option.destroy && this.option.destroy.apply(this); 18 | } 19 | 20 | createEl () { 21 | this.$dom = document.createElement(this.option.tag); 22 | typeof this.option.html === 'string' 23 | ? this.$dom.innerHTML = this.option.html 24 | : this.$dom.appendChild(this.option.html); 25 | this.parent.$wrap.appendChild(this.$dom); 26 | } 27 | 28 | addAllEvent () { 29 | this.option.defaultEvent && Object.keys(this.option.defaultEvent).forEach(item => { 30 | const key = this.option.defaultEvent[item]; 31 | this[key] = bind(this[key], this); 32 | addEvent(this.$dom, item, this[key], false, false); 33 | }); 34 | this.option.event && Object.keys(this.option.event).forEach(item => { 35 | const key = '__' + item; 36 | this[key] = bind(this.option.event[item], this); 37 | addEvent(this.$dom, item, this[key], false, false); 38 | }); 39 | } 40 | 41 | removeAllEvent () { 42 | if(!(this.$dom instanceof HTMLElement)) { 43 | this.$dom = this.$dom[0]; 44 | } 45 | this.option.defaultEvent && Object.keys(this.option.defaultEvent).forEach(item => { 46 | const key = this.option.defaultEvent[item]; 47 | this[key] = bind(this[key], this); 48 | removeEvent(this.$dom, item, this[key], false, false); 49 | }); 50 | this.option.event && Object.keys(this.option.event).forEach(item => { 51 | const key = '__' + item; 52 | this[key] = bind(this.option.event[item], this); 53 | removeEvent(this.$dom, item, this[key], false, false); 54 | }); 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /src/clarity.js: -------------------------------------------------------------------------------- 1 | import {deepAssign, isObject, addClassName, removeClassName, setStyle, $} from 'chimee-helper'; 2 | import Base from './base.js'; 3 | 4 | /** 5 | * clarity 配置 6 | */ 7 | 8 | const defaultOption = { 9 | tag: 'chimee-clarity', 10 | width: '2em', 11 | html: ` 12 | 13 | 14 |
    15 |
    16 | 17 | 18 | 19 | 20 | 21 |
    22 |
    23 | `, 24 | defaultEvent: { 25 | click: 'click' 26 | }, 27 | duration: 3, 28 | bias: 0, 29 | increment: 0, 30 | repeatTimes: 0, 31 | immediate: false, 32 | abort: false, 33 | list: [] 34 | }; 35 | 36 | export default class Clarity extends Base { 37 | constructor (parent, option) { 38 | super(parent); 39 | this.option = deepAssign(defaultOption, isObject(option) ? option : {}); 40 | this.init(); 41 | } 42 | 43 | init () { 44 | super.create(); 45 | addClassName(this.$dom, 'chimee-flex-component'); 46 | 47 | this.$text = $(this.$dom).find('chimee-clarity-text'); 48 | this.$list = $(this.$dom).find('chimee-clarity-list'); 49 | this.$listUl = this.$list.find('ul'); 50 | 51 | // 用户自定义配置 52 | this.option.width && setStyle(this.$dom, 'width', this.option.width); 53 | 54 | const list = this.option.list; 55 | if(!list.length) { 56 | return setStyle(this.$dom, { 57 | display: 'none' 58 | }); 59 | } 60 | this.initTextList(this.option.list); 61 | } 62 | 63 | initTextList (list) { 64 | this.$listUl.html(''); 65 | list.forEach(item => { 66 | const li = $(document.createElement('li')); 67 | li.attr('data-url', item.src); 68 | li.text(item.name); 69 | if(item.src === this.parent.$videoConfig.src) { 70 | this.$text.text(item.name); 71 | li.addClass('active'); 72 | } 73 | this.$listUl.append(li); 74 | }); 75 | } 76 | 77 | click (e) { 78 | const elem = e.target; 79 | if(elem.tagName === 'LI') { 80 | const url = elem.getAttribute('data-url') || ''; 81 | this.switchClarity(url).then(() => { 82 | this.loadOption = undefined; 83 | Array.from(elem.parentElement.children).map(item => { 84 | removeClassName(item, 'active'); 85 | }); 86 | addClassName(e.target, 'active'); 87 | this.$text.text(e.target.textContent); 88 | }).catch((e) => { 89 | console.warn(e); 90 | }); 91 | } 92 | } 93 | 94 | switchClarity (url) { 95 | if (this.loadOption) { 96 | this.loadOption.abort = true; 97 | } 98 | const {duration, bias, increment, repeatTimes, immediate, abort} = this.option; 99 | this.loadOption = { 100 | duration, 101 | bias, 102 | increment, 103 | repeatTimes, 104 | immediate, 105 | abort 106 | }; 107 | return this.parent.$silentLoad(url, this.loadOption); 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /src/component.js: -------------------------------------------------------------------------------- 1 | import {addClassName} from 'chimee-helper'; 2 | import Base from './base.js'; 3 | 4 | /** 5 | * 自定义组件配置 6 | */ 7 | 8 | export default class Component extends Base { 9 | constructor (parent, option) { 10 | super(parent); 11 | this.option = option; 12 | this.init(); 13 | } 14 | 15 | init () { 16 | super.create(); 17 | addClassName(this.$dom, 'chimee-flex-component'); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/control.css: -------------------------------------------------------------------------------- 1 | /* 暂时存放到这的, 用来设置 container video 的基本样式 */ 2 | container { 3 | position: relative; 4 | } 5 | 6 | container, 7 | video { 8 | display: block; 9 | width: 100%; 10 | height: 100%; 11 | background: #000; 12 | outline: none; 13 | } 14 | 15 | video:focus { 16 | outline: none; 17 | } 18 | 19 | /* 用到的变量 */ 20 | :root { 21 | --barColor: #de698c; 22 | --trackColor: #4c4c4c; 23 | } 24 | 25 | /* 全局默认样式 */ 26 | .chimee-flex-component svg g { 27 | fill: #fff; 28 | stroke: #fff; 29 | } 30 | 31 | .chimee-flex-component svg:hover g { 32 | fill: #fff; 33 | stroke: #fff; 34 | } 35 | 36 | /* 默认隐藏 */ 37 | chimee-volume-state-mute, 38 | chimee-volume-state-low, 39 | chimee-volume-state-high, 40 | chimee-control-state-pause, 41 | chimee-control-state-play, 42 | chimee-progressbar-tip, 43 | chimee-screen-full, 44 | chimee-playbackrate-list, 45 | chimee-clarity-list, 46 | chimee-screen-small { 47 | display: none; 48 | } 49 | 50 | /* 满足条件时显示 */ 51 | chimee-control.full chimee-screen-full, 52 | chimee-control.small chimee-screen-small, 53 | chimee-volume.mute chimee-volume-state-mute, 54 | chimee-volume.low chimee-volume-state-low, 55 | chimee-volume.high chimee-volume-state-high, 56 | chimee-control.pause chimee-control-state-pause, 57 | chimee-control.play chimee-control-state-play, 58 | chimee-control.full chimee-screen-full, 59 | chimee-control.small chimee-screen-small { 60 | display: inline-block; 61 | width: 1.4em; 62 | height: 100%; 63 | } 64 | 65 | /* 开始写具体样式 */ 66 | chimee-control { 67 | position: absolute; 68 | bottom: 0; 69 | left: 0; 70 | display: block; 71 | width: 100%; 72 | height: 100%; 73 | line-height: 2em; 74 | font-size: 14px; 75 | user-select: none; 76 | overflow: hidden; 77 | font-family: Roboto, Arial, Helvetica, sans-serif; 78 | transition: visibility 0.5s ease; 79 | } 80 | 81 | chimee-control-wrap { 82 | position: absolute; 83 | left: 0; 84 | bottom: 0; 85 | width: 100%; 86 | height: 2.2em; 87 | display: flex; 88 | flex-flow: row nowrap; 89 | justify-content: flex-start; 90 | align-items: flex-start; 91 | background: rgba(0, 0, 0, 0.5); 92 | transition: bottom 0.5s ease; 93 | pointer-events: auto; 94 | } 95 | 96 | .chimee-flex-component { 97 | order: 1; 98 | flex-grow: 0; 99 | height: 2em; 100 | cursor: pointer; 101 | } 102 | 103 | .chimee-flex-component svg { 104 | vertical-align: middle; 105 | width: 2em; 106 | height: 1.5em; 107 | } 108 | 109 | /* 播放器状态,播放/暂停 */ 110 | chimee-control-state.chimee-flex-component { 111 | flex-basis: 3em; 112 | text-align: right; 113 | margin-right: 1em; 114 | } 115 | 116 | /* 播放器状态,播放/暂停 动画效果 */ 117 | chimee-control-state .left, 118 | chimee-control-state .right { 119 | transition: d 0.2s ease-in-out; 120 | } 121 | 122 | /* 时间显示 */ 123 | chimee-progresstime.chimee-flex-component { 124 | color: #fff; 125 | font-weight: normal; 126 | text-align: center; 127 | white-space: nowrap; 128 | } 129 | 130 | chimee-progresstime-pass, 131 | chimee-progresstime-total { 132 | display: inline; 133 | } 134 | 135 | /* 播放器控制条 */ 136 | chimee-progressbar.chimee-flex-component { 137 | position: relative; 138 | flex-grow: 1; 139 | } 140 | 141 | chimee-progressbar-wrap { 142 | display: inline-block; 143 | height: 100%; 144 | position: absolute; 145 | left: 1em; 146 | right: 1em; 147 | top: 0; 148 | pointer-events: none; 149 | } 150 | 151 | chimee-progressbar.progressbar-layout-top { 152 | position: static; 153 | 154 | & chimee-progressbar-wrap { 155 | top: -1.6em; 156 | height: 1.6em; 157 | left: 0; 158 | right: 0; 159 | } 160 | 161 | & .chimee-progressbar-line { 162 | left: 0; 163 | top: 0.8em; 164 | } 165 | } 166 | 167 | .chimee-progressbar-line { 168 | position: absolute; 169 | top: 0.9em; 170 | left: 0; 171 | display: inline-block; 172 | height: 3px; 173 | } 174 | 175 | chimee-progressbar-bg { 176 | width: 100%; 177 | background: var(--trackColor); 178 | } 179 | 180 | chimee-progressbar-buffer { 181 | width: 0; 182 | background: #6f6f6f; 183 | } 184 | 185 | chimee-progressbar-all { 186 | background: var(--barColor); 187 | } 188 | 189 | chimee-progressbar-ball { 190 | content: ''; 191 | position: absolute; 192 | right: -0.8em; 193 | top: -0.3em; 194 | display: inline-block; 195 | width: 0.8em; 196 | height: 0.8em; 197 | border-radius: 0.8em; 198 | background: #fff; 199 | pointer-events: none; 200 | } 201 | 202 | chimee-progressbar-tip { 203 | position: absolute; 204 | bottom: 0.5em; 205 | top: -1.5em; 206 | left: 0; 207 | z-index: 10; 208 | padding: 0 0.5em; 209 | height: 1.5em; 210 | background: #fff; 211 | line-height: 1.5em; 212 | border-radius: 4px; 213 | color: #000; 214 | text-align: center; 215 | } 216 | 217 | /* 音量控制 */ 218 | chimee-volume.chimee-flex-component { 219 | cursor: pointer; 220 | padding: 0; 221 | flex-basis: 7em; 222 | white-space: nowrap; 223 | margin-right: 1em; 224 | position: relative; 225 | } 226 | 227 | chimee-volume.chimee-flex-component.vertical { 228 | padding-right: 1em; 229 | flex-basis: 2em; 230 | } 231 | 232 | chimee-volume-state { 233 | display: inline-block; 234 | width: 2em; 235 | vertical-align: top; 236 | } 237 | 238 | /* 动画所用到的元素 css */ 239 | chimee-volume .ring1, 240 | chimee-volume .ring2, 241 | chimee-volume .line { 242 | stroke-dasharray: 150; 243 | stroke-dashoffset: 150; 244 | transition: stroke-dashoffset 0.7s ease-in-out; 245 | } 246 | 247 | chimee-volume.mute .line, 248 | chimee-volume.mute .ring1, 249 | chimee-volume.mute .ring2 { 250 | stroke-dashoffset: 0; 251 | } 252 | 253 | chimee-volume.high .ring1, 254 | chimee-volume.high .ring2 { 255 | stroke-dashoffset: 0; 256 | } 257 | 258 | chimee-volume.low .ring2 { 259 | stroke-dashoffset: 0; 260 | } 261 | 262 | chimee-volume.vertical:hover chimee-volume-bar { 263 | display: inline-block; 264 | } 265 | 266 | chimee-volume.vertical chimee-volume-bar { 267 | position: absolute; 268 | top: -7em; 269 | left: -0.2em; 270 | width: 2em; 271 | height: 4em; 272 | padding-bottom: 3em; 273 | display: none; 274 | vertical-align: middle; 275 | } 276 | 277 | chimee-volume.vertical chimee-volume-bar::before { 278 | content: ''; 279 | position: absolute; 280 | left: 0; 281 | right: 0; 282 | top: 0; 283 | bottom: 1em; 284 | background: #212121; 285 | border-radius: 4px; 286 | } 287 | 288 | chimee-volume.horizonal chimee-volume-bar { 289 | position: relative; 290 | height: 1.2em; 291 | width: 4em; 292 | display: inline-block; 293 | vertical-align: middle; 294 | } 295 | 296 | chimee-volume.vertical chimee-volume-bar-wrap { 297 | display: inline-block; 298 | position: absolute; 299 | bottom: 1em; 300 | left: 0; 301 | top: 1em; 302 | right: 0; 303 | height: 4em; 304 | } 305 | 306 | chimee-volume.vertical chimee-volume-bar-all, 307 | chimee-volume.vertical chimee-volume-bar-bg { 308 | position: absolute; 309 | bottom: 0; 310 | left: 1em; 311 | display: inline-block; 312 | width: 2px; 313 | border-radius: 4px; 314 | } 315 | 316 | chimee-volume.vertical chimee-volume-bar-bg { 317 | height: 4em; 318 | background: var(--trackColor); 319 | opacity: 0.5; 320 | } 321 | 322 | chimee-volume.vertical chimee-volume-bar-all { 323 | background: var(--barColor); 324 | } 325 | 326 | chimee-volume.vertical chimee-volume-bar-all::after { 327 | content: ''; 328 | position: absolute; 329 | right: -0.34em; 330 | top: -0.4em; 331 | display: inline-block; 332 | width: 0.8em; 333 | height: 0.8em; 334 | border-radius: 0.8em; 335 | background: #fff; 336 | pointer-events: none; 337 | } 338 | 339 | chimee-volume.horizonal chimee-volume-bar { 340 | position: relative; 341 | height: 1.2em; 342 | width: 4em; 343 | display: inline-block; 344 | vertical-align: middle; 345 | } 346 | 347 | chimee-volume.horizonal chimee-volume-bar-all, 348 | chimee-volume.horizonal chimee-volume-bar-bg { 349 | position: absolute; 350 | top: 0.4em; 351 | left: 0; 352 | display: inline-block; 353 | height: 2px; 354 | } 355 | 356 | chimee-volume.horizonal chimee-volume-bar-bg { 357 | width: 4em; 358 | background: var(--trackColor); 359 | opacity: 0.5; 360 | } 361 | 362 | chimee-volume.horizonal chimee-volume-bar-all { 363 | background: var(--barColor); 364 | } 365 | 366 | chimee-volume.horizonal chimee-volume-bar-all::after { 367 | content: ''; 368 | position: absolute; 369 | right: -0.4em; 370 | top: -0.3em; 371 | display: inline-block; 372 | width: 0.8em; 373 | height: 0.8em; 374 | border-radius: 0.8em; 375 | background: #fff; 376 | pointer-events: none; 377 | } 378 | 379 | /* 全屏 */ 380 | chimee-screen.chimee-flex-component { 381 | flex-basis: 3em; 382 | text-align: left; 383 | } 384 | 385 | /* 清晰度切换, 倍速切换 */ 386 | chimee-clarity.chimee-flex-component, 387 | chimee-playbackrate.chimee-flex-component { 388 | position: relative; 389 | color: #fff; 390 | width: 6em; 391 | height: 1.75em; 392 | padding: 0; 393 | padding-right: 1em; 394 | padding-left: 1em; 395 | text-align: center; 396 | vertical-align: middle; 397 | font-size: 16px; 398 | } 399 | 400 | chimee-playbackrate.chimee-flex-component:hover chimee-playbackrate-list, 401 | chimee-clarity.chimee-flex-component:hover chimee-clarity-list { 402 | display: inline-block; 403 | } 404 | 405 | chimee-playbackrate.chimee-flex-component svg, 406 | chimee-clarity.chimee-flex-component svg { 407 | width: auto; 408 | height: auto; 409 | } 410 | 411 | chimee-playbackrate-list, 412 | chimee-clarity-list { 413 | position: absolute; 414 | left: 0; 415 | bottom: 1em; 416 | width: 100%; 417 | padding-bottom: 1.5em; 418 | opacity: 0.8; 419 | box-sizing: content-box; 420 | line-height: 0; 421 | } 422 | 423 | chimee-playbackrate-list ul, 424 | chimee-clarity-list ul { 425 | margin: 0; 426 | padding: 0.5em 0; 427 | background: #292929; 428 | line-height: 2em; 429 | } 430 | 431 | chimee-playbackrate-list li, 432 | chimee-clarity-list li { 433 | list-style-type: none; 434 | } 435 | 436 | chimee-playbackrate-list li:hover, 437 | chimee-playbackrate-list li.active, 438 | chimee-clarity-list li:hover, 439 | chimee-clarity-list li.active { 440 | color: var(--barColor); 441 | } 442 | 443 | chimee-playbackrate-list-arrow, 444 | chimee-clarity-list-arrow { 445 | position: absolute; 446 | bottom: 1.5em; 447 | width: 100%; 448 | } 449 | -------------------------------------------------------------------------------- /src/createchild.js: -------------------------------------------------------------------------------- 1 | import Component from './component.js'; 2 | import Play from './play.js'; 3 | import Volume from './volume.js'; 4 | import ProgressBar from './progressbar.js'; 5 | import ProgressTime from './progresstime.js'; 6 | import Screen from './screen.js'; 7 | import Clarity from './clarity.js'; 8 | import Playbackrate from './playbackrate.js'; 9 | 10 | function hundleChildren (plugin) { 11 | let childConfig = {}; 12 | if(!plugin.$config.children) { 13 | childConfig = plugin.isLive ? { 14 | play: true, // 底部播放暂停按钮 15 | progressTime: false, // 播放时间 16 | progressBar: false, // 播放进度控制条 17 | volume: true, // 声音控制 18 | screen: true, // 全屏控制 19 | } : { 20 | play: true, // 底部播放暂停按钮 21 | progressTime: true, // 播放时间 22 | progressBar: true, // 播放进度控制条 23 | volume: true, // 声音控制 24 | screen: true, // 全屏控制 25 | }; 26 | }else{ 27 | childConfig = plugin.$config.children; 28 | } 29 | 30 | return childConfig; 31 | } 32 | 33 | /** 34 | * 1. 将所有的 ui component 输出到 html 结构中 35 | * 2. 为这些 component 绑定响应的事件 36 | * @param {*} dom 所有 ui 节点的子容器 37 | * @param {*} config 关于 ui 的一些列设置 38 | * @return {Array} 所有子节点 39 | */ 40 | 41 | export function createChild (plugin) { 42 | const childConfig = plugin.config.children = hundleChildren(plugin); 43 | const children = {}; 44 | if(!childConfig) { 45 | children.play = new Play(plugin); 46 | children.progressTime = new ProgressTime(plugin); 47 | children.progressBar = new ProgressBar(plugin); 48 | children.volume = new Volume(plugin); 49 | children.screen = new Screen(plugin); 50 | }else{ 51 | Object.keys(childConfig).forEach(item => { 52 | switch(item) { 53 | case 'play': 54 | if(childConfig.play) { 55 | children.play = new Play(plugin, childConfig.play); 56 | } 57 | break; 58 | case 'progressTime': 59 | if(childConfig.progressTime) { 60 | children.progressTime = new ProgressTime(plugin, childConfig.progressTime); 61 | } 62 | break; 63 | case 'progressBar': 64 | children.progressBar = new ProgressBar(plugin, childConfig.progressBar); 65 | break; 66 | case 'volume': 67 | if(childConfig.volume) { 68 | children.volume = new Volume(plugin, childConfig.volume); 69 | } 70 | break; 71 | case 'screen': 72 | if(childConfig.screen) { 73 | children.screen = new Screen(plugin, childConfig.screen); 74 | } 75 | break; 76 | case 'clarity': 77 | if(childConfig.clarity) { 78 | children.clarity = new Clarity(plugin, childConfig.clarity); 79 | } 80 | break; 81 | case 'playbackrate': 82 | if(childConfig.playbackrate && Array.isArray(childConfig.playbackrate.list) && childConfig.playbackrate.list.length) { 83 | children.playbackrate = new Playbackrate(plugin, childConfig.playbackrate); 84 | } 85 | break; 86 | default: 87 | children[item] = new Component(plugin, childConfig[item]); 88 | break; 89 | } 90 | }); 91 | } 92 | 93 | return children; 94 | } 95 | -------------------------------------------------------------------------------- /src/image/.DS_Store: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Chimeejs/chimee-plugin-controlbar/122787e29c3f905a101c7bb722937a81abc4b600/src/image/.DS_Store -------------------------------------------------------------------------------- /src/image/danma-off.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/image/danma-on.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/image/enlarge.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Sketch. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/image/loading.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/image/pause.svg: -------------------------------------------------------------------------------- 1 | 5 | 7 | -------------------------------------------------------------------------------- /src/image/play.svg: -------------------------------------------------------------------------------- 1 | 5 | 7 | -------------------------------------------------------------------------------- /src/image/shrink.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Sketch. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/image/spause.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Sketch. 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/image/splay.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Sketch. 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /src/image/vhigh.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | Created with Sketch. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/image/vlow.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Sketch. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/image/vmute.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Created with Sketch. 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import './control.css'; 2 | import {accessor, applyDecorators} from 'toxic-decorators'; 3 | import {isObject, deepAssign, setStyle} from 'chimee-helper'; 4 | import {createChild} from './createchild.js'; 5 | import { isArray } from 'toxic-predicate-functions'; 6 | 7 | const majorColorStyle = ` 8 | .chimee-flex-component svg * { 9 | fill: majorColor; 10 | stroke: majorColor; 11 | } 12 | 13 | chimee-progressbar-all { 14 | background: majorColor; 15 | } 16 | 17 | chimee-volume.chimee-flex-component chimee-volume-bar-all { 18 | background: majorColor; 19 | } 20 | 21 | chimee-playbackrate-list li:hover, 22 | chimee-playbackrate-list li.active, 23 | chimee-clarity-list li:hover, 24 | chimee-clarity-list li.active { 25 | color: majorColor; 26 | } 27 | `; 28 | 29 | const hoverColorStyle = ` 30 | .chimee-flex-component svg:hover *{ 31 | fill: hoverColor; 32 | stroke: hoverColor; 33 | } 34 | `; 35 | 36 | /** 37 | * 插件默认配置 38 | */ 39 | 40 | const defaultConfig = { 41 | hideBarTime: 2000, // hidebar 延迟时间, barShowByMouse 为 move 时有效,enter 时为0, 用户设置无效 42 | barShowByMouse: 'move', // 控制条显示由, move 还是 enter/leave 来控制显示/隐藏, 43 | }; 44 | 45 | const chimeeControl = { 46 | name: 'chimeeControl', 47 | el: 'chimee-control', 48 | data: { 49 | children: {}, 50 | show: false, 51 | disabled: true 52 | }, 53 | level: 99, 54 | operable: false, 55 | penetrate: false, 56 | create () {}, 57 | init (videoConfig) { 58 | }, 59 | destroy () { 60 | window.clearTimeout(this.timeId); 61 | for(const i in this.children) { 62 | this.children[i].destroy(); 63 | } 64 | }, 65 | inited () { 66 | const videoConfig = this.$videoConfig; 67 | if(videoConfig.controls) { 68 | this.show = true; 69 | videoConfig.controls = false; 70 | } 71 | const _this = this; 72 | applyDecorators(videoConfig, { 73 | controls: accessor({ 74 | get () { 75 | return _this.show; 76 | }, 77 | set (value) { 78 | _this.show = Boolean(value); 79 | _this._display(); 80 | return false; 81 | } 82 | }, {preSet: true}) 83 | }, {self: true}); 84 | this.config = isObject(this.$config) ? deepAssign(defaultConfig, this.$config) : defaultConfig; 85 | 86 | this.config.hideBarTime = this.config.barShowByMouse === 'move' ? this.config.hideBarTime : 0; 87 | this.$dom.innerHTML = ''; 88 | this.$wrap = this.$dom.querySelector('chimee-control-wrap'); 89 | this.children = createChild(this); 90 | this._setStyle(); 91 | 92 | for(const i in this.children) { 93 | this.children[i].inited && this.children[i].inited(); 94 | } 95 | }, 96 | events: { 97 | load () { 98 | // update src 充值进度条/时间/播放状态 99 | // load 的时候不会触发 pause(), 手动将控制条显示出来 100 | this._showItself(); 101 | this._progressUpdate(); 102 | this.children.play && this.children.play.changeState('pause'); 103 | this.children.progressTime && this.children.progressTime.updateTotal(); 104 | this.children.progressBar && this.children.progressBar.changePointerEvent('none'); 105 | }, 106 | loadstart () { 107 | this._disable(true); 108 | }, 109 | canplay () { 110 | this._disable(false); 111 | }, 112 | play () { 113 | this.children.play && this.children.play.changeState('play'); 114 | this.config.barShowByMouse === 'move' && this._hideItself(); 115 | }, 116 | pause () { 117 | this.children.play && this.children.play.changeState('pause'); 118 | this._showItself(); 119 | }, 120 | c_mouseenter () { 121 | if(this.config.barShowByMouse === 'move') return; 122 | this._showItself(); 123 | }, 124 | c_mousemove () { 125 | this._mousemove(); 126 | }, 127 | c_mouseleave () { 128 | if(this.config.barShowByMouse === 'move') return; 129 | this._hideItself(); 130 | }, 131 | durationchange () { 132 | this.children.progressTime && this.children.progressTime.updateTotal(); 133 | this.children.progressBar && this.children.progressBar.changePointerEvent('auto'); 134 | }, 135 | timeupdate () { 136 | this._progressUpdate(); 137 | }, 138 | progress () { 139 | this.children.progressBar && this.children.progressBar.progress(); 140 | }, 141 | volumechange () { 142 | this.children.volume && this.children.volume.update(); 143 | }, 144 | keydown (e) { 145 | // if(this.disabled) return; 146 | e.stopPropagation(); 147 | switch (e.keyCode) { 148 | case 32: { 149 | e.preventDefault(); 150 | this.children.play && this.children.play.click(e); 151 | break; 152 | } 153 | case 37: { 154 | e.preventDefault(); 155 | const reduceTime = this.currentTime - 10; 156 | this.currentTime = reduceTime < 0 ? 0 : reduceTime; 157 | this._mousemove(); 158 | break; 159 | } 160 | case 39: { 161 | e.preventDefault(); 162 | const raiseTime = this.currentTime + 10; 163 | this.currentTime = raiseTime > this.duration ? this.duration : raiseTime; 164 | this._mousemove(); 165 | break; 166 | } 167 | case 38: { 168 | e.preventDefault(); 169 | const raiseVolume = this.volume + 0.1; 170 | this.volume = raiseVolume > 1 ? 1 : raiseVolume; 171 | this._mousemove(); 172 | break; 173 | } 174 | case 40: { 175 | e.preventDefault(); 176 | const reduceVolume = this.volume - 0.1; 177 | this.volume = reduceVolume < 0 ? 0 : reduceVolume; 178 | this._mousemove(); 179 | break; 180 | } 181 | } 182 | }, 183 | click (e) { 184 | const time = new Date(); 185 | const preTime = this.clickTime; 186 | this.clickTime = time; 187 | if(time - preTime < 300) { 188 | clearTimeout(this.clickTimeId); 189 | return; 190 | } 191 | this.clickTimeId = setTimeout(() => { 192 | !this.disabled && this.children.play && this.children.play.click(e); 193 | }, 300); 194 | 195 | }, 196 | dblclick (e) { 197 | // this.dblclick = true; 198 | !this.disabled && this.children.screen && this.children.screen.click(); 199 | } 200 | }, 201 | methods: { 202 | _progressUpdate () { 203 | this.children.progressBar && this.children.progressBar.update(); 204 | this.children.progressTime && this.children.progressTime.updatePass(); 205 | }, 206 | _hideItself () { 207 | window.clearTimeout(this.timeId); 208 | this.timeId = setTimeout(() => { 209 | let bottom = this.$wrap.offsetHeight; 210 | bottom = this.children.progressBar ? this.children.progressBar.$wrap[0].offsetTop - bottom : -bottom; 211 | setStyle(this.$wrap, { 212 | bottom: bottom + 'px' 213 | }); 214 | setStyle(this.$dom, { 215 | visibility: 'hidden' 216 | }); 217 | }, this.config.hideBarTime); 218 | }, 219 | _showItself () { 220 | window.clearTimeout(this.timeId); 221 | setStyle(this.$wrap, { 222 | bottom: '0' 223 | }); 224 | setStyle(this.$dom, { 225 | visibility: 'visible' 226 | }); 227 | }, 228 | _display () { 229 | const display = this.show ? 'block' : 'none'; 230 | setStyle(this.$dom, { 231 | display 232 | }); 233 | }, 234 | _mousemove (e) { 235 | if(this.paused || this.config.barShowByMouse === 'enter') return; 236 | this._showItself(); 237 | this._hideItself(); 238 | }, 239 | // controlbar 不可以点 240 | // 键盘/鼠标事件不监听 241 | _disable (disabled) { 242 | if(!this.show) return; 243 | this.disabled = disabled; 244 | // setStyle(this.$wrap, 'pointerEvents', disabled ? 'none' : 'auto'); 245 | }, 246 | _setStyle () { 247 | let css = ''; 248 | css += this.config.majorColor ? majorColorStyle.replace(/majorColor/g, this.config.majorColor) : ''; 249 | css += this.config.hoverColor ? hoverColorStyle.replace(/hoverColor/g, this.config.hoverColor) : ''; 250 | const style = document.createElement('style'); 251 | style.setAttribute('type', 'text/css'); 252 | style.innerHTML = css; 253 | document.head.appendChild(style); 254 | }, 255 | updateClarity (list) { 256 | if(!isArray(list)) { 257 | return console.error('list must be an array.'); 258 | } 259 | this.children.clarity.initTextList(list); 260 | } 261 | } 262 | }; 263 | 264 | export default chimeeControl; 265 | -------------------------------------------------------------------------------- /src/play.js: -------------------------------------------------------------------------------- 1 | import {deepAssign, isObject, addClassName, removeClassName, $} from 'chimee-helper'; 2 | import Base from './base.js'; 3 | 4 | /** 5 | * play 配置 6 | */ 7 | 8 | const defaultOption = { 9 | tag: 'chimee-control-state', 10 | html: ` 11 | 12 | 13 | `, 14 | animate: { 15 | icon: ` 16 | 17 | 18 | 19 | 20 | 21 | 22 | `, 23 | path: { 24 | play: { 25 | left: 'M0.921875,0.265625L0.921875,197.074852L95.7890625,149L96.2929688,49Z', 26 | right: 'M90.3142151,45.9315226L90.3142151,151.774115L201.600944,99.9938782L201.600944,98.0237571Z' 27 | }, 28 | pause: { 29 | left: 'M0.921875,1.265625L0.921875,198.074852L79.3611677,198.074852L79.3611677,0.258923126Z', 30 | right: 'M126.921875,1.265625L126.921875,198.074852L205.361168,198.074852L205.361168,0.258923126Z' 31 | } 32 | } 33 | }, 34 | defaultEvent: { 35 | click: 'click' 36 | } 37 | }; 38 | 39 | export default class Play extends Base { 40 | constructor (parent, option) { 41 | super(parent); 42 | this.option = deepAssign(defaultOption, isObject(option) ? option : {}); 43 | this.animate = false; 44 | this.init(); 45 | } 46 | 47 | init () { 48 | // 创建 html / 绑定事件 49 | super.create(); 50 | this.$dom = $(this.$dom); 51 | this.$dom.addClass('chimee-flex-component'); 52 | 53 | // 判断是否是默认或者用户提供 icon 54 | if(this.option.icon && this.option.icon.play && this.option.icon.pause) { 55 | this.$play = this.$dom.find('chimee-control-state-play'); 56 | this.$pause = this.$dom.find('chimee-control-state-pause'); 57 | this.$play.html(this.option.icon.play); 58 | this.$pause.html(this.option.icon.pause); 59 | }else if(!this.option.bitmap) { 60 | this.animate = true; 61 | this.option.animate.path = this.option.path ? this.option.path : this.option.animate.path; 62 | this.$dom.html(this.option.animate.icon); 63 | this.$left = this.$dom.find('.left'); 64 | this.$right = this.$dom.find('.right'); 65 | } 66 | this.changeState('pause'); 67 | } 68 | 69 | changeState (state) { 70 | const nextState = state === 'play' ? 'pause' : 'play'; 71 | this.state = state; 72 | addClassName(this.parent.$dom, nextState); 73 | removeClassName(this.parent.$dom, state); 74 | this.animate && this.setPath(nextState); 75 | } 76 | 77 | setPath (state) { 78 | const path = this.option.animate.path; 79 | if(state === 'play') { 80 | this.$left.attr('d', path.play.left); 81 | this.$right.attr('d', path.play.right); 82 | }else{ 83 | this.$left.attr('d', path.pause.left); 84 | this.$right.attr('d', path.pause.right); 85 | } 86 | } 87 | 88 | click (e) { 89 | const nextState = this.state === 'play' ? 'pause' : 'play'; 90 | this.changeState(nextState); 91 | this.parent.$emit(nextState); 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /src/playbackrate.js: -------------------------------------------------------------------------------- 1 | import {deepAssign, isObject, addClassName, removeClassName, setStyle, $} from 'chimee-helper'; 2 | import Base from './base.js'; 3 | 4 | /** 5 | * playbackrate 配置 6 | */ 7 | 8 | const defaultOption = { 9 | tag: 'chimee-playbackrate', 10 | width: '4em', 11 | html: ` 12 | 13 | 14 |
      15 |
      16 | 17 | 18 | 19 | 20 | 21 |
      22 |
      23 | `, 24 | defaultEvent: { 25 | click: 'click' 26 | } 27 | }; 28 | 29 | export default class Playbackrate extends Base { 30 | constructor (parent, option) { 31 | super(parent); 32 | this.option = deepAssign(defaultOption, isObject(option) ? option : {}); 33 | this.init(); 34 | } 35 | 36 | init () { 37 | super.create(); 38 | addClassName(this.$dom, 'chimee-flex-component'); 39 | 40 | this.$text = $(this.$dom).find('chimee-playbackrate-text'); 41 | this.$list = $(this.$dom).find('chimee-playbackrate-list'); 42 | this.$listUl = this.$list.find('ul'); 43 | 44 | // 用户自定义配置 45 | this.option.width && setStyle(this.$dom, 'width', this.option.width); 46 | 47 | this.initTextList(); 48 | } 49 | 50 | initTextList () { 51 | const length = this.option.list.length; 52 | let hasRenderDefault = false; 53 | this.option.list.forEach((item, i) => { 54 | const li = $(document.createElement('li')); 55 | li.attr('data-value', item.value); 56 | li.text(item.name); 57 | if(item.default || (!hasRenderDefault && i === length - 1)) { 58 | hasRenderDefault = true; 59 | !item.default && console.warn('播放速率列表需要给每项配置 `default(boolean)` 来标明是否是默认播放速率'); 60 | this.$text.text(item.name); 61 | li.addClass('active'); 62 | this.switchPlaybackrate(item.value); 63 | } 64 | this.$listUl.append(li); 65 | }); 66 | } 67 | 68 | click (e) { 69 | const elem = e.target; 70 | if(elem.tagName === 'LI') { 71 | const rate = elem.getAttribute('data-value') || 1; 72 | this.switchPlaybackrate(rate); 73 | Array.from(elem.parentElement.children).map(item => { 74 | removeClassName(item, 'active'); 75 | }); 76 | addClassName(e.target, 'active'); 77 | this.$text.text(e.target.textContent); 78 | 79 | } 80 | } 81 | 82 | switchPlaybackrate (rate) { 83 | setTimeout(_ => { 84 | this.parent.playbackRate = rate; 85 | }, 0); 86 | } 87 | 88 | } 89 | -------------------------------------------------------------------------------- /src/progressbar.js: -------------------------------------------------------------------------------- 1 | import {deepAssign, isObject, formatTime, $, addEvent, removeEvent, setStyle} from 'chimee-helper'; 2 | import {autobind} from 'toxic-decorators'; 3 | import Base from './base.js'; 4 | 5 | const defaultOption = { 6 | tag: 'chimee-progressbar', 7 | html: ` 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | ` 17 | }; 18 | 19 | export default class ProgressBar extends Base { 20 | constructor (parent, option) { 21 | super(parent); 22 | this.option = deepAssign(defaultOption, isObject(option) ? option : {}); 23 | this.visiable = option !== false; 24 | this.init(); 25 | } 26 | 27 | init () { 28 | super.create(); 29 | this.$dom = $(this.$dom); 30 | this.$wrap = this.$dom.find('chimee-progressbar-wrap'); 31 | this.$buffer = this.$dom.find('chimee-progressbar-buffer'); 32 | this.$all = this.$dom.find('chimee-progressbar-all'); 33 | this.$tip = this.$dom.find('chimee-progressbar-tip'); 34 | this.$track = this.$dom.find('chimee-progressbar-track'); 35 | this.$line = this.$dom.find('.chimee-progressbar-line'); 36 | this.$ball = this.$dom.find('chimee-progressbar-ball'); 37 | this.$dom.addClass('chimee-flex-component'); 38 | 39 | // css 配置 40 | !this.visiable && this.$dom.css('visibility', 'hidden'); 41 | // this.$line.css({ 42 | // top: this.$wrap. 43 | // }); 44 | // 进度条居中布局,还是在上方 45 | if(this.option.layout === 'top') { 46 | this.$dom.addClass('progressbar-layout-top'); 47 | this.$wrap.css({ 48 | // left: -this.$dom[0].offsetLeft + 'px', 49 | top: -this.$ball[0].offsetHeight + 'px', 50 | // height: this.$ball[0].offsetHeight * 2 + 'px' 51 | }); 52 | // this.$line.css({ 53 | // top: this.$ball[0].offsetHeight + 'px' 54 | // }) 55 | setStyle(this.parent.$wrap, 'paddingTop', this.$ball[0].offsetHeight + 'px'); 56 | } 57 | this.addWrapEvent(); 58 | } 59 | destroy () { 60 | this.removeWrapEvent(); 61 | // 解绑全屏监听事件 62 | this.watch_screen && this.watch_screen(); 63 | super.destroy(); 64 | } 65 | addWrapEvent () { 66 | this.$wrap.on('mousedown', this.mousedown); 67 | this.$wrap.on('mousemove', this.tipShow); 68 | this.$wrap.on('mouseleave', this.tipEnd); 69 | } 70 | removeWrapEvent () { 71 | this.$wrap.off('mousedown', this.mousedown); 72 | this.$wrap.off('mousemove', this.tipShow); 73 | this.$wrap.off('mouseleave', this.tipEnd); 74 | } 75 | 76 | /** 77 | * 缓存进度条更新 progress 事件 78 | */ 79 | progress () { 80 | let buffer = 0; 81 | try{ 82 | buffer = this.parent.buffered.end(0); 83 | }catch (e) {} 84 | const bufferWidth = buffer / this.parent.duration * 100 + '%'; 85 | this.$buffer.css('width', bufferWidth); 86 | } 87 | 88 | /** 89 | * requestAnimationFrame 来更新进度条, timeupdate 事件 90 | */ 91 | update () { 92 | // const allWidth = this.$wrap[0].offsetWidth - this.$ball[0].offsetWidth; 93 | const time = this._currentTime !== undefined ? this._currentTime : this.parent.currentTime; 94 | const timePer = time ? time / this.parent.duration : 0; 95 | // const timeWidth = timePer * allWidth; 96 | this.$all.css('width', `calc(${timePer * 100}% - ${this.$ball[0].offsetWidth / 2}px`); 97 | } 98 | @autobind 99 | mousedown (e) { 100 | // const ballRect = this.$ball[0].getClientRects()[0]; 101 | // const ballLeft = ballRect.left; 102 | // const ballRight = ballRect.left + ballRect.width; 103 | // this.inBall = e.clientX <= ballRight && e.clientX >= ballLeft; 104 | if(e.target === this.$tip[0]) return; 105 | 106 | // return when duration is NaN chimee#129 107 | if(this.parent.duration !== this.parent.duration) { 108 | console.error('isNaN(duration) === true'); 109 | return; 110 | } 111 | this._currentTime = e.offsetX / this.$wrap[0].offsetWidth * this.parent.duration; 112 | // if(!this.inBall) this.update(); 113 | this.startX = e.clientX; 114 | this.startTime = this._currentTime; 115 | addEvent(window, 'mousemove', this.draging); 116 | addEvent(window, 'mouseup', this.dragEnd); 117 | addEvent(window, 'contextmenu', this.dragEnd); 118 | } 119 | 120 | /** 121 | * 开始拖拽 122 | * @param {EventObject} e 鼠标事件 123 | */ 124 | @autobind 125 | draging (e) { 126 | this.endX = e.clientX; 127 | const dragTime = (this.endX - this.startX) / this.$wrap[0].offsetWidth * this.parent.duration; 128 | const dragAfterTime = +(this.startTime + dragTime).toFixed(2); 129 | this._currentTime = dragAfterTime < 0 ? 0 : dragAfterTime > this.parent.duration ? this.parent.duration : dragAfterTime; 130 | this.update(); 131 | } 132 | 133 | /** 134 | * 结束拖拽 135 | */ 136 | @autobind 137 | dragEnd () { 138 | this.update(); 139 | this.startX = 0; 140 | this.startTime = 0; 141 | // if(!this.inBall) { 142 | this.parent.currentTime = this._currentTime; 143 | // this.inBall = false; 144 | // } 145 | this._currentTime = undefined; 146 | removeEvent(window, 'mousemove', this.draging); 147 | removeEvent(window, 'mouseup', this.dragEnd); 148 | removeEvent(window, 'contextmenu', this.dragEnd); 149 | } 150 | 151 | @autobind 152 | tipShow (e) { 153 | if(e.target === this.$tip[0] || e.target === this.$ball[0]) { 154 | this.$tip.css('display', 'none'); 155 | return; 156 | }; 157 | let time = e.offsetX / this.$wrap[0].offsetWidth * this.parent.duration; 158 | time = time < 0 ? 0 : time > this.parent.duration ? this.parent.duration : time; 159 | const tipContent = formatTime(time); 160 | let left = e.offsetX - this.$tip[0].offsetWidth / 2; 161 | const leftBound = this.$wrap[0].offsetWidth - this.$tip[0].offsetWidth; 162 | left = left < 0 ? 0 : left > leftBound ? leftBound : left; 163 | this.$tip.text(tipContent); 164 | this.$tip.css('display', 'inline-block'); 165 | this.$tip.css('left', `${left}px`); 166 | } 167 | @autobind 168 | tipEnd () { 169 | this.$tip.css('display', 'none'); 170 | } 171 | 172 | changePointerEvent (value) { 173 | this.$wrap.css('pointerEvents', value); 174 | } 175 | } 176 | -------------------------------------------------------------------------------- /src/progresstime.js: -------------------------------------------------------------------------------- 1 | import {deepAssign, isObject, formatTime, $} from 'chimee-helper'; 2 | import Base from './base.js'; 3 | 4 | /** 5 | * progressTime 配置 6 | */ 7 | 8 | const defaultOption = { 9 | tag: 'chimee-progresstime', 10 | html: ` 11 | 00:00/00:00 15 | 16 | ` 17 | }; 18 | 19 | export default class ProgressTime extends Base { 20 | constructor (parent, option) { 21 | super(parent); 22 | this.option = deepAssign(defaultOption, isObject(option) ? option : {}); 23 | this.init(); 24 | } 25 | 26 | init () { 27 | super.create(); 28 | this.$dom = $(this.$dom); 29 | this.$total = this.$dom.find('chimee-progresstime-total-value'); 30 | this.$pass = this.$dom.find('chimee-progresstime-pass'); 31 | this.$dom.addClass('chimee-flex-component'); 32 | } 33 | 34 | updatePass () { 35 | this.$pass.text(formatTime(this.parent.currentTime)); 36 | } 37 | 38 | updateTotal () { 39 | this.$total.text(formatTime(this.parent.duration)); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/screen.js: -------------------------------------------------------------------------------- 1 | import {deepAssign, isObject, addClassName, removeClassName, $} from 'chimee-helper'; 2 | import {autobind} from 'toxic-decorators'; 3 | import Base from './base.js'; 4 | 5 | /** 6 | * Screen 配置 7 | */ 8 | 9 | const defaultOption = { 10 | tag: 'chimee-screen', 11 | html: ` 12 | 13 | 14 | 15 | Created with Sketch. 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | Created with Sketch. 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | `, 39 | defaultEvent: { 40 | click: 'click' 41 | } 42 | }; 43 | 44 | export default class Screen extends Base { 45 | constructor (parent, option) { 46 | super(parent); 47 | this.state = 'small'; 48 | this.option = deepAssign(defaultOption, isObject(option) ? option : {}); 49 | this.init(); 50 | } 51 | 52 | init () { 53 | super.create(); 54 | this.$dom = $(this.$dom); 55 | this.changeState(this.state); 56 | // addClassName(this.$dom, 'flex-item'); 57 | this.$dom.addClass('chimee-flex-component'); 58 | 59 | this.$full = this.$dom.find('chimee-screen-full'); 60 | this.$small = this.$dom.find('chimee-screen-small'); 61 | // 判断是否是默认或者用户提供 icon 62 | if(this.option.icon && this.option.icon.full && this.option.icon.small) { 63 | // if((!this.option.icon.play && this.option.icon.puase) || (this.option.icon.play && !this.option.icon.puase)) { 64 | // console.warn(`Please provide a play and pause icon!If you can't, we will use default icon!`); 65 | // } 66 | this.$full.html(this.option.icon.full); 67 | this.$small.html(this.option.icon.small); 68 | }else if(this.option.bitmap) { 69 | this.$full.html(''); 70 | this.$small.html(''); 71 | } 72 | } 73 | 74 | changeState (state) { 75 | const removeState = state === 'small' ? 'full' : 'small'; 76 | addClassName(this.parent.$dom, state); 77 | removeClassName(this.parent.$dom, removeState); 78 | } 79 | 80 | click () { 81 | let full = false; 82 | if(this.state === 'small') { 83 | this.state = 'full'; 84 | full = true; 85 | }else{ 86 | this.state = 'small'; 87 | full = false; 88 | } 89 | this.changeState(this.state); 90 | this.parent.$fullscreen(full, 'container'); 91 | if(full) { 92 | this.watch_screen = this.parent.$watch('isFullscreen', this.screenChange); 93 | }else{ 94 | this.watch_screen(); 95 | } 96 | } 97 | @autobind 98 | screenChange () { 99 | if(!this.parent.fullscreenElement) return; 100 | this.state = 'small'; 101 | this.changeState('small'); 102 | this.parent.$fullscreen(false, 'container'); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/volume.js: -------------------------------------------------------------------------------- 1 | import {deepAssign, isObject, $, addEvent, removeEvent} from 'chimee-helper'; 2 | import {autobind} from 'toxic-decorators'; 3 | import Base from './base.js'; 4 | 5 | /** 6 | * Volume 配置 7 | */ 8 | 9 | const defaultOption = { 10 | tag: 'chimee-volume', 11 | html: ` 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | `, 27 | animate: { 28 | icon: ` 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | `, 38 | }, 39 | defaultEvent: { 40 | mousedown: 'mousedown' 41 | } 42 | }; 43 | 44 | const getElementPath = function (elem) { 45 | const path = []; 46 | if(elem === null) return path; 47 | path.push(elem); 48 | while(elem.parentNode !== null) { 49 | elem = elem.parentNode; 50 | path.push(elem); 51 | }; 52 | return path; 53 | }; 54 | 55 | export default class Volume extends Base { 56 | constructor (parent, option) { 57 | super(parent); 58 | this.parent.preVolume = 0; 59 | this.option = deepAssign(defaultOption, isObject(option) ? option : {}); 60 | this.init(); 61 | } 62 | 63 | init () { 64 | super.create(); 65 | this.$dom = $(this.$dom); 66 | this.$state = this.$dom.find('chimee-volume-state'); 67 | this.$bar = this.$dom.find('chimee-volume-bar'); 68 | this.$all = this.$dom.find('chimee-volume-bar-all'); 69 | this.$bg = this.$dom.find('chimee-volume-bar-bg'); 70 | this.layout = this.option.layout === 'vertical' ? 'vertical' : 'horizonal'; 71 | 72 | // 判断是否是默认或者用户提供 icon 73 | if(this.option.icon && this.option.icon.mute && this.option.icon.low) { 74 | this.option.icon.high = this.option.icon.high || this.option.icon.low; 75 | this.$mute = this.$dom.find('chimee-volume-state-mute'); 76 | this.$low = this.$dom.find('chimee-volume-state-low'); 77 | this.$high = this.$dom.find('chimee-volume-state-high'); 78 | this.$mute.html(this.option.icon.mute); 79 | this.$low.html(this.option.icon.low); 80 | this.$high.html(this.option.icon.high); 81 | }else if(!this.option.bitmap) { 82 | this.animate = true; 83 | this.$state.html(this.option.animate.icon); 84 | } 85 | 86 | this.$dom.addClass(`chimee-flex-component ${this.layout}`); 87 | this.changeState(); 88 | 89 | this.watch_muted = this.parent.$watch('muted', (val) => { 90 | this.update(); 91 | }); 92 | } 93 | 94 | inited () { 95 | this.update(); 96 | } 97 | 98 | destroy () { 99 | this.watch_muted(); 100 | super.destroy(); 101 | } 102 | 103 | changeState () { 104 | if(this.parent.muted || this.parent.volume === 0) { 105 | this.state = 'mute'; 106 | }else if(this.parent.volume > 0 && this.parent.volume <= 0.5) { 107 | this.state = 'low'; 108 | }else if(this.parent.volume > 0.5 && this.parent.volume <= 1) { 109 | this.state = 'high'; 110 | } 111 | this.$dom.removeClass('mute low high'); 112 | this.$dom.addClass(this.state); 113 | } 114 | 115 | click (e) { 116 | const path = e.path || getElementPath(e.target); 117 | if(path.indexOf(this.$state[0]) !== -1) { 118 | this.stateClick(e); 119 | return 'state'; 120 | }else if(path.indexOf(this.$bar[0]) !== -1) { 121 | this.barClick(e); 122 | return 'bar'; 123 | } 124 | return 'padding'; 125 | } 126 | 127 | stateClick () { 128 | if(this.parent.muted) { 129 | this.parent.muted = false; 130 | return; 131 | } 132 | const currentVolume = this.parent.volume; 133 | this.parent.volume = currentVolume === 0 ? this.parent.preVolume : 0; 134 | this.parent.preVolume = currentVolume; 135 | this.changeState(); 136 | } 137 | 138 | barClick (e) { 139 | const volume = this.layout === 'vertical' 140 | ? 1 - e.offsetY / this.$bg[0].offsetHeight 141 | : e.offsetX / this.$bg[0].offsetWidth; 142 | this.parent.volume = volume < 0 ? 0 : volume > 1 ? 1 : volume; 143 | this.update(); 144 | } 145 | 146 | mousedown (e) { 147 | if(this.click(e) !== 'bar') return; 148 | this.startX = this.layout === 'vertical' ? e.clientY : e.clientX; 149 | this.startVolume = this.parent.volume; 150 | addEvent(window, 'mousemove', this.draging); 151 | addEvent(window, 'mouseup', this.dragEnd); 152 | addEvent(window, 'contextmenu', this.dragEnd); 153 | } 154 | 155 | /** 156 | * 更新声音条 157 | */ 158 | update () { 159 | this.changeState(); 160 | const volume = this.parent.muted ? 0 : this.parent.volume; 161 | this.layout === 'vertical' ? this.$all.css('height', `${volume * 100}%`) : this.$all.css('width', `${volume * 100}%`); 162 | } 163 | 164 | /** 165 | * 开始拖拽 166 | * @param {EventObject} e 鼠标事件 167 | */ 168 | @autobind 169 | draging (e) { 170 | this.endX = this.layout === 'vertical' ? e.clientY : e.clientX; 171 | const dragVolume = this.layout === 'vertical' ? (this.startX - this.endX) / this.$bg[0].offsetHeight : (this.endX - this.startX) / this.$bg[0].offsetWidth; 172 | const dragAfterVolume = +(this.startVolume + dragVolume).toFixed(2); 173 | this.parent.volume = dragAfterVolume < 0 ? 0 : dragAfterVolume > 1 ? 1 : dragAfterVolume; 174 | this.parent.muted = false; 175 | } 176 | 177 | /** 178 | * 结束拖拽 179 | */ 180 | @autobind 181 | dragEnd () { 182 | this.startX = 0; 183 | this.startVolume = 0; 184 | removeEvent(window, 'mousemove', this.draging); 185 | removeEvent(window, 'mouseup', this.dragEnd); 186 | removeEvent(window, 'contextmenu', this.dragEnd); 187 | } 188 | } 189 | --------------------------------------------------------------------------------