* [html5] fix appear events when components's v-if is false, & fix appear 
events in update hooks.


Project: http://git-wip-us.apache.org/repos/asf/incubator-weex/repo
Commit: http://git-wip-us.apache.org/repos/asf/incubator-weex/commit/ca4f1997
Tree: http://git-wip-us.apache.org/repos/asf/incubator-weex/tree/ca4f1997
Diff: http://git-wip-us.apache.org/repos/asf/incubator-weex/diff/ca4f1997

Branch: refs/heads/0.12-dev
Commit: ca4f1997d2ff0677dd942b88724cd9095d06948e
Parents: b040496
Author: MrRaindrop <tekk...@gmail.com>
Authored: Thu Apr 13 17:37:11 2017 +0800
Committer: MrRaindrop <tekk...@gmail.com>
Committed: Thu Apr 13 17:37:11 2017 +0800

----------------------------------------------------------------------
 .eslintrc                                       |   3 +-
 .gitignore                                      |   1 +
 build/webpack.test.web.config.js                |   2 +-
 html5/render/vue/mixins/base.js                 |   1 +
 html5/render/vue/utils/component.js             |  83 +--
 html5/render/vue/utils/event.js                 |  32 +-
 .../vue/data/build/dotvue/scoped-style.js       | 525 -------------------
 .../vue/data/dotvue/first-screen-appear.vue     |  21 +
 html5/test/render/vue/data/dotvue/foo.vue       |  27 +
 html5/test/render/vue/helper/main.js            |  80 +++
 html5/test/render/vue/helper/mixin/done.js      |   9 +
 html5/test/render/vue/helper/mixin/index.js     |   1 +
 html5/test/render/vue/helper/runtime.js         |  40 +-
 html5/test/render/vue/utils/component.js        |  38 ++
 14 files changed, 253 insertions(+), 610 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/ca4f1997/.eslintrc
----------------------------------------------------------------------
diff --git a/.eslintrc b/.eslintrc
index f3d34fd..75c1fc2 100644
--- a/.eslintrc
+++ b/.eslintrc
@@ -33,7 +33,8 @@
     "notifyTrimMemory": false,
     "markupState": false,
     "compileAndRunBundle": false,
-    "expect": false
+    "expect": false,
+    "sinon": false
   },
 
   "rules": {

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/ca4f1997/.gitignore
----------------------------------------------------------------------
diff --git a/.gitignore b/.gitignore
index 50e9ce3..334fdb8 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,6 +9,7 @@
 examples/build
 web-entry
 test/build
+data/build
 weex_tmp
 coverage
 dist

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/ca4f1997/build/webpack.test.web.config.js
----------------------------------------------------------------------
diff --git a/build/webpack.test.web.config.js b/build/webpack.test.web.config.js
index 6b55480..7357d0f 100644
--- a/build/webpack.test.web.config.js
+++ b/build/webpack.test.web.config.js
@@ -26,7 +26,7 @@ function walk(dir) {
         // var entryFile = path.join(entryDirectory, path.basename(file, 
extname) + '.js');
         // fs.outputFileSync(entryFile, getEntryFileContent(entryFile, 
