env.js
4.29 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
/* @flow */
/* globals MutationObserver */
import { noop } from 'shared/util'
// can we use __proto__?
export const hasProto = '__proto__' in {}
// Browser environment sniffing
export const inBrowser = typeof window !== 'undefined'
export const UA = inBrowser && window.navigator.userAgent.toLowerCase()
export const isIE = UA && /msie|trident/.test(UA)
export const isIE9 = UA && UA.indexOf('msie 9.0') > 0
export const isEdge = UA && UA.indexOf('edge/') > 0
export const isAndroid = UA && UA.indexOf('android') > 0
export const isIOS = UA && /iphone|ipad|ipod|ios/.test(UA)
// this needs to be lazy-evaled because vue may be required before
// vue-server-renderer can set VUE_ENV
let _isServer
export const isServerRendering = () => {
if (_isServer === undefined) {
/* istanbul ignore if */
if (!inBrowser && typeof global !== 'undefined') {
// detect presence of vue-server-renderer and avoid
// Webpack shimming the process
_isServer = global['process'].env.VUE_ENV === 'server'
} else {
_isServer = false
}
}
return _isServer
}
// detect devtools
export const devtools = inBrowser && window.__VUE_DEVTOOLS_GLOBAL_HOOK__
/* istanbul ignore next */
function isNative (Ctor: Function): boolean {
return /native code/.test(Ctor.toString())
}
/**
* Defer a task to execute it asynchronously.
*/
export const nextTick = (function () {
const callbacks = []
let pending = false
let timerFunc
function nextTickHandler () {
pending = false
const copies = callbacks.slice(0)
callbacks.length = 0
for (let i = 0; i < copies.length; i++) {
copies[i]()
}
}
// the nextTick behavior leverages the microtask queue, which can be accessed
// via either native Promise.then or MutationObserver.
// MutationObserver has wider support, however it is seriously bugged in
// UIWebView in iOS >= 9.3.3 when triggered in touch event handlers. It
// completely stops working after triggering a few times... so, if native
// Promise is available, we will use it:
/* istanbul ignore if */
if (typeof Promise !== 'undefined' && isNative(Promise)) {
var p = Promise.resolve()
var logError = err => { console.error(err) }
timerFunc = () => {
p.then(nextTickHandler).catch(logError)
// in problematic UIWebViews, Promise.then doesn't completely break, but
// it can get stuck in a weird state where callbacks are pushed into the
// microtask queue but the queue isn't being flushed, until the browser
// needs to do some other work, e.g. handle a timer. Therefore we can
// "force" the microtask queue to be flushed by adding an empty timer.
if (isIOS) setTimeout(noop)
}
} else if (typeof MutationObserver !== 'undefined' && (
isNative(MutationObserver) ||
// PhantomJS and iOS 7.x
MutationObserver.toString() === '[object MutationObserverConstructor]'
)) {
// use MutationObserver where native Promise is not available,
// e.g. PhantomJS IE11, iOS7, Android 4.4
var counter = 1
var observer = new MutationObserver(nextTickHandler)
var textNode = document.createTextNode(String(counter))
observer.observe(textNode, {
characterData: true
})
timerFunc = () => {
counter = (counter + 1) % 2
textNode.data = String(counter)
}
} else {
// fallback to setTimeout
/* istanbul ignore next */
timerFunc = () => {
setTimeout(nextTickHandler, 0)
}
}
return function queueNextTick (cb?: Function, ctx?: Object) {
let _resolve
callbacks.push(() => {
if (cb) cb.call(ctx)
if (_resolve) _resolve(ctx)
})
if (!pending) {
pending = true
timerFunc()
}
if (!cb && typeof Promise !== 'undefined') {
return new Promise(resolve => {
_resolve = resolve
})
}
}
})()
let _Set
/* istanbul ignore if */
if (typeof Set !== 'undefined' && isNative(Set)) {
// use native Set when available.
_Set = Set
} else {
// a non-standard Set polyfill that only works with primitive keys.
_Set = class Set {
set: Object;
constructor () {
this.set = Object.create(null)
}
has (key: string | number) {
return this.set[key] === true
}
add (key: string | number) {
this.set[key] = true
}
clear () {
this.set = Object.create(null)
}
}
}
export { _Set }