Repository: jinhuiWong/vux-2.0 Branch: master Commit: e4d51274ed98 Files: 385 Total size: 953.4 KB Directory structure: gitextract_ozu3psze/ ├── .babelrc ├── .editorconfig ├── .gitignore ├── .npmrc ├── LICENSE ├── README.md ├── build/ │ ├── build.js │ ├── check-versions.js │ ├── dev-client.js │ ├── dev-server.js │ ├── utils.js │ ├── webpack.base.conf.js │ ├── webpack.dev.conf.js │ └── webpack.prod.conf.js ├── config/ │ ├── dev.env.js │ ├── index.js │ ├── prod.env.js │ └── test.env.js ├── index.html ├── package.json ├── src/ │ ├── App.vue │ ├── Home.vue │ ├── Wechat.vue │ ├── components/ │ │ ├── actionsheet/ │ │ │ ├── component.json │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── alert/ │ │ │ ├── component.json │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── badge/ │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── blur/ │ │ │ ├── blur.js │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── box/ │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── button-tab/ │ │ │ ├── button-tab-item.vue │ │ │ ├── button-tab.vue │ │ │ ├── index.js │ │ │ └── metas.yml │ │ ├── calendar/ │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── card/ │ │ │ ├── component.json │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── cell/ │ │ │ ├── component.json │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── check-icon/ │ │ │ └── index.vue │ │ ├── checker/ │ │ │ ├── checker-item.vue │ │ │ ├── checker.vue │ │ │ ├── index.js │ │ │ └── metas.yml │ │ ├── checklist/ │ │ │ ├── component.json │ │ │ ├── index.vue │ │ │ ├── metas.yml │ │ │ └── object-filter.js │ │ ├── circle/ │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── clocker/ │ │ │ ├── clocker.js │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── color-picker/ │ │ │ └── index.vue │ │ ├── confirm/ │ │ │ ├── component.json │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── countdown/ │ │ │ └── index.vue │ │ ├── countup/ │ │ │ └── index.vue │ │ ├── datetime/ │ │ │ ├── datetimepicker.js │ │ │ ├── format.js │ │ │ ├── index.vue │ │ │ └── util.js │ │ ├── dev-tip/ │ │ │ └── index.vue │ │ ├── dialog/ │ │ │ ├── component.json │ │ │ └── index.vue │ │ ├── divider/ │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── flexbox/ │ │ │ ├── flexbox-item.vue │ │ │ ├── flexbox.vue │ │ │ └── index.js │ │ ├── fullpage/ │ │ │ ├── DemoBasic.vue │ │ │ ├── index.vue │ │ │ └── lib.js │ │ ├── group/ │ │ │ ├── component.json │ │ │ ├── index.md │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── group-title/ │ │ │ └── index.vue │ │ ├── icon/ │ │ │ ├── component.json │ │ │ ├── index.md │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── index.js │ │ ├── inline-calendar/ │ │ │ ├── index.vue │ │ │ ├── metas.yml │ │ │ ├── props.js │ │ │ └── util.js │ │ ├── inline-desc/ │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── inline-x-number/ │ │ │ └── index.vue │ │ ├── loading/ │ │ │ ├── component.json │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── masker/ │ │ │ ├── converter.js │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── number-roller/ │ │ │ ├── index.vue │ │ │ └── lib.js │ │ ├── orientation/ │ │ │ ├── index.js │ │ │ └── orientation.js │ │ ├── panel/ │ │ │ ├── component.json │ │ │ └── index.vue │ │ ├── picker/ │ │ │ ├── animate.js │ │ │ ├── chain.js │ │ │ ├── index.vue │ │ │ ├── metas.yml │ │ │ ├── scroller.css │ │ │ ├── scroller.js │ │ │ └── util.js │ │ ├── popover/ │ │ │ ├── DemoIndex.vue │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── popup/ │ │ │ ├── index.vue │ │ │ └── popup.js │ │ ├── popup-picker/ │ │ │ └── index.vue │ │ ├── previewer/ │ │ │ ├── component.json │ │ │ └── index.vue │ │ ├── progress/ │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── qrcode/ │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── radio/ │ │ │ ├── component.json │ │ │ └── index.vue │ │ ├── range/ │ │ │ ├── index.vue │ │ │ ├── powerange.less │ │ │ └── range/ │ │ │ └── lib/ │ │ │ ├── horizontal.js │ │ │ ├── lib/ │ │ │ │ ├── classes.js │ │ │ │ ├── closest-num.js │ │ │ │ ├── closest.js │ │ │ │ ├── delegate.js │ │ │ │ ├── emitter.js │ │ │ │ ├── event.js │ │ │ │ ├── events.js │ │ │ │ ├── indexof.js │ │ │ │ ├── matches-selector.js │ │ │ │ ├── mouse.js │ │ │ │ ├── percentage-calc.js │ │ │ │ ├── query.js │ │ │ │ └── super.js │ │ │ ├── main.js │ │ │ └── powerange.js │ │ ├── rater/ │ │ │ └── index.vue │ │ ├── scroller/ │ │ │ └── index.vue │ │ ├── search/ │ │ │ └── index.vue │ │ ├── selector/ │ │ │ ├── component.json │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── shake/ │ │ │ └── index.vue │ │ ├── spinner/ │ │ │ ├── index.vue │ │ │ ├── requestAnimationFrame.js │ │ │ └── spinner.js │ │ ├── step/ │ │ │ ├── index.js │ │ │ ├── step-item.vue │ │ │ └── step.vue │ │ ├── sticky/ │ │ │ ├── index.vue │ │ │ └── sticky.js │ │ ├── swiper/ │ │ │ ├── index.vue │ │ │ └── swiper.js │ │ ├── swiper-item/ │ │ │ └── index.vue │ │ ├── tab/ │ │ │ ├── index.js │ │ │ ├── tab-item.vue │ │ │ └── tab.vue │ │ ├── tabbar/ │ │ │ ├── component.json │ │ │ ├── index.js │ │ │ ├── metas.yml │ │ │ ├── tabbar-item.vue │ │ │ └── tabbar.vue │ │ ├── timeline/ │ │ │ ├── index.js │ │ │ ├── timeline-item.vue │ │ │ └── timeline.vue │ │ ├── tip/ │ │ │ └── index.vue │ │ ├── toast/ │ │ │ ├── component.json │ │ │ └── index.vue │ │ ├── video/ │ │ │ ├── index.vue │ │ │ ├── zy.media.css │ │ │ └── zy.media.js │ │ ├── view-box/ │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── wechat-emotion/ │ │ │ └── index.vue │ │ ├── x-address/ │ │ │ ├── data/ │ │ │ │ └── compress.js │ │ │ ├── index.vue │ │ │ ├── list.json │ │ │ └── metas.yml │ │ ├── x-button/ │ │ │ ├── component.json │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── x-header/ │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ ├── x-img/ │ │ │ └── index.vue │ │ ├── x-input/ │ │ │ └── index.vue │ │ ├── x-number/ │ │ │ └── index.vue │ │ ├── x-switch/ │ │ │ ├── index.vue │ │ │ └── metas.yml │ │ └── x-textarea/ │ │ ├── component.json │ │ ├── index.vue │ │ └── metas.yml │ ├── demos/ │ │ ├── 1px.vue │ │ ├── Actionsheet.vue │ │ ├── Address.vue │ │ ├── Alert.vue │ │ ├── Badge.vue │ │ ├── Blur.vue │ │ ├── Button-tab.vue │ │ ├── Calendar.vue │ │ ├── Card.vue │ │ ├── Cell.vue │ │ ├── Center.vue │ │ ├── Checker.vue │ │ ├── Checklist.vue │ │ ├── Circle.vue │ │ ├── Clocker.vue │ │ ├── Close.vue │ │ ├── Color-picker.vue │ │ ├── Comment.vue │ │ ├── Confirm.vue │ │ ├── Countdown.vue │ │ ├── Countup.vue │ │ ├── Date-formatter.vue │ │ ├── Datetime.vue │ │ ├── Demo.vue │ │ ├── Device.vue │ │ ├── Dialog.vue │ │ ├── Divider.vue │ │ ├── Donate.vue │ │ ├── Flexbox.vue │ │ ├── Icon-loading.vue │ │ ├── Icon.vue │ │ ├── Inline-calendar-start-date.vue │ │ ├── Inline-calendar.vue │ │ ├── Input.vue │ │ ├── Inview.vue │ │ ├── Issue189.vue │ │ ├── Issue414.vue │ │ ├── Issue461.vue │ │ ├── Loading.vue │ │ ├── Masker.vue │ │ ├── Number-roller.vue │ │ ├── Number.vue │ │ ├── Orientation.vue │ │ ├── Panel.vue │ │ ├── Picker.vue │ │ ├── Popup-picker.vue │ │ ├── Popup.vue │ │ ├── Previewer.vue │ │ ├── Progress.vue │ │ ├── Pulldown.vue │ │ ├── PulldownPullup.vue │ │ ├── Pullup.vue │ │ ├── Qrcode.vue │ │ ├── Radio.vue │ │ ├── Range.vue │ │ ├── Rater.vue │ │ ├── Reddot.vue │ │ ├── Scroller-full.vue │ │ ├── Scroller-header.vue │ │ ├── Scroller-swiper.vue │ │ ├── Scroller.vue │ │ ├── Search-static.vue │ │ ├── Search.vue │ │ ├── Selector.vue │ │ ├── Shake.vue │ │ ├── Spinner.vue │ │ ├── Step.vue │ │ ├── Sticky.vue │ │ ├── Swiper.vue │ │ ├── Switch.vue │ │ ├── Tab.vue │ │ ├── Tabbar.vue │ │ ├── TabbarLink.vue │ │ ├── Test.vue │ │ ├── Textarea.vue │ │ ├── Thanks.vue │ │ ├── Timeline.vue │ │ ├── Tip.vue │ │ ├── Toast.vue │ │ ├── Wechat-emotion.vue │ │ ├── X-button.vue │ │ ├── X-header.vue │ │ ├── X-img-scroller.vue │ │ ├── XImg.vue │ │ └── style.css │ ├── directives/ │ │ ├── click-outside/ │ │ │ └── index.js │ │ └── inview/ │ │ ├── index.js │ │ └── inview.js │ ├── filters/ │ │ ├── array2String.js │ │ ├── friendly-time.js │ │ ├── name2value.js │ │ └── value2name.js │ ├── libs/ │ │ ├── base.js │ │ ├── eventor.js │ │ ├── mixin_uuid.js │ │ ├── router.js │ │ └── trim.js │ ├── main.js │ ├── mixins/ │ │ └── multi-items.js │ ├── pages/ │ │ └── home/ │ │ └── home.vue │ ├── plugins/ │ │ ├── alert/ │ │ │ └── index.js │ │ ├── device/ │ │ │ └── index.js │ │ └── toast/ │ │ └── index.js │ ├── routers/ │ │ └── router.js │ ├── styles/ │ │ ├── 1px.less │ │ ├── center.less │ │ ├── close.less │ │ ├── index.less │ │ ├── index.vue │ │ ├── loading.less │ │ ├── reddot.less │ │ ├── reset.less │ │ ├── tap.less │ │ ├── transition.less │ │ ├── variable.less │ │ └── weui/ │ │ ├── base/ │ │ │ ├── fn.less │ │ │ ├── mixin/ │ │ │ │ ├── mobile.less │ │ │ │ ├── setArrow.less │ │ │ │ ├── setChecked.less │ │ │ │ ├── setOnepx.less │ │ │ │ └── text.less │ │ │ ├── reset.less │ │ │ └── variable/ │ │ │ ├── global.less │ │ │ ├── monokai.less │ │ │ ├── weui_button.less │ │ │ ├── weui_cell.less │ │ │ ├── weui_grid.less │ │ │ ├── weui_msg.less │ │ │ └── weui_progress.less │ │ ├── icon/ │ │ │ ├── weui_font.less │ │ │ └── weui_icon_font.less │ │ ├── weui.less │ │ └── widget/ │ │ ├── weui_button/ │ │ │ ├── weui_btn_default.less │ │ │ ├── weui_btn_disabled.less │ │ │ ├── weui_btn_global.less │ │ │ ├── weui_btn_plain.less │ │ │ ├── weui_btn_primary.less │ │ │ ├── weui_btn_warn.less │ │ │ └── weui_button.less │ │ ├── weui_cell/ │ │ │ ├── weui_access.less │ │ │ ├── weui_cell_global.less │ │ │ ├── weui_check/ │ │ │ │ ├── weui_check_common.less │ │ │ │ ├── weui_checkbox.less │ │ │ │ └── weui_radio.less │ │ │ ├── weui_check.less │ │ │ ├── weui_form/ │ │ │ │ ├── weui_form_common.less │ │ │ │ ├── weui_select.less │ │ │ │ ├── weui_select_after.less │ │ │ │ ├── weui_select_before.less │ │ │ │ └── weui_vcode.less │ │ │ ├── weui_form.less │ │ │ ├── weui_switch.less │ │ │ └── weui_uploader.less │ │ ├── weui_grid/ │ │ │ └── weui_grid.less │ │ ├── weui_media_box/ │ │ │ └── weui_media_box.less │ │ ├── weui_page/ │ │ │ ├── weui_article.less │ │ │ └── weui_msg.less │ │ ├── weui_panel/ │ │ │ └── weui_panel.less │ │ ├── weui_progress/ │ │ │ └── weui_progress.less │ │ ├── weui_searchbar/ │ │ │ └── weui_searchbar.less │ │ ├── weui_tab/ │ │ │ ├── navbar.less │ │ │ ├── tabbar.less │ │ │ ├── weui_tab.less │ │ │ └── weui_tab_tabbar.less │ │ └── weui_tips/ │ │ ├── weui_actionsheet.less │ │ ├── weui_dialog.less │ │ ├── weui_mask.less │ │ └── weui_toast.less │ ├── vuex/ │ │ └── store.js │ └── yi.vue ├── static/ │ └── .gitkeep └── test/ ├── e2e/ │ ├── custom-assertions/ │ │ └── elementCount.js │ ├── nightwatch.conf.js │ ├── runner.js │ └── specs/ │ └── test.js └── unit/ ├── .eslintrc ├── index.js ├── karma.conf.js └── specs/ └── Hello.spec.js ================================================ FILE CONTENTS ================================================ ================================================ FILE: .babelrc ================================================ { "presets": ["es2015", "stage-2"], "plugins": ["transform-runtime"], "comments": false } ================================================ FILE: .editorconfig ================================================ root = true [*] charset = utf-8 indent_style = space indent_size = 2 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true ================================================ FILE: .gitignore ================================================ .DS_Store node_modules/ dist/ npm-debug.log test/unit/coverage test/e2e/reports selenium-debug.log node_modules/ .idea/ ================================================ FILE: .npmrc ================================================ sass_binary_site=https://npm.taobao.org/mirrors/node-sass/ phantomjs_cdnurl=https://npm.taobao.org/mirrors/phantomjs/ registry=https://registry.npm.taobao.org ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2016 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: README.md ================================================ # vux-2.0 根据vux-https://github.com/airyland/vux 修改升级的2.0 ## Docs + [中文文档](https://jinhuiwong.gitbooks.io/vuxx/content/) ## Demo

