提交 5c27707f 编写于 作者: fxy060608's avatar fxy060608

framework

上级 808bd26d
src/core/helpers/html-parser.js
\ No newline at end of file
module.exports = {
ignore: [
"./packages",
],
presets: [
["@vue/app", {
useBuiltIns: "entry"
}]
]
}
const {
error
} = require('@vue/cli-shared-utils')
const Service = require('@vue/cli-service')
const service = new Service(process.env.VUE_CLI_CONTEXT || process.cwd(), {
inlineOptions: require('./vue.config.js')
})
service.run('build', {
name: 'index',
watch: process.env.UNI_WATCH === 'true',
target: 'lib',
formats: process.env.UNI_WATCH === 'true' ? 'umd' : 'umd-min',
entry: './lib/' + process.env.UNI_PLATFORM + '/main.js'
}).catch(err => {
error(err)
process.exit(1)
})
const path = require('path')
const resolve = dir => path.resolve(__dirname, '../', dir)
const pkgPath = resolve('package.json')
const webpackConfig = require('./webpack.config.js')
module.exports = {
baseUrl: '/',
outputDir: resolve('./packages/uni-' + process.env.UNI_PLATFORM + '/dist'),
lintOnSave: true, // or error
runtimeCompiler: false,
transpileDependencies: [],
productionSourceMap: false,
configureWebpack: webpackConfig,
chainWebpack: config => {
config.devtool('source-map')
config.module
.rule('eslint')
.include
.add(resolve('src'))
.add(resolve('lib/' + process.env.UNI_PLATFORM))
.end()
.use('eslint-loader')
.loader(resolve('node_modules/eslint-loader'))
.options({
fix: true,
configFile: pkgPath
})
config.plugins.delete('hmr') // remove hot module reload
},
css: {
extract: true
}
}
const path = require('path')
const webpack = require('webpack')
const resolve = dir => path.resolve(__dirname, '../', dir)
const pkg = require('../package.json')
module.exports = {
mode: 'production',
devtool: false,
externals: {
vue: {
commonjs: 'vue',
commonjs2: 'vue',
root: 'Vue'
},
'vue-router': {
commonjs: 'vue-router',
commonjs2: 'vue-router',
root: 'VueRouter'
}
},
resolve: {
alias: {
'uni-core': resolve('src/core'),
'uni-view': resolve('src/core/view'),
'uni-service': resolve('src/core/service'),
'uni-shared': resolve('src/shared'),
'uni-mixins': resolve('src/core/view/mixins'),
'uni-helpers': resolve('src/core/helpers'),
'uni-platform': resolve('src/platforms/' + process.env.UNI_PLATFORM)
}
},
module: {
rules: []
},
plugins: [
new webpack.DefinePlugin({
__VERSION__: JSON.stringify(pkg.version),
__PLATFORM__: JSON.stringify(process.env.UNI_PLATFORM)
}),
new webpack.ProvidePlugin({
'console': [resolve('src/core/helpers/console'), 'default'],
'UniViewJSBridge': [resolve('src/core/view/bridge')],
'UniServiceJSBridge': [resolve('src/core/service/bridge')]
})
]
}
const path = require('path')
const webpack = require('webpack')
const resolve = dir => path.resolve(__dirname, '../', dir)
const pkg = require('../package.json')
let service = process.VUE_CLI_SERVICE
if (!service || process.env.VUE_CLI_API_MODE) {
const Service = require('@vue/cli-service')
service = new Service(process.env.VUE_CLI_CONTEXT || process.cwd())
service.init(process.env.VUE_CLI_MODE || process.env.NODE_ENV)
}
const config = service.resolveWebpackConfig()
config.resolve.alias = {
'@': resolve('src'),
'uni-core': resolve('src/core'),
'uni-view': resolve('src/core/view'),
'uni-service': resolve('src/core/service'),
'uni-shared': resolve('src/shared'),
'uni-mixins': resolve('src/core/view/mixins'),
'uni-helpers': resolve('src/core/helpers'),
'uni-platform': resolve('src/platforms/' + process.env.UNI_PLATFORM),
'uni-components': resolve('src/core/view/components')
}
const isEslintLoader = config.module.rules[config.module.rules.length - 1].enforce
if (isEslintLoader) {
config.module.rules.splice(config.module.rules.length - 1, 1)
} else {
throw new Error('eslint-loader is undefined')
}
config.plugins = config.plugins.concat([
new webpack.DefinePlugin({
__VERSION__: JSON.stringify(pkg.version),
__PLATFORM__: JSON.stringify(process.env.UNI_PLATFORM)
}),
new webpack.ProvidePlugin({
'UniViewJSBridge': [resolve('src/core/view/bridge')],
'UniServiceJSBridge': [resolve('src/core/service/bridge')]
})
])
module.exports = config
/**
* 1.导出全局对象(UniViewJSBridge,UniServiceJSBridge,uni,getApp,getCurrentPages)
* 2.引入 Vue 插件(uniVueServicePlugin,uniVueServicePlugin)
* 3.引入 Vue 组件
*/
import Vue from 'vue'
global.UniViewJSBridge = {
subscribeHandler: UniViewJSBridge.subscribeHandler
}
global.UniServiceJSBridge = {
subscribeHandler: UniServiceJSBridge.subscribeHandler
}
const {
default: uni,
getApp,
getCurrentPages
} = require('uni-service')
global.uni = Object.assign(uni, require('uni-view').default)
global.wx = global.uni
global.getApp = getApp
global.getCurrentPages = getCurrentPages
Vue.use(require('uni-service/plugins').default, {
routes: __uniRoutes
})
Vue.use(require('uni-view/plugins').default, {
routes: __uniRoutes
})
require('uni-view/components')
{
"name": "uniapp-js-framework",
"version": "0.0.1",
"scripts": {
"lint": "eslint --fix --config package.json --ext .js --ext .vue --ignore-path .eslintignore build src",
"dev:h5": "npm run lint && cross-env NODE_ENV=production UNI_WATCH=true UNI_PLATFORM=h5 node build/build.js",
"build:h5": "npm run lint && cross-env NODE_ENV=production UNI_WATCH=false UNI_PLATFORM=h5 node build/build.js",
"test:unit": "cross-env NODE_ENV=test UNI_PLATFORM=h5 mocha-webpack --require tests/unit/setup.js --webpack-config build/webpack.config.test.js tests/unit/**/*.spec.js"
},
"dependencies": {},
"devDependencies": {
"@vue/cli-plugin-babel": "^3.1.1",
"@vue/cli-plugin-eslint": "^3.1.4",
"@vue/cli-plugin-unit-mocha": "^3.1.1",
"@vue/cli-service": "^3.1.2",
"@vue/test-utils": "^1.0.0-beta.25",
"babel-eslint": "^10.0.1",
"babylon": "^6.18.0",
"chai": "^4.1.2",
"cross-env": "^5.2.0",
"eslint": "^5.5.0",
"eslint-config-standard": "^12.0.0",
"eslint-loader": "^2.1.0",
"eslint-plugin-import": "^2.14.0",
"eslint-plugin-node": "^7.0.1",
"eslint-plugin-promise": "^4.0.0",
"eslint-plugin-standard": "^4.0.0",
"eslint-plugin-vue": "^4.7.1",
"jsdom": "^13.0.0",
"jsdom-global": "^3.0.2",
"strip-json-comments": "^2.0.1",
"vue": "^2.5.17",
"vue-router": "^3.0.1",
"vue-template-compiler": "^2.5.17",
"webpack": "^4.18.0",
"webpack-bundle-analyzer": "^3.0.3",
"webpack-virtual-modules": "^0.1.10"
},
"eslintConfig": {
"root": true,
"env": {
"node": true
},
"extends": [
"plugin:vue/recommended",
"standard"
],
"globals": {
"getApp": true,
"getCurrentPages": true,
"plus": true,
"uni": true,
"Vue": true,
"wx": true,
"__uniConfig": true,
"__uniRoutes": true,
"UniViewJSBridge": true,
"UniServiceJSBridge": true,
"__PLATFORM__": true,
"__VERSION__": true
},
"rules": {
"no-tabs": 0,
"standard/no-callback-literal": 0
},
"parserOptions": {
"parser": "babel-eslint"
}
},
"browserslist": [
"last 3 versions",
"Android >= 4.1",
"ios >= 8"
],
"license": "ISC",
"main": "index.js",
"description": "",
"author": ""
}
The MIT License (MIT)
Copyright (c) 2017-present, Yuxi (Evan) You
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.
此差异已折叠。
因为 它太大了无法显示 source diff 。你可以改为 查看blob
{
"name": "@dcloudio/uni-h5",
"version": "0.0.5",
"description": "uni-app h5",
"main": "dist/index.umd.min.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "fxy060608",
"license": "MIT"
}
module.exports = {
plugins: [
require('autoprefixer')({ browsers: ['iOS >= 8', 'Android >= 4'] })
]
}
<svg width="21" height="37" viewBox="0 0 21 37" xmlns="http://www.w3.org/2000/svg"><title>Rectangle 1</title><path d="M17 36L0 19a2.91 2.91 0 0 1 0-1L17 1c.131-.057.5-.06 1 0l2 2c.057.522.06.899 0 1L6 18a.898.898 0 0 0 0 1l14 14c.056.098.062.474 0 1l-2 2c-.494.057-.868.06-1 0z" fill="#FFF" fill-rule="evenodd"/></svg>
\ No newline at end of file
import {
isFn,
isPlainObject
} from 'uni-shared'
import {
tryCatch,
tryCatchFramework
} from './catch'
import protocol from './protocol'
import validateParam from './params'
function invokeCallbackHandlerFail (err, apiName, callbackId) {
const errMsg = `${apiName}:fail ${err}`
console.error(errMsg)
if (callbackId === -1) {
throw new Error(errMsg)
}
if (typeof callbackId === 'number') {
invokeCallbackHandler(callbackId, {
errMsg
})
}
return false
}
const callbackApiParamTypes = [{
name: 'callback',
type: Function,
required: true
}]
function validateParams (apiName, paramsData, callbackId) {
let paramTypes = protocol[apiName]
if (!paramTypes && isCallbackApi(apiName)) {
paramTypes = callbackApiParamTypes
}
if (paramTypes) {
if (Array.isArray(paramTypes) && Array.isArray(paramsData)) {
const paramTypeObj = Object.create(null)
const paramsDataObj = Object.create(null)
const paramsDataLength = paramsData.length
paramTypes.forEach((paramType, index) => {
paramTypeObj[paramType.name] = paramType
if (paramsDataLength > index) {
paramsDataObj[paramType.name] = paramsData[index]
}
})
paramTypes = paramTypeObj
paramsData = paramsDataObj
}
if (isFn(paramTypes.beforeValidate)) {
const err = paramTypes.beforeValidate(paramsData)
if (err) {
return invokeCallbackHandlerFail(err, apiName, callbackId)
}
}
const keys = Object.keys(paramTypes)
for (let i = 0; i < keys.length; i++) {
if (keys[i] === 'beforeValidate') {
continue
}
const err = validateParam(keys[i], paramTypes, paramsData)
if (err) {
return invokeCallbackHandlerFail(err, apiName, callbackId)
}
}
}
return true
}
let invokeCallbackId = 1
const invokeCallbacks = {}
function createKeepAliveApiCallback (apiName, callback) {
const callbackId = invokeCallbackId++
const invokeCallbackName = 'api.' + apiName + '.' + callbackId
const invokeCallback = function (res) {
callback(res)
}
invokeCallbacks[callbackId] = {
name: invokeCallbackName,
keepAlive: true,
callback: invokeCallback
}
return callbackId
}
function createApiCallback (apiName, params = {}, extras = {}) {
if (!isPlainObject(params)) {
return {
params
}
}
params = Object.assign({}, params)
const apiCallbacks = {}
for (let name in params) {
const param = params[name]
if (isFn(param)) {
apiCallbacks[name] = tryCatch(param)
delete params[name]
}
}
const {
success,
fail,
cancel,
complete
} = apiCallbacks
const hasSuccess = isFn(success)
const hasFail = isFn(fail)
const hasCancel = isFn(cancel)
const hasComplete = isFn(complete)
if (!hasSuccess && !hasFail && !hasCancel && !hasComplete) { // 无回调
return {
params
}
}
const wrapperCallbacks = {}
for (let name in extras) {
const extra = extras[name]
if (isFn(extra)) {
wrapperCallbacks[name] = tryCatchFramework(extra)
delete extras[name]
}
}
const {
beforeSuccess,
afterSuccess,
beforeFail,
afterFail,
beforeCancel,
afterCancel,
afterAll
} = wrapperCallbacks
const callbackId = invokeCallbackId++
const invokeCallbackName = 'api.' + apiName + '.' + callbackId
const invokeCallback = function (res) {
res.errMsg = res.errMsg || apiName + ':ok'
const errMsg = res.errMsg
if (errMsg.indexOf(apiName + ':ok') === 0) {
isFn(beforeSuccess) && beforeSuccess(res)
hasSuccess && success(res)
isFn(afterSuccess) && afterSuccess(res)
} else if (errMsg.indexOf(apiName + ':cancel') === 0) {
res.errMsg = res.errMsg.replace(apiName + ':cancel', apiName + ':fail cancel')
hasFail && fail(res)
isFn(beforeCancel) && beforeCancel(res)
hasCancel && cancel(res)
isFn(afterCancel) && afterCancel(res)
} else if (errMsg.indexOf(apiName + ':fail') === 0) {
isFn(beforeFail) && beforeFail(res)
hasFail && fail(res)
isFn(afterFail) && afterFail(res)
}
hasComplete && complete(res)
isFn(afterAll) && afterAll(res)
}
invokeCallbacks[callbackId] = {
name: invokeCallbackName,
callback: invokeCallback
}
return {
params,
callbackId
}
}
function createInvokeCallback (apiName, params = {}, extras = {}) {
const {
params: args,
callbackId
} = createApiCallback(apiName, params, extras)
if (isPlainObject(args) && !validateParams(apiName, args, callbackId)) {
return {
params: args,
callbackId: false
}
}
return {
params: args,
callbackId
}
}
export function invokeCallbackHandler (invokeCallbackId, res) {
if (typeof invokeCallbackId === 'number') {
const invokeCallback = invokeCallbacks[invokeCallbackId]
if (invokeCallback) {
if (!invokeCallback.keepAlive) {
delete invokeCallbacks[invokeCallbackId]
}
return invokeCallback.callback(res)
}
}
return res
}
const SYNC_API_RE = /hideKeyboard|upx2px|canIUse|^create|Sync$|Manager$/
const TASK_APIS = ['request', 'downloadFile', 'uploadFile', 'connectSocket']
const CALLBACK_API_RE = /^on/
function isSyncApi (name) {
return SYNC_API_RE.test(name)
}
function isCallbackApi (name) {
return CALLBACK_API_RE.test(name)
}
function isTaskApi (name) {
return TASK_APIS.indexOf(name) !== -1
}
function handlePromise (promise) {
return promise.then(data => {
return [null, data]
})
.catch(err => [err])
}
function shouldPromise (name) {
if (isSyncApi(name)) {
return false
}
if (isCallbackApi(name)) {
return false
}
return true
}
export function promisify (name, api) {
if (!shouldPromise(name)) {
return api
}
return function promiseApi (options = {}, ...params) {
if (isFn(options.success) || isFn(options.fail) || isFn(options.complete)) {
return api(options, ...params)
}
return handlePromise(new Promise((resolve, reject) => {
api(Object.assign({}, options, {
success: resolve,
fail: reject
}), ...params)
/* eslint-disable no-extend-native */
Promise.prototype.finally = function (callback) {
const promise = this.constructor
return this.then(
value => promise.resolve(callback()).then(() => value),
reason => promise.resolve(callback()).then(() => {
throw reason
})
)
}
}))
}
}
export function wrapperUnimplemented (name) {
return function (args) {
console.error('API `' + name + '` is not yet implemented')
}
}
export function wrapper (name, invokeMethod, extras) {
return function (...args) {
if (isSyncApi(name)) {
if (validateParams(name, args, -1)) {
return invokeMethod.apply(null, args)
}
} else if (isCallbackApi(name)) {
if (validateParams(name, args, -1)) {
return invokeMethod(createKeepAliveApiCallback(name, args[0]))
}
} else {
let argsObj = {}
if (args.length) {
argsObj = args[0]
}
const {
params,
callbackId
} = createInvokeCallback(name, argsObj, extras)
if (callbackId !== false) {
let res
if (isFn(params)) {
res = invokeMethod(callbackId)
} else {
res = invokeMethod(params, callbackId)
}
if (res && !isTaskApi(name)) {
res = invokeCallbackHandler(callbackId, res)
if (isPlainObject(res)) {
res.errMsg = res.errMsg || name + ':ok'
}
}
return res
}
}
}
}
const callbacks = {}
export default function createCallbacks (namespace) {
let scopedCallbacks = callbacks[namespace]
if (!scopedCallbacks) {
scopedCallbacks = {
id: 1,
callbacks: Object.create(null)
}
callbacks[namespace] = scopedCallbacks
}
return {
get (id) {
return scopedCallbacks.callbacks[id]
},
pop (id) {
const callback = scopedCallbacks.callbacks[id]
if (callback) {
delete scopedCallbacks.callbacks[id]
}
return callback
},
push (callback) {
const id = scopedCallbacks.id++
scopedCallbacks.callbacks[id] = callback
return id
}
}
}
/**
* 框架内 try-catch
*/
export function tryCatchFramework (fn) {
return function () {
try {
return fn.apply(fn, arguments)
} catch (e) {
// TODO
console.error(e)
}
}
}
/**
* 开发者 try-catch
*/
export function tryCatch (fn) {
return function () {
try {
return fn.apply(fn, arguments)
} catch (e) {
// TODO
console.error(e)
}
}
}
const unshift = Array.prototype.unshift
function format (args) {
unshift.call(args, '[system]')
return args
}
function createLog (method) {
return function () {
let printLog = true
if (method === 'debug' && !__uniConfig.debug) {
printLog = false
}
printLog && global.console[method].apply(global.console, format(arguments))
}
}
export default {
log: createLog('log'),
info: createLog('info'),
warn: createLog('warn'),
debug: createLog('debug'),
error: createLog('error')
}
export const NAVBAR_HEIGHT = 44
export const TABBAR_HEIGHT = 50
export default function getRealRoute (fromRoute, toRoute) {
if (!toRoute) {
toRoute = fromRoute
if (toRoute.indexOf('/') === 0) {
return toRoute
}
const pages = getCurrentPages()
if (pages.length) {
fromRoute = pages[pages.length - 1].$page.route
} else {
fromRoute = ''
}
} else {
if (toRoute.indexOf('/') === 0) {
return toRoute
}
}
if (toRoute.indexOf('./') === 0) {
return getRealRoute(fromRoute, toRoute.substr(2))
}
const toRouteArray = toRoute.split('/')
const toRouteLength = toRouteArray.length
let i = 0
for (; i < toRouteLength && toRouteArray[i] === '..'; i++) {
// noop
}
toRouteArray.splice(0, i)
toRoute = toRouteArray.join('/')
const fromRouteArray = fromRoute.length > 0 ? fromRoute.split('/') : []
fromRouteArray.splice(fromRouteArray.length - i - 1, i + 1)
return '/' + fromRouteArray.concat(toRouteArray).join('/')
}
/*
* HTML5 Parser By Sam Blowes
*
* Designed for HTML5 documents
*
* Original code by John Resig (ejohn.org)
* http://ejohn.org/blog/pure-javascript-html-parser/
* Original code by Erik Arvidsson, Mozilla Public License
* http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
*
* ----------------------------------------------------------------------------
* License
* ----------------------------------------------------------------------------
*
* This code is triple licensed using Apache Software License 2.0,
* Mozilla Public License or GNU Public License
*
* ////////////////////////////////////////////////////////////////////////////
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy
* of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* ////////////////////////////////////////////////////////////////////////////
*
* The contents of this file are subject to the Mozilla Public License
* Version 1.1 (the "License"); you may not use this file except in
* compliance with the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS"
* basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the
* License for the specific language governing rights and limitations
* under the License.
*
* The Original Code is Simple HTML Parser.
*
* The Initial Developer of the Original Code is Erik Arvidsson.
* Portions created by Erik Arvidssson are Copyright (C) 2004. All Rights
* Reserved.
*
* ////////////////////////////////////////////////////////////////////////////
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
* ----------------------------------------------------------------------------
* Usage
* ----------------------------------------------------------------------------
*
* // Use like so:
* HTMLParser(htmlString, {
* start: function(tag, attrs, unary) {},
* end: function(tag) {},
* chars: function(text) {},
* comment: function(text) {}
* });
*
* // or to get an XML string:
* HTMLtoXML(htmlString);
*
* // or to get an XML DOM Document
* HTMLtoDOM(htmlString);
*
* // or to inject into an existing document/DOM node
* HTMLtoDOM(htmlString, document);
* HTMLtoDOM(htmlString, document.body);
*
*/
// Regular Expressions for parsing tags and attributes
var startTag =
/^<([-A-Za-z0-9_]+)((?:\s+[a-zA-Z_:][-a-zA-Z0-9_:.]*(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)>/
var endTag = /^<\/([-A-Za-z0-9_]+)[^>]*>/
var attr = /([a-zA-Z_:][-a-zA-Z0-9_:.]*)(?:\s*=\s*(?:(?:"((?:\\.|[^"])*)")|(?:'((?:\\.|[^'])*)')|([^>\s]+)))?/g
// Empty Elements - HTML 5
var empty = makeMap(
'area,base,basefont,br,col,frame,hr,img,input,link,meta,param,embed,command,keygen,source,track,wbr')
// Block Elements - HTML 5
var block = makeMap(
'a,address,article,applet,aside,audio,blockquote,button,canvas,center,dd,del,dir,div,dl,dt,fieldset,figcaption,figure,footer,form,frameset,h1,h2,h3,h4,h5,h6,header,hgroup,hr,iframe,ins,isindex,li,map,menu,noframes,noscript,object,ol,output,p,pre,section,script,table,tbody,td,tfoot,th,thead,tr,ul,video'
)
// Inline Elements - HTML 5
var inline = makeMap(
'abbr,acronym,applet,b,basefont,bdo,big,br,button,cite,code,del,dfn,em,font,i,iframe,img,input,ins,kbd,label,map,object,q,s,samp,script,select,small,span,strike,strong,sub,sup,textarea,tt,u,var'
)
// Elements that you can, intentionally, leave open
// (and which close themselves)
var closeSelf = makeMap('colgroup,dd,dt,li,options,p,td,tfoot,th,thead,tr')
// Attributes that have their values filled in disabled="disabled"
var fillAttrs = makeMap(
'checked,compact,declare,defer,disabled,ismap,multiple,nohref,noresize,noshade,nowrap,readonly,selected')
// Special Elements (can contain anything)
var special = makeMap('script,style')
export default function HTMLParser (html, handler) {
var index; var chars; var match; var stack = []
var last = html
stack.last = function () {
return this[this.length - 1]
}
while (html) {
chars = true
// Make sure we're not in a script or style element
if (!stack.last() || !special[stack.last()]) {
// Comment
if (html.indexOf('<!--') == 0) {
index = html.indexOf('-->')
if (index >= 0) {
if (handler.comment) { handler.comment(html.substring(4, index)) }
html = html.substring(index + 3)
chars = false
}
// end tag
} else if (html.indexOf('</') == 0) {
match = html.match(endTag)
if (match) {
html = html.substring(match[0].length)
match[0].replace(endTag, parseEndTag)
chars = false
}
// start tag
} else if (html.indexOf('<') == 0) {
match = html.match(startTag)
if (match) {
html = html.substring(match[0].length)
match[0].replace(startTag, parseStartTag)
chars = false
}
}
if (chars) {
index = html.indexOf('<')
var text = index < 0 ? html : html.substring(0, index)
html = index < 0 ? '' : html.substring(index)
if (handler.chars) { handler.chars(text) }
}
} else {
html = html.replace(new RegExp('([\\s\\S]*?)<\/' + stack.last() + '[^>]*>'), function (all, text) {
text = text.replace(/<!--([\s\S]*?)-->|<!\[CDATA\[([\s\S]*?)]]>/g, '$1$2')
if (handler.chars) { handler.chars(text) }
return ''
})
parseEndTag('', stack.last())
}
if (html == last) { throw 'Parse Error: ' + html }
last = html
}
// Clean up any remaining tags
parseEndTag()
function parseStartTag (tag, tagName, rest, unary) {
tagName = tagName.toLowerCase()
if (block[tagName]) {
while (stack.last() && inline[stack.last()]) {
parseEndTag('', stack.last())
}
}
if (closeSelf[tagName] && stack.last() == tagName) {
parseEndTag('', tagName)
}
unary = empty[tagName] || !!unary
if (!unary) { stack.push(tagName) }
if (handler.start) {
var attrs = []
rest.replace(attr, function (match, name) {
var value = arguments[2] ? arguments[2]
: arguments[3] ? arguments[3]
: arguments[4] ? arguments[4]
: fillAttrs[name] ? name : ''
attrs.push({
name: name,
value: value,
escaped: value.replace(/(^|[^\\])"/g, '$1\\\"') // "
})
})
if (handler.start) { handler.start(tagName, attrs, unary) }
}
}
function parseEndTag (tag, tagName) {
// If no tag name is provided, clean shop
if (!tagName) { var pos = 0 }
// Find the closest opened tag of the same type
else {
for (var pos = stack.length - 1; pos >= 0; pos--) {
if (stack[pos] == tagName) { break }
}
}
if (pos >= 0) {
// Close all the open elements, up the stack
for (var i = stack.length - 1; i >= pos; i--) {
if (handler.end) { handler.end(stack[i]) }
}
// Remove the open elements from the stack
stack.length = pos
}
}
};
function makeMap (str) {
var obj = {}
var items = str.split(',')
for (var i = 0; i < items.length; i++) { obj[items[i]] = true }
return obj
}
const components = ['SystemAsyncLoading', 'SystemAsyncError']
export function isPage (vm) {
if (vm.$parent && vm.$parent.$options.name === 'PageBody') {
if (components.indexOf(vm.$options.name) !== -1) {
return false
}
return true
}
return false
}
export function normalizeDataset (dataset = {}) {
const result = Object.assign({}, dataset)
if (__PLATFORM__ === 'h5') {
const keys = Object.keys(result)
const len = keys.length
if (len) {
// remove data-v-
for (let i = 0; i < len; i++) {
const key = keys[i]
const len = key.length
if (key.substr(0, 1) === 'v' && (len === 9 || len === 10)) {
delete result[key]
break
}
}
}
}
return result
}
export function upx2px (str) {
str = str + ''
if (str.indexOf('upx') !== -1) { // upx转换
return uni.upx2px(parseInt(str) || 0)
}
return parseInt(str) || 0
}
import {
isFn,
hasOwn,
toRawType,
isPlainObject
} from 'uni-shared'
export default function validateParam (key, paramTypes, paramsData) {
const paramOptions = paramTypes[key]
const absent = !hasOwn(paramsData, key)
let value = paramsData[key]
const booleanIndex = getTypeIndex(Boolean, paramOptions.type)
if (booleanIndex > -1) {
if (absent && !hasOwn(paramOptions, 'default')) {
value = false
}
}
if (value === undefined) {
if (hasOwn(paramOptions, 'default')) {
const paramDefault = paramOptions['default']
value = isFn(paramDefault) ? paramDefault() : paramDefault
paramsData[key] = value // 默认值
}
}
return assertParam(paramOptions, key, value, absent, paramsData)
}
function assertParam (
paramOptions,
name,
value,
absent,
paramsData
) {
if (paramOptions.required && absent) {
return `Missing required parameter \`${name}\``
}
if (value == null && !paramOptions.required) {
const validator = paramOptions.validator
if (validator) {
return validator(value, paramsData)
}
return
}
let type = paramOptions.type
let valid = !type || type === true
const expectedTypes = []
if (type) {
if (!Array.isArray(type)) {
type = [type]
}
for (let i = 0; i < type.length && !valid; i++) {
const assertedType = assertType(value, type[i])
expectedTypes.push(assertedType.expectedType || '')
valid = assertedType.valid
}
}
if (!valid) {
return getInvalidTypeMessage(name, value, expectedTypes)
}
const validator = paramOptions.validator
if (validator) {
return validator(value, paramsData)
}
}
const simpleCheckRE = /^(String|Number|Boolean|Function|Symbol)$/
function assertType (value, type) {
let valid
const expectedType = getType(type)
if (simpleCheckRE.test(expectedType)) {
const t = typeof value
valid = t === expectedType.toLowerCase()
if (!valid && t === 'object') {
valid = value instanceof type
}
} else if (expectedType === 'Object') {
valid = isPlainObject(value)
} else if (expectedType === 'Array') {
valid = Array.isArray(value)
} else {
valid = value instanceof type
}
return {
valid,
expectedType
}
}
function getType (fn) {
const match = fn && fn.toString().match(/^\s*function (\w+)/)
return match ? match[1] : ''
}
function isSameType (a, b) {
return getType(a) === getType(b)
}
function getTypeIndex (type, expectedTypes) {
if (!Array.isArray(expectedTypes)) {
return isSameType(expectedTypes, type) ? 0 : -1
}
for (let i = 0, len = expectedTypes.length; i < len; i++) {
if (isSameType(expectedTypes[i], type)) {
return i
}
}
return -1
}
function getInvalidTypeMessage (name, value, expectedTypes) {
let message = `parameter \`${name}\`.` +
` Expected ${expectedTypes.join(', ')}`
const expectedType = expectedTypes[0]
const receivedType = toRawType(value)
const expectedValue = styleValue(value, expectedType)
const receivedValue = styleValue(value, receivedType)
if (expectedTypes.length === 1 &&
isExplicable(expectedType) &&
!isBoolean(expectedType, receivedType)) {
message += ` with value ${expectedValue}`
}
message += `, got ${receivedType} `
if (isExplicable(receivedType)) {
message += `with value ${receivedValue}.`
}
return message
}
function styleValue (value, type) {
if (type === 'String') {
return `"${value}"`
} else if (type === 'Number') {
return `${Number(value)}`
} else {
return `${value}`
}
}
const explicitTypes = ['string', 'number', 'boolean']
function isExplicable (value) {
return explicitTypes.some(elem => value.toLowerCase() === elem)
}
function isBoolean (...args) {
return args.some(elem => elem.toLowerCase() === 'boolean')
}
import {
hasOwn,
isPlainObject
} from 'uni-shared'
/**
* mpvue event
*/
export function wrapperMPEvent (event) {
return Object.assign({
mp: event,
_processed: true
}, event)
}
/**
* app-plus titleNView
*/
export function mergeTitleNView (navigationBar, titleNView) {
if (isPlainObject(titleNView)) {
if (hasOwn(titleNView, 'backgroundColor')) {
navigationBar.backgroundColor = titleNView.backgroundColor
}
if (hasOwn(titleNView, 'buttons')) {
navigationBar.buttons = titleNView.buttons
}
if (hasOwn(titleNView, 'titleColor')) {
navigationBar.textColor = titleNView.titleColor
}
if (hasOwn(titleNView, 'titleText')) {
navigationBar.titleText = titleNView.titleText
}
if (hasOwn(titleNView, 'titleSize')) {
navigationBar.titleSize = titleNView.titleSize
}
if (hasOwn(titleNView, 'type')) {
navigationBar.type = titleNView.type
}
}
return navigationBar
}
export const canIUse = [{
name: 'schema',
type: String,
required: true
}]
const validator = [{
name: 'id',
type: String,
required: true
}]
export const createAudioContext = validator
export const createVideoContext = validator
export const createMapContext = validator
export const makePhoneCall = {
'phoneNumber': {
type: String,
required: true,
validator (phoneNumber) {
if (!phoneNumber) {
return `makePhoneCall:fail parameter error: parameter.phoneNumber should not be empty String;`
}
}
}
}
export const openDocument = {
filePath: {
type: String,
required: true
},
fileType: {
type: String
}
}
const protocol = Object.create(null)
const modules = require.context('./', true, /\.js$/)
modules.keys().forEach(function (key) {
if (key !== './index.js') {
Object.assign(protocol, modules(key))
}
})
export default protocol
const type = {
WGS84: 'WGS84',
GCJ02: 'GCJ02'
}
export const getLocation = {
type: {
type: String,
validator (value, params) {
value = (value || '').toUpperCase()
params.type = Object.values(type).indexOf(value) < 0 ? type.WGS84 : value
},
default: type.WGS84
},
altitude: {
altitude: Boolean,
default: false
}
}
export const openLocation = {
latitude: {
type: Number,
required: true
},
longitude: {
type: Number,
required: true
},
scale: {
type: Number,
validator (value, params) {
value = Math.floor(value)
params.scale = value >= 5 && value <= 18 ? value : 18
},
default: 18
},
name: {
type: String
},
address: {
type: String
}
}
const SIZE_TYPES = ['original', 'compressed']
const SOURCE_TYPES = ['album', 'camera']
export const chooseImage = {
'count': {
type: Number,
required: false,
default: 9,
validator (count, params) {
if (count <= 0) {
params.count = 9
}
}
},
'sizeType': {
type: Array,
required: false,
default: SIZE_TYPES,
validator (sizeType, params) {
// 非必传的参数,不符合预期时处理为默认值。
const length = sizeType.length
if (!length) {
params.sizeType = SIZE_TYPES
} else {
for (let i = 0; i < length; i++) {
if (typeof sizeType[i] !== 'string' || !~SIZE_TYPES.indexOf(sizeType[i])) {
params.sizeType = SIZE_TYPES
break
}
}
}
}
},
'sourceType': {
type: Array,
required: false,
default: SOURCE_TYPES,
validator (sourceType, params) {
const length = sourceType.length
if (!length) {
params.sourceType = SOURCE_TYPES
} else {
for (let i = 0; i < length; i++) {
if (typeof sourceType[i] !== 'string' || !~SOURCE_TYPES.indexOf(sourceType[i])) {
params.sourceType = SOURCE_TYPES
break
}
}
}
}
}
}
const SOURCE_TYPES = ['album', 'camera']
export const chooseVideo = {
'sourceType': {
type: Array,
required: false,
default: SOURCE_TYPES,
validator (sourceType, params) {
const length = sourceType.length
if (!length) {
params.sourceType = SOURCE_TYPES
} else {
for (let i = 0; i < length; i++) {
if (typeof sourceType[i] !== 'string' || !~SOURCE_TYPES.indexOf(sourceType[i])) {
params.sourceType = SOURCE_TYPES
break
}
}
}
}
}
}
import getRealPath from 'uni-platform/helpers/get-real-path'
export const getImageInfo = {
'src': {
type: String,
required: true,
validator (src, params) {
params.src = getRealPath(src)
}
}
}
import getRealPath from 'uni-platform/helpers/get-real-path'
export const previewImage = {
urls: {
type: Array,
required: true,
validator (value, params) {
var typeError
params.urls = value.map(url => {
if (typeof url === 'string') {
return getRealPath(url)
} else {
typeError = true
}
})
if (typeError) {
return 'url is not string'
}
}
},
current: {
type: String,
validator (value, params) {
params.type = value ? getRealPath(value) : ''
}
}
}
const FRONT_COLORS = ['#ffffff', '#000000']
export const setNavigationBarColor = {
'frontColor': {
type: String,
required: true,
validator (frontColor, params) {
if (FRONT_COLORS.indexOf(frontColor) === -1) {
return `invalid frontColor "${frontColor}"`
}
}
},
'backgroundColor': {
type: String,
required: true
},
'animation': {
type: Object,
default () {
return {
duration: 0,
timingFunc: 'linear'
}
},
validator (animation = {}, params) {
params.animation = {
duration: animation.duration || 0,
timingFunc: animation.timingFunc || 'linear'
}
}
}
}
export const setNavigationBarTitle = {
'title': {
type: String,
required: true
}
}
export const downloadFile = {
url: {
type: String,
required: true
},
header: {
type: Object,
validator (value, params) {
params.header = value || {}
}
}
}
const method = {
OPTIONS: 'OPTIONS',
GET: 'GET',
HEAD: 'HEAD',
POST: 'POST',
PUT: 'PUT',
DELETE: 'DELETE',
TRACE: 'TRACE',
CONNECT: 'CONNECT'
}
const dataType = {
JSON: 'JSON'
}
const responseType = {
TEXT: 'TEXT',
ARRAYBUFFER: 'ARRAYBUFFER'
}
export const request = {
url: {
type: String,
required: true
},
data: {
type: [Object, String, ArrayBuffer],
validator (value, params) {
params.data = value || ''
}
},
header: {
type: Object,
validator (value, params) {
params.header = value || {}
}
},
method: {
type: String,
validator (value, params) {
value = (value || '').toUpperCase()
params.method = Object.values(method).indexOf(value) < 0 ? method.GET : value
}
},
dataType: {
type: String,
validator (value, params) {
params.dataType = (value || dataType.JSON).toUpperCase()
}
},
responseType: {
type: String,
validator (value, params) {
value = (value || '').toUpperCase()
params.responseType = Object.values(responseType).indexOf(value) < 0 ? responseType.TEXT : value
}
}
}
const method = {
OPTIONS: 'OPTIONS',
GET: 'GET',
HEAD: 'HEAD',
POST: 'POST',
PUT: 'PUT',
DELETE: 'DELETE',
TRACE: 'TRACE',
CONNECT: 'CONNECT'
}
export const connectSocket = {
url: {
type: String,
required: true
},
header: {
type: Object,
validator (value, params) {
params.header = value || {}
}
},
method: {
type: String,
validator (value, params) {
value = (value || '').toUpperCase()
params.method = Object.values(method).indexOf(value) < 0 ? method.GET : value
}
},
protocols: {
type: Array,
validator (value, params) {
params.protocols = (value || []).filter(str => typeof str === 'string')
}
}
}
export const sendSocketMessage = {
data: {
type: [String, ArrayBuffer]
}
}
export const closeSocket = {
code: {
type: Number
},
reason: {
type: String
}
}
import getRealPath from 'uni-platform/helpers/get-real-path'
export const uploadFile = {
url: {
type: String,
required: true
},
filePath: {
type: String,
required: true,
validator (value, params) {
params.type = getRealPath(value)
}
},
name: {
type: String,
required: true
},
header: {
type: Object,
validator (value, params) {
params.header = value || {}
}
},
formData: {
type: Object,
validator (value, params) {
params.formData = value || {}
}
}
}
export const pageScrollTo = {
scrollTop: {
type: Number,
required: true
},
duration: {
type: Number,
default: 300,
validator (duration, params) {
params.duration = Math.max(0, duration)
}
}
}
const service = {
OAUTH: 'OAUTH',
SHARE: 'SHARE',
PAYMENT: 'PAYMENT',
PUSH: 'PUSH'
}
export const getProvider = {
service: {
type: String,
required: true,
validator (value, params) {
value = (value || '').toUpperCase()
if (value && Object.values(service).indexOf(value) < 0) {
return 'service error'
}
}
}
}
import getRealPath from 'uni-platform/helpers/get-real-path'
export const showModal = {
title: {
type: String,
default: ''
},
content: {
type: String,
default: ''
},
showCancel: {
type: Boolean,
default: true
},
cancelText: {
type: String,
default: '取消'
},
cancelColor: {
type: String,
default: '#000000'
},
confirmText: {
type: String,
default: '确定'
},
confirmColor: {
type: String,
default: '#007aff'
},
visible: {
type: Boolean,
default: true
}
}
export const showToast = {
title: {
type: String,
default: ''
},
icon: {
default: 'success',
validator (icon, params) {
if (['success', 'loading', 'none'].indexOf(icon) === -1) {
params.icon = 'success'
}
}
},
image: {
type: String,
default: '',
validator (image, params) {
if (image) {
params.image = getRealPath(image)
}
}
},
duration: {
type: Number,
default: 1500
},
mask: {
type: Boolean,
default: false
},
visible: {
type: Boolean,
default: true
}
}
export const showLoading = {
title: {
type: String,
default: ''
},
icon: {
type: String,
default: 'loading'
},
duration: {
type: Number,
default: 100000000 // 简单处理 showLoading,直接设置个大值
},
mask: {
type: Boolean,
default: false
},
visible: {
type: Boolean,
default: true
}
}
export const showActionSheet = {
itemList: {
type: Array,
required: true,
validator (itemList, params) {
if (!itemList.length) {
return 'parameter.itemList should have at least 1 item'
}
}
},
itemColor: {
type: String,
default: '#000000'
},
visible: {
type: Boolean,
default: true
}
}
import getRealRoute from '../get-real-route'
function encodeQueryString (url) {
if (typeof url === 'string') {
const urls = url.split('?')
url = urls[0]
const params = [];
(urls[1] || '').split('&').forEach(function (pair) {
if (pair) {
const pairs = pair.split('=')
params.push(pairs[0] + '=' + encodeURIComponent(pairs[1]))
}
})
return params.length ? url + '?' + params.join('&') : url
}
return url
}
function createValidator (type) {
return function validator (url, params) {
// 格式化为绝对路径路由
url = getRealRoute(url)
const pagePath = url.split('?')[0]
// 匹配路由是否存在
const routeOptions = __uniRoutes.find(({
path,
alias
}) => path === pagePath || alias === pagePath)
if (!routeOptions) {
return 'page `' + url + '` is not found'
}
// 检测不同类型跳转
if (type === 'navigateTo' || type === 'redirectTo') {
if (routeOptions.meta.isTabBar) {
return `can not ${type} a tabbar page`
}
} else if (type === 'switchTab') {
if (!routeOptions.meta.isTabBar) {
return 'can not switch to no-tabBar page'
}
}
// tabBar不允许传递参数
if (routeOptions.meta.isTabBar) {
url = pagePath
}
// 首页自动格式化为`/`
if (routeOptions.meta.isEntry) {
url = url.replace(routeOptions.alias, '/')
}
// 参数格式化
params.url = encodeQueryString(url)
}
}
function createProtocol (type) {
return {
url: {
type: String,
required: true,
validator: createValidator(type)
}
}
}
export const redirectTo = createProtocol('redirectTo')
export const reLaunch = createProtocol('reLaunch')
export const navigateTo = createProtocol('navigateTo')
export const switchTab = createProtocol('switchTab')
export const navigateBack = {
delta: {
type: Number,
validator (delta, params) {
delta = parseInt(delta) || 1
params.delta = Math.min(getCurrentPages().length - 1, delta)
}
}
}
export const setStorage = {
'key': {
type: String,
required: true
},
'data': {
required: true
}
}
export const setStorageSync = [{
name: 'key',
type: String,
required: true
}, {
name: 'data',
required: true
}]
import {
getLen
} from 'uni-shared'
function beforeValidate (params) {
let isTabBar = false
const pages = getCurrentPages()
if (pages.length) {
if (pages[pages.length - 1].$page.meta.isTabBar) {
isTabBar = true
}
}
if (!isTabBar) {
return 'not TabBar page'
}
}
const indexValidator = {
type: Number,
required: true,
validator (index, params) {
if (index >= __uniConfig.tabBar.list.length) {
return 'tabbar item not found'
}
}
}
export const setTabBarItem = {
beforeValidate,
index: indexValidator,
text: {
type: String
},
iconPath: {
type: String
},
selectedIconPath: {
type: String
}
}
export const setTabBarStyle = {
beforeValidate,
color: {
type: String
},
selectedColor: {
type: String
},
backgroundColor: {
type: String
},
borderStyle: {
type: String,
validator (borderStyle, params) {
if (borderStyle) {
params.borderStyle = borderStyle === 'black' ? 'black' : 'white'
}
}
}
}
export const hideTabBar = {
beforeValidate,
animation: {
type: Boolean,
default: false
}
}
export const showTabBar = {
beforeValidate,
animation: {
type: Boolean,
default: false
}
}
export const hideTabBarRedDot = {
beforeValidate,
index: indexValidator
}
export const showTabBarRedDot = {
beforeValidate,
index: indexValidator
}
export const removeTabBarBadge = {
beforeValidate,
index: indexValidator
}
export const setTabBarBadge = {
beforeValidate,
index: indexValidator,
text: {
type: String,
required: true,
validator (text, params) {
if (getLen(text) >= 4) {
params.text = '...'
}
}
}
}
module.exports = [
'uni-app',
'uni-tabbar',
'uni-page',
'uni-page-head',
'uni-page-wrapper',
'uni-page-body',
'uni-page-refresh',
'uni-actionsheet',
'uni-modal',
'uni-picker',
'uni-toast',
'uni-ad',
'uni-audio',
'uni-button',
'uni-camera',
'uni-canvas',
'uni-checkbox',
'uni-checkbox-group',
'uni-cover-image',
'uni-cover-view',
'uni-form',
'uni-functional-page-navigator',
// 'uni-icon',
'uni-image',
'uni-input',
'uni-label',
'uni-live-player',
'uni-live-pusher',
'uni-map',
'uni-movable-area',
'uni-movable-view',
'uni-navigator',
'uni-official-account',
'uni-open-data',
'uni-picker',
'uni-picker-view',
'uni-picker-view-column',
'uni-progress',
'uni-radio',
'uni-radio-group',
'uni-rich-text',
'uni-scroll-view',
'uni-slider',
'uni-swiper',
'uni-swiper-item',
'uni-switch',
'uni-text',
'uni-textarea',
'uni-video',
'uni-view',
'uni-web-view'
]
import {
hasOwn
} from 'uni-shared'
import platformSchema from 'uni-platform/helpers/can-i-use'
// TODO 待处理其他 API 的检测
export function canIUse (schema) {
if (hasOwn(platformSchema, schema)) {
return platformSchema[schema]
}
return true
}
function operateAudioPlayer (audioId, pageId, type, data) {
UniServiceJSBridge.publishHandler(pageId + '-audio-' + audioId, {
audioId,
type,
data
}, pageId)
}
class AudioContext {
constructor (id, pageId) {
this.id = id
this.pageId = pageId
}
setSrc (src) {
operateAudioPlayer(this.id, this.pageId, 'setSrc', {
src
})
}
play () {
operateAudioPlayer(this.id, this.pageId, 'play')
}
pause () {
operateAudioPlayer(this.id, this.pageId, 'pause')
}
stop () {
operateAudioPlayer(this.id, this.pageId, 'stop')
}
seek (position) {
operateAudioPlayer(this.id, this.pageId, 'seek', {
position
})
}
}
export function createAudioContext (id, context) {
if (context) {
return new AudioContext(id, context.$page.id)
}
const app = getApp()
if (app.$route && app.$route.params.__id__) {
return new AudioContext(id, app.$route.params.__id__)
} else {
UniServiceJSBridge.emit('onError', 'createAudioContext:fail')
}
}
/**
* 可以批量设置的监听事件
*/
var innerAudioContextEventNames = ['onCanplay', 'onPlay', 'onPause', 'onStop', 'onEnded', 'onTimeUpdate', 'onError', 'onWaiting', 'onSeeking', 'onSeeked']
/**
* 音频上下文对象
*/
class InnerAudioContext {
/**
* 原始音频对象
*/
_audio
/**
* 是否暂停中
*/
_stoping
/**
* 开始时间
*/
startTime
/**
* 事件监听
*/
_events
/**
* 音频上下文初始化
*/
constructor () {
var audio = this._audio = new Audio()
this._stoping = false
// 和audio对象同名同效果的属性
var watchers = ['src', 'autoplay', 'loop', 'duration', 'currentTime', 'paused', 'volume']
watchers.forEach((watcher) => {
Object.defineProperty(this, watcher, {
set (val) {
audio[watcher] = val
return audio[watcher]
},
get () {
return audio[watcher]
}
})
})
this.startTime = 0
Object.defineProperty(this, 'obeyMuteSwitch', {
set (val) {
return false
},
get () {
return false
}
})
Object.defineProperty(this, 'buffered', {
get () {
var buffered = audio.buffered
if (buffered.length) {
return buffered[buffered.length - 1].end()
} else {
return 0
}
}
})
// 初始化事件监听列表
this._events = {}
innerAudioContextEventNames.forEach(eventName => {
this._events[eventName] = []
})
audio.addEventListener('loadedmetadata', () => {
var startTime = Number(this.startTime) || 0
if (startTime > 0) {
audio.currentTime = startTime
}
})
// 和audio对象同名同效果的事件
var eventNames = ['canplay', 'play', 'pause', 'ended', 'timeUpdate', 'error', 'waiting', 'seeking', 'seeked']
var stopEventNames = ['pause', 'seeking', 'seeked', 'timeUpdate']
eventNames.forEach(eventName => {
audio.addEventListener(eventName.toLowerCase(), () => {
// stop事件过滤
if (this._stoping && stopEventNames.indexOf(eventName) >= 0) {
return
}
this._events[`on${eventName.substr(0, 1).toUpperCase()}${eventName.substr(1)}`].forEach((callback) => {
callback()
})
}, false)
})
}
/**
* 播放
*/
play () {
this._stoping = false
this._audio.play()
}
/**
* 暂停
*/
pause () {
this._audio.pause()
}
/**
* 停止
*/
stop () {
this._stoping = true
this._audio.pause()
this._audio.currentTime = 0
this._events.onStop.forEach((callback) => {
callback()
})
}
/**
* 跳转到
* @param {number} position
*/
seek (position) {
this._stoping = false
position = Number(position)
if (typeof position === 'number' && !isNaN(position)) {
this._audio.currentTime = position
}
}
/**
* 销毁
*/
destroy () {
this.stop()
}
}
// 批量设置音频上下文事件监听方法
innerAudioContextEventNames.forEach((eventName) => {
InnerAudioContext.prototype[eventName] = function (callback) {
if (typeof callback === 'function') {
this.event[eventName].push(callback)
}
}
})
/**
* 创建音频上下文
*/
export function createInnerAudioContext () {
return new InnerAudioContext()
}
function operateMapPlayer (mapId, pageId, type, data) {
UniServiceJSBridge.publishHandler(pageId + '-map-' + mapId, {
mapId,
type,
data
}, pageId)
}
class MapContext {
constructor (id, pageId) {
this.id = id
this.pageId = pageId
}
getCenterLocation ({
success,
fail,
complete
}) {
operateMapPlayer(this.id, this.pageId, 'getCenterLocation', {
success,
fail,
complete
})
}
moveToLocation () {
operateMapPlayer(this.id, this.pageId, 'moveToLocation')
}
translateMarker ({
markerId,
destination,
autoRotate,
rotate,
duration,
animationEnd,
fail
}) {
operateMapPlayer(this.id, this.pageId, 'translateMarker', {
markerId,
destination,
autoRotate,
rotate,
duration,
animationEnd,
fail
})
}
includePoints ({
points,
padding
}) {
operateMapPlayer(this.id, this.pageId, 'includePoints', {
points,
padding
})
}
getRegion ({
success,
fail,
complete
}) {
operateMapPlayer(this.id, this.pageId, 'getRegion', {
success,
fail,
complete
})
}
getScale ({
success,
fail,
complete
}) {
operateMapPlayer(this.id, this.pageId, 'getScale', {
success,
fail,
complete
})
}
}
export function createMapContext (id, context) {
if (context) {
return new MapContext(id, context.$page.id)
}
const app = getApp()
if (app.$route && app.$route.params.__id__) {
return new MapContext(id, app.$route.params.__id__)
} else {
UniServiceJSBridge.emit('onError', 'createMapContext:fail')
}
}
function operateVideoPlayer (videoId, pageId, type, data) {
UniServiceJSBridge.publishHandler(pageId + '-video-' + videoId, {
videoId,
type,
data
}, pageId)
}
const RATES = [0.5, 0.8, 1.0, 1.25, 1.5]
class VideoContext {
constructor (id, pageId) {
this.id = id
this.pageId = pageId
}
play () {
operateVideoPlayer(this.id, this.pageId, 'play')
}
pause () {
operateVideoPlayer(this.id, this.pageId, 'pause')
}
stop () {
operateVideoPlayer(this.id, this.pageId, 'stop')
}
seek (position) {
operateVideoPlayer(this.id, this.pageId, 'seek', {
position
})
}
sendDanmu ({
text,
color
} = {}) {
operateVideoPlayer(this.id, this.pageId, 'sendDanmu', {
text,
color
})
}
playbackRate (rate) {
if (!~RATES.indexOf(rate)) {
rate = 1.0
}
operateVideoPlayer(this.id, this.pageId, 'playbackRate', {
rate
})
}
requestFullScreen () {
operateVideoPlayer(this.id, this.pageId, 'requestFullScreen')
}
exitFullScreen () {
operateVideoPlayer(this.id, this.pageId, 'exitFullScreen')
}
showStatusBar () {
operateVideoPlayer(this.id, this.pageId, 'showStatusBar')
}
hideStatusBar () {
operateVideoPlayer(this.id, this.pageId, 'hideStatusBar')
}
}
export function createVideoContext (id, context) {
if (context) {
return new VideoContext(id, context.$page.id)
}
const app = getApp()
if (app.$route && app.$route.params.__id__) {
return new VideoContext(id, app.$route.params.__id__)
} else {
UniServiceJSBridge.emit('onError', 'createVideoContext:fail')
}
}
const api = Object.create(null)
const modules = require.context('./', true, /\.js$/)
modules.keys().forEach(function (key) {
if (key !== './index.js') {
Object.assign(api, modules(key))
}
})
export default api
/**
* 查看位置
* @param {*} param0
* @param {*} callbackId
*/
export function openLocation ({
latitude,
longitude,
scale,
name,
address
}, callbackId) {
const {
invokeCallbackHandler: invoke
} = UniServiceJSBridge
getApp().$router.push({
type: 'navigateTo',
path: '/open-location',
params: {
latitude,
longitude,
scale,
name,
address
}
}, function () {
invoke(callbackId, {
errMsg: 'openLocation:ok'
})
}, function () {
invoke(callbackId, {
errMsg: 'openLocation:fail'
})
})
}
/**
* 选择位置
* @param {*} callbackId
*/
export function chooseLocation (options, callbackId) {
const {
invokeCallbackHandler: invoke
} = UniServiceJSBridge
getApp().$router.push({
type: 'navigateTo',
path: '/choose-location'
}, function () {
var fn = data => {
UniServiceJSBridge.unsubscribe('onChooseLocation', fn)
if (data) {
invoke(callbackId, Object.assign(data, {
errMsg: 'chooseLocation:ok'
}))
} else {
invoke(callbackId, {
errMsg: 'chooseLocation:fail'
})
}
}
UniServiceJSBridge.subscribe('onChooseLocation', fn)
}, function () {
invoke(callbackId, {
errMsg: 'chooseLocation:fail'
})
})
}
export function getImageInfo ({
src
}, callbackId) {
const {
invokeCallbackHandler: invoke
} = UniServiceJSBridge
const img = new Image()
img.onload = function () {
invoke(callbackId, {
errMsg: 'getImageInfo:ok',
width: img.naturalWidth,
height: img.naturalHeight
})
}
img.onerror = function (e) {
invoke(callbackId, {
errMsg: 'getImageInfo:fail'
})
}
img.src = src
}
export function previewImage ({
urls,
current
}, callbackId) {
const {
invokeCallbackHandler: invoke
} = UniServiceJSBridge
getApp().$router.push({
type: 'navigateTo',
path: '/preview-image',
params: {
urls,
current
}
}, function () {
invoke(callbackId, {
errMsg: 'previewImage:ok'
})
}, function () {
invoke(callbackId, {
errMsg: 'previewImage:fail'
})
})
}
function setNavigationBar (type, args) {
const pages = getCurrentPages()
if (pages.length) {
const page = pages[pages.length - 1].$parent.$parent
switch (type) {
case 'setNavigationBarColor':
const {
frontColor,
backgroundColor,
animation: {
duration,
timingFunc
}
} = args
if (frontColor) {
page.navigationBar.textColor = frontColor === '#000000' ? 'black' : 'white'
}
if (backgroundColor) {
page.navigationBar.backgroundColor = backgroundColor
}
page.navigationBar.duration = duration + 'ms'
page.navigationBar.timingFunc = timingFunc
break
case 'showNavigationBarLoading':
page.navigationBar.loading = true
break
case 'hideNavigationBarLoading':
page.navigationBar.loading = false
break
case 'setNavigationBarTitle':
const {
title
} = args
page.navigationBar.titleText = title
break
}
}
return {}
}
export function setNavigationBarColor (args) {
return setNavigationBar('setNavigationBarColor', args)
}
export function showNavigationBarLoading () {
return setNavigationBar('showNavigationBarLoading')
}
export function hideNavigationBarLoading () {
return setNavigationBar('hideNavigationBarLoading')
}
export function setNavigationBarTitle (args) {
return setNavigationBar('setNavigationBarTitle', args)
}
/**
* 请求任务类
*/
class RequestTask {
_xhr
constructor (xhr) {
this._xhr = xhr
}
abort () {
if (this._xhr) {
this._xhr.abort()
delete this._xhr
}
}
}
/**
* 拼接网址和参数
* @param {string} url 网址
* @param {any} data 参数
* @return {string}
*/
function setUrl (url, data) {
var str = url.split('#')
var hash = str[1] || ''
str = str[0].split('?')
var query = str[1] || ''
url = str[0]
for (var key in data) {
if (data.hasOwnProperty(key)) {
var keyEncode = encodeURIComponent(key)
var valEncode = encodeURIComponent(data[key])
var reg = new RegExp('((^|&)' + keyEncode + '=)[^&]*(&|$)', 'i')
if (query.match(reg)) {
query.replace(reg, '$1' + valEncode)
} else {
query += '&' + keyEncode + '=' + valEncode
}
}
}
return url + '?' + query + (hash ? '#' + hash : '')
}
/**
* 解析响应头
* @param {string} headers
* @return {object}
*/
function parseHeaders (headers) {
var headersObject = {}
var headersArray = headers.split('\n')
headersArray.forEach(header => {
var find = header.match(/(\S+\s*):\s*(.*)/)
if (!find || find.length !== 3) {
return
}
var key = find[1]
var val = find[2]
headersObject[key] = val
})
return headersObject
}
/**
* 发起网络请求
* @param {object} param0
* @param {string} callbackId
* @return {RequestTask}
*/
export function request ({
url,
data,
header,
method,
dataType,
responseType
}, callbackId) {
const {
invokeCallbackHandler: invoke
} = UniServiceJSBridge
var body = null
var timeout = (__uniConfig.networkTimeout && __uniConfig.networkTimeout.request) || 60 * 1000
// 根据请求类型处理数据
var contentType
for (const key in header) {
if (header.hasOwnProperty(key)) {
if (key.toLowerCase() === 'content-type') {
contentType = header[key]
if (contentType.indexOf('application/json') === 0) {
contentType = 'json'
} else if (contentType.indexOf('application/x-www-form-urlencoded') === 0) {
contentType = 'urlencoded'
} else {
contentType = 'string'
}
break
}
}
}
if (method === 'GET') {
url = setUrl(url, data)
} else {
if (!contentType) {
/**
* 跨域时部分服务器OPTION响应头Access-Control-Allow-Headers未包含Content-Type会请求失败
*/
header['Content-Type'] = 'application/json'
contentType = 'json'
}
if (typeof data === 'string' || data instanceof ArrayBuffer) {
body = data
} else {
if (contentType === 'json') {
try {
body = JSON.stringify(data)
} catch (error) {
body = data.toString()
}
} else if (contentType === 'urlencoded') {
let bodyArray = []
for (let key in data) {
if (data.hasOwnProperty(key)) {
bodyArray.push(encodeURIComponent(key) + '=' + encodeURIComponent(data[key]))
}
}
body = bodyArray.join('&')
} else {
body = data.toString()
}
}
}
var xhr = new XMLHttpRequest()
var requestTask = new RequestTask(xhr)
xhr.open(method, url)
for (var key in header) {
if (header.hasOwnProperty(key)) {
xhr.setRequestHeader(key, header[key])
}
}
var timer = setTimeout(function () {
xhr.onload = xhr.onabort = xhr.onerror = null
requestTask.abort()
invoke(callbackId, {
errMsg: 'request:fail timeout'
})
}, timeout)
xhr.responseType = responseType.toLowerCase()
xhr.onload = function () {
clearTimeout(timer)
let statusCode = xhr.status
let res = responseType === 'TEXT' ? xhr.responseText : xhr.response
if (responseType === 'TEXT' && dataType === 'JSON') {
try {
res = JSON.parse(res)
} catch (error) {
// 和微信一致解析失败不抛出错误
// invoke(callbackId, {
// errMsg: 'request:fail json parse error'
// })
// return
}
}
invoke(callbackId, {
data: res,
statusCode,
header: parseHeaders(xhr.getAllResponseHeaders()),
errMsg: 'request:ok'
})
}
xhr.onabort = function () {
clearTimeout(timer)
invoke(callbackId, {
errMsg: 'request:fail abort'
})
}
xhr.onerror = function () {
clearTimeout(timer)
invoke(callbackId, {
errMsg: 'request:fail'
})
}
xhr.send(body)
return requestTask
}
var socketTask
/**
* SocketTask
*/
class SocketTask {
/**
* WebSocket实例
*/
_webSocket
/**
* 构造函数
* @param {string} url
* @param {Array} protocols
*/
constructor (url, protocols) {
this._webSocket = new WebSocket(url, protocols)
}
/**
* 发送
* @param {any} data
*/
send (options = {}) {
var data = options.data
const ws = this._webSocket
try {
ws.send(data)
this._callback(options, 'sendSocketMessage:ok')
} catch (error) {
this._callback(options, `sendSocketMessage:fail ${error}`)
}
}
/**
* 关闭
* @param {number} code
* @param {string} reason
*/
close (options = {}) {
var code = options.data
var reason = options.data
const ws = this._webSocket
try {
ws.close(code, reason)
this._callback(options, 'sendSocketMessage:ok')
} catch (error) {
this._callback(options, `sendSocketMessage:fail ${error}`)
}
}
/**
* 监听开启
* @param {Function} callback
*/
onOpen (callback) {
this._on('open', callback)
}
/**
* 监听关闭
* @param {Function} callback
*/
onClose (callback) {
this._on('close', callback)
}
/**
* 监听错误
* @param {Function} callback
*/
onError (callback) {
this._on('error', callback)
}
/**
* 监听消息
* @param {Function} callback
*/
onMessage (callback) {
this._on('message', callback)
}
/**
* 监听事件
* @param {string} eventName
* @param {Function} callback
*/
_on (eventName, callback) {
this._webSocket.addEventListener(eventName, event => {
if (eventName === 'message') {
callback({
data: event.data
})
} else {
callback()
}
}, false)
}
/**
* 通用回调处理
*/
_callback ({
success,
fail,
complete
}, errMsg) {
var data = {
errMsg
}
if (/:ok$/.test(errMsg)) {
if (typeof success === 'function') {
success(data)
}
} else {
if (typeof fail === 'function') {
fail(data)
}
}
if (typeof complete === 'function') {
complete(data)
}
}
}
/**
* 创建一个 WebSocket 连接
* @param {any} data 数据
* @return {SocketTask}
*/
export function connectSocket ({
url,
protocols
}, callbackId) {
const {
invokeCallbackHandler: invoke
} = UniServiceJSBridge
socketTask = new SocketTask(url, protocols)
setTimeout(() => {
invoke(callbackId, {
errMsg: 'connectSocket:ok'
})
}, 0)
return socketTask
}
/**
* 通过 WebSocket 连接发送数据
* @param {any} options
* @param {string} callbackId
*/
export function sendSocketMessage (options, callbackId) {
const {
invokeCallbackHandler: invoke
} = UniServiceJSBridge
if (socketTask && socketTask._webSocket.readyState === WebSocket.OPEN) {
socketTask.send(Object.assign(options, {
complete (res) {
invoke(callbackId, res)
}
}))
} else {
invoke(callbackId, {
errMsg: 'sendSocketMessage:fail WebSocket is not connected '
})
}
}
/**
* 关闭WebSocket连接
* @param {any} options
* @param {string} callbackId
*/
export function closeSocket (options, callbackId) {
const {
invokeCallbackHandler: invoke
} = UniServiceJSBridge
if (socketTask && socketTask._webSocket.readyState !== WebSocket.CLOSED) {
socketTask.close(Object.assign(options, {
complete (res) {
invoke(callbackId, res)
}
}))
} else {
invoke(callbackId, {
errMsg: 'closeSocket:fail WebSocket is not connected'
})
}
}
/**
* 监听事件
* @param {string} method
* @param {Function} callback
*/
function on (method) {
return function (callback) {
if (socketTask) {
socketTask[method](callback)
}
}
}
/**
* 监听WebSocket连接打开事件
* @param {Function} cb
*/
export const onSocketOpen = on('onOpen')
/**
* 监听WebSocket错误
* @param {Function} cb
*/
export const onSocketError = on('onError')
/**
* 监听WebSocket接受到服务器的消息事件
* @param {Function} cb
*/
export const onSocketMessage = on('onMessage')
/**
* 监听WebSocket关闭
* @param {Function} callback
*/
export const onSocketClose = on('onClose')
export function pageScrollTo (args) {
const pages = getCurrentPages()
if (pages.length) {
UniServiceJSBridge.publishHandler('pageScrollTo', args, pages[pages.length - 1].$page.id)
}
return {}
}
let pageId
export function setPullDownRefreshPageId (pullDownRefreshPageId) {
pageId = pullDownRefreshPageId
}
export function startPullDownRefresh () {
if (pageId) {
UniServiceJSBridge.emit(pageId + '.stopPullDownRefresh', {}, pageId)
}
const pages = getCurrentPages()
if (pages.length) {
pageId = pages[pages.length - 1].$page.id
UniServiceJSBridge.emit(pageId + '.startPullDownRefresh', {}, pageId)
}
return {}
}
export function stopPullDownRefresh () {
if (pageId) {
UniServiceJSBridge.emit(pageId + '.stopPullDownRefresh', {}, pageId)
pageId = null
}
return {}
}
const {
emit,
invokeCallbackHandler: invoke
} = UniServiceJSBridge
export function showModal (args, callbackId) {
emit('onShowModal', args, function (type) {
invoke(callbackId, {
[type]: true
})
})
}
export function showToast (args) {
emit('onShowToast', args)
return {}
}
export function hideToast () {
emit('onHideToast')
return {}
}
export function showLoading (args) {
emit('onShowToast', args)
return {}
}
export function hideLoading () {
emit('onHideToast')
return {}
}
export function showActionSheet (args, callbackId) {
emit('onShowActionSheet', args, function (tapIndex) {
if (tapIndex === -1) {
invoke(callbackId, {
errMsg: 'showActionSheet:fail cancel'
})
} else {
invoke(callbackId, {
tapIndex
})
}
})
}
import {
isFn
} from 'uni-shared'
function onAppRoute (type, {
url,
delta,
from = 'navigateBack'
} = {}) {
const router = getApp().$router
switch (type) {
case 'redirectTo':
router.replace({
type,
path: url
})
break
case 'navigateTo':
router.push({
type,
path: url
})
break
case 'navigateBack':
let canBack = true
const pages = getCurrentPages()
if (pages.length) {
const page = pages[pages.length - 1]
if (isFn(page.$options.onBackPress) && page.$options.onBackPress.call(page, {
from
}) === true) {
canBack = false
}
}
if (canBack) {
router.go(-delta)
}
break
case 'reLaunch':
router.replace({
type,
path: url
})
break
case 'switchTab':
router.replace({
type,
path: url
})
break
}
return {
errMsg: type + ':ok'
}
}
export function redirectTo (args) {
return onAppRoute('redirectTo', args)
}
export function navigateTo (args) {
return onAppRoute('navigateTo', args)
}
export function navigateBack (args) {
return onAppRoute('navigateBack', args)
}
export function reLaunch (args) {
return onAppRoute('reLaunch', args)
}
export function switchTab (args) {
return onAppRoute('switchTab', args)
}
export function setStorage ({
key,
data
} = {}) {
let value = {
type: typeof data === 'object' ? 'object' : 'string',
data: data
}
localStorage.setItem(key, JSON.stringify(value))
let keyList = localStorage.getItem('uni-storage-keys')
if (!keyList) {
localStorage.setItem('uni-storage-keys', JSON.stringify([key]))
} else {
let keys = JSON.parse(keyList)
if (keys.indexOf(key) < 0) {
keys.push(key)
localStorage.setItem('uni-storage-keys', JSON.stringify(keys))
}
}
return {
errMsg: 'setStorage:ok'
}
}
export function setStorageSync (key, data) {
setStorage({ key, data })
}
export function getStorage ({
key
} = {}) {
let data = localStorage.getItem(key)
return data ? {
data: JSON.parse(data).data,
errMsg: 'getStorage:ok'
} : {
data: '',
errMsg: 'getStorage:fail'
}
}
export function getStorageSync (key) {
const res = getStorage({ key })
return res.data
}
export function removeStorage ({
key
} = {}) {
let keyList = localStorage.getItem('uni-storage-keys')
if (keyList) {
let keys = JSON.parse(keyList)
let index = keys.indexOf(key)
keys.splice(index, 1)
localStorage.setItem('uni-storage-keys', JSON.stringify(keys))
}
localStorage.removeItem(key)
return {
errMsg: 'removeStorage:ok'
}
}
export function removeStorageSync (key) {
removeStorage({ key })
}
export function clearStorage () {
localStorage.clear()
return {
errMsg: 'clearStorage:ok'
}
}
export function clearStorageSync () {
clearStorage()
}
export function getStorageInfo () { // TODO 暂时先不做大小的转换
let keyList = localStorage.getItem('uni-storage-keys')
return keyList ? {
keys: JSON.parse(keyList),
currentSize: 0,
limitSize: 0,
errMsg: 'getStorageInfo:ok'
} : {
keys: '',
currentSize: 0,
limitSize: 0,
errMsg: 'getStorageInfo:fail'
}
}
export function getStorageInfoSync () {
const res = getStorageInfo()
delete res.errMsg
return res
}
import {
setProperties
} from 'uni-shared'
const setTabBarItemProps = ['text', 'iconPath', 'selectedIconPath']
const setTabBarStyleProps = ['color', 'selectedColor', 'backgroundColor', 'borderStyle']
const setTabBarBadgeProps = ['badge', 'redDot']
function setTabBar (type, args = {}) {
const app = getApp()
if (app) {
const {
index
} = args
const tabBar = app.$children[0].tabBar
switch (type) {
case 'showTabBar':
app.$children[0].hideTabBar = false
break
case 'hideTabBar':
app.$children[0].hideTabBar = true
break
case 'setTabBarItem':
setProperties(tabBar.list[index], setTabBarItemProps, args)
break
case 'setTabBarStyle':
setProperties(tabBar, setTabBarStyleProps, args)
break
case 'showTabBarRedDot':
setProperties(tabBar.list[index], setTabBarBadgeProps, {
badge: '',
redDot: true
})
break
case 'setTabBarBadge':
setProperties(tabBar.list[index], setTabBarBadgeProps, {
badge: args.text,
redDot: true
})
break
case 'hideTabBarRedDot':
case 'removeTabBarBadge':
setProperties(tabBar.list[index], setTabBarBadgeProps, {
badge: '',
redDot: false
})
break
}
}
return {}
}
export function setTabBarItem (args) {
return setTabBar('setTabBarItem', args)
}
export function setTabBarStyle (args) {
return setTabBar('setTabBarStyle', args)
}
export function hideTabBar (args) {
return setTabBar('hideTabBar', args)
}
export function showTabBar (args) {
return setTabBar('showTabBar', args)
}
export function hideTabBarRedDot (args) {
return setTabBar('hideTabBarRedDot', args)
}
export function showTabBarRedDot (args) {
return setTabBar('showTabBarRedDot', args)
}
export function removeTabBarBadge (args) {
return setTabBar('removeTabBarBadge', args)
}
export function setTabBarBadge (args) {
return setTabBar('setTabBarBadge', args)
}
import {
isFn
} from 'uni-shared'
import createCallbacks from 'uni-helpers/callbacks'
const requestComponentInfoCallbacks = createCallbacks('requestComponentInfo')
class NodesRef {
constructor (selectorQuery, component, selector, single) {
this._selectorQuery = selectorQuery
this._component = component
this._selector = selector
this._single = single
}
boundingClientRect (callback) {
this._selectorQuery._push(
this._selector,
this._component,
this._single, {
id: true,
dataset: true,
rect: true,
size: true
},
callback)
return this._selectorQuery
}
fields (fields, callback) {
this._selectorQuery._push(
this._selector,
this._component,
this._single,
fields,
callback
)
return this._selectorQuery
}
scrollOffset (callback) {
this._selectorQuery._push(
this._selector,
this._component,
this._single, {
id: true,
dataset: true,
scrollOffset: true
},
callback
)
return this._selectorQuery
}
}
function requestComponentInfo (pageId, queue, callback) {
const reqId = requestComponentInfoCallbacks.push(callback)
UniServiceJSBridge.publishHandler('requestComponentInfo', {
reqId,
reqs: queue
}, pageId)
}
class SelectorQuery {
constructor (pageId) {
this.pageId = pageId
this._queue = []
this._queueCb = []
}
exec (callback) {
requestComponentInfo(this.pageId, this._queue, res => {
const queueCbs = this._queueCb
res.forEach((result, index) => {
const queueCb = queueCbs[index]
if (isFn(queueCb)) {
queueCb.call(this, result)
}
})
isFn(callback) && callback.call(this, res)
})
}
['in'] (component) {
console.error('Unsupported method:SelectorQuery.in')
return this
}
select (selector) {
return new NodesRef(this, this._component, selector, true)
}
selectAll (selector) {
return new NodesRef(this, this._component, selector, false)
}
selectViewport () {
return new NodesRef(this, 0, '', true)
}
_push (selector, component, single, fields, callback) {
this._queue.push({
component,
selector,
single,
fields
})
this._queueCb.push(callback)
}
}
export function createSelectorQuery (context) {
if (context) {
return new SelectorQuery(context.$page.id)
}
const app = getApp()
if (app.$route && app.$route.params.__id__) {
return new SelectorQuery(app.$route.params.__id__)
} else {
UniServiceJSBridge.emit('onError', 'createSelectorQuery:fail')
}
}
import Vue from 'vue'
import initOn from './on'
import initSubscribe from './subscribe'
const Emitter = new Vue()
export const on = Emitter.$on.bind(Emitter)
export const off = Emitter.$off.bind(Emitter)
export const once = Emitter.$once.bind(Emitter)
export const emit = Emitter.$emit.bind(Emitter)
export {
invokeCallbackHandler
}
from 'uni-helpers/api'
export function subscribe (event, callback) {
return on('view.' + event, callback)
}
export function unsubscribe (event, callback) {
return off('view.' + event, callback)
}
export function subscribeHandler (event, args, pageId) {
return emit('view.' + event, args, pageId)
}
export {
publishHandler
}
from 'uni-platform/service/bridge'
initOn(on)
initSubscribe(subscribe)
import {
callAppHook,
callPageHook
} from '../plugins/util'
import {
setPullDownRefreshPageId
} from '../api/page-event'
function onError (err) {
callAppHook(getApp(), 'onError', err)
}
function onPageNotFound (page) {
callAppHook(getApp(), 'onPageNotFound', page)
}
function onPullDownRefresh (args, pageId) {
const page = getCurrentPages().find(page => page.$page.id === pageId)
if (page) {
setPullDownRefreshPageId(pageId)
callPageHook(page, 'onPullDownRefresh')
}
}
function callCurrentPageHook (hook, ...args) {
const pages = getCurrentPages()
if (pages.length) {
callPageHook(pages[pages.length - 1], hook, ...args)
}
}
function createCallCurrentPageHook (hook) {
return function (...args) {
callCurrentPageHook(hook, ...args)
}
}
function onAppEnterBackground () {
callAppHook(getApp(), 'onHide')
callCurrentPageHook('onHide')
}
function onAppEnterForeground () {
callAppHook(getApp(), 'onShow')
callCurrentPageHook('onShow')
}
export default function initOn (on) {
on('onError', onError)
on('onPageNotFound', onPageNotFound)
on('onAppEnterBackground', onAppEnterBackground)
on('onAppEnterForeground', onAppEnterForeground)
on('onPullDownRefresh', onPullDownRefresh)
on('onTabItemTap', createCallCurrentPageHook('onTabItemTap'))
on('onNavigationBarButtonTap', createCallCurrentPageHook('onNavigationBarButtonTap'))
}
import createCallbacks from 'uni-helpers/callbacks'
import {
callPageHook
} from '../plugins/util'
function createPageEvent (eventType) {
return function (args, pageId) {
const pages = getCurrentPages()
const page = pages.find(page => page.$page.id === pageId)
if (page) {
callPageHook(page, eventType, args)
} else {
console.error(`Not Found:Page[${pageId}]`)
}
}
}
const requestComponentInfoCallbacks = createCallbacks('requestComponentInfo')
function onRequestComponentInfo ({
reqId,
res
}) {
const callback = requestComponentInfoCallbacks.pop(reqId)
if (callback) {
callback(res)
}
}
const requestComponentObserverCallbacks = createCallbacks('requestComponentObserver')
function onRequestComponentObserver ({
reqId,
reqEnd,
res
}) {
const callback = requestComponentObserverCallbacks.get(reqId)
if (callback) {
if (reqEnd) {
requestComponentObserverCallbacks.pop(reqId)
}
callback(res)
}
}
export default function initSubscribe (subscribe) {
subscribe('onPageReady', createPageEvent('onReady'))
subscribe('onPageScroll', createPageEvent('onPageScroll'))
subscribe('onReachBottom', createPageEvent('onReachBottom'))
subscribe('onRequestComponentInfo', onRequestComponentInfo)
subscribe('onRequestComponentObserver', onRequestComponentObserver)
}
import {
wrapper,
wrapperUnimplemented,
promisify
} from 'uni-helpers/api'
import todoApis from 'uni-platform/helpers/todo-api'
import baseApi from './api'
import platformApi from 'uni-platform/service/api'
const uni = Object.create(null)
/* eslint-disable no-undef */
uni.version = __VERSION__
todoApis.forEach(name => {
uni[name] = wrapperUnimplemented(name)
})
Object.keys(baseApi).forEach(name => {
uni[name] = promisify(name, wrapper(name, baseApi[name]))
})
Object.keys(platformApi).forEach(name => {
uni[name] = promisify(name, wrapper(name, platformApi[name]))
})
export {
getApp,
getCurrentPages
}
from './plugins/app'
export default uni
import initRouterGuard from './router-guard'
let appVm = false
export function getApp () {
return appVm
}
export function getCurrentPages (isAll = false) {
const pages = []
const childrenVm = appVm.$children[0]
if (childrenVm && childrenVm.$children.length) {
const tabBarVm = childrenVm.$children.find(vm => vm.$options.name === 'TabBar')
const app = getApp()
childrenVm.$children.forEach(vm => {
if (tabBarVm !== vm && vm.$children.length && vm.$children[0].$options.name === 'Page' && vm.$children[0].$slots.page) {
// vm.$children[0]=Page->PageBody->RealPage
const pageVm = vm.$children[0].$children.find(vm => vm.$options.name === 'PageBody').$children.find(vm => !!vm.$page)
if (pageVm) {
let isActive = true
if (!isAll && tabBarVm && pageVm.$page && pageVm.$page.meta.isTabBar) { // 选项卡仅列出活动的
if (app.$route.meta && app.$route.meta.isTabBar) { // 当前页面路由是 tabBar
if (app.$route.path !== pageVm.$page.path) {
isActive = false
}
} else {
if (tabBarVm.__path__ !== pageVm.$page.path) {
isActive = false
}
}
}
if (isActive) {
pages.push(pageVm)
}
} else {
// TODO
// console.error('pageVm is undefined')
}
}
})
}
return pages
}
export default function createApp (vm, routes) {
appVm = vm
appVm.globalData = appVm.$options.globalData || {}
// initEvents(appVm)
initRouterGuard(appVm, routes)
}
import {
callAppHook
} from '../util'
import createApp from './create-app'
export {
getApp,
getCurrentPages
}
from './create-app'
export function createAppMixin (routes, entryRoute) {
return {
created: function AppCreated () {
createApp(this, routes)
// TODO
if (!entryRoute.meta.name) { // PageNotFound
UniServiceJSBridge.emit('onPageNotFound', {
path: entryRoute.path,
query: entryRoute.query,
isEntryPage: true
})
// TODO 跳转至缺省404页面
}
},
beforeMount: function appBeforeMount () {
// TODO 平台代码
this.$el = document.getElementById('app')
},
mounted: function appMounted () {
// 稍微靠后点,让 App 有机会在 mounted 事件前注册一些全局事件监听,如 UI 显示(showModal)
callAppHook(this, 'onLaunch', {
scene: 1001
})
callAppHook(this, 'onShow', {})
}
}
}
import {
callPageHook
} from '../util'
function addKeepAliveInclude (componentName) {
if (this.keepAliveInclude.indexOf(componentName) === -1) { // 目标页面,自动 include
this.keepAliveInclude.push(componentName)
}
}
function removeKeepAliveInclude (componentName) {
const index = this.keepAliveInclude.indexOf(componentName)
if (index !== -1) {
this.keepAliveInclude.splice(index, 1)
}
}
function switchTab (routes) {
// 关闭非 tabBar 页面
const pages = getCurrentPages()
for (let i = pages.length - 1; i >= 0; i--) {
const pageVm = pages[i]
const meta = pageVm.$page.meta
if (!meta.isTabBar) {
removeKeepAliveInclude.call(this, meta.name + '-' + pageVm.$page.id)
callPageHook(pageVm, 'onUnload')
}
}
}
function reLaunch (toName) {
__uniConfig.reLaunch = (__uniConfig.reLaunch || 1) + 1
// 关闭所有页面
const pages = getCurrentPages()
for (let i = pages.length - 1; i >= 0; i--) {
callPageHook(pages[i], 'onUnload')
}
this.keepAliveInclude = []
}
function beforeEach (to, from, next, routes) {
const fromId = from.params.__id__
const toId = to.params.__id__
if (toId === fromId) { // 相同页面阻止
next(false)
} else if (to.meta.id && to.meta.id !== toId) { // id 不妥,replace跳转
next({
path: to.path,
replace: true
})
} else {
const fromName = from.meta.name + '-' + fromId
const toName = to.meta.name + '-' + toId
switch (to.type) {
case 'navigateTo':
break
case 'redirectTo':
// 关闭前一个页面
removeKeepAliveInclude.call(this, fromName)
break
case 'switchTab':
switchTab.call(this, routes)
break
case 'reLaunch':
reLaunch.call(this, toName)
break
default:
// 后退或非 API 访问
if (fromId && fromId > toId) { // back
removeKeepAliveInclude.call(this, fromName)
}
break
}
if (to.type !== 'reLaunch' && from.meta.id) { // 如果不是 reLaunch,且 meta 指定了 id
addKeepAliveInclude.call(this, fromName)
}
// if (to.type !== 'reLaunch') { // TODO 如果 reLaunch,1.keepAlive的话,无法触发页面生命周期,并刷新页面,2.不 keepAlive 的话,页面状态无法再次保留,且 routeView 的 cache 有问题
addKeepAliveInclude.call(this, toName)
// }
if (process.env.NODE_ENV !== 'production') {
console.debug(`Core:keepAliveInclude=${JSON.stringify(this.keepAliveInclude)}`)
}
/* eslint-disable no-undef */
if (__PLATFORM__ === 'h5') {
if (to.meta && to.meta.name) {
document.body.className = 'uni-body ' + to.meta.name
}
}
next()
}
}
function afterEach (to, from) {
const fromId = from.params.__id__
const toId = to.params.__id__
const fromVm = getCurrentPages().find(pageVm => pageVm.$page.id === fromId)
switch (to.type) {
case 'navigateTo': // 前一个页面触发 onHide
fromVm && callPageHook(fromVm, 'onHide')
break
case 'redirectTo': // 前一个页面触发 onUnload
fromVm && callPageHook(fromVm, 'onUnload')
break
case 'switchTab':
if (from.meta.isTabBar) { // 前一个页面是 tabBar 触发 onHide,非 tabBar 页面在 beforeEach 中已触发 onUnload
fromVm && callPageHook(fromVm, 'onHide')
}
break
case 'reLaunch':
break
default:
if (fromId && fromId > toId) { // history back
fromVm && callPageHook(fromVm, 'onUnload')
}
break
}
if (to.type !== 'reLaunch') { // 因为 reLaunch 会重置 id,故不触发 onShow,switchTab 在 beforeRouteEnter 中触发
// 直接获取所有 pages,getCurrentPages 正常情况下仅返回页面栈内,传 true 则返回所有已存在(主要是 tabBar 页面)
const toVm = getCurrentPages(true).find(pageVm => pageVm.$page.id === toId)
if (toVm) { // 目标页面若已存在,则触发 onShow
callPageHook(toVm, 'onShow')
}
}
}
export default function initRouterGuard (appVm, routes) {
// 处理keepAliveInclude
appVm.$router.beforeEach(function (to, from, next) {
beforeEach.call(appVm, to, from, next, routes)
})
// 处理前进时的 onUnload,onHide 和后退时的 onShow
appVm.$router.afterEach(function (to, from) {
afterEach.call(appVm, to, from)
})
}
import VueRouter from 'vue-router'
import {
isFn
} from 'uni-shared'
import {
isPage
} from 'uni-helpers'
import {
createAppMixin
} from './app'
import {
createPageMixin
} from './page'
function getMinId (routes) {
let minId = 0
routes.forEach(route => {
if (route.meta.id) {
minId++
}
})
return minId
}
function getHash () {
const href = window.location.href
const index = href.indexOf('#')
return index === -1 ? '' : decodeURI(href.slice(index + 1))
}
function getLocation (base = '/') {
let path = decodeURI(window.location.pathname)
if (base && path.indexOf(base) === 0) {
path = path.slice(base.length)
}
return (path || '/') + window.location.search + window.location.hash
}
/**
* Service 层 Vue 插件
* 1.init keepAliveInclude?
* 2.init router
* 3.init entryRoute
* 4.hack vue _init (app)
* 5.use router
*/
export default {
install (Vue, {
routes
} = {}) {
const minId = getMinId(routes)
const router = new VueRouter({
id: minId,
mode: __uniConfig.router.mode,
base: __uniConfig.router.base,
routes,
scrollBehavior (to, from, savedPosition) {
if (savedPosition) {
return savedPosition
} else {
return {
x: 0,
y: 0
}
}
}
})
const keepAliveInclude = []
// 需跨平台,根据用户配置 hash 或 history 来调用
const entryRoute = router.match(__uniConfig.router.mode === 'history' ? getLocation(__uniConfig.router.base)
: getHash())
if (entryRoute.meta.name) {
if (entryRoute.meta.id) {
keepAliveInclude.push(entryRoute.meta.name + '-' + entryRoute.meta.id)
} else {
keepAliveInclude.push(entryRoute.meta.name + '-' + (minId + 1))
}
}
/* eslint-disable no-undef */
if (__PLATFORM__ === 'h5') {
if (entryRoute.meta && entryRoute.meta.name) {
document.body.className = 'uni-body ' + entryRoute.meta.name
}
}
Vue.mixin({
beforeCreate () {
const options = this.$options
if (options.mpType === 'app') {
options.data = function () {
return {
keepAliveInclude
}
}
const appMixin = createAppMixin(routes, entryRoute)
// mixin app hooks
Object.keys(appMixin).forEach(hook => {
options[hook] = options[hook] ? [].concat(appMixin[hook], options[hook]) : [appMixin[hook]]
})
// router
options.router = router
// onError
if (!isFn(options.onError)) {
options.onError = function (err) {
console.error(err)
}
}
} else if (isPage(this)) {
const pageMixin = createPageMixin()
// mixin page hooks
Object.keys(pageMixin).forEach(hook => {
options[hook] = options[hook] ? [].concat(pageMixin[hook], options[hook]) : [pageMixin[hook]]
})
} else {
if (this.$parent && this.$parent.__page__) {
this.__page__ = this.$parent.__page__
}
}
}
})
Object.defineProperty(Vue.prototype, '$page', {
get () {
return this.__page__
}
})
Vue.use(VueRouter)
}
}
export default function createPage (pageVm) {
const $route = pageVm.$route
pageVm.route = $route.meta.pagePath
pageVm.__page__ = {
id: $route.params.__id__,
path: $route.path,
route: $route.meta.pagePath,
meta: Object.assign({}, $route.meta)
}
}
import {
callPageHook
} from '../util'
import createPage from './create-page'
export function createPageMixin () {
return {
created: function pageCreated () {
createPage(this)
callPageHook(this, 'onLoad', this.$route.query)
callPageHook(this, 'onShow')
}
}
}
import {
isFn
} from 'uni-shared'
function callHook (vm, hook, params) {
return isFn(vm.$options[hook]) && vm.$options[hook].apply(vm, params)
}
export function callAppHook (vm, hook, ...params) {
if (hook !== 'onError') {
console.debug(`App:${hook} have been invoked` + (params.length ? ` ${JSON.stringify(params)}` : ''))
}
return callHook(vm, hook, params)
}
export function callPageHook (vm, hook, ...params) {
// hack 一下,H5 平台通知 View 层onShow,方便 View 层来切换 scroll 事件监听
if (__PLATFORM__ === 'h5') {
if (hook === 'onLoad') {
UniServiceJSBridge.publishHandler('onPageLoad', vm, vm.$page.id)
}
if (hook === 'onShow') {
UniServiceJSBridge.publishHandler('onPageShow', vm, vm.$page.id)
}
}
if (hook !== 'onPageScroll') {
console.debug(`${vm.$page.route}[${vm.$page.id}]:${hook} have been invoked`)
}
return callHook(vm, hook, params)
}
export * from './upx'
const EPS = 1e-4
const BASE_DEVICE_WIDTH = 750
const isIOS = navigator.userAgent.match('iPhone')
let deviceWidth = global.innerWidth || global.screen.width || 375
let deviceDPR = global.devicePixelRatio || 2
export function checkDeviceWidth () {
let newDeviceWidth = global.innerWidth || global.screen.width || 375
const newDeviceDPR = global.devicePixelRatio || 2
const newDeviceHeight = global.innerHeight || global.screen.height || 375
if (global.screen.orientation && /^landscape/.test(global.screen.orientation.type || '')) {
newDeviceWidth = newDeviceHeight
}
if (newDeviceWidth !== deviceWidth || newDeviceDPR !== deviceDPR) {
deviceWidth = newDeviceWidth
deviceDPR = newDeviceDPR
}
}
export function upx2px (number, newDeviceWidth) {
number = Number(number)
if (number === 0) {
return 0
}
number = (number / BASE_DEVICE_WIDTH) * (newDeviceWidth || deviceWidth)
number = Math.floor(number + EPS)
if (number === 0) {
if (deviceDPR === 1 || !isIOS) {
return 1
} else {
return 0.5
}
}
return number
}
import Vue from 'vue'
import initSubscribe from './subscribe'
const Emitter = new Vue()
export const on = Emitter.$on.bind(Emitter)
export const off = Emitter.$off.bind(Emitter)
export const once = Emitter.$once.bind(Emitter)
export const emit = Emitter.$emit.bind(Emitter)
export function subscribe (event, callback) {
return on('service.' + event, callback)
}
export function unsubscribe (event, callback) {
return off('service.' + event, callback)
}
export function subscribeHandler (event, args, pageId) {
emit('service.' + event, args, pageId)
}
export {
publishHandler
}
from 'uni-platform/view/bridge'
initSubscribe(subscribe)
import {
isFn,
isPlainObject,
supportsPassive
} from 'uni-shared'
import {
NAVBAR_HEIGHT,
TABBAR_HEIGHT
} from 'uni-helpers/constants'
import {
pageScrollTo,
disableScroll,
createScrollListener
} from './scroll'
import requestComponentInfo from './request-component-info'
const passiveOptions = supportsPassive ? {
passive: false
} : false
function updateCssVar (vm) {
if (uni.canIUse('css.var')) {
const pageVm = vm.$parent.$parent
const windowTop = pageVm.showNavigationBar && pageVm.navigationBar.type !== 'transparent' ? (NAVBAR_HEIGHT + 'px')
: '0px'
const windowBottom = getApp().$children[0].showTabBar ? (TABBAR_HEIGHT + 'px') : '0px'
const style = document.documentElement.style
style.setProperty('--window-top', windowTop)
style.setProperty('--window-bottom', windowBottom)
console.debug(`${vm.$page.route}[${vm.$page.id}]:--window-top=${windowTop}`)
console.debug(`${vm.$page.route}[${vm.$page.id}]:--window-bottom=${windowBottom}`)
}
}
export default function initSubscribe (subscribe) {
subscribe('requestComponentInfo', requestComponentInfo)
subscribe('pageScrollTo', pageScrollTo)
if (__PLATFORM__ === 'h5') {
let scrollListener = false
let disableScrollListener = false
subscribe('onPageLoad', vm => { // 用户 onLoad 之前 update
updateCssVar(vm)
})
subscribe('onPageShow', vm => {
const pageVm = vm.$parent.$parent
if (vm._isMounted) { // 非首次 show 才 update(首次 show 的时候在 onPageLoad 中触发了)
updateCssVar(vm)
}
if (disableScrollListener) {
document.removeEventListener('touchmove', disableScrollListener, passiveOptions)
}
if (pageVm.disableScroll) {
disableScrollListener = disableScroll
document.addEventListener('touchmove', disableScrollListener, passiveOptions)
}
const enablePageScroll = isFn(vm.$options.onPageScroll)
const enablePageReachBottom = isFn(vm.$options.onReachBottom)
const onReachBottomDistance = pageVm.onReachBottomDistance
const enableTransparentTitleNView = isPlainObject(pageVm.titleNView) && pageVm.titleNView.type === 'transparent'
if (scrollListener) {
document.removeEventListener('scroll', scrollListener)
}
if (enableTransparentTitleNView || enablePageScroll || enablePageReachBottom) { // 初始化 scroll 监听
scrollListener = createScrollListener(vm.$page.id, {
enablePageScroll,
enablePageReachBottom,
onReachBottomDistance,
enableTransparentTitleNView
})
setTimeout(function () { // 避免监听太早,直接触发了 scroll
document.addEventListener('scroll', scrollListener)
}, 10)
}
})
}
}
import {
normalizeDataset
} from 'uni-helpers'
function getRootInfo (fields) {
const info = {}
if (fields.id) {
info.id = ''
}
if (fields.dataset) {
info.dataset = {}
}
if (fields.rect) {
info.left = 0
info.right = 0
info.top = 0
info.bottom = 0
}
if (fields.size) {
info.width = document.documentElement.clientWidth
info.height = document.documentElement.clientHeight
}
if (fields.scrollOffset) {
info.scrollLeft = document.documentElement.scrollLeft || document.body.scrollLeft || 0
info.scrollTop = document.documentElement.scrollTop || document.body.scrollTop || 0
}
return info
}
function getNodeInfo (el, fields) {
const info = {}
if (fields.id) {
info.id = el.id
}
if (fields.dataset) {
info.dataset = normalizeDataset(el.dataset || {})
}
if (fields.rect || fields.size) {
const rect = el.getBoundingClientRect()
if (fields.rect) {
info.left = rect.left
info.right = rect.right
info.top = rect.top
info.bottom = rect.bottom
}
if (fields.size) {
info.width = rect.width
info.height = rect.height
}
}
// TODO 组件 props
if (fields.properties) {
fields.properties.forEach(prop => {
prop = prop.replace(/-([a-z])/g, function (e, t) {
return t.toUpperCase()
})
// props
})
}
if (fields.scrollOffset) {
if (el.tagName === 'UNI-SCROLL-VIEW' && el.__vue__ && el.__vue__.getScrollPosition) {
Object.assign(info, el.__vue__.getScrollPosition())
} else {
info.scrollLeft = 0
info.scrollTop = 0
}
}
return info
}
function getNodesInfo (pageVm, component, selector, single, fields) {
const $el = pageVm.$el
if (single) {
const node = $el.querySelector(selector)
if (node) {
return getNodeInfo(node, fields)
}
return null
} else {
const nodeList = $el.querySelectorAll(selector)
if (nodeList && nodeList.length) {
return ([]).map.call(nodeList, node => {
return getNodeInfo(node, fields)
})
}
return []
}
}
export default function requestComponentInfo ({
reqId,
reqs
}, pageId) {
const pages = getCurrentPages() // 跨平台时,View 层也应该实现该方法,举例 App 上,View 层的 getCurrentPages 返回长度为1的当前页面数组
const pageVm = pages.find(page => page.$page.id === pageId)
if (!pageVm) {
// TODO 是否需要 defer
throw new Error(`Not Found:Page[${pageId}]`)
}
const result = []
reqs.forEach(function ({
component,
selector,
single,
fields
}) {
if (component === 0) {
result.push(getRootInfo(fields))
} else {
result.push(getNodesInfo(pageVm, component, selector, single, fields))
}
})
UniViewJSBridge.publishHandler('onRequestComponentInfo', {
reqId,
res: result
}, pageVm.$page.id)
}
import {
publishHandler
} from 'uni-platform/view/bridge'
export function disableScroll (evt) {
evt.preventDefault()
}
export function pageScrollTo ({
scrollTop,
duration
}) {
const documentElement = document.documentElement
const {
clientHeight,
scrollHeight
} = documentElement
scrollTop = Math.min(scrollTop, scrollHeight - clientHeight)
if (duration === 0) {
documentElement.scrollTop = scrollTop
return
}
if (window.scrollY === scrollTop) {
return
}
function scrollTo (duration) {
if (duration <= 0) {
window.scrollTo(0, scrollTop)
return
}
const distaince = scrollTop - window.scrollY
requestAnimationFrame(function () {
window.scrollTo(0, window.scrollY + distaince / duration * 10)
scrollTo(duration - 10)
})
}
scrollTo(duration)
// TODO 暂不使用 transform 会导致 fixed 元素不可见
// const body = document.body
// const bodyStyle = body.style
//
// function webkitTransitionEnd() {
// bodyStyle.webkitTransition = ''
// bodyStyle.webkitTransform = ''
// documentElement.scrollTop = scrollTop
// body.removeEventListener('webkitTransitionEnd', webkitTransitionEnd)
// }
//
// body.addEventListener('webkitTransitionEnd', webkitTransitionEnd)
// bodyStyle.webkitTransition = `-webkit-transform ${duration}ms ease-out`
// bodyStyle.webkitTransform = `translateY(${documentElement.scrollTop}px) translateZ(0)`
}
export function createScrollListener (pageId, {
enablePageScroll,
enablePageReachBottom,
onReachBottomDistance,
enableTransparentTitleNView
}) {
let ticking = false
let hasReachBottom = false
let onReachBottom = true
function isReachBottom () {
const {
clientHeight,
scrollHeight
} = document.documentElement
const scrollY = window.scrollY
let isBottom = scrollY > 0 && scrollHeight > clientHeight && (scrollY + clientHeight + onReachBottomDistance) >=
scrollHeight
if (isBottom && !hasReachBottom) {
hasReachBottom = true
return true
}
if (!isBottom && hasReachBottom) {
hasReachBottom = false
}
return false
}
function trigger () {
// publish
const scrollTop = window.pageYOffset
if (enablePageScroll) { // 向 Service 发送 onPageScroll 事件
publishHandler('onPageScroll', {
scrollTop
}, pageId)
}
if (enableTransparentTitleNView) {
UniViewJSBridge.emit('onPageScroll', {
scrollTop
})
}
if (enablePageReachBottom && onReachBottom && isReachBottom()) {
publishHandler('onReachBottom', {}, pageId)
onReachBottom = false
setTimeout(function () {
onReachBottom = true
}, 350)
}
ticking = false
}
return function onScroll () {
if (!ticking) {
requestAnimationFrame(trigger)
}
ticking = true
}
}
<script>
import {
hover,
emitter,
listeners
} from 'uni-mixins'
export default {
name: 'Button',
mixins: [hover, emitter, listeners],
props: {
hoverClass: {
type: String,
default: 'button-hover'
},
disabled: {
type: [Boolean, String],
default: false
},
id: {
type: String,
default: ''
},
hoverStopPropagation: {
type: Boolean,
default: false
},
hoverStartTime: {
type: Number,
default: 20
},
hoverStayTime: {
type: Number,
default: 70
},
formType: {
type: String,
default: '',
validator (value) {
// 只有这几个可取值,其它都是非法的。
return ~['', 'submit', 'reset'].indexOf(value)
}
}
},
data () {
return {
clickFunction: null
}
},
methods: {
_onClick ($event, isLabelClick) {
if (this.disabled) {
return
}
if (isLabelClick) {
this.$el.click()
}
// TODO 通知父表单执行相应的行为
if (this.formType) {
this.$dispatch('Form', this.formType === 'submit' ? 'uni-form-submit' : 'uni-form-reset', {
type: this.formType
})
}
},
_bindObjectListeners (data, value) {
if (value) {
for (let key in value) {
let existing = data.on[key]
let ours = value[key]
data.on[key] = existing ? [].concat(existing, ours) : ours
}
}
return data
}
},
render (createElement) {
let $listeners = Object.create(null)
if (this.$listeners) {
Object.keys(this.$listeners).forEach(e => {
if (this.disabled && (e === 'click' || e === 'tap')) {
return
}
$listeners[e] = this.$listeners[e]
})
}
if (this.hoverClass && this.hoverClass !== 'none') {
return createElement('uni-button', this._bindObjectListeners({
class: [this.hovering ? this.hoverClass : ''],
attrs: {
'disabled': this.disabled
},
on: {
touchstart: this._hoverTouchStart,
touchend: this._hoverTouchEnd,
touchcancel: this._hoverTouchCancel,
click: this._onClick
}
}, $listeners), this.$slots.default)
} else {
return createElement('uni-button', this._bindObjectListeners({
class: [this.hovering ? this.hoverClass : ''],
attrs: {
'disabled': this.disabled
},
on: {
click: this._onClick
}
}, $listeners), this.$slots.default)
}
},
listeners: {
'label-click': '_onClick',
'@label-click': '_onClick'
}
}
</script>
<style>
uni-button {
position: relative;
display: block;
margin-left: auto;
margin-right: auto;
padding-left: 14px;
padding-right: 14px;
box-sizing: border-box;
font-size: 18px;
text-align: center;
text-decoration: none;
line-height: 2.55555556;
border-radius: 5px;
-webkit-tap-highlight-color: transparent;
overflow: hidden;
color: #000000;
background-color: #F8F8F8;
}
uni-button[hidden] {
display: none !important;
}
uni-button:after {
content: " ";
width: 200%;
height: 200%;
position: absolute;
top: 0;
left: 0;
border: 1px solid rgba(0, 0, 0, 0.2);
-webkit-transform: scale(0.5);
transform: scale(0.5);
-webkit-transform-origin: 0 0;
transform-origin: 0 0;
box-sizing: border-box;
border-radius: 10px;
}
uni-button[native] {
padding-left: 0;
padding-right: 0;
}
uni-button[native] .uni-button-cover-view-wrapper {
border: inherit;
border-color: inherit;
border-radius: inherit;
background-color: inherit;
}
uni-button[native] .uni-button-cover-view-inner {
padding-left: 14px;
padding-right: 14px;
}
uni-button uni-cover-view {
line-height: inherit;
white-space: inherit;
}
uni-button[type=default] {
color: #000000;
background-color: #F8F8F8;
}
uni-button[type=primary] {
color: #FFFFFF;
background-color: #007aff;
}
uni-button[type=warn] {
color: #FFFFFF;
background-color: #E64340;
}
uni-button[disabled] {
color: rgba(255, 255, 255, 0.6);
}
uni-button[disabled][type=default],
uni-button[disabled]:not([type]) {
color: rgba(0, 0, 0, 0.3);
background-color: #F7F7F7;
}
uni-button[disabled][type=primary] {
background-color: rgba(0, 122, 255, 0.6);
}
uni-button[disabled][type=warn] {
background-color: #EC8B89;
}
uni-button[type=primary][plain] {
color: #007aff;
border: 1px solid #007aff;
background-color: transparent;
}
uni-button[type=primary][plain][disabled] {
color: rgba(0, 0, 0, 0.2);
border-color: rgba(0, 0, 0, 0.2);
}
uni-button[type=primary][plain]:after {
border-width: 0;
}
uni-button[type=default][plain] {
color: #353535;
border: 1px solid #353535;
background-color: transparent;
}
uni-button[type=default][plain][disabled] {
color: rgba(0, 0, 0, 0.2);
border-color: rgba(0, 0, 0, 0.2);
}
uni-button[type=default][plain]:after {
border-width: 0;
}
uni-button[plain] {
color: #353535;
border: 1px solid #353535;
background-color: transparent;
}
uni-button[plain][disabled] {
color: rgba(0, 0, 0, 0.2);
border-color: rgba(0, 0, 0, 0.2);
}
uni-button[plain]:after {
border-width: 0;
}
uni-button[plain][native] .uni-button-cover-view-inner {
padding: 0;
}
uni-button[type=warn][plain] {
color: #e64340;
border: 1px solid #e64340;
background-color: transparent;
}
uni-button[type=warn][plain][disabled] {
color: rgba(0, 0, 0, 0.2);
border-color: rgba(0, 0, 0, 0.2);
}
uni-button[type=warn][plain]:after {
border-width: 0;
}
uni-button[size=mini] {
display: inline-block;
line-height: 2.3;
font-size: 13px;
padding: 0 1.34em;
}
uni-button[size=mini][native] {
padding: 0;
}
uni-button[size=mini][native] .uni-button-cover-view-inner {
padding: 0 1.34em;
}
uni-button[loading]:before {
content: " ";
display: inline-block;
width: 18px;
height: 18px;
vertical-align: middle;
-webkit-animation: uni-loading 1s steps(12, end) infinite;
animation: uni-loading 1s steps(12, end) infinite;
background-size: 100%;
}
uni-button[loading][type=primary] {
color: rgba(255, 255, 255, 0.6);
background-color: #0062cc;
}
uni-button[loading][type=primary][plain] {
color: #007aff;
background-color: transparent;
}
uni-button[loading][type=default] {
color: rgba(0, 0, 0, 0.6);
background-color: #DEDEDE;
}
uni-button[loading][type=default][plain] {
color: #353535;
background-color: transparent;
}
uni-button[loading][type=warn] {
color: rgba(255, 255, 255, 0.6);
background-color: #CE3C39;
}
uni-button[loading][type=warn][plain] {
color: #e64340;
background-color: transparent;
}
uni-button[loading][native]:before {
content: none;
}
.button-hover {
color: rgba(0, 0, 0, 0.6);
background-color: #DEDEDE;
}
.button-hover[plain] {
color: rgba(53, 53, 53, 0.6);
border-color: rgba(53, 53, 53, 0.6);
background-color: transparent;
}
.button-hover[type=primary] {
color: rgba(255, 255, 255, 0.6);
background-color: #0062cc;
}
.button-hover[type=primary][plain] {
color: rgba(26, 173, 25, 0.6);
border-color: rgba(26, 173, 25, 0.6);
background-color: transparent;
}
.button-hover[type=default] {
color: rgba(0, 0, 0, 0.6);
background-color: #DEDEDE;
}
.button-hover[type=default][plain] {
color: rgba(53, 53, 53, 0.6);
border-color: rgba(53, 53, 53, 0.6);
background-color: transparent;
}
.button-hover[type=warn] {
color: rgba(255, 255, 255, 0.6);
background-color: #CE3C39;
}
.button-hover[type=warn][plain] {
color: rgba(230, 67, 64, 0.6);
border-color: rgba(230, 67, 64, 0.6);
background-color: transparent;
}
</style>
<template>
<uni-checkbox-group v-on="$listeners">
<slot />
</uni-checkbox-group>
</template>
<script>
import {
emitter,
listeners
} from 'uni-mixins'
export default {
name: 'CheckboxGroup',
mixins: [emitter, listeners],
props: {
name: {
type: String,
default: ''
}
},
data () {
return {
checkboxList: []
}
},
listeners: {
'@checkbox-change': '_changeHandler',
'@checkbox-group-update': '_checkboxGroupUpdateHandler'
},
created () {
this.$dispatch('Form', 'uni-form-group-update', {
type: 'add',
vm: this
})
},
beforeDestroy () {
this.$dispatch('Form', 'uni-form-group-update', {
type: 'remove',
vm: this
})
},
methods: {
_changeHandler ($event) {
let value = []
this.checkboxList.forEach(vm => {
if (vm.checkboxChecked) {
value.push(vm.value)
}
})
this.$trigger('change', $event, {
value: value
})
},
_checkboxGroupUpdateHandler ($event) {
if ($event.type === 'add') {
this.checkboxList.push($event.vm)
} else {
let index = this.checkboxList.indexOf($event.vm)
this.checkboxList.splice(index, 1)
}
},
_getFormData () {
let data = {}
if (this.name !== '') {
let value = []
this.checkboxList.forEach(vm => {
if (vm.checkboxChecked) {
value.push(vm.value)
}
})
data['value'] = value
data['key'] = this.name
}
return data
}
}
}
</script>
<style>
uni-checkbox-group[hidden] {
display: none;
}
</style>
此差异已折叠。
<template>
<uni-form v-on="$listeners">
<span>
<slot />
</span>
</uni-form>
</template>
<script>
import {
listeners
} from 'uni-mixins'
export default {
name: 'Form',
mixins: [listeners],
data () {
return {
childrenList: []
}
},
listeners: {
'@form-submit': '_onSubmit',
'@form-reset': '_onReset',
'@form-group-update': '_formGroupUpdateHandler'
},
methods: {
_onSubmit ($event) {
let data = {}
this.childrenList.forEach(vm => {
if (vm._getFormData && vm._getFormData().key) {
data[vm._getFormData().key] = vm._getFormData().value
}
})
this.$trigger('submit', $event, {
value: data
})
},
_onReset ($event) {
this.$trigger('reset', $event, {})
this.childrenList.forEach(vm => {
vm._resetFormData && vm._resetFormData()
})
},
_formGroupUpdateHandler ($event) {
if ($event.type === 'add') {
this.childrenList.push($event.vm)
} else {
const index = this.childrenList.indexOf($event.vm)
this.childrenList.splice(index, 1)
}
}
}
}
</script>
<style>
</style>
#### image
|属性名|已完成|
|:-|:-|
|src|Y|
|mode|Y|
|lazy-load||
|@error|Y|
|@load|Y|
\ No newline at end of file
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册