forked from bootstrap-vue/bootstrap-vue
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathplay.js
More file actions
172 lines (141 loc) · 4.27 KB
/
Copy pathplay.js
File metadata and controls
172 lines (141 loc) · 4.27 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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
import Vue from 'vue'
import debounce from 'lodash/debounce'
import hljs from 'highlight.js'
import needsTranspiler from '../utils/needs-transpiler'
// --- Constants ---
const NAME_REGEX = /<!-- (.*)\.vue -->/
const NAME_DEFINITION_REGEX = /<!-- .*\.vue -->/
const TEMPLATE_REGEX = /<template>([\s\S]*)<\/template>/
const SCRIPT_REGEX = /<script>([\s\S]*)<\/script>/
const CLASS_NAMES = {
editable: 'editable',
live: 'live',
error: 'error'
}
// --- Helper functions ---
// Default "transpiler" function
let compiler = code => code
const match = (regex, text) => (regex.exec(text) || [])[1]
const removeNode = node => node && node.parentNode && node.parentNode.removeChild(node)
const parseVueTemplate = text => {
let template = match(TEMPLATE_REGEX, text)
let script = match(SCRIPT_REGEX, text)
let options = {}
// It is plain code
if (!template) {
template = text
}
// Try to evaluate script
if (script && script.includes('export default')) {
try {
const code = compiler(script.replace('export default', ';options = '))
// eslint-disable-next-line no-eval
eval(code)
} catch (e) {
return false
}
}
return { template, script, options }
}
const createVM = (name, node, vnode) => {
try {
// Try to parse the vue template
let vt = parseVueTemplate(node.textContent)
if (!vt) {
return null
}
let { template, options } = vt
// Create a placeholder after node
let holder = document.createElement('div')
node.parentNode.insertBefore(holder, node)
// Create VM
return new Vue({
...options,
el: holder,
template: `<div class='bd-example vue-example vue-example-${name}'>${template}</div>`,
router: vnode.context.$router
})
} catch (e) {
console.error('[v-play]', e)
}
return null
}
const destroyVM = (name, vm) => {
if (vm) {
vm.$destroy()
removeNode(vm.$el)
vm.$el.innerHTML = ''
}
;[...document.querySelectorAll(`.vue-example-${name}`)].forEach(removeNode)
}
const processExamples = (el, binding, vnode, oldVnode) => {
if (vnode.context.$options['beforeDestroy']) {
vnode.context.$options['beforeDestroy'] = []
.concat(vnode.context.$options['beforeDestroy'])
.filter(h => h)
} else {
vnode.context.$options['beforeDestroy'] = []
}
// Get all code-snippets
const pres = [...el.querySelectorAll('pre.hljs')]
// Iterate over them and parse
pres.forEach(pre => {
// Store example name globally
const name = match(NAME_REGEX, pre.textContent)
// Exit early when no name is given
if (!name) {
return
}
// Remove name definition
let text = pre.textContent.replace(NAME_DEFINITION_REGEX, '').trim()
pre.textContent = text
// Highlight again
hljs.highlightBlock(pre)
// Add editable class
pre.classList.add(CLASS_NAMES.editable)
// Initial load
let vm = createVM(name, pre, vnode)
// Ensure we destroy the VM when parent is destroyed
vnode.context.$options['beforeDestroy'].push(() => destroyVM(name, vm))
// Enable live edit on double click
pre.ondblclick = async () => {
// Add live class
pre.classList.add(CLASS_NAMES.live)
// Make editable
pre.contentEditable = true
pre.onblur = () => {
// Re-highlight
hljs.highlightBlock(pre)
}
pre.onkeyup = debounce(() => {
// Recreate VM
destroyVM(name, vm)
vm = createVM(name, pre, vnode)
// Toggle error class
if (vm === null) {
pre.classList.add(CLASS_NAMES.error)
} else {
pre.classList.remove(CLASS_NAMES.error)
}
}, 500)
}
})
}
// Register our v-play directive
Vue.directive('play', (el, binding, vnode, oldVnode) => {
vnode.context.$nextTick(() => {
if (needsTranspiler) {
window && window.$nuxt && window.$nuxt.$loading.start()
import('../utils/compile-js').then(module => {
// Save the compiler reference for template parser
compiler = module.default
// Convert examples to live/editable
processExamples(el, binding, vnode, oldVnode)
window && window.$nuxt && window.$nuxt.$loading.finish()
})
} else {
// Convert examples to live/editable
processExamples(el, binding, vnode, oldVnode)
}
})
})