http://jhwong.cn/vux2
(You can view the demos' codes in /src/demos)

## Use ``` bash # install npm install vuxx #vue-cli项目引用 #在webpack.base.conf.js添加loader { test: /vuxx.src.*?js$/, loader: 'babel' } #使用组件 ``` ## Development Setup ``` bash # install dependencies npm install # serve with hot reload at localhost:8080 npm run dev # build for production with minification npm run build ``` 交流qq群:537322257 ![alt text]( http://og1rlwcj8.bkt.clouddn.com/7f4c4fe1gw1evv8bc0r3tj20go0ghjs3.jpg "Title") ================================================ FILE: build/build.js ================================================ // https://github.com/shelljs/shelljs require('./check-versions')() require('shelljs/global') env.NODE_ENV = 'production' var path = require('path') var config = require('../config') var ora = require('ora') var webpack = require('webpack') var webpackConfig = require('./webpack.prod.conf') console.log( ' Tip:\n' + ' Built files are meant to be served over an HTTP server.\n' + ' Opening index.html over file:// won\'t work.\n' ) var spinner = ora('打包中...') spinner.start() var assetsPath = path.join(config.build.assetsRoot, config.build.assetsSubDirectory) rm('-rf', assetsPath) mkdir('-p', assetsPath) cp('-R', 'static/*', assetsPath) webpack(webpackConfig, function (err, stats) { spinner.stop() if (err) throw err process.stdout.write(stats.toString({ colors: true, modules: false, children: false, chunks: false, chunkModules: false }) + '\n') }) ================================================ FILE: build/check-versions.js ================================================ var semver = require('semver') var chalk = require('chalk') var packageConfig = require('../package.json') var exec = function (cmd) { return require('child_process') .execSync(cmd).toString().trim() } var versionRequirements = [ { name: 'node', currentVersion: semver.clean(process.version), versionRequirement: packageConfig.engines.node }, { name: 'npm', currentVersion: exec('npm --version'), versionRequirement: packageConfig.engines.npm } ] module.exports = function () { var warnings = [] for (var i = 0; i < versionRequirements.length; i++) { var mod = versionRequirements[i] if (!semver.satisfies(mod.currentVersion, mod.versionRequirement)) { warnings.push(mod.name + ': ' + chalk.red(mod.currentVersion) + ' should be ' + chalk.green(mod.versionRequirement) ) } } if (warnings.length) { console.log('') console.log(chalk.yellow('To use this template, you must update following to modules:')) console.log() for (var i = 0; i < warnings.length; i++) { var warning = warnings[i] console.log(' ' + warning) } console.log() process.exit(1) } } ================================================ FILE: build/dev-client.js ================================================ /* eslint-disable */ require('eventsource-polyfill') var hotClient = require('webpack-hot-middleware/client?noInfo=true&reload=true') hotClient.subscribe(function (event) { if (event.action === 'reload') { window.location.reload() } }) ================================================ FILE: build/dev-server.js ================================================ require('./check-versions')(); var config = require('../config'); if (!process.env.NODE_ENV) process.env.NODE_ENV = config.dev.env var path = require('path'); var fs = require('fs'); var http = require('http'); var https = require('https'); var express = require('express'); var webpack = require('webpack'); var opn = require('opn'); var proxyMiddleware = require('http-proxy-middleware') var webpackConfig = process.env.NODE_ENV === 'testing' ? require('./webpack.prod.conf') : require('./webpack.dev.conf') // 定义监听端口 // default port where dev server listens for incoming traffic var port = process.env.PORT || config.dev.port; // Define HTTP proxies to your custom API backend // https://github.com/chimurai/http-proxy-middleware var proxyTable = config.dev.proxyTable; var app = express() var compiler = webpack(webpackConfig); var devMiddleware = require('webpack-dev-middleware')(compiler, { publicPath: webpackConfig.output.publicPath, stats: { colors: true, chunks: false } }); var hotMiddleware = require('webpack-hot-middleware')(compiler); // force page reload when html-webpack-plugin template changes compiler.plugin('compilation', function(compilation) { compilation.plugin('html-webpack-plugin-after-emit', function(data, cb) { hotMiddleware.publish({ action: 'reload' }) cb() }) }); // proxy api requests // Object.keys(proxyTable).forEach(function (context) { // var options = proxyTable[context] // if (typeof options === 'string') { // options = { target: options } // } // app.use(proxyMiddleware(context, options)) // }) // 设置反向代理地址 // Object.keys(proxyTable).forEach(function(context) { // var options = proxyTable[context]; // if (typeof options === 'string') { // options = { target: options, changeOrigin: true } // } // //var apiProxy =proxyMiddleware(context, options); // //console.log(apiProxy); // //app.use('/biz',apiProxy); // app.use(context, proxyMiddleware(options)); // }); // handle fallback for HTML5 history API app.use(require('connect-history-api-fallback')()) // serve webpack bundle output app.use(devMiddleware) // enable hot-reload and state-preserving // compilation error display app.use(hotMiddleware) module.exports = app.listen(port, function(err) { if (err) { console.log(err) return } var uri = 'http://localhost:' + port + config.build.assetsPublicPath console.log('Listening at ' + uri + '\n') opn(uri) }) ================================================ FILE: build/utils.js ================================================ var path = require('path') var config = require('../config') var ExtractTextPlugin = require('extract-text-webpack-plugin') exports.assetsPath = function (_path) { var assetsSubDirectory = process.env.NODE_ENV === 'production' ? config.build.assetsSubDirectory : config.dev.assetsSubDirectory return path.posix.join(assetsSubDirectory, _path) } exports.cssLoaders = function (options) { options = options || {} // generate loader string to be used with extract text plugin function generateLoaders (loaders) { var sourceLoader = loaders.map(function (loader) { var extraParamChar if (/\?/.test(loader)) { loader = loader.replace(/\?/, '-loader?') extraParamChar = '&' } else { loader = loader + '-loader' extraParamChar = '?' } return loader + (options.sourceMap ? extraParamChar + 'sourceMap' : '') }).join('!') // Extract CSS when that option is specified // (which is the case during production build) if (options.extract) { return ExtractTextPlugin.extract('vue-style-loader', sourceLoader) } else { return ['vue-style-loader', sourceLoader].join('!') } } // http://vuejs.github.io/vue-loader/en/configurations/extract-css.html return { css: generateLoaders(['css']), postcss: generateLoaders(['css']), less: generateLoaders(['css', 'less']), sass: generateLoaders(['css', 'sass?indentedSyntax']), scss: generateLoaders(['css', 'sass']), stylus: generateLoaders(['css', 'stylus']), styl: generateLoaders(['css', 'stylus']) } } // Generate loaders for standalone style files (outside of .vue) exports.styleLoaders = function (options) { var output = [] var loaders = exports.cssLoaders(options) for (var extension in loaders) { var loader = loaders[extension] output.push({ test: new RegExp('\\.' + extension + '$'), loader: loader }) } return output } ================================================ FILE: build/webpack.base.conf.js ================================================ var path = require('path') var config = require('../config') var utils = require('./utils') var projectRoot = path.resolve(__dirname, '../') var env = process.env.NODE_ENV // check env & config/index.js to decide weither to enable CSS Sourcemaps for the // various preprocessor loaders added to vue-loader at the end of this file var cssSourceMapDev = (env === 'development' && config.dev.cssSourceMap) var cssSourceMapProd = (env === 'production' && config.build.productionSourceMap) var useCssSourceMap = cssSourceMapDev || cssSourceMapProd module.exports = { entry: { app: './src/main.js', fetch : 'whatwg-fetch' }, //entry: ["whatwg-fetch","./src/main.js"], output: { path: config.build.assetsRoot, publicPath: process.env.NODE_ENV === 'production' ? config.build.assetsPublicPath : config.dev.assetsPublicPath, filename: '[name].js' }, resolve: { extensions: ['', '.js', '.vue'], fallback: [path.join(__dirname, '../node_modules')], alias: { 'vue$': 'vue/dist/vue', 'src': path.resolve(__dirname, '../src'), 'assets': path.resolve(__dirname, '../src/assets'), 'components': path.resolve(__dirname, '../src/components'), 'pages' : path.resolve(__dirname,'../src/pages'), 'store' : path.resolve(__dirname,'../src/store') } }, resolveLoader: { fallback: [path.join(__dirname, '../node_modules')] }, module: { loaders: [ { test: /\.vue$/, loader: 'vue' }, { test: /\.js$/, loader: 'babel', include: projectRoot, exclude: /node_modules/ }, { test: /\.json$/, loader: 'json' }, { test: /\.(png|jpe?g|gif|svg)(\?.*)?$/, loader: 'url', query: { limit: 1, // name: utils.assetsPath('img/[name].[hash:7].[ext]') name: utils.assetsPath('img/[name].[ext]') } }, { test: /\.(woff2?|eot|ttf|otf)(\?.*)?$/, loader: 'url', query: { limit: 10000, name: utils.assetsPath('fonts/[name].[hash:7].[ext]') } } ] }, vue: { loaders: utils.cssLoaders({ sourceMap: useCssSourceMap }), postcss: [ require('autoprefixer')({ browsers: ['last 2 versions'] }) ] } } ================================================ FILE: build/webpack.dev.conf.js ================================================ var config = require('../config') var webpack = require('webpack') var merge = require('webpack-merge') var utils = require('./utils') var baseWebpackConfig = require('./webpack.base.conf') var HtmlWebpackPlugin = require('html-webpack-plugin') // add hot-reload related code to entry chunks Object.keys(baseWebpackConfig.entry).forEach(function (name) { baseWebpackConfig.entry[name] = ['./build/dev-client'].concat(baseWebpackConfig.entry[name]) }) module.exports = merge(baseWebpackConfig, { module: { loaders: utils.styleLoaders({ sourceMap: config.dev.cssSourceMap }) }, // eval-source-map is faster for development devtool: '#eval-source-map', plugins: [ new webpack.DefinePlugin({ 'process.env': config.dev.env }), // https://github.com/glenjamin/webpack-hot-middleware#installation--usage new webpack.optimize.OccurenceOrderPlugin(), new webpack.HotModuleReplacementPlugin(), new webpack.NoErrorsPlugin(), // https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ filename: 'index.html', template: 'index.html', inject: true }) ] }) ================================================ FILE: build/webpack.prod.conf.js ================================================ var path = require('path') var config = require('../config') var utils = require('./utils') var webpack = require('webpack') var merge = require('webpack-merge') var baseWebpackConfig = require('./webpack.base.conf') var ExtractTextPlugin = require('extract-text-webpack-plugin') var HtmlWebpackPlugin = require('html-webpack-plugin') var env = process.env.NODE_ENV === 'testing' ? require('../config/test.env') : config.build.env var webpackConfig = merge(baseWebpackConfig, { module: { loaders: utils.styleLoaders({ sourceMap: config.build.productionSourceMap, extract: true }) }, devtool: config.build.productionSourceMap ? '#source-map' : false, output: { path: config.build.assetsRoot, filename: utils.assetsPath('js/[name].[chunkhash].js'), chunkFilename: utils.assetsPath('js/[id].[chunkhash].js') }, vue: { loaders: utils.cssLoaders({ sourceMap: config.build.productionSourceMap, extract: true // extract: false }) }, plugins: [ // http://vuejs.github.io/vue-loader/en/workflow/production.html new webpack.DefinePlugin({ 'process.env': env }), new webpack.optimize.UglifyJsPlugin({ compress: { warnings: false } }), new webpack.optimize.OccurenceOrderPlugin(), // extract css into its own file new ExtractTextPlugin(utils.assetsPath('css/[name].[contenthash].css')), // generate dist index.html with correct asset hash for caching. // you can customize output by editing /index.html // see https://github.com/ampedandwired/html-webpack-plugin new HtmlWebpackPlugin({ filename: process.env.NODE_ENV === 'testing' ? 'index.html' : config.build.index, template: 'index.html', inject: true, minify: { removeComments: true, collapseWhitespace: true, removeAttributeQuotes: true // more options: // https://github.com/kangax/html-minifier#options-quick-reference }, // necessary to consistently work with multiple chunks via CommonsChunkPlugin chunksSortMode: 'dependency' }), // split vendor js into its own file new webpack.optimize.CommonsChunkPlugin({ name: 'vendor', minChunks: function (module, count) { // any required modules inside node_modules are extracted to vendor return ( module.resource && /\.js$/.test(module.resource) && module.resource.indexOf( path.join(__dirname, '../node_modules') ) === 0 ) } }), // extract webpack runtime and module manifest to its own file in order to // prevent vendor hash from being updated whenever app bundle is updated new webpack.optimize.CommonsChunkPlugin({ name: 'manifest', chunks: ['vendor'] }) ] }) if (config.build.productionGzip) { var CompressionWebpackPlugin = require('compression-webpack-plugin') webpackConfig.plugins.push( new CompressionWebpackPlugin({ asset: '[path].gz[query]', algorithm: 'gzip', test: new RegExp( '\\.(' + config.build.productionGzipExtensions.join('|') + ')$' ), threshold: 10240, minRatio: 0.8 }) ) } module.exports = webpackConfig ================================================ FILE: config/dev.env.js ================================================ var merge = require('webpack-merge') var prodEnv = require('./prod.env') module.exports = merge(prodEnv, { NODE_ENV: '"development"' }) ================================================ FILE: config/index.js ================================================ // see http://vuejs-templates.github.io/webpack for documentation. var path = require('path') module.exports = { build: { env: require('./prod.env'), index: path.resolve(__dirname, '../dist/index.html'), assetsRoot: path.resolve(__dirname, '../dist'), assetsSubDirectory: 'static', assetsPublicPath: '/vux2/', productionSourceMap: true, // Gzip off by default as many popular static hosts such as // Surge or Netlify already gzip all static assets for you. // Before setting to `true`, make sure to: // npm install --save-dev compression-webpack-plugin productionGzip: false, productionGzipExtensions: ['js', 'css'] }, dev: { env: require('./dev.env'), port: 8080, assetsSubDirectory: 'static', assetsPublicPath: '/', //反向代理配置 proxyTable: {}, // CSS Sourcemaps off by default because relative paths are "buggy" // with this option, according to the CSS-Loader README // (https://github.com/webpack/css-loader#sourcemaps) // In our experience, they generally work as expected, // just be aware of this issue when enabling this option. cssSourceMap: false } } ================================================ FILE: config/prod.env.js ================================================ module.exports = { NODE_ENV: '"production"' } ================================================ FILE: config/test.env.js ================================================ var merge = require('webpack-merge') var devEnv = require('./dev.env') module.exports = merge(devEnv, { NODE_ENV: '"testing"' }) ================================================ FILE: index.html ================================================ Vux2.0
================================================ FILE: package.json ================================================ { "name": "vuxx", "version": "0.1.7", "description": "vux2.0 project", "author": "wong <405176029@qq.com>", "repository": { "type": "git", "url": "https://github.com/jinhuiWong/vux-2.0" }, "homepage": "https://github.com/jinhuiWong/vux-2.0", "bugs": { "url": "https://github.com/jinhuiWong/vux-2.0/issues" }, "license": "MIT", "keywords": [ "vux", "vue", "weui", "weex", "vue-components", "web-components", "component", "components", "mobile ui", "framework", "frontend" ], "scripts": { "dev": "node build/dev-server.js", "build": "node build/build.js", "unit": "karma start test/unit/karma.conf.js --single-run", "e2e": "node test/e2e/runner.js", "test": "npm run unit && npm run e2e" }, "dependencies": { "array-filter": "^1.0.0", "array-find": "^1.0.0", "array-from": "^2.1.1", "array-map": "0.0.0", "array-shuffle": "^1.0.1", "autosize": "^3.0.20", "countup": "^1.6.2", "fastclick": "^1.0.6", "less": "^2.7.1", "less-loader": "^2.2.3", "node-sass": "^3.13.0", "object-assign": "^4.1.0", "photoswipe": "^4.1.1", "qr.js": "0.0.0", "sass-loader": "^4.0.2", "shake.js": "^1.2.2", "validator": "^6.2.0", "vue": "^2.0.1", "vue-router": "^2.0.1", "vuex": "^2.0.0", "vuex-router-sync": "^3.0.0", "vux-blazy": "^1.6.4", "vux-xscroll": "^3.1.8", "webp-support": "^1.0.3", "whatwg-fetch": "^1.0.0" }, "devDependencies": { "autoprefixer": "^6.4.0", "babel-core": "^6.0.0", "babel-loader": "^6.0.0", "babel-plugin-component": "^0.5.1", "babel-plugin-transform-runtime": "^6.0.0", "babel-preset-es2015": "^6.0.0", "babel-preset-stage-2": "^6.0.0", "babel-register": "^6.0.0", "chai": "^3.5.0", "chalk": "^1.1.3", "chromedriver": "^2.21.2", "connect-history-api-fallback": "^1.1.0", "cross-spawn": "^4.0.2", "css-loader": "^0.25.0", "eventsource-polyfill": "^0.9.6", "express": "^4.13.3", "extract-text-webpack-plugin": "^1.0.1", "file-loader": "^0.9.0", "function-bind": "^1.0.2", "html-webpack-plugin": "^2.8.1", "http-proxy-middleware": "^0.17.2", "inject-loader": "^2.0.1", "isparta-loader": "^2.0.0", "json-loader": "^0.5.4", "karma": "^1.3.0", "karma-coverage": "^1.1.1", "karma-mocha": "^1.2.0", "karma-phantomjs-launcher": "^1.0.0", "karma-sinon-chai": "^1.2.0", "karma-sourcemap-loader": "^0.3.7", "karma-spec-reporter": "0.0.26", "karma-webpack": "^1.7.0", "less-loader": "^2.2.3", "lolex": "^1.4.0", "mocha": "^3.1.0", "nightwatch": "^0.9.8", "opn": "^4.0.2", "ora": "^0.3.0", "phantomjs-prebuilt": "^2.1.3", "selenium-server": "2.53.1", "semver": "^5.3.0", "shelljs": "^0.7.4", "sinon": "^1.17.3", "sinon-chai": "^2.8.0", "url-loader": "^0.5.7", "vue-loader": "^9.4.0", "vue-style-loader": "^1.0.0", "webpack": "^1.13.2", "webpack-dev-middleware": "^1.8.3", "webpack-hot-middleware": "^2.12.2", "webpack-merge": "^0.14.1" }, "engines": { "node": ">= 4.0.0", "npm": ">= 3.0.0" } } ================================================ FILE: src/App.vue ================================================ ================================================ FILE: src/Home.vue ================================================ ================================================ FILE: src/Wechat.vue ================================================ ================================================ FILE: src/components/actionsheet/component.json ================================================ { "vux": { "is_weui": true } } ================================================ FILE: src/components/actionsheet/index.vue ================================================ ================================================ FILE: src/components/actionsheet/metas.yml ================================================ props: show: en: if show the component zh-CN: 是否显示 show-cancel: en: if show the cancel menu zh-CN: 是否显示取消菜单 cancel-text: en: text of cancel menu zh-CN: 取消菜单文字 menus: en: "menu items, for example: `{menu1: 'some text'}`, menu name with `.noop` will not trigger click events" zh-CN: "菜单项列表,举例:`{menu1: '删除'}`,如果名字上带有`.noop`表明这是纯文本展示,不会触发事件,用于展示描述" events: on-click-menu: en: triggers when clicking on the menu zh-CN: 点击菜单时触发,参数为当前菜单项对象 on-click-menu-{menuName}: en: shortcut event for easily listening, you can listen on `on-click-menu-delete` if you have a menu named `delete` zh-CN: 点击事件的快捷方式, 如果你有一个菜单名字为`delete`, 那么你可以监听 `on-click-menu-delete` on-click-menu-cancel: en: triggers when click on cancel menu zh-CN: 点击取消菜单时触发 ================================================ FILE: src/components/alert/component.json ================================================ { "vux": { "is_weui": true } } ================================================ FILE: src/components/alert/index.vue ================================================ ================================================ FILE: src/components/alert/metas.yml ================================================ extends: - Dialog when: - - en: Show some information that user should pay attention to and should be closed only after user has clicked the close button - zh-CN: 显示一个用户必须注意到并且必须点击按钮确认才能关闭的信息 props: show: en: visibility of the component zh-CN: 是否显示 title: en: title zh-CN: 弹窗标题 button-text: en: button text zh-CN: 按钮文字 mask-transition: en: mask transition zh-CN: 遮罩动画 dialog-transition: en: dialog transition zh-CN: 弹窗主体动画 ================================================ FILE: src/components/badge/index.vue ================================================ ================================================ FILE: src/components/badge/metas.yml ================================================ props: text: en: text of the Badge zh-CN: 显示的文字 ================================================ FILE: src/components/blur/blur.js ================================================ /* Image Blur plugin, author @msurguy Usage: Create a set of elements that follows the following HTML structure:
...
Add the following css: .container { overflow: hidden width: 100% position: relative } .container .bg-blur-overlay { z-index: -1 position: absolute width: 100% height: 100% background-image: url('data:image/svg+xmlbase64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0idXRmLTgiPz4gPHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyI+PGRlZnM+PGxpbmVhckdyYWRpZW50IGlkPSJncmFkIiBncmFkaWVudFVuaXRzPSJvYmplY3RCb3VuZGluZ0JveCIgeDE9IjAuNSIgeTE9IjAuMCIgeDI9IjAuNSIgeTI9IjEuMCI+PHN0b3Agb2Zmc2V0PSI0NiUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC4wOCIvPjxzdG9wIG9mZnNldD0iNTklIiBzdG9wLWNvbG9yPSIjMDAwMDAwIiBzdG9wLW9wYWNpdHk9IjAuMDgiLz48c3RvcCBvZmZzZXQ9IjEwMCUiIHN0b3AtY29sb3I9IiMwMDAwMDAiIHN0b3Atb3BhY2l0eT0iMC45Ii8+PC9saW5lYXJHcmFkaWVudD48L2RlZnM+PHJlY3QgeD0iMCIgeT0iMCIgd2lkdGg9IjEwMCUiIGhlaWdodD0iMTAwJSIgZmlsbD0idXJsKCNncmFkKSIgLz48L3N2Zz4g') background-size: 100% background-image: -webkit-gradient(linear, 50% 0%, 50% 100%, color-stop(46%, rgba(0, 0, 0, 0.08)), color-stop(59%, rgba(0, 0, 0, 0.08)), color-stop(100%, rgba(0, 0, 0, 0.9))) background-image: -moz-linear-gradient(top, rgba(0, 0, 0, 0.08) 46%, rgba(0, 0, 0, 0.08) 59%, rgba(0, 0, 0, 0.9) 100%) background-image: -webkit-linear-gradient(top, rgba(0, 0, 0, 0.08) 46%, rgba(0, 0, 0, 0.08) 59%, rgba(0, 0, 0, 0.9) 100%) background-image: linear-gradient(to bottom, rgba(0, 0, 0, 0.08) 46%, rgba(0, 0, 0, 0.08) 59%, rgba(0, 0, 0, 0.9) 100%) } .container .bg-blur { z-index: -2 opacity: 0 position: absolute width: 100% min-height: 100% height: auto display: block top: 0 left: 0 } .container .content { z-index: 1 } */ import Eventor from '../../libs/eventor' // Random ID generator var randomID = function () { return '_' + Math.random().toString(36).substr(2, 9) } // micro lib that creates SVG elements and adds attributes to it var SVG = { // namespaces svgns: 'http://www.w3.org/2000/svg', xlink: 'http://www.w3.org/1999/xlink', // creating of SVG element createElement (name, attrs) { var element = document.createElementNS(SVG.svgns, name) if (attrs) { SVG.setAttr(element, attrs) } return element }, // setting attributes setAttr (element, attrs) { for (var i in attrs) { if (i === 'href') { // path of an image should be stored as xlink:href attribute element.setAttributeNS(SVG.xlink, i, attrs[i]) } else { // other common attribute element.setAttribute(i, attrs[i]) } } return element } } // backgroundBlur PUBLIC CLASS DEFINITION // ================================ var Blur = function (element, options) { this.internalID = randomID() this.element = element this.width = element.offsetWidth this.height = element.offsetHeight this.element = element this.parent = this.element.parentNode this.options = Object.assign({}, Blur.DEFAULTS, options) this.overlayEl = this.createOverlay() this.blurredImage = null this.attachListeners() this.generateBlurredImage(this.options.url) } Blur.VERSION = '0.0.1' Eventor.mixTo(Blur) Blur.DEFAULTS = { url: '', // URL to the image blurAmount: 10, // Amount of blurrines imageClass: '', // CSS class that will be applied to the image and to the SVG element, overlayClass: '', // CSS class of the element that will overlay the blur image duration: false, // If the image needs to be faded in, how long should that take opacity: 1 // Specify the final opacity } Blur.prototype.setBlurAmount = function (blurAmount) { this.options.blurAmount = blurAmount } Blur.prototype.attachListeners = function () { this.on('ui.blur.loaded', this.fadeIn.bind(this)) this.on('ui.blur.unload', this.fadeOut.bind(this)) } Blur.prototype.fadeIn = function () { } Blur.prototype.fadeOut = function () { } Blur.prototype.generateBlurredImage = function (url) { const previousImage = this.blurredImage this.internalID = randomID() if (previousImage) { previousImage.parentNode.removeChild(previousImage) } this.blurredImage = this.createSVG(url, this.width, this.height) } Blur.prototype.createOverlay = function () { if (this.options.overlayClass && this.options.overlayClass !== '') { const div = document.createElement('div') div.classList.add(this.options.overlayClass) this.parent.insertBefore(div, this.element) return div } return false } Blur.prototype.createSVG = function (url, width, height) { var that = this var svg = SVG.createElement('svg', { // our SVG element xmlns: SVG.svgns, version: '1.1', width: width, height: height, id: 'blurred' + this.internalID, 'class': this.options.imageClass, viewBox: '0 0 ' + width + ' ' + height, preserveAspectRatio: 'none' }) var filterId = 'blur' + this.internalID // id of the filter that is called by image element var filter = SVG.createElement('filter', { // filter id: filterId }) var gaussianBlur = SVG.createElement('feGaussianBlur', { // gaussian blur element 'in': 'SourceGraphic', // "in" is keyword. Opera generates an error if we don't put quotes stdDeviation: this.options.blurAmount // intensity of blur }) var image = SVG.createElement('image', { // The image that uses the filter of blur x: 0, y: 0, width: width, height: height, 'externalResourcesRequired': 'true', href: url, style: 'filter:url(#' + filterId + ')', // filter link preserveAspectRatio: 'none' }) image.addEventListener('load', function () { that.emit('ui.blur.loaded') }, true) image.addEventListener('SVGLoad', function () { that.emit('ui.blur.loaded') }, true) filter.appendChild(gaussianBlur) // adding the element of blur into the element of filter svg.appendChild(filter) // adding the filter into the SVG svg.appendChild(image) // adding an element of an image into the SVG // Ensure that the image is shown after duration + 100 msec in case the SVG load event didn't fire or took too long if (that.options.duration && that.options.duration > 0) { svg.style.opacity = 0 window.setTimeout(function () { if (getStyle(svg, 'opacity') === '0') { svg.style.opacity = 1 } }, this.options.duration + 100) } this.element.insertBefore(svg, this.element.firstChild) return svg } Blur.prototype.createIMG = function (url, width, height) { var that = this var originalImage = this.prependImage(url) var newBlurAmount = ((this.options.blurAmount * 2) > 100) ? 100 : (this.options.blurAmount * 2) // apply special CSS attributes to the image to blur it const styles = { // filter property here the intensity of blur multipied by two is around equal to the intensity in common browsers. filter: 'progid:DXImageTransform.Microsoft.Blur(pixelradius=' + newBlurAmount + ') ', // aligning of the blurred image by vertical and horizontal top: -this.options.blurAmount * 2.5, left: -this.options.blurAmount * 2.5, width: width + (this.options.blurAmount * 2.5), height: height + (this.options.blurAmount * 2.5) } for (var i in styles) { originalImage.style[i] = styles[i] } originalImage.setAttribute('id', this.internalID) originalImage.onload = function () { that.trigger('ui.blur.loaded') } // Ensure that the image is shown after duration + 100 msec in case the image load event didn't fire or took too long if (this.options.duration && this.options.duration > 0) { window.setTimeout(function () { if (getStyle(originalImage, 'opacity') === '0') { originalImage.style.opacity = 1 } }, this.options.duration + 100) } return originalImage } Blur.prototype.prependImage = function (url) { const img = document.createElement('img') img.url = url img.setAttribute('id', this.internalID) img.classList.add(this.options.imageClass) if (this.overlayEl) { this.parent.insertBefore(img, this.overlayEl) } else { this.parent.insertBefore(img, this.parent.firstChild) } return img } export default Blur function getStyle (ele, prop) { return window.getComputedStyle(ele, null).getPropertyValue(prop) } ================================================ FILE: src/components/blur/index.vue ================================================ ================================================ FILE: src/components/blur/metas.yml ================================================ tips: - - en: If the component renders slowly on your target platform, you can try `css3 blur filter` - zh-CN: 如果在手机上渲染过慢,可以尝试使用css3的`blur filter` props: blur-amount: en: blur amount of the effect zh-CN: 模糊程度 url: en: url of the image zh-CN: 图片地址 height: en: height of the container zh-CN: 容器高度 slots: default: en: content of the container, above the blur image zh-CN: 容器内容,显示在模糊内容上面 ================================================ FILE: src/components/box/index.vue ================================================ ================================================ FILE: src/components/box/metas.yml ================================================ intro: en: a small component for lazy persons who don't want to write less code zh-CN: 为div设置margin值,懒人才需要的组件 props: gap: en: margin value zh-CN: margin值 ================================================ FILE: src/components/button-tab/button-tab-item.vue ================================================ ================================================ FILE: src/components/button-tab/button-tab.vue ================================================ ================================================ FILE: src/components/button-tab/index.js ================================================ import ButtonTab from './button-tab' import ButtonTabItem from './button-tab-item' export { ButtonTab, ButtonTabItem } ================================================ FILE: src/components/button-tab/metas.yml ================================================ button-tab: props: index: zh-CN: 当前选中索引值,从0开始 height: zh-CN: 高度值 button-tab-item: props: selected: zh-CN: 是否选中 events: on-item-click: zh-CN: 当前按钮点击时触发 ================================================ FILE: src/components/calendar/index.vue ================================================ ================================================ FILE: src/components/calendar/metas.yml ================================================ extends: - inline-calendar - popup - cell intro: zh-CN: 扩展自inline-calendar, 相关属性可查看inline-calendar文档 props: value: zh-CN: 选中值 title: zh-CN: label文字 events: on-change: zh-CN: 值改变时触发 ================================================ FILE: src/components/card/component.json ================================================ { "vux": { "is_weui": true } } ================================================ FILE: src/components/card/index.vue ================================================ ================================================ FILE: src/components/card/metas.yml ================================================ props: header.title: zh-CN: 头部标题,不指定则不显示 footer.title: zh-CN: 底部标题,不指定则不显示 footer.link: zh-CN: 底部链接,普通url或者v-link参数 events: on-click-footer: zh-CN: 点击底部时触发 on-click-header: zh-CN: 点击头部时触发 slots: header: zh-CN: 头部位置 content: zh-CN: 中间主体位置 footer: zh-CN: 底部位置 ================================================ FILE: src/components/cell/component.json ================================================ { "vux": { "is_weui": true } } ================================================ FILE: src/components/cell/index.vue ================================================ ================================================ FILE: src/components/cell/metas.yml ================================================ props: title: en: label text zh-CN: 左边标题文字 value: en: right text zh-CN: 右侧文字,复杂的样式布局请使用slot inline-desc: en: the text below title zh-CN: 标题下面文字,一般为说明文字 is-link: en: if this is a link, if true, there will be an arrow on the right zh-CN: 是否为链接,如果是,右侧将会出现指引点击的右箭头 primary: en: "main content area, can be in ['title', 'content']" zh-CN: "可选值为 ['title', 'content'],对应的div会加上weui_cell_primary类名实现内容宽度自适应" slots: default: en: "right area, you can use default slot instead of prop:value so you can use complexed layout" zh-CN: 右侧内容,相比于value的优点是可以用复杂的样式或者调用组件 value: en: "[deprecated] the same as default slot" zh-CN: "[废弃] 同默认slot" icon: en: icon area before title zh-CN: 标题左侧的图像位置 after-title: en: after title zh-CN: 标题右侧位置 child: en: the child element of the cell, you can add an element with absolute position zh-CN: cell的直接子元素,因此可以添加一个相对于cell绝对定位的元素 ================================================ FILE: src/components/check-icon/index.vue ================================================ ================================================ FILE: src/components/checker/checker-item.vue ================================================ ================================================ FILE: src/components/checker/checker.vue ================================================ ================================================ FILE: src/components/checker/index.js ================================================ import Checker from './checker' import CheckerItem from './checker-item' export { Checker, CheckerItem } ================================================ FILE: src/components/checker/metas.yml ================================================ checker: props: default-item-class: zh-CN: 默认class selected-item-class: zh-CN: 选中样式 disabled-item-class: zh-CN: 不可选样式 type: zh-CN: 类型,单选为radio, 多选为checkbox value: zh-CN: 表单值 max: zh-CN: 最多可选个数,多选时可用 events: on-change: zh-CN: value值变化时触发 checker-item: props: value: zh-CN: 当前项的值 disabled: zh-CN: 是否为不可选 events: on-item-click: zh-CN: 当前项被点击时触发,参数为(itemValue, itemDisabled) ================================================ FILE: src/components/checklist/component.json ================================================ { "vux": { "is_weui": true } } ================================================ FILE: src/components/checklist/index.vue ================================================ ================================================ FILE: src/components/checklist/metas.yml ================================================ props: value: zh-CN: 表单值 title: zh-CN: 标题 required: zh-CN: 是否为必填 options: zh-CN: 选项列表,可以为`[{name:'name',value:'value'}]`的形式 max: zh-CN: 最多可选个数 min: zh-CN: 最少可选个数 fill-mode: zh-CN: 是否允许填写值 random-order: zh-CN: 是否随机打乱选项顺序 events: on-change: zh-CN: 值变化时触发,参数为 (value) ================================================ FILE: src/components/checklist/object-filter.js ================================================ export const getValue = function (item) { return typeof item === 'object' ? item.value : item } export const getKey = function (item) { return typeof item === 'object' ? item.key : item } ================================================ FILE: src/components/circle/index.vue ================================================ ================================================ FILE: src/components/circle/metas.yml ================================================ props: stroke-width: zh-CN: 线条宽度 stoke-color: zh-CN: 线条颜色 trail-width: zh-CN: 背景线条宽度 trail-color: zh-CN: 背景线条颜色 percent: zh-CN: 进度百分比 ================================================ FILE: src/components/clocker/clocker.js ================================================ // https://github.com/MoeKit/clocker var instances = [] var matchers = [] // Miliseconds matchers.push(/^[0-9]*$/.source) // Month/Day/Year [hours:minutes:seconds] matchers.push(/([0-9]{1,2}\/){2}[0-9]{4}( [0-9]{1,2}(:[0-9]{2}){2})?/ .source) // Year/Day/Month [hours:minutes:seconds] and // Year-Day-Month [hours:minutes:seconds] matchers.push(/[0-9]{4}([\/\-][0-9]{1,2}){2}( [0-9]{1,2}(:[0-9]{2}){2})?/ .source) // Cast the matchers to a regular expression object matchers = new RegExp(matchers.join('|')) // Parse a Date formatted has String to a native object function parseDateString (dateString) { // Pass through when a native object is sent if (dateString instanceof Date) { return dateString } // Caste string to date object if (String(dateString).match(matchers)) { // If looks like a milisecond value cast to number before // final casting (Thanks to @msigley) if (String(dateString).match(/^[0-9]*$/)) { dateString = Number(dateString) } // Replace dashes to slashes if (String(dateString).match(/\-/)) { dateString = String(dateString).replace(/\-/g, '/') } return new Date(dateString) } else { throw new Error('Couldn\'t cast `' + dateString + '` to a date object.') } } // Map to convert from a directive to offset object property var DIRECTIVE_KEY_MAP = { 'Y': 'years', 'm': 'months', 'w': 'weeks', 'D': 'days', 'H': 'hours', 'M': 'minutes', 'S': 'seconds' } // Returns an escaped regexp from the string function escapedRegExp (str) { var sanitize = str.toString().replace(/([.?*+^$[\]\\(){}|-])/g, '\\$1') return new RegExp(sanitize) } // Time string formatter function strftime (offsetObject) { return function (format) { var directives = format.match(/%(-|!)?[A-Z]{1}(:[^]+)?/gi) if (directives) { for (var i = 0, len = directives.length; i < len; ++i) { var directive = directives[i].match(/%(-|!)?([a-zA-Z]{1})(:[^]+)?/) var regexp = escapedRegExp(directive[0]) var modifier = directive[1] || '' var plural = directive[3] || '' var value = null // Get the key directive = directive[2] // Swap shot-versions directives if (DIRECTIVE_KEY_MAP.hasOwnProperty(directive)) { value = DIRECTIVE_KEY_MAP[directive] value = Number(offsetObject[value]) } if (value !== null) { // Pluralize if (modifier === '!') { value = pluralize(plural, value) } // Add zero-padding if (modifier === '') { if (value < 10) { value = '0' + value.toString() } } // Replace the directive format = format.replace(regexp, value.toString()) } } } format = format.replace('%_M1', offsetObject.minutes_1) .replace('%_M2', offsetObject.minutes_2) .replace('%_S1', offsetObject.seconds_1) .replace('%_S2', offsetObject.seconds_2) .replace('%_H1', offsetObject.hours_1) .replace('%_H2', offsetObject.hours_2) .replace('%_D1', offsetObject.days_1) .replace('%_D2', offsetObject.days_2) format = format.replace(/%%/, '%') return format } } // Pluralize function pluralize (format, count) { var plural = 's' var singular = '' if (format) { format = format.replace(/(:||\s)/gi, '').split(/,/) if (format.length === 1) { plural = format[0] } else { singular = format[0] plural = format[1] } } if (Math.abs(count) === 1) { return singular } else { return plural } } function splitNumber (number) { number = number + '' number = (number.length === 1 ? ('0' + number) : number) + '' return number.split('') } // The Final Countdown var Countdown = function (finalDate, option) { option = option || {} this.PRECISION = option.precision || 100 // 0.1 seconds, used to update the DOM this.interval = null this.offset = {} // Register this instance this.instanceNumber = instances.length instances.push(this) // Set the final date and start this.setFinalDate(finalDate) } var Eventor = require('../../libs/eventor') Eventor.mixTo(Countdown) var pro = Countdown.prototype var fns = { start () { if (this.interval !== null) { clearInterval(this.interval) } var self = this this.update() this.interval = setInterval(function () { self.update() }, this.PRECISION) return this }, stop () { clearInterval(this.interval) this.interval = null this._dispatchEvent('stoped') return this }, toggle () { if (this.interval) { this.stop() } else { this.start() } return this }, pause () { return this.stop() }, resume () { return this.start() }, remove () { this.stop() instances[this.instanceNumber] = null }, setFinalDate (value) { this.finalDate = parseDateString(value) // Cast the given date return this }, getOffset () { this.totalSecsLeft = this.finalDate.getTime() - new Date().getTime() // In miliseconds this.totalSecsLeft = Math.ceil(this.totalSecsLeft / 1000) this.totalSecsLeft = this.totalSecsLeft < 0 ? 0 : this.totalSecsLeft // Calculate the offsets return { seconds: this.totalSecsLeft % 60, minutes: Math.floor(this.totalSecsLeft / 60) % 60, hours: Math.floor(this.totalSecsLeft / 60 / 60) % 24, days: Math.floor(this.totalSecsLeft / 60 / 60 / 24), weeks: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 7), months: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 30), years: Math.floor(this.totalSecsLeft / 60 / 60 / 24 / 365) } }, update () { // Calculate the offsets this.offset = this.getOffset() // split offset only for days, hours, minutes, seconds and two number like 45, do not support 100 var list = ['days', 'hours', 'minutes', 'seconds'] for (var i = 0; i < list.length; i++) { var key = list[i] var numbers = splitNumber(this.offset[key]) this.offset[key + '_1'] = numbers[0] this.offset[key + '_2'] = numbers[1] } // Dispatch an event if (this.totalSecsLeft === 0) { this.stop() this._dispatchEvent('finish') } else { this._dispatchEvent('update') } return this }, _dispatchEvent (eventName) { var event = {} event.finalDate = this.finalDate event.offset = this.offset event.strftime = strftime(this.offset) this.emit(eventName, event) this.emit('tick', event) } } for (var i in fns) { pro[i] = fns[i] } module.exports = Countdown ================================================ FILE: src/components/clocker/index.vue ================================================ ================================================ FILE: src/components/clocker/metas.yml ================================================ props: time: en: the end time zh-CN: 结束时间 format: en: the result format zh-CN: 显示格式 events: on-tick: en: triggers on time ticking zh-CN: 时间计算时触发,但非每1s触发 on-finish: en: triggers on time end zh-CN: 时间结束时触发 slots: default: en: if specified, will be the format for the result zh-CN: 若存在,则作为最终显示出来的结果模板 ================================================ FILE: src/components/color-picker/index.vue ================================================ ================================================ FILE: src/components/confirm/component.json ================================================ { "vux": { "is_weui": true } } ================================================ FILE: src/components/confirm/index.vue ================================================ ================================================ FILE: src/components/confirm/metas.yml ================================================ tags: en: - message - confirm zh-CN: - 消息提示 - 确认 props: show: en: visibility of the component zh-CN: 是否显示 title: en: dialog title zh-CN: 弹窗标题 confirm-text: en: confirm button text zh-CN: 确认按钮文字 cancel-text: en: cancel button text zh-CN: 取消按钮文字 mask-transition: en: mask's transition zh-CN: 遮罩动画 dialog-transition: en: dialog's transition zh-CN: 弹窗动画 slots: default: en: body content of the dialog zh-CN: 弹窗主体内容 events: on-cancel: en: triggers when the cancel button is clicked zh-CN: 点击取消按钮时触发 on-confirm: en: triggers when the confirm button is clicked zh-CN: 点击确定按钮时触发 ================================================ FILE: src/components/countdown/index.vue ================================================ ================================================ FILE: src/components/countup/index.vue ================================================ ================================================ FILE: src/components/datetime/datetimepicker.js ================================================ import Scroller from '../picker/scroller' import { each, trimZero, addZero, getMaxDay, parseRow, parseDate, getElement, toElement, removeElement } from './util' const MASK_TEMPLATE = '
' const TEMPLATE = `
cancel
ok
` var SHOW_ANIMATION_TIME = 100 // ms var SHOW_CONTAINER_TIME = 300 var TYPE_MAP = { year: ['YYYY'], month: ['MM', 'M'], day: ['DD', 'D'], hour: ['HH', 'H'], minute: ['mm', 'm'] } var MASK = null var CURRENT_PICKER var NOW = new Date() var DEFAULT_CONFIG = { template: TEMPLATE, trigger: null, output: null, currentYear: NOW.getFullYear(), currentMonth: NOW.getMonth() + 1, minYear: 2000, maxYear: 2030, yearRow: '{value}', monthRow: '{value}', dayRow: '{value}', hourRow: '{value}', minuteRow: '{value}', format: 'YYYY-MM-DD', value: NOW.getFullYear() + '-' + (NOW.getMonth() + 1) + '-' + NOW.getDate(), onSelect () {}, onConfirm () {}, onClear () {}, onShow () {}, onHide () {}, confirmText: 'ok', clearText: '', cancelText: 'cancel' } function renderScroller (el, data, value, fn) { var scroller = new Scroller(el, { data: data, defaultValue: value, onSelect: fn }) return scroller } function showMask () { if (!MASK) { MASK = toElement(MASK_TEMPLATE) document.body.appendChild(MASK) MASK.addEventListener('click', function () { CURRENT_PICKER && CURRENT_PICKER.hide() }, false) } MASK.style.display = 'block' setTimeout(function () { MASK && (MASK.style.opacity = 0.5) }, 0) } function hideMask () { if (!MASK) { return } MASK.style.opacity = 0 setTimeout(function () { MASK && (MASK.style.display = 'none') // hideMaskTimer = null }, SHOW_ANIMATION_TIME) } function DatetimePicker (config) { var self = this self.config = {} self.value = config.value || '' each(DEFAULT_CONFIG, function (key, val) { self.config[key] = config[key] || val }) var trigger = self.config.trigger if (trigger) { var output = self.config.output || trigger trigger = self.trigger = getElement(trigger) output = self.output = getElement(output) trigger.addEventListener('click', function (e) { e.preventDefault() self.show(self.value) }, false) } } DatetimePicker.prototype = { _show (newValueMap) { var self = this self.container.style.display = 'block' each(TYPE_MAP, function (type) { self[type + 'Scroller'] && self[type + 'Scroller'].select(trimZero(newValueMap[type]), false) }) setTimeout(function () { self.container.style['-webkit-transform'] = 'translateY(0)' self.container.style.transform = 'translateY(0)' }, 0) }, show (value) { var self = this var config = self.config CURRENT_PICKER = self var valueMap = self.valueMap = parseDate(config.format, value || config.value) var newValueMap = {} each(TYPE_MAP, function (type, list) { newValueMap[type] = list.length === 1 ? valueMap[list[0]] : (valueMap[list[0]] || valueMap[list[1]]) }) if (self.container) { self._show(newValueMap) } else { var container = self.container = toElement(config.template) document.body.appendChild(container) self.container.style.display = 'block' container.addEventListener('touchstart', function (e) { // e.preventDefault() }, false) each(TYPE_MAP, function (type) { // 清除格式里没有列 var div = self.find('[data-role=' + type + ']') if (newValueMap[type] === undefined) { removeElement(div) return } var data if (type === 'day') { data = self._makeData(type, trimZero(newValueMap.year), trimZero(newValueMap.month)) } else { data = self._makeData(type) } self[type + 'Scroller'] = renderScroller(div, data, trimZero(newValueMap[type]), function (currentValue) { config.onSelect.call(self, type, currentValue) var currentDay if (!self.dayScroller) { return } if (type === 'year') { var currentMonth = self.monthScroller ? self.monthScroller.value : config.currentMonth currentDay = self.dayScroller.value self._setDayScroller(currentValue, currentMonth, currentDay) } else if (type === 'month') { var currentYear = self.yearScroller ? self.yearScroller.value : config.currentYear currentDay = self.dayScroller.value self._setDayScroller(currentYear, currentValue, currentDay) } }) }) if (!self.renderText) { if (self.config.confirmText) { self.find('[data-role=confirm]').innerText = self.config.confirmText } if (self.config.cancelText) { self.find('[data-role=cancel]').innerText = self.config.cancelText } if (self.config.clearText) { self.find('[data-role=clear]').innerText = self.config.clearText } self.renderText = true } this._show(newValueMap) self.find('[data-role=cancel]').addEventListener('click', function (e) { e.preventDefault() self.hide() }, false) self.find('[data-role=confirm]').addEventListener('click', function (e) { e.preventDefault() self.confirm() }, false) if (self.config.clearText) { self.find('[data-role=clear]').addEventListener('click', function (e) { e.preventDefault() self.clear() }, false) } } showMask() config.onShow.call(self) }, _makeData (type, year, month) { var config = this.config var valueMap = this.valueMap var list = TYPE_MAP[type] var data = [] var min var max if (type === 'year') { min = config.minYear max = config.maxYear } else if (type === 'month') { min = 1 max = 12 } else if (type === 'day') { min = 1 max = getMaxDay(year, month) } else if (type === 'hour') { min = 0 max = 23 } else if (type === 'minute') { min = 0 max = 59 } for (var i = min; i <= max; i++) { var name if (type === 'year') { name = parseRow(config.yearRow, i) } else { var val = valueMap[list[0]] ? addZero(i) : i name = parseRow(config[type + 'Row'], val) } data.push({ name: name, value: i }) } return data }, _setDayScroller (year, month, day) { var self = this var maxDay = getMaxDay(year, month) if (day > maxDay) { day = maxDay } self.dayScroller.destroy() var div = self.find('[data-role=day]') self.dayScroller = renderScroller(div, self._makeData('day', year, month), day, function (currentValue) { self.config.onSelect.call(self, 'day', currentValue) }) }, find (selector) { return this.container.querySelector(selector) }, hide () { var self = this self.container.style.removeProperty('transform') self.container.style.removeProperty('-webkit-transform') setTimeout(function () { self.container.style.display = 'none' }, SHOW_CONTAINER_TIME) hideMask() self.config.onHide.call(self) }, select (type, value) { this[type + 'Scroller'].select(value, false) }, destroy () { var self = this removeElement(MASK) removeElement(self.container) MASK = null self.container = null }, getValue () { var self = this var config = self.config var value = config.format function formatValue (scroller, expr1, expr2) { if (scroller) { var val = scroller.value if (expr1) { value = value.replace(new RegExp(expr1, 'g'), addZero(val)) } if (expr2) { value = value.replace(new RegExp(expr2, 'g'), trimZero(val)) } } } each(TYPE_MAP, function (key, list) { formatValue(self[key + 'Scroller'], list[0], list[1]) }) return value }, confirm () { var self = this var value = self.getValue() this.value = value if (self.config.onConfirm.call(self, value) === false) { return } self.hide() }, clear () { var self = this var value = self.getValue() if (self.config.onClear.call(self, value) === false) { return } self.hide() } } export default DatetimePicker ================================================ FILE: src/components/datetime/format.js ================================================ module.exports = function (date, fmt = 'YYYY-MM-DD HH:mm:ss') { var o = { 'M+': date.getMonth() + 1, 'D+': date.getDate(), 'h+': date.getHours() % 12 === 0 ? 12 : date.getHours() % 12, 'H+': date.getHours(), 'm+': date.getMinutes(), 's+': date.getSeconds(), 'q+': Math.floor((date.getMonth() + 3) / 3), 'S': date.getMilliseconds() } var week = { '0': '/u65e5', '1': '/u4e00', '2': '/u4e8c', '3': '/u4e09', '4': '/u56db', '5': '/u4e94', '6': '/u516d' } if (/(Y+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, (date.getFullYear() + '').substr(4 - RegExp.$1.length)) } if (/(E+)/.test(fmt)) { fmt = fmt.replace(RegExp.$1, ((RegExp.$1.length > 1) ? (RegExp.$1.length > 2 ? '/u661f/u671f' : '/u5468') : '') + week[date.getDay() + '']) } for (var k in o) { if (new RegExp('(' + k + ')').test(fmt)) { fmt = fmt.replace(RegExp.$1, (RegExp.$1.length === 1) ? (o[k]) : (('00' + o[k]).substr(('' + o[k]).length))) } } return fmt } ================================================ FILE: src/components/datetime/index.vue ================================================ ================================================ FILE: src/components/datetime/util.js ================================================ import formater from './format' export function each (obj, fn) { for (var key in obj) { if (obj.hasOwnProperty(key)) { if (fn.call(obj[key], key, obj[key]) === false) { break } } } } export function trimZero (val) { val = String(val) val = val ? parseFloat(val.replace(/^0+/g, '')) : '' val = val || 0 val = val + '' return val } export function addZero (val) { val = String(val) return val.length < 2 ? '0' + val : val } export function isLeapYear (year) { return year % 100 !== 0 && year % 4 === 0 || year % 400 === 0 } export function getMaxDay (year, month) { year = parseFloat(year) month = parseFloat(month) if (month === 2) { return isLeapYear(year) ? 29 : 28 } return [4, 6, 9, 11].indexOf(month) >= 0 ? 30 : 31 } export function parseRow (tmpl, value) { return tmpl.replace(/\{value\}/g, value) } // parse Date String export function parseDate (format, value) { var formatParts = format.split(/[^A-Za-z]+/) var valueParts = value.split(/\D+/) if (formatParts.length !== valueParts.length) { // if it is error date, use current date var date = formater(new Date(), format) valueParts = date.split(/\D+/) } var result = {} for (var i = 0; i < formatParts.length; i++) { if (formatParts[i]) { result[formatParts[i]] = valueParts[i] } } return result } export function getElement (expr) { return (typeof expr === 'string') ? document.querySelector(expr) : expr } export function toElement (html) { var tempContainer = document.createElement('div') tempContainer.innerHTML = html return tempContainer.firstElementChild } export function removeElement (el) { el && el.parentNode.removeChild(el) } ================================================ FILE: src/components/dev-tip/index.vue ================================================ ================================================ FILE: src/components/dialog/component.json ================================================ { "vux": { "is_weui": true } } ================================================ FILE: src/components/dialog/index.vue ================================================ ================================================ FILE: src/components/divider/index.vue ================================================ ================================================ FILE: src/components/divider/metas.yml ================================================ tips: - en: cannot custom line color for the moment zh-CN: 暂时不可定义分割线颜色 slots: default: en: divider title zh-CN: 分隔线标题 references: - http://www.daqianduan.com/4258.html ================================================ FILE: src/components/flexbox/flexbox-item.vue ================================================ ================================================ FILE: src/components/flexbox/flexbox.vue ================================================ ================================================ FILE: src/components/flexbox/index.js ================================================ import Flexbox from './flexbox' import FlexboxItem from './flexbox-item' export { Flexbox, FlexboxItem } ================================================ FILE: src/components/fullpage/DemoBasic.vue ================================================ ================================================ FILE: src/components/fullpage/index.vue ================================================ ================================================ FILE: src/components/fullpage/lib.js ================================================ /*! * zepto.fullpage.js v0.5.0 (https://github.com/yanhaijing/zepto.fullpage) * API https://github.com/yanhaijing/zepto.fullpage/blob/master/doc/api.md * Copyright 2014 yanhaijing. All Rights Reserved * Licensed under MIT (https://github.com/yanhaijing/zepto.fullpage/blob/master/LICENSE) */ const BOX_CLASS = 'vux-fullpage-box' const ANIM_CLASS = 'vux-fullpage-box-anim' const ITEM_CLASS = 'vux-fullpage-item' const DIR_CLASS = 'vux-fullpage-dir' var d = { page: null, start: 0, duration: 500, loop: false, drag: false, dir: 'v', der: 0.1, change: function (data) {}, beforeChange: function (data) {}, afterChange: function (data) {}, orientationchange: function (orientation) {} } function touchmove (e) { e.preventDefault() } function fix (cur, pagesLength, loop) { if (cur < 0) { return loop ? pagesLength - 1 : 0 } if (cur >= pagesLength) { return loop ? 0 : pagesLength - 1 } return cur } function move (ele, dir, dist) { var xPx = '0px' var yPx = '0px' if (dir === 'v') yPx = dist + 'px' else xPx = dist + 'px' ele.style.cssText += ('-webkit-transform : translate3d(' + xPx + ', ' + yPx + ', 0px);' + 'transform : translate3d(' + xPx + ', ' + yPx + ', 0px)') } function init (option) { var o = option || {} for (var key in d) { if (!o.hasOwnProperty(key)) { o[key] = d[key] } } var that = this that.curIndex = -1 that.o = o that.startY = 0 that.movingFlag = false that.ele.classList.add(BOX_CLASS) that.parentEle = that.ele.parentNode var query = o.page if (query && query.indexOf('.') === 0) { query = query.substring(1, query.length) that.pageEles = that.ele.getElementsByClassName(query) } if (!query) { that.pageEles = that.ele.children } for (var i = 0; i < that.pageEles.length; i++) { var pageEle = that.pageEles[i] pageEle.classList.add(ITEM_CLASS) pageEle.classList.add(DIR_CLASS + o.dir) } that.pagesLength = that.pageEles.length that.update() that.initEvent() that.start() } function Fullpage (ele, option) { this.ele = ele init.call(this, option) } Fullpage.prototype.update = function () { let pageEle if (this.o.dir === 'h') { this.width = this.parentEle.offsetWidth for (let i = 0; i < this.pageEles.length; i++) { pageEle = this.pageEles[i] pageEle.style.width = this.width + 'px' } this.ele.style.width = (this.width * this.pagesLength) + 'px' } this.height = this.parentEle.offsetHeight for (let i = 0; i < this.pageEles.length; i++) { pageEle = this.pageEles[i] pageEle.style.height = this.height + 'px' } this.moveTo(this.curIndex < 0 ? this.o.start : this.curIndex) } Fullpage.prototype.initEvent = function () { var that = this var ele = that.ele ele.addEventListener('touchstart', function (e) { if (!that.status) { return 1 } // e.preventDefault() if (that.movingFlag) { return 0 } that.startX = e.targetTouches[0].pageX that.startY = e.targetTouches[0].pageY }) ele.addEventListener('touchend', function (e) { if (!that.status) { return 1 } // e.preventDefault() if (that.movingFlag) { return 0 } var sub = that.o.dir === 'v' ? (e.changedTouches[0].pageY - that.startY) / that.height : (e.changedTouches[0].pageX - that.startX) / that.width var der = (sub > that.o.der || sub < -that.o.der) ? sub > 0 ? -1 : 1 : 0 that.moveTo(that.curIndex + der, true) }) if (that.o.drag) { ele.addEventListener('touchmove', function (e) { if (!that.status) { return 1 } // e.preventDefault() if (that.movingFlag) { that.startX = e.targetTouches[0].pageX that.startY = e.targetTouches[0].pageY return 0 } var y = e.changedTouches[0].pageY - that.startY if ((that.curIndex === 0 && y > 0) || (that.curIndex === that.pagesLength - 1 && y < 0)) y /= 2 var x = e.changedTouches[0].pageX - that.startX if ((that.curIndex === 0 && x > 0) || (that.curIndex === that.pagesLength - 1 && x < 0)) x /= 2 var dist = (that.o.dir === 'v' ? (-that.curIndex * that.height + y) : (-that.curIndex * that.width + x)) ele.classList.remove(ANIM_CLASS) move(ele, that.o.dir, dist) }) } window.addEventListener('orientationchange', function () { if (window.orientation === 180 || window.orientation === 0) { that.o.orientationchange('portrait') } if (window.orientation === 90 || window.orientation === -90) { that.o.orientationchange('landscape') } }, false) window.addEventListener('resize', function () { that.update() }, false) } Fullpage.prototype.holdTouch = function () { document.addEventListener('touchmove', touchmove) } Fullpage.prototype.unholdTouch = function () { document.removeEventListener('touchmove', touchmove) } Fullpage.prototype.start = function () { this.status = 1 this.holdTouch() } Fullpage.prototype.stop = function () { this.status = 0 this.unholdTouch() } Fullpage.prototype.getCurIndex = function () { return this.curIndex } Fullpage.prototype.moveTo = function (next, anim) { var that = this var ele = that.ele var cur = that.curIndex next = fix(next, that.pagesLength, that.o.loop) if (anim) { ele.classList.add(ANIM_CLASS) } else { ele.classList.remove(ANIM_CLASS) } if (next !== cur) { var flag = that.o.beforeChange({ next: next, cur: cur }) // beforeChange return false to stop scrolling if (flag === false) { return 1 } } that.movingFlag = true that.curIndex = next move(ele, that.o.dir, -next * (that.o.dir === 'v' ? that.height : that.width)) if (next !== cur) { that.o.change({ prev: cur, cur: next }) } window.setTimeout(function () { that.movingFlag = false if (next !== cur) { that.o.afterChange({ prev: cur, cur: next }) for (var i = 0; i < that.pageEles.length; i++) { var pageEle = that.pageEles[i] if (i === next) { pageEle.classList.add('cur') } else { pageEle.classList.remove('cur') } } } }, that.o.duration) } Fullpage.prototype.movePrev = function (anim) { this.moveTo(this.curIndex - 1, anim) } Fullpage.prototype.moveNext = function (anim) { this.moveTo(this.curIndex + 1, anim) } export default Fullpage ================================================ FILE: src/components/group/component.json ================================================ { "vux": { "is_weui": true } } ================================================ FILE: src/components/group/index.md ================================================ [zh-CN] `Group`是一个特殊的表单`wrapper`组件,主要用于将表单分组,单个表单元素也算一组。所以常见的`行内`组件都`必须`作为`Group`的子组件。 包括: + Cell + XInput + XTextarea + Switch + Calendar + XNumber + Radio + Address + Datetime + Selector ================================================ FILE: src/components/group/index.vue ================================================ ================================================ FILE: src/components/group/metas.yml ================================================ props: title: en: group title zh-CN:分组标题 titleColor: en: group title's color zh-CN: 分组标题文字颜色 labelWidth: en: common label width for child components zh-CN: 为子元素设定统一label宽度 labelAlign: en: common label text-align for child components zh-CN: 为子元素设定统一对齐方式 labelMarginRight: en: common margin right value for child components zh-CN: 为子元素设定统一的右边margin gutter: en: set the marginTop value when there is no title zh-CN: 设定group的上边距,只能用于没有标题时 slot: default: en: content body for child components zh-CN: 子组件插槽 ================================================ FILE: src/components/group-title/index.vue ================================================ ================================================ FILE: src/components/icon/component.json ================================================ { "vux": { "is_weui": true } } ================================================ FILE: src/components/icon/index.md ================================================ ``` ``` ================================================ FILE: src/components/icon/index.vue ================================================ ================================================ FILE: src/components/icon/metas.yml ================================================ props: type: en: icon name zh-CN: 图标名字,可选值见demo ================================================ FILE: src/components/index.js ================================================ /* only for building vux.css */ import Style from '../styles/index.vue' // eslint-disable-line import Radio from './radio' import DevTip from './dev-tip' import XInput from './x-input' import XNumber from './x-number' import Cell from './cell' import InlineDesc from './inline-desc' import Checklist from './checklist' import XSwitch from './x-switch' import XTextarea from './x-textarea' import Group from './group' import GroupTitle from './group-title' import Box from './box' import Tip from './tip' import Selector from './selector' import XButton from './x-button' import Swiper from './swiper' import SwiperItem from './swiper-item' import Sticky from './sticky' import Picker from './picker' import Datetime from './datetime' import Popup from './popup' import Range from './range' import Actionsheet from './actionsheet' import Clocker from './clocker' import Rater from './rater' import PopupPicker from './popup-picker' import XAddress from './x-address' import Toast from './toast' import Loading from './loading' import Alert from './alert' import Confirm from './confirm' import XProgress from './progress' import XImg from './x-img' import Spinner from './spinner' import Calendar from './calendar' import Icon from './icon' import XCircle from './circle' import ColorPicker from './color-picker' import AddressChinaData from './x-address/list.json' import Divider from './divider' import Blur from './blur' import Countup from './countup' import Scroller from './scroller' import Shake from './shake' import WechatEmotion from './wechat-emotion' import Search from './search' import DateFormatter from './datetime/format' import Masker from './masker' import Countdown from './countdown' import FriendlyTime from '../filters/friendly-time' import XHeader from './x-header' import Panel from './panel' import InlineCalendar from './inline-calendar' import Badge from './badge' import XDialog from './dialog' import Card from './card' import Previewer from './previewer' import NumberRoller from './number-roller' import ViewBox from './view-box' import Popover from './popover' import { ButtonTab, ButtonTabItem } from './button-tab' import { Checker, CheckerItem } from './checker' import { Flexbox, FlexboxItem } from './flexbox' import { Step, StepItem } from './step' import { Timeline, TimelineItem } from './timeline' import { Tabbar, TabbarItem } from './tabbar' import { Tab, TabItem } from './tab' const vux = { Radio, Group, DevTip, XInput, GroupTitle, XNumber, Checklist, XSwitch, Box, Tip, Selector, Cell, InlineDesc, XButton, XTextarea, Flexbox, FlexboxItem, Tab, TabItem, Swiper, SwiperItem, Sticky, Picker, Datetime, Popup, Range, Actionsheet, Clocker, Rater, PopupPicker, XAddress, Toast, Loading, Alert, Confirm, XProgress, XImg, Spinner, Calendar, Icon, XCircle, ColorPicker, AddressChinaData, Divider, Blur, Countup, Scroller, Shake, WechatEmotion, Search, DateFormatter, Masker, Countdown, FriendlyTime, XHeader, Checker, CheckerItem, Timeline, TimelineItem, Step, StepItem, Tabbar, TabbarItem, Panel, ButtonTab, ButtonTabItem, InlineCalendar, Badge, XDialog, Card, Previewer, NumberRoller, ViewBox, Popover } // if (DEV) { // eslint-disable-line // const { getMetas } = require('../../build/build-metas') // const metas = getMetas(vux) // if (window.fetch) { // window.fetch(`http://${window.location.hostname}:8899/api/doc`, { // method: 'POST', // body: JSON.stringify(metas), // headers: { // 'Accept': 'application/json', // 'Content-Type': 'application/json' // } // }) // } // } module.exports = vux ================================================ FILE: src/components/inline-calendar/index.vue ================================================ ================================================ FILE: src/components/inline-calendar/metas.yml ================================================ props: value: en: zh-CN: 当前选中日期,双向绑定,默认为空,即选中当天日期 render-month: en: zh-CN: 指定渲染日期,如 [2018, 8] start-date: en: zh-CN: 起始日期,格式为'YYYY-MM-dd' end-date: en: zh-CN: 结束日期,格式为'YYYY-MM-dd' show-last-month: en: zh-CN: 是否显示上个月的日期 show-next-month: en: zh-CN: 是否显示下个月的日期 highlight-weekend: en: zh-CN: 是否高亮周末 return-six-rows: en: zh-CN: 是否总是渲染6行日期 hide-header: en: zh-CN: 是否隐藏日历头部 hide-week-list: en: zh-CN: 是否隐藏星期列表 replace-text-list: en: zh-CN: "替换列表,可以将默认的日期换成文字,比如今天的日期替换成今,{'TODAY':'今'}" weeks-list: en: zh-CN: 星期列表,从周日开始 custom-slot-fn: en: zh-CN: 用于为特定日期添加额外的html内容,参数为(行index,列index,日期详细属性) render-on-value-change: en: zh-CN: 当日期变化时是否重新渲染日历,如果是渲染了多个日历的话需要设为false disable-past: en: zh-CN: 禁止选择过去的日期,该选项可以与start-date同时使用 disable-future: en: zh-CN: 禁止选择未来的日期,该选项可以end-date同时使用 ================================================ FILE: src/components/inline-calendar/props.js ================================================ export default () => ({ value: { type: String, default: '' }, renderMonth: { type: Array, // [2018, 8] default () { return [null, null] } }, startDate: { type: String }, endDate: { type: String }, showLastMonth: { type: Boolean, default: true }, showNextMonth: { type: Boolean, default: true }, highlightWeekend: { type: Boolean, default: false }, returnSixRows: { type: Boolean, default: true }, hideHeader: { type: Boolean, default: false }, hideWeekList: { type: Boolean, default: false }, replaceTextList: { type: Object, default () { return {} } }, weeksList: { type: Array, default: () => ['Su', 'Mo', 'Tu', 'We', 'Th', 'Fr', 'Sa'] }, customSlotFn: { type: Function, default: () => '' }, renderOnValueChange: { type: Boolean, default: true }, disablePast: { type: Boolean, default: false }, disableFuture: { type: Boolean, default: false } }) ================================================ FILE: src/components/inline-calendar/util.js ================================================ import format from '../datetime/format' export function zero (n) { return n < 10 ? '0' + n : n } export function splitValue (value) { let split = value.split('-') return { year: parseInt(split[0], 10), month: parseInt(split[1], 10) - 1, day: parseInt(split[2], 10) } } export function getPrevTime (year, month) { if (month === 0) { return { month: 11, year: year - 1 } } else { return { year, month: month - 1 } } } export function getNextTime (year, month) { if (month === 11) { return { month: 0, year: year + 1 } } else { return { year, month: month + 1 } } } function getTime (str) { if (typeof str === 'number') { return str } return typeof str === 'string' ? new Date(str.replace(/-/g, '/')).getTime() : str.getTime() } function isBetween (value, start, end) { value = getTime(value) let isGte = start ? value >= getTime(start) : true let isLte = end ? value <= getTime(end) : true return isGte && isLte } export function getDays ({year, month, value, isRange = false, rangeBegin, rangeEnd, returnSixRows = true, disablePast = false, disableFuture = false}) { let today = format(new Date(), 'YYYY-MM-DD') let startOfToday = new Date() startOfToday.setHours(0, 0, 0, 0) let _splitValue = splitValue(value || today) // if year or month is not specified, get them from value if (typeof year !== 'number' || typeof month !== 'number' || month < 0) { year = _splitValue.year month = _splitValue.month } // if disablePast === true if (disablePast) { if (!rangeBegin) { rangeBegin = startOfToday } else { rangeBegin = Math.max(startOfToday.getTime(), getTime(rangeBegin)) } } // if disableFuture === true if (disableFuture) { if (!rangeEnd) { rangeEnd = startOfToday } else { rangeEnd = Math.min(startOfToday.getTime(), getTime(rangeEnd)) } } var firstDayOfMonth = new Date(year, month, 1).getDay() var lastDateOfMonth = new Date(year, month + 1, 0).getDate() var lastDayOfLastMonth = new Date(year, month, 0).getDate() var i var line = 0 var temp = [] for (i = 1; i <= lastDateOfMonth; i++) { var dow = new Date(year, month, i).getDay() // 第一行 if (dow === 0) { temp[line] = [] } else if (i === 1) { temp[line] = [] var k = lastDayOfLastMonth - firstDayOfMonth + 1 for (let j = 0; j < firstDayOfMonth; j++) { let rs = getPrevTime(year, month) temp[line].push({ year: rs.year, month: rs.month, month_str: rs.month + 1, day: k, disabled: true, isLastMonth: true }) k++ } } let _format = format(new Date(year + '/' + (month + 1) + '/' + i), 'YYYY/MM/DD') let options = { year: year, month: month, month_str: month + 1, day: i, isCurrent: value && format(new Date(value), 'YYYY/MM/DD') === _format, disabled: !isBetween(_format, rangeBegin, rangeEnd), isToday: format(new Date(), 'YYYY/MM/DD') === _format } temp[line].push(options) if (dow === 6) { line++ } else if (i === lastDateOfMonth) { let k = 1 for (dow; dow < 6; dow++) { let rs = getNextTime(year, month) temp[line].push({ year: rs.year, month: rs.month, month_str: rs.month + 1, day: k, disabled: true, isNextMonth: true }) k++ } } } if (returnSixRows && temp.length === 5) { let rs = getNextTime(year, month) let start = temp[4][6].isNextMonth ? temp[4][6].day : 0 temp[6] = [] for (let i = 0; i < 7; i++) { temp[6].push({ year: rs.year, month: rs.month, month_str: rs.month + 1, day: ++start, disabled: true, isNextMonth: true }) } } // 2026-02, there is only 4 lines if (returnSixRows && temp.length === 4) { let rs = getNextTime(year, month) let start = 0 temp[5] = [] temp[6] = [] for (let i = 0; i < 7; i++) { temp[5].push({ year: rs.year, month: rs.month, month_str: rs.month + 1, day: ++start, disabled: true, isNextMonth: true }) temp[6].push({ year: rs.year, month: rs.month, month_str: rs.month + 1, day: ++start, disabled: true, isNextMonth: true }) } } return { year: year, month: month, month_str: month + 1, days: temp } } ================================================ FILE: src/components/inline-desc/index.vue ================================================ ================================================ FILE: src/components/inline-desc/metas.yml ================================================ intro: en: it is the child component of cell, you cannot use it alone zh-CN: cell的子组件,不能单独使用 slots: default: en: text content zh-CN: 文字内容 ================================================ FILE: src/components/inline-x-number/index.vue ================================================ ================================================ FILE: src/components/loading/component.json ================================================ { "vux": { "is_weui": true } } ================================================ FILE: src/components/loading/index.vue ================================================ ================================================ FILE: src/components/loading/metas.yml ================================================ props: show: en: visibility of the component zh-CN: 显示状态 text: en: loading text zh-CN: 提示文字 position: en: position, default is `fixed`, you can use `absolute` zh-CN: 定位方式,默认为`fixed`,在100%的布局下用`absolute`可以避免抖动 slots: default: en: content area zh-CN: 提示文字区域 ================================================ FILE: src/components/masker/converter.js ================================================ /*! * HEX <=> RGB Conversion * Copyright(c) 2011 Daniel Lamb * MIT Licensed */ export function toRGB (color) { let num = parseInt(color, 16) return [num >> 16, num >> 8 & 255, num & 255] } export function toHex (red, green, blue) { return ((blue | green << 8 | red << 16) | 1 << 24).toString(16).slice(1) } ================================================ FILE: src/components/masker/index.vue ================================================ ================================================ FILE: src/components/masker/metas.yml ================================================ props: color: en: the mask's color in rgb format, for example, '0, 0, 0' zh-CN: 遮罩颜色,rgb值,'0, 0, 0' opacity: en: the opacity of the mask zh-CN: 遮罩透明度 slots: default: en: content below the mask zh-CN: 背景内容,位于遮罩下方,一般为图片 content: en: content above the mask zh-CN: 遮罩上方内容,一般显示标题消息 ================================================ FILE: src/components/number-roller/index.vue ================================================ ================================================ FILE: src/components/number-roller/lib.js ================================================ const Roller = class { constructor (opts) { this.container = typeof opts.container === 'string' ? document.querySelector(opts.container) : opts.container this.width = opts.width || 1 if (!this.container) { throw Error('no container') } this.container.style.overflow = 'hidden' this.rollHeight = parseInt(getComputedStyle(this.container).height) if (this.rollHeight < 1) { this.container.style.height = '20px' this.rollHeight = 20 } this.setWidth() } roll (n) { var self = this this.number = parseInt(n) + '' if (this.number.length < this.width) { this.number = new Array(this.width - this.number.length + 1).join('0') + this.number } else if (this.number.length > this.width) { this.width = this.number.length this.setWidth() } Array.prototype.forEach.call(this.container.querySelectorAll('.num'), function (item, i) { var currentNum = parseInt(item.querySelector('div:last-child').innerHTML) var goalNum = parseInt(self.number[i]) var gapNum = 0 var gapStr = '' if (currentNum === goalNum) { return } else if (currentNum < goalNum) { gapNum = goalNum - currentNum for (let j = currentNum; j < goalNum + 1; j++) { gapStr += '
' + j + '
' } } else { gapNum = 10 - currentNum + goalNum for (let j = currentNum; j < 10; j++) { gapStr += '
' + j + '
' } for (let j = 0; j < goalNum + 1; j++) { gapStr += '
' + j + '
' } } item.style.cssText += '-webkit-transition-duration:0;s-webkit-transform:translateY(0)' item.innerHTML = gapStr let time = gapNum * (1 / 9) setTimeout(() => { item.style.cssText += '-webkit-transition-duration:' + time + 's;-webkit-transform:translateY(-' + self.rollHeight * gapNum + 'px)' }, 50) }) } setWidth (n) { n = n || this.width var str = '' for (var i = 0; i < n; i++) { str += '
0
' } this.container.innerHTML = str } } export default Roller ================================================ FILE: src/components/orientation/index.js ================================================ /** * v-orientaion="landscape" v-orientaion="portrait" */ import Orientation from './orientation' export default { bind () { const _this = this const value = this.expression const _value = value.toString()[0].toUpperCase() + value.toString().slice(1) if (Orientation['is' + _value]()) { this.el.style.display = 'block' } else { this.el.style.display = 'none' } Orientation.change(function (e) { let info = Orientation.getInfo() _this.el.style.display = info[value] ? 'block' : 'none' }) }, update () { }, unbind () { } } ================================================ FILE: src/components/orientation/orientation.js ================================================ /** * 横竖屏切换监听器 * @author ningzbruc@gmail.com * @gitbub http://gitbub.com/kingback/orientation/ * @date 2014-01-15 * @version 0.0.1 */ // 原生不支持用resize模拟 var ORIENTATION_CHANGE = 'orientationchange' var RESIZE = 'resize' var EVT_ORIENTATION_CHANGE = 'onorientationchange' in window ? ORIENTATION_CHANGE : RESIZE var FUNCTION = 'function' var ON = 'on' var AFTER = 'after' /** * 混入对象属性 * @method merge * @param {Object} s 提供对象 * @return {Object} r 接收对象 * @private */ function merge (s) { var r = {} var k if (s) { for (k in s) { if (s.hasOwnProperty(k)) { r[k] = s[k] } } } return r } /** * 绑定方法上下文 * @method bind * @param {Function} fn * @param {Object} context * @return {Function} * @private */ function bind (fn, context) { return fn.bind ? fn.bind(context) : function () { fn.apply(context, arguments) } } /** * 横竖屏切换监听器 * @constructor Orientation * @class */ function Orientation () { this.init.apply(this, arguments) } Orientation.prototype = { /** * 构造器 * @property constructor * @public */ constructor: Orientation, /** * 初始化 * @method init * @public */ init: function (cfg) { this._cfg = merge({ delay: 400 }, cfg) // 事件订阅数组 this._subs = { on: [], after: [] } // 当前信息 this.info = this.getInfo() // 绑定事件回调上下文 this._onWinOrientationChange = bind(this._onWinOrientationChange, this) // 绑定窗口切换事件 window.addEventListener(EVT_ORIENTATION_CHANGE, this._onWinOrientationChange, false) }, /** * 销毁 * @method destroy * @public */ destroy: function () { window.removeEventListener(EVT_ORIENTATION_CHANGE, this._onWinOrientationChange, false) delete this._subs }, /** * 创建新实例 * @method create * @return {Orientation} Orientation实例对象 * @public */ create: function (cfg) { return new Orientation(cfg) }, /** * 获取横竖屏信息 * @method getInfo * @return {Object} 横竖屏相关信息 * @public */ getInfo: function () { // 90度为横屏 return (EVT_ORIENTATION_CHANGE === ORIENTATION_CHANGE) ? { landscape: (window.orientation === 90 || window.orientation === -90), portrait: (window.orientation === 0 || window.orientation === -180), orientation: window.orientation } : { landscape: window.screen.width > window.screen.height, portrait: window.screen.width <= window.screen.height, orientation: window.screen.width > window.screen.height ? 90 : 0 } }, /** * 是否是横屏 * @method isLandscape * @return {Boolean} * @public */ isLandscape: function () { return this.info.landscape }, /** * 是否是竖屏 * @method isPortrait * @return {Boolean} * @public */ isPortrait: function () { return this.info.portrait }, /** * 添加change事件 * @method change * @param {Function} fn 回调函数 * @param {Boolean} after 时候绑定after事件 * @chainable */ change: function (fn, after) { if (typeof fn === FUNCTION) { this._subs[after ? AFTER : ON].push(fn) } return this }, /** * 触发横竖屏事件 * @method _fireChange * @param {EventFacade} e * @protected */ _fireChange: function (e) { var self = this var info = this.getInfo() var subs = this._subs var i var l // 如果不等于上次方向,则触发 if (info.landscape !== this.info.landscape) { this.info = merge(info) info.originEvent = e info.originType = e.type // 执行on for (i = 0, l = subs.on.length; i < l; i++) { subs.on[i].call(self, e) } // 执行after setTimeout(function () { for (i = 0, l = subs.after.length; i < l; i++) { subs.after[i].call(self, e) } }, 0) } }, /** * 检查旋转是否已经完成 * @method _checkChange * @param {EventFacade} e * @protected */ _checkChange: function (e) { var self = this if (self._cfg.delay) { // iPad打开键盘时旋转比较慢 clearTimeout(this._changeTimer) self._changeTimer = setTimeout(function () { self._fireChange(e) }, self._cfg.delay) } else { self._fireChange(e) } }, /** * 横竖屏事件回调 * @method _onWinOrientationChange * @param {EventFacade} e 事件对象 * @protected */ _onWinOrientationChange: function (e) { if (e.type === RESIZE) { this._fireChange(e) } else { this._checkChange(e) } } } // 返回实例 export default Orientation.prototype.create() ================================================ FILE: src/components/panel/component.json ================================================ { "vux": { "is_weui": true } } ================================================ FILE: src/components/panel/index.vue ================================================ ================================================ FILE: src/components/picker/animate.js ================================================ const time = Date.now || function () { return +new Date() } let running = {} let counter = 1 let desiredFrames = 60 let millisecondsPerSecond = 1000 // http://paulirish.com/2011/requestanimationframe-for-smart-animating/ // http://my.opera.com/emoller/blog/2011/12/20/requestanimationframe-for-smart-er-animating // requestAnimationFrame polyfill by Erik Möller // fixes from Paul Irish and Tino Zijdel ;(function () { var lastTime = 0 var vendors = ['ms', 'moz', 'webkit', 'o'] for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'] window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'] } if (!window.requestAnimationFrame) { window.requestAnimationFrame = function (callback, element) { var currTime = new Date().getTime() var timeToCall = Math.max(0, 16 - (currTime - lastTime)) var id = window.setTimeout(function () { callback(currTime + timeToCall) }, timeToCall) lastTime = currTime + timeToCall return id } } if (!window.cancelAnimationFrame) { window.cancelAnimationFrame = function (id) { clearTimeout(id) } } }()) export default { // A requestAnimationFrame wrapper / polyfill. requestAnimationFrame: (function () { var requestFrame = window.requestAnimationFrame return function (callback, root) { requestFrame(callback, root) } })(), // Stops the given animation. stop (id) { var cleared = running[id] != null if (cleared) { running[id] = null } return cleared }, // Whether the given animation is still running. isRunning (id) { return running[id] != null }, // Start the animation. start (stepCallback, verifyCallback, completedCallback, duration, easingMethod, root) { var _this = this var start = time() var lastFrame = start var percent = 0 var dropCounter = 0 var id = counter++ if (!root) { root = document.body } // Compacting running db automatically every few new animations if (id % 20 === 0) { var newRunning = {} for (var usedId in running) { newRunning[usedId] = true } running = newRunning } // This is the internal step method which is called every few milliseconds var step = function (virtual) { // Normalize virtual value var render = virtual !== true // Get current time var now = time() // Verification is executed before next animation step if (!running[id] || (verifyCallback && !verifyCallback(id))) { running[id] = null completedCallback && completedCallback(desiredFrames - (dropCounter / ((now - start) / millisecondsPerSecond)), id, false) return } // For the current rendering to apply let's update omitted steps in memory. // This is important to bring internal state variables up-to-date with progress in time. if (render) { var droppedFrames = Math.round((now - lastFrame) / (millisecondsPerSecond / desiredFrames)) - 1 for (var j = 0; j < Math.min(droppedFrames, 4); j++) { step(true) dropCounter++ } } // Compute percent value if (duration) { percent = (now - start) / duration if (percent > 1) { percent = 1 } } // Execute step callback, then... var value = easingMethod ? easingMethod(percent) : percent if ((stepCallback(value, now, render) === false || percent === 1) && render) { running[id] = null completedCallback && completedCallback(desiredFrames - (dropCounter / ((now - start) / millisecondsPerSecond)), id, percent === 1 || duration == null) } else if (render) { lastFrame = now _this.requestAnimationFrame(step, root) } } // Mark as running running[id] = true // Init first step _this.requestAnimationFrame(step, root) // Return unique animation ID return id } } ================================================ FILE: src/components/picker/chain.js ================================================ import filter from 'array-filter' const Manager = class { constructor (data, count, fixedColumns) { this.data = data this.count = count if (fixedColumns) { this.fixedColumns = fixedColumns } } getChildren (value) { return filter(this.data, one => { return one.parent === value }) } getFirstColumn () { return filter(this.data, one => { return !one.parent || one.parent === 0 || one.parent === '0' }) } getPure (obj) { return JSON.parse(JSON.stringify(obj)) } getColumns (value) { // check is data contains the values if (value.length > 0) { const matchCount = this.getPure(this.data).filter((item) => { return this.getPure(value).indexOf(item.value) > -1 }).length if (matchCount < this.getPure(value).length) { value = [] } } var datas = [] const max = this.fixedColumns || 8 for (var i = 0; i < max; i++) { if (i === 0) { datas.push(this.getFirstColumn()) } else { // 没有数据时,取得上一级的第一个 if (!value[i]) { if (typeof datas[i - 1][0] === 'undefined') { break } else { const topValue = datas[i - 1][0].value datas.push(this.getChildren(topValue)) } } else { datas.push(this.getChildren(value[i - 1])) } } } const list = datas.filter((item) => { return item.length > 0 }) // correct the column this.count = list.length return list } } export default Manager ================================================ FILE: src/components/picker/index.vue ================================================ ================================================ FILE: src/components/picker/metas.yml ================================================ ================================================ FILE: src/components/picker/scroller.css ================================================ .scroller-component { display: block; position: relative; height: 238px; overflow: hidden; width: 100%; } .scroller-content { position: absolute; left: 0; top: 0; width: 100%; z-index: 1; } .scroller-mask { position: absolute; left: 0; top: 0; height: 100%; margin: 0 auto; width: 100%; z-index: 3; background-image: -webkit-linear-gradient(top, rgba(255,255,255,0.95), rgba(255,255,255,0.6)), -webkit-linear-gradient(bottom, rgba(255,255,255,0.95), rgba(255,255,255,0.6)); background-image: linear-gradient(to bottom, rgba(255,255,255,0.95), rgba(255,255,255,0.6)), linear-gradient(to top, rgba(255,255,255,0.95), rgba(255,255,255,0.6)); background-position: top, bottom; background-size: 100% 102px; background-repeat: no-repeat; } .scroller-item { text-align: center; font-size: 16px; height: 34px; line-height: 34px; color: #000; } .scroller-indicator { width: 100%; height: 34px; position: absolute; left: 0; top: 102px; z-index: 3; background-image: -webkit-linear-gradient(top, #d0d0d0, #d0d0d0, transparent, transparent), -webkit-linear-gradient(bottom, #d0d0d0, #d0d0d0, transparent, transparent); background-image: linear-gradient(to bottom, #d0d0d0, #d0d0d0, transparent, transparent), linear-gradient(to top, #d0d0d0, #d0d0d0, transparent, transparent); background-position: top, bottom; background-size: 100% 1px; background-repeat: no-repeat; } .scroller-item { line-clamp: 1; -webkit-line-clamp: 1; overflow: hidden; text-overflow: ellipsis; } ================================================ FILE: src/components/picker/scroller.js ================================================ /* * Anima Scroller * Based Zynga Scroller (http://github.com/zynga/scroller) * Copyright 2011, Zynga Inc. * Licensed under the MIT License. * https://raw.github.com/zynga/scroller/master/MIT-LICENSE.txt */ const TEMPLATE = `
` import Animate from './animate' import { getElement, getComputedStyle, easeOutCubic, easeInOutCubic } from './util' var Scroller = function (container, options) { var self = this options = options || {} self.options = { itemClass: 'scroller-item', onSelect () {}, defaultValue: 0, data: [] } for (var key in options) { if (options[key] !== undefined) { self.options[key] = options[key] } } self.__container = getElement(container) var tempContainer = document.createElement('div') tempContainer.innerHTML = options.template || TEMPLATE var component = self.__component = tempContainer.querySelector('[data-role=component]') var content = self.__content = component.querySelector('[data-role=content]') var indicator = component.querySelector('[data-role=indicator]') var data = self.options.data var html = '' if (data.length && data[0].constructor === Object) { data.forEach(function (row) { html += '
' + row.name + '
' }) } else { data.forEach(function (val) { html += '
' + val + '
' }) } content.innerHTML = html self.__container.appendChild(component) self.__itemHeight = parseInt(getComputedStyle(indicator, 'height'), 10) self.__callback = options.callback || function (top) { content.style.webkitTransform = 'translate3d(0, ' + (-top) + 'px, 0)' } var rect = component.getBoundingClientRect() self.__clientTop = (rect.top + component.clientTop) || 0 self.__setDimensions(component.clientHeight, content.offsetHeight) if (component.clientHeight === 0) { self.__setDimensions(parseInt(getComputedStyle(component, 'height'), 10), 204) } self.select(self.options.defaultValue, false) component.addEventListener('touchstart', function (e) { if (e.target.tagName.match(/input|textarea|select/i)) { return } e.preventDefault() self.__doTouchStart(e.touches, e.timeStamp) }, false) component.addEventListener('touchmove', function (e) { self.__doTouchMove(e.touches, e.timeStamp) }, false) component.addEventListener('touchend', function (e) { self.__doTouchEnd(e.timeStamp) }, false) } var members = { value: null, __prevValue: null, __isSingleTouch: false, __isTracking: false, __didDecelerationComplete: false, __isGesturing: false, __isDragging: false, __isDecelerating: false, __isAnimating: false, __clientTop: 0, __clientHeight: 0, __contentHeight: 0, __itemHeight: 0, __scrollTop: 0, __minScrollTop: 0, __maxScrollTop: 0, __scheduledTop: 0, __lastTouchTop: null, __lastTouchMove: null, __positions: null, __minDecelerationScrollTop: null, __maxDecelerationScrollTop: null, __decelerationVelocityY: null, __setDimensions (clientHeight, contentHeight) { var self = this self.__clientHeight = clientHeight self.__contentHeight = contentHeight var totalItemCount = self.options.data.length var clientItemCount = Math.round(self.__clientHeight / self.__itemHeight) self.__minScrollTop = -self.__itemHeight * (clientItemCount / 2) self.__maxScrollTop = self.__minScrollTop + totalItemCount * self.__itemHeight - 0.1 }, selectByIndex (index, animate) { var self = this if (index < 0 || index > self.__content.childElementCount - 1) { return } self.__scrollTop = self.__minScrollTop + index * self.__itemHeight self.scrollTo(self.__scrollTop, animate) self.__selectItem(self.__content.children[index]) }, select (value, animate) { var self = this var children = self.__content.children for (var i = 0, len = children.length; i < len; i++) { if (children[i].dataset.value === value) { self.selectByIndex(i, animate) return } } self.selectByIndex(0, animate) }, getValue () { return this.value }, scrollTo (top, animate) { var self = this animate = (animate === undefined) ? true : animate if (self.__isDecelerating) { Animate.stop(self.__isDecelerating) self.__isDecelerating = false } top = Math.round(top / self.__itemHeight) * self.__itemHeight top = Math.max(Math.min(self.__maxScrollTop, top), self.__minScrollTop) if (top === self.__scrollTop || !animate) { self.__publish(top) self.__scrollingComplete() return } self.__publish(top, 250) }, destroy () { this.__component.parentNode && this.__component.parentNode.removeChild(this.__component) }, __selectItem (selectedItem) { var self = this var selectedItemClass = self.options.itemClass + '-selected' var lastSelectedElem = self.__content.querySelector('.' + selectedItemClass) if (lastSelectedElem) { lastSelectedElem.classList.remove(selectedItemClass) } selectedItem.classList.add(selectedItemClass) if (self.value !== null) { self.__prevValue = self.value } self.value = selectedItem.dataset.value }, __scrollingComplete () { var self = this var index = Math.round((self.__scrollTop - self.__minScrollTop - self.__itemHeight / 2) / self.__itemHeight) self.__selectItem(self.__content.children[index]) if (self.__prevValue !== null && self.__prevValue !== self.value) { self.options.onSelect(self.value) } }, __doTouchStart (touches, timeStamp) { var self = this if (touches.length == null) { throw new Error('Invalid touch list: ' + touches) } if (timeStamp instanceof Date) { timeStamp = timeStamp.valueOf() } if (typeof timeStamp !== 'number') { throw new Error('Invalid timestamp value: ' + timeStamp) } self.__interruptedAnimation = true if (self.__isDecelerating) { Animate.stop(self.__isDecelerating) self.__isDecelerating = false self.__interruptedAnimation = true } if (self.__isAnimating) { Animate.stop(self.__isAnimating) self.__isAnimating = false self.__interruptedAnimation = true } // Use center point when dealing with two fingers var currentTouchTop var isSingleTouch = touches.length === 1 if (isSingleTouch) { currentTouchTop = touches[0].pageY } else { currentTouchTop = Math.abs(touches[0].pageY + touches[1].pageY) / 2 } self.__initialTouchTop = currentTouchTop self.__lastTouchTop = currentTouchTop self.__lastTouchMove = timeStamp self.__lastScale = 1 self.__enableScrollY = !isSingleTouch self.__isTracking = true self.__didDecelerationComplete = false self.__isDragging = !isSingleTouch self.__isSingleTouch = isSingleTouch self.__positions = [] }, __doTouchMove (touches, timeStamp, scale) { var self = this if (touches.length == null) { throw new Error('Invalid touch list: ' + touches) } if (timeStamp instanceof Date) { timeStamp = timeStamp.valueOf() } if (typeof timeStamp !== 'number') { throw new Error('Invalid timestamp value: ' + timeStamp) } // Ignore event when tracking is not enabled (event might be outside of element) if (!self.__isTracking) { return } var currentTouchTop // Compute move based around of center of fingers if (touches.length === 2) { currentTouchTop = Math.abs(touches[0].pageY + touches[1].pageY) / 2 } else { currentTouchTop = touches[0].pageY } var positions = self.__positions // Are we already is dragging mode? if (self.__isDragging) { var moveY = currentTouchTop - self.__lastTouchTop var scrollTop = self.__scrollTop if (self.__enableScrollY) { scrollTop -= moveY var minScrollTop = self.__minScrollTop var maxScrollTop = self.__maxScrollTop if (scrollTop > maxScrollTop || scrollTop < minScrollTop) { // Slow down on the edges if (scrollTop > maxScrollTop) { scrollTop = maxScrollTop } else { scrollTop = minScrollTop } } } // Keep list from growing infinitely (holding min 10, max 20 measure points) if (positions.length > 40) { positions.splice(0, 20) } // Track scroll movement for decleration positions.push(scrollTop, timeStamp) // Sync scroll position self.__publish(scrollTop) // Otherwise figure out whether we are switching into dragging mode now. } else { var minimumTrackingForScroll = 0 var minimumTrackingForDrag = 5 var distanceY = Math.abs(currentTouchTop - self.__initialTouchTop) self.__enableScrollY = distanceY >= minimumTrackingForScroll positions.push(self.__scrollTop, timeStamp) self.__isDragging = self.__enableScrollY && (distanceY >= minimumTrackingForDrag) if (self.__isDragging) { self.__interruptedAnimation = false } } // Update last touch positions and time stamp for next event self.__lastTouchTop = currentTouchTop self.__lastTouchMove = timeStamp self.__lastScale = scale }, __doTouchEnd (timeStamp) { var self = this if (timeStamp instanceof Date) { timeStamp = timeStamp.valueOf() } if (typeof timeStamp !== 'number') { throw new Error('Invalid timestamp value: ' + timeStamp) } // Ignore event when tracking is not enabled (no touchstart event on element) // This is required as this listener ('touchmove') sits on the document and not on the element itself. if (!self.__isTracking) { return } // Not touching anymore (when two finger hit the screen there are two touch end events) self.__isTracking = false // Be sure to reset the dragging flag now. Here we also detect whether // the finger has moved fast enough to switch into a deceleration animation. if (self.__isDragging) { // Reset dragging flag self.__isDragging = false // Start deceleration // Verify that the last move detected was in some relevant time frame if (self.__isSingleTouch && (timeStamp - self.__lastTouchMove) <= 100) { // Then figure out what the scroll position was about 100ms ago var positions = self.__positions var endPos = positions.length - 1 var startPos = endPos // Move pointer to position measured 100ms ago for (var i = endPos; i > 0 && positions[i] > (self.__lastTouchMove - 100); i -= 2) { startPos = i } // If start and stop position is identical in a 100ms timeframe, // we cannot compute any useful deceleration. if (startPos !== endPos) { // Compute relative movement between these two points var timeOffset = positions[endPos] - positions[startPos] var movedTop = self.__scrollTop - positions[startPos - 1] // Based on 50ms compute the movement to apply for each render step self.__decelerationVelocityY = movedTop / timeOffset * (1000 / 60) // How much velocity is required to start the deceleration var minVelocityToStartDeceleration = 4 // Verify that we have enough velocity to start deceleration if (Math.abs(self.__decelerationVelocityY) > minVelocityToStartDeceleration) { self.__startDeceleration(timeStamp) } } } } if (!self.__isDecelerating) { self.scrollTo(self.__scrollTop) } // Fully cleanup list self.__positions.length = 0 }, // Applies the scroll position to the content element __publish (top, animationDuration) { var self = this // Remember whether we had an animation, then we try to continue based on the current "drive" of the animation var wasAnimating = self.__isAnimating if (wasAnimating) { Animate.stop(wasAnimating) self.__isAnimating = false } if (animationDuration) { // Keep scheduled positions for scrollBy functionality self.__scheduledTop = top var oldTop = self.__scrollTop var diffTop = top - oldTop var step = function (percent, now, render) { self.__scrollTop = oldTop + (diffTop * percent) // Push values out if (self.__callback) { self.__callback(self.__scrollTop) } } var verify = function (id) { return self.__isAnimating === id } var completed = function (renderedFramesPerSecond, animationId, wasFinished) { if (animationId === self.__isAnimating) { self.__isAnimating = false } if (self.__didDecelerationComplete || wasFinished) { self.__scrollingComplete() } } // When continuing based on previous animation we choose an ease-out animation instead of ease-in-out self.__isAnimating = Animate.start(step, verify, completed, animationDuration, wasAnimating ? easeOutCubic : easeInOutCubic) } else { self.__scheduledTop = self.__scrollTop = top // Push values out if (self.__callback) { self.__callback(top) } } }, // Called when a touch sequence end and the speed of the finger was high enough to switch into deceleration mode. __startDeceleration (timeStamp) { var self = this self.__minDecelerationScrollTop = self.__minScrollTop self.__maxDecelerationScrollTop = self.__maxScrollTop // Wrap class method var step = function (percent, now, render) { self.__stepThroughDeceleration(render) } // How much velocity is required to keep the deceleration running var minVelocityToKeepDecelerating = 0.5 // Detect whether it's still worth to continue animating steps // If we are already slow enough to not being user perceivable anymore, we stop the whole process here. var verify = function () { var shouldContinue = Math.abs(self.__decelerationVelocityY) >= minVelocityToKeepDecelerating if (!shouldContinue) { self.__didDecelerationComplete = true } return shouldContinue } var completed = function (renderedFramesPerSecond, animationId, wasFinished) { self.__isDecelerating = false if (self.__scrollTop <= self.__minScrollTop || self.__scrollTop >= self.__maxScrollTop) { self.scrollTo(self.__scrollTop) return } if (self.__didDecelerationComplete) { self.__scrollingComplete() } } // Start animation and switch on flag self.__isDecelerating = Animate.start(step, verify, completed) }, // Called on every step of the animation __stepThroughDeceleration (render) { var self = this var scrollTop = self.__scrollTop + self.__decelerationVelocityY var scrollTopFixed = Math.max(Math.min(self.__maxDecelerationScrollTop, scrollTop), self.__minDecelerationScrollTop) if (scrollTopFixed !== scrollTop) { scrollTop = scrollTopFixed self.__decelerationVelocityY = 0 } if (Math.abs(self.__decelerationVelocityY) <= 1) { if (Math.abs(scrollTop % self.__itemHeight) < 1) { self.__decelerationVelocityY = 0 } } else { self.__decelerationVelocityY *= 0.95 } self.__publish(scrollTop) } } // Copy over members to prototype for (var key in members) { Scroller.prototype[key] = members[key] } module.exports = Scroller ================================================ FILE: src/components/picker/util.js ================================================ export function getElement (expr) { return (typeof expr === 'string') ? document.querySelector(expr) : expr } export function getComputedStyle (el, key) { var computedStyle = window.getComputedStyle(el) return computedStyle[key] || '' } // Easing Equations (c) 2003 Robert Penner, all rights reserved. // Open source under the BSD License. export function easeOutCubic (pos) { return (Math.pow((pos - 1), 3) + 1) } export function easeInOutCubic (pos) { if ((pos /= 0.5) < 1) { return 0.5 * Math.pow(pos, 3) } return 0.5 * (Math.pow((pos - 2), 3) + 2) } ================================================ FILE: src/components/popover/DemoIndex.vue ================================================ ================================================ FILE: src/components/popover/index.vue ================================================ ================================================ FILE: src/components/popover/metas.yml ================================================ keywords: - popover - float menu - tooltip - 弹窗 - 菜单 props: content: en: content of the popover zh-CN: 弹出窗口内容 placement: en: position of the popover zh-CN: 弹出窗口位置 enum: - top - right - bottom - left gutter: en: the gutter between trigger element and popover arrow zh-CN: 箭头和触发元素之间的距离 slots: default: en: trigger element zh-CN: 触发元素 content: en: content of the popover zh-CN: 弹窗内容 events: on-show: en: triggers when popover shows zh-CN: 弹窗显示时触发 on-hide: en: triggers when popover hides zh-CN: 弹窗隐藏时触发 ================================================ FILE: src/components/popup/index.vue ================================================ ================================================ FILE: src/components/popup/popup.js ================================================ // not a good way but works well window.__$vuxPopups = window.__$vuxPopups || {} const popupDialog = function (option) { this.uuid = Math.random().toString(36).substring(3, 8) this.params = {} this.isShow = false if (Object.prototype.toString.call(option) === '[object Object]') { this.params = { input: option.input || '', container: document.querySelector(option.input) || '', innerHTML: option.innerHTML || '', hideOnBlur: option.hideOnBlur, onOpen: option.onOpen || function () {}, onClose: option.onClose || function () {} } } if (!!document.querySelectorAll('.vux-popup-mask').length <= 0) { this.divMask = document.createElement('a') this.divMask.className = 'vux-popup-mask' this.divMask.dataset.uuid = '' // 用于多个popup共享一个mask this.divMask.href = 'javascript:void(0)' document.body.appendChild(this.divMask) } let div if (!option.container) { div = document.createElement('div') } else { div = option.container } div.className = 'vux-popup-dialog vux-popup-dialog-' + this.uuid if (!option.container) { document.body.appendChild(div) } this.container = document.querySelector('.vux-popup-dialog-' + this.uuid) this.mask = document.querySelector('.vux-popup-mask') this.mask.dataset.uuid += `,${this.uuid}` this._bindEvents() option = null return this } popupDialog.prototype.onClickMask = function () { if (this.params.hideOnBlur && this.isShow) { this.hide(false) } } popupDialog.prototype._bindEvents = function () { this.params.hideOnBlur && this.mask.addEventListener('click', this.onClickMask.bind(this), false) } popupDialog.prototype.show = function () { this.mask.classList.add('vux-popup-show') this.container.classList.add('vux-popup-show') this.params.onOpen && this.params.onOpen(this) this.isShow = true window.__$vuxPopups[this.uuid] = 1 } popupDialog.prototype.hide = function (shouldCallback = true) { this.container.classList.remove('vux-popup-show') if (!document.querySelector('.vux-popup-dialog.vux-popup-show')) { this.mask.classList.remove('vux-popup-show') } shouldCallback === false && this.params.onClose && this.params.hideOnBlur && this.params.onClose(this) this.isShow = false delete window.__$vuxPopups[this.uuid] } popupDialog.prototype.html = function (html) { this.container.innerHTML = html } popupDialog.prototype.destroy = function () { this.mask.dataset.uuid = this.mask.dataset.uuid.replace(new RegExp(`,${this.uuid}`, 'g'), '') if (!this.mask.dataset.uuid) { this.mask.removeEventListener('click', this.onClickMask.bind(this), false) this.mask && this.mask.parentNode && this.mask.parentNode.removeChild(this.mask) } else { this.hide() } delete window.__$vuxPopups[this.uuid] } export default popupDialog ================================================ FILE: src/components/popup-picker/index.vue ================================================ ================================================ FILE: src/components/previewer/component.json ================================================ { "tags": ["swipephoto", "swipe", "preview"] } ================================================ FILE: src/components/previewer/index.vue ================================================ ================================================ FILE: src/components/progress/index.vue ================================================ ================================================ FILE: src/components/progress/metas.yml ================================================ props: percent: en: percent of the progress, 0-100 zh-CN: 进度值,0到100 show-cancel: en: if show the cancel button zh-CN: 是否显示取消按钮 events: on-cancel: en: triggers when the cancel button is clicked zh-CN: 点击取消按钮时触发 ================================================ FILE: src/components/qrcode/index.vue ================================================ ================================================ FILE: src/components/qrcode/metas.yml ================================================ props: value: zh-CN: 编码内容,如果为链接,请保证有http(s)协议名 size: zh-CN: 尺寸大小 bg-color: zh-CN: 背景颜色 fg-color: zh-CN: 二级码着色 ================================================ FILE: src/components/radio/component.json ================================================ { "vux": { "is_weui": true } } ================================================ FILE: src/components/radio/index.vue ================================================ ================================================ FILE: src/components/range/index.vue ================================================ ================================================ FILE: src/components/range/powerange.less ================================================ /** * * Main stylesheet for Powerange. * http://abpetkov.github.io/powerange/ * */ /** * Horizontal slider style (default). */ .range-bar { background-color: @range-color-bar-default; border-radius: 15px; display: block; height: 1px; position: relative; width: 100%; } .range-bar-disabled { opacity: @range-opacity-disabled; } .range-quantity { background-color: @range-color-bar-active; border-radius: 15px; display: block; height: 100%; width: 0; } .range-handle { background-color: #fff; border-radius: 100%; cursor: move; height: 30px; left: 0; top: -13px; position: absolute; width: 30px; box-shadow: 0 1px 3px rgba(0, 0, 0, 0.4); } .range-min, .range-max { color: #181819; font-size: 12px; position: absolute; text-align: center; top: 50%; transform: translateY(-50%); width: 24px; } .range-min { left: -30px; } .range-max { right: -30px; } /** * Style for disabling text selection on handle move. */ .unselectable { user-select: none; } /** * Style for handle cursor on disabled slider. */ .range-disabled { cursor: default; } ================================================ FILE: src/components/range/range/lib/horizontal.js ================================================ /** * External dependencies. * */ var inherits = require('./lib/super') var closest = require('./lib/closest-num') var percentage = require('./lib/percentage-calc') /** * Require main class. */ var Powerange = require('./main') /** * Expose `Horizontal`. */ module.exports = Horizontal /** * Create horizontal slider object. * * @api public */ function getWidth (el) { let width = getComputedStyle(el, null)['width'] if (width === '100%' || width === 'auto') { return 0 } return parseInt(width, 10) } function Horizontal () { Powerange.apply(this, arguments) if (this.options.step) { this.step(getWidth(this.slider) || this.options.initialBarWidth, getWidth(this.handle)) } this.setStart(this.options.start) } /** * Inherit the main class. */ inherits(Horizontal, Powerange) /** * Set horizontal slider position. * * @param {Number} start * @api private */ Horizontal.prototype.setStart = function (start) { var begin = (start === null) ? this.options.min : start var part = percentage.from(begin - this.options.min, this.options.max - this.options.min) || 0 var offset = percentage.of(part, this.slider.offsetWidth - this.handle.offsetWidth) var position = (this.options.step) ? closest.find(offset, this.steps) : offset this.setPosition(position) this.setValue(this.handle.style.left, this.slider.offsetWidth - this.handle.offsetWidth) } /** * Set horizontal slider current position. * * @param {Number} val * @api private */ Horizontal.prototype.setPosition = function (val) { this.handle.style.left = val + 'px' this.slider.querySelector('.range-quantity').style.width = val + 'px' } /** * On slider mouse down. * * @param {Object} e * @api private */ Horizontal.prototype.onmousedown = function (e) { if (e.touches) e = e.touches[0] this.startX = e.clientX this.handleOffsetX = this.handle.offsetLeft this.restrictHandleX = this.slider.offsetWidth - this.handle.offsetWidth this.unselectable(this.slider, true) } /** * On slider mouse move. * * @param {Object} e * @api private */ Horizontal.prototype.onmousemove = function (e) { e.preventDefault() if (e.touches) e = e.touches[0] var leftOffset = this.handleOffsetX + e.clientX - this.startX var position = (this.steps) ? closest.find(leftOffset, this.steps) : leftOffset if (leftOffset <= 0) { this.setPosition(0) } else if (leftOffset >= this.restrictHandleX) { this.setPosition(this.restrictHandleX) } else { this.setPosition(position) } this.setValue(this.handle.style.left, this.slider.offsetWidth - this.handle.offsetWidth) } /** * On mouse up. * * @param {Object} e * @api private */ Horizontal.prototype.onmouseup = function (e) { this.unselectable(this.slider, false) } ================================================ FILE: src/components/range/range/lib/lib/classes.js ================================================ /** * Module dependencies. */ var index = require('./indexof') /** * Whitespace regexp. */ var re = /\s+/ /** * toString reference. */ var toString = Object.prototype.toString /** * Wrap `el` in a `ClassList`. * * @param {Element} el * @return {ClassList} * @api public */ module.exports = function (el) { return new ClassList(el) } /** * Initialize a new ClassList for `el`. * * @param {Element} el * @api private */ function ClassList (el) { if (!el || !el.nodeType) { throw new Error('A DOM element reference is required') } this.el = el this.list = el.classList } /** * Add class `name` if not already present. * * @param {String} name * @return {ClassList} * @api public */ ClassList.prototype.add = function (name) { // classList if (this.list) { this.list.add(name) return this } // fallback var arr = this.array() var i = index(arr, name) if (!~i) arr.push(name) this.el.className = arr.join(' ') return this } /** * Remove class `name` when present, or * pass a regular expression to remove * any which match. * * @param {String|RegExp} name * @return {ClassList} * @api public */ ClassList.prototype.remove = function (name) { if (toString.call(name) === '[object RegExp]') { return this.removeMatching(name) } // classList if (this.list) { this.list.remove(name) return this } // fallback var arr = this.array() var i = index(arr, name) if (~i) arr.splice(i, 1) this.el.className = arr.join(' ') return this } /** * Remove all classes matching `re`. * * @param {RegExp} re * @return {ClassList} * @api private */ ClassList.prototype.removeMatching = function (re) { var arr = this.array() for (var i = 0; i < arr.length; i++) { if (re.test(arr[i])) { this.remove(arr[i]) } } return this } /** * Toggle class `name`, can force state via `force`. * * For browsers that support classList, but do not support `force` yet, * the mistake will be detected and corrected. * * @param {String} name * @param {Boolean} force * @return {ClassList} * @api public */ ClassList.prototype.toggle = function (name, force) { // classList if (this.list) { if (typeof force !== 'undefined') { if (force !== this.list.toggle(name, force)) { this.list.toggle(name) // toggle again to correct } } else { this.list.toggle(name) } return this } // fallback if (typeof force !== 'undefined') { if (!force) { this.remove(name) } else { this.add(name) } } else { if (this.has(name)) { this.remove(name) } else { this.add(name) } } return this } /** * Return an array of classes. * * @return {Array} * @api public */ ClassList.prototype.array = function () { var className = this.el.getAttribute('class') || '' var str = className.replace(/^\s+|\s+$/g, '') var arr = str.split(re) if (arr[0] === '') arr.shift() return arr } /** * Check if class `name` is present. * * @param {String} name * @return {ClassList} * @api public */ ClassList.prototype.has = ClassList.prototype.contains = function (name) { return this.list ? this.list.contains(name) : !!~index(this.array(), name) } ================================================ FILE: src/components/range/range/lib/lib/closest-num.js ================================================ /** * Closest-num 0.0.1 * https://github.com/abpetkov/closest-num * * Author: Alexander Petkov * https://github.com/abpetkov * * Copyright 2014, Alexander Petkov * License: The MIT License (MIT) * http://opensource.org/licenses/MIT * */ /** * Get closest number in array. * * @param {Number} target * @param {Array} points * @returns {Number} closest * @api private */ exports.find = function (target, points) { var diff = null var current = null var closest = points[0] for (var i = 0; i < points.length; i++) { diff = Math.abs(target - closest) current = Math.abs(target - points[i]) if (current < diff) { closest = points[i] } } return closest } ================================================ FILE: src/components/range/range/lib/lib/closest.js ================================================ /** * Module Dependencies */ var matches = require('./matches-selector') /** * Export `closest` */ module.exports = closest /** * Closest * * @param {Element} el * @param {String} selector * @param {Element} scope (optional) */ function closest (el, selector, scope) { scope = scope || document.documentElement // walk up the dom while (el && el !== scope) { if (matches(el, selector)) return el el = el.parentNode } // check scope for match return matches(el, selector) ? el : null } ================================================ FILE: src/components/range/range/lib/lib/delegate.js ================================================ /** * Module dependencies. */ var closest = require('./closest') var event = require('./event') /** * Delegate event `type` to `selector` * and invoke `fn(e)`. A callback function * is returned which may be passed to `.unbind()`. * * @param {Element} el * @param {String} selector * @param {String} type * @param {Function} fn * @param {Boolean} capture * @return {Function} * @api public */ exports.bind = function (el, selector, type, fn, capture) { return event.bind(el, type, function (e) { var target = e.target || e.srcElement e.delegateTarget = closest(target, selector, true, el) if (e.delegateTarget) fn.call(el, e) }, capture) } /** * Unbind event `type`'s callback `fn`. * * @param {Element} el * @param {String} type * @param {Function} fn * @param {Boolean} capture * @api public */ exports.unbind = function (el, type, fn, capture) { event.unbind(el, type, fn, capture) } ================================================ FILE: src/components/range/range/lib/lib/emitter.js ================================================ /** * Expose `Emitter`. */ module.exports = Emitter /** * Initialize a new `Emitter`. * * @api public */ function Emitter (obj) { if (obj) return mixin(obj) } /** * Mixin the emitter properties. * * @param {Object} obj * @return {Object} * @api private */ function mixin (obj) { for (var key in Emitter.prototype) { obj[key] = Emitter.prototype[key] } return obj } /** * Listen on the given `event` with `fn`. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.on = Emitter.prototype.addEventListener = function (event, fn) { this._callbacks = this._callbacks || {} ;(this._callbacks['$' + event] = this._callbacks['$' + event] || []).push(fn) return this } /** * Adds an `event` listener that will be invoked a single * time then automatically removed. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.once = function (event, fn) { function on () { this.off(event, on) fn.apply(this, arguments) } on.fn = fn this.on(event, on) return this } /** * Remove the given callback for `event` or all * registered callbacks. * * @param {String} event * @param {Function} fn * @return {Emitter} * @api public */ Emitter.prototype.off = Emitter.prototype.removeListener = Emitter.prototype.removeAllListeners = Emitter.prototype.removeEventListener = function (event, fn) { this._callbacks = this._callbacks || {} // all if (!arguments.length) { this._callbacks = {} return this } // specific event var callbacks = this._callbacks['$' + event] if (!callbacks) return this // remove all handlers if (arguments.length === 1) { delete this._callbacks['$' + event] return this } // remove specific handler var cb for (var i = 0; i < callbacks.length; i++) { cb = callbacks[i] if (cb === fn || cb.fn === fn) { callbacks.splice(i, 1) break } } return this } /** * Emit `event` with the given args. * * @param {String} event * @param {Mixed} ... * @return {Emitter} */ Emitter.prototype.emit = function (event) { this._callbacks = this._callbacks || {} var args = [].slice.call(arguments, 1) var callbacks = this._callbacks['$' + event] if (callbacks) { callbacks = callbacks.slice(0) for (var i = 0, len = callbacks.length; i < len; ++i) { callbacks[i].apply(this, args) } } return this } /** * Return array of callbacks for `event`. * * @param {String} event * @return {Array} * @api public */ Emitter.prototype.listeners = function (event) { this._callbacks = this._callbacks || {} return this._callbacks['$' + event] || [] } /** * Check if this emitter has `event` handlers. * * @param {String} event * @return {Boolean} * @api public */ Emitter.prototype.hasListeners = function (event) { return !!this.listeners(event).length } ================================================ FILE: src/components/range/range/lib/lib/event.js ================================================ var bind = window.addEventListener ? 'addEventListener' : 'attachEvent' var unbind = window.removeEventListener ? 'removeEventListener' : 'detachEvent' var prefix = bind !== 'addEventListener' ? 'on' : '' /** * Bind `el` event `type` to `fn`. * * @param {Element} el * @param {String} type * @param {Function} fn * @param {Boolean} capture * @return {Function} * @api public */ exports.bind = function (el, type, fn, capture) { el[bind](prefix + type, fn, capture || false) return fn } /** * Unbind `el` event `type`'s callback `fn`. * * @param {Element} el * @param {String} type * @param {Function} fn * @param {Boolean} capture * @return {Function} * @api public */ exports.unbind = function (el, type, fn, capture) { el[unbind](prefix + type, fn, capture || false) return fn } ================================================ FILE: src/components/range/range/lib/lib/events.js ================================================ /** * Module dependencies. */ var events = require('./event') var delegate = require('./delegate') /** * Expose `Events`. */ module.exports = Events /** * Initialize an `Events` with the given * `el` object which events will be bound to, * and the `obj` which will receive method calls. * * @param {Object} el * @param {Object} obj * @api public */ function Events (el, obj) { if (!(this instanceof Events)) return new Events(el, obj) if (!el) throw new Error('element required') if (!obj) throw new Error('object required') this.el = el this.obj = obj this._events = {} } /** * Subscription helper. */ Events.prototype.sub = function (event, method, cb) { this._events[event] = this._events[event] || {} this._events[event][method] = cb } /** * Bind to `event` with optional `method` name. * When `method` is undefined it becomes `event` * with the "on" prefix. * * Examples: * * Direct event handling: * * events.bind('click') // implies "onclick" * events.bind('click', 'remove') * events.bind('click', 'sort', 'asc') * * Delegated event handling: * * events.bind('click li > a') * events.bind('click li > a', 'remove') * events.bind('click a.sort-ascending', 'sort', 'asc') * events.bind('click a.sort-descending', 'sort', 'desc') * * @param {String} event * @param {String|function} [method] * @return {Function} callback * @api public */ Events.prototype.bind = function (event, method) { var e = parse(event) var el = this.el var obj = this.obj var name = e.name method = method || 'on' + name var args = [].slice.call(arguments, 2) // callback var cb = function () { var a = [].slice.call(arguments).concat(args) obj[method].apply(obj, a) } // bind if (e.selector) { cb = delegate.bind(el, e.selector, name, cb) } else { events.bind(el, name, cb) } // subscription for unbinding this.sub(name, method, cb) return cb } /** * Unbind a single binding, all bindings for `event`, * or all bindings within the manager. * * Examples: * * Unbind direct handlers: * * events.unbind('click', 'remove') * events.unbind('click') * events.unbind() * * Unbind delegate handlers: * * events.unbind('click', 'remove') * events.unbind('click') * events.unbind() * * @param {String|Function} [event] * @param {String|Function} [method] * @api public */ Events.prototype.unbind = function (event, method) { if (arguments.length === 0) return this.unbindAll() if (arguments.length === 1) return this.unbindAllOf(event) // no bindings for this event var bindings = this._events[event] if (!bindings) return // no bindings for this method var cb = bindings[method] if (!cb) return events.unbind(this.el, event, cb) } /** * Unbind all events. * * @api private */ Events.prototype.unbindAll = function () { for (var event in this._events) { this.unbindAllOf(event) } } /** * Unbind all events for `event`. * * @param {String} event * @api private */ Events.prototype.unbindAllOf = function (event) { var bindings = this._events[event] if (!bindings) return for (var method in bindings) { this.unbind(event, method) } } /** * Parse `event`. * * @param {String} event * @return {Object} * @api private */ function parse (event) { var parts = event.split(/ +/) return { name: parts.shift(), selector: parts.join(' ') } } ================================================ FILE: src/components/range/range/lib/lib/indexof.js ================================================ module.exports = function (arr, obj) { if (arr.indexOf) return arr.indexOf(obj) for (var i = 0; i < arr.length; ++i) { if (arr[i] === obj) return i } return -1 } ================================================ FILE: src/components/range/range/lib/lib/matches-selector.js ================================================ /** * Module dependencies. */ var query = require('./query') /** * Element prototype. */ var proto = Element.prototype /** * Vendor function. */ var vendor = proto.matches || proto.webkitMatchesSelector || proto.mozMatchesSelector || proto.msMatchesSelector || proto.oMatchesSelector /** * Expose `match()`. */ module.exports = match /** * Match `el` to `selector`. * * @param {Element} el * @param {String} selector * @return {Boolean} * @api public */ function match (el, selector) { if (!el || el.nodeType !== 1) return false if (vendor) return vendor.call(el, selector) var nodes = query.all(selector, el.parentNode) for (var i = 0; i < nodes.length; ++i) { if (nodes[i] === el) return true } return false } ================================================ FILE: src/components/range/range/lib/lib/mouse.js ================================================ /** * dependencies. */ var emitter = require('./emitter') var event = require('./event') /** * export `Mouse` */ module.exports = function (el, obj) { return new Mouse(el, obj) } /** * initialize new `Mouse`. * * @param {Element} el * @param {Object} obj */ function Mouse (el, obj) { this.obj = obj || {} this.el = el } /** * mixin emitter. */ emitter(Mouse.prototype) /** * bind mouse. * * @return {Mouse} */ Mouse.prototype.bind = function () { var obj = this.obj var self = this // up function up (e) { obj.onmouseup && obj.onmouseup(e) event.unbind(document, 'mousemove', move) event.unbind(document, 'mouseup', up) self.emit('up', e) } // move function move (e) { obj.onmousemove && obj.onmousemove(e) self.emit('move', e) } // down self.down = function (e) { obj.onmousedown && obj.onmousedown(e) event.bind(document, 'mouseup', up) event.bind(document, 'mousemove', move) self.emit('down', e) } // bind all. event.bind(this.el, 'mousedown', self.down) return this } /** * unbind mouse. * * @return {Mouse} */ Mouse.prototype.unbind = function () { event.unbind(this.el, 'mousedown', this.down) this.down = null } ================================================ FILE: src/components/range/range/lib/lib/percentage-calc.js ================================================ /** * Percentage-Calc 0.0.1 * https://github.com/abpetkov/percentage-calc * * Authored by Alexander Petkov * https://github.com/abpetkov * * Copyright 2014, Alexander Petkov * License: The MIT License (MIT) * http://opensource.org/licenses/MIT * */ /** * Check if number. * * @param {Number} num * @returns {Boolean} * @api public */ exports.isNumber = function (num) { return typeof num === 'number' } /** * Calculate percentage of a number. * * @param {Number} perc * @param {Number} num * @returns {Number} result * @api public */ exports.of = function (perc, num) { if (exports.isNumber(perc) && exports.isNumber(num)) return (perc / 100) * num } /** * Calculate percentage of a number out ot another number. * * @param {Number} part * @param {Number} target * @returns {Number} result * @api public */ exports.from = function (part, target) { if (exports.isNumber(part) && exports.isNumber(target)) return (part / target) * 100 } ================================================ FILE: src/components/range/range/lib/lib/query.js ================================================ function one (selector, el) { return el.querySelector(selector) } exports = module.exports = function (selector, el) { el = el || document return one(selector, el) } exports.all = function (selector, el) { el = el || document return el.querySelectorAll(selector) } exports.engine = function (obj) { if (!obj.one) throw new Error('.one callback required') if (!obj.all) throw new Error('.all callback required') // one = obj.one exports.all = obj.all return exports } ================================================ FILE: src/components/range/range/lib/lib/super.js ================================================ /** * slice */ var slice = Array.prototype.slice /** * Primary export */ var exports = module.exports = super_ /** * ### _super (dest, orig) * * Inherits the prototype methods or merges objects. * This is the primary export and it is recommended * that it be imported as `inherits` in node to match * the auto imported browser interface. * * var inherits = require('super') * * @param {Object|Function} destination object * @param {Object|Function} source object * @name _super * @api public */ function super_ () { var args = slice.call(arguments) if (!args.length) return if (typeof args[0] !== 'function') return exports.merge(args) exports.inherits.apply(null, args) } /** * ### extend (proto[, klass]) * * Provide `.extend` mechanism to allow extenion without * needing to use dependancy. * * function Bar () { * this._konstructed = true * } * * Bar.extend = inherits.extend * * var Fu = Bar.extend({ * initialize () { * this._initialized = true * } * }) * * var fu = new Fu() * fu.should.be.instanceof(Fu) // true * fu.should.be.instanceof(Bar) // true * * @param {Object} properties/methods to add to new prototype * @param {Object} properties/methods to add to new class * @returns {Object} new constructor * @name extend * @api public */ exports.extend = function (proto, klass) { var self = this var child = function () { return self.apply(this, arguments) } exports.merge([ child, this ]) exports.inherits(child, this) if (proto) exports.merge([ child.prototype, proto ]) if (klass) exports.merge([ child, klass ]) child.extend = this.extend // prevent overwrite return child } /** * ### inherits (ctor, superCtor) * * Inherit the prototype methods from on contructor * to another. * * @param {Function} destination * @param {Function} source * @api private */ exports.inherits = function (ctor, SuperCtor) { ctor.super_ = SuperCtor if (Object.create) { ctor.prototype = Object.create(SuperCtor.prototype, { constructor: { value: ctor, enumerable: false, writable: true, configurable: true } }) } else { ctor.prototype = new SuperCtor() ctor.prototype.constructor = ctor } } /** * Extends multiple objects. * * @param {Array} array of objects * @api private */ exports.merge = function (arr) { var main = arr.length === 2 ? arr.shift() : {} var obj = null for (var i = 0, len = arr.length; i < len; i++) { obj = arr[i] for (var p in obj) { if (!obj.hasOwnProperty(p)) continue main[p] = obj[p] } } return main } ================================================ FILE: src/components/range/range/lib/main.js ================================================ /** * External dependencies. * */ var mouse = require('./lib/mouse') var events = require('./lib/events') var classes = require('./lib/classes') var percentage = require('./lib/percentage-calc') /** * Expose `Powerange`. */ module.exports = Powerange /** * Create Powerange object. * * @constructor * @param {Object} element * @param {Object} options * @api public */ function Powerange (element, options) { if (!(this instanceof Powerange)) return new Powerange(element, options) this.element = element this.options = options || {} this.slider = this.create('span', 'range-bar') this.hasAppend = false if (this.element !== null && this.element.type === 'text') this.init() } /** * Bind events on handle element. * * @api private */ Powerange.prototype.bindEvents = function () { this.handle = this.slider.querySelector('.range-handle') this.touch = events(this.handle, this) this.touch.bind('touchstart', 'onmousedown') this.touch.bind('touchmove', 'onmousemove') this.touch.bind('touchend', 'onmouseup') this.mouse = mouse(this.handle, this) this.mouse.bind() } /** * Hide the target element. * * @api private */ Powerange.prototype.hide = function () { this.element.style.display = 'none' } /** * Append the target after the element. * * @api private */ Powerange.prototype.append = function () { if (!this.hasAppend) { var slider = this.generate() this.insertAfter(this.element, slider) } this.hasAppend = true } /** * Generate the appropriate type of slider. * * @returns {Object} this.slider * @api private */ Powerange.prototype.generate = function () { var elems = { 'handle': { 'type': 'span', 'selector': 'range-handle' }, 'min': { 'type': 'span', 'selector': 'range-min' }, 'max': { 'type': 'span', 'selector': 'range-max' }, 'quantity': { 'type': 'span', 'selector': 'range-quantity' } } for (var key in elems) { if (elems.hasOwnProperty(key)) { var temp = this.create(elems[key].type, elems[key].selector) this.slider.appendChild(temp) } } return this.slider } /** * Create HTML element. * * @param {String} type * @param {String} name * @returns {Object} elem * @api private */ Powerange.prototype.create = function (type, name) { var elem = document.createElement(type) elem.className = name return elem } /** * Insert element after another element. * * @param {Object} reference * @param {Object} target * @api private */ Powerange.prototype.insertAfter = function (reference, target) { reference.parentNode.insertBefore(target, reference.nextSibling) } /** * Set min and max values. * * @param {Number} min * @param {Number} max * @api private */ Powerange.prototype.setRange = function (min, max) { if (typeof min === 'number' && typeof max === 'number' && !this.options.hideRange) { this.slider.querySelector('.range-min').innerHTML = this.options.minHTML || min this.slider.querySelector('.range-max').innerHTML = this.options.maxHTML || max } } /** * Set slider current value. * * @param {Number} offset * @param {Number} size * @api private */ Powerange.prototype.setValue = function (offset, size) { var part = percentage.from(parseFloat(offset), size) if (offset === '0px' || size === 0) { value = this.options.min } else { var value = percentage.of(part, this.options.max - this.options.min) + this.options.min value = (this.options.decimal) ? (Math.round(value * 100) / 100) : Math.round(value) if (value > this.options.max) { value = this.options.max } } var changed = false changed = this.element.value !== value this.element.value = value this.options.callback(value) if (changed) this.changeEvent() } /** * Set step. * * @param {Number} sliderSize * @param {Number} handleSize * @returns {Array} this.steps * @api private */ Powerange.prototype.step = function (sliderSize, handleSize) { var dimension = sliderSize - handleSize var part = percentage.from(this.checkStep(this.options.step), this.options.max - this.options.min) var interval = percentage.of(part, dimension) var steps = [] for (var i = 0; i <= dimension; i += interval) { steps.push(i) } this.steps = steps return this.steps } /** * Check values. * * @param {Number} start * @api private */ Powerange.prototype.checkValues = function (start) { if (start < this.options.min) this.options.start = this.options.min if (start > this.options.max) this.options.start = this.options.max if (this.options.min >= this.options.max) this.options.min = this.options.max } /** * Make sure `step` is positive. * * @param {Number} value * @returns {Number} this.options.step * @api private */ Powerange.prototype.checkStep = function (value) { if (value < 0) value = Math.abs(value) this.options.step = value return this.options.step } /** * Disable range slider. * * @api private */ Powerange.prototype.disable = function (force) { if (this.options.disable || force) { this.mouse.unbind() this.touch.unbind() } if (this.options.disable) { if (this.options.disableOpacity) { this.slider.style.opacity = this.options.disableOpacity } classes(this.slider).add('range-bar-disabled') } } /** * Make element unselectable. * * @param {Object} element * @param {Boolean} set * @api private */ Powerange.prototype.unselectable = function (element, set) { if (!classes(this.slider).has('unselectable') && set === true) { classes(this.slider).add('unselectable') } else { classes(this.slider).remove('unselectable') } } /** * Handle the onchange event. * * @param {Boolean} state * @api private */ Powerange.prototype.changeEvent = function (state) { if (typeof Event === 'function' || !document.fireEvent) { var event = document.createEvent('HTMLEvents') event.initEvent('change', false, true) this.element.dispatchEvent(event) } else { this.element.fireEvent('onchange') } } /** * Initialize main class. * * @api private */ Powerange.prototype.init = function () { this.hide() this.append() this.bindEvents() this.checkValues(this.options.start) this.setRange(this.options.min, this.options.max) this.disable() } Powerange.prototype.reInit = function (opts) { this.options.start = opts.value this.options.min = opts.min this.options.max = opts.max this.disable(true) this.init() } ================================================ FILE: src/components/range/range/lib/powerange.js ================================================ /** * Require classes. */ var Horizontal = require('./horizontal') /** * Set default values. * * @api public */ var defaults = { callback () {}, decimal: false, disable: false, disableOpacity: null, hideRange: false, min: 0, max: 100, start: null, step: null, vertical: false } /** * Expose proper type of `Powerange`. */ module.exports = function (element, options) { options = options || {} for (var i in defaults) { if (options[i] == null) { options[i] = defaults[i] } } return new Horizontal(element, options) } ================================================ FILE: src/components/rater/index.vue ================================================ ================================================ FILE: src/components/scroller/index.vue ================================================ ================================================ FILE: src/components/search/index.vue ================================================ ================================================ FILE: src/components/selector/component.json ================================================ { "vux": { "is_weui": true } } ================================================ FILE: src/components/selector/index.vue ================================================ ================================================ FILE: src/components/selector/metas.yml ================================================ tips: - - en: key should be a `String` if you use key=>value - zh-CN: 选项的key必须是字符串,使用数字会出现问题 ================================================ FILE: src/components/shake/index.vue ================================================ ================================================ FILE: src/components/spinner/index.vue ================================================ ================================================ FILE: src/components/spinner/requestAnimationFrame.js ================================================ var lastTime = 0 var vendors = ['webkit', 'moz'] for (var x = 0; x < vendors.length && !window.requestAnimationFrame; ++x) { window.requestAnimationFrame = window[vendors[x] + 'RequestAnimationFrame'] window.cancelAnimationFrame = window[vendors[x] + 'CancelAnimationFrame'] || window[vendors[x] + 'CancelRequestAnimationFrame'] } if (!window.requestAnimationFrame) { window.requestAnimationFrame = function (callback, element) { var currTime = new Date().getTime() var timeToCall = Math.max(0, 16 - (currTime - lastTime)) var id = window.setTimeout(function () { callback(currTime + timeToCall) }, timeToCall) lastTime = currTime + timeToCall return id } } if (!window.cancelAnimationFrame) { window.cancelAnimationFrame = function (id) { clearTimeout(id) } } ================================================ FILE: src/components/spinner/spinner.js ================================================ /** * http://ionicframework.com/docs/api/directive/ionSpinner/ */ import './requestAnimationFrame' var TRANSLATE32 = 'translate(32,32)' var STROKE_OPACITY = 'stroke-opacity' var ROUND = 'round' var INDEFINITE = 'indefinite' var DURATION = '750ms' var NONE = 'none' var SHORTCUTS = { a: 'animate', an: 'attributeName', at: 'animateTransform', c: 'circle', da: 'stroke-dasharray', os: 'stroke-dashoffset', f: 'fill', lc: 'stroke-linecap', rc: 'repeatCount', sw: 'stroke-width', t: 'transform', v: 'values' } var SPIN_ANIMATION = { v: '0,32,32;360,32,32', an: 'transform', type: 'rotate', rc: INDEFINITE, dur: DURATION } function createSvgElement (tagName, data, parent, spinnerName) { var ele = document.createElement(SHORTCUTS[tagName] || tagName) var k, x, y for (k in data) { if (Object.prototype.toString.call(data[k]) === '[object Array]') { for (x = 0; x < data[k].length; x++) { if (data[k][x].fn) { for (y = 0; y < data[k][x].t; y++) { createSvgElement(k, data[k][x].fn(y, spinnerName), ele, spinnerName) } } else { createSvgElement(k, data[k][x], ele, spinnerName) } } } else { setSvgAttribute(ele, k, data[k]) } } parent.appendChild(ele) } function setSvgAttribute (ele, k, v) { ele.setAttribute(SHORTCUTS[k] || k, v) } function animationValues (strValues, i) { var values = strValues.split(';') var back = values.slice(i) var front = values.slice(0, values.length - back.length) values = back.concat(front).reverse() return values.join(';') + ';' + values[0] } var IOS_SPINNER = { sw: 4, lc: ROUND, line: [{ fn (i, spinnerName) { return { y1: spinnerName === 'ios' ? 17 : 12, y2: spinnerName === 'ios' ? 29 : 20, t: TRANSLATE32 + ' rotate(' + (30 * i + (i < 6 ? 180 : -180)) + ')', a: [{ fn () { return { an: STROKE_OPACITY, dur: DURATION, v: animationValues('0;.1;.15;.25;.35;.45;.55;.65;.7;.85;1', i), rc: INDEFINITE } }, t: 1 }] } }, t: 12 }] } var spinners = { android: { c: [{ sw: 6, da: 128, os: 82, r: 26, cx: 32, cy: 32, f: NONE }] }, ios: IOS_SPINNER, 'ios-small': IOS_SPINNER, bubbles: { sw: 0, c: [{ fn (i) { return { cx: 24 * Math.cos(2 * Math.PI * i / 8), cy: 24 * Math.sin(2 * Math.PI * i / 8), t: TRANSLATE32, a: [{ fn () { return { an: 'r', dur: DURATION, v: animationValues('1;2;3;4;5;6;7;8', i), rc: INDEFINITE } }, t: 1 }] } }, t: 8 }] }, circles: { c: [{ fn (i) { return { r: 5, cx: 24 * Math.cos(2 * Math.PI * i / 8), cy: 24 * Math.sin(2 * Math.PI * i / 8), t: TRANSLATE32, sw: 0, a: [{ fn () { return { an: 'fill-opacity', dur: DURATION, v: animationValues('.3;.3;.3;.4;.7;.85;.9;1', i), rc: INDEFINITE } }, t: 1 }] } }, t: 8 }] }, crescent: { c: [{ sw: 4, da: 128, os: 82, r: 26, cx: 32, cy: 32, f: NONE, at: [SPIN_ANIMATION] }] }, dots: { c: [{ fn (i) { return { cx: 16 + (16 * i), cy: 32, sw: 0, a: [{ fn () { return { an: 'fill-opacity', dur: DURATION, v: animationValues('.5;.6;.8;1;.8;.6;.5', i), rc: INDEFINITE } }, t: 1 }, { fn () { return { an: 'r', dur: DURATION, v: animationValues('4;5;6;5;4;3;3', i), rc: INDEFINITE } }, t: 1 }] } }, t: 3 }] }, lines: { sw: 7, lc: ROUND, line: [{ fn (i) { return { x1: 10 + (i * 14), x2: 10 + (i * 14), a: [{ fn () { return { an: 'y1', dur: DURATION, v: animationValues('16;18;28;18;16', i), rc: INDEFINITE } }, t: 1 }, { fn () { return { an: 'y2', dur: DURATION, v: animationValues('48;44;36;46;48', i), rc: INDEFINITE } }, t: 1 }, { fn () { return { an: STROKE_OPACITY, dur: DURATION, v: animationValues('1;.8;.5;.4;1', i), rc: INDEFINITE } }, t: 1 }] } }, t: 4 }] }, ripple: { f: NONE, 'fill-rule': 'evenodd', sw: 3, circle: [{ fn (i) { return { cx: 32, cy: 32, a: [{ fn () { return { an: 'r', begin: (i * -1) + 's', dur: '2s', v: '0;24', keyTimes: '0;1', keySplines: '0.1,0.2,0.3,1', calcMode: 'spline', rc: INDEFINITE } }, t: 1 }, { fn () { return { an: STROKE_OPACITY, begin: (i * -1) + 's', dur: '2s', v: '.2;1;.2;0', rc: INDEFINITE } }, t: 1 }] } }, t: 2 }] }, spiral: { defs: [{ linearGradient: [{ id: 'sGD', gradientUnits: 'userSpaceOnUse', x1: 55, y1: 46, x2: 2, y2: 46, stop: [{ offset: 0.1, class: 'stop1' }, { offset: 1, class: 'stop2' }] }] }], g: [{ sw: 4, lc: ROUND, f: NONE, path: [{ stroke: 'url(#sGD)', d: 'M4,32 c0,15,12,28,28,28c8,0,16-4,21-9' }, { d: 'M60,32 C60,16,47.464,4,32,4S4,16,4,32' }], at: [SPIN_ANIMATION] }] } } var animations = { android (ele) { var self = this this.stop = false var rIndex = 0 var rotateCircle = 0 var startTime var svgEle = ele.querySelector('g') var circleEle = ele.querySelector('circle') function run () { if (self.stop) return var v = easeInOutCubic(Date.now() - startTime, 650) var scaleX = 1 var translateX = 0 var dasharray = (188 - (58 * v)) var dashoffset = (182 - (182 * v)) if (rIndex % 2) { scaleX = -1 translateX = -64 dasharray = (128 - (-58 * v)) dashoffset = (182 * v) } var rotateLine = [0, -101, -90, -11, -180, 79, -270, -191][rIndex] setSvgAttribute(circleEle, 'da', Math.max(Math.min(dasharray, 188), 128)) setSvgAttribute(circleEle, 'os', Math.max(Math.min(dashoffset, 182), 0)) setSvgAttribute(circleEle, 't', 'scale(' + scaleX + ',1) translate(' + translateX + ',0) rotate(' + rotateLine + ',32,32)') rotateCircle += 4.1 if (rotateCircle > 359) rotateCircle = 0 setSvgAttribute(svgEle, 't', 'rotate(' + rotateCircle + ',32,32)') if (v >= 1) { rIndex++ if (rIndex > 7) rIndex = 0 startTime = Date.now() } requestAnimationFrame(run) } return function () { startTime = Date.now() run() return self } } } function easeInOutCubic (t, c) { t /= c / 2 if (t < 1) return 1 / 2 * t * t * t t -= 2 return 1 / 2 * (t * t * t + 2) } export default function (el, icon) { var spinnerName, anim // eslint-disable-line spinnerName = icon var container = document.createElement('div') createSvgElement('svg', { viewBox: '0 0 64 64', g: [spinners[spinnerName]] }, container, spinnerName) // Specifically for animations to work, // Android 4.3 and below requires the element to be // added as an html string, rather than dynmically // building up the svg element and appending it. el.innerHTML = container.innerHTML start() function start () { if (animations[spinnerName]) { anim = animations[spinnerName](el)() } } return el } ================================================ FILE: src/components/step/index.js ================================================ import Step from './step' import StepItem from './step-item' export { Step, StepItem } ================================================ FILE: src/components/step/step-item.vue ================================================ ================================================ FILE: src/components/step/step.vue ================================================ ================================================ FILE: src/components/sticky/index.vue ================================================ ================================================ FILE: src/components/sticky/sticky.js ================================================ // http://efe.baidu.com/blog/position-sticky/ // 检测iOS版本大于等于6 function gtIOS6 () { var userAgent = window.navigator.userAgent var ios = userAgent.match(/(iPad|iPhone|iPod)\s+OS\s([\d_\.]+)/) return ios && ios[2] && (parseInt(ios[2].replace(/_/g, '.'), 10) >= 6) } // 判断是否支持sticky属性 function isSupportSticky () { var prefixTestList = ['', '-webkit-', '-ms-', '-moz-', '-o-'] var stickyText = '' for (var i = 0; i < prefixTestList.length; i++) { stickyText += 'position:' + prefixTestList[i] + 'sticky' } // 创建一个dom来检查 var div = document.createElement('div') var body = document.body div.style.cssText = 'display:none' + stickyText body.appendChild(div) var isSupport = /sticky/i.test(window.getComputedStyle(div).position) body.removeChild(div) div = null return isSupport } export default function (nav) { if (gtIOS6() || isSupportSticky()) { // 大于等于iOS6版本使用sticky nav.classList.add('vux-sticky') } else { var navOffsetY = nav.offsetTop window.addEventListener('scroll', function () { window.scrollY >= navOffsetY ? nav.classList.add('vux-fixed') : nav.classList.remove('vux-fixed') }) } } ================================================ FILE: src/components/swiper/index.vue ================================================ ================================================ FILE: src/components/swiper/swiper.js ================================================ import arrayFrom from 'array-from' import objectAssign from 'object-assign' class Swiper { constructor (options) { this._default = { container: '.vux-swiper', item: '.vux-swiper-item', direction: 'vertical', activeClass: 'active', threshold: 50, duration: 300, auto: false, loop: false, interval: 3000, height: 'auto', minMovingDistance: 0 } this._options = objectAssign(this._default, options) this._options.height = this._options.height.replace('px', '') this._start = {} this._move = {} this._end = {} this._eventHandlers = {} this._prev = this._current = this._goto = 0 this._width = this._height = this._distance = 0 this._offset = [] this.$box = this._options.container this.$container = this._options.container.querySelector('.vux-swiper') this.$items = this.$container.querySelectorAll(this._options.item) this.count = this.$items.length this.realCount = this.$items.length // real items length this._position = [] // used by go event this._firstItemIndex = 0 if (!this.count) { return } this._init() this._auto() this._bind() this._onResize() return this } _auto () { const me = this me.stop() if (me._options.auto) { me.timer = setTimeout(() => { me.next() }, me._options.interval) } } updateItemWidth () { this._width = this.$box.offsetWidth this._distance = this._options.direction === 'horizontal' ? this._width : this._height } stop () { this.timer && clearTimeout(this.timer) } _loop () { return this._options.loop && this.realCount >= 3 } _onResize () { const me = this this.resizeHandler = () => { setTimeout(() => { me.updateItemWidth() me._setOffset() me._setTransfrom() }, 100) } window.addEventListener('orientationchange', this.resizeHandler, false) } _init () { if (this._options.loop) { this._loopTwoItems() } this._height = this._options.height === 'auto' ? 'auto' : this._options.height - 0 this.updateItemWidth() this._initPosition() this._activate(this._current) this._setOffset() this._setTransfrom() if (this._loop()) { this._loopRender() } } _initPosition () { for (let i = 0; i < this.realCount; i++) { this._position.push(i) } } _movePosition (position) { const me = this if (position > 0) { let firstIndex = me._position.splice(0, 1) me._position.push(firstIndex[0]) } else if (position < 0) { let lastIndex = me._position.pop() me._position.unshift(lastIndex) } } _setOffset () { let me = this let index = me._position.indexOf(me._current) me._offset = [] arrayFrom(me.$items).forEach(function ($item, key) { me._offset.push((key - index) * me._distance) }) } _setTransition (duration) { duration = duration || (this._options.duration || 'none') let transition = duration === 'none' ? 'none' : duration + 'ms' arrayFrom(this.$items).forEach(function ($item, key) { $item.style.webkitTransition = transition $item.style.transition = transition }) } _setTransfrom (offset) { const me = this offset = offset || 0 arrayFrom(me.$items).forEach(function ($item, key) { let distance = me._offset[key] + offset let transform = `translate3d(${distance}px, 0, 0)` if (me._options.direction === 'vertical') { transform = `translate3d(0, ${distance}px, 0)` } $item.style.webkitTransform = transform $item.style.transform = transform }) } _bind () { const me = this me.touchstartHandler = (e) => { me.stop() me._start.x = e.changedTouches[0].pageX me._start.y = e.changedTouches[0].pageY me._setTransition('none') } me.touchmoveHandler = (e) => { me._move.x = e.changedTouches[0].pageX me._move.y = e.changedTouches[0].pageY let distanceX = me._move.x - me._start.x let distanceY = me._move.y - me._start.y let distance = distanceY let noScrollerY = Math.abs(distanceX) > Math.abs(distanceY) if (me._options.direction === 'horizontal' && noScrollerY) { distance = distanceX } if (((me._options.minMovingDistance && Math.abs(distance) >= me._options.minMovingDistance) || !me._options.minMovingDistance) && noScrollerY) { me._setTransfrom(distance) } noScrollerY && e.preventDefault() } me.touchendHandler = (e) => { me._end.x = e.changedTouches[0].pageX me._end.y = e.changedTouches[0].pageY let distance = me._end.y - me._start.y if (me._options.direction === 'horizontal') { distance = me._end.x - me._start.x } distance = me.getDistance(distance) if (distance !== 0 && me._options.minMovingDistance && Math.abs(distance) < me._options.minMovingDistance) { return } if (distance > me._options.threshold) { me.move(-1) } else if (distance < -me._options.threshold) { me.move(1) } else { me.move(0) } me._loopRender() } me.transitionEndHandler = (e) => { me._activate(me._current) let cb = me._eventHandlers.swiped cb && cb.apply(me, [me._prev % me.count, me._current % me.count]) me._auto() me._loopRender() e.preventDefault() } me.$container.addEventListener('touchstart', me.touchstartHandler, false) me.$container.addEventListener('touchmove', me.touchmoveHandler, false) me.$container.addEventListener('touchend', me.touchendHandler, false) me.$items[1] && me.$items[1].addEventListener('webkitTransitionEnd', me.transitionEndHandler, false) } _loopTwoItems () { // issue #596 (support when onlt two) if (this.count === 2) { let div = document.createElement('div') let $item for (let i = this.$items.length - 1; i >= 0; i--) { div.innerHTML = this.$items[i].outerHTML $item = div.querySelector(this._options.item) $item.classList.add(`${this._options.item.replace('.', '')}-clone`) this.$container.appendChild($item) } this.realCount = 4 } } _loopRender () { const me = this if (me._loop()) { // issue #507 (delete cloneNode) if (me._offset[me._offset.length - 1] === 0) { me.$container.appendChild(me.$items[0]) me._loopEvent(1) } else if (me._offset[0] === 0) { me.$container.insertBefore(me.$items[me.$items.length - 1], me.$container.firstChild) me._loopEvent(-1) } } } _loopEvent (num) { const me = this me._itemDestoy() me.$items = me.$container.querySelectorAll(me._options.item) me.$items[1] && me.$items[1].addEventListener('webkitTransitionEnd', me.transitionEndHandler, false) me._movePosition(num) me._setOffset() me._setTransfrom() } getDistance (distance) { if (this._loop()) { return distance } else { if (distance > 0 && this._current === 0) { return 0 } else if (distance < 0 && this._current === this.realCount - 1) { return 0 } else { return distance } } } _moveIndex (num) { if (num !== 0) { this._prev = this._current this._current += this.realCount this._current += num this._current %= this.realCount } } _activate (index) { let clazz = this._options.activeClass Array.prototype.forEach.call(this.$items, ($item, key) => { $item.classList.remove(clazz) if (index === Number($item.dataset.index)) { $item.classList.add(clazz) } }) } go (index) { const me = this me.stop() index = index || 0 index += this.realCount index = index % this.realCount index = this._position.indexOf(index) - this._position.indexOf(this._current) me._moveIndex(index) me._setOffset() me._setTransition() me._setTransfrom() me._auto() return this } next () { this.move(1) return this } move (num) { this.go(this._current + num) return this } on (event, callback) { if (this._eventHandlers[event]) { console.error(`[swiper] event ${event} is already register`) } if (typeof callback !== 'function') { console.error('[swiper] parameter callback must be a function') } this._eventHandlers[event] = callback return this } _itemDestoy () { for (let item of this.$items) { item.removeEventListener('webkitTransitionEnd', this.transitionEndHandler, false) } } destroy () { this.stop() this._current = 0 this._setTransfrom(0) window.removeEventListener('orientationchange', this.resizeHandler, false) this.$container.removeEventListener('touchstart', this.touchstartHandler, false) this.$container.removeEventListener('touchmove', this.touchmoveHandler, false) this.$container.removeEventListener('touchend', this.touchendHandler, false) this._itemDestoy() // remove clone item (used by loop only 2) if (this._options.loop && this.count === 2) { let $item = this.$container.querySelector(`${this._options.item}-clone`) $item && this.$container.removeChild($item) $item = this.$container.querySelector(`${this._options.item}-clone`) $item && this.$container.removeChild($item) } } } export default Swiper ================================================ FILE: src/components/swiper-item/index.vue ================================================ ================================================ FILE: src/components/tab/index.js ================================================ import Tab from './tab' import TabItem from './tab-item' export { Tab, TabItem } ================================================ FILE: src/components/tab/tab-item.vue ================================================ ================================================ FILE: src/components/tab/tab.vue ================================================ ================================================ FILE: src/components/tabbar/component.json ================================================ { "vux": { "is_weui": true } } ================================================ FILE: src/components/tabbar/index.js ================================================ import Tabbar from './tabbar' import TabbarItem from './tabbar-item' export { Tabbar, TabbarItem } ================================================ FILE: src/components/tabbar/metas.yml ================================================ tabbar: props: icon-class: en: icon's classname zh-CN: 图标的class名 slots: default: en: main content for tabbar items zh-CN: tabbar主体内容,只允许tabbar-item tabbar-item: props: badge: en: badge text, if not specified, the badge will not show zh-CN: 徽标文字,不指定则不显示 show-dot: en: if show the red dot zh-CN: 是否显示红点 link: en: the url for current tabbar item, can be a url or a `v-link` value zh-CN: 链接,可以为普通url或者用`v-link`写法 icon-class: en: the classname for current icon, if tabbar and tabbar-item both specify icon-class, tabbar-item's will be usePulldown zh-CN: 图标类名,如果tabbar也同时定义了icon-class, 会使用tabbar-item的 slots: icon: en: icon area zh-CN: 图标区域 label: en: label text zh-CN: 图标下方文字 events: on-item-click: en: triggers when tabbar item is click zh-CN: 点击菜单项时触发 ================================================ FILE: src/components/tabbar/tabbar-item.vue ================================================ ================================================ FILE: src/components/tabbar/tabbar.vue ================================================ ================================================ FILE: src/components/timeline/index.js ================================================ import Timeline from './timeline' import TimelineItem from './timeline-item' module.exports = { Timeline, TimelineItem } ================================================ FILE: src/components/timeline/timeline-item.vue ================================================ ================================================ FILE: src/components/timeline/timeline.vue ================================================ ================================================ FILE: src/components/tip/index.vue ================================================ ================================================ FILE: src/components/toast/component.json ================================================ { "vux": { "is_weui": true } } ================================================ FILE: src/components/toast/index.vue ================================================ ================================================ FILE: src/components/video/index.vue ================================================ ================================================ FILE: src/components/video/zy.media.css ================================================ body { margin: 0 } /* zy.media style */ .zy_media { background: #000; position: relative } .zy_media video, .zy_media audio { width: 100%; position: absolute; top: 0; left: 0; display: block } .zy_fullscreen { overflow: hidden } .zy_fullscreen .zy_media { position: fixed; left: 0; top: 0; right: 0; bottom: 0; z-index: 1000 } .zy_fullscreen .zy_wrap, .zy_fullscreen video { width: 100%; height: 100% } .zy_wrap { width: 100% } /* 视频标题 */ .zy_title { height: 34px; padding-left: 10px; color: #fff; font-size: 12px; line-height: 34px; white-space: nowrap; text-overflow: ellipsis; overflow: hidden; background: rgba(0, 0, 0, .25); position: absolute; left: 0; right: 0; top: 0; -webkit-transition: top .5s; transition: top .5s } /* 视频主区中播放、加载中、错误提示 */ .zy_media .dec_play, .zy_media .dec_loading, .zy_media .dec_error { margin: -32px 0 0 -31px; position: absolute; top: 50%; left: 50% } .zy_media .dec_play::before { width: 60px; height: 60px; content: ''; border-radius: 60px; border: #e5e5e4 1px solid; display: block } .zy_media .dec_play::after { width: 0; height: 0; content: ''; border-color: transparent transparent transparent #e5e5e4; border-width: 14px 20px; border-style: solid; position: absolute; top: 16px; left: 23px; z-index: 2; display: block } .zy_media .dec_loading { width: 62px; height: 62px; -webkit-animation: ani_loading .6s infinite linear; -webkit-animation-fill-mode: forwards; animation: ani_loading .6s infinite linear; animation-fill-mode: forwards } @-webkit-keyframes ani_loading { 100% { -webkit-transform: rotate(360deg) } } @keyframes ani_loading { 100% { transform: rotate(360deg) } } .zy_media .dec_loading::before { width: 7px; height: 7px; content: ''; border-radius: 7px; background: #fff; opacity: .8; position: absolute; top: 25px } .zy_media .dec_loading::after { width: 48px; height: 48px; content: ''; border-radius: 50px; border: 7px solid #fff; opacity: .2; display: block } .zy_media .dec_error { width: 62px; height: 62px; margin-top: -53px; margin-left: -25px; white-space: nowrap; color: #fff; font-size: 12px; text-align: center; position: absolute; top: 50%; left: 50%; } /* 控制栏 */ .zy_controls { height: 44px; background: rgba(0, 0, 0, .55); position: absolute; left: 0; right: 0; bottom: 0; -webkit-transition: bottom .5s; transition: bottom .5s; display: -webkit-box; display: box; display: -webkit-flex; display: flex } /* 播放、暂停按钮 */ .zy_playpause_btn { width: 26px; height: 30px; margin-right: 4px; padding: 13px 0 0 14px; position: relative } .zy_play::before { width: 0; height: 0; content: ''; border-color: transparent transparent transparent #cbcbcb; border-width: 8px 12px; border-style: solid; display: block } .zy_pause::before, .zy_pause::after { width: 3px; height: 14px; content: ''; background: #cbcbcb; position: absolute; top: 13px; left: 14px } .zy_pause::after { left: 22px } /* 时间线操作区 */ .zy_timeline { margin-right: 10px; -webkit-box-flex: 1; -webkit-flex: 1 1 auto; flex: 1 1 auto } .zy_timeline_slider { width: 100%; height: 1px; background: #999; position: relative; top: 21px; left: 0 } .zy_timeline_buffering { width: 100%; height: 15px; top: -7px; background-image: -webkit-gradient(linear, 0 100%, 100% 0, color-stop(.25, rgba(255, 255, 255, .15)), color-stop(.25, transparent), color-stop(.5, transparent), color-stop(.5, rgba(255, 255, 255, .15)), color-stop(.75, rgba(255, 255, 255, .15)), color-stop(.75, transparent), to(transparent)); background-image: -webkit-linear-gradient(-45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); background-image: linear-gradient(-45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); -webkit-background-size: 15px 15px; background-size: 15px 15px; -webkit-animation: ani_buffering 2s linear infinite; animation: ani_buffering 2s linear infinite; position: absolute; } @-webkit-keyframes ani_buffering { from { background-position: 0 0 } to { background-position: 30px 0 } } @keyframes ani_buffering { from { background-position: 0 0 } to { background-position: 30px 0 } } .zy_timeline_loaded { width: 0; height: 1px; background: #e5e5e5; position: absolute; top: 0; left: 0; z-index: 1 } .zy_timeline_current { width: 0; height: 1px; background: #ff6159; position: relative; z-index: 2 } .zy_timeline_handle { width: 16px; height: 16px; border-radius: 16px; background: #e5e5e5; position: absolute; top: -8px; left: -8px; z-index: 3 } /* 时间展示 */ .zy_time { width: auto; height: 44px; margin-right: 5px; line-height: 44px; font-size: 11px; color: #999; text-align: center } .zy_time .zy_currenttime { color: #e5e5e5 } /* 全屏控制按钮 */ .zy_fullscreen_btn { width: 38px; height: 44px; background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaBAMAAAEsY2FrAAAAElBMVEX///////////////////////+65XQCAAAABXRSTlMAHm1u3TG+li4AAAB5SURBVBgZBcGxbQNBEAQwPnCXC49TviU4UQnKx8ZP/62YVB58qQCIBwArGgAAwK4HkAUEgEXAEmBFG/AH+B0gN5BrQLwAAG4bXLOBewPXB/AGu6VtG4CeAUCdAaCcAVCcAQAAAAMAzrAD4IwdAM7PDgDOJwBt2wAA/9uDEjcL3fqtAAAAAElFTkSuQmCC); background-repeat: no-repeat; background-position: center; -webkit-background-size: 16px; background-size: 16px } .zy_unfullscreen { background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABoAAAAaBAMAAAEsY2FrAAAAElBMVEX///////////////////////+65XQCAAAABXRSTlMAHm1u3TG+li4AAAB5SURBVBgZBcGxDcMwEAQwGtH1QuD0WiGAB8gI39z+q4SEhR8AwALAwmAwgCAIS4AV0BYg7UAWEIttwNeA1x7gO8BrQDsAAGlBDpA3kOuAeIO4eDYZAM+WAeDZGQA8nwFo2w4AAAAAANq2A9D7AKDuA0C5D4DiPgDAH9lBEChOLXSRAAAAAElFTkSuQmCC) } ================================================ FILE: src/components/video/zy.media.js ================================================ /* * * zy.media.js * HTML5