fullpath));
         var name = path.join('html5/test/render/vue/data', 'build', dir, 
path.basename(file, extname));
-        entry[name] = fullpath
+        entry[name] = [fullpath]
       } else if (stat.isDirectory() && file !== 'build' && file !== 'include') 
{
         var subdir = path.join(dir, file);
         walk(subdir);

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/ca4f1997/html5/render/vue/mixins/base.js
----------------------------------------------------------------------
diff --git a/html5/render/vue/mixins/base.js b/html5/render/vue/mixins/base.js
index c13fb91..0bfd2ad 100644
--- a/html5/render/vue/mixins/base.js
+++ b/html5/render/vue/mixins/base.js
@@ -67,6 +67,7 @@ export default {
     if (process.env.NODE_ENV === 'development') {
       tagUpdated()
     }
+    watchAppear(this)
   },
 
   methods: {

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/ca4f1997/html5/render/vue/utils/component.js
----------------------------------------------------------------------
diff --git a/html5/render/vue/utils/component.js 
b/html5/render/vue/utils/component.js
index 0bd07b8..989499e 100644
--- a/html5/render/vue/utils/component.js
+++ b/html5/render/vue/utils/component.js
@@ -33,11 +33,12 @@ export function hasIntersection (rect, ctRect) {
 }
 
 /**
- * [isElementVisible description]
+ * isElementVisible
  * @param  {HTMLElement}  el    a dom element.
  * @param  {HTMLElement}  container  optional, the container of this el.
  */
 export function isElementVisible (el, container) {
+  if (!el.getBoundingClientRect) { return false }
   const bodyRect = {
     top: 0,
     left: 0,
@@ -68,10 +69,23 @@ export function isComponentVisible (component) {
   return false
 }
 
-// TODO: improve the efficiency
-export function watchAppear (context) {
-  if (!context) return null
+// to trigger the appear/disappear event.
+function triggerEvent (elm, handlers, isShow, dir) {
+  const evt = isShow ? 'appear' : 'disappear'
+  let listener = handlers[evt]
+  if (listener && listener.fn) {
+    listener = listener.fn
+  }
+  if (listener) {
+    listener(createEvent(elm, evt, {
+      direction: dir
+    }))
+  }
+}
 
+export function watchAppear (context) {
+  if (!context || !context.$el) return null
+  const el = context.$el
   context.$nextTick(() => {
     if ((context.$options && context.$options._parentListeners)
       || (context.$vnode && context.$vnode.data && context.$vnode.data.on)) {
@@ -86,42 +100,37 @@ export function watchAppear (context) {
         else {
           isWindow = true
         }
-        let lastScrollTop = container.scrollTop || window.pageYOffset
-
-        context._visible = isElementVisible(context.$el, isWindow ? 
document.body : container)
-        if (context._visible && on.appear) {
-          if (on.appear.fn) {
-            on.appear = on.appear.fn
-          }
-          on.appear(createEvent(context.$el, 'appear', { direction: null }))
+        const visible = isElementVisible(el, isWindow ? document.body : 
container)
+        context._visible = visible
+        // if the component hasn't appeared for once yet, then it shouldn't 
trigger
+        // a disappear event at all.
+        if (context._appearedOnce) {
+          triggerEvent(el, on, visible, null)
         }
-        const handler = throttle(event => {
-          const visible = isElementVisible(context.$el, isWindow ? 
document.body : container)
-          let listener = null
-          let type = null
-          if (visible !== context._visible) {
-            context._visible = visible
-            if (visible) {
-              listener = on.appear
-              type = 'appear'
-            }
-            else {
-              listener = on.disappear
-              type = 'disappear'
-            }
-            if (listener && listener.fn) {
-              listener = listener.fn
+        else if (visible) {
+          context._appearedOnce = true
+          triggerEvent(el, on, true, null)
+        }
+
+        let lastScrollTop = container.scrollTop || window.pageYOffset
+        // no need to watch the same vComponent again.
+        if (!context._scrollWatched) {
+          context._scrollWatched = true
+          const handler = throttle(event => {
+            const visible = isElementVisible(el, isWindow ? document.body : 
container)
+            if (visible !== context._visible) {
+              context._visible = visible
+              const scrollTop = container.scrollTop || window.pageYOffset
+              const dir = scrollTop < lastScrollTop
+                ? 'down' : scrollTop > lastScrollTop
+                ? 'up' : null
+              triggerEvent(el, on, visible, dir)
+              lastScrollTop = scrollTop
             }
-            const scrollTop = container.scrollTop || window.pageYOffset
-            listener && listener(createEvent(context.$el, type, {
-              direction: scrollTop < lastScrollTop ? 'down'
-                : scrollTop > lastScrollTop ? 'up' : null
-            }))
-            lastScrollTop = scrollTop
-          }
-        }, 25, true)
+          }, 25, true)
 
-        container.addEventListener('scroll', handler, false)
+          container.addEventListener('scroll', handler, false)
+        }
       }
     }
   })

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/ca4f1997/html5/render/vue/utils/event.js
----------------------------------------------------------------------
diff --git a/html5/render/vue/utils/event.js b/html5/render/vue/utils/event.js
index f518bfc..ac84c87 100644
--- a/html5/render/vue/utils/event.js
+++ b/html5/render/vue/utils/event.js
@@ -33,17 +33,22 @@ function extend (to, ...args) {
  * @param {DOMString} type
  * @param {Object} props
  */
-export function createEvent (context, type, props) {
+export function createEvent (target, type, props) {
   const event = new Event(type, { bubbles: false })
   // event.preventDefault()
   // event.stopPropagation()
 
   extend(event, props)
 
-  Object.defineProperty(event, 'target', {
-    enumerable: true,
-    value: context || null
-  })
+  try {
+    Object.defineProperty(event, 'target', {
+      enumerable: true,
+      value: target || null
+    })
+  }
+  catch (err) {
+    return extend({}, event, { target: target || null })
+  }
 
   return event
 }
@@ -53,7 +58,7 @@ export function createEvent (context, type, props) {
  * @param {DOMString} type
  * @param {Object} props
  */
-export function createCustomEvent (context, type, props) {
+export function createCustomEvent (target, type, props) {
   // compatibility: http://caniuse.com/#search=customevent
   // const event = new CustomEvent(type)
   const event = document.createEvent('CustomEvent')
@@ -63,11 +68,16 @@ export function createCustomEvent (context, type, props) {
 
   extend(event, props)
 
-  // TODO: event.target is readonly
-  Object.defineProperty(event, 'target', {
-    enumerable: true,
-    value: context || null
-  })
+  // event.target is readonly
+  try {
+    Object.defineProperty(event, 'target', {
+      enumerable: true,
+      value: target || null
+    })
+  }
+  catch (err) {
+    return extend({}, event, { target: target || null })
+  }
 
   return event
 }

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/ca4f1997/html5/test/render/vue/data/build/dotvue/scoped-style.js
----------------------------------------------------------------------
diff --git a/html5/test/render/vue/data/build/dotvue/scoped-style.js 
b/html5/test/render/vue/data/build/dotvue/scoped-style.js
deleted file mode 100644
index b87ba8a..0000000
--- a/html5/test/render/vue/data/build/dotvue/scoped-style.js
+++ /dev/null
@@ -1,525 +0,0 @@
-(function webpackUniversalModuleDefinition(root, factory) {
-       if(typeof exports === 'object' && typeof module === 'object')
-               module.exports = factory();
-       else if(typeof define === 'function' && define.amd)
-               define([], factory);
-       else {
-               var a = factory();
-               for(var i in a) (typeof exports === 'object' ? exports : 
root)[i] = a[i];
-       }
-})(this, function() {
-return /******/ (function(modules) { // webpackBootstrap
-/******/       // The module cache
-/******/       var installedModules = {};
-
-/******/       // The require function
-/******/       function __webpack_require__(moduleId) {
-
-/******/               // Check if module is in cache
-/******/               if(installedModules[moduleId])
-/******/                       return installedModules[moduleId].exports;
-
-/******/               // Create a new module (and put it into the cache)
-/******/               var module = installedModules[moduleId] = {
-/******/                       exports: {},
-/******/                       id: moduleId,
-/******/                       loaded: false
-/******/               };
-
-/******/               // Execute the module function
-/******/               modules[moduleId].call(module.exports, module, 
module.exports, __webpack_require__);
-
-/******/               // Flag the module as loaded
-/******/               module.loaded = true;
-
-/******/               // Return the exports of the module
-/******/               return module.exports;
-/******/       }
-
-
-/******/       // expose the modules object (__webpack_modules__)
-/******/       __webpack_require__.m = modules;
-
-/******/       // expose the module cache
-/******/       __webpack_require__.c = installedModules;
-
-/******/       // __webpack_public_path__
-/******/       __webpack_require__.p = "";
-
-/******/       // Load entry module and return exports
-/******/       return __webpack_require__(0);
-/******/ })
-/************************************************************************/
-/******/ ([
-/* 0 */
-/***/ function(module, exports, __webpack_require__) {
-
-       
-       /* styles */
-       __webpack_require__(1)
-
-       var Component = __webpack_require__(6)(
-         /* script */
-         null,
-         /* template */
-         __webpack_require__(7),
-         /* scopeId */
-         "data-v-0776c87b",
-         /* cssModules */
-         null
-       )
-       Component.options.__file = 
"/Users/rdp/www/rdp/github/MrRaindrop/weex/html5/test/render/vue/data/dotvue/scoped-style.vue"
-       if (Component.esModule && Object.keys(Component.esModule).some(function 
(key) {return key !== "default" && key !== "__esModule"})) 
{console.error("named exports are not supported in *.vue files.")}
-       if (Component.options.functional) {console.error("[vue-loader] 
scoped-style.vue: functional components are not supported with templates, they 
should use render functions.")}
-
-       /* hot reload */
-       if (false) {(function () {
-         var hotAPI = require("vue-loader/node_modules/vue-hot-reload-api")
-         hotAPI.install(require("vue"), false)
-         if (!hotAPI.compatible) return
-         module.hot.accept()
-         if (!module.hot.data) {
-           hotAPI.createRecord("data-v-0776c87b", Component.options)
-         } else {
-           hotAPI.reload("data-v-0776c87b", Component.options)
-         }
-       })()}
-
-       module.exports = Component.exports
-
-
-/***/ },
-/* 1 */
-/***/ function(module, exports, __webpack_require__) {
-
-       // style-loader: Adds some css to the DOM by adding a <style> tag
-
-       // load the styles
-       var content = __webpack_require__(2);
-       if(typeof content === 'string') content = [[module.id, content, '']];
-       if(content.locals) module.exports = content.locals;
-       // add the styles to the DOM
-       var update = __webpack_require__(4)("27230129", content, false);
-       // Hot Module Replacement
-       if(false) {
-        // When the styles change, update the <style> tags
-        if(!content.locals) {
-          
module.hot.accept("!!../../../../../../node_modules/.0.26.1@css-loader/index.js!../../../../../../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-0776c87b\",\"scoped\":true,\"hasInlineConfig\":false}!../../../../../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./scoped-style.vue",
 function() {
-            var newContent = 
require("!!../../../../../../node_modules/.0.26.1@css-loader/index.js!../../../../../../node_modules/vue-loader/lib/style-compiler/index.js?{\"id\":\"data-v-0776c87b\",\"scoped\":true,\"hasInlineConfig\":false}!../../../../../../node_modules/vue-loader/lib/selector.js?type=styles&index=0!./scoped-style.vue");
-            if(typeof newContent === 'string') newContent = [[module.id, 
newContent, '']];
-            update(newContent);
-          });
-        }
-        // When the module is disposed, remove the <style> tags
-        module.hot.dispose(function() { update(); });
-       }
-
-/***/ },
-/* 2 */
-/***/ function(module, exports, __webpack_require__) {
-
-       exports = module.exports = __webpack_require__(3)();
-       // imports
-
-
-       // module
-       exports.push([module.id, "\n.ct[data-v-0776c87b] {\n  width: 200px;\n  
flex-direction: row;\n  transform: translate3d(100px, 100px, 0);\n}\n", ""]);
-
-       // exports
-
-
-/***/ },
-/* 3 */
-/***/ function(module, exports) {
-
-       /*
-               MIT License http://www.opensource.org/licenses/mit-license.php
-               Author Tobias Koppers @sokra
-       */
-       // css base code, injected by the css-loader
-       module.exports = function() {
-               var list = [];
-
-               // return the list of modules as css string
-               list.toString = function toString() {
-                       var result = [];
-                       for(var i = 0; i < this.length; i++) {
-                               var item = this[i];
-                               if(item[2]) {
-                                       result.push("@media " + item[2] + "{" + 
item[1] + "}");
-                               } else {
-                                       result.push(item[1]);
-                               }
-                       }
-                       return result.join("");
-               };
-
-               // import a list of modules into the list
-               list.i = function(modules, mediaQuery) {
-                       if(typeof modules === "string")
-                               modules = [[null, modules, ""]];
-                       var alreadyImportedModules = {};
-                       for(var i = 0; i < this.length; i++) {
-                               var id = this[i][0];
-                               if(typeof id === "number")
-                                       alreadyImportedModules[id] = true;
-                       }
-                       for(i = 0; i < modules.length; i++) {
-                               var item = modules[i];
-                               // skip already imported module
-                               // this implementation is not 100% perfect for 
weird media query combinations
-                               //  when a module is imported multiple times 
with different media queries.
-                               //  I hope this will never occur (Hey this way 
we have smaller bundles)
-                               if(typeof item[0] !== "number" || 
!alreadyImportedModules[item[0]]) {
-                                       if(mediaQuery && !item[2]) {
-                                               item[2] = mediaQuery;
-                                       } else if(mediaQuery) {
-                                               item[2] = "(" + item[2] + ") 
and (" + mediaQuery + ")";
-                                       }
-                                       list.push(item);
-                               }
-                       }
-               };
-               return list;
-       };
-
-
-/***/ },
-/* 4 */
-/***/ function(module, exports, __webpack_require__) {
-
-       /*
-         MIT License http://www.opensource.org/licenses/mit-license.php
-         Author Tobias Koppers @sokra
-         Modified by Evan You @yyx990803
-       */
-
-       var hasDocument = typeof document !== 'undefined'
-
-       if (false) {
-         if (!hasDocument) {
-           throw new Error(
-           'vue-style-loader cannot be used in a non-browser environment. ' +
-           "Use { target: 'node' } in your Webpack config to indicate a 
server-rendering environment."
-         ) }
-       }
-
-       var listToStyles = __webpack_require__(5)
-
-       /*
-       type StyleObject = {
-         id: number;
-         parts: Array<StyleObjectPart>
-       }
-
-       type StyleObjectPart = {
-         css: string;
-         media: string;
-         sourceMap: ?string
-       }
-       */
-
-       var stylesInDom = {/*
-         [id: number]: {
-           id: number,
-           refs: number,
-           parts: Array<(obj?: StyleObjectPart) => void>
-         }
-       */}
-
-       var head = hasDocument && (document.head || 
document.getElementsByTagName('head')[0])
-       var singletonElement = null
-       var singletonCounter = 0
-       var isProduction = false
-       var noop = function () {}
-
-       // Force single-tag solution on IE6-9, which has a hard limit on the # 
of <style>
-       // tags it will allow on a page
-       var isOldIE = typeof navigator !== 'undefined' && /msie 
[6-9]\b/.test(navigator.userAgent.toLowerCase())
-
-       module.exports = function (parentId, list, _isProduction) {
-         isProduction = _isProduction
-
-         var styles = listToStyles(parentId, list)
-         addStylesToDom(styles)
-
-         return function update (newList) {
-           var mayRemove = []
-           for (var i = 0; i < styles.length; i++) {
-             var item = styles[i]
-             var domStyle = stylesInDom[item.id]
-             domStyle.refs--
-             mayRemove.push(domStyle)
-           }
-           if (newList) {
-             styles = listToStyles(parentId, newList)
-             addStylesToDom(styles)
-           } else {
-             styles = []
-           }
-           for (var i = 0; i < mayRemove.length; i++) {
-             var domStyle = mayRemove[i]
-             if (domStyle.refs === 0) {
-               for (var j = 0; j < domStyle.parts.length; j++) {
-                 domStyle.parts[j]()
-               }
-               delete stylesInDom[domStyle.id]
-             }
-           }
-         }
-       }
-
-       function addStylesToDom (styles /* Array<StyleObject> */) {
-         for (var i = 0; i < styles.length; i++) {
-           var item = styles[i]
-           var domStyle = stylesInDom[item.id]
-           if (domStyle) {
-             domStyle.refs++
-             for (var j = 0; j < domStyle.parts.length; j++) {
-               domStyle.parts[j](item.parts[j])
-             }
-             for (; j < item.parts.length; j++) {
-               domStyle.parts.push(addStyle(item.parts[j]))
-             }
-             if (domStyle.parts.length > item.parts.length) {
-               domStyle.parts.length = item.parts.length
-             }
-           } else {
-             var parts = []
-             for (var j = 0; j < item.parts.length; j++) {
-               parts.push(addStyle(item.parts[j]))
-             }
-             stylesInDom[item.id] = { id: item.id, refs: 1, parts: parts }
-           }
-         }
-       }
-
-       function createStyleElement () {
-         var styleElement = document.createElement('style')
-         styleElement.type = 'text/css'
-         head.appendChild(styleElement)
-         return styleElement
-       }
-
-       function addStyle (obj /* StyleObjectPart */) {
-         var update, remove
-         var styleElement = document.querySelector('style[data-vue-ssr-id~="' 
+ obj.id + '"]')
-
-         if (styleElement) {
-           if (isProduction) {
-             // has SSR styles and in production mode.
-             // simply do nothing.
-             return noop
-           } else {
-             // has SSR styles but in dev mode.
-             // for some reason Chrome can't handle source map in 
server-rendered
-             // style tags - source maps in <style> only works if the style 
tag is
-             // created and inserted dynamically. So we remove the server 
rendered
-             // styles and inject new ones.
-             styleElement.parentNode.removeChild(styleElement)
-           }
-         }
-
-         if (isOldIE) {
-           // use singleton mode for IE9.
-           var styleIndex = singletonCounter++
-           styleElement = singletonElement || (singletonElement = 
createStyleElement())
-           update = applyToSingletonTag.bind(null, styleElement, styleIndex, 
false)
-           remove = applyToSingletonTag.bind(null, styleElement, styleIndex, 
true)
-         } else {
-           // use multi-style-tag mode in all other cases
-           styleElement = createStyleElement()
-           update = applyToTag.bind(null, styleElement)
-           remove = function () {
-             styleElement.parentNode.removeChild(styleElement)
-           }
-         }
-
-         update(obj)
-
-         return function updateStyle (newObj /* StyleObjectPart */) {
-           if (newObj) {
-             if (newObj.css === obj.css &&
-                 newObj.media === obj.media &&
-                 newObj.sourceMap === obj.sourceMap) {
-               return
-             }
-             update(obj = newObj)
-           } else {
-             remove()
-           }
-         }
-       }
-
-       var replaceText = (function () {
-         var textStore = []
-
-         return function (index, replacement) {
-           textStore[index] = replacement
-           return textStore.filter(Boolean).join('\n')
-         }
-       })()
-
-       function applyToSingletonTag (styleElement, index, remove, obj) {
-         var css = remove ? '' : obj.css
-
-         if (styleElement.styleSheet) {
-           styleElement.styleSheet.cssText = replaceText(index, css)
-         } else {
-           var cssNode = document.createTextNode(css)
-           var childNodes = styleElement.childNodes
-           if (childNodes[index]) styleElement.removeChild(childNodes[index])
-           if (childNodes.length) {
-             styleElement.insertBefore(cssNode, childNodes[index])
-           } else {
-             styleElement.appendChild(cssNode)
-           }
-         }
-       }
-
-       function applyToTag (styleElement, obj) {
-         var css = obj.css
-         var media = obj.media
-         var sourceMap = obj.sourceMap
-
-         if (media) {
-           styleElement.setAttribute('media', media)
-         }
-
-         if (sourceMap) {
-           // https://developer.chrome.com/devtools/docs/javascript-debugging
-           // this makes source maps inside style tags work properly in Chrome
-           css += '\n/*# sourceURL=' + sourceMap.sources[0] + ' */'
-           // http://stackoverflow.com/a/26603875
-           css += '\n/*# sourceMappingURL=data:application/json;base64,' + 
btoa(unescape(encodeURIComponent(JSON.stringify(sourceMap)))) + ' */'
-         }
-
-         if (styleElement.styleSheet) {
-           styleElement.styleSheet.cssText = css
-         } else {
-           while (styleElement.firstChild) {
-             styleElement.removeChild(styleElement.firstChild)
-           }
-           styleElement.appendChild(document.createTextNode(css))
-         }
-       }
-
-
-/***/ },
-/* 5 */
-/***/ function(module, exports) {
-
-       /**
-        * Translates the list format produced by css-loader into something
-        * easier to manipulate.
-        */
-       module.exports = function listToStyles (parentId, list) {
-         var styles = []
-         var newStyles = {}
-         for (var i = 0; i < list.length; i++) {
-           var item = list[i]
-           var id = item[0]
-           var css = item[1]
-           var media = item[2]
-           var sourceMap = item[3]
-           var part = {
-             id: parentId + ':' + i,
-             css: css,
-             media: media,
-             sourceMap: sourceMap
-           }
-           if (!newStyles[id]) {
-             styles.push(newStyles[id] = { id: id, parts: [part] })
-           } else {
-             newStyles[id].parts.push(part)
-           }
-         }
-         return styles
-       }
-
-
-/***/ },
-/* 6 */
-/***/ function(module, exports) {
-
-       // this module is a runtime utility for cleaner component module output 
and will
-       // be included in the final webpack user bundle
-
-       module.exports = function normalizeComponent (
-         rawScriptExports,
-         compiledTemplate,
-         scopeId,
-         cssModules
-       ) {
-         var esModule
-         var scriptExports = rawScriptExports = rawScriptExports || {}
-
-         // ES6 modules interop
-         var type = typeof rawScriptExports.default
-         if (type === 'object' || type === 'function') {
-           esModule = rawScriptExports
-           scriptExports = rawScriptExports.default
-         }
-
-         // Vue.extend constructor export interop
-         var options = typeof scriptExports === 'function'
-           ? scriptExports.options
-           : scriptExports
-
-         // render functions
-         if (compiledTemplate) {
-           options.render = compiledTemplate.render
-           options.staticRenderFns = compiledTemplate.staticRenderFns
-         }
-
-         // scopedId
-         if (scopeId) {
-           options._scopeId = scopeId
-         }
-
-         // inject cssModules
-         if (cssModules) {
-           var computed = Object.create(options.computed || null)
-           Object.keys(cssModules).forEach(function (key) {
-             var module = cssModules[key]
-             computed[key] = function () { return module }
-           })
-           options.computed = computed
-         }
-
-         return {
-           esModule: esModule,
-           exports: scriptExports,
-           options: options
-         }
-       }
-
-
-/***/ },
-/* 7 */
-/***/ function(module, exports, __webpack_require__) {
-
-       module.exports={render:function (){var _vm=this;var 
_h=_vm.$createElement;var _c=_vm._self._c||_h;
-         return _c('div', [_c('div', {
-           ref: "foo",
-           staticClass: "ct",
-           staticStyle: {
-             "height": "200px"
-           },
-           style: ({
-             backgroundColor: 'red'
-           })
-         })])
-       },staticRenderFns: []}
-       module.exports.render._withStripped = true
-       if (false) {
-         module.hot.accept()
-         if (module.hot.data) {
-            
require("vue-loader/node_modules/vue-hot-reload-api").rerender("data-v-0776c87b",
 module.exports)
-         }
-       }
-
-/***/ }
-/******/ ])
-});
-;
\ No newline at end of file

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/ca4f1997/html5/test/render/vue/data/dotvue/first-screen-appear.vue
----------------------------------------------------------------------
diff --git a/html5/test/render/vue/data/dotvue/first-screen-appear.vue 
b/html5/test/render/vue/data/dotvue/first-screen-appear.vue
new file mode 100644
index 0000000..c2fe877
--- /dev/null
+++ b/html5/test/render/vue/data/dotvue/first-screen-appear.vue
@@ -0,0 +1,21 @@
+<template>
+  <div>
+    <foo @appear="appear" @disappear="disappear"></foo>
+  </div>
+</template>
+
+<script>
+  module.exports = {
+    components: {
+      foo: require('./foo.vue')
+    },
+    methods: {
+      appear: function (evt) {
+        window._spy_first_screen_appear(evt)
+      },
+      disappear: function (evt) {
+        window._spy_first_screen_appear(evt)
+      }
+    }
+  }
+</script>

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/ca4f1997/html5/test/render/vue/data/dotvue/foo.vue
----------------------------------------------------------------------
diff --git a/html5/test/render/vue/data/dotvue/foo.vue 
b/html5/test/render/vue/data/dotvue/foo.vue
new file mode 100644
index 0000000..57d8591
--- /dev/null
+++ b/html5/test/render/vue/data/dotvue/foo.vue
@@ -0,0 +1,27 @@
+<template>
+  <div v-if="show" 
style="width:200px;height:200px;background-color:red;"></div>
+</template>
+
+<script>
+module.exports = {
+  data () {
+    return {
+      show: false
+    }
+  },
+  mounted () {
+    setTimeout(() => {
+      this.show = true
+      setTimeout(() => {
+        this.show = false
+        setTimeout(() => {
+          this.show = true
+          setTimeout(() => {
+            this.done('test-first-screen-appear')
+          }, 25)
+        }, 300)
+      }, 300)
+    }, 300)
+  }
+}
+</script>

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/ca4f1997/html5/test/render/vue/helper/main.js
----------------------------------------------------------------------
diff --git a/html5/test/render/vue/helper/main.js 
b/html5/test/render/vue/helper/main.js
new file mode 100644
index 0000000..962e078
--- /dev/null
+++ b/html5/test/render/vue/helper/main.js
@@ -0,0 +1,80 @@
+import weex from '../../../../render/vue/env/weex'
+import * as utils from './utils'
+
+const helper = {
+  roots: {},
+  _done: {},
+
+  utils,
+  /**
+   * register a component.
+   * @param  {string} name,
+   * @param  {object} component.
+   */
+  register (name, component) {
+    weex.registerComponent(name, component)
+    // components[name] = component
+  },
+
+  /**
+   * create a vm instance of Vue.
+   * @param  {Object} options.
+   * @return {Vue} vue instance.
+   */
+  createVm (options = {}, id) {
+    // options.components = components
+    let ct, root
+    const Vue = weex.__vue__
+    if (id) {
+      ct = document.createElement('div')
+      ct.id = `${id}-root`
+      ct.style.cssText = 'width:100%;height:300px;overflow:scroll;'
+      root = document.createElement('div')
+      root.id = id
+      ct.appendChild(root)
+      document.body.appendChild(ct)
+      this.roots[id] = root
+    }
+    return new Vue(options).$mount(root)
+  },
+
+  clearAll () {
+    const roots = this.roots
+    Object.keys(roots).forEach((id) => {
+      const ct = roots[id].parentNode
+      document.body.removeChild(ct)
+    })
+    this.roots = {}
+  },
+
+  clear (id) {
+    if (!id) {
+      return this.clearAll()
+    }
+    const roots = this.roots
+    const root = roots[id]
+    if (!root) { return }
+    const ct = roots[id].parentNode
+    document.body.removeChild(ct)
+  },
+
+  registerDone (id, cb) {
+    this._done[id] = cb
+  },
+
+  done (id, ...args) {
+    const done = this._done[id]
+    done && done(...args)
+  },
+
+  /**
+   * [compile description]
+   * @param  {[type]} template [description]
+   * @return {[type]}          [description]
+   */
+  compile (template) {
+    return helper.createVm({ template })
+  }
+}
+
+export default helper

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/ca4f1997/html5/test/render/vue/helper/mixin/done.js
----------------------------------------------------------------------
diff --git a/html5/test/render/vue/helper/mixin/done.js 
b/html5/test/render/vue/helper/mixin/done.js
new file mode 100644
index 0000000..d5a2b76
--- /dev/null
+++ b/html5/test/render/vue/helper/mixin/done.js
@@ -0,0 +1,9 @@
+import helper from '../main'
+
+export const doneMixin = {
+  methods: {
+    done (id, ...args) {
+      helper.done(id, ...args)
+    }
+  }
+}

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/ca4f1997/html5/test/render/vue/helper/mixin/index.js
----------------------------------------------------------------------
diff --git a/html5/test/render/vue/helper/mixin/index.js 
b/html5/test/render/vue/helper/mixin/index.js
new file mode 100644
index 0000000..9c34780
--- /dev/null
+++ b/html5/test/render/vue/helper/mixin/index.js
@@ -0,0 +1 @@
+export * from './done'

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/ca4f1997/html5/test/render/vue/helper/runtime.js
----------------------------------------------------------------------
diff --git a/html5/test/render/vue/helper/runtime.js 
b/html5/test/render/vue/helper/runtime.js
index 522b132..09f3f01 100644
--- a/html5/test/render/vue/helper/runtime.js
+++ b/html5/test/render/vue/helper/runtime.js
@@ -6,8 +6,8 @@ import Vue from 'vue/dist/vue.runtime.esm.js'
 import { base, style } from '../../../../render/vue/mixins'
 import weex from '../../../../render/vue/env/weex'
 import { setVue } from '../../../../render/vue/env'
-
-import * as utils from './utils'
+import helper from './main'
+import { doneMixin } from './mixin'
 
 /**
  * Describe tests for current versions of Vue.
@@ -24,6 +24,9 @@ export function init (title, fn) {
       Vue.mixin(base)
       Vue.mixin(style)
 
+      // for test only mixins.
+      Vue.mixin(doneMixin)
+
       window.global = window
       global.weex = weex
       setVue(Vue)
@@ -31,39 +34,6 @@ export function init (title, fn) {
       window._no_remove_style_sheets = true
     })
 
-    const helper = {
-
-      utils,
-      /**
-       * register a component.
-       * @param  {string} name,
-       * @param  {object} component.
-       */
-      register (name, component) {
-        global.weex.registerComponent(name, component)
-        // components[name] = component
-      },
-
-      /**
-       * create a vm instance of Vue.
-       * @param  {Object} options.
-       * @return {Vue} vue instance.
-       */
-      createVm (options = {}) {
-        // options.components = components
-        return new Vue(options).$mount()
-      },
-
-      /**
-       * [compile description]
-       * @param  {[type]} template [description]
-       * @return {[type]}          [description]
-       */
-      compile (template) {
-        return helper.createVm({ template })
-      }
-    }
-
     /**
      * describe a vue-render test for certain vue verson.
      */

http://git-wip-us.apache.org/repos/asf/incubator-weex/blob/ca4f1997/html5/test/render/vue/utils/component.js
----------------------------------------------------------------------
diff --git a/html5/test/render/vue/utils/component.js 
b/html5/test/render/vue/utils/component.js
new file mode 100644
index 0000000..06d7c70
--- /dev/null
+++ b/html5/test/render/vue/utils/component.js
@@ -0,0 +1,38 @@
+import { init } from '../helper/runtime'
+import div from '../../../../render/vue/components/div'
+
+import firstScreenAppearBundle from 
'../data/build/dotvue/first-screen-appear.js'
+
+init('utils component', (Vue, helper) => {
+  const spys = {
+    'appear': sinon.spy(),
+    'disappear': sinon.spy()
+  }
+  window._spy_first_screen_appear = function (evt) {
+    const spy = spys[evt.type]
+    spy && spy(evt)
+  }
+
+  before(() => {
+    helper.register('div', div)
+  })
+
+  describe('watchAppear', function () {
+    it('should work when mounted and updated.', function (done) {
+      const id = 'test-first-screen-appear'
+      helper.createVm(firstScreenAppearBundle, id)
+      helper.registerDone(id, () => {
+        const { appear: appearSpy, disappear: disappearSpy } = spys
+        expect(appearSpy.callCount).to.equal(2)
+        expect(disappearSpy.callCount).to.equal(1)
+        expect(appearSpy.args[0][0].type).to.equal('appear')
+        expect(appearSpy.args[1][0].type).to.equal('appear')
+        expect(disappearSpy.args[0][0].type).to.equal('disappear')
+        expect(appearSpy.args[0][0].direction).to.not.exist
+        expect(appearSpy.args[1][0].direction).to.not.exist
+        expect(disappearSpy.args[0][0].direction).to.not.exist
+        done()
+      })
+    })
+  })
+})

Reply via email to