Add theme (#516)
diff --git a/docs/.vuepress/config.js b/docs/.vuepress/config.js
index 2f01a83..3ac2b1d 100644
--- a/docs/.vuepress/config.js
+++ b/docs/.vuepress/config.js
@@ -9,7 +9,6 @@
lang: 'zh-CN'
}
},
- theme: 'fast',
themeConfig: {
repo: 'apache/incubator-weex-site',
docsRepo: 'apache/incubator-weex-site',
diff --git a/docs/.vuepress/theme/Layout.vue b/docs/.vuepress/theme/Layout.vue
new file mode 100755
index 0000000..e2412f6
--- /dev/null
+++ b/docs/.vuepress/theme/Layout.vue
@@ -0,0 +1,217 @@
+<template>
+ <div
+ class="theme-container vuepress-theme-fast"
+ :class="pageClasses"
+ @touchstart="onTouchStart"
+ @touchend="onTouchEnd"
+ >
+ <Navbar
+ v-if="shouldShowNavbar"
+ @toggle-sidebar="toggleSidebar"
+ />
+
+ <div
+ class="sidebar-mask"
+ @click="toggleSidebar(false)"
+ ></div>
+
+ <Sidebar
+ :items="sidebarItems"
+ @toggle-sidebar="toggleSidebar"
+ >
+ <slot
+ name="sidebar-top"
+ slot="top"
+ />
+ <slot
+ name="sidebar-bottom"
+ slot="bottom"
+ />
+ </Sidebar>
+
+ <div
+ class="custom-layout"
+ v-if="$page.frontmatter.layout"
+ >
+ <component :is="$page.frontmatter.layout"/>
+ </div>
+
+ <Home v-else-if="$page.frontmatter.home"/>
+
+ <Page
+ v-else
+ :sidebar-items="sidebarItems"
+ >
+ <slot
+ name="page-top"
+ slot="top"
+ />
+ <slot
+ name="page-bottom"
+ slot="bottom"
+ />
+ </Page>
+
+ <SWUpdatePopup :updateEvent="swUpdateEvent"/>
+ </div>
+</template>
+<script>
+import Vue from 'vue'
+import nprogress from 'nprogress'
+import Home from './src/Home.vue'
+import Navbar from './src/Navbar.vue'
+import Page from './src/Page.vue'
+import Sidebar from './src/Sidebar.vue'
+import SWUpdatePopup from './src/SWUpdatePopup.vue'
+import { resolveSidebarItems, setSpm } from './src/util'
+
+export default {
+ components: { Home, Page, Sidebar, Navbar, SWUpdatePopup },
+
+ data () {
+ return {
+ isSidebarOpen: false,
+ swUpdateEvent: null
+ }
+ },
+
+ computed: {
+ shouldShowNavbar () {
+ const { themeConfig } = this.$site
+ const { frontmatter } = this.$page
+ if (
+ frontmatter.navbar === false ||
+ themeConfig.navbar === false) {
+ return false
+ }
+ return (
+ this.$title ||
+ themeConfig.logo ||
+ themeConfig.repo ||
+ themeConfig.nav ||
+ this.$themeLocaleConfig.nav
+ )
+ },
+
+ shouldShowSidebar () {
+ const { frontmatter } = this.$page
+ return (
+ !frontmatter.layout &&
+ !frontmatter.home &&
+ frontmatter.sidebar !== false &&
+ this.sidebarItems.length
+ )
+ },
+
+ sidebarItems () {
+ return resolveSidebarItems(
+ this.$page,
+ this.$route,
+ this.$site,
+ this.$localePath
+ )
+ },
+
+ pageClasses () {
+ const userPageClass = this.$page.frontmatter.pageClass
+ return [
+ {
+ 'no-navbar': !this.shouldShowNavbar,
+ 'sidebar-open': this.isSidebarOpen,
+ 'no-sidebar': !this.shouldShowSidebar
+ },
+ userPageClass
+ ]
+ }
+ },
+
+ mounted () {
+ window.addEventListener('scroll', this.onScroll)
+
+ // configure progress bar
+ nprogress.configure({ showSpinner: false })
+
+ this.$router.beforeEach((to, from, next) => {
+ if (to.path !== from.path && !Vue.component(to.name)) {
+ nprogress.start()
+ }
+ next()
+ })
+
+ this.$router.afterEach(() => {
+ nprogress.done()
+ this.isSidebarOpen = false
+ })
+
+ this.$on('sw-updated', this.onSWUpdated)
+
+ const siteCfg = this.$site || {}
+ const themeCfg = siteCfg.themeConfig || {}
+ const SPM_A = themeCfg.spm
+ const SPM_B = window.location.pathname.replace('.html', '')
+
+ try {
+ if (process.env.NODE_ENV === 'production' && SPM_A) {
+ setSpm(`${SPM_A}.${SPM_B}`)
+ }
+ } catch {
+ if (SPM_A) {
+ setSpm(`${SPM_A}.${SPM_B}`)
+ }
+ }
+ },
+ watch: {
+ '$route' (to, from) {
+ const siteCfg = this.$site || {}
+ const themeCfg = siteCfg.themeConfig || {}
+ const currPath = to.path || ''
+ const fromPath = from.path || ''
+ const SPM_A = themeCfg.spm
+ const SPM_B = currPath.replace('.html', '')
+
+ try {
+ if (process.env.NODE_ENV === 'production' && currPath !== fromPath && SPM_A) {
+ setSpm(`${SPM_A}.${SPM_B}`)
+ }
+ } catch {
+ if (SPM_A) {
+ setSpm(`${SPM_A}.${SPM_B}`)
+ }
+ }
+ }
+ },
+
+ methods: {
+ toggleSidebar (to) {
+ this.isSidebarOpen = typeof to === 'boolean' ? to : !this.isSidebarOpen
+ },
+
+ // side swipe
+ onTouchStart (e) {
+ this.touchStart = {
+ x: e.changedTouches[0].clientX,
+ y: e.changedTouches[0].clientY
+ }
+ },
+
+ onTouchEnd (e) {
+ const dx = e.changedTouches[0].clientX - this.touchStart.x
+ const dy = e.changedTouches[0].clientY - this.touchStart.y
+ if (Math.abs(dx) > Math.abs(dy) && Math.abs(dx) > 40) {
+ if (dx > 0 && this.touchStart.x <= 80) {
+ this.toggleSidebar(true)
+ } else {
+ this.toggleSidebar(false)
+ }
+ }
+ },
+
+ onSWUpdated (e) {
+ this.swUpdateEvent = e
+ }
+ }
+}
+</script>
+
+<style src="prismjs/themes/prism-tomorrow.css"></style>
+<style src="./src/styles/theme.styl" lang="stylus"></style>
diff --git a/docs/.vuepress/theme/src/AlgoliaSearchBox.vue b/docs/.vuepress/theme/src/AlgoliaSearchBox.vue
new file mode 100755
index 0000000..e5f516e
--- /dev/null
+++ b/docs/.vuepress/theme/src/AlgoliaSearchBox.vue
@@ -0,0 +1,140 @@
+<template>
+ <form
+ id="search-form"
+ class="algolia-search-wrapper search-box"
+ >
+ <input
+ id="algolia-search-input"
+ class="search-query"
+ >
+ </form>
+</template>
+
+<script>
+export default {
+ props: ['options'],
+
+ mounted () {
+ this.initialize()
+ },
+
+ methods: {
+ initialize () {
+ Promise.all([
+ import(/* webpackChunkName: "docsearch" */ 'docsearch.js/dist/cdn/docsearch.min.js'),
+ import(/* webpackChunkName: "docsearch" */ 'docsearch.js/dist/cdn/docsearch.min.css')
+ ]).then(([docsearch]) => {
+ docsearch = docsearch.default
+ docsearch(Object.assign(this.options, {
+ debug: false,
+ inputSelector: '#algolia-search-input'
+ }))
+ })
+ }
+ },
+
+ watch: {
+ options (newValue) {
+ this.$el.innerHTML = '<input id="algolia-search-input" class="search-query">'
+ this.initialize(newValue)
+ }
+ }
+}
+</script>
+
+<style lang="stylus">
+@import './styles/config.styl'
+
+.algolia-search-wrapper
+ & > span
+ vertical-align middle
+ .algolia-autocomplete
+ line-height normal
+ .ds-dropdown-menu
+ background-color #fff
+ border 1px solid #cfd4db
+ border-radius 4px
+ font-size 16px
+ margin 6px 0 0
+ padding 4px
+ text-align left
+ &:before
+ border-color #999
+ [class*=ds-dataset-]
+ border none
+ padding 0
+ .ds-suggestions
+ margin-top 0
+ .ds-suggestion
+ border-bottom 1px solid $borderColor
+ .algolia-docsearch-suggestion--highlight
+ color #00b4ff
+ .algolia-docsearch-suggestion
+ border-color $borderColor
+ padding 0
+ .algolia-docsearch-suggestion--category-header
+ padding 5px 10px
+ margin-top 0
+ background $accentColor
+ color #fff
+ font-weight 600
+ .algolia-docsearch-suggestion--highlight
+ background rgba(255, 255, 255, 0.6)
+ .algolia-docsearch-suggestion--wrapper
+ padding 0
+ .algolia-docsearch-suggestion--title
+ font-weight 600
+ margin-bottom 0
+ color $textColor
+ .algolia-docsearch-suggestion--subcategory-column
+ vertical-align top
+ padding 5px 7px 5px 5px
+ border-color $borderColor
+ background #f1f3f5
+ &:after
+ display none
+ .algolia-docsearch-suggestion--subcategory-column-text
+ color #555
+ .algolia-docsearch-footer
+ border-color $borderColor
+ .ds-cursor .algolia-docsearch-suggestion--content
+ background-color #e7edf3 !important
+ color $textColor
+
+@media (min-width: $MQMobile)
+ .algolia-search-wrapper
+ .algolia-autocomplete
+ .algolia-docsearch-suggestion
+ .algolia-docsearch-suggestion--subcategory-column
+ float none
+ width 150px
+ min-width 150px
+ display table-cell
+ .algolia-docsearch-suggestion--content
+ float none
+ display table-cell
+ width 100%
+ vertical-align top
+ .ds-dropdown-menu
+ min-width 515px !important
+
+@media (max-width: $MQMobile)
+ .algolia-search-wrapper
+ .ds-dropdown-menu
+ min-width calc(100vw - 4rem) !important
+ max-width calc(100vw - 4rem) !important
+ .algolia-docsearch-suggestion--wrapper
+ padding 5px 7px 5px 5px !important
+ .algolia-docsearch-suggestion--subcategory-column
+ padding 0 !important
+ background white !important
+ .algolia-docsearch-suggestion--subcategory-column-text:after
+ content " > "
+ font-size 10px
+ line-height 14.4px
+ display inline-block
+ width 5px
+ margin -3px 3px 0
+ vertical-align middle
+
+</style>
diff --git a/docs/.vuepress/theme/src/DropdownLink.vue b/docs/.vuepress/theme/src/DropdownLink.vue
new file mode 100755
index 0000000..e6000a6
--- /dev/null
+++ b/docs/.vuepress/theme/src/DropdownLink.vue
@@ -0,0 +1,181 @@
+<template>
+ <div
+ class="dropdown-wrapper"
+ :class="{ open }"
+ >
+ <a
+ class="dropdown-title"
+ @click="toggle"
+ >
+ <span class="title">{{ item.text }}</span>
+ <span
+ class="arrow"
+ :class="open ? 'down' : 'right'"
+ ></span>
+ </a>
+
+ <DropdownTransition>
+ <ul
+ class="nav-dropdown"
+ v-show="open"
+ >
+ <li
+ class="dropdown-item"
+ :key="subItem.link || index"
+ v-for="(subItem, index) in item.items"
+ >
+ <h4 v-if="subItem.type === 'links'">{{ subItem.text }}</h4>
+
+ <ul
+ class="dropdown-subitem-wrapper"
+ v-if="subItem.type === 'links'"
+ >
+ <li
+ class="dropdown-subitem"
+ :key="childSubItem.link"
+ v-for="childSubItem in subItem.items"
+ >
+ <NavLink :item="childSubItem"/>
+ </li>
+ </ul>
+
+ <NavLink
+ v-else
+ :item="subItem"
+ />
+ </li>
+ </ul>
+ </DropdownTransition>
+ </div>
+</template>
+
+<script>
+import NavLink from './NavLink.vue'
+import DropdownTransition from './DropdownTransition.vue'
+
+export default {
+ components: { NavLink, DropdownTransition },
+
+ data () {
+ return {
+ open: false
+ }
+ },
+
+ props: {
+ item: {
+ required: true
+ }
+ },
+
+ methods: {
+ toggle () {
+ this.open = !this.open
+ }
+ }
+}
+</script>
+
+<style lang="stylus">
+@import './styles/config.styl'
+
+.dropdown-wrapper
+ cursor pointer
+ .dropdown-title
+ display block
+ &:hover
+ border-color transparent
+ .arrow
+ vertical-align middle
+ margin-top -1px
+ margin-left 0.4rem
+ .nav-dropdown
+ .dropdown-item
+ color inherit
+ line-height 1.7rem
+ h4
+ margin 0.45rem 0 0
+ border-top 1px solid #eee
+ padding 0.45rem 1.5rem 0 1.25rem
+ .dropdown-subitem-wrapper
+ padding 0
+ list-style none
+ .dropdown-subitem
+ font-size 0.9em
+ a
+ display block
+ line-height 1.7rem
+ position relative
+ border-bottom none
+ font-weight 400
+ margin-bottom 0
+ padding 0 1.5rem 0 1.25rem
+ &:hover
+ color $accentColor
+ &.router-link-active
+ color $accentColor
+ &::after
+ content ""
+ width 0
+ height 0
+ border-left 5px solid $accentColor
+ border-top 3px solid transparent
+ border-bottom 3px solid transparent
+ position absolute
+ top calc(50% - 2px)
+ left 9px
+ &:first-child h4
+ margin-top 0
+ padding-top 0
+ border-top 0
+
+@media (max-width: $MQMobile)
+ .dropdown-wrapper
+ &.open .dropdown-title
+ margin-bottom 0.5rem
+ .nav-dropdown
+ transition height .1s ease-out
+ overflow hidden
+ .dropdown-item
+ h4
+ border-top 0
+ margin-top 0
+ padding-top 0
+ h4, & > a
+ font-size 15px
+ line-height 2rem
+ .dropdown-subitem
+ font-size 14px
+ padding-left 1rem
+
+@media (min-width: $MQMobile)
+ .dropdown-wrapper
+ height 1.8rem
+ &:hover .nav-dropdown
+ // override the inline style.
+ display block !important
+ .dropdown-title .arrow
+ // make the arrow always down at desktop
+ border-left 4px solid transparent
+ border-right 4px solid transparent
+ border-top 6px solid $arrowBgColor
+ border-bottom 0
+ .nav-dropdown
+ display none
+ // Avoid height shaked by clicking
+ height auto !important
+ box-sizing border-box;
+ max-height calc(100vh - 2.7rem)
+ overflow-y auto
+ position absolute
+ top 100%
+ right 0
+ background-color #fff
+ padding 0.6rem 0
+ border 1px solid #ddd
+ border-bottom-color #ccc
+ text-align left
+ border-radius 0.25rem
+ white-space nowrap
+ margin 0
+</style>
diff --git a/docs/.vuepress/theme/src/DropdownTransition.vue b/docs/.vuepress/theme/src/DropdownTransition.vue
new file mode 100755
index 0000000..8c711a1
--- /dev/null
+++ b/docs/.vuepress/theme/src/DropdownTransition.vue
@@ -0,0 +1,33 @@
+<template>
+ <transition
+ name="dropdown"
+ @enter="setHeight"
+ @after-enter="unsetHeight"
+ @before-leave="setHeight"
+ >
+ <slot/>
+ </transition>
+</template>
+
+<script>
+export default {
+ name: 'DropdownTransition',
+
+ methods: {
+ setHeight (items) {
+ // explicitly set height so that it can be transitioned
+ items.style.height = items.scrollHeight + 'px'
+ },
+
+ unsetHeight (items) {
+ items.style.height = ''
+ }
+ }
+}
+</script>
+
+<style lang="stylus">
+.dropdown-enter, .dropdown-leave-to
+ height 0 !important
+
+</style>
diff --git a/docs/.vuepress/theme/src/Home.vue b/docs/.vuepress/theme/src/Home.vue
new file mode 100755
index 0000000..69ac0e1
--- /dev/null
+++ b/docs/.vuepress/theme/src/Home.vue
@@ -0,0 +1,161 @@
+<template>
+ <div class="home">
+ <div class="hero">
+ <img
+ v-if="data.heroImage"
+ :src="$withBase(data.heroImage)"
+ alt="hero"
+ >
+
+ <h1>{{ data.heroText || $title || 'Hello' }}</h1>
+
+ <p class="description">
+ {{ data.tagline || $description || 'Welcome to your VuePress site' }}
+ </p>
+
+ <p
+ class="action"
+ v-if="data.actionText && data.actionLink"
+ >
+ <NavLink
+ class="action-button"
+ :item="actionLink"
+ />
+ </p>
+ </div>
+
+ <div
+ class="features"
+ v-if="data.features && data.features.length"
+ >
+ <div
+ class="feature"
+ v-for="feature in data.features"
+ >
+ <h2>{{ feature.title }}</h2>
+ <p>{{ feature.details }}</p>
+ </div>
+ </div>
+
+ <Content custom/>
+
+ <div
+ class="footer"
+ v-if="data.footer"
+ >
+ {{ data.footer }}
+ </div>
+ </div>
+</template>
+
+<script>
+import NavLink from './NavLink.vue'
+
+export default {
+ components: { NavLink },
+
+ computed: {
+ data () {
+ return this.$page.frontmatter
+ },
+
+ actionLink () {
+ return {
+ link: this.data.actionLink,
+ text: this.data.actionText
+ }
+ }
+ }
+}
+</script>
+
+<style lang="stylus">
+@import './styles/config.styl'
+
+.home
+ padding $navbarHeight 2rem 0
+ max-width 960px
+ margin 0px auto
+ .hero
+ text-align center
+ img
+ max-height 280px
+ display block
+ margin 3rem auto 1.5rem
+ h1
+ font-size 3rem
+ h1, .description, .action
+ margin 1.8rem auto
+ .description
+ max-width 35rem
+ font-size 1.6rem
+ line-height 1.3
+ color lighten($textColor, 40%)
+ .action-button
+ display inline-block
+ font-size 1.2rem
+ color #fff
+ background-color $accentColor
+ padding 0.8rem 1.6rem
+ border-radius 4px
+ transition background-color .1s ease
+ box-sizing border-box
+ border-bottom 1px solid darken($accentColor, 10%)
+ &:hover
+ background-color lighten($accentColor, 10%)
+ .features
+ border-top 1px solid $borderColor
+ padding 1.2rem 0
+ margin-top 2.5rem
+ display flex
+ flex-wrap wrap
+ align-items flex-start
+ align-content stretch
+ justify-content space-between
+ .feature
+ flex-grow 1
+ flex-basis 30%
+ max-width 30%
+ h2
+ font-size 1.4rem
+ font-weight 500
+ border-bottom none
+ padding-bottom 0
+ color lighten($textColor, 10%)
+ p
+ color lighten($textColor, 25%)
+ .footer
+ padding 2.5rem
+ border-top 1px solid $borderColor
+ text-align center
+ color lighten($textColor, 25%)
+
+@media (max-width: $MQMobile)
+ .home
+ .features
+ flex-direction column
+ .feature
+ max-width 100%
+ padding 0 2.5rem
+
+@media (max-width: $MQMobileNarrow)
+ .home
+ padding-left 1.5rem
+ padding-right 1.5rem
+ .hero
+ img
+ max-height 210px
+ margin 2rem auto 1.2rem
+ h1
+ font-size 2rem
+ h1, .description, .action
+ margin 1.2rem auto
+ .description
+ font-size 1.2rem
+ .action-button
+ font-size 1rem
+ padding 0.6rem 1.2rem
+ .feature
+ h2
+ font-size 1.25rem
+</style>
diff --git a/docs/.vuepress/theme/src/License.vue b/docs/.vuepress/theme/src/License.vue
new file mode 100644
index 0000000..cb5db72
--- /dev/null
+++ b/docs/.vuepress/theme/src/License.vue
@@ -0,0 +1,170 @@
+<template>
+ <footer class="footer-container">
+ <div class="footer-body">
+ <img class="logo" src="/logo@2x.svg">
+ <img class="apache" src="//img.alicdn.com/tfs/TB11BRiIW6qK1RjSZFmXXX0PFXa-365-365.png">
+ <div class="cols-container">
+ <div class="col col-12">
+ <h3>Disclaimer</h3>
+ <p>Apache Weex is an effort undergoing incubation at The Apache Software Foundation (ASF), sponsored by the
+ Incubator. Incubation is required of all newly accepted projects until a further review indicates that the
+ infrastructure, communications, and decision making process have stabilized in a manner consistent with
+ other successful ASF projects. While incubation status is not necessarily a reflection of the completeness
+ or stability of the code, it does indicate that the project has yet to be fully endorsed by the ASF.</p>
+ </div>
+ <div class="col col-4">
+ <dl><dt>ASF</dt>
+ <dd><a href="http://www.apache.org" target="_self">Foundation</a></dd>
+ <dd><a href="http://www.apache.org/licenses/" target="_self">License</a></dd>
+ <dd><a href="http://www.apache.org/events/current-event" target="_self">Events</a></dd>
+ <dd><a href="http://www.apache.org/foundation/sponsorship.html" target="_self">Sponsorship</a></dd>
+ <dd><a href="http://www.apache.org/foundation/thanks.html" target="_self">Thanks</a></dd>
+ </dl>
+ </div>
+ <div class="col col-4">
+ <dl><dt>Documentation</dt>
+ <dd><a href="/guide/develop/create-a-new-app.html" target="_self">Quick start</a></dd>
+ <dd><a href="/guide/develop/setup-develop-environment.html" target="_self">Developer guide</a></dd>
+ </dl>
+ </div>
+ <div class="col col-4">
+ <dl><dt>Resources</dt>
+ <dd><a href="/blog/write-a-blog.html" target="_self">Blog</a></dd>
+ <dd><a href="/community/" target="_self">Community</a></dd>
+ <dd><a href="/guide/contribute/thanks.html" target="_self">Thanks by Weex</a></dd>
+ <dd><a href="https://www.apache.org/security/" target="_self">Security</a></dd>
+ </dl>
+ </div>
+ </div>
+ <div class="copyright"><span>Copyright © 2018-2019 The Apache Software Foundation. Apache and the Apache feather
+ logo are trademarks of The Apache Software Foundation.</span></div>
+ </div>
+ </footer>
+</template>
+
+<style scoped>
+.footer-container {
+ background: #F8F8F8;
+}
+
+.footer-container .footer-body {
+ max-width: 1280px;
+ margin: 0 auto;
+ box-sizing: border-box;
+ padding: 40px 40px 0;
+}
+
+@media screen and (max-width: 640px) {
+ .footer-container .footer-body {
+ padding-left: 20px;
+ padding-right: 20px;
+ }
+}
+
+.footer-container .footer-body img {
+ width: 100px;
+ height: 40px;
+ margin-left: -10px;
+ margin-bottom: 28px;
+ vertical-align: middle;
+}
+
+.footer-container .footer-body .apache {
+ width: 50px;
+ height: 50px;
+ margin-left: 0;
+}
+
+.footer-container .footer-body .cols-container {
+ margin-bottom: 60px;
+}
+
+.footer-container .footer-body .cols-container .col {
+ display: inline-block;
+ box-sizing: border-box;
+ vertical-align: top;
+}
+
+.footer-container .footer-body .cols-container .col-12 {
+ width: 50%;
+ padding-right: 125px;
+}
+
+.footer-container .footer-body .cols-container .col-6 {
+ width: 25%;
+}
+
+.footer-container .footer-body .cols-container .col-4 {
+ width: 16.666667%;
+}
+
+.footer-container .footer-body .cols-container h3 {
+ font-family: Avenir-Heavy;
+ font-size: 18px;
+ color: #333;
+ line-height: 18px;
+ margin-bottom: 20px;
+}
+
+.footer-container .footer-body .cols-container p {
+ font-family: Avenir-Medium;
+ font-size: 12px;
+ color: #999;
+ line-height: 18px;
+}
+
+.footer-container .footer-body .cols-container dl {
+ font-family: Avenir-Heavy;
+ line-height: 18px;
+}
+
+.footer-container .footer-body .cols-container dt {
+ font-weight: bold;
+ font-size: 18px;
+ color: #333;
+ margin-bottom: 20px;
+}
+
+.footer-container .footer-body .cols-container dd {
+ padding: 0;
+ margin: 0;
+}
+
+.footer-container .footer-body .cols-container dd a {
+ text-decoration: none;
+ display: block;
+ font-size: 14px;
+ color: #999;
+ margin: 10px 0;
+}
+
+.footer-container .footer-body .cols-container dd a:hover {
+ color: #2DACEC;
+}
+
+.footer-container .footer-body .copyright {
+ border-top: 1px solid #ccc;
+ min-height: 60px;
+ line-height: 20px;
+ text-align: center;
+ font-family: Avenir-Medium;
+ font-size: 12px;
+ color: #999;
+ display: flex;
+ align-items: center;
+}
+
+.footer-container .footer-body .copyright span {
+ display: inline-block;
+ margin: 0 auto;
+}
+
+@media screen and (max-width: 640px) {
+ .footer-container .footer-body .cols-container .col {
+ width: 100%;
+ text-align: center;
+ padding: 0;
+ }
+}
+</style>
+
diff --git a/docs/.vuepress/theme/src/NavLink.vue b/docs/.vuepress/theme/src/NavLink.vue
new file mode 100755
index 0000000..d9fa488
--- /dev/null
+++ b/docs/.vuepress/theme/src/NavLink.vue
@@ -0,0 +1,49 @@
+<template>
+ <router-link
+ class="nav-link"
+ :to="link"
+ v-if="!isExternal(link)"
+ :exact="exact"
+ >{{ item.text }}</router-link>
+ <a
+ v-else
+ :href="link"
+ class="nav-link external"
+ :target="isMailto(link) || isTel(link) ? null : '_blank'"
+ :rel="isMailto(link) || isTel(link) ? null : 'noopener noreferrer'"
+ >
+ {{ item.text }}
+ <OutboundLink/>
+ </a>
+</template>
+
+<script>
+import { isExternal, isMailto, isTel, ensureExt } from './util'
+
+export default {
+ props: {
+ item: {
+ required: true
+ }
+ },
+
+ computed: {
+ link () {
+ return ensureExt(this.item.link)
+ },
+
+ exact () {
+ if (this.$site.locales) {
+ return Object.keys(this.$site.locales).some(rootLink => rootLink === this.link)
+ }
+ return this.link === '/'
+ }
+ },
+
+ methods: {
+ isExternal,
+ isMailto,
+ isTel
+ }
+}
+</script>
diff --git a/docs/.vuepress/theme/src/NavLinks.vue b/docs/.vuepress/theme/src/NavLinks.vue
new file mode 100755
index 0000000..ba1fa46
--- /dev/null
+++ b/docs/.vuepress/theme/src/NavLinks.vue
@@ -0,0 +1,115 @@
+<template>
+ <nav
+ class="nav-links"
+ v-if="userLinks.length || repoLink"
+ >
+ <!-- user links -->
+ <div
+ class="nav-item"
+ v-for="item in userLinks"
+ :key="item.link"
+ >
+ <DropdownLink
+ v-if="item.type === 'links'"
+ :item="item"
+ />
+ <NavLink
+ v-else
+ :item="item"
+ />
+ </div>
+ </nav>
+</template>
+
+<script>
+import DropdownLink from './DropdownLink.vue'
+import { resolveNavLinkItem } from './util'
+import NavLink from './NavLink.vue'
+
+export default {
+ components: { NavLink, DropdownLink },
+
+ computed: {
+ userNav () {
+ return this.$themeLocaleConfig.nav || this.$site.themeConfig.nav || []
+ },
+
+ nav () {
+ const { locales } = this.$site
+ if (locales && Object.keys(locales).length > 1) {
+ const currentLink = this.$page.path
+ const routes = this.$router.options.routes
+ const themeLocales = this.$site.themeConfig.locales || {}
+ const languageDropdown = {
+ text: this.$themeLocaleConfig.selectText || 'Languages',
+ items: Object.keys(locales).map(path => {
+ const locale = locales[path]
+ const text = themeLocales[path] && themeLocales[path].label || locale.lang
+ let link
+ // Stay on the current page
+ if (locale.lang === this.$lang) {
+ link = currentLink
+ } else {
+ // Try to stay on the same page
+ link = currentLink.replace(this.$localeConfig.path, path)
+ // fallback to homepage
+ if (!routes.some(route => route.path === link)) {
+ link = path
+ }
+ }
+ return { text, link }
+ })
+ }
+ return [...this.userNav, languageDropdown]
+ }
+ return this.userNav
+ },
+
+ userLinks () {
+ return (this.nav || []).map(link => {
+ return Object.assign(resolveNavLinkItem(link), {
+ items: (link.items || []).map(resolveNavLinkItem)
+ })
+ })
+ }
+ }
+}
+</script>
+
+<style lang="stylus">
+@import './styles/config.styl'
+
+.nav-links
+ display inline-block
+ a
+ line-height 1.4rem
+ color inherit
+ &:hover, &.router-link-active
+ color $accentColor
+ .nav-item
+ position relative
+ display inline-block
+ margin-left 2rem
+ line-height 2rem
+
+@media (max-width: $MQMobile)
+ .nav-links
+ .nav-item, .repo-link
+ margin-left 0
+
+@media (min-width: $MQMobile)
+ .nav-links a
+ &:hover, &.router-link-active
+ color $textColor
+ .nav-item > a:not(.external)
+ &:hover, &.router-link-active
+ color $accentColor
+ &.router-link-active:after
+ content ''
+ display block
+ width 100%
+ height 3px
+ position absolute
+ bottom -0.77rem
+ background-color $accentColor
+</style>
diff --git a/docs/.vuepress/theme/src/Navbar.vue b/docs/.vuepress/theme/src/Navbar.vue
new file mode 100755
index 0000000..068af9c
--- /dev/null
+++ b/docs/.vuepress/theme/src/Navbar.vue
@@ -0,0 +1,130 @@
+<template>
+ <header class="navbar">
+ <SidebarButton @toggle-sidebar="$emit('toggle-sidebar')"/>
+
+ <router-link
+ :to="$localePath"
+ class="home-link"
+ >
+ <img
+ class="logo"
+ v-if="$site.themeConfig.logo"
+ :src="$withBase($site.themeConfig.logo)"
+ :alt="$siteTitle"
+ >
+ </router-link>
+ <span class="version">
+ <a class="version-bg" :href="$site.themeConfig.versionLink">
+ <img src="./version-bg.svg" :alt="$site.themeConfig.version">
+ <span class="version-no">{{$site.themeConfig.version}}</span>
+ </a>
+ </span>
+
+ <div class="links">
+ <AlgoliaSearchBox
+ v-if="isAlgoliaSearch"
+ :options="algolia"
+ />
+ <SearchBox v-else-if="$site.themeConfig.search !== false"/>
+
+ <NavLinks class="can-hide"/>
+
+ <!-- repo link -->
+ <a
+ v-if="repoLink"
+ :href="repoLink"
+ class="repo-link"
+ target="_blank"
+ rel="noopener noreferrer"
+ >
+ <img class="github-icon" src="./github.svg" alt="github">
+ </a>
+ </div>
+ </header>
+</template>
+
+<script>
+import SidebarButton from './SidebarButton.vue'
+import AlgoliaSearchBox from './AlgoliaSearchBox'
+import SearchBox from './SearchBox.vue'
+import NavLinks from './NavLinks.vue'
+
+export default {
+ components: { SidebarButton, NavLinks, SearchBox, AlgoliaSearchBox },
+
+ computed: {
+ algolia () {
+ return this.$themeLocaleConfig.algolia || this.$site.themeConfig.algolia || {}
+ },
+
+ isAlgoliaSearch () {
+ return this.algolia && this.algolia.apiKey && this.algolia.indexName
+ },
+
+ repoLink () {
+ const { repo } = this.$site.themeConfig
+ if (repo) {
+ return /^https?:/.test(repo)
+ ? repo
+ : `https://github.com/${repo}`
+ }
+ },
+ }
+}
+</script>
+
+<style lang="stylus">
+@import './styles/config.styl'
+
+.navbar
+ padding 0.7rem 1.5rem
+ line-height $navbarHeight - 1.4rem
+ position relative
+ a, span, img
+ display inline-block
+ .logo
+ height $navbarHeight - 1.4rem
+ min-width $navbarHeight - 1.4rem
+ margin-right 10px
+ vertical-align top
+ .site-name
+ font-size 1.3rem
+ font-weight 600
+ color $textColor
+ position relative
+ .version
+ position relative
+ .version-bg
+ position absolute
+ line-height 0
+ top -6px
+ .version-no
+ position absolute
+ top 7.5px
+ left 8px
+ font-size 12px
+ transform scale(0.84)
+ color: #fff
+ .links
+ font-size 0.9rem
+ position absolute
+ right 1.5rem
+ top 0.7rem
+ display flex
+ justify-content center
+ .repo-link
+ width 2.2rem
+ height 2.2rem
+ margin-left 1.5rem
+ .github-icon
+ width 2.2rem
+ height 2.2rem
+
+@media (max-width: $MQMobile)
+ .navbar
+ padding-left 4rem
+ .can-hide
+ display none
+ .repo-link
+ display none
+</style>
diff --git a/docs/.vuepress/theme/src/NotFound.vue b/docs/.vuepress/theme/src/NotFound.vue
new file mode 100755
index 0000000..6aefe79
--- /dev/null
+++ b/docs/.vuepress/theme/src/NotFound.vue
@@ -0,0 +1,26 @@
+<template>
+ <div class="theme-container">
+ <div class="content">
+ <h1>404</h1>
+ <blockquote>{{ getMsg() }}</blockquote>
+ <router-link to="/">Take me home.</router-link>
+ </div>
+ </div>
+</template>
+
+<script>
+const msgs = [
+ `There's nothing here.`,
+ `How did we get here?`,
+ `That's a Four-Oh-Four.`,
+ `Looks like we've got some broken links.`
+]
+
+export default {
+ methods: {
+ getMsg () {
+ return msgs[Math.floor(Math.random() * msgs.length)]
+ }
+ }
+}
+</script>
diff --git a/docs/.vuepress/theme/src/Page.vue b/docs/.vuepress/theme/src/Page.vue
new file mode 100755
index 0000000..71b48ef
--- /dev/null
+++ b/docs/.vuepress/theme/src/Page.vue
@@ -0,0 +1,365 @@
+<template>
+ <div class="page">
+ <slot name="top"/>
+
+ <Content :custom="false"/>
+
+ <div class="page-nav" v-if="prev || next">
+ <p class="inner">
+ <span
+ v-if="prev"
+ class="prev"
+ >
+ ←
+ <router-link
+ v-if="prev"
+ class="prev"
+ :to="prev.path"
+ >
+ {{ prev.title || prev.path }}
+ </router-link>
+ </span>
+
+ <span
+ v-if="next"
+ class="next"
+ >
+ <router-link
+ v-if="next"
+ :to="next.path"
+ >
+ {{ next.title || next.path }}
+ </router-link>
+ →
+ </span>
+ </p>
+ </div>
+
+ <div class="page-edit">
+ <div
+ class="last-updated"
+ v-if="lastUpdated"
+ >
+ <span class="prefix">{{ lastUpdatedText }}: </span>
+ <span class="time">{{ lastUpdated }}</span>
+ </div>
+
+ <div
+ class="edit-link"
+ v-if="editLink"
+ >
+ <a
+ :href="editLink"
+ target="_blank"
+ rel="noopener noreferrer"
+ >{{ editLinkText }}</a>
+ <OutboundLink/>
+ <a
+ :href="issueLink()"
+ target="_blank"
+ rel="noopener noreferrer"
+ class="issueText"
+ >{{ openIssueText }}</a>
+ <OutboundLink/>
+ </div>
+
+ <div class="score">
+ <span class="choice"><img @click="scorePlus" class="score-icon" :src="scoreGood ? 'https://img.alicdn.com/tfs/TB1DOvPqCzqK1RjSZPxXXc4tVXa-20-18.svg' : 'https://img.alicdn.com/tfs/TB1h7TSqpYqK1RjSZLeXXbXppXa-20-18.svg'" />{{ scoreText.good }}</span>
+ <span class="choice"><img @click="scoreMinus" class="score-icon bad" :src="scoreBad ? 'https://img.alicdn.com/tfs/TB1DOvPqCzqK1RjSZPxXXc4tVXa-20-18.svg' : 'https://img.alicdn.com/tfs/TB1h7TSqpYqK1RjSZLeXXbXppXa-20-18.svg'" />{{ scoreText.bad }}</span>
+ </div>
+ </div>
+
+ <div v-show="isApache" class="license-wrap">
+ <License />
+ </div>
+
+ <slot name="bottom"/>
+ </div>
+</template>
+
+<script>
+import axios from 'axios';
+import qs from 'qs';
+import { resolvePage, normalize, outboundRE, endingSlashRE } from './util'
+import License from './License.vue';
+
+export default {
+ props: ['sidebarItems'],
+
+ components: {
+ License
+ },
+
+ data() {
+ return {
+ scoreGood: false,
+ scoreBad: false,
+ path: '',
+ isApache: window.location.href.indexOf('weex.apache.org') !== -1
+ }
+ },
+
+ watch: {
+ $route() {
+ if (!this.path) {
+ this.path = this.$route.path;
+ } else if (this.path !== this.$route.path) {
+ // console.log(this.$route.path, this.path)
+ this.scoreGood = false;
+ this.scoreBad = false;
+ }
+ }
+ },
+
+ computed: {
+ lastUpdated () {
+ if (this.$page.lastUpdated) {
+ return new Date(this.$page.lastUpdated).toLocaleDateString(this.$lang)
+ }
+ },
+
+ lastUpdatedText () {
+ if (typeof this.$themeLocaleConfig.lastUpdated === 'string') {
+ return this.$themeLocaleConfig.lastUpdated
+ }
+ if (typeof this.$site.themeConfig.lastUpdated === 'string') {
+ return this.$site.themeConfig.lastUpdated
+ }
+ return 'Last Updated'
+ },
+
+ prev () {
+ const prev = this.$page.frontmatter.prev
+ if (prev === false) {
+ return
+ } else if (prev) {
+ return resolvePage(this.$site.pages, prev, this.$route.path)
+ } else {
+ return resolvePrev(this.$page, this.sidebarItems)
+ }
+ },
+
+ next () {
+ const next = this.$page.frontmatter.next
+ if (next === false) {
+ return
+ } else if (next) {
+ return resolvePage(this.$site.pages, next, this.$route.path)
+ } else {
+ return resolveNext(this.$page, this.sidebarItems)
+ }
+ },
+
+ editLink () {
+ if (this.$page.frontmatter.editLink === false) {
+ return
+ }
+ const {
+ repo,
+ editLinks,
+ docsDir = '',
+ docsBranch = 'master',
+ docsRepo = repo
+ } = this.$site.themeConfig
+
+ let path = normalize(this.$page.path)
+ if (endingSlashRE.test(path)) {
+ path += 'README.md'
+ } else {
+ path += '.md'
+ }
+ if (docsRepo && editLinks) {
+ return this.createEditLink(repo, docsRepo, docsDir, docsBranch, path)
+ }
+ },
+
+ editLinkText () {
+ return (
+ this.$themeLocaleConfig.editLinkText ||
+ this.$site.themeConfig.editLinkText ||
+ `Edit this page`
+ )
+ },
+
+ issueLink() {
+ return function() {
+ return `https://github.com/apache/incubator-weex-site/issues/new?body=${encodeURIComponent(location.href)}`
+ }
+ },
+
+ openIssueText() {
+ return (
+ this.$themeLocaleConfig.openIssueText ||
+ this.$site.themeConfig.openIssueText ||
+ null
+ )
+ },
+
+ scoreText () {
+ return (
+ this.$themeLocaleConfig.scoreText ||
+ this.$site.themeConfig.scoreText ||
+ null
+ )
+ }
+ },
+
+ methods: {
+ createEditLink (repo, docsRepo, docsDir, docsBranch, path) {
+ const bitbucket = /bitbucket.org/
+ if (bitbucket.test(repo)) {
+ const base = outboundRE.test(docsRepo)
+ ? docsRepo
+ : repo
+ return (
+ base.replace(endingSlashRE, '') +
+ `/${docsBranch}` +
+ (docsDir ? '/' + docsDir.replace(endingSlashRE, '') : '') +
+ path +
+ `?mode=edit&spa=0&at=${docsBranch}&fileviewer=file-view-default`
+ )
+ }
+
+ const base = outboundRE.test(docsRepo)
+ ? docsRepo
+ : `https://github.com/${docsRepo}`
+
+ return (
+ base.replace(endingSlashRE, '') +
+ `/edit/${docsBranch}` +
+ (docsDir ? '/' + docsDir.replace(endingSlashRE, '') : '') +
+ path
+ )
+ },
+
+ scorePlus () {
+ if (this.scoreGood) return;
+ if (axios) {
+ axios.post(`${this.API_PREFIX}/api/like/islike`, qs.stringify({url: this.$route.path, islike: 1 })).then(() => {
+ this.scoreGood = true;
+ this.scoreBad = false;
+ }).catch(err => console.error(err))
+ }
+ },
+
+ scoreMinus () {
+ if (this.scoreBad) return;
+ if (axios) {
+ axios.post(`${this.API_PREFIX}/api/like/islike`, qs.stringify({url: this.$route.path, islike: 0 })).then(() => {
+ this.scoreGood = false;
+ this.scoreBad = true;
+ }).catch(err => console.error(err))
+ }
+ }
+ }
+}
+
+function resolvePrev (page, items) {
+ return find(page, items, -1)
+}
+
+function resolveNext (page, items) {
+ return find(page, items, 1)
+}
+
+function find (page, items, offset) {
+ const res = []
+ items.forEach(item => {
+ if (item.type === 'group') {
+ res.push(...item.children || [])
+ } else {
+ res.push(item)
+ }
+ })
+ for (let i = 0; i < res.length; i++) {
+ const cur = res[i]
+ if (cur.type === 'page' && cur.path === page.path) {
+ return res[i + offset]
+ }
+ }
+}
+</script>
+
+<style lang="stylus">
+@import './styles/config.styl'
+@require './styles/wrapper.styl'
+
+.page
+ padding-bottom 2rem
+
+.page-edit
+ @extend $wrapper
+ padding-top 1rem
+ padding-bottom 1rem
+ overflow auto
+ .edit-link
+ margin-left 1rem
+ display inline-block
+ a
+ color #606273
+ margin-right 0.25rem
+ text-decoration underline
+ font-style italic
+ font-size 14px
+ .issueText
+ margin-left 16px
+ .last-updated
+ font-size 14px
+ display inline-block
+ .prefix
+ font-weight 500
+ color #373D41
+ .time
+ font-weight 400
+ color #373D41
+ .score
+ float right
+ display flex
+ font-size 14px
+ color #8A94AC
+ .choice
+ margin-left 1rem
+ display flex
+ align-items center
+ .score-icon
+ margin-right 0.3rem
+ cursor pointer
+ .bad
+ transform rotate(180deg)
+ position relative
+ top 2px
+
+.page-nav
+ @extend $wrapper
+ padding-top 1rem
+ padding-bottom 0
+ .inner
+ min-height 2rem
+ margin-top 0
+ margin-bottom 0
+ border-bottom 1px solid $borderColor
+ overflow auto // clear float
+ .next
+ float right
+
+.license-wrap
+ margin-top 3rem
+ margin-left 11rem
+ margin-right -4rem
+
+@media (max-width: $MQMobile)
+ .license-wrap
+ margin-left 0
+ margin-right 0
+ .page-edit
+ margin-left 0
+ .page-nav
+ margin-left 0
+ .edit-link
+ margin-bottom .5rem
+ .last-updated
+ font-size .8em
+ float none
+ text-align left
+
+</style>
diff --git a/docs/.vuepress/theme/src/SWUpdatePopup.vue b/docs/.vuepress/theme/src/SWUpdatePopup.vue
new file mode 100755
index 0000000..b224db3
--- /dev/null
+++ b/docs/.vuepress/theme/src/SWUpdatePopup.vue
@@ -0,0 +1,85 @@
+<template>
+ <transition name="sw-update-popup">
+ <div
+ v-if="enabled"
+ class="sw-update-popup"
+ >
+ {{message}}<br>
+ <button @click="reload">{{buttonText}}</button>
+ </div>
+ </transition>
+</template>
+
+<script>
+export default {
+ props: {
+ updateEvent: {
+ type: Object,
+ default: null
+ }
+ },
+
+ computed: {
+ popupConfig () {
+ for (const config of [this.$themeLocaleConfig, this.$site.themeConfig]) {
+ const sw = config.serviceWorker
+ if (sw && sw.updatePopup) {
+ return typeof sw.updatePopup === 'object' ? sw.updatePopup : {}
+ }
+ }
+ return null
+ },
+
+ enabled () {
+ return Boolean(this.popupConfig && this.updateEvent)
+ },
+
+ message () {
+ const c = this.popupConfig
+ return (c && c.message) || 'New content is available.'
+ },
+
+ buttonText () {
+ const c = this.popupConfig
+ return (c && c.buttonText) || 'Refresh'
+ }
+ },
+
+ methods: {
+ reload () {
+ if (this.updateEvent) {
+ this.updateEvent.skipWaiting().then(() => {
+ location.reload(true)
+ })
+ this.updateEvent = null
+ }
+ }
+ }
+}
+</script>
+
+<style lang="stylus">
+@import './styles/config.styl'
+
+.sw-update-popup
+ position fixed
+ right 1em
+ bottom 1em
+ padding 1em
+ border 1px solid $accentColor
+ border-radius 3px
+ background #fff
+ box-shadow 0 4px 16px rgba(0, 0, 0, 0.5)
+ text-align center
+
+ button
+ margin-top 0.5em
+ padding 0.25em 2em
+
+.sw-update-popup-enter-active, .sw-update-popup-leave-active
+ transition opacity 0.3s, transform 0.3s
+
+.sw-update-popup-enter, .sw-update-popup-leave-to
+ opacity 0
+ transform translate(0, 50%) scale(0.5)
+</style>
diff --git a/docs/.vuepress/theme/src/SearchBox.vue b/docs/.vuepress/theme/src/SearchBox.vue
new file mode 100755
index 0000000..a72d036
--- /dev/null
+++ b/docs/.vuepress/theme/src/SearchBox.vue
@@ -0,0 +1,235 @@
+<template>
+ <div class="search-box">
+ <input
+ @input="query = $event.target.value"
+ aria-label="Search"
+ :value="query"
+ autocomplete="off"
+ spellcheck="false"
+ @focus="focused = true"
+ @blur="focused = false"
+ @keyup.enter="go(focusIndex)"
+ @keyup.up="onUp"
+ @keyup.down="onDown"
+ >
+ <ul
+ class="suggestions"
+ v-if="showSuggestions"
+ :class="{ 'align-right': alignRight }"
+ @mouseleave="unfocus"
+ >
+ <li
+ class="suggestion"
+ v-for="(s, i) in suggestions"
+ :class="{ focused: i === focusIndex }"
+ @mousedown="go(i)"
+ @mouseenter="focus(i)"
+ >
+ <a :href="s.path" @click.prevent>
+ <span class="page-title">{{ s.title || s.path }}</span>
+ <span v-if="s.header" class="header">> {{ s.header.title }}</span>
+ </a>
+ </li>
+ </ul>
+ </div>
+</template>
+
+<script>
+export default {
+ data () {
+ return {
+ query: '',
+ focused: false,
+ focusIndex: 0
+ }
+ },
+
+ computed: {
+ showSuggestions () {
+ return (
+ this.focused &&
+ this.suggestions &&
+ this.suggestions.length
+ )
+ },
+
+ suggestions () {
+ const query = this.query.trim().toLowerCase()
+ if (!query) {
+ return
+ }
+
+ const { pages, themeConfig } = this.$site
+ const max = themeConfig.searchMaxSuggestions || 5
+ const localePath = this.$localePath
+ const matches = item => (
+ item.title &&
+ item.title.toLowerCase().indexOf(query) > -1
+ )
+ const res = []
+ for (let i = 0; i < pages.length; i++) {
+ if (res.length >= max) break
+ const p = pages[i]
+ // filter out results that do not match current locale
+ if (this.getPageLocalePath(p) !== localePath) {
+ continue
+ }
+ if (matches(p)) {
+ res.push(p)
+ } else if (p.headers) {
+ for (let j = 0; j < p.headers.length; j++) {
+ if (res.length >= max) break
+ const h = p.headers[j]
+ if (matches(h)) {
+ res.push(Object.assign({}, p, {
+ path: p.path + '#' + h.slug,
+ header: h
+ }))
+ }
+ }
+ }
+ }
+ return res
+ },
+
+ // make suggestions align right when there are not enough items
+ alignRight () {
+ const navCount = (this.$site.themeConfig.nav || []).length
+ const repo = this.$site.repo ? 1 : 0
+ return navCount + repo <= 2
+ }
+ },
+
+ methods: {
+ getPageLocalePath (page) {
+ for (const localePath in this.$site.locales || {}) {
+ if (localePath !== '/' && page.path.indexOf(localePath) === 0) {
+ return localePath
+ }
+ }
+ return '/'
+ },
+
+ onUp () {
+ if (this.showSuggestions) {
+ if (this.focusIndex > 0) {
+ this.focusIndex--
+ } else {
+ this.focusIndex = this.suggestions.length - 1
+ }
+ }
+ },
+
+ onDown () {
+ if (this.showSuggestions) {
+ if (this.focusIndex < this.suggestions.length - 1) {
+ this.focusIndex++
+ } else {
+ this.focusIndex = 0
+ }
+ }
+ },
+
+ go (i) {
+ if (!this.showSuggestions) {
+ return
+ }
+ this.$router.push(this.suggestions[i].path)
+ this.query = ''
+ this.focusIndex = 0
+ },
+
+ focus (i) {
+ this.focusIndex = i
+ },
+
+ unfocus () {
+ this.focusIndex = -1
+ }
+ }
+}
+</script>
+
+<style lang="stylus">
+@import './styles/config.styl'
+
+.search-box
+ display inline-block
+ position relative
+ margin-left 1.5rem
+ input
+ cursor text
+ width 10rem
+ color lighten($textColor, 25%)
+ display inline-block
+ border 1px solid darken($borderColor, 10%)
+ border-radius 2rem
+ font-size 0.9rem
+ line-height 2rem
+ padding 0 0.5rem 0 2rem
+ outline none
+ transition all .2s ease
+ background #fff url(./search.svg) 0.6rem 0.5rem no-repeat
+ background-size 1rem
+ &:focus
+ cursor auto
+ border-color $accentColor
+ .suggestions
+ background #fff
+ width 20rem
+ position absolute
+ top 1.5rem
+ border 1px solid darken($borderColor, 10%)
+ border-radius 6px
+ padding 0.4rem
+ list-style-type none
+ &.align-right
+ right 0
+ .suggestion
+ line-height 1.4
+ padding 0.4rem 0.6rem
+ border-radius 4px
+ cursor pointer
+ a
+ color lighten($textColor, 35%)
+ .page-title
+ font-weight 600
+ .header
+ font-size 0.9em
+ margin-left 0.25em
+ &.focused
+ background-color #f3f4f5
+ a
+ color $accentColor
+
+@media (max-width: $MQNarrow)
+ .search-box
+ input
+ cursor pointer
+ width 0
+ border-color transparent
+ position relative
+ // left 1rem
+ &:focus
+ cursor text
+ left 0
+ width 10rem
+
+@media (max-width: $MQNarrow) and (min-width: $MQMobile)
+ .search-box
+ .suggestions
+ left 0
+
+@media (max-width: $MQMobile)
+ .search-box
+ margin-left 0
+ .suggestions
+ right 0
+
+@media (max-width: $MQMobileNarrow)
+ .search-box
+ .suggestions
+ width calc(100vw - 4rem)
+ input:focus
+ width 8rem
+</style>
diff --git a/docs/.vuepress/theme/src/Sidebar.vue b/docs/.vuepress/theme/src/Sidebar.vue
new file mode 100755
index 0000000..616e328
--- /dev/null
+++ b/docs/.vuepress/theme/src/Sidebar.vue
@@ -0,0 +1,152 @@
+<template>
+ <div class="sidebar">
+ <NavLinks/>
+ <slot name="top"/>
+ <ul class="sidebar-links" v-if="items.length" ref="sidebarList">
+ <li v-for="(item, i) in items" :key="i">
+ <SidebarGroup
+ v-if="item.type === 'group'"
+ :item="item"
+ :first="i === 0"
+ :open="i === openGroupIndex"
+ :collapsable="item.collapsable"
+ @toggle="toggleGroup(i)"
+ />
+ <SidebarLink v-else :item="item"/>
+ </li>
+ </ul>
+ <slot name="bottom"/>
+ </div>
+</template>
+
+<script>
+import SidebarGroup from './SidebarGroup.vue'
+import SidebarLink from './SidebarLink.vue'
+import NavLinks from './NavLinks.vue'
+import { isActive } from './util'
+
+export default {
+ components: { SidebarGroup, SidebarLink, NavLinks },
+
+ props: ['items'],
+
+ data () {
+ return {
+ openGroupIndex: 0
+ }
+ },
+
+ created () {
+ this.refreshIndex()
+ },
+
+ mounted() {
+ this.updateEmas();
+ },
+
+ watch: {
+ '$route' () {
+ this.refreshIndex();
+ this.updateEmas();
+ }
+ },
+
+ methods: {
+ refreshIndex () {
+ const index = resolveOpenGroupIndex(
+ this.$route,
+ this.items
+ )
+ if (index > -1) {
+ this.openGroupIndex = index
+ }
+ },
+
+ toggleGroup (index) {
+ this.openGroupIndex = index === this.openGroupIndex ? -1 : index
+ },
+
+ isActive (page) {
+ return isActive(this.$route, page.path)
+ },
+
+ updateEmas() {
+ const url = window.location.href;
+ const path = this.$route.path;
+ const isEmasURL = ((url.indexOf('emas.weex.io') !== -1) || (url.indexOf('weex.emas-poc.com') !== -1));
+ const sidebarList = this.$refs.sidebarList
+ if (isEmasURL) {
+ sidebarList && (sidebarList.style.display = 'block');
+ } else {
+ if (this.$route.path.indexOf('/zh/community/') !== -1) {
+ sidebarList && (sidebarList.style.display = 'none');
+ // 打包编译时会做存在性校验,报 Cannot read property 'lastElementChild' of undefined
+ // 在社区tab下,由英文切换到中文,sidebarList.lastElementChild 并不是 EMAS DOM 节点
+ // 所以需要统筹判断一下 sidebarList.lastElementChild 是不是 EMAS DOM 节点,如果是,则直接隐藏,如果不是,则异步隐藏
+ const isEmas = (sidebarList && sidebarList.lastElementChild.innerText.indexOf('企业级服务') !== -1)
+ if (isEmas) {
+ sidebarList && (sidebarList.lastElementChild.style.display = 'none')
+ sidebarList && (sidebarList.style.display = 'block');
+ } else {
+ setTimeout(function() {
+ sidebarList && (sidebarList.lastElementChild.style.display = 'none')
+ sidebarList && (sidebarList.style.display = 'block');
+ }, 0)
+ }
+ } else {
+ if (sidebarList && sidebarList.lastElementChild.style.display === 'none') {
+ sidebarList.lastElementChild.style.display = 'block';
+ }
+ sidebarList && (sidebarList.style.display = 'block');
+ }
+ }
+ }
+ }
+}
+
+function resolveOpenGroupIndex (route, items) {
+ for (let i = 0; i < items.length; i++) {
+ const item = items[i]
+ if (item.type === 'group' && item.children.some(c => isActive(route, c.path))) {
+ return i
+ }
+ }
+ return -1
+}
+</script>
+
+<style lang="stylus">
+@import './styles/config.styl'
+
+.sidebar
+ ul
+ padding 0
+ margin 0
+ list-style-type none
+ a
+ display inline-block
+ .nav-links
+ display none
+ border-bottom 1px solid $borderColor
+ padding 0.5rem 0 0.75rem 0
+ a
+ font-weight 600
+ .nav-item, .repo-link
+ display block
+ line-height 1.25rem
+ font-size 1.1em
+ padding 0.5rem 0 0.5rem 1.5rem
+ .sidebar-links
+ display none
+ padding 1.5rem 0
+
+@media (max-width: $MQMobile)
+ .sidebar
+ .nav-links
+ display block
+ .dropdown-wrapper .nav-dropdown .dropdown-item a.router-link-active::after
+ top calc(1rem - 2px)
+ .sidebar-links
+ display none
+ padding 1rem 0
+</style>
diff --git a/docs/.vuepress/theme/src/SidebarButton.vue b/docs/.vuepress/theme/src/SidebarButton.vue
new file mode 100755
index 0000000..0a22243
--- /dev/null
+++ b/docs/.vuepress/theme/src/SidebarButton.vue
@@ -0,0 +1,28 @@
+<template>
+ <div class="sidebar-button" @click="$emit('toggle-sidebar')">
+ <svg class="icon" xmlns="http://www.w3.org/2000/svg" aria-hidden="true" role="img" viewBox="0 0 448 512">
+ <path fill="currentColor" d="M436 124H12c-6.627 0-12-5.373-12-12V80c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12zm0 160H12c-6.627 0-12-5.373-12-12v-32c0-6.627 5.373-12 12-12h424c6.627 0 12 5.373 12 12v32c0 6.627-5.373 12-12 12z" class=""></path>
+ </svg>
+ </div>
+</template>
+
+<style lang="stylus">
+@import './styles/config.styl'
+
+.sidebar-button
+ display none
+ width 1.25rem
+ height 1.25rem
+ position absolute
+ padding 0.6rem
+ top 0.6rem
+ left 1rem
+ .icon
+ display block
+ width 1.25rem
+ height 1.25rem
+
+@media (max-width: $MQMobile)
+ .sidebar-button
+ display block
+</style>
diff --git a/docs/.vuepress/theme/src/SidebarGroup.vue b/docs/.vuepress/theme/src/SidebarGroup.vue
new file mode 100755
index 0000000..119dfa1
--- /dev/null
+++ b/docs/.vuepress/theme/src/SidebarGroup.vue
@@ -0,0 +1,77 @@
+<template>
+ <div
+ class="sidebar-group"
+ :class="{ first, collapsable }"
+ >
+ <p
+ class="sidebar-heading"
+ :class="{ open }"
+ @click="$emit('toggle')"
+ >
+ <span>{{ item.title }}</span>
+ <span
+ class="arrow"
+ v-if="collapsable"
+ :class="open ? 'down' : 'right'">
+ </span>
+ </p>
+
+ <DropdownTransition>
+ <ul
+ ref="items"
+ class="sidebar-group-items"
+ v-if="open || !collapsable"
+ >
+ <li v-for="child in item.children">
+ <SidebarLink :item="child"/>
+ </li>
+ </ul>
+ </DropdownTransition>
+ </div>
+</template>
+
+<script>
+import SidebarLink from './SidebarLink.vue'
+import DropdownTransition from './DropdownTransition.vue'
+
+export default {
+ name: 'SidebarGroup',
+ props: ['item', 'first', 'open', 'collapsable'],
+ components: { SidebarLink, DropdownTransition }
+}
+</script>
+
+<style lang="stylus">
+.sidebar-group
+ &:not(.first)
+ margin-top 1em
+ .sidebar-group
+ padding-left 0.5em
+ &:not(.collapsable)
+ .sidebar-heading
+ cursor auto
+ color inherit
+
+.sidebar-heading
+ color #999
+ transition color .15s ease
+ cursor pointer
+ font-size 1.1em
+ font-weight bold
+ // text-transform uppercase
+ padding 0 1.5rem
+ margin-top 0
+ margin-bottom 0.5rem
+ &.open, &:hover
+ color inherit
+ .arrow
+ position relative
+ top -0.12em
+ left 0.5em
+ &:.open .arrow
+ top -0.18em
+
+.sidebar-group-items
+ transition height .1s ease-out
+ overflow hidden
+</style>
diff --git a/docs/.vuepress/theme/src/SidebarLink.vue b/docs/.vuepress/theme/src/SidebarLink.vue
new file mode 100755
index 0000000..8288bf9
--- /dev/null
+++ b/docs/.vuepress/theme/src/SidebarLink.vue
@@ -0,0 +1,91 @@
+<script>
+import { isActive, hashRE, groupHeaders } from './util'
+
+export default {
+ functional: true,
+
+ props: ['item'],
+
+ render (h, { parent: { $page, $site, $route }, props: { item }}) {
+ // use custom active class matching logic
+ // due to edge case of paths ending with / + hash
+ const selfActive = isActive($route, item.path)
+ // for sidebar: auto pages, a hash link should be active if one of its child
+ // matches
+ const active = item.type === 'auto'
+ ? selfActive || item.children.some(c => isActive($route, item.basePath + '#' + c.slug))
+ : selfActive
+ const link = renderLink(h, item.path, item.title || item.path, active)
+ const configDepth = $page.frontmatter.sidebarDepth != null
+ ? $page.frontmatter.sidebarDepth
+ : $site.themeConfig.sidebarDepth
+ const maxDepth = configDepth == null ? 1 : configDepth
+ const displayAllHeaders = !!$site.themeConfig.displayAllHeaders
+ if (item.type === 'auto') {
+ return [link, renderChildren(h, item.children, item.basePath, $route, maxDepth)]
+ } else if ((active || displayAllHeaders) && item.headers && !hashRE.test(item.path)) {
+ const children = groupHeaders(item.headers)
+ return [link, renderChildren(h, children, item.path, $route, maxDepth)]
+ } else {
+ return link
+ }
+ }
+}
+
+function renderLink (h, to, text, active) {
+ return h('router-link', {
+ props: {
+ to,
+ activeClass: '',
+ exactActiveClass: ''
+ },
+ class: {
+ active,
+ 'sidebar-link': true
+ }
+ }, text)
+}
+
+function renderChildren (h, children, path, route, maxDepth, depth = 1) {
+ if (!children || depth > maxDepth) return null
+ return h('ul', { class: 'sidebar-sub-headers' }, children.map(c => {
+ const active = isActive(route, path + '#' + c.slug)
+ return h('li', { class: 'sidebar-sub-header' }, [
+ renderLink(h, path + '#' + c.slug, c.title, active),
+ renderChildren(h, c.children, path, route, maxDepth, depth + 1)
+ ])
+ }))
+}
+</script>
+
+<style lang="stylus">
+@import './styles/config.styl'
+
+.sidebar .sidebar-sub-headers
+ padding-left 1rem
+ font-size 0.95em
+
+a.sidebar-link
+ font-weight 400
+ display inline-block
+ color $textColor
+ border-left 0.25rem solid transparent
+ padding 0.35rem 1rem 0.35rem 1.25rem
+ line-height 1.4
+ width: 100%
+ box-sizing: border-box
+ &:hover
+ color $accentColor
+ &.active
+ font-weight 600
+ color $accentColor
+ border-left-color $accentColor
+ .sidebar-group &
+ padding-left 2rem
+ .sidebar-sub-headers &
+ padding-top 0.25rem
+ padding-bottom 0.25rem
+ border-left none
+ &.active
+ font-weight 500
+</style>
diff --git a/docs/.vuepress/theme/src/github.svg b/docs/.vuepress/theme/src/github.svg
new file mode 100644
index 0000000..34d5df1
--- /dev/null
+++ b/docs/.vuepress/theme/src/github.svg
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 22.0.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
+<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ viewBox="0 0 1024 1024" style="enable-background:new 0 0 1024 1024;" xml:space="preserve">
+<style type="text/css">
+ .st0{fill:#A3ABBE;}
+ .st1{fill:#FFFFFF;}
+</style>
+<g>
+ <circle class="st0" cx="506.7" cy="512.2" r="448"/>
+ <path class="st1" d="M744.9,360.2c34.2-37.7,0-113.2,0-113.2c-39.5-5.4-118.6,46.7-118.6,46.7c-97.1-23.4-226.5,0-226.5,0
+ c-60.2-50.3-123.1-51.2-123.1-51.2c-43.1,61.1-5.4,119.5-5.4,119.5c-60.2,53.5-52.1,132.1-52.1,132.1
+ C230,715.2,426.8,714.3,426.8,714.3c-21.6,8.1-42.2,62-42.2,62h-68.3c-22.5,1.8-51.2-51.2-51.2-51.2
+ c-20.7-35.1-61.6-35.7-61.6-35.7c-25.1-0.2-25.7,6.6-21.5,12.6c3.3,4.6,15.6,10.4,18.5,12c31,15.9,52,75.8,52,75.8
+ c18,44.9,66.5,46.7,66.5,46.7h80.9v110.7c34.3,8.4,70.1,12.9,106.9,12.9c38.8,0,76.4-4.9,112.3-14.2l-1.7-183.1
+ c-1.8-32.4-34.2-52.1-34.2-52.1c221.1-9,210.3-212.1,210.3-212.1C795.2,419.6,744.9,360.2,744.9,360.2z"/>
+</g>
+</svg>
diff --git a/docs/.vuepress/theme/src/search.svg b/docs/.vuepress/theme/src/search.svg
new file mode 100755
index 0000000..03d8391
--- /dev/null
+++ b/docs/.vuepress/theme/src/search.svg
@@ -0,0 +1 @@
+<?xml version="1.0" encoding="UTF-8"?><svg xmlns="http://www.w3.org/2000/svg" width="12" height="13"><g stroke-width="2" stroke="#aaa" fill="none"><path d="M11.29 11.71l-4-4"/><circle cx="5" cy="5" r="4"/></g></svg>
diff --git a/docs/.vuepress/theme/src/styles/arrow.styl b/docs/.vuepress/theme/src/styles/arrow.styl
new file mode 100755
index 0000000..20bffc0
--- /dev/null
+++ b/docs/.vuepress/theme/src/styles/arrow.styl
@@ -0,0 +1,22 @@
+@require './config'
+
+.arrow
+ display inline-block
+ width 0
+ height 0
+ &.up
+ border-left 4px solid transparent
+ border-right 4px solid transparent
+ border-bottom 6px solid $arrowBgColor
+ &.down
+ border-left 4px solid transparent
+ border-right 4px solid transparent
+ border-top 6px solid $arrowBgColor
+ &.right
+ border-top 4px solid transparent
+ border-bottom 4px solid transparent
+ border-left 6px solid $arrowBgColor
+ &.left
+ border-top 4px solid transparent
+ border-bottom 4px solid transparent
+ border-right 6px solid $arrowBgColor
diff --git a/docs/.vuepress/theme/src/styles/code.styl b/docs/.vuepress/theme/src/styles/code.styl
new file mode 100755
index 0000000..a4b53a0
--- /dev/null
+++ b/docs/.vuepress/theme/src/styles/code.styl
@@ -0,0 +1,137 @@
+@require './config'
+
+.content
+ code
+ color $codeTextColor
+ padding 0.25rem 0.5rem
+ margin 0
+ font-size 0.85em
+ background-color rgba(27,31,35,0.05)
+ border-radius 3px
+
+.content
+ pre, pre[class*="language-"]
+ line-height 1.4
+ padding 1.25rem 1.5rem
+ margin 0.85rem 0
+ background-color $codeBgColor
+ border-radius 6px
+ overflow auto
+ code
+ color #525252
+ padding 0
+ background-color transparent
+ border-radius 0
+
+div[class*="language-"]
+ position relative
+ background-color $codeBgColor
+ border-radius 6px
+ .highlight-lines
+ user-select none
+ padding-top 1.3rem
+ position absolute
+ top 0
+ left 0
+ width 100%
+ line-height 1.4
+ .highlighted
+ background-color rgba(0, 0, 0, 66%)
+ pre, pre[class*="language-"]
+ background transparent
+ position relative
+ z-index 1
+ &::before
+ position absolute
+ z-index 3
+ top 0.8em
+ right 1em
+ font-size 0.75rem
+ color rgba(255, 255, 255, 0.4)
+ &:not(.line-numbers-mode)
+ .line-numbers-wrapper
+ display none
+ &.line-numbers-mode
+ .highlight-lines .highlighted
+ position relative
+ &:before
+ content ' '
+ position absolute
+ z-index 3
+ left 0
+ top 0
+ display block
+ width $lineNumbersWrapperWidth
+ height 100%
+ background-color rgba(0, 0, 0, 66%)
+ pre
+ padding-left $lineNumbersWrapperWidth + 1 rem
+ vertical-align middle
+ .line-numbers-wrapper
+ position absolute
+ top 0
+ width $lineNumbersWrapperWidth
+ text-align center
+ color rgba(255, 255, 255, 0.3)
+ padding 1.25rem 0
+ line-height 1.4
+ br
+ user-select none
+ .line-number
+ position relative
+ z-index 4
+ user-select none
+ font-size 0.85em
+ &::after
+ content ''
+ position absolute
+ z-index 2
+ top 0
+ left 0
+ width $lineNumbersWrapperWidth
+ height 100%
+ border-radius 6px 0 0 6px
+ border-right 1px solid rgba(0, 0, 0, 66%)
+ background-color $codeBgColor
+
+
+for lang in js ts html md vue css sass scss less stylus go java c sh yaml
+ div{'[class~="language-' + lang + '"]'}
+ &:before
+ content ('' + lang)
+
+div[class~="language-javascript"]
+ &:before
+ content "js"
+
+div[class~="language-typescript"]
+ &:before
+ content "ts"
+
+div[class~="language-markup"]
+ &:before
+ content "html"
+
+div[class~="language-markdown"]
+ &:before
+ content "md"
+
+div[class~="language-json"]:before
+ content "json"
+
+div[class~="language-ruby"]:before
+ content "rb"
+
+div[class~="language-python"]:before
+ content "py"
+
+div[class~="language-bash"]:before
+ content "sh"
+
+.token
+ color #5081da
+.token.punctuation
+ color #828282
+
+div[class*="language-"] .highlight-lines .highlighted
+ background-color: rgba(220, 220, 220, 0.66);
\ No newline at end of file
diff --git a/docs/.vuepress/theme/src/styles/config.styl b/docs/.vuepress/theme/src/styles/config.styl
new file mode 100755
index 0000000..ddcd699
--- /dev/null
+++ b/docs/.vuepress/theme/src/styles/config.styl
@@ -0,0 +1,22 @@
+// colors
+$accentColor = #00B4FF
+$textColor = #2C3E50
+$borderColor = #eaecef
+$codeBgColor = #F8F9FB
+$arrowBgColor = #ccc
+$codeTextColor = #e96900
+
+// layout
+$navbarHeight = 3.6rem
+$sidebarWidth = 20rem
+$contentWidth = 53rem
+
+// responsive breakpoints
+$MQNarrow = 959px
+$MQMobile = 719px
+$MQMobileNarrow = 419px
+
+// code
+$lineNumbersWrapperWidth = 3.5rem
+
+@import '~@temp/override.styl'
diff --git a/docs/.vuepress/theme/src/styles/custom-blocks.styl b/docs/.vuepress/theme/src/styles/custom-blocks.styl
new file mode 100755
index 0000000..3ccc2df
--- /dev/null
+++ b/docs/.vuepress/theme/src/styles/custom-blocks.styl
@@ -0,0 +1,28 @@
+.custom-block
+ .custom-block-title
+ font-weight 600
+ margin-bottom -0.4rem
+ &.tip, &.warning, &.danger
+ padding .1rem 1.5rem
+ border-left-width .5rem
+ border-left-style solid
+ margin 1rem 0
+ &.tip
+ background-color #f3f5f7
+ border-color #42b983
+ &.warning
+ background-color rgba(255,229,100,.3)
+ border-color darken(#ffe564, 35%)
+ color darken(#ffe564, 70%)
+ .custom-block-title
+ color darken(#ffe564, 50%)
+ a
+ color $textColor
+ &.danger
+ background-color #ffe6e6
+ border-color darken(red, 20%)
+ color darken(red, 70%)
+ .custom-block-title
+ color darken(red, 40%)
+ a
+ color $textColor
diff --git a/docs/.vuepress/theme/src/styles/mobile.styl b/docs/.vuepress/theme/src/styles/mobile.styl
new file mode 100755
index 0000000..7758305
--- /dev/null
+++ b/docs/.vuepress/theme/src/styles/mobile.styl
@@ -0,0 +1,41 @@
+@require './config'
+
+$mobileSidebarWidth = $sidebarWidth * 0.82
+
+// narrow desktop / iPad
+@media (max-width: $MQNarrow)
+ .sidebar
+ font-size 15px
+ width $mobileSidebarWidth
+ .page
+ padding-right 2rem
+ // padding-left $mobileSidebarWidth
+
+// wide mobile
+@media (max-width: $MQMobile)
+ .sidebar
+ top 0
+ padding-top $navbarHeight
+ transform translateX(-100%)
+ transition transform .2s ease
+ .page
+ padding-left 0
+ padding-right 0
+ .content:not(.custom)
+ margin-left 0
+ .theme-container
+ &.sidebar-open
+ .sidebar
+ transform translateX(0)
+ &.no-navbar
+ .sidebar
+ padding-top: 0
+
+// narrow mobile
+@media (max-width: $MQMobileNarrow)
+ h1
+ font-size 1.9rem
+ .content
+ div[class*="language-"]
+ margin 0.85rem -1.5rem
+ border-radius 0
diff --git a/docs/.vuepress/theme/src/styles/nprogress.styl b/docs/.vuepress/theme/src/styles/nprogress.styl
new file mode 100755
index 0000000..f186a67
--- /dev/null
+++ b/docs/.vuepress/theme/src/styles/nprogress.styl
@@ -0,0 +1,48 @@
+#nprogress
+ pointer-events none
+ .bar
+ background $accentColor
+ position fixed
+ z-index 1031
+ top 0
+ left 0
+ width 100%
+ height 2px
+ .peg
+ display block
+ position absolute
+ right 0px
+ width 100px
+ height 100%
+ box-shadow 0 0 10px $accentColor, 0 0 5px $accentColor
+ opacity 1.0
+ transform rotate(3deg) translate(0px, -4px)
+ .spinner
+ display block
+ position fixed
+ z-index 1031
+ top 15px
+ right 15px
+ .spinner-icon
+ width 18px
+ height 18px
+ box-sizing border-box
+ border solid 2px transparent
+ border-top-color $accentColor
+ border-left-color $accentColor
+ border-radius 50%
+ animation nprogress-spinner 400ms linear infinite
+
+.nprogress-custom-parent
+ overflow hidden
+ position relative
+
+.nprogress-custom-parent #nprogress .spinner,
+.nprogress-custom-parent #nprogress .bar
+ position absolute
+
+@keyframes nprogress-spinner
+ 0%
+ transform rotate(0deg)
+ 100%
+ transform rotate(360deg)
diff --git a/docs/.vuepress/theme/src/styles/theme.styl b/docs/.vuepress/theme/src/styles/theme.styl
new file mode 100755
index 0000000..72748c3
--- /dev/null
+++ b/docs/.vuepress/theme/src/styles/theme.styl
@@ -0,0 +1,199 @@
+@require './config'
+@require './nprogress'
+@require './code'
+@require './custom-blocks'
+@require './arrow'
+@require './wrapper'
+@require './toc'
+
+html, body
+ padding 0
+ margin 0
+
+body
+ // font-family -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen, Ubuntu, Cantarell, "Fira Sans", "Droid Sans", "Helvetica Neue", sans-serif
+ font-family Roboto, "Helvetica Neue", Helvetica, Tahoma, Arial, FZLanTingHeiS-L-G, "PingFang SC", "Microsoft YaHei"
+ -webkit-font-smoothing antialiased
+ -moz-osx-font-smoothing grayscale
+ font-size 15px
+ color $textColor
+
+.page
+ padding-left 4rem
+ padding-right 4rem
+
+.navbar
+ position fixed
+ z-index 20
+ top 0
+ left 0
+ right 0
+ height $navbarHeight
+ background-color #fff
+ box-sizing border-box
+ box-shadow 0 3px 12px 0 rgba(0,0,0,0.10);
+
+.sidebar-mask
+ position fixed
+ z-index 9
+ top 0
+ left 0
+ width 100vw
+ height 100vh
+ display none
+
+.sidebar
+ font-size 15px
+ background-color #fff
+ width $sidebarWidth
+ position fixed
+ z-index 10
+ margin 0
+ top $navbarHeight
+ left 0
+ bottom 0
+ box-sizing border-box
+ border-right 1px solid #F2F3F4
+ overflow-y auto
+
+.content:not(.custom)
+ @extend $wrapper
+ > *:first-child
+ margin-top $navbarHeight
+ a:hover
+ text-decoration underline
+ p.demo
+ padding 1rem 1.5rem
+ border 1px solid #ddd
+ border-radius 4px
+ img
+ max-width 100%
+
+.content.custom
+ padding 0
+ margin 0
+ img
+ max-width 100%
+
+a
+ font-weight 500
+ color $accentColor
+ text-decoration none
+
+p a code
+ font-weight 400
+ color $accentColor
+
+kbd
+ background #eee
+ border solid 0.15rem #ddd
+ border-bottom solid 0.25rem #ddd
+ border-radius 0.15rem
+ padding 0 0.15em
+
+blockquote
+ font-size 1.2rem
+ color #999
+ border-left .25rem solid #dfe2e5
+ margin-left 0
+ padding-left 1rem
+
+ul, ol
+ padding-left 1.2em
+
+strong
+ font-weight 600
+
+h1, h2, h3, h4, h5, h6
+ font-weight 600
+ line-height 1.25
+ color #04152C
+ .content:not(.custom) > &
+ margin-top (0.5rem - $navbarHeight)
+ padding-top ($navbarHeight + 1rem)
+ margin-bottom 0
+ &:first-child
+ margin-top -1.5rem
+ margin-bottom 1rem
+ + p, + pre, + .custom-block
+ margin-top 2rem
+ &:hover .header-anchor
+ opacity: 1
+
+h1
+ font-size 1.5rem
+
+h2
+ padding-top 6.6rem
+ font-size 1rem
+ padding-bottom .3rem
+ border-bottom 1px solid $borderColor
+
+h3
+ font-size 0.8rem
+
+a.header-anchor
+ font-size 0.85em
+ float left
+ margin-left -0.87em
+ padding-right 0.23em
+ margin-top 0.125em
+ opacity 0
+ &:hover
+ text-decoration none
+
+code, kbd, .line-number
+ font-family source-code-pro, Menlo, Monaco, Consolas, "Courier New", monospace
+
+p, ul, ol
+ line-height 25px
+
+hr
+ border 0
+ border-top 1px solid $borderColor
+
+table
+ border-collapse collapse
+ margin 1rem 0
+ display: block
+ overflow-x: auto
+
+tr
+ border-top 1px solid #dfe2e5
+ &:nth-child(2n)
+ background-color #f6f8fa
+
+th, td
+ border 1px solid #dfe2e5
+ padding .6em 1em
+
+.custom-layout
+ padding-top $navbarHeight
+
+.theme-container
+ &.sidebar-open
+ .sidebar-mask
+ display: block
+ &.no-navbar
+ .content:not(.custom) > h1, h2, h3, h4, h5, h6
+ margin-top 1.5rem
+ padding-top 0
+ .sidebar
+ top 0
+ .custom-layout
+ padding-top 0
+
+@media screen and (max-width: 1400px)
+ .content:not(.custom)
+ margin-left 12rem
+
+
+@media (min-width: ($MQMobile + 1px))
+ .theme-container.no-sidebar
+ .sidebar
+ display none
+ .page
+ padding-left 0
+ padding-right 0
+
+@require './mobile.styl'
diff --git a/docs/.vuepress/theme/src/styles/toc.styl b/docs/.vuepress/theme/src/styles/toc.styl
new file mode 100755
index 0000000..d3e7106
--- /dev/null
+++ b/docs/.vuepress/theme/src/styles/toc.styl
@@ -0,0 +1,3 @@
+.table-of-contents
+ .badge
+ vertical-align middle
diff --git a/docs/.vuepress/theme/src/styles/wrapper.styl b/docs/.vuepress/theme/src/styles/wrapper.styl
new file mode 100755
index 0000000..cb7f6f8
--- /dev/null
+++ b/docs/.vuepress/theme/src/styles/wrapper.styl
@@ -0,0 +1,11 @@
+$wrapper
+ max-width $contentWidth
+ margin 0 auto
+ padding 2rem 2.5rem
+ @media screen and (max-width: 1400px)
+ margin-left 12rem
+ @media (max-width: $MQNarrow)
+ padding 2rem
+ @media (max-width: $MQMobileNarrow)
+ padding 1.5rem
+
diff --git a/docs/.vuepress/theme/src/util.js b/docs/.vuepress/theme/src/util.js
new file mode 100755
index 0000000..b2ade1c
--- /dev/null
+++ b/docs/.vuepress/theme/src/util.js
@@ -0,0 +1,243 @@
+export const hashRE = /#.*$/
+export const extRE = /\.(md|html)$/
+export const endingSlashRE = /\/$/
+export const outboundRE = /^(https?:|mailto:|tel:)/
+
+export function normalize (path) {
+ return decodeURI(path)
+ .replace(hashRE, '')
+ .replace(extRE, '')
+}
+
+export function getHash (path) {
+ const match = path.match(hashRE)
+ if (match) {
+ return match[0]
+ }
+}
+
+export function isExternal (path) {
+ return outboundRE.test(path)
+}
+
+export function isMailto (path) {
+ return /^mailto:/.test(path)
+}
+
+export function isTel (path) {
+ return /^tel:/.test(path)
+}
+
+export function ensureExt (path) {
+ if (isExternal(path)) {
+ return path
+ }
+ const hashMatch = path.match(hashRE)
+ const hash = hashMatch ? hashMatch[0] : ''
+ const normalized = normalize(path)
+
+ if (endingSlashRE.test(normalized)) {
+ return path
+ }
+ return normalized + '.html' + hash
+}
+
+export function isActive (route, path) {
+ const routeHash = route.hash
+ const linkHash = getHash(path)
+ if (linkHash && routeHash !== linkHash) {
+ return false
+ }
+ const routePath = normalize(route.path)
+ const pagePath = normalize(path)
+ return routePath === pagePath
+}
+
+export function resolvePage (pages, rawPath, base) {
+ if (base) {
+ rawPath = resolvePath(rawPath, base)
+ }
+ const path = normalize(rawPath)
+ for (let i = 0; i < pages.length; i++) {
+ if (normalize(pages[i].path) === path) {
+ return Object.assign({}, pages[i], {
+ type: 'page',
+ path: ensureExt(rawPath)
+ })
+ }
+ }
+ console.error(`[vuepress] No matching page found for sidebar item "${rawPath}"`)
+ return {}
+}
+
+function resolvePath (relative, base, append) {
+ const firstChar = relative.charAt(0)
+ if (firstChar === '/') {
+ return relative
+ }
+
+ if (firstChar === '?' || firstChar === '#') {
+ return base + relative
+ }
+
+ const stack = base.split('/')
+
+ // remove trailing segment if:
+ // - not appending
+ // - appending to trailing slash (last segment is empty)
+ if (!append || !stack[stack.length - 1]) {
+ stack.pop()
+ }
+
+ // resolve relative path
+ const segments = relative.replace(/^\//, '').split('/')
+ for (let i = 0; i < segments.length; i++) {
+ const segment = segments[i]
+ if (segment === '..') {
+ stack.pop()
+ } else if (segment !== '.') {
+ stack.push(segment)
+ }
+ }
+
+ // ensure leading slash
+ if (stack[0] !== '') {
+ stack.unshift('')
+ }
+
+ return stack.join('/')
+}
+
+export function resolveSidebarItems (page, route, site, localePath) {
+ const { pages, themeConfig } = site
+
+ const localeConfig =
+ localePath && themeConfig.locales
+ ? themeConfig.locales[localePath] || themeConfig
+ : themeConfig
+
+ const pageSidebarConfig =
+ page.frontmatter.sidebar || localeConfig.sidebar || themeConfig.sidebar
+ if (pageSidebarConfig === 'auto') {
+ return resolveHeaders(page)
+ }
+
+ const sidebarConfig = localeConfig.sidebar || themeConfig.sidebar
+ if (!sidebarConfig) {
+ return []
+ } else {
+ const { base, config } = resolveMatchingConfig(route, sidebarConfig)
+ return config ? config.map(item => resolveItem(item, pages, base)) : []
+ }
+}
+
+function resolveHeaders (page) {
+ const headers = groupHeaders(page.headers || [])
+ return [
+ {
+ type: 'group',
+ collapsable: false,
+ title: page.title,
+ children: headers.map(h => ({
+ type: 'auto',
+ title: h.title,
+ basePath: page.path,
+ path: page.path + '#' + h.slug,
+ children: h.children || []
+ }))
+ }
+ ]
+}
+
+export function groupHeaders (headers) {
+ // group h3s under h2
+ headers = headers.map(h => Object.assign({}, h))
+ let lastH2
+ headers.forEach(h => {
+ if (h.level === 2) {
+ lastH2 = h
+ } else if (lastH2) {
+ ;(lastH2.children || (lastH2.children = [])).push(h)
+ }
+ })
+ return headers.filter(h => h.level === 2)
+}
+
+export function resolveNavLinkItem (linkItem) {
+ return Object.assign(linkItem, {
+ type: linkItem.items && linkItem.items.length ? 'links' : 'link'
+ })
+}
+
+export function resolveMatchingConfig (route, config) {
+ if (Array.isArray(config)) {
+ return {
+ base: '/',
+ config: config
+ }
+ }
+ for (const base in config) {
+ if (ensureEndingSlash(route.path).indexOf(base) === 0) {
+ return {
+ base,
+ config: config[base]
+ }
+ }
+ }
+ return {}
+}
+
+function ensureEndingSlash (path) {
+ return /(\.html|\/)$/.test(path) ? path : path + '/'
+}
+
+function resolveItem (item, pages, base, isNested) {
+ if (typeof item === 'string') {
+ return resolvePage(pages, item, base)
+ } else if (Array.isArray(item)) {
+ return Object.assign(resolvePage(pages, item[0], base), {
+ title: item[1]
+ })
+ } else {
+ if (isNested) {
+ console.error(
+ '[vuepress] Nested sidebar groups are not supported. ' +
+ 'Consider using navbar + categories instead.'
+ )
+ }
+ const children = item.children || []
+ return {
+ type: 'group',
+ title: item.title,
+ children: children.map(child => resolveItem(child, pages, base, true)),
+ collapsable: item.collapsable !== false
+ }
+ }
+}
+
+export const setSpm = spm => {
+ if (!spm) return
+
+ const spmArr = spm.split('.')
+ if (spmArr.length != 2) {
+ console.log('setSpm传入参数不合法, 正确格式: a位.b位')
+ return
+ }
+ const spmA = spmArr[0]
+ const spmB = spmArr[1].replace(/\//g, '-')
+
+ const q = window.goldlog_queue || (window.goldlog_queue = [])
+ q.push({
+ action: 'goldlog.setPageSPM',
+ arguments: [spmA, spmB]
+ })
+ q.push({
+ action: 'goldlog.sendPV',
+ arguments: [
+ // 必填的配置项
+ {
+ is_auto: false
+ }
+ ]
+ })
+}
diff --git a/docs/.vuepress/theme/src/version-bg.svg b/docs/.vuepress/theme/src/version-bg.svg
new file mode 100644
index 0000000..8c5e0f2
--- /dev/null
+++ b/docs/.vuepress/theme/src/version-bg.svg
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg width="42px" height="15px" viewBox="0 0 42 15" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
+ <!-- Generator: Sketch 52.3 (67297) - http://www.bohemiancoding.com/sketch -->
+ <title>Group 15</title>
+ <desc>Created with Sketch.</desc>
+ <g id="weex" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
+ <g id="工具-1" transform="translate(-111.000000, -32.000000)" fill="#515151">
+ <g id="Group-5">
+ <g id="Group-16" transform="translate(110.000000, 32.000000)">
+ <g id="Group-15">
+ <rect id="Rectangle-24" x="4" y="0" width="39" height="15" rx="7.5"></rect>
+ <polygon id="Path-2" points="13.5593872 15.0015869 0.988769531 15.0015869 7.25271206 12.3242188"></polygon>
+ </g>
+ </g>
+ </g>
+ </g>
+ </g>
+</svg>
\ No newline at end of file
diff --git a/package.json b/package.json
index cb137c1..1ccc049 100644
--- a/package.json
+++ b/package.json
@@ -20,7 +20,8 @@
"homepage": "https://github.com/apache/incubator-weex-site#readme",
"devDependencies": {
"vuepress": "^0.14.11",
- "vuepress-theme-fast": "0.0.11"
+ "nprogress": "^0.2.0",
+ "workbox-build": "^3.1.0"
},
"dependencies": {
"axios": "^0.18.0"