Commit 63c1508e authored by JingChao's avatar JingChao

修复问题

parent 0ce9e464
......@@ -9,8 +9,6 @@
<!-- safari私有meta标签 允许全屏模式浏览 指定safari顶部状态栏样式(黑色) -->
<meta name="apple-mobile-web-app-capable" content="yes">
<meta name="apple-mobile-web-app-status-bar-style" content="black">
<script type="text/javascript" src="./static/vuePlatform.js"></script>
<script type="text/javascript" src="../../cordova.js"></script>
<title>车租易</title>
</head>
<body>
......
......@@ -50,3 +50,73 @@ export function detectOS () {
return 'other'
}
}
export function range (num, min, max) {
return Math.min(Math.max(num, min), max)
}
function trimExtraChar (value, char, regExp) {
const index = value.indexOf(char)
if (index === -1) {
return value
}
if (char === '-' && index !== 0) {
return value.slice(0, index)
}
return value.slice(0, index + 1) + value.slice(index).replace(regExp, '')
}
export function formatNumber (value, allowDot) {
if (value) {
if (allowDot) {
value = trimExtraChar(value, '.', /\./g)
} else {
value = value.split('.')[0]
}
value = trimExtraChar(value, '-', /-/g)
const regExp = allowDot ? /[^-0-9.]/g : /[^-0-9]/g
return value.replace(regExp, '')
}
}
export function isDef (val) {
return val !== undefined && val !== null && val !== ''
}
export function isUndefined (value) {
return Object.prototype.toString.call(value) === '[object Undefined]'
}
export function isString (value) {
return Object.prototype.toString.call(value) === '[object String]'
}
export function isNumber (value) {
return Object.prototype.toString.call(value) === '[object Number]'
}
export function isBoolean (value) {
return Object.prototype.toString.call(value) === '[object Boolean]'
}
export function isNull (value) {
return Object.prototype.toString.call(value) === '[object Null]'
}
export function isObject (value) {
return Object.prototype.toString.call(value) === '[object Object]'
}
export function isFunction (value) {
return Object.prototype.toString.call(value) === '[object Function]'
}
export function isArray (value) {
return Object.prototype.toString.call(value) === '[object Array]'
}
export function isDate (value) {
return Object.prototype.toString.call(value) === '[object Date]'
}
export function isRegExp (value) {
return Object.prototype.toString.call(value) === '[object RegExp]'
}
......@@ -20,6 +20,7 @@ curreny-input 金额输入框
```
v-model 绑定value值 number值
disable 是否只读 true/false
......@@ -5,19 +5,31 @@
<template>
<input
:value="formatValue" :readonly="disable" type="text"
ref="currencyInput"
v-bind="$attrs"
:value="formatValue" :readonly="readonly" :disabled="disabled" type="text"
@input="onInput($event.target.value)" @focus="onFocus" @blur="onBlur">
</template>
<script>
import {formatNumber} from '../../../packages/common/utils'
export default {
name: 'CurrencyInput',
inheritAttrs: false,
props: {
value: {
type: Number,
type: Number | String,
default: 0,
},
disable: {
decimal: {
type: Boolean,
default: true,
},
disabled: {
type: Boolean,
default: false,
},
readonly: {
type: Boolean,
default: false,
},
......@@ -35,9 +47,16 @@ export default {
},
},
computed: {
inputMode () {
if (this.decimal) {
return 'decimal'
} else {
return 'numeric'
}
},
formatValue () {
let currency = this.$options.filters['currency']
if (!this.focused) {
if (!this.focused && this.value) {
return currency(this.value)
} else {
return this.value
......@@ -46,19 +65,42 @@ export default {
},
methods: {
onInput: function (value) {
const allowDot = this.decimal
value = formatNumber(value, allowDot)
const { currencyInput } = this.$refs
if (currencyInput && value !== currencyInput.value) {
currencyInput.value = value
}
let uncurrency = this.$options.filter['uncurrency']
this.currencyValue = uncurrency(value)
this.$emit('input', this.currencyValue)
},
onFocus (event) {
this.focused = true
setTimeout(function () {
event.target.type = 'number'
event.target.focus()
}, 0)
if (this.readonly || this.disabled) {
if (this.$refs.currencyInput) {
this.$refs.currencyInput.blur()
this.focused = false
}
} else {
this.focused = true
if (this.decimal) {
setTimeout(function () {
event.target.type = 'text'
event.target.inputMode = 'decimal'
event.target.focus()
}, 0)
} else {
setTimeout(function () {
event.target.type = 'tel'
event.target.inputMode = 'numeric'
event.target.focus()
}, 0)
}
}
},
onBlur (event) {
event.target.type = 'text'
event.target.inputMode = ''
this.focused = false
},
},
......
/**
* @Author think
* @Date 2020-07-30 09:34
*/
<template>
<div :style="{'min-height':minHeight}" :class="{'h-field-disabled':disabled}" class="h-field h-date">
<div v-if="showLeftIcon" class="field-icon field-left-icon" @click="onClickLeftIcon">
<img v-if="!hasLeftIcon" :src="leftIcon">
<slot name="left-icon"/>
</div>
<div v-if="label" :class="{'required': required}" :style="{'flex':proportion[0] }" class="field-title field-label">
<span>{{ label }}</span>
</div>
<div :style="{'flex':proportion[1] }" class="field-value">
<div class="field-body">
<input
v-bind="$attrs"
v-model="value"
:disabled="disabled" :placeholder="dataPlaceholder" :class="('field-control-'+inputAlign)" class="field-control"
type="text" readonly
@click="showDate">
<!--<slot name="icon"/>-->
<i v-if="showClean" class="field-icon field-right-icon icon ion-close-circled" @click="cleanValue"/>
<!--<slot name="icon"/>-->
<img v-if="!showClean" :src="rightIcon" class="field-right-icon date-field-icon" >
</div>
</div>
</div>
</template>
<script>
export default {
name: 'DateField',
inheritAttrs: false,
props: {
value: {
default: null,
type: String | Date,
},
placeholder: {
type: String,
default: null,
},
clearable: {
type: Boolean,
default: true,
},
format: {
default: 'YYYY-MM-DD',
type: String,
},
minYear: {
type: String,
default: '1800',
},
maxYear: {
type: String,
default: '2500',
},
disabled: {
type: Boolean,
default: false,
},
label: {
type: String,
default: '',
required: true,
},
required: {
type: Boolean,
default: false,
},
inputAlign: {
type: String,
default: 'right',
},
proportion: {
// name/content 横向面积比例
type: Array,
default: () => [1, 2],
},
itemHeight: {
type: Number,
default: 45,
},
showIcon: {
type: Boolean,
default: true,
},
leftIcon: {
type: String,
default: null,
},
rightIcon: {
type: String,
default: require('./right-gray@2x.png'),
},
},
data () {
return {
}
},
computed: {
minHeight () {
if (this.$parent.itemHeight) {
return this.$parent.itemHeight + 'px'
} else {
return this.itemHeight + 'px'
}
},
hasLeftIcon () {
return !!this.$slots['left-icon']
},
showLeftIcon () {
return !!(this.leftIcon || this.$slots['left-icon'])
},
dataPlaceholder () {
return this.placeholder ? this.placeholder : '请输入' + this.label
},
showClean () {
let vm = this
if (vm.disabled) {
return false
}
if (vm.clearable && (vm.value !== '' && vm.value !== undefined && vm.value !== null)) {
return true
} else {
return !vm.showIcon
}
},
},
methods: {
cleanValue () {
this.$emit('input', null)
this.$emit('clean', null)
},
showDate () {
let vm = this
if (!vm.disabled) {
let date = new Date().format('yyyy-MM-dd')
if (vm.value) {
date = vm.value
}
vm.$vux.datetime.show({
cancelText: '取消',
confirmText: '确定',
minYear: vm.minYear,
maxYear: vm.maxYear,
format: vm.format,
value: date,
onConfirm (val) {
vm.$emit('input', val)
vm.$emit('onSelect', val)
},
})
}
},
},
}
</script>
<style lang="less">
.h-date{
background-color: #fff;
position: relative;
.field-value{
.date-field-icon{
height: 0.25rem;
}
}
}
</style>
/**
* @Author think
* @Date 2020-07-30 09:34
*/
<template>
<section :style="{'min-height':minHeight}" class="h-date">
<section :style="{'min-height':minHeight}" :class="{'vue-1px-b':hasBorder}" class="date-border">
<section :class="{'required':required}" :style="{'flex':proportion[0] }" class="name">{{ label }}</section>
<section :style="{'flex':proportion[1] }" class="date-content">
<input
v-model="value" :disabled="disabled" :placeholder="dataPlaceholder" type="text"
readonly
@click="showDate">
<!--<slot name="icon"/>-->
<i v-if="showClean" class="icon ion-close-circled" @click="cleanValue"/>
<!--<slot name="icon"/>-->
<img v-if="!showClean" :src="selectIcon" class="icon" >
</section>
</section>
</section>
</template>
<script>
export default {
name: 'DateFiled',
props: {
value: {
default: null,
type: String | Date,
},
placeholder: {
type: String,
default: null,
},
clearable: {
type: Boolean,
default: false,
},
format: {
default: 'YYYY-MM-DD',
type: String,
},
minYear: {
type: String,
default: '1800',
},
maxYear: {
type: String,
default: '2500',
},
disabled: {
type: Boolean,
default: false,
},
label: {
type: String,
default: '',
required: true,
},
required: {
type: Boolean,
default: false,
},
proportion: {
// name/content 横向面积比例
type: Array,
default: () => [1, 2],
},
itemHeight: {
type: Number,
default: 45,
},
hasBorder: {
type: Boolean,
default: true,
},
showIcon: {
type: Boolean,
default: true,
},
rightIcon: {
type: String,
default: '',
},
},
data () {
return {
Icon: require('./right-gray@2x.png'),
}
},
computed: {
minHeight () {
if (this.$parent.itemHeight) {
return this.$parent.itemHeight + 'px'
} else {
return this.itemHeight + 'px'
}
},
selectIcon () {
return this.rightIcon ? this.rightIcon : this.Icon
},
dataPlaceholder () {
return this.placeholder ? this.placeholder : '请输入' + this.label
},
showClean () {
let vm = this
if (vm.clearable && (vm.value !== '' && vm.value !== undefined && vm.value !== null)) {
return true
} else {
return !vm.showIcon
}
},
},
methods: {
cleanValue () {
this.$emit('input', null)
this.$emit('clean', null)
},
showDate () {
let vm = this
if (!vm.disabled) {
let date = new Date().format('yyyy-MM-dd')
if (vm.value) {
date = vm.value
}
vm.$vux.datetime.show({
cancelText: '取消',
confirmText: '确定',
minYear: vm.minYear,
maxYear: vm.maxYear,
format: vm.format,
value: date,
onConfirm (val) {
vm.$emit('input', val)
vm.$emit('onSelect', val)
},
})
}
},
},
}
</script>
<style lang="less">
.h-date{
width: 100%;
display: flex;
align-items: center;
border: none;
padding: 0 0 0 0.3rem;
background-color: #fff;
.date-border{
width: 100%;
height: 100%;
display: flex;
align-items: center;
padding: 0 0.3rem 0 0;
min-height: 45px;
}
.name{
line-height: 0.5rem;
font-size: 14px;
color: #333;
}
.required {
display: flex;
align-items: center;
}
.required::after {
content: '*';
color: #D24E4E;
//height: 0.16rem;
padding-top: 0.08rem;
margin-left: 0.05rem;
}
.date-content{
height: 100%;
width: 100%;
font-size: 14px;
color: #666;
display: flex;
align-items: center;
input{
font-size: 14px;
color: #666;
line-height: 0.4rem;
width: 100%;
text-align: right;
border: none;
}
.icon{
height: 0.25rem;
margin-left: 4px
}
}
}
</style>
Field 输入框
```html
<h-field v-model="number" label="金额"/>
<h-field
v-model="number" format type="number"
clearable error
required label="金额"/>
<h-field
v-model="name"
clearable
required label="姓名"/>
<h-field
v-model="age"
:decimal="false"
type="number"
clearable error
required label="年龄"/>
<h-field
v-model="phone" :left-icon="leftIcon"
type="tel"
required label="手机号"/>
<h-field
v-model="password"
type="password"
required label="密码">
<i slot="left-icon" class="field-icon field-right-icon icon ion-close-circled"/>
</h-field>
<h-field
v-model="address" :autosize="true"
type="textarea"
inputAlign="left"
rows="1"
error
required label="通讯地址"/>
<h-field
v-model="homeAddress"
:autosize="true"
:word-limit="true" type="textarea" maxlength="50"
inputAlign="left"
clearable error
required label="家庭地址"/>
```
|字段名称 |字段说明 |类型 |必填 |默认 |
| ----|------|-------|------|------|
|value|当前值|String|Y|-|
|type|输入类型|String|N|text|
|decimal|是否支持小数(type=number有效)|Boolean|N|false|
|proportion|label与value比例|Array|N|[1,2]|
|required|是否必填|Boolean|N|false|
|readonly|只读属性|Boolean|N|false|
|disabled|禁用属性|Boolean|N|false|
|itemHeight|字端默认高度|Number|N|45|
|label|输入框左侧文本|String|Y|-|
|leftIcon|左侧图标名称或图片链接|String|N|-|
|rightIcon|右侧图标名称或图片链接|String|N|-|
|clearable|是否启用清除图标,点击清除图标后会清空输入框|Boolean|false|-|
|clearTrigger|显示清除图标的时机,always 表示输入框不为空时展示,focus 表示输入框聚焦且不为空时展示|String|N|focus|
|format|是否启用金额千分位化(type=number有效)|Boolean|false|-|
|maxlength|输入的最大字符数|Number,String|false|-|
|inputAlign|输入框对齐方式,可选值为 center left|String|false|right|
|placeholder|输入框默认提示|String|N|请输入+label|
|showPlaceholder|是否显示placeholder|Boolean|N|true|
|error|必输校验后输入框标红|Boolean|N|false|
|autosize|是否自适应内容高度,只对 textarea 有效,可传入对象,如 { maxHeight: 100, minHeight: 50 },单位为px|Boolean,Object|N|-|
|wordLimit|是否显示字数统计,需要设置maxlength属性|Boolean|N|false|
/**
* @Author think
* @Date 2020-08-20 11:08
*/
<template>
<div :style="{'min-height':minHeight}" :class="{'h-field-disabled':disabled}" class="h-field">
<div v-if="showLeftIcon" class="field-icon field-left-icon" @click="onClickLeftIcon">
<img v-if="!hasLeftIcon" :src="leftIcon">
<slot name="left-icon"/>
</div>
<div v-if="label" :class="{'required': required}" :style="{'flex':proportion[0] }" class="field-title field-label">
<span>{{ label }}</span>
</div>
<div :style="{'flex':proportion[1] }" class="field-value">
<div :class="{'field-control-error':hasError}" class="field-body">
<input
v-if="type!=='number' && type!=='textarea'"
ref="fieldInput"
v-bind="$attrs"
:readonly="readonly" :disabled="disabled" :placeholder="placehold" :value="value"
:type="type" :class="('field-control-'+inputAlign)" class="field-control"
@input="onInput($event.target.value)"
@focus="onFocus"
@blur="onBlur">
<input
v-if="type==='number'" ref="fieldInput"
v-bind="$attrs"
:readonly="readonly" :disabled="disabled" :placeholder="placehold" :value="currencyValue"
:type="inputType" :inputmode="inputMode" :class="('field-control-'+inputAlign)" class="field-control"
@input="onInput($event.target.value)"
@focus="onFocus"
@blur="onBlur">
<textarea
v-if="type==='textarea'"
ref="fieldInput" v-bind="$attrs"
:readonly="readonly" :disabled="disabled" :placeholder="placehold" :value="value"
:type="type" :class="('field-control-'+inputAlign)" class="field-control"
@input="onInput($event.target.value)"
@focus="onFocus"
@blur="onBlur"/>
<i v-if="showCancel" class="field-icon field-right-icon icon ion-close-circled" @touchstart="cleanValue"/>
<div v-if="showRightIcon" class="field-icon field-right-icon" @click="onClickRightIcon">
<img v-if="!hasRightIcon" :src="rightIcon">
<slot name="right-icon"/>
</div>
</div>
<div v-if="wordLimit && maxlength" class="field-word-limit"><span class="field-word-num">{{ count }}</span>/{{
maxlength }}
</div>
</div>
</div>
</template>
<script>
import { formatNumber, isDef, isObject } from '../../../packages/common/utils'
export default {
name: 'Field',
inheritAttrs: false,
props: {
value: {
type: String | Number,
default: null,
},
type: {
type: String,
default: 'text',
},
decimal: {
type: Boolean,
default: false,
},
proportion: {
// name/content 横向面积比例
type: Array,
default: () => [1, 2],
},
required: {
type: Boolean,
default: false,
},
readonly: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
itemHeight: {
type: Number,
default: 45,
},
label: {
type: String,
default: null,
required: true,
},
leftIcon: {
type: String,
default: null,
},
rightIcon: {
type: String,
default: null,
},
clearable: {
type: Boolean,
default: false,
},
clearTrigger: {
type: String,
default: 'focus',
},
format: {
type: Boolean,
default: false,
},
maxlength: {
type: Number | String,
default: null,
},
inputAlign: {
type: String,
default: 'right',
},
showPlaceholder: {
type: Boolean,
default: true,
},
placeholder: {
type: String,
default: null,
},
error: {
type: Boolean,
default: false,
},
autosize: {
type: Boolean | Object,
default: null,
},
wordLimit: {
type: Boolean,
default: false,
},
},
data () {
return {
focused: false,
hasError: false,
count: 0,
}
},
computed: {
minHeight () {
if (this.$parent.itemHeight) {
return this.$parent.itemHeight + 'px'
} else {
return this.itemHeight + 'px'
}
},
hasLeftIcon () {
return !!this.$slots['left-icon']
},
showLeftIcon () {
return !!(this.leftIcon || this.$slots['left-icon'])
},
hasRightIcon () {
return !!this.$slots['right-icon']
},
showRightIcon () {
return !!(this.rightIcon || this.$slots['right-icon'])
},
placehold () {
if (this.showPlaceholder) {
return this.placeholder ? this.placeholder : '请输入' + this.label
}
},
inputType () {
if (this.type === 'number' && this.decimal) {
return 'text'
} else {
return 'tel'
}
},
inputMode () {
if (this.type === 'number' && this.decimal) {
return 'decimal'
} else {
return 'numeric'
}
},
showCancel () {
if (this.clearable && !this.readonly && !this.disabled) {
if (this.clearTrigger === 'focus') {
const hasValue = isDef(this.value)
return hasValue && this.focused
} else {
return isDef(this.value)
}
}
},
currencyValue () {
let currency = this.$options.filters['currency']
if (this.type === 'number' && this.format) {
if (!this.focused && this.value) {
return currency(this.value)
} else {
return this.value
}
} else {
return this.value
}
},
},
filter: {
uncurrency (val) {
if (!val) return null
return (Number((val).replace(/,/gi, ''))) === 0 ? null : Number((val).replace(/,/gi, ''))
},
},
watch: {
value () {
this.$nextTick(this.adjustSize)
},
},
mounted () {
// this.onInput(this.value)
if (this.wordLimit && this.maxlength) {
this.count = (this.value || '').length
}
this.$nextTick(this.adjustSize)
},
methods: {
onInput: function (value) {
const { maxlength, decimal } = this
if (isDef(maxlength) && value.length >= maxlength) {
value = value.slice(0, maxlength)
}
if (this.wordLimit && this.maxlength) {
this.count = (value || '').length
}
if (this.type === 'number') {
value = formatNumber(value, decimal)
}
const { fieldInput } = this.$refs
if (fieldInput && value !== fieldInput.value) {
fieldInput.value = value
}
this.$emit('input', value)
},
onFocus (event) {
let vm = this
this.focused = true
this.$emit('focus', event)
if (vm.readonly || vm.disabled) {
if (vm.$refs.fieldInput) {
vm.$refs.fieldInput.blur()
this.focused = false
}
}
},
// 检验输入的合法性
validate () {
const { fieldInput } = this.$refs
const { required, error } = this
if (required && error) {
this.hasError = !isDef(fieldInput.value)
}
},
onBlur (event) {
this.focused = false
this.$emit('blur', event)
this.validate()
},
cleanValue (event) {
this.count = 0
this.$emit('input', null)
this.$emit('clean', event)
},
onClickLeftIcon (event) {
this.$emit('click-left-icon', event)
},
onClickRightIcon (event) {
this.$emit('click-right-icon', event)
},
adjustSize () {
const { fieldInput } = this.$refs
if (!(this.type === 'textarea' && this.autosize) || !fieldInput) {
return
}
fieldInput.style.height = 'auto'
let height = fieldInput.scrollHeight
if (isObject(this.autosize)) {
const { maxHeight, minHeight } = this.autosize
if (maxHeight) {
height = Math.min(height, maxHeight)
}
if (minHeight) {
height = Math.max(height, minHeight)
}
}
if (height) {
fieldInput.style.height = height + 'px'
}
},
},
}
</script>
<style lang="less">
@import "../../../packages/common/styles/vue-1px";
.h-field {
width: 100%;
display: flex;
padding: 10px 15px;
background-color: #fff;
position: relative;
line-height: 24px;
&:after {
.setBottomLine();
left: 15px;
}
.field-icon {
// width: 16px;
font-size: 16px;
img {
width: 16px;
position: relative;
top: 2px;
}
}
.field-left-icon {
//line-height: 25px;
margin-right: 4px;
}
.field-right-icon {
//line-height: 25px;
margin-left: 4px;
}
.field-title {
line-height: 0.5rem;
font-size: 14px;
color: #333;
letter-spacing: 0;
margin-right: 12px;
}
.required {
&:before {
position: absolute;
left: 8px;
color: #D24E4E;
font-size: 14px;
content: '*';
}
}
.field-value {
position: relative;
overflow: hidden;
color: #666;
text-align: right;
vertical-align: middle;
.field-body {
display: flex;
align-items: center;
.field-control {
display: block;
box-sizing: border-box;
font-size: 14px;
width: 100%;
min-width: 0;
margin: 0;
padding: 0;
color: #666;
line-height: inherit;
background-color: transparent;
border: 0;
resize: none;
&-left {
text-align: left;
}
&-right {
text-align: right;
}
&-center {
text-align: center;
}
}
}
.field-control-error {
.field-control {
&,
&::placeholder {
color: #ee0a24;
-webkit-text-fill-color: currentColor;
}
}
}
.field-word-limit {
margin-top: 4px;
color: #646566;
font-size: 12px;
line-height: 16px;
text-align: right;
}
}
&-disabled {
.field-label {
color: #999;
}
.field-value {
.field-body {
.field-control {
color: #999;
}
}
}
}
}
</style>
......@@ -101,7 +101,7 @@ export default {
this.$refs.input.value = ''
}
},
change ($event) {
change (event) {
let vm = this
let { files } = event.target
if (vm.disabled || !files.length) {
......@@ -124,13 +124,11 @@ export default {
.catch(vm.resetInput)
}
}
vm.fileRead(files)
vm.fileRead(toArray(files))
},
fileRead (files) {
const oversize = isOversize(files, this.maxSize)
if (oversize) {
files = files.filter(file => file.size <= this.maxSize)
}
const oversizeFiles = isOversize(files, this.maxSize)
if (Array.isArray(files)) {
const maxCount = this.maxCount - this.fileList.length
......@@ -144,17 +142,18 @@ export default {
content: contents[index],
}))
this.onAfterRead(fileList, oversize)
this.onAfterRead(fileList, oversizeFiles)
})
} else {
readFile(files, this.resultType).then(content => {
this.onAfterRead({ file: files, content: content }, oversize)
this.onAfterRead({ file: files, content: content }, oversizeFiles)
})
}
},
onAfterRead (files, oversize) {
if (oversize) {
this.$emit('oversize', toArray(files))
onAfterRead (files, oversizeFiles) {
if (oversizeFiles.length) {
this.$emit('oversize', toArray(oversizeFiles))
files = files.filter(file => file.size <= this.maxSize)
// return
}
this.resetInput()
......
......@@ -23,7 +23,7 @@ export function readFile (file, resultType) {
}
export function isOversize (files, maxSize) {
return toArray(files).some(file => file.size > maxSize)
return toArray(files).filter(file => file.size > maxSize)
}
export function isImageDataUrl (dataUrl) {
......
/**
* @Author Think
* @Date 2018/11/24
*/
* @Author Think
* @Date 2018/11/24
*/
<template>
<div :class="{'h-ios': isIos}" class="h-view">
<slot />
<slot/>
</div>
</template>
......@@ -42,39 +42,48 @@ export default {
},
created () {
this.fullScreen && detectOS() === 'ios' && (this.isIos = true)
document.body.classList.add('platform-' + detectOS())
},
}
</script>
<style lang="less" scoped>
.h-view {
width: 100%;
height: 100%;
overflow: hidden;
// padding-bottom: 44px;
// background-color: $bgColor;
}
.platform-ios{
.h-view{
// padding-bottom: 64px;
<style lang="less">
::selection {
background: #3367d6;
color: #fff;
}
input:hover ,input:focus{
caret-color: #3367d6;
}
.h-view {
width: 100%;
height: 100%;
overflow: hidden;
// padding-bottom: 44px;
// background-color: $bgColor;
}
}
// iPhoneX适配
@media only screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) {
.platform-ios {
.h-view {
// padding-bottom: 120px;
// padding-bottom: 64px;
}
}
}
// iPhoneX Max适配
@media only screen and (device-width: 414px) and (device-height: 896px) {
.platform-ios {
.h-view {
// padding-bottom: 120px;
// iPhoneX适配
@media only screen and (device-width: 375px) and (device-height: 812px) and (-webkit-device-pixel-ratio: 3) {
.platform-ios {
.h-view {
// padding-bottom: 120px;
}
}
}
// iPhoneX Max适配
@media only screen and (device-width: 414px) and (device-height: 896px) {
.platform-ios {
.h-view {
// padding-bottom: 120px;
}
}
}
}
</style>
<template>
<div :class="type" class="function">{{ text }}</div>
<div :class="type" class="function" @click="itemClick">{{ text }}</div>
</template>
<script>
......@@ -22,8 +22,8 @@ export default {
this.$parent && this.$parent.optionItem.splice(this.$parent.optionItem.indexOf(this), 1)
},
methods: {
buttonClick (e) {
this.$emit('press', this.type)
itemClick (e) {
this.$parent && this.$parent.reset()
},
},
}
......
<template>
<div :class="cusClass" class="swipeout-list">
<div ref="item" :class="bottomBorder" class="item" data-type="0">
<div ref="optionItem" :class="bottomBorder" class="option-item" data-type="0">
<div
class="list-box"
@touchstart.capture="touchStart"
......@@ -62,7 +62,7 @@ export default {
},
methods: {
touchStart (ev) {
this.$refs.item.style.transform = 'translate3d(0px,0px,0px)'
this.$refs.optionItem.style.transform = 'translate3d(0px,0px,0px)'
this.reset()
ev = ev || event
// tounches类数组,等于1时表示此时有只有一只手指在触摸屏幕
......@@ -88,7 +88,7 @@ export default {
},
// 判断当前是否有滑块处于滑动状态
checkSlide () {
let listItems = this.$refs.item
let listItems = this.$refs.optionItem
for (let i = 0; i < listItems.length; i++) {
if (listItems[i].dataset.type === '1') {
return true
......@@ -106,10 +106,10 @@ export default {
})
},
reset () {
let l = document.getElementsByClassName('item').length
let l = document.getElementsByClassName('option-item').length
for (let i = 0; i < l; i++) {
document.getElementsByClassName('item')[i].style.transform = 'translate3d(0px,0px,0px)'
document.getElementsByClassName('item')[i].dataset.type = '0'
document.getElementsByClassName('option-item')[i].style.transform = 'translate3d(0px,0px,0px)'
document.getElementsByClassName('option-item')[i].dataset.type = '0'
}
},
},
......@@ -124,14 +124,14 @@ export default {
overflow: visible;
padding: 0 0 0 15px;
width: 100%;
.item[data-type="0"] {
.option-item[data-type="0"] {
transform: translate3d(0px, 0px, 0px);
transition: all 0.4s;
}
.item[data-type="1"] {
.option-item[data-type="1"] {
transition: all 1s;
}
.item {
.option-item {
overflow: visible;
position: relative;
height: 100%;
......
<template>
<div
:class="[showActivated,focus]"
:class="[showActivated,cusClass]"
:style="{'min-height':minHeight}"
class="hls-item">
<div
......@@ -11,14 +11,12 @@
@touchcancel="end"
@mousedown="start"
@mouseup="end"
@mouseleave="end"
@focusin="focusin"
@focusout="focusout">
<div v-if="showName" :style="{'min-height':minHeight,'flex':proportion[0] }" class="add-name">
@mouseleave="end">
<div v-if="showName && hasName" :style="{'min-height':minHeight,'flex':proportion[0] }" class="add-name">
<slot name="left-icon"/>
<slot name="name"/>
</div>
<div v-if="showContent" :style="{'min-height':minHeight,'flex':proportion[1] }" class="add-content">
<div v-if="showContent && hasContent" :style="{'min-height':minHeight,'flex':proportion[1] }" class="add-content">
<slot name="content"/>
<slot name="right-icon"/>
<img v-if="showArrow" :src="rightIcon" class="right-icon" style="height: 0.2rem">
......@@ -55,6 +53,10 @@ export default {
type: String,
default: '',
},
cusClass: {
type: String,
default: '',
},
hasBorder: {
type: Boolean,
default: true,
......@@ -67,7 +69,6 @@ export default {
data () {
return {
showActivated: '',
focus: '',
hasTouchEvent: 'ontouchstart' in document,
Icon: require('./right-gray@2x.png'),
}
......@@ -82,6 +83,12 @@ export default {
bottomBorder () {
return this.hasBorder ? 'vue-1px-b' : ''
},
hasName () {
return !!this.$slots['name'] || !!this.$slots['left-icon']
},
hasContent () {
return !!this.$slots['content'] || !!this.$slots['right-icon']
},
},
mounted () {
},
......@@ -90,12 +97,6 @@ export default {
// 移动浏览器中长按元素会触发显示菜单,导致touchend事件不会触发,需要阻止该行为
e.preventDefault()
},
focusin (e) {
this.focus = 'focus'
},
focusout (e) {
this.focus = ''
},
start (e) {
if (e.target.readOnly) return
if (e.target.nodeName === 'INPUT' || e.target.nodeName === 'TEXTAREA' || e.target.nodeName === 'BUTTON' || e.target.nodeName === 'LABEL') return
......@@ -157,15 +158,15 @@ export default {
}
.required {
display: flex;
position: relative;
}
.required::after {
content: '*';
.required::before {
position: absolute;
left: -8px;
color: #D24E4E;
height: 8px;
padding-top: 2px;
margin-left: 2px;
font-size: 14px;
content: '*';
}
}
......
......@@ -20,7 +20,10 @@ export default {
cusClass: {
type: String,
default: '',
},
showFocusBorder: {
type: Boolean,
default: false,
},
},
data () {
......
......@@ -34,6 +34,10 @@ export default {
type: Boolean,
default: false,
},
showBottomBorder: {
type: Boolean,
default: false,
},
overflowX: {
type: Boolean,
default: true,
......@@ -83,7 +87,7 @@ export default {
throw 'tab index out of bound exception'
} else {
this.defaultActive === value
this.setTabActive(value)
// this.setTabActive(value)
}
},
},
......
......@@ -2,7 +2,7 @@
<section :class="[tabClass]" class="h-tab-item" @click="titleClick">
<div class="h-item">
<slot/>
<div class="bottom-border"/>
<div v-if="showBottomBorder" class="bottom-border"/>
</div>
<div v-show="showDivider" class="tab-divider"/>
</section>
......@@ -21,6 +21,9 @@ export default {
showDivider () {
return this.$parent.showDivider
},
showBottomBorder () {
return this.$parent.showBottomBorder
},
tabClass () {
if (this.$parent.hasBorder) {
return this.$parent.position === 'top' ? 'vue-1px-b' : 'vue-1px-t'
......
......@@ -13,7 +13,7 @@ export default {
data () {
return {
width: 50,
height: 80,
height: 60,
}
},
computed: {
......@@ -33,10 +33,10 @@ export default {
this.ratio = 1
this.width *= this.ratio
this.height *= this.ratio
this.initRadius = 18 * this.ratio
this.initRadius = 11 * this.ratio // 外面阴影
this.minHeadRadius = 12 * this.ratio
this.minTailRadius = 5 * this.ratio
this.initArrowRadius = 10 * this.ratio
this.initArrowRadius = 6 * this.ratio // 里面的刷新
this.minArrowRadius = 6 * this.ratio
this.arrowWidth = 3 * this.ratio
this.maxDistance = 40 * this.ratio
......
......@@ -36,7 +36,8 @@
:bubbleY="bubbleY"
name="pulldown"
>
<div v-if="pullDown" ref="pulldown" :style="pullDownStyle" :class="c('__pulldown')">
<div
v-if="pullDown" ref="pulldown" :style="pullDownStyle" :class="c('__pulldown')">
<div v-if="pullDownBefore" :class="c('__pulldown__before')">
<Bubble :y="bubbleY"/>
</div>
......@@ -113,8 +114,8 @@ export default {
// 下拉刷新配置
type: Object,
default: () => ({
threshold: 90, // 触发 pullingDown 的距离
stop: 40, // pullingDown 正在刷新 hold 时的距离
threshold: 40, // 触发 pullingDown 的距离
stop: 20, // pullingDown 正在刷新 hold 时的距离
txt: '刷新成功',
}),
},
......@@ -188,6 +189,9 @@ export default {
bubbleY: 0, // 气泡y坐标,
isIos: false,
fullScreen: true,
fontSize: Number(window.document.documentElement.style.fontSize.replace('px', '')),
winHeight: window.innerHeight,
winWidth: window.innerWidth,
}
},
computed: {
......@@ -220,7 +224,7 @@ export default {
this.fullScreen && detectOS() === 'ios' && (this.isIos = true)
},
async mounted () {
this.pullDownInitTop = parseInt(this.$refs.pulldown && getComputedStyle(this.$refs.pulldown).top) || -100
this.pullDownInitTop = parseInt(this.$refs.pulldown && getComputedStyle(this.$refs.pulldown).top) || -60
await this.$nextTick()
this.initScroll()
......@@ -232,6 +236,60 @@ export default {
})
},
methods: {
getHeaderHeight () {
let vm = this
let $el = vm.$el.previousElementSibling
let headerHeight = 0
do {
if ($el) {
let elHeight = window.getComputedStyle($el).height
let part = /^\d+(\.\d+)?px$/
if (elHeight && part.test(elHeight)) {
headerHeight += Number(elHeight.replace('px', ''))
}
if ($el._prevClass && $el._prevClass.indexOf('h-header') === 0) {
if (detectOS() === 'ios' && vm.winWidth === 375 && vm.winHeight === 812) {
headerHeight += 0.8 * vm.fontSize
} else if (detectOS() === 'ios' && vm.winWidth === 414 && vm.winHeight === 896) {
headerHeight += 0.8 * vm.fontSize
} else if (detectOS() === 'ios') {
headerHeight += 0.4 * vm.fontSize
}
}
$el = $el.previousElementSibling
}
} while ($el)
return headerHeight
},
getNextElementHeight () {
let vm = this
let nextElement = this.$el.nextElementSibling
let nextHeight = 0
do {
if (nextElement) {
let elHeight = window.getComputedStyle(nextElement).height
let part = /^\d+(\.\d+)?px$/
if (elHeight && part.test(elHeight)) {
nextHeight += Number(elHeight.replace('px', ''))
}
if (nextElement._prevClass && nextElement._prevClass.indexOf('h-bottom-tab') === 0) {
let height = nextElement.clientHeight
if (detectOS() === 'ios' && height > Math.ceil(vm.fontSize * 0.88)) {
nextHeight += 0
} else {
if (detectOS() === 'ios' && vm.winWidth === 375 && vm.winHeight === 812) {
nextHeight += vm.fontSize * 0.68
} else if (detectOS() === 'ios' && vm.winWidth === 414 && vm.winHeight === 896) {
nextHeight += vm.fontSize * 0.68
}
}
}
nextElement = nextElement.nextElementSibling
}
} while (nextElement)
return nextHeight
},
// 初始化scroll
initScroll () {
let vm = this
......@@ -239,7 +297,10 @@ export default {
// 设置scrollContent的最小高,实现高度不足时也有回弹效果
if (this.$refs.scrollContent) {
const headerHeight = vm.getHeaderHeight()
// const nextHeight = vm.getNextElementHeight()
this.$refs.scrollContent.style.minHeight = `${this.$refs.scroll.getBoundingClientRect().height + 1}px`
this.$refs.scrollContent.style.paddingTop = `${headerHeight}px`
if (vm.hasFoot.footFlag) {
let height = vm.hasFoot.height || 88
// this.$refs.scrollContent.style.minHeight = `${this.$refs.scroll.getBoundingClientRect().height - height}px`
......@@ -408,7 +469,8 @@ export default {
width 100%
overflow hidden !important
box-sizing border-box
position relative
position absolute !important
top 0
height 100%
&__wrapper {
......@@ -422,7 +484,7 @@ export default {
text-size-adjust: none;
-webkit-transform-origin: left top;
transform-origin: left top;
padding-bottom: 0.9rem;
padding-bottom: 0.4rem;
}
&__pullup {
......@@ -438,7 +500,7 @@ export default {
&__pulldown {
position absolute
left 0
top -50px; /*no*/
top 0; /*no*/
width 100%
display flex
justify-content center
......@@ -461,20 +523,24 @@ export default {
}
}
}
.platform-ios{
.vue-better-scroll {
&__wrapper {
// padding-bottom: 2.16rem;
}
.has-footer {
.vue-better-scroll__wrapper {
padding-bottom: 1rem;
}
}
.platform-ios{
.has-footer {
.vue-better-scroll__wrapper {
padding-bottom: 1rem;
}
}
}
// iPhoneX适配
@media (device-width: 375px) and (device-height: 812px) and (-webkit-min-device-pixel-ratio: 3) {
.platform-ios {
.vue-better-scroll {
&__wrapper {
// padding-bottom: 2.84rem;
.has-footer {
.vue-better-scroll__wrapper {
padding-bottom: 1.8rem;
}
}
}
......@@ -483,9 +549,9 @@ export default {
// iPhoneX Max适配
@media (device-width: 414px) and (device-height: 896px) {
.platform-ios {
.vue-better-scroll {
&__wrapper {
// padding-bottom: 2.84rem;
.has-footer {
.vue-better-scroll__wrapper {
padding-bottom: 1.8rem;
}
}
}
......
/**
* @Author think
* @Date 2020-07-30 09:34
*/
<template>
<div :style="{'min-height':minHeight}" :class="{'h-field-disabled':disabled}" class="h-field h-select">
<div v-if="showLeftIcon" class="field-icon field-left-icon" @click="onClickLeftIcon">
<img v-if="!hasLeftIcon" :src="leftIcon">
<slot name="left-icon"/>
</div>
<div v-if="label" :class="{'required': required}" :style="{'flex':proportion[0] }" class="field-title field-label">
<span>{{ label }}</span>
</div>
<div :style="{'flex':proportion[1] }" class="field-value">
<div class="field-body">
<input
v-bind="$attrs"
v-model="codeName" :disabled="disabled" :placeholder="selectPlaceholder" :class="('field-control-'+inputAlign)"
type="text" class="field-control"
readonly
@click="showSelect">
<input v-model="value" type="text" hidden>
<i v-if="showClean" class="field-icon field-right-icon icon ion-close-circled" @click="cleanValue"/>
<!--<slot name="icon"/>-->
<img v-if="!showClean" :src="rightIcon" class="field-right-icon date-field-icon" >
</div>
</div>
</div>
<!--</section>-->
</template>
<script>
import VueSelect from './index'
export default {
name: 'SelectField',
inheritAttrs: false,
props: {
value: {
default: null,
type: Number | String,
},
placeholder: {
type: String,
default: null,
},
clearable: {
type: Boolean,
default: true,
},
dataArray: {
type: Array,
default: () => [],
},
multiple: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
valueKey: {
type: String,
default: 'code',
},
valueName: {
type: String,
default: 'code_name',
},
code: {
type: String,
default: 'code',
},
object: {
type: Object,
default: () => {},
},
label: {
type: String,
default: '',
},
required: {
type: Boolean,
default: false,
},
proportion: {
// name/content 横向面积比例
type: Array,
default: () => [1, 2],
},
itemHeight: {
type: Number,
default: 45,
},
inputAlign: {
type: String,
default: 'right',
},
showIcon: {
type: Boolean,
default: true,
},
leftIcon: {
type: String,
default: null,
},
rightIcon: {
type: String,
default: require('./right-gray@2x.png'),
},
},
data () {
return {
}
},
computed: {
hasLeftIcon () {
return !!this.$slots['left-icon']
},
showLeftIcon () {
return !!(this.leftIcon || this.$slots['left-icon'])
},
codeName () {
let vm = this
let name
if (vm.multiple) {
name = []
if (Array.isArray(vm.value)) {
vm.dataArray.forEach((selectItem, listIndex, listArray) => {
vm.value.forEach((index, item, array) => {
if (selectItem[vm.valueKey] === item) {
name.push(selectItem[vm.valueName])
}
})
})
} else {
console.error('multiple select value must be Array')
}
} else {
vm.dataArray.forEach((selectItem, listIndex, listArray) => {
// eslint-disable-next-line eqeqeq
if (selectItem[vm.valueKey] == vm.value) {
name = selectItem[vm.valueName]
}
})
}
return name
},
showClean () {
let vm = this
if (vm.disabled) {
return false
}
if (vm.clearable && (vm.value !== '' && vm.value !== undefined && vm.value !== null)) {
return true
} else {
return !vm.showIcon
}
},
minHeight () {
if (this.$parent.itemHeight) {
return this.$parent.itemHeight + 'px'
} else {
return this.itemHeight + 'px'
}
},
selectPlaceholder () {
return this.placeholder ? this.placeholder : '请选择' + this.label
},
},
methods: {
selectCall (v1, v2, v3) {
let vm = this
if (!vm.multiple) {
this.$emit('input', v2[vm.code])
this.$emit('onSelect', v1, v2, v3)
} else {
this.$emit('input', v1)
this.$emit('onSelect', v1, v2)
}
},
cleanValue () {
this.$emit('input', null)
this.$emit('clean', null)
},
showSelect () {
let vm = this
if (!vm.disabled) {
let list = []
vm.dataArray.forEach((date, index, array) => {
list.push({
value: date[vm.valueKey],
name: date[vm.valueName],
parent: date.parent,
})
})
VueSelect.show({
list: list,
callBack: vm.selectCall,
code: vm.code,
object: vm.object || {},
multiple: vm.multiple,
})
}
},
},
}
</script>
<style lang="less">
.h-select{
background-color: #fff;
position: relative;
.field-value{
.date-field-icon{
height: 0.25rem;
}
}
}
</style>
/**
* @Author think
* @Date 2020-07-30 09:34
*/
<template>
<section :style="{'min-height':minHeight}" class="h-select">
<section :style="{'min-height':minHeight}" :class="{'vue-1px-b':hasBorder}" class="select-border">
<section :class="{'required':required}" :style="{'flex':proportion[0] }" class="name">{{ label }}</section>
<section :style="{'flex':proportion[1] }" class="select-content">
<input
v-model="codeName" :disabled="disabled" :placeholder="selectPlaceholder" type="text"
readonly
@click="showSelect">
<input v-model="value" type="text" hidden>
<i v-if="showClean" class="icon ion-close-circled" @click="cleanValue"/>
<!--<slot name="icon"/>-->
<img v-if="!showClean" :src="selectIcon" class="icon" >
</section>
</section>
</section>
</template>
<script>
import VueSelect from './index'
export default {
name: 'SelectFiled',
props: {
value: {
default: null,
type: Number | String,
},
placeholder: {
type: String,
default: null,
},
clearable: {
type: Boolean,
default: false,
},
dataArray: {
type: Array,
default: () => [],
},
multiple: {
type: Boolean,
default: false,
},
disabled: {
type: Boolean,
default: false,
},
valueKey: {
type: String,
default: 'code',
},
valueName: {
type: String,
default: 'code_name',
},
code: {
type: String,
default: 'code',
},
object: {
type: Object,
default: () => {},
},
label: {
type: String,
default: '',
},
required: {
type: Boolean,
default: false,
},
proportion: {
// name/content 横向面积比例
type: Array,
default: () => [1, 2],
},
itemHeight: {
type: Number,
default: 45,
},
hasBorder: {
type: Boolean,
default: true,
},
showIcon: {
type: Boolean,
default: true,
},
rightIcon: {
type: String,
default: '',
},
},
data () {
return {
Icon: require('./right-gray@2x.png'),
}
},
computed: {
codeName () {
let vm = this
let name
if (vm.multiple) {
name = []
if (Array.isArray(vm.value)) {
vm.dataArray.forEach((selectItem, listIndex, listArray) => {
vm.value.forEach((index, item, array) => {
if (selectItem[vm.valueKey] === item) {
name.push(selectItem[vm.valueName])
}
})
})
} else {
console.error('multiple select value must be Array')
}
} else {
vm.dataArray.forEach((selectItem, listIndex, listArray) => {
// eslint-disable-next-line eqeqeq
if (selectItem[vm.valueKey] == vm.value) {
name = selectItem[vm.valueName]
}
})
}
return name
},
showClean () {
let vm = this
if (vm.clearable && (vm.value !== '' && vm.value !== undefined && vm.value !== null)) {
return true
} else {
return !vm.showIcon
}
},
minHeight () {
if (this.$parent.itemHeight) {
return this.$parent.itemHeight + 'px'
} else {
return this.itemHeight + 'px'
}
},
selectIcon () {
return this.rightIcon ? this.rightIcon : this.Icon
},
selectPlaceholder () {
return this.placeholder ? this.placeholder : '请选择' + this.label
},
},
methods: {
selectCall (v1, v2, v3) {
let vm = this
if (!vm.multiple) {
this.$emit('input', v2[vm.code])
this.$emit('onSelect', v1, v2, v3)
} else {
this.$emit('input', v1)
this.$emit('onSelect', v1, v2)
}
},
cleanValue () {
this.$emit('input', null)
this.$emit('clean', null)
},
showSelect () {
let vm = this
if (!vm.disabled) {
let list = []
vm.dataArray.forEach((date, index, array) => {
list.push({
value: date[vm.valueKey],
name: date[vm.valueName],
parent: date.parent,
})
})
VueSelect.show({
list: list,
callBack: vm.selectCall,
code: vm.code,
object: vm.object || {},
multiple: vm.multiple,
})
}
},
},
}
</script>
<style lang="less">
.h-select{
width: 100%;
display: flex;
align-items: center;
border: none;
padding: 0 0 0 0.3rem;
background-color: #fff;
.select-border{
width: 100%;
height: 100%;
display: flex;
align-items: center;
padding: 0 0.3rem 0 0;
min-height: 45px;
}
.name{
line-height: 0.5rem;
font-size: 14px;
color: #333;
}
.required {
display: flex;
align-items: center;
}
.required::after {
content: '*';
color: #D24E4E;
//height: 0.16rem;
padding-top: 0.08rem;
margin-left: 0.05rem;
}
.select-content{
height: 100%;
width: 100%;
font-size: 14px;
color: #666;
display: flex;
align-items: center;
input{
font-size: 14px;
color: #666;
line-height: 0.4rem;
width: 100%;
text-align: right;
border: none;
}
.icon{
height: 0.25rem;
margin-left: 4px
}
}
}
</style>
......@@ -30,8 +30,9 @@ import HLayout from './HLayout/index'
import CurrencyInput from './CurrencyInput/index'
import HProgress from './Progress/index'
import HRange from './Range/index'
import SelectFiled from './Select/SelectFiled'
import DateFiled from './DateFiled/index'
import SelectField from './Select/SelectField'
import DateField from './DateField/index'
import Field from './Field/index'
import errLoadingPic from '../common/picture/errloading.jpg'
......@@ -70,6 +71,7 @@ export default (Vue) => {
Vue.component('currency-input', CurrencyInput)
Vue.component('h-progress', HProgress)
Vue.component('h-range', HRange)
Vue.component('SelectFiled', SelectFiled)
Vue.component('DateFiled', DateFiled)
Vue.component('SelectField', SelectField)
Vue.component('DateField', DateField)
Vue.component('h-field', Field)
}
......@@ -27,8 +27,9 @@ import HLayout from './components/HLayout/index'
import CurrencyInput from './components/CurrencyInput/index'
import HProgress from './components/Progress/index'
import HRange from './components/Range/index'
import SelectFiled from './components/Select/SelectFiled'
import DateFiled from './components/DateFiled/index'
import SelectField from './components/Select/SelectField'
import DateField from './components/DateField/index'
import Field from './components/Field/index'
import componentInstall from './components/component'
// compontenPlugins
......@@ -73,8 +74,9 @@ export {
CurrencyInput,
HProgress,
HRange,
SelectFiled,
DateFiled,
SelectField,
DateField,
Field,
ActionSheetPlugin,
ShowPicturePlugin,
SelectPlugin,
......
/*!
* vum.bundle.js is a concatenation of:
* vum.js, angular.js, angular-animate.js,
* angular-sanitize.js, angular-ui-router.js,
* and vum-angular.js
*/
/*!
* Copyright 2015 Drifty Co.
* http://drifty.com/
*
* vum, v1.3.0
* A powerful HTML5 mobile app framework.
* http://vumframework.com/
*
* By @maxlynch, @benjsperry, @adamdbradley <3
*
* Licensed under the MIT license. Please see LICENSE for more information.
*
*/
/* eslint-disable */
(function () {
// Create global vum obj and its namespaces
// build processes may have already created an vum obj
window.vum = window.vum || {}
window.vum.version = '1.3.0';
(function (window, document, vum) {
var readyCallbacks = []
var isDomReady = document.readyState === 'complete' || document.readyState === 'interactive'
function domReady () {
isDomReady = true
for (var x = 0; x < readyCallbacks.length; x++) {
vum.requestAnimationFrame(readyCallbacks[x])
}
readyCallbacks = []
document.removeEventListener('DOMContentLoaded', domReady)
}
if (!isDomReady) {
document.addEventListener('DOMContentLoaded', domReady)
}
})(window, document, vum);
(function (window, document, vum) {
function getParameterByName (name) {
name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]')
var regex = new RegExp('[\\?&]' + name + '=([^&#]*)'),
results = regex.exec(location.search)
return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '))
}
var IOS = 'ios'
var ANDROID = 'android'
var WINDOWS_PHONE = 'windowsphone'
var EDGE = 'edge'
var CROSSWALK = 'crosswalk'
var requestAnimationFrame = requestAnimationFrame // eslint-disable-line
// From the man himself, Mr. Paul Irish.
// The requestAnimationFrame polyfill
// Put it on window just to preserve its context
// without having to use .call
window._rAF = (function () {
return window.requestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.mozRequestAnimationFrame ||
function (callback) {
window.setTimeout(callback, 16)
}
})()
function requestAnimationFrame (cb) {
return window._rAF(cb)
}
/**
* @ngdoc utility
* @name vum.Platform
* @module vum
* @description
* A set of utility methods that can be used to retrieve the device ready state and
* various other information such as what kind of platform the app is currently installed on.
*
* @usage
* ```js
* angular.module('PlatformApp', ['vum'])
* .controller('PlatformCtrl', function($scope) {
*
* vum.Platform.ready(function(){
* // will execute when device is ready, or immediately if the device is already ready.
* });
*
* var deviceInformation = vum.Platform.device();
*
* var isWebView = vum.Platform.isWebView();
* var isIPad = vum.Platform.isIPad();
* var isIOS = vum.Platform.isIOS();
* var isAndroid = vum.Platform.isAndroid();
* var isWindowsPhone = vum.Platform.isWindowsPhone();
*
* var currentPlatform = vum.Platform.platform();
* var currentPlatformVersion = vum.Platform.version();
*
* vum.Platform.exitApp(); // stops the app
* });
* ```
*/
var self = vum.Platform = {
// Put navigator on platform so it can be mocked and set
// the browser does not allow window.navigator to be set
navigator: window.navigator,
/**
* @ngdoc property
* @name vum.Platform#isReady
* @returns {boolean} Whether the device is ready.
*/
isReady: false,
/**
* @ngdoc property
* @name vum.Platform#isFullScreen
* @returns {boolean} Whether the device is fullscreen.
*/
isFullScreen: false,
/**
* @ngdoc property
* @name vum.Platform#platforms
* @returns {Array(string)} An array of all platforms found.
*/
platforms: null,
/**
* @ngdoc property
* @name vum.Platform#grade
* @returns {string} What grade the current platform is.
*/
grade: null,
/**
* @ngdoc property
* @name vum.Platform#ua
* @returns {string} What User Agent is.
*/
ua: navigator.userAgent,
/**
* @ngdoc method
* @name vum.Platform#ready
* @description
* Trigger a callback once the device is ready, or immediately
* if the device is already ready. This method can be run from
* anywhere and does not need to be wrapped by any additonal methods.
* When the app is within a WebView (Cordova), it'll fire
* the callback once the device is ready. If the app is within
* a web browser, it'll fire the callback after `window.load`.
* Please remember that Cordova features (Camera, FileSystem, etc) still
* will not work in a web browser.
* @param {function} callback The function to call.
*/
ready: function (cb) {
// run through tasks to complete now that the device is ready
if (self.isReady) {
cb()
} else {
// the platform isn't ready yet, add it to this array
// which will be called once the platform is ready
readyCallbacks.push(cb)
}
},
/**
* @private
*/
detect: function () {
self._checkPlatforms()
requestAnimationFrame(function () {
// only add to the body class if we got platform info
for (var i = 0; i < self.platforms.length; i++) {
document.body.classList.add('platform-' + self.platforms[i])
}
})
},
/**
* @ngdoc method
* @name vum.Platform#setGrade
* @description Set the grade of the device: 'a', 'b', or 'c'. 'a' is the best
* (most css features enabled), 'c' is the worst. By default, sets the grade
* depending on the current device.
* @param {string} grade The new grade to set.
*/
setGrade: function (grade) {
var oldGrade = self.grade
self.grade = grade
requestAnimationFrame(function () {
if (oldGrade) {
document.body.classList.remove('grade-' + oldGrade)
}
document.body.classList.add('grade-' + grade)
})
},
/**
* @ngdoc method
* @name vum.Platform#device
* @description Return the current device (given by cordova).
* @returns {object} The device object.
*/
device: function () {
return window.device || {}
},
_checkPlatforms: function () {
self.platforms = []
var grade = 'a'
/*if (self.isWebView()) {
self.platforms.push('webview')
if (!(!window.cordova && !window.PhoneGap && !window.phonegap)) {
self.platforms.push('cordova')
} else if (typeof window.forge === 'object') {
self.platforms.push('trigger')
}
} else {
self.platforms.push('browser')
}
if (self.isIPad()) self.platforms.push('ipad')*/
var platform = self.platform()
if (platform) {
self.platforms.push(platform)
/*var version = self.version()
if (version) {
var v = version.toString()
if (v.indexOf('.') > 0) {
v = v.replace('.', '_')
} else {
v += '_0'
}
self.platforms.push(platform + v.split('_')[0])
self.platforms.push(platform + v)
if (self.isAndroid() && version < 4.4) {
grade = (version < 4 ? 'c' : 'b')
} else if (self.isWindowsPhone()) {
grade = 'b'
}
}*/
}
// self.setGrade(grade)
},
/**
* @ngdoc method
* @name vum.Platform#isWebView
* @returns {boolean} Check if we are running within a WebView (such as Cordova).
*/
isWebView: function () {
return !(!window.cordova && !window.PhoneGap && !window.phonegap && window.forge !== 'object')
},
/**
* @ngdoc method
* @name vum.Platform#isIPad
* @returns {boolean} Whether we are running on iPad.
*/
isIPad: function () {
if (/iPad/i.test(self.navigator.platform)) {
return true
}
return /iPad/i.test(self.ua)
},
/**
* @ngdoc method
* @name vum.Platform#isIOS
* @returns {boolean} Whether we are running on iOS.
*/
isIOS: function () {
return self.is(IOS)
},
/**
* @ngdoc method
* @name vum.Platform#isAndroid
* @returns {boolean} Whether we are running on Android.
*/
isAndroid: function () {
return self.is(ANDROID)
},
/**
* @ngdoc method
* @name vum.Platform#isWindowsPhone
* @returns {boolean} Whether we are running on Windows Phone.
*/
isWindowsPhone: function () {
return self.is(WINDOWS_PHONE)
},
/**
* @ngdoc method
* @name vum.Platform#isEdge
* @returns {boolean} Whether we are running on MS Edge/Windows 10 (inc. Phone)
*/
isEdge: function () {
return self.is(EDGE)
},
isCrosswalk: function () {
return self.is(CROSSWALK)
},
/**
* @ngdoc method
* @name vum.Platform#platform
* @returns {string} The name of the current platform.
*/
platform: function () {
// singleton to get the platform name
if (platformName === null) self.setPlatform(self.device().platform)
return platformName
},
/**
* @private
*/
setPlatform: function (n) {
if (typeof n !== 'undefined' && n !== null && n.length) {
platformName = n.toLowerCase()
} else if (getParameterByName('vumplatform')) {
platformName = getParameterByName('vumplatform')
} else if (self.ua.indexOf('Edge') > -1) {
platformName = EDGE
} else if (self.ua.indexOf('Windows Phone') > -1) {
platformName = WINDOWS_PHONE
} else if (self.ua.indexOf('Android') > 0) {
platformName = ANDROID
} else if (/iPhone|iPad|iPod/.test(self.ua)) {
platformName = IOS
} else {
platformName = self.navigator.platform && navigator.platform.toLowerCase().split(' ')[0] || ''
}
},
/**
* @ngdoc method
* @name vum.Platform#version
* @returns {number} The version of the current device platform.
*/
version: function () {
// singleton to get the platform version
if (platformVersion === null) self.setVersion(self.device().version)
return platformVersion
},
/**
* @private
*/
setVersion: function (v) {
if (typeof v !== 'undefined' && v !== null) {
v = v.split('.')
v = parseFloat(v[0] + '.' + (v.length > 1 ? v[1] : 0))
if (!isNaN(v)) {
platformVersion = v
return
}
}
platformVersion = 0
// fallback to user-agent checking
var pName = self.platform()
var versionMatch = {
'android': /Android (\d+).(\d+)?/,
'ios': /OS (\d+)_(\d+)?/,
'windowsphone': /Windows Phone (\d+).(\d+)?/,
}
if (versionMatch[pName]) {
v = self.ua.match(versionMatch[pName])
if (v && v.length > 2) {
platformVersion = parseFloat(v[1] + '.' + v[2])
}
}
},
/**
* @ngdoc method
* @name vum.Platform#is
* @param {string} Platform name.
* @returns {boolean} Whether the platform name provided is detected.
*/
is: function (type) {
type = type.toLowerCase()
// check if it has an array of platforms
if (self.platforms) {
for (var x = 0; x < self.platforms.length; x++) {
if (self.platforms[x] === type) return true
}
}
// exact match
var pName = self.platform()
if (pName) {
return pName === type.toLowerCase()
}
// A quick hack for to check userAgent
return self.ua.toLowerCase().indexOf(type) >= 0
},
/**
* @ngdoc method
* @name vum.Platform#exitApp
* @description Exit the app.
*/
exitApp: function () {
self.ready(function () {
navigator.app && navigator.app.exitApp && navigator.app.exitApp()
})
},
/**
* @ngdoc method
* @name vum.Platform#showStatusBar
* @description Shows or hides the device status bar (in Cordova). Requires `cordova plugin add org.apache.cordova.statusbar`
* @param {boolean} shouldShow Whether or not to show the status bar.
*/
showStatusBar: function (val) {
// Only useful when run within cordova
self._showStatusBar = val
self.ready(function () {
// run this only when or if the platform (cordova) is ready
requestAnimationFrame(function () {
if (self._showStatusBar) {
// they do not want it to be full screen
window.StatusBar && window.StatusBar.show()
document.body.classList.remove('status-bar-hide')
} else {
// it should be full screen
window.StatusBar && window.StatusBar.hide()
document.body.classList.add('status-bar-hide')
}
})
})
},
/**
* @ngdoc method
* @name vum.Platform#fullScreen
* @description
* Sets whether the app is fullscreen or not (in Cordova).
* @param {boolean=} showFullScreen Whether or not to set the app to fullscreen. Defaults to true. Requires `cordova plugin add org.apache.cordova.statusbar`
* @param {boolean=} showStatusBar Whether or not to show the device's status bar. Defaults to false.
*/
fullScreen: function (showFullScreen, showStatusBar) {
// showFullScreen: default is true if no param provided
self.isFullScreen = (showFullScreen !== false)
// add/remove the fullscreen classname to the body
vum.DomUtil.ready(function () {
// run this only when or if the DOM is ready
requestAnimationFrame(function () {
if (self.isFullScreen) {
document.body.classList.add('fullscreen')
} else {
document.body.classList.remove('fullscreen')
}
})
// showStatusBar: default is false if no param provided
self.showStatusBar((showStatusBar === true))
})
},
}
var platformName = null, // just the name, like iOS or Android
platformVersion = null, // a float of the major and minor, like 7.1
readyCallbacks = [],
windowLoadListenderAttached,
platformReadyTimer = 2000 // How long to wait for platform ready before emitting a warning
verifyPlatformReady()
// Warn the user if deviceready did not fire in a reasonable amount of time, and how to fix it.
function verifyPlatformReady () {
setTimeout(function () {
if (!self.isReady && self.isWebView()) {
void 0
}
}, platformReadyTimer)
}
// setup listeners to know when the device is ready to go
function onWindowLoad () {
if (self.isWebView()) {
// the window and scripts are fully loaded, and a cordova/phonegap
// object exists then let's listen for the deviceready
document.addEventListener('deviceready', onPlatformReady, false)
} else {
// the window and scripts are fully loaded, but the window object doesn't have the
// cordova/phonegap object, so its just a browser, not a webview wrapped w/ cordova
onPlatformReady()
}
if (windowLoadListenderAttached) {
window.removeEventListener('load', onWindowLoad, false)
}
}
if (document.readyState === 'complete') {
onWindowLoad()
} else {
windowLoadListenderAttached = true
window.addEventListener('load', onWindowLoad, false)
}
function onPlatformReady () {
// the device is all set to go, init our own stuff then fire off our event
self.isReady = true
self.detect()
for (var x = 0; x < readyCallbacks.length; x++) {
// fire off all the callbacks that were added before the platform was ready
readyCallbacks[x]()
}
readyCallbacks = []
vum.trigger('platformready', {target: document})
requestAnimationFrame(function () {
document.body.classList.add('platform-ready')
})
}
// document.addEventListener('')
})(window, document, vum);
/**
* ion-events.js
*
* Author: Max Lynch <max@drifty.com>
*
* Framework events handles various mobile browser events, and
* detects special events like tap/swipe/etc. and emits them
* as custom events that can be used in an app.
*
* Portions lovingly adapted from github.com/maker/ratchet and github.com/alexgibson/tap.js - thanks guys!
*/
(function (vum) {
// Custom event polyfill
vum.CustomEvent = (function () {
if (typeof window.CustomEvent === 'function') return CustomEvent
var customEvent = function (event, params) {
var evt
params = params || {
bubbles: false,
cancelable: false,
detail: undefined,
}
try {
evt = document.createEvent('CustomEvent')
evt.initCustomEvent(event, params.bubbles, params.cancelable, params.detail)
} catch (error) {
// fallback for browsers that don't support createEvent('CustomEvent')
evt = document.createEvent('Event')
for (var param in params) {
evt[param] = params[param]
}
evt.initEvent(event, params.bubbles, params.cancelable)
}
return evt
}
customEvent.prototype = window.Event.prototype
return customEvent
})()
/**
* @ngdoc utility
* @name vum.EventController
* @module vum
*/
vum.EventController = {
VIRTUALIZED_EVENTS: ['tap', 'swipe', 'swiperight', 'swipeleft', 'drag', 'hold', 'release'],
/**
* @ngdoc method
* @name vum.EventController#trigger
* @alias vum.trigger
* @param {string} eventType The event to trigger.
* @param {object} data The data for the event. Hint: pass in
* `{target: targetElement}`
* @param {boolean=} bubbles Whether the event should bubble up the DOM.
* @param {boolean=} cancelable Whether the event should be cancelable.
*/
// Trigger a new event
trigger: function (eventType, data, bubbles, cancelable) {
var event = new vum.CustomEvent(eventType, {
detail: data,
bubbles: !!bubbles,
cancelable: !!cancelable,
})
// Make sure to trigger the event on the given target, or dispatch it from
// the window if we don't have an event target
data && data.target && data.target.dispatchEvent && data.target.dispatchEvent(event) || window.dispatchEvent(event)
},
/**
* @ngdoc method
* @name vum.EventController#on
* @alias vum.on
* @description Listen to an event on an element.
* @param {string} type The event to listen for.
* @param {function} callback The listener to be called.
* @param {DOMElement} element The element to listen for the event on.
*/
on: function (type, callback, element) {
var e = element || window
// Bind a gesture if it's a virtual event
for (var i = 0, j = this.VIRTUALIZED_EVENTS.length; i < j; i++) {
if (type == this.VIRTUALIZED_EVENTS[i]) {
var gesture = new vum.Gesture(element)
gesture.on(type, callback)
return gesture
}
}
// Otherwise bind a normal event
e.addEventListener(type, callback)
},
/**
* @ngdoc method
* @name vum.EventController#off
* @alias vum.off
* @description Remove an event listener.
* @param {string} type
* @param {function} callback
* @param {DOMElement} element
*/
off: function (type, callback, element) {
element.removeEventListener(type, callback)
},
/**
* @ngdoc method
* @name vum.EventController#onGesture
* @alias vum.onGesture
* @description Add an event listener for a gesture on an element.
*
* Available eventTypes (from [hammer.js](http://eightmedia.github.io/hammer.js/)):
*
* `hold`, `tap`, `doubletap`, `drag`, `dragstart`, `dragend`, `dragup`, `dragdown`, <br/>
* `dragleft`, `dragright`, `swipe`, `swipeup`, `swipedown`, `swipeleft`, `swiperight`, <br/>
* `transform`, `transformstart`, `transformend`, `rotate`, `pinch`, `pinchin`, `pinchout`, <br/>
* `touch`, `release`
*
* @param {string} eventType The gesture event to listen for.
* @param {function(e)} callback The function to call when the gesture
* happens.
* @param {DOMElement} element The angular element to listen for the event on.
* @param {object} options object.
* @returns {vum.Gesture} The gesture object (use this to remove the gesture later on).
*/
onGesture: function (type, callback, element, options) {
var gesture = new vum.Gesture(element, options)
gesture.on(type, callback)
return gesture
},
/**
* @ngdoc method
* @name vum.EventController#offGesture
* @alias vum.offGesture
* @description Remove an event listener for a gesture created on an element.
* @param {vum.Gesture} gesture The gesture that should be removed.
* @param {string} eventType The gesture event to remove the listener for.
* @param {function(e)} callback The listener to remove.
*/
offGesture: function (gesture, type, callback) {
gesture && gesture.off(type, callback)
},
handlePopState: function () {
},
}
// Map some convenient top-level functions for event handling
vum.on = function () {
vum.EventController.on.apply(vum.EventController, arguments)
}
vum.off = function () {
vum.EventController.off.apply(vum.EventController, arguments)
}
vum.trigger = vum.EventController.trigger// function() { vum.EventController.trigger.apply(vum.EventController.trigger, arguments); };
vum.onGesture = function () {
return vum.EventController.onGesture.apply(vum.EventController.onGesture, arguments)
}
vum.offGesture = function () {
return vum.EventController.offGesture.apply(vum.EventController.offGesture, arguments)
}
})(window.vum);
(function (vum) {
vum.$vumPlatform = {
/**
* @ngdoc method
* @name $vumPlatform#onHardwareBackButton
* @description
* Some platforms have a hardware back button, so this is one way to
* bind to it.
* @param {function} callback the callback to trigger when this event occurs
*/
onHardwareBackButton: function (cb) {
vum.Platform.ready(function () {
document.addEventListener('backbutton', cb, false)
})
},
/**
* @ngdoc method
* @name $vumPlatform#offHardwareBackButton
* @description
* Remove an event listener for the backbutton.
* @param {function} callback The listener function that was
* originally bound.
*/
offHardwareBackButton: function (fn) {
vum.Platform.ready(function () {
document.removeEventListener('backbutton', fn)
})
},
/**
* @ngdoc method
* @name $vumPlatform#registerBackButtonAction
* @description
* Register a hardware back button action. Only one action will execute
* when the back button is clicked, so this method decides which of
* the registered back button actions has the highest priority.
*
* For example, if an actionsheet is showing, the back button should
* close the actionsheet, but it should not also go back a page view
* or close a modal which may be open.
*
* The priorities for the existing back button hooks are as follows:
* Return to previous view = 100
* Close side menu = 150
* Dismiss modal = 200
* Close action sheet = 300
* Dismiss popup = 400
* Dismiss loading overlay = 500
*
* Your back button action will override each of the above actions
* whose priority is less than the priority you provide. For example,
* an action assigned a priority of 101 will override the 'return to
* previous view' action, but not any of the other actions.
*
* @param {function} callback Called when the back button is pressed,
* if this listener is the highest priority.
* @param {number} priority Only the highest priority will execute.
* @param {*=} actionId The id to assign this action. Default: a
* random unique id.
* @returns {function} A function that, when called, will deregister
* this backButtonAction.
*/
$backButtonActions: {},
registerBackButtonAction: function (fn, priority, actionId) {
if (!vum.$vumPlatform._hasBackButtonHandler) {
// add a back button listener if one hasn't been setup yet
vum.$vumPlatform.$backButtonActions = {}
vum.$vumPlatform.onHardwareBackButton(vum.$vumPlatform.hardwareBackButtonClick)
vum.$vumPlatform._hasBackButtonHandler = true
}
var action = {
id: (actionId || vum.Utils.nextUid()),
priority: (priority || 0),
fn: fn,
}
vum.$vumPlatform.$backButtonActions[action.id] = action
// return a function to de-register this back button action
return function () {
delete vum.$vumPlatform.$backButtonActions[action.id]
}
},
/**
* @private
*/
hardwareBackButtonClick: function (e) {
// loop through all the registered back button actions
// and only run the last one of the highest priority
var priorityAction, actionId
for (actionId in vum.$vumPlatform.$backButtonActions) {
if (!priorityAction || vum.$vumPlatform.$backButtonActions[actionId].priority >= priorityAction.priority) {
priorityAction = vum.$vumPlatform.$backButtonActions[actionId]
}
}
if (priorityAction) {
priorityAction.fn(e)
return priorityAction
}
},
is: function (type) {
return vum.Platform.is(type)
},
/**
* @ngdoc method
* @name $vumPlatform#on
* @description
* Add Cordova event listeners, such as `pause`, `resume`, `volumedownbutton`, `batterylow`,
* `offline`, etc. More information about available event types can be found in
* [Cordova's event documentation](https://cordova.apache.org/docs/en/edge/cordova_events_events.md.html#Events).
* @param {string} type Cordova [event type](https://cordova.apache.org/docs/en/edge/cordova_events_events.md.html#Events).
* @param {function} callback Called when the Cordova event is fired.
* @returns {function} Returns a deregistration function to remove the event listener.
*/
on: function (type, cb) {
vum.Platform.ready(function () {
document.addEventListener(type, cb, false)
})
return function () {
vum.Platform.ready(function () {
document.removeEventListener(type, cb)
})
}
},
/**
* @ngdoc method
* @name $vumPlatform#ready
* @description
* Trigger a callback once the device is ready,
* or immediately if the device is already ready.
* @param {function=} callback The function to call.
*/
ready: function (cb) {
vum.Platform.ready(function () {
cb()
})
},
getWinSize: function () {
let winWidth
let winHeight
// 获取窗口宽度
if (window.innerWidth) { winWidth = window.innerWidth } else if ((document.body) && (document.body.clientWidth))
// 获取窗口高度
{ winWidth = document.body.clientWidth }
if (window.innerHeight) { winHeight = window.innerHeight } else if ((document.body) && (document.body.clientHeight)) { winHeight = document.body.clientHeight }
// 通过深入Document内部对body进行检测,获取窗口大小
if (document.documentElement && document.documentElement.clientHeight && document.documentElement.clientWidth) {
winHeight = document.documentElement.clientHeight
winWidth = document.documentElement.clientWidth
}
return {'width': winWidth, 'height': winHeight}
},
}
return vum.$vumPlatform
})(window.vum);
(function () {
var nextId = 0
vum.Utils = {
nextUid: function () {
return 'vue' + (nextId++)
},
}
var jqLite // delay binding since jQuery could be loaded after us.
var hasOwnProperty = Object.prototype.hasOwnProperty
/**
* @ngdoc function
* @name angular.isUndefined
* @module ng
* @kind function
*
* @description
* Determines if a reference is undefined.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is undefined.
*/
vum.isUndefined = function (value) {
return typeof value === 'undefined'
}
/**
* @ngdoc function
* @name angular.isDefined
* @module ng
* @kind function
*
* @description
* Determines if a reference is defined.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is defined.
*/
vum.isDefined = function (value) {
return typeof value !== 'undefined'
}
/**
* @ngdoc function
* @name angular.isObject
* @module ng
* @kind function
*
* @description
* Determines if a reference is an `Object`. Unlike `typeof` in JavaScript, `null`s are not
* considered to be objects. Note that JavaScript arrays are objects.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is an `Object` but not `null`.
*/
vum.isObject = function (value) {
// http://jsperf.com/isobject4
return value !== null && typeof value === 'object'
}
/**
* Determine if a value is an object with a null prototype
*
* @returns {boolean} True if `value` is an `Object` with a null prototype
*/
vum.isBlankObject = function (value) {
return value !== null && typeof value === 'object' && !getPrototypeOf(value)
}
/**
* @ngdoc function
* @name angular.isString
* @module ng
* @kind function
*
* @description
* Determines if a reference is a `String`.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `String`.
*/
vum.isString = function (value) {
return typeof value === 'string'
}
/**
* @ngdoc function
* @name angular.isNumber
* @module ng
* @kind function
*
* @description
* Determines if a reference is a `Number`.
*
* This includes the "special" numbers `NaN`, `+Infinity` and `-Infinity`.
*
* If you wish to exclude these then you can use the native
* [`isFinite'](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/isFinite)
* method.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `Number`.
*/
vum.isNumber = function (value) {
return typeof value === 'number'
}
/**
* @ngdoc function
* @name angular.isDate
* @module ng
* @kind function
*
* @description
* Determines if a value is a date.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `Date`.
*/
vum.isDate = function (value) {
return toString.call(value) === '[object Date]'
}
/**
* @ngdoc function
* @name angular.isArray
* @module ng
* @kind function
*
* @description
* Determines if a reference is an `Array`.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is an `Array`.
*/
var isArray = Array.isArray
/**
* @ngdoc function
* @name angular.isFunction
* @module ng
* @kind function
*
* @description
* Determines if a reference is a `Function`.
*
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `Function`.
*/
vum.isFunction = function (value) {
return typeof value === 'function'
}
/**
* Determines if a value is a regular expression object.
*
* @private
* @param {*} value Reference to check.
* @returns {boolean} True if `value` is a `RegExp`.
*/
vum.isRegExp = function (value) {
return toString.call(value) === '[object RegExp]'
}
/**
* Checks if `obj` is a window object.
*
* @private
* @param {*} obj Object to check
* @returns {boolean} True if `obj` is a window obj.
*/
vum.isWindow = function (obj) {
return obj && obj.window === obj
}
vum.isScope = function (obj) {
return obj && obj.$evalAsync && obj.$watch
}
vum.isFile = function (obj) {
return toString.call(obj) === '[object File]'
}
vum.isFormData = function (obj) {
return toString.call(obj) === '[object FormData]'
}
vum.isBlob = function (obj) {
return toString.call(obj) === '[object Blob]'
}
vum.isBoolean = function (value) {
return typeof value === 'boolean'
}
vum.isPromiseLike = function (obj) {
return obj && isFunction(obj.then)
}
var TYPED_ARRAY_REGEXP = /^\[object (?:Uint8|Uint8Clamped|Uint16|Uint32|Int8|Int16|Int32|Float32|Float64)Array\]$/
vum.isTypedArray = function (value) {
return value && isNumber(value.length) && TYPED_ARRAY_REGEXP.test(toString.call(value))
}
vum.isArrayBuffer = function (obj) {
return toString.call(obj) === '[object ArrayBuffer]'
}
vum.toJsonReplacer = function (key, value) {
var val = value
if (typeof key === 'string' && key.charAt(0) === '$' && key.charAt(1) === '$') {
val = undefined
} else if (vum.isWindow(value)) {
val = '$WINDOW'
} else if (value && document === value) {
val = '$DOCUMENT'
} else if (vum.isScope(value)) {
val = '$SCOPE'
}
return val
}
/**
* @ngdoc function
* @name angular.toJson
* @module ng
* @kind function
*
* @description
* Serializes input into a JSON-formatted string. Properties with leading $$ characters will be
* stripped since angular uses this notation internally.
*
* @param {Object|Array|Date|string|number} obj Input to be serialized into JSON.
* @param {boolean|number} [pretty=2] If set to true, the JSON output will contain newlines and whitespace.
* If set to an integer, the JSON output will contain that many spaces per indentation.
* @returns {string|undefined} JSON-ified string representing `obj`.
*/
vum.toJson = function (obj, pretty) {
if (vum.isUndefined(obj)) return undefined
if (!vum.isNumber(pretty)) {
pretty = pretty ? 2 : null
}
return JSON.stringify(obj, vum.toJsonReplacer, pretty)
}
function isArrayLike (obj) {
// `null`, `undefined` and `window` are not array-like
if (obj == null || isWindow(obj)) return false
// arrays, strings and jQuery/jqLite objects are array like
// * jqLite is either the jQuery or jqLite constructor function
// * we have to check the existence of jqLite first as this method is called
// via the forEach method when constructing the jqLite object in the first place
if (isArray(obj) || vum.isString(obj) || (jqLite && obj instanceof jqLite)) return true
// Support: iOS 8.2 (not reproducible in simulator)
// "length" in obj used to prevent JIT error (gh-11508)
var length = 'length' in Object(obj) && obj.length
// NodeList objects (with `item` method) and
// other objects with suitable length characteristics are array-like
return vum.isNumber(length) &&
(length >= 0 && ((length - 1) in obj || obj instanceof Array) || typeof obj.item === 'function')
}
/**
* @ngdoc function
* @name angular.forEach
* @module ng
* @kind function
*
* @description
* Invokes the `iterator` function once for each item in `obj` collection, which can be either an
* object or an array. The `iterator` function is invoked with `iterator(value, key, obj)`, where `value`
* is the value of an object property or an array element, `key` is the object property key or
* array element index and obj is the `obj` itself. Specifying a `context` for the function is optional.
*
* It is worth noting that `.forEach` does not iterate over inherited properties because it filters
* using the `hasOwnProperty` method.
*
* Unlike ES262's
* [Array.prototype.forEach](http://www.ecma-international.org/ecma-262/5.1/#sec-15.4.4.18),
* providing 'undefined' or 'null' values for `obj` will not throw a TypeError, but rather just
* return the value provided.
*
```js
var values = {name: 'misko', gender: 'male'};
var log = [];
angular.forEach(values, function(value, key) {
this.push(key + ': ' + value);
}, log);
expect(log).toEqual(['name: misko', 'gender: male']);
```
*
* @param {Object|Array} obj Object to iterate over.
* @param {Function} iterator Iterator function.
* @param {Object=} context Object to become context (`this`) for the iterator function.
* @returns {Object|Array} Reference to `obj`.
*/
vum.forEach = function (obj, iterator, context) {
var key, length
if (obj) {
if (vum.isFunction(obj)) {
for (key in obj) {
// Need to check if hasOwnProperty exists,
// as on IE8 the result of querySelectorAll is an object without a hasOwnProperty function
if (key != 'prototype' && key != 'length' && key != 'name' && (!obj.hasOwnProperty || obj.hasOwnProperty(key))) {
iterator.call(context, obj[key], key, obj)
}
}
} else if (isArray(obj) || isArrayLike(obj)) {
var isPrimitive = typeof obj !== 'object'
for (key = 0, length = obj.length; key < length; key++) {
if (isPrimitive || key in obj) {
iterator.call(context, obj[key], key, obj)
}
}
} else if (obj.forEach && obj.forEach !== forEach) {
obj.forEach(iterator, context, obj)
} else if (vum.isBlankObject(obj)) {
// createMap() fast path --- Safe to avoid hasOwnProperty check because prototype chain is empty
for (key in obj) {
iterator.call(context, obj[key], key, obj)
}
} else if (typeof obj.hasOwnProperty === 'function') {
// Slow path for objects inheriting Object.prototype, hasOwnProperty check needed
for (key in obj) {
if (obj.hasOwnProperty(key)) {
iterator.call(context, obj[key], key, obj)
}
}
} else {
// Slow path for objects which do not have a method `hasOwnProperty`
for (key in obj) {
if (hasOwnProperty.call(obj, key)) {
iterator.call(context, obj[key], key, obj)
}
}
}
}
return obj
}
vum.forEachSorted = function (obj, iterator, context) {
var keys = Object.keys(obj).sort()
for (var i = 0; i < keys.length; i++) {
iterator.call(context, obj[keys[i]], keys[i])
}
return keys
}
})(window.vum)
})()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment