`
9 | return div.innerHTML.indexOf(encoded) > 0
10 | }
11 |
12 | // #3663
13 | // IE encodes newlines inside attribute values while other browsers don't
14 | export const shouldDecodeNewlines = inBrowser ? shouldDecode('\n', '
') : false
15 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/src/platforms/web/util/index.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import { warn } from 'core/util/index'
4 |
5 | export * from './attrs'
6 | export * from './class'
7 | export * from './element'
8 |
9 | /**
10 | * Query an element selector if it's not an element already.
11 | */
12 | export function query (el: string | Element): Element {
13 | if (typeof el === 'string') {
14 | const selected = document.querySelector(el)
15 | if (!selected) {
16 | process.env.NODE_ENV !== 'production' && warn(
17 | 'Cannot find element: ' + el
18 | )
19 | return document.createElement('div')
20 | }
21 | return selected
22 | } else {
23 | return el
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/src/platforms/weex/compiler/directives/index.js:
--------------------------------------------------------------------------------
1 | import model from './model'
2 |
3 | export default {
4 | model
5 | }
6 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/src/platforms/weex/compiler/directives/model.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import { addHandler, addAttr } from 'compiler/helpers'
4 | import { genComponentModel, genAssignmentCode } from 'compiler/directives/model'
5 |
6 | export default function model (
7 | el: ASTElement,
8 | dir: ASTDirective,
9 | _warn: Function
10 | ): ?boolean {
11 | if (el.tag === 'input' || el.tag === 'textarea') {
12 | genDefaultModel(el, dir.value, dir.modifiers)
13 | } else {
14 | genComponentModel(el, dir.value, dir.modifiers)
15 | }
16 | }
17 |
18 | function genDefaultModel (
19 | el: ASTElement,
20 | value: string,
21 | modifiers: ?ASTModifiers
22 | ): ?boolean {
23 | const { lazy, trim, number } = modifiers || {}
24 | const event = lazy ? 'change' : 'input'
25 |
26 | let valueExpression = `$event.target.attr.value${trim ? '.trim()' : ''}`
27 | if (number) {
28 | valueExpression = `_n(${valueExpression})`
29 | }
30 |
31 | const code = genAssignmentCode(value, valueExpression)
32 | addAttr(el, 'value', `(${value})`)
33 | addHandler(el, event, code, null, true)
34 | }
35 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/src/platforms/weex/compiler/index.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import { genStaticKeys } from 'shared/util'
4 | import { createCompiler } from 'compiler/index'
5 |
6 | import modules from './modules/index'
7 | import directives from './directives/index'
8 |
9 | import {
10 | isUnaryTag,
11 | mustUseProp,
12 | isReservedTag,
13 | canBeLeftOpenTag,
14 | getTagNamespace
15 | } from '../util/index'
16 |
17 | export const baseOptions: CompilerOptions = {
18 | modules,
19 | directives,
20 | isUnaryTag,
21 | mustUseProp,
22 | canBeLeftOpenTag,
23 | isReservedTag,
24 | getTagNamespace,
25 | preserveWhitespace: false,
26 | staticKeys: genStaticKeys(modules)
27 | }
28 |
29 | const { compile, compileToFunctions } = createCompiler(baseOptions)
30 | export { compile, compileToFunctions }
31 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/src/platforms/weex/compiler/modules/append.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | function preTransformNode (el: ASTElement, options: CompilerOptions) {
4 | if (el.tag === 'cell' && !el.attrsList.some(item => item.name === 'append')) {
5 | el.attrsMap.append = 'tree'
6 | el.attrsList.push({ name: 'append', value: 'tree' })
7 | }
8 | if (el.attrsMap.append === 'tree') {
9 | el.appendAsTree = true
10 | }
11 | }
12 |
13 | function genData (el: ASTElement): string {
14 | return el.appendAsTree ? `appendAsTree:true,` : ''
15 | }
16 |
17 | export default {
18 | staticKeys: ['appendAsTree'],
19 | preTransformNode,
20 | genData
21 | }
22 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/src/platforms/weex/compiler/modules/index.js:
--------------------------------------------------------------------------------
1 | import klass from './class'
2 | import style from './style'
3 | import props from './props'
4 | import append from './append'
5 |
6 | export default [
7 | klass,
8 | style,
9 | props,
10 | append
11 | ]
12 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/src/platforms/weex/compiler/modules/props.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import { cached, camelize } from 'shared/util'
4 |
5 | const normalize = cached(camelize)
6 |
7 | function normalizeKeyName (str: string) : string {
8 | if (str.match(/^v\-/)) {
9 | return str.replace(/(v-[a-z\-]+\:)([a-z\-]+)$/i, ($, directive, prop) => {
10 | return directive + normalize(prop)
11 | })
12 | }
13 | return normalize(str)
14 | }
15 |
16 | function transformNode (el: ASTElement, options: CompilerOptions) {
17 | if (Array.isArray(el.attrsList)) {
18 | el.attrsList.forEach(attr => {
19 | if (attr.name && attr.name.match(/\-/)) {
20 | const realName = normalizeKeyName(attr.name)
21 | if (el.attrsMap) {
22 | el.attrsMap[realName] = el.attrsMap[attr.name]
23 | delete el.attrsMap[attr.name]
24 | }
25 | attr.name = realName
26 | }
27 | })
28 | }
29 | }
30 | export default {
31 | transformNode
32 | }
33 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/src/platforms/weex/runtime/components/index.js:
--------------------------------------------------------------------------------
1 | import Transition from './transition'
2 | import TransitionGroup from './transition-group'
3 |
4 | export default {
5 | Transition,
6 | TransitionGroup
7 | }
8 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/src/platforms/weex/runtime/components/transition.js:
--------------------------------------------------------------------------------
1 | // reuse same transition component logic from web
2 | export {
3 | transitionProps,
4 | extractTransitionData
5 | } from 'web/runtime/components/transition'
6 |
7 | import Transition from 'web/runtime/components/transition'
8 |
9 | export default Transition
10 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/src/platforms/weex/runtime/directives/index.js:
--------------------------------------------------------------------------------
1 | export default {
2 | }
3 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/src/platforms/weex/runtime/index.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import Vue from 'core/index'
4 | import { patch } from 'weex/runtime/patch'
5 | import { mountComponent } from 'core/instance/lifecycle'
6 | import platformDirectives from 'weex/runtime/directives/index'
7 | import platformComponents from 'weex/runtime/components/index'
8 |
9 | import {
10 | query,
11 | mustUseProp,
12 | isReservedTag,
13 | isUnknownElement
14 | } from 'weex/util/index'
15 |
16 | // install platform specific utils
17 | Vue.config.mustUseProp = mustUseProp
18 | Vue.config.isReservedTag = isReservedTag
19 | Vue.config.isUnknownElement = isUnknownElement
20 |
21 | // install platform runtime directives and components
22 | Vue.options.directives = platformDirectives
23 | Vue.options.components = platformComponents
24 |
25 | // install platform patch function
26 | Vue.prototype.__patch__ = patch
27 |
28 | // wrap mount
29 | Vue.prototype.$mount = function (
30 | el?: any,
31 | hydrating?: boolean
32 | ): Component {
33 | return mountComponent(
34 | this,
35 | el && query(el, this.$document),
36 | hydrating
37 | )
38 | }
39 |
40 | export default Vue
41 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/src/platforms/weex/runtime/modules/attrs.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import { extend } from 'shared/util'
4 |
5 | function updateAttrs (oldVnode: VNodeWithData, vnode: VNodeWithData) {
6 | if (!oldVnode.data.attrs && !vnode.data.attrs) {
7 | return
8 | }
9 | let key, cur, old
10 | const elm = vnode.elm
11 | const oldAttrs = oldVnode.data.attrs || {}
12 | let attrs = vnode.data.attrs || {}
13 | // clone observed objects, as the user probably wants to mutate it
14 | if (attrs.__ob__) {
15 | attrs = vnode.data.attrs = extend({}, attrs)
16 | }
17 |
18 | for (key in attrs) {
19 | cur = attrs[key]
20 | old = oldAttrs[key]
21 | if (old !== cur) {
22 | elm.setAttr(key, cur)
23 | }
24 | }
25 | for (key in oldAttrs) {
26 | if (attrs[key] == null) {
27 | elm.setAttr(key)
28 | }
29 | }
30 | }
31 |
32 | export default {
33 | create: updateAttrs,
34 | update: updateAttrs
35 | }
36 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/src/platforms/weex/runtime/modules/index.js:
--------------------------------------------------------------------------------
1 | import attrs from './attrs'
2 | import klass from './class'
3 | import events from './events'
4 | import style from './style'
5 | import transition from './transition'
6 |
7 | export default [
8 | attrs,
9 | klass,
10 | events,
11 | style,
12 | transition
13 | ]
14 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/src/platforms/weex/runtime/patch.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | import * as nodeOps from 'weex/runtime/node-ops'
4 | import { createPatchFunction } from 'core/vdom/patch'
5 | import baseModules from 'core/vdom/modules/index'
6 | import platformModules from 'weex/runtime/modules/index'
7 |
8 | // the directive module should be applied last, after all
9 | // built-in modules have been applied.
10 | const modules = platformModules.concat(baseModules)
11 |
12 | export const patch: Function = createPatchFunction({
13 | nodeOps,
14 | modules,
15 | LONG_LIST_THRESHOLD: 10
16 | })
17 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/src/platforms/weex/runtime/text-node.js:
--------------------------------------------------------------------------------
1 | let latestNodeId = 1
2 |
3 | export default function TextNode (text) {
4 | this.instanceId = ''
5 | this.nodeId = latestNodeId++
6 | this.parentNode = null
7 | this.nodeType = 3
8 | this.text = text
9 | }
10 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/src/server/template-renderer/parse-template.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | export type ParsedTemplate = {
4 | head: string;
5 | neck: string;
6 | tail: string;
7 | };
8 |
9 | export function parseTemplate (
10 | template: string,
11 | contentPlaceholder?: string = ''
12 | ): ParsedTemplate {
13 | if (typeof template === 'object') {
14 | return template
15 | }
16 |
17 | let i = template.indexOf('')
18 | const j = template.indexOf(contentPlaceholder)
19 |
20 | if (j < 0) {
21 | throw new Error(`Content placeholder not found in template.`)
22 | }
23 |
24 | if (i < 0) {
25 | i = template.indexOf('')
26 | if (i < 0) {
27 | i = j
28 | }
29 | }
30 |
31 | return {
32 | head: template.slice(0, i),
33 | neck: template.slice(i, j),
34 | tail: template.slice(j + contentPlaceholder.length)
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/src/server/write.js:
--------------------------------------------------------------------------------
1 | /* @flow */
2 |
3 | const MAX_STACK_DEPTH = 1000
4 |
5 | export function createWriteFunction (
6 | write: (text: string, next: Function) => boolean,
7 | onError: Function
8 | ): Function {
9 | let stackDepth = 0
10 | const cachedWrite = (text, next) => {
11 | if (text && cachedWrite.caching) {
12 | cachedWrite.cacheBuffer[cachedWrite.cacheBuffer.length - 1] += text
13 | }
14 | const waitForNext = write(text, next)
15 | if (waitForNext !== true) {
16 | if (stackDepth >= MAX_STACK_DEPTH) {
17 | process.nextTick(() => {
18 | try { next() } catch (e) {
19 | onError(e)
20 | }
21 | })
22 | } else {
23 | stackDepth++
24 | next()
25 | stackDepth--
26 | }
27 | }
28 | }
29 | cachedWrite.caching = false
30 | cachedWrite.cacheBuffer = []
31 | return cachedWrite
32 | }
33 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/src/shared/constants.js:
--------------------------------------------------------------------------------
1 | export const SSR_ATTR = 'data-server-rendered'
2 |
3 | export const ASSET_TYPES = [
4 | 'component',
5 | 'directive',
6 | 'filter'
7 | ]
8 |
9 | export const LIFECYCLE_HOOKS = [
10 | 'beforeCreate',
11 | 'created',
12 | 'beforeMount',
13 | 'mounted',
14 | 'beforeUpdate',
15 | 'updated',
16 | 'beforeDestroy',
17 | 'destroyed',
18 | 'activated',
19 | 'deactivated'
20 | ]
21 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/e2e/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "rules": {
3 | "indent": 0
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/e2e/runner.js:
--------------------------------------------------------------------------------
1 | var path = require('path')
2 | var spawn = require('cross-spawn')
3 | var httpServer = require('http-server')
4 | var server = httpServer.createServer({
5 | root: path.resolve(__dirname, '../../')
6 | })
7 |
8 | server.listen(8080)
9 |
10 | var args = process.argv.slice(2)
11 | if (args.indexOf('--config') === -1) {
12 | args = args.concat(['--config', 'build/nightwatch.config.js'])
13 | }
14 | if (args.indexOf('--env') === -1) {
15 | args = args.concat(['--env', 'chrome,phantomjs'])
16 | }
17 | var i = args.indexOf('--test')
18 | if (i > -1) {
19 | args[i + 1] = 'test/e2e/specs/' + args[i + 1] + '.js'
20 | }
21 |
22 | var runner = spawn('./node_modules/.bin/nightwatch', args, {
23 | stdio: 'inherit'
24 | })
25 |
26 | runner.on('exit', function (code) {
27 | server.close()
28 | process.exit(code)
29 | })
30 |
31 | runner.on('error', function (err) {
32 | server.close()
33 | throw err
34 | })
35 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/e2e/specs/commits.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'commits': function (browser) {
3 | browser
4 | .url('http://localhost:8080/examples/commits/')
5 | .waitForElementVisible('li', 5000)
6 | .assert.count('input', 2)
7 | .assert.count('label', 2)
8 | .assert.containsText('label[for="master"]', 'master')
9 | .assert.containsText('label[for="dev"]', 'dev')
10 | .assert.checked('#master')
11 | .assert.checked('#dev', false)
12 | .assert.containsText('p', 'vuejs/vue@master')
13 | .assert.count('li', 3)
14 | .assert.count('li .commit', 3)
15 | .assert.count('li .message', 3)
16 | .click('#dev')
17 | .assert.containsText('p', 'vuejs/vue@dev')
18 | .assert.count('li', 3)
19 | .assert.count('li .commit', 3)
20 | .assert.count('li .message', 3)
21 | .end()
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/e2e/specs/markdown.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'markdown': function (browser) {
3 | browser
4 | .url('http://localhost:8080/examples/markdown/')
5 | .waitForElementVisible('#editor', 1000)
6 | .assert.value('textarea', '# hello')
7 | .assert.hasHTML('#editor div', '
hello
')
8 | .setValue('textarea', '\n## foo\n\n- bar\n- baz')
9 | // assert the output is not updated yet because of debounce
10 | .assert.hasHTML('#editor div', '
hello
')
11 | .waitFor(500)
12 | .assert.hasHTML('#editor div',
13 | '
hello
\n' +
14 | '
foo
\n' +
15 | '
'
16 | )
17 | .end()
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/e2e/specs/modal.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | 'modal': function (browser) {
3 | browser
4 | .url('http://localhost:8080/examples/modal/')
5 | .waitForElementVisible('#app', 1000)
6 | .assert.elementNotPresent('.modal-mask')
7 | .click('#show-modal')
8 | .assert.elementPresent('.modal-mask')
9 | .assert.elementPresent('.modal-wrapper')
10 | .assert.elementPresent('.modal-container')
11 | .waitFor(50)
12 | .assert.cssClassPresent('.modal-mask', 'modal-enter-active')
13 | .waitFor(300)
14 | .assert.cssClassNotPresent('.modal-mask', 'modal-enter-active')
15 | .assert.containsText('.modal-header h3', 'custom header')
16 | .assert.containsText('.modal-body', 'default body')
17 | .assert.containsText('.modal-footer', 'default footer')
18 | .click('.modal-default-button')
19 | // should have transition
20 | .assert.elementPresent('.modal-mask')
21 | .waitFor(50)
22 | .assert.cssClassPresent('.modal-mask', 'modal-leave-active')
23 | .waitFor(300)
24 | .assert.elementNotPresent('.modal-mask')
25 | .end()
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/helpers/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "jasmine": true
4 | },
5 | "globals": {
6 | "waitForUpdate": true
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/helpers/classlist.js:
--------------------------------------------------------------------------------
1 | beforeEach(() => {
2 | jasmine.addMatchers({
3 | // since classList may not be supported in all browsers
4 | toHaveClass: () => {
5 | return {
6 | compare: (el, cls) => {
7 | const pass = el.classList
8 | ? el.classList.contains(cls)
9 | : el.getAttribute('class').split(/\s+/g).indexOf(cls) > -1
10 | return {
11 | pass,
12 | message: `Expected element${pass ? ' ' : ' not '}to have class ${cls}`
13 | }
14 | }
15 | }
16 | }
17 | })
18 | })
19 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/helpers/to-equal.js:
--------------------------------------------------------------------------------
1 | import { isEqual } from 'lodash'
2 |
3 | beforeEach(() => {
4 | jasmine.addMatchers({
5 | // override built-in toEqual because it behaves incorrectly
6 | // on Vue-observed arrays in Safari
7 | toEqual: () => {
8 | return {
9 | compare: (a, b) => {
10 | const pass = isEqual(a, b)
11 | return {
12 | pass,
13 | message: `Expected ${a} to equal ${b}`
14 | }
15 | }
16 | }
17 | }
18 | })
19 | })
20 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/helpers/trigger-event.js:
--------------------------------------------------------------------------------
1 | window.triggerEvent = function triggerEvent (target, event, process) {
2 | var e = document.createEvent('HTMLEvents')
3 | e.initEvent(event, true, true)
4 | if (process) process(e)
5 | target.dispatchEvent(e)
6 | }
7 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/helpers/vdom.js:
--------------------------------------------------------------------------------
1 | import VNode from 'core/vdom/vnode'
2 |
3 | window.createTextVNode = function (text) {
4 | return new VNode(undefined, undefined, undefined, text)
5 | }
6 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/ssr/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "jasmine": true
4 | },
5 | "plugins": ["jasmine"],
6 | "rules": {
7 | "jasmine/no-focused-tests": 2
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/ssr/compile-with-webpack.js:
--------------------------------------------------------------------------------
1 | import path from 'path'
2 | import webpack from 'webpack'
3 | import MemoeryFS from 'memory-fs'
4 |
5 | export function compileWithWebpack (file, extraConfig, cb) {
6 | const config = Object.assign({
7 | entry: path.resolve(__dirname, 'fixtures', file),
8 | module: {
9 | rules: [
10 | {
11 | test: /\.js$/,
12 | loader: 'babel-loader'
13 | },
14 | {
15 | test: /\.(png|woff2)$/,
16 | loader: 'file-loader',
17 | options: {
18 | name: '[name].[ext]'
19 | }
20 | }
21 | ]
22 | }
23 | }, extraConfig)
24 |
25 | const compiler = webpack(config)
26 | const fs = new MemoeryFS()
27 | compiler.outputFileSystem = fs
28 |
29 | compiler.run((err, stats) => {
30 | expect(err).toBeFalsy()
31 | expect(stats.errors).toBeFalsy()
32 | cb(fs)
33 | })
34 | }
35 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/ssr/fixtures/app.js:
--------------------------------------------------------------------------------
1 | import Vue from '../../../dist/vue.runtime.common.js'
2 |
3 | export default context => {
4 | return new Promise(resolve => {
5 | context.msg = 'hello'
6 | resolve(new Vue({
7 | render (h) {
8 | return h('div', context.url)
9 | }
10 | }))
11 | })
12 | }
13 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/ssr/fixtures/async-bar.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | render (h) {
3 | return h('div', 'async bar')
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/ssr/fixtures/async-foo.js:
--------------------------------------------------------------------------------
1 | // import image and font
2 | import font from './test.woff2'
3 | import image from './test.png'
4 |
5 | module.exports = {
6 | render (h) {
7 | return h('div', `async ${font} ${image}`)
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/ssr/fixtures/cache.js:
--------------------------------------------------------------------------------
1 | import Vue from '../../../dist/vue.runtime.common.js'
2 |
3 | const app = {
4 | name: 'app',
5 | props: ['id'],
6 | serverCacheKey: props => props.id,
7 | render (h) {
8 | return h('div', '/test')
9 | }
10 | }
11 |
12 | export default () => {
13 | return Promise.resolve(new Vue({
14 | render: h => h(app, { props: { id: 1 }})
15 | }))
16 | }
17 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/ssr/fixtures/error.js:
--------------------------------------------------------------------------------
1 | throw new Error('foo')
2 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/ssr/fixtures/split.js:
--------------------------------------------------------------------------------
1 | import Vue from '../../../dist/vue.runtime.common.js'
2 |
3 | // async component!
4 | const Foo = () => import('./async-foo')
5 | const Bar = () => import('./async-bar') // eslint-disable-line
6 |
7 | export default context => {
8 | return new Promise(resolve => {
9 | context.msg = 'hello'
10 | const vm = new Vue({
11 | render (h) {
12 | return h('div', [
13 | context.url,
14 | h(Foo)
15 | ])
16 | }
17 | })
18 |
19 | // simulate router.onReady
20 | Foo().then(comp => {
21 | // resolve now to make the render sync
22 | Foo.resolved = Vue.extend(comp)
23 | resolve(vm)
24 | })
25 | })
26 | }
27 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/ssr/fixtures/test.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/huang303513/SourceCodeResearchAndExploration/b63ca9be30b25ec4caa546b991f87d069d9e3753/Vue2.2.6源码阅读201711/vue/test/ssr/fixtures/test.png
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/ssr/fixtures/test.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/huang303513/SourceCodeResearchAndExploration/b63ca9be30b25ec4caa546b991f87d069d9e3753/Vue2.2.6源码阅读201711/vue/test/ssr/fixtures/test.woff2
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/ssr/jasmine.json:
--------------------------------------------------------------------------------
1 | {
2 | "spec_dir": "test/ssr",
3 | "spec_files": [
4 | "*.spec.js"
5 | ],
6 | "helpers": [
7 | "../../node_modules/babel-register/lib/node.js"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/unit/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "jasmine": true
4 | },
5 | "globals": {
6 | "waitForUpdate": true,
7 | "triggerEvent": true,
8 | "createTextVNode": true
9 | },
10 | "plugins": ["jasmine"],
11 | "rules": {
12 | "jasmine/no-focused-tests": 2
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/unit/features/directives/cloak.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | describe('Directive v-cloak', () => {
4 | it('should be removed after compile', () => {
5 | const el = document.createElement('div')
6 | el.setAttribute('v-cloak', '')
7 | const vm = new Vue({ el })
8 | expect(vm.$el.hasAttribute('v-cloak')).toBe(false)
9 | })
10 | })
11 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/unit/features/directives/model-dynamic.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | describe('Directive v-model dynamic input type', () => {
4 | it('should warn', function () {
5 | new Vue({
6 | data: {
7 | type: 'text',
8 | text: 'hi'
9 | },
10 | template: `
`
11 | }).$mount()
12 | expect(`v-model does not support dynamic input types`).toHaveBeenWarned()
13 | })
14 | })
15 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/unit/features/directives/model-file.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | describe('Directive v-model file', () => {
4 | it('warn to use @change instead', () => {
5 | new Vue({
6 | data: {
7 | file: ''
8 | },
9 | template: '
'
10 | }).$mount()
11 | expect('Use a v-on:change listener instead').toHaveBeenWarned()
12 | })
13 | })
14 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/unit/features/directives/model-parse.spec.js:
--------------------------------------------------------------------------------
1 | import { parseModel } from 'compiler/directives/model'
2 |
3 | describe('model expression parser', () => {
4 | it('parse object dot notation', () => {
5 | const res = parseModel('a.b.c')
6 | expect(res.exp).toBe('a.b.c')
7 | expect(res.idx).toBe(null)
8 | })
9 |
10 | it('parse string in brackets', () => {
11 | const res = parseModel('a["b"][c]')
12 | expect(res.exp).toBe('a["b"]')
13 | expect(res.idx).toBe('c')
14 | })
15 |
16 | it('parse brackets with object dot notation', () => {
17 | const res = parseModel('a["b"][c].xxx')
18 | expect(res.exp).toBe('a["b"][c].xxx')
19 | expect(res.idx).toBe(null)
20 | })
21 |
22 | it('parse nested brackets', () => {
23 | const res = parseModel('a[i[c]]')
24 | expect(res.exp).toBe('a')
25 | expect(res.idx).toBe('i[c]')
26 | })
27 |
28 | it('combined', () => {
29 | const res = parseModel('test.xxx.a["asa"][test1[idx]]')
30 | expect(res.exp).toBe('test.xxx.a["asa"]')
31 | expect(res.idx).toBe('test1[idx]')
32 | })
33 | })
34 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/unit/features/directives/pre.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | describe('Directive v-pre', function () {
4 | it('should not compile inner content', function () {
5 | const vm = new Vue({
6 | template: `
7 |
{{ a }}
8 |
{{ a }}
9 |
10 |
11 |
12 |
`,
13 | data: {
14 | a: 123
15 | }
16 | })
17 | vm.$mount()
18 | expect(vm.$el.firstChild.textContent).toBe('{{ a }}')
19 | expect(vm.$el.children[1].textContent).toBe('123')
20 | expect(vm.$el.lastChild.innerHTML).toBe('
')
21 | })
22 |
23 | it('should not compile on root node', function () {
24 | const vm = new Vue({
25 | template: '
{{ a }}
',
26 | replace: true,
27 | data: {
28 | a: 123
29 | }
30 | })
31 | vm.$mount()
32 | expect(vm.$el.firstChild.textContent).toBe('{{ a }}')
33 | })
34 | })
35 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/unit/features/global-api/compile.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | describe('Global API: compile', () => {
4 | it('should compile render functions', () => {
5 | const res = Vue.compile('
{{ msg }}
')
6 | const vm = new Vue({
7 | data: {
8 | msg: 'hello'
9 | },
10 | render: res.render,
11 | staticRenderFns: res.staticRenderFns
12 | }).$mount()
13 | expect(vm.$el.innerHTML).toContain('
hello')
14 | })
15 | })
16 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/unit/features/global-api/use.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | describe('Global API: use', () => {
4 | const def = {}
5 | const options = {}
6 | const pluginStub = {
7 | install: (Vue, opts) => {
8 | Vue.directive('plugin-test', def)
9 | expect(opts).toBe(options)
10 | }
11 | }
12 |
13 | it('should apply Object plugin', () => {
14 | Vue.use(pluginStub, options)
15 | expect(Vue.options.directives['plugin-test']).toBe(def)
16 | delete Vue.options.directives['plugin-test']
17 | })
18 |
19 | it('should apply Function plugin', () => {
20 | Vue.use(pluginStub.install, options)
21 | expect(Vue.options.directives['plugin-test']).toBe(def)
22 | delete Vue.options.directives['plugin-test']
23 | })
24 | })
25 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/unit/features/instance/init.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | describe('Initialization', () => {
4 | it('without new', () => {
5 | try { Vue() } catch (e) {}
6 | expect('Vue is a constructor and should be called with the `new` keyword').toHaveBeenWarned()
7 | })
8 |
9 | it('with new', () => {
10 | expect(new Vue() instanceof Vue).toBe(true)
11 | })
12 | })
13 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/unit/features/instance/render-proxy.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | if (typeof Proxy !== 'undefined') {
4 | describe('render proxy', () => {
5 | it('should warn missing property in render fns with `with`', () => {
6 | new Vue({
7 | template: `
{{ a }}
`
8 | }).$mount()
9 | expect(`Property or method "a" is not defined`).toHaveBeenWarned()
10 | })
11 |
12 | it('should warn missing property in render fns without `with`', () => {
13 | const render = function (h) {
14 | return h('div', [this.a])
15 | }
16 | render._withStripped = true
17 | new Vue({
18 | render
19 | }).$mount()
20 | expect(`Property or method "a" is not defined`).toHaveBeenWarned()
21 | })
22 |
23 | it('should not warn for hand-written render functions', () => {
24 | new Vue({
25 | render (h) {
26 | return h('div', [this.a])
27 | }
28 | }).$mount()
29 | expect(`Property or method "a" is not defined`).not.toHaveBeenWarned()
30 | })
31 | })
32 | }
33 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/unit/features/options/extends.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | describe('Options extends', () => {
4 | it('should work on objects', () => {
5 | const A = {
6 | data () {
7 | return { a: 1 }
8 | }
9 | }
10 | const B = {
11 | extends: A,
12 | data () {
13 | return { b: 2 }
14 | }
15 | }
16 | const vm = new Vue({
17 | extends: B,
18 | data: {
19 | c: 3
20 | }
21 | })
22 | expect(vm.a).toBe(1)
23 | expect(vm.b).toBe(2)
24 | expect(vm.c).toBe(3)
25 | })
26 |
27 | it('should work on extended constructors', () => {
28 | const A = Vue.extend({
29 | data () {
30 | return { a: 1 }
31 | }
32 | })
33 | const B = Vue.extend({
34 | extends: A,
35 | data () {
36 | return { b: 2 }
37 | }
38 | })
39 | const vm = new Vue({
40 | extends: B,
41 | data: {
42 | c: 3
43 | }
44 | })
45 | expect(vm.a).toBe(1)
46 | expect(vm.b).toBe(2)
47 | expect(vm.c).toBe(3)
48 | })
49 | })
50 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/unit/features/options/methods.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | describe('Options methods', () => {
4 | it('should have correct context', () => {
5 | const vm = new Vue({
6 | data: {
7 | a: 1
8 | },
9 | methods: {
10 | plus () {
11 | this.a++
12 | }
13 | }
14 | })
15 | vm.plus()
16 | expect(vm.a).toBe(2)
17 | })
18 |
19 | it('should warn undefined methods', () => {
20 | new Vue({
21 | methods: {
22 | hello: undefined
23 | }
24 | })
25 | expect(`method "hello" has an undefined value in the component definition`).toHaveBeenWarned()
26 | })
27 | })
28 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/unit/features/options/parent.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | describe('Options parent', () => {
4 | it('should work', () => {
5 | const parent = new Vue({
6 | render () {}
7 | }).$mount()
8 |
9 | const child = new Vue({
10 | parent: parent,
11 | render () {}
12 | }).$mount()
13 |
14 | // this option is straight-forward
15 | // it should register 'parent' as a $parent for 'child'
16 | // and push 'child' to $children array on 'parent'
17 | expect(child.$options.parent).toBeDefined()
18 | expect(child.$options.parent).toEqual(parent)
19 | expect(child.$parent).toBeDefined()
20 | expect(child.$parent).toEqual(parent)
21 | expect(parent.$children).toContain(child)
22 |
23 | // destroy 'child' and check if it was removed from 'parent' $children
24 | child.$destroy()
25 | expect(parent.$children.length).toEqual(0)
26 | parent.$destroy()
27 | })
28 | })
29 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/unit/features/options/propsData.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | describe('Options propsData', () => {
4 | it('should work', done => {
5 | const A = Vue.extend({
6 | props: ['a'],
7 | template: '
{{ a }}
'
8 | })
9 | const vm = new A({
10 | propsData: {
11 | a: 123
12 | }
13 | }).$mount()
14 | expect(vm.a).toBe(123)
15 | expect(vm.$el.textContent).toBe('123')
16 | vm.a = 234
17 | waitForUpdate(() => {
18 | expect(vm.$el.textContent).toBe('234')
19 | }).then(done)
20 | })
21 |
22 | it('warn non instantiation usage', () => {
23 | Vue.extend({
24 | propsData: {
25 | a: 123
26 | }
27 | })
28 | expect('option "propsData" can only be used during instance creation').toHaveBeenWarned()
29 | })
30 | })
31 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/unit/features/options/render.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'entries/web-runtime'
2 |
3 | describe('Options render', () => {
4 | it('basic usage', () => {
5 | const vm = new Vue({
6 | render (h) {
7 | const children = []
8 | for (let i = 0; i < this.items.length; i++) {
9 | children.push(h('li', { staticClass: 'task' }, [this.items[i].name]))
10 | }
11 | return h('ul', { staticClass: 'tasks' }, children)
12 | },
13 | data: {
14 | items: [{ id: 1, name: 'task1' }, { id: 2, name: 'task2' }]
15 | }
16 | }).$mount()
17 | expect(vm.$el.tagName).toBe('UL')
18 | for (let i = 0; i < vm.$el.children.length; i++) {
19 | const li = vm.$el.children[i]
20 | expect(li.tagName).toBe('LI')
21 | expect(li.textContent).toBe(vm.items[i].name)
22 | }
23 | })
24 |
25 | it('allow null data', () => {
26 | const vm = new Vue({
27 | render (h) {
28 | return h('div', null, 'hello' /* string as children*/)
29 | }
30 | }).$mount()
31 | expect(vm.$el.tagName).toBe('DIV')
32 | expect(vm.$el.textContent).toBe('hello')
33 | })
34 |
35 | it('should warn non `render` option and non `template` option', () => {
36 | new Vue().$mount()
37 | expect('Failed to mount component: template or render function not defined.').toHaveBeenWarned()
38 | })
39 | })
40 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/unit/features/options/renderError.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 |
3 | describe('Options renderError', () => {
4 | it('should be used on render errors', done => {
5 | Vue.config.errorHandler = () => {}
6 | const vm = new Vue({
7 | data: {
8 | ok: true
9 | },
10 | render (h) {
11 | if (this.ok) {
12 | return h('div', 'ok')
13 | } else {
14 | throw new Error('no')
15 | }
16 | },
17 | renderError (h, err) {
18 | return h('div', err.toString())
19 | }
20 | }).$mount()
21 | expect(vm.$el.textContent).toBe('ok')
22 | vm.ok = false
23 | waitForUpdate(() => {
24 | expect(vm.$el.textContent).toBe('Error: no')
25 | Vue.config.errorHandler = null
26 | }).then(done)
27 | })
28 | })
29 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/unit/index.js:
--------------------------------------------------------------------------------
1 | require('es6-promise/auto')
2 |
3 | // import all helpers
4 | const helpersContext = require.context('../helpers', true)
5 | helpersContext.keys().forEach(helpersContext)
6 |
7 | // require all test files
8 | const testsContext = require.context('./', true, /\.spec$/)
9 | testsContext.keys().forEach(testsContext)
10 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/unit/modules/util/next-tick.spec.js:
--------------------------------------------------------------------------------
1 | import { nextTick } from 'core/util/env'
2 |
3 | describe('nextTick', () => {
4 | it('accepts a callback', done => {
5 | nextTick(done)
6 | })
7 |
8 | it('returns undefined when passed a callback', () => {
9 | expect(nextTick(() => {})).toBeUndefined()
10 | })
11 |
12 | if (typeof Promise !== 'undefined') {
13 | it('returns a Promise when provided no callback', done => {
14 | nextTick().then(done)
15 | })
16 |
17 | it('returns a Promise with a context argument when provided a falsy callback and an object', done => {
18 | const obj = {}
19 | nextTick(undefined, obj).then(ctx => {
20 | expect(ctx).toBe(obj)
21 | done()
22 | })
23 | })
24 |
25 | it('returned Promise should resolve correctly vs callback', done => {
26 | const spy = jasmine.createSpy()
27 | nextTick(spy)
28 | nextTick().then(() => {
29 | expect(spy).toHaveBeenCalled()
30 | done()
31 | })
32 | })
33 | }
34 | })
35 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/unit/modules/vdom/modules/directive.spec.js:
--------------------------------------------------------------------------------
1 | import Vue from 'vue'
2 | import { patch } from 'web/runtime/patch'
3 | import VNode from 'core/vdom/vnode'
4 |
5 | describe('vdom directive module', () => {
6 | it('should work', () => {
7 | const directive1 = {
8 | bind: jasmine.createSpy('bind'),
9 | update: jasmine.createSpy('update'),
10 | unbind: jasmine.createSpy('unbind')
11 | }
12 | const vm = new Vue({ directives: { directive1 }})
13 | // create
14 | const vnode1 = new VNode('div', {}, [
15 | new VNode('p', {
16 | directives: [{
17 | name: 'directive1', value: 'hello', arg: 'arg1', modifiers: { modifire1: true }
18 | }]
19 | }, undefined, 'hello world', undefined, vm)
20 | ])
21 | patch(null, vnode1)
22 | expect(directive1.bind).toHaveBeenCalled()
23 | // update
24 | const vnode2 = new VNode('div', {}, [
25 | new VNode('p', {
26 | directives: [{
27 | name: 'directive1', value: 'world', arg: 'arg1', modifiers: { modifire1: true }
28 | }]
29 | }, undefined, 'hello world', undefined, vm)
30 | ])
31 | patch(vnode1, vnode2)
32 | expect(directive1.update).toHaveBeenCalled()
33 | // destroy
34 | const vnode3 = new VNode('div')
35 | patch(vnode2, vnode3)
36 | expect(directive1.unbind).toHaveBeenCalled()
37 | })
38 | })
39 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/weex/.eslintrc:
--------------------------------------------------------------------------------
1 | {
2 | "env": {
3 | "jasmine": true
4 | },
5 | "plugins": ["jasmine"],
6 | "rules": {
7 | "jasmine/no-focused-tests": 2
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/weex/compiler/append.spec.js:
--------------------------------------------------------------------------------
1 | import { compile } from '../../../packages/weex-template-compiler'
2 | import { strToRegExp } from '../helpers/index'
3 |
4 | describe('append props', () => {
5 | it('append="tree"', () => {
6 | const { render, staticRenderFns, errors } = compile(`
|
`)
7 | expect(render).not.toBeUndefined()
8 | expect(staticRenderFns).not.toBeUndefined()
9 | expect(staticRenderFns.length).toEqual(1)
10 | expect(staticRenderFns).toMatch(strToRegExp(`appendAsTree:true`))
11 | expect(staticRenderFns).toMatch(strToRegExp(`attrs:{"append":"tree"}`))
12 | expect(errors).toEqual([])
13 | })
14 | })
15 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/weex/compiler/props.spec.js:
--------------------------------------------------------------------------------
1 | import { compile } from '../../../packages/weex-template-compiler'
2 | import { strToRegExp } from '../helpers/index'
3 |
4 | describe('compile props', () => {
5 | it('custom props', () => {
6 | const { render, staticRenderFns, errors } = compile(`
`)
7 | expect(render).not.toBeUndefined()
8 | expect(staticRenderFns).not.toBeUndefined()
9 | expect(staticRenderFns.length).toEqual(0)
10 | expect(render).toMatch(strToRegExp(`attrs:{"custom":"whatever"}`))
11 | expect(errors).toEqual([])
12 | })
13 |
14 | it('camelize props', () => {
15 | const { render, staticRenderFns, errors } = compile(`
`)
16 | expect(render).not.toBeUndefined()
17 | expect(staticRenderFns).not.toBeUndefined()
18 | expect(staticRenderFns.length).toEqual(0)
19 | expect(render).toMatch(strToRegExp(`attrs:{"kebabCase":"whatever"}`))
20 | expect(errors).toEqual([])
21 | })
22 | })
23 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/test/weex/jasmine.json:
--------------------------------------------------------------------------------
1 | {
2 | "spec_dir": "test/weex",
3 | "spec_files": [
4 | "**/*[sS]pec.js"
5 | ],
6 | "helpers": [
7 | "../../node_modules/babel-register/lib/node.js"
8 | ]
9 | }
10 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/types/plugin.d.ts:
--------------------------------------------------------------------------------
1 | import { Vue as _Vue } from "./vue";
2 |
3 | export type PluginFunction
= (Vue: typeof _Vue, options?: T) => void;
4 |
5 | export interface PluginObject {
6 | install: PluginFunction;
7 | [key: string]: any;
8 | }
9 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/types/test/augmentation-test.ts:
--------------------------------------------------------------------------------
1 | import Vue = require("../index");
2 |
3 | declare module "../vue" {
4 | // add instance property and method
5 | interface Vue {
6 | $instanceProperty: string;
7 | $instanceMethod(): void;
8 | }
9 |
10 | // add static property and method
11 | namespace Vue {
12 | const staticProperty: string;
13 | function staticMethod(): void;
14 | }
15 | }
16 |
17 | // augment ComponentOptions
18 | declare module "../options" {
19 | interface ComponentOptions {
20 | foo?: string;
21 | }
22 | }
23 |
24 | const vm = new Vue({
25 | data: {
26 | a: true
27 | },
28 | foo: "foo"
29 | });
30 |
31 | vm.$instanceProperty;
32 | vm.$instanceMethod();
33 |
34 | Vue.staticProperty;
35 | Vue.staticMethod();
36 |
--------------------------------------------------------------------------------
/Vue2.2.6源码阅读201711/vue/types/test/plugin-test.ts:
--------------------------------------------------------------------------------
1 | import Vue = require("../index");
2 | import { PluginFunction, PluginObject } from "../index";
3 |
4 | class Option {
5 | prefix: string;
6 | suffix: string;
7 | }
8 |
9 | const plugin: PluginObject