├── .eslintrc ├── src ├── compiler │ ├── index.js │ └── resolve-slots.js ├── directives │ ├── element │ │ ├── index.js │ │ ├── partial.js │ │ └── slot.js │ ├── public │ │ ├── cloak.js │ │ ├── text.js │ │ ├── ref.js │ │ ├── el.js │ │ ├── index.js │ │ ├── show.js │ │ ├── model │ │ │ ├── radio.js │ │ │ ├── checkbox.js │ │ │ ├── index.js │ │ │ └── select.js │ │ ├── html.js │ │ ├── if.js │ │ └── on.js │ ├── internal │ │ ├── index.js │ │ ├── transition.js │ │ ├── prop.js │ │ ├── class.js │ │ └── style.js │ └── priorities.js ├── util │ ├── index.js │ ├── debug.js │ ├── component.js │ └── env.js ├── transition │ ├── queue.js │ └── index.js ├── index.js ├── instance │ ├── vue.js │ ├── api │ │ └── lifecycle.js │ └── internal │ │ ├── misc.js │ │ └── init.js ├── observer │ ├── dep.js │ └── array.js ├── fragment │ └── factory.js ├── config.js ├── batcher.js ├── cache.js ├── filters │ ├── index.js │ └── array-filters.js └── parsers │ └── directive.js ├── .editorconfig ├── circle.yml ├── .gitignore ├── examples ├── todomvc │ ├── package.json │ ├── js │ │ ├── store.js │ │ ├── routes.js │ │ └── app.js │ ├── readme.md │ ├── perf.js │ └── index.html ├── svg │ ├── style.css │ ├── index.html │ └── svg.js ├── firebase │ ├── style.css │ ├── index.html │ └── app.js ├── markdown │ ├── style.css │ └── index.html ├── grid │ ├── grid.js │ ├── style.css │ └── index.html ├── elastic-header │ ├── style.css │ └── index.html ├── modal │ ├── modal.css │ └── index.html ├── commits │ ├── index.html │ └── app.js ├── tree │ ├── index.html │ └── tree.js └── select2 │ └── index.html ├── dist └── README.md ├── test ├── .eslintrc ├── unit │ ├── specs │ │ ├── util │ │ │ ├── env_spec.js │ │ │ ├── debug_spec.js │ │ │ ├── component_spec.js │ │ │ └── dom_spec.js │ │ ├── directives │ │ │ ├── public │ │ │ │ ├── cloak_spec.js │ │ │ │ ├── pre_spec.js │ │ │ │ ├── text_spec.js │ │ │ │ ├── el_spec.js │ │ │ │ ├── html_spec.js │ │ │ │ ├── show_spec.js │ │ │ │ ├── for │ │ │ │ │ └── for_ref_spec.js │ │ │ │ ├── bind_spec.js │ │ │ │ └── ref_spec.js │ │ │ ├── internal │ │ │ │ ├── class_spec.js │ │ │ │ ├── transition_spec.js │ │ │ │ └── style_spec.js │ │ │ └── element │ │ │ │ └── partial_spec.js │ │ ├── observer │ │ │ └── dep_spec.js │ │ ├── instance │ │ │ ├── init_spec.js │ │ │ └── misc_spec.js │ │ ├── cache_spec.js │ │ ├── index.js │ │ ├── batcher_spec.js │ │ ├── parsers │ │ │ ├── directive_spec.js │ │ │ └── text_spec.js │ │ └── global_api_spec.js │ ├── index.html │ └── lib │ │ └── MIT.LICENSE └── e2e │ ├── modal.js │ ├── markdown.js │ ├── commits.js │ ├── svg.js │ ├── tree.js │ └── grid.js ├── bower.json ├── LICENSE ├── CODE_OF_CONDUCT.md ├── backers.md ├── issue_template.md ├── package.json ├── README.md └── CONTRIBUTING.md /.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "vue", 3 | "rules": { 4 | "no-duplicate-imports": 0 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /src/compiler/index.js: -------------------------------------------------------------------------------- 1 | export * from './compile' 2 | export * from './transclude' 3 | export * from './resolve-slots' 4 | -------------------------------------------------------------------------------- /src/directives/element/index.js: -------------------------------------------------------------------------------- 1 | import slot from './slot' 2 | import partial from './partial' 3 | 4 | export default { 5 | slot, 6 | partial 7 | } 8 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | root = true 2 | 3 | [*] 4 | charset = utf-8 5 | indent_style = space 6 | indent_size = 2 7 | end_of_line = lf 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | -------------------------------------------------------------------------------- /circle.yml: -------------------------------------------------------------------------------- 1 | machine: 2 | node: 3 | version: 5 4 | 5 | general: 6 | branches: 7 | only: 8 | - master 9 | - next 10 | 11 | test: 12 | override: 13 | - bash ./build/ci.sh 14 | -------------------------------------------------------------------------------- /src/directives/public/cloak.js: -------------------------------------------------------------------------------- 1 | export default { 2 | bind () { 3 | var el = this.el 4 | this.vm.$once('pre-hook:compiled', function () { 5 | el.removeAttribute('v-cloak') 6 | }) 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | dist/vue.js.map 2 | dist/vue.min.js.gz 3 | test/unit/specs.js 4 | test/unit/specs.js.map 5 | explorations 6 | node_modules 7 | .DS_Store 8 | .idea 9 | benchmarks/browser.js 10 | coverage 11 | perf 12 | -------------------------------------------------------------------------------- /examples/todomvc/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "private": true, 3 | "dependencies": { 4 | "director": "^1.2.0", 5 | "vue": "^1.0.0", 6 | "todomvc-common": "^1.0.1", 7 | "todomvc-app-css": "^2.0.0" 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /src/util/index.js: -------------------------------------------------------------------------------- 1 | export * from './lang' 2 | export * from './env' 3 | export * from './dom' 4 | export * from './options' 5 | export * from './component' 6 | export * from './debug' 7 | export { defineReactive } from '../observer/index' 8 | -------------------------------------------------------------------------------- /dist/README.md: -------------------------------------------------------------------------------- 1 | # NOTE! 2 | 3 | The `dist` folder contains the standalone build for Vue.js, however files here are only checked-in when a release happens. If you are on the `dev` branch, files here are **NOT** up to date. Only the `master` branch contains the built files for the latest stable version. -------------------------------------------------------------------------------- /src/directives/public/text.js: -------------------------------------------------------------------------------- 1 | import { _toString } from '../../util/index' 2 | 3 | export default { 4 | 5 | bind () { 6 | this.attr = this.el.nodeType === 3 7 | ? 'data' 8 | : 'textContent' 9 | }, 10 | 11 | update (value) { 12 | this.el[this.attr] = _toString(value) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /src/directives/internal/index.js: -------------------------------------------------------------------------------- 1 | import style from './style' 2 | import vClass from './class' 3 | import component from './component' 4 | import prop from './prop' 5 | import transition from './transition' 6 | 7 | export default { 8 | style, 9 | 'class': vClass, 10 | component, 11 | prop, 12 | transition 13 | } 14 | -------------------------------------------------------------------------------- /src/directives/priorities.js: -------------------------------------------------------------------------------- 1 | export const ON = 700 2 | export const MODEL = 800 3 | export const BIND = 850 4 | export const TRANSITION = 1100 5 | export const EL = 1500 6 | export const COMPONENT = 1500 7 | export const PARTIAL = 1750 8 | export const IF = 2100 9 | export const FOR = 2200 10 | export const SLOT = 2300 11 | -------------------------------------------------------------------------------- /src/directives/public/ref.js: -------------------------------------------------------------------------------- 1 | import { warn } from '../../util/index' 2 | 3 | export default { 4 | bind () { 5 | process.env.NODE_ENV !== 'production' && warn( 6 | 'v-ref:' + this.arg + ' must be used on a child ' + 7 | 'component. Found on <' + this.el.tagName.toLowerCase() + '>.', 8 | this.vm 9 | ) 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /test/.eslintrc: -------------------------------------------------------------------------------- 1 | { 2 | "parserOptions": { 3 | "ecmaVersion": 5 4 | }, 5 | "env": { 6 | "jasmine": true 7 | }, 8 | "globals": { 9 | "$": true, 10 | "jQuery": true, 11 | "casper": true, 12 | "getWarnCount": true 13 | }, 14 | "rules": { 15 | "no-multi-str": 0, 16 | "object-curly-spacing": 0, 17 | "array-bracket-spacing": 0 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/todomvc/js/store.js: -------------------------------------------------------------------------------- 1 | /*jshint unused:false */ 2 | 3 | (function (exports) { 4 | 5 | 'use strict'; 6 | 7 | var STORAGE_KEY = 'todos-vuejs'; 8 | 9 | exports.todoStorage = { 10 | fetch: function () { 11 | return JSON.parse(localStorage.getItem(STORAGE_KEY) || '[]'); 12 | }, 13 | save: function (todos) { 14 | localStorage.setItem(STORAGE_KEY, JSON.stringify(todos)); 15 | } 16 | }; 17 | 18 | })(window); 19 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "vue", 3 | "main": "dist/vue.js", 4 | "description": "Simple, Fast & Composable MVVM for building interative interfaces", 5 | "authors": ["Evan You "], 6 | "license": "MIT", 7 | "ignore": [ 8 | ".*", 9 | "examples", 10 | "build", 11 | "perf", 12 | "test", 13 | "grunt", 14 | "gruntfile.js", 15 | "*.json", 16 | "*.md", 17 | "*.yml" 18 | ] 19 | } 20 | -------------------------------------------------------------------------------- /test/unit/specs/util/env_spec.js: -------------------------------------------------------------------------------- 1 | var _ = require('src/util') 2 | 3 | describe('Util - Environment', function () { 4 | describe('nextTick', function () { 5 | it('should accept context', function (done) { 6 | var ctx = {} 7 | _.nextTick(function () { 8 | this.id = 1 9 | }, ctx) 10 | _.nextTick(function () { 11 | expect(ctx.id).toBe(1) 12 | done() 13 | }) 14 | }) 15 | }) 16 | }) 17 | -------------------------------------------------------------------------------- /test/unit/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vue.js unit tests 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /examples/todomvc/js/routes.js: -------------------------------------------------------------------------------- 1 | /*global app, Router */ 2 | 3 | (function (app, Router) { 4 | 5 | 'use strict'; 6 | 7 | var router = new Router(); 8 | 9 | ['all', 'active', 'completed'].forEach(function (visibility) { 10 | router.on(visibility, function () { 11 | app.visibility = visibility; 12 | }); 13 | }); 14 | 15 | router.configure({ 16 | notfound: function () { 17 | window.location.hash = ''; 18 | app.visibility = 'all'; 19 | } 20 | }); 21 | 22 | router.init(); 23 | 24 | })(app, Router); 25 | -------------------------------------------------------------------------------- /examples/svg/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Helvetica Neue, Arial, sans-serif; 3 | } 4 | 5 | polygon { 6 | fill: #42b983; 7 | opacity: .75; 8 | } 9 | 10 | circle { 11 | fill: transparent; 12 | stroke: #999; 13 | } 14 | 15 | text { 16 | font-family: Helvetica Neue, Arial, sans-serif; 17 | font-size: 10px; 18 | fill: #666; 19 | } 20 | 21 | label { 22 | display: inline-block; 23 | margin-left: 10px; 24 | width: 20px; 25 | } 26 | 27 | #raw { 28 | position: absolute; 29 | top: 0; 30 | left: 300px; 31 | } -------------------------------------------------------------------------------- /examples/firebase/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Helvetica, Arial, sans-serif; 3 | } 4 | 5 | ul { 6 | padding: 0; 7 | } 8 | 9 | .user { 10 | height: 30px; 11 | line-height: 30px; 12 | padding: 10px; 13 | border-top: 1px solid #eee; 14 | overflow: hidden; 15 | transition: all .25s ease; 16 | } 17 | 18 | .user:last-child { 19 | border-bottom: 1px solid #eee; 20 | } 21 | 22 | .v-enter, .v-leave { 23 | height: 0; 24 | padding-top: 0; 25 | padding-bottom: 0; 26 | border-top-width: 0; 27 | border-bottom-width: 0; 28 | } 29 | 30 | .errors { 31 | color: #f00; 32 | } 33 | -------------------------------------------------------------------------------- /src/directives/internal/transition.js: -------------------------------------------------------------------------------- 1 | import { resolveAsset, addClass, removeClass } from '../../util/index' 2 | import { TRANSITION } from '../priorities' 3 | import Transition from '../../transition/transition' 4 | 5 | export default { 6 | 7 | priority: TRANSITION, 8 | 9 | update (id, oldId) { 10 | var el = this.el 11 | // resolve on owner vm 12 | var hooks = resolveAsset(this.vm.$options, 'transitions', id) 13 | id = id || 'v' 14 | el.__v_trans = new Transition(el, id, hooks, this.vm) 15 | if (oldId) { 16 | removeClass(el, oldId + '-transition') 17 | } 18 | addClass(el, id + '-transition') 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/util/debug.js: -------------------------------------------------------------------------------- 1 | import config from '../config' 2 | import { hyphenate } from './lang' 3 | 4 | let warn 5 | let formatComponentName 6 | 7 | if (process.env.NODE_ENV !== 'production') { 8 | const hasConsole = typeof console !== 'undefined' 9 | 10 | warn = (msg, vm) => { 11 | if (hasConsole && (!config.silent)) { 12 | console.error('[Vue warn]: ' + msg + (vm ? formatComponentName(vm) : '')) 13 | } 14 | } 15 | 16 | formatComponentName = vm => { 17 | var name = vm._isVue ? vm.$options.name : vm.name 18 | return name 19 | ? ' (found in component: <' + hyphenate(name) + '>)' 20 | : '' 21 | } 22 | } 23 | 24 | export { warn } 25 | -------------------------------------------------------------------------------- /test/unit/specs/directives/public/cloak_spec.js: -------------------------------------------------------------------------------- 1 | var compile = require('src/compiler').compile 2 | var Vue = require('src') 3 | 4 | describe('v-cloak', function () { 5 | it('should not remove during compile', function () { 6 | var el = document.createElement('div') 7 | el.setAttribute('v-cloak', '') 8 | compile(el, Vue.options) 9 | expect(el.hasAttribute('v-cloak')).toBe(true) 10 | }) 11 | 12 | it('should remove after compile', function () { 13 | var el = document.createElement('div') 14 | el.setAttribute('v-cloak', '') 15 | new Vue({ 16 | el: el 17 | }) 18 | expect(el.hasAttribute('v-cloak')).toBe(false) 19 | }) 20 | }) 21 | -------------------------------------------------------------------------------- /src/directives/public/el.js: -------------------------------------------------------------------------------- 1 | import { camelize, hasOwn, defineReactive } from '../../util/index' 2 | import { EL } from '../priorities' 3 | 4 | export default { 5 | 6 | priority: EL, 7 | 8 | bind () { 9 | /* istanbul ignore if */ 10 | if (!this.arg) { 11 | return 12 | } 13 | var id = this.id = camelize(this.arg) 14 | var refs = (this._scope || this.vm).$els 15 | if (hasOwn(refs, id)) { 16 | refs[id] = this.el 17 | } else { 18 | defineReactive(refs, id, this.el) 19 | } 20 | }, 21 | 22 | unbind () { 23 | var refs = (this._scope || this.vm).$els 24 | if (refs[this.id] === this.el) { 25 | refs[this.id] = null 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /src/directives/public/index.js: -------------------------------------------------------------------------------- 1 | // text & html 2 | import text from './text' 3 | import html from './html' 4 | // logic control 5 | import vFor from './for' 6 | import vIf from './if' 7 | import show from './show' 8 | // two-way binding 9 | import model from './model/index' 10 | // event handling 11 | import on from './on' 12 | // attributes 13 | import bind from './bind' 14 | // ref & el 15 | import el from './el' 16 | import ref from './ref' 17 | // cloak 18 | import cloak from './cloak' 19 | 20 | // must export plain object 21 | export default { 22 | text, 23 | html, 24 | 'for': vFor, 25 | 'if': vIf, 26 | show, 27 | model, 28 | on, 29 | bind, 30 | el, 31 | ref, 32 | cloak 33 | } 34 | -------------------------------------------------------------------------------- /examples/markdown/style.css: -------------------------------------------------------------------------------- 1 | html, body, #editor { 2 | margin: 0; 3 | height: 100%; 4 | font-family: 'Helvetica Neue', Arial, sans-serif; 5 | color: #333; 6 | } 7 | 8 | textarea, #editor div { 9 | display: inline-block; 10 | width: 49%; 11 | height: 100%; 12 | vertical-align: top; 13 | -webkit-box-sizing: border-box; 14 | -moz-box-sizing: border-box; 15 | box-sizing: border-box; 16 | padding: 0 20px; 17 | } 18 | 19 | textarea { 20 | border: none; 21 | border-right: 1px solid #ccc; 22 | resize: none; 23 | outline: none; 24 | background-color: #f6f6f6; 25 | font-size: 14px; 26 | font-family: 'Monaco', courier, monospace; 27 | padding: 20px; 28 | } 29 | 30 | code { 31 | color: #f66; 32 | } -------------------------------------------------------------------------------- /examples/markdown/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vue.js markdown editor example 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 | 14 |
15 |
16 | 17 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /test/unit/specs/directives/public/pre_spec.js: -------------------------------------------------------------------------------- 1 | var Vue = require('src') 2 | 3 | describe('v-pre', function () { 4 | it('should work', function () { 5 | var vm = new Vue({ 6 | el: document.createElement('div'), 7 | template: '
{{a}}
', 8 | data: { 9 | a: 123 10 | } 11 | }) 12 | expect(vm.$el.firstChild.textContent).toBe('{{a}}') 13 | }) 14 | 15 | it('should work on root node', function () { 16 | var vm = new Vue({ 17 | el: document.createElement('div'), 18 | template: '
{{a}}
', 19 | replace: true, 20 | data: { 21 | a: 123 22 | } 23 | }) 24 | expect(vm.$el.textContent).toBe('{{a}}') 25 | expect(getWarnCount()).toBe(0) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /test/e2e/modal.js: -------------------------------------------------------------------------------- 1 | casper.test.begin('modal', 8, function (test) { 2 | casper 3 | .start('examples/modal/index.html') 4 | .then(function () { 5 | test.assertNotVisible('.modal-mask') 6 | }) 7 | .thenClick('#show-modal', function () { 8 | test.assertVisible('.modal-mask') 9 | test.assertVisible('.modal-wrapper') 10 | test.assertVisible('.modal-container') 11 | test.assertSelectorHasText('.modal-header h3', 'custom header') 12 | test.assertSelectorHasText('.modal-body', 'default body') 13 | test.assertSelectorHasText('.modal-footer', 'default footer') 14 | }) 15 | .thenClick('.modal-default-button', function () { 16 | test.assertNotVisible('.modal-mask') 17 | }) 18 | // run 19 | .run(function () { 20 | test.done() 21 | }) 22 | }) 23 | -------------------------------------------------------------------------------- /test/unit/specs/observer/dep_spec.js: -------------------------------------------------------------------------------- 1 | var Dep = require('src/observer/dep') 2 | 3 | describe('Dep', function () { 4 | var d 5 | beforeEach(function () { 6 | d = new Dep() 7 | }) 8 | 9 | it('addSub', function () { 10 | var sub = {} 11 | d.addSub(sub) 12 | expect(d.subs.length).toBe(1) 13 | expect(d.subs.indexOf(sub)).toBe(0) 14 | }) 15 | 16 | it('removeSub', function () { 17 | var sub = {} 18 | d.addSub(sub) 19 | d.removeSub(sub) 20 | expect(d.subs.length).toBe(0) 21 | expect(d.subs.indexOf(sub)).toBe(-1) 22 | }) 23 | 24 | it('notify', function () { 25 | var sub = { 26 | update: jasmine.createSpy('sub') 27 | } 28 | d.addSub(sub) 29 | d.notify() 30 | expect(sub.update).toHaveBeenCalled() 31 | }) 32 | }) 33 | -------------------------------------------------------------------------------- /src/transition/queue.js: -------------------------------------------------------------------------------- 1 | import { nextTick } from '../util/index' 2 | 3 | let queue = [] 4 | let queued = false 5 | 6 | /** 7 | * Push a job into the queue. 8 | * 9 | * @param {Function} job 10 | */ 11 | 12 | export function pushJob (job) { 13 | queue.push(job) 14 | if (!queued) { 15 | queued = true 16 | nextTick(flush) 17 | } 18 | } 19 | 20 | /** 21 | * Flush the queue, and do one forced reflow before 22 | * triggering transitions. 23 | */ 24 | 25 | function flush () { 26 | // Force layout 27 | var f = document.documentElement.offsetHeight 28 | for (var i = 0; i < queue.length; i++) { 29 | queue[i]() 30 | } 31 | queue = [] 32 | queued = false 33 | // dummy return, so js linters don't complain about 34 | // unused variable f 35 | return f 36 | } 37 | -------------------------------------------------------------------------------- /src/directives/public/show.js: -------------------------------------------------------------------------------- 1 | import { getAttr, inDoc } from '../../util/index' 2 | import { applyTransition } from '../../transition/index' 3 | 4 | export default { 5 | 6 | bind () { 7 | // check else block 8 | var next = this.el.nextElementSibling 9 | if (next && getAttr(next, 'v-else') !== null) { 10 | this.elseEl = next 11 | } 12 | }, 13 | 14 | update (value) { 15 | this.apply(this.el, value) 16 | if (this.elseEl) { 17 | this.apply(this.elseEl, !value) 18 | } 19 | }, 20 | 21 | apply (el, value) { 22 | if (inDoc(el)) { 23 | applyTransition(el, value ? 1 : -1, toggle, this.vm) 24 | } else { 25 | toggle() 26 | } 27 | function toggle () { 28 | el.style.display = value ? '' : 'none' 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/unit/specs/directives/public/text_spec.js: -------------------------------------------------------------------------------- 1 | var _ = require('src/util') 2 | var def = require('src/directives/public/text') 3 | 4 | describe('v-text', function () { 5 | it('element', function () { 6 | var dir = { 7 | el: document.createElement('div') 8 | } 9 | _.extend(dir, def) 10 | dir.bind() 11 | dir.update('foo') 12 | expect(dir.el.textContent).toBe('foo') 13 | dir.update(123) 14 | expect(dir.el.textContent).toBe('123') 15 | }) 16 | 17 | it('text node', function () { 18 | var dir = { 19 | el: document.createTextNode(' ') 20 | } 21 | _.extend(dir, def) 22 | dir.bind() 23 | dir.update('foo') 24 | expect(dir.el.nodeValue).toBe('foo') 25 | dir.update(123) 26 | expect(dir.el.nodeValue).toBe('123') 27 | }) 28 | }) 29 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import Vue from './instance/vue' 2 | import installGlobalAPI from './global-api' 3 | import { inBrowser, devtools } from './util/index' 4 | import config from './config' 5 | 6 | installGlobalAPI(Vue) 7 | 8 | Vue.version = '1.0.24' 9 | 10 | export default Vue 11 | 12 | // devtools global hook 13 | /* istanbul ignore next */ 14 | setTimeout(() => { 15 | if (config.devtools) { 16 | if (devtools) { 17 | devtools.emit('init', Vue) 18 | } else if ( 19 | process.env.NODE_ENV !== 'production' && 20 | inBrowser && /Chrome\/\d+/.test(window.navigator.userAgent) 21 | ) { 22 | console.log( 23 | 'Download the Vue Devtools for a better development experience:\n' + 24 | 'https://github.com/vuejs/vue-devtools' 25 | ) 26 | } 27 | } 28 | }, 0) 29 | -------------------------------------------------------------------------------- /src/directives/public/model/radio.js: -------------------------------------------------------------------------------- 1 | import { toNumber, looseEqual } from '../../../util/index' 2 | 3 | export default { 4 | 5 | bind () { 6 | var self = this 7 | var el = this.el 8 | 9 | this.getValue = function () { 10 | // value overwrite via v-bind:value 11 | if (el.hasOwnProperty('_value')) { 12 | return el._value 13 | } 14 | var val = el.value 15 | if (self.params.number) { 16 | val = toNumber(val) 17 | } 18 | return val 19 | } 20 | 21 | this.listener = function () { 22 | self.set(self.getValue()) 23 | } 24 | this.on('change', this.listener) 25 | 26 | if (el.hasAttribute('checked')) { 27 | this.afterBind = this.listener 28 | } 29 | }, 30 | 31 | update (value) { 32 | this.el.checked = looseEqual(value, this.getValue()) 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /examples/grid/grid.js: -------------------------------------------------------------------------------- 1 | // register the grid component 2 | Vue.component('demo-grid', { 3 | template: '#grid-template', 4 | replace: true, 5 | props: { 6 | data: Array, 7 | columns: Array, 8 | filterKey: String 9 | }, 10 | data: function () { 11 | var sortOrders = {} 12 | this.columns.forEach(function (key) { 13 | sortOrders[key] = 1 14 | }) 15 | return { 16 | sortKey: '', 17 | sortOrders: sortOrders 18 | } 19 | }, 20 | methods: { 21 | sortBy: function (key) { 22 | this.sortKey = key 23 | this.sortOrders[key] = this.sortOrders[key] * -1 24 | } 25 | } 26 | }) 27 | 28 | // bootstrap the demo 29 | var demo = new Vue({ 30 | el: '#demo', 31 | data: { 32 | searchQuery: '', 33 | gridColumns: ['name', 'power'], 34 | gridData: [ 35 | { name: 'Chuck Norris', power: Infinity }, 36 | { name: 'Bruce Lee', power: 9000 }, 37 | { name: 'Jackie Chan', power: 7000 }, 38 | { name: 'Jet Li', power: 8000 } 39 | ] 40 | } 41 | }) 42 | -------------------------------------------------------------------------------- /examples/elastic-header/style.css: -------------------------------------------------------------------------------- 1 | h1 { 2 | font-weight: 300; 3 | font-size: 1.8em; 4 | margin-top: 0; 5 | } 6 | a { 7 | color: #fff; 8 | } 9 | .draggable-header-view { 10 | background-color: #fff; 11 | box-shadow: 0 4px 16px rgba(0,0,0,.15); 12 | width: 320px; 13 | height: 560px; 14 | overflow: hidden; 15 | margin: 30px auto; 16 | position: relative; 17 | font-family: 'Roboto', Helvetica, Arial, sans-serif; 18 | color: #fff; 19 | font-size: 14px; 20 | font-weight: 300; 21 | -webkit-user-select: none; 22 | -moz-user-select: none; 23 | -ms-user-select: none; 24 | user-select: none; 25 | } 26 | .draggable-header-view .bg { 27 | position: absolute; 28 | top: 0; 29 | left: 0; 30 | z-index: 0; 31 | } 32 | .draggable-header-view .header, .draggable-header-view .content { 33 | position: relative; 34 | z-index: 1; 35 | padding: 30px; 36 | box-sizing: border-box; 37 | } 38 | .draggable-header-view .header { 39 | height: 160px; 40 | } 41 | .draggable-header-view .content { 42 | color: #333; 43 | line-height: 1.5em; 44 | } 45 | -------------------------------------------------------------------------------- /src/directives/element/partial.js: -------------------------------------------------------------------------------- 1 | import vIf from '../public/if' 2 | import FragmentFactory from '../../fragment/factory' 3 | import { PARTIAL } from '../priorities' 4 | import { 5 | createAnchor, 6 | replace, 7 | resolveAsset 8 | } from '../../util/index' 9 | 10 | export default { 11 | 12 | priority: PARTIAL, 13 | 14 | params: ['name'], 15 | 16 | // watch changes to name for dynamic partials 17 | paramWatchers: { 18 | name (value) { 19 | vIf.remove.call(this) 20 | if (value) { 21 | this.insert(value) 22 | } 23 | } 24 | }, 25 | 26 | bind () { 27 | this.anchor = createAnchor('v-partial') 28 | replace(this.el, this.anchor) 29 | this.insert(this.params.name) 30 | }, 31 | 32 | insert (id) { 33 | var partial = resolveAsset(this.vm.$options, 'partials', id, true) 34 | if (partial) { 35 | this.factory = new FragmentFactory(this.vm, partial) 36 | vIf.insert.call(this) 37 | } 38 | }, 39 | 40 | unbind () { 41 | if (this.frag) { 42 | this.frag.destroy() 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /test/unit/specs/directives/public/el_spec.js: -------------------------------------------------------------------------------- 1 | var _ = require('src/util') 2 | var Vue = require('src') 3 | 4 | describe('el', function () { 5 | var el 6 | beforeEach(function () { 7 | el = document.createElement('div') 8 | }) 9 | 10 | it('normal', function (done) { 11 | var vm = new Vue({ 12 | el: el, 13 | data: { 14 | ok: true 15 | }, 16 | template: '
' 17 | }) 18 | expect(vm.$els.testEl).toBeTruthy() 19 | expect(vm.$els.testEl.id).toBe('test') 20 | vm.ok = false 21 | _.nextTick(function () { 22 | expect(vm.$els.testEl).toBeNull() 23 | vm.ok = true 24 | _.nextTick(function () { 25 | expect(vm.$els.testEl.id).toBe('test') 26 | done() 27 | }) 28 | }) 29 | }) 30 | 31 | it('inside v-for', function () { 32 | var vm = new Vue({ 33 | el: el, 34 | data: { items: [1, 2] }, 35 | template: '

{{n}}

{{$els.test.textContent}}
' 36 | }) 37 | expect(vm.$el.textContent).toBe('1122') 38 | }) 39 | }) 40 | -------------------------------------------------------------------------------- /examples/todomvc/readme.md: -------------------------------------------------------------------------------- 1 | # Vue.js TodoMVC Example 2 | 3 | > Vue.js is a library for building interactive web interfaces. 4 | It provides data-driven, nestable view components with a simple and flexible API. 5 | 6 | > _[Vue.js - vuejs.org](http://vuejs.org)_ 7 | 8 | ## Learning Vue.js 9 | The [Vue.js website](http://vuejs.org/) is a great resource to get started. 10 | 11 | Here are some links you may find helpful: 12 | 13 | * [Official Guide](http://vuejs.org/guide/) 14 | * [API Reference](http://vuejs.org/api/) 15 | * [Examples](http://vuejs.org/examples/) 16 | * [Building Larger Apps with Vue.js](http://vuejs.org/guide/application.html) 17 | 18 | Get help from other Vue.js users: 19 | 20 | * [Vue.js official forum](http://forum.vuejs.org) 21 | * [Vue.js on Twitter](https://twitter.com/vuejs) 22 | * [Vue.js on Gitter](https://gitter.im/vuejs/vue) 23 | 24 | _If you have other helpful links to share, or find any of the links above no longer work, please [let us know](https://github.com/tastejs/todomvc/issues)._ 25 | 26 | ## Credit 27 | 28 | This TodoMVC application was created by [Evan You](http://evanyou.me). 29 | -------------------------------------------------------------------------------- /src/instance/vue.js: -------------------------------------------------------------------------------- 1 | import initMixin from './internal/init' 2 | import stateMixin from './internal/state' 3 | import eventsMixin from './internal/events' 4 | import lifecycleMixin from './internal/lifecycle' 5 | import miscMixin from './internal/misc' 6 | 7 | import dataAPI from './api/data' 8 | import domAPI from './api/dom' 9 | import eventsAPI from './api/events' 10 | import lifecycleAPI from './api/lifecycle' 11 | 12 | /** 13 | * The exposed Vue constructor. 14 | * 15 | * API conventions: 16 | * - public API methods/properties are prefixed with `$` 17 | * - internal methods/properties are prefixed with `_` 18 | * - non-prefixed properties are assumed to be proxied user 19 | * data. 20 | * 21 | * @constructor 22 | * @param {Object} [options] 23 | * @public 24 | */ 25 | 26 | function Vue (options) { 27 | this._init(options) 28 | } 29 | 30 | // install internals 31 | initMixin(Vue) 32 | stateMixin(Vue) 33 | eventsMixin(Vue) 34 | lifecycleMixin(Vue) 35 | miscMixin(Vue) 36 | 37 | // install instance APIs 38 | dataAPI(Vue) 39 | domAPI(Vue) 40 | eventsAPI(Vue) 41 | lifecycleAPI(Vue) 42 | 43 | export default Vue 44 | -------------------------------------------------------------------------------- /examples/grid/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: Helvetica Neue, Arial, sans-serif; 3 | font-size: 14px; 4 | color: #444; 5 | } 6 | 7 | table { 8 | border: 2px solid #42b983; 9 | border-radius: 3px; 10 | background-color: #fff; 11 | } 12 | 13 | th { 14 | background-color: #42b983; 15 | color: rgba(255,255,255,0.66); 16 | cursor: pointer; 17 | -webkit-user-select: none; 18 | -moz-user-select: none; 19 | -user-select: none; 20 | } 21 | 22 | td { 23 | background-color: #f9f9f9; 24 | } 25 | 26 | th, td { 27 | min-width: 120px; 28 | padding: 10px 20px; 29 | } 30 | 31 | th.active { 32 | color: #fff; 33 | } 34 | 35 | th.active .arrow { 36 | opacity: 1; 37 | } 38 | 39 | .arrow { 40 | display: inline-block; 41 | vertical-align: middle; 42 | width: 0; 43 | height: 0; 44 | margin-left: 5px; 45 | opacity: 0.66; 46 | } 47 | 48 | .arrow.asc { 49 | border-left: 4px solid transparent; 50 | border-right: 4px solid transparent; 51 | border-bottom: 4px solid #fff; 52 | } 53 | 54 | .arrow.dsc { 55 | border-left: 4px solid transparent; 56 | border-right: 4px solid transparent; 57 | border-top: 4px solid #fff; 58 | } -------------------------------------------------------------------------------- /test/unit/lib/MIT.LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2008-2014 Pivotal Labs 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2013-2016 Yuxi Evan You 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /test/unit/specs/util/debug_spec.js: -------------------------------------------------------------------------------- 1 | var _ = require('src/util') 2 | var Vue = require('src') 3 | var config = require('src/config') 4 | var warnPrefix = '[Vue warn]: ' 5 | 6 | if (typeof console !== 'undefined') { 7 | describe('Util - Debug', function () { 8 | beforeEach(function () { 9 | spyOn(console, 'error') 10 | }) 11 | 12 | it('warn when silent is false', function () { 13 | config.silent = false 14 | _.warn.and.callThrough() 15 | _.warn('oops') 16 | expect(console.error).toHaveBeenCalledWith(warnPrefix + 'oops') 17 | }) 18 | 19 | it('format component name', function () { 20 | config.silent = false 21 | _.warn.and.callThrough() 22 | _.warn('oops', new Vue({ name: 'foo' })) 23 | expect(console.error).toHaveBeenCalledWith(warnPrefix + 'oops (found in component: )') 24 | _.warn('oops', { name: 'bar' }) 25 | expect(console.error).toHaveBeenCalledWith(warnPrefix + 'oops (found in component: )') 26 | }) 27 | 28 | it('not warn when silent is ture', function () { 29 | config.silent = true 30 | _.warn.and.callThrough() 31 | _.warn('oops') 32 | expect(console.error).not.toHaveBeenCalled() 33 | }) 34 | }) 35 | } 36 | -------------------------------------------------------------------------------- /src/directives/public/html.js: -------------------------------------------------------------------------------- 1 | import { parseTemplate } from '../../parsers/template' 2 | import { 3 | createAnchor, 4 | before, 5 | replace, 6 | remove, 7 | _toString, 8 | toArray 9 | } from '../../util/index' 10 | 11 | export default { 12 | 13 | bind () { 14 | // a comment node means this is a binding for 15 | // {{{ inline unescaped html }}} 16 | if (this.el.nodeType === 8) { 17 | // hold nodes 18 | this.nodes = [] 19 | // replace the placeholder with proper anchor 20 | this.anchor = createAnchor('v-html') 21 | replace(this.el, this.anchor) 22 | } 23 | }, 24 | 25 | update (value) { 26 | value = _toString(value) 27 | if (this.nodes) { 28 | this.swap(value) 29 | } else { 30 | this.el.innerHTML = value 31 | } 32 | }, 33 | 34 | swap (value) { 35 | // remove old nodes 36 | var i = this.nodes.length 37 | while (i--) { 38 | remove(this.nodes[i]) 39 | } 40 | // convert new value to a fragment 41 | // do not attempt to retrieve from id selector 42 | var frag = parseTemplate(value, true, true) 43 | // save a reference to these nodes so we can remove later 44 | this.nodes = toArray(frag.childNodes) 45 | before(frag, this.anchor) 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /examples/firebase/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vue.js Firebase example 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 |
    17 |
  • 18 | {{user.name}} - {{user.email}} 19 | 20 |
  • 21 |
22 |
23 | 24 | 25 | 26 |
27 |
    28 |
  • Name cannot be empty.
  • 29 |
  • Please provide a valid email address.
  • 30 |
31 |
32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /examples/modal/modal.css: -------------------------------------------------------------------------------- 1 | .modal-mask { 2 | position: fixed; 3 | z-index: 9998; 4 | top: 0; 5 | left: 0; 6 | width: 100%; 7 | height: 100%; 8 | background-color: rgba(0, 0, 0, .5); 9 | display: table; 10 | transition: opacity .3s ease; 11 | } 12 | 13 | .modal-wrapper { 14 | display: table-cell; 15 | vertical-align: middle; 16 | } 17 | 18 | .modal-container { 19 | width: 300px; 20 | margin: 0px auto; 21 | padding: 20px 30px; 22 | background-color: #fff; 23 | border-radius: 2px; 24 | box-shadow: 0 2px 8px rgba(0, 0, 0, .33); 25 | transition: all .3s ease; 26 | font-family: Helvetica, Arial, sans-serif; 27 | } 28 | 29 | .modal-header h3 { 30 | margin-top: 0; 31 | color: #42b983; 32 | } 33 | 34 | .modal-body { 35 | margin: 20px 0; 36 | } 37 | 38 | .modal-default-button { 39 | float: right; 40 | } 41 | 42 | /* 43 | * the following styles are auto-applied to elements with 44 | * transition="modal" when their visibility is toggled 45 | * by Vue.js. 46 | * 47 | * You can easily play with the modal transition by editing 48 | * these styles. 49 | */ 50 | 51 | .modal-enter, .modal-leave { 52 | opacity: 0; 53 | } 54 | 55 | .modal-enter .modal-container, 56 | .modal-leave .modal-container { 57 | -webkit-transform: scale(1.1); 58 | transform: scale(1.1); 59 | } 60 | -------------------------------------------------------------------------------- /src/observer/dep.js: -------------------------------------------------------------------------------- 1 | import { toArray } from '../util/index' 2 | 3 | let uid = 0 4 | 5 | /** 6 | * A dep is an observable that can have multiple 7 | * directives subscribing to it. 8 | * 9 | * @constructor 10 | */ 11 | 12 | export default function Dep () { 13 | this.id = uid++ 14 | this.subs = [] 15 | } 16 | 17 | // the current target watcher being evaluated. 18 | // this is globally unique because there could be only one 19 | // watcher being evaluated at any time. 20 | Dep.target = null 21 | 22 | /** 23 | * Add a directive subscriber. 24 | * 25 | * @param {Directive} sub 26 | */ 27 | 28 | Dep.prototype.addSub = function (sub) { 29 | this.subs.push(sub) 30 | } 31 | 32 | /** 33 | * Remove a directive subscriber. 34 | * 35 | * @param {Directive} sub 36 | */ 37 | 38 | Dep.prototype.removeSub = function (sub) { 39 | this.subs.$remove(sub) 40 | } 41 | 42 | /** 43 | * Add self as a dependency to the target watcher. 44 | */ 45 | 46 | Dep.prototype.depend = function () { 47 | Dep.target.addDep(this) 48 | } 49 | 50 | /** 51 | * Notify all subscribers of a new value. 52 | */ 53 | 54 | Dep.prototype.notify = function () { 55 | // stablize the subscriber list first 56 | var subs = toArray(this.subs) 57 | for (var i = 0, l = subs.length; i < l; i++) { 58 | subs[i].update() 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /examples/firebase/app.js: -------------------------------------------------------------------------------- 1 | var emailRE = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/ 2 | 3 | // Firebase ref 4 | var usersRef = new Firebase('https://vue-demo.firebaseIO.com/users') 5 | 6 | // create Vue app 7 | var app = new Vue({ 8 | // element to mount to 9 | el: '#app', 10 | // initial data 11 | data: { 12 | newUser: { 13 | name: '', 14 | email: '' 15 | } 16 | }, 17 | // firebase binding 18 | // https://github.com/vuejs/vuefire 19 | firebase: { 20 | users: usersRef 21 | }, 22 | // computed property for form validation state 23 | computed: { 24 | validation: function () { 25 | return { 26 | name: !!this.newUser.name.trim(), 27 | email: emailRE.test(this.newUser.email) 28 | } 29 | }, 30 | isValid: function () { 31 | var validation = this.validation 32 | return Object.keys(validation).every(function (key) { 33 | return validation[key] 34 | }) 35 | } 36 | }, 37 | // methods 38 | methods: { 39 | addUser: function () { 40 | if (this.isValid) { 41 | usersRef.push(this.newUser) 42 | this.newUser.name = '' 43 | this.newUser.email = '' 44 | } 45 | }, 46 | removeUser: function (user) { 47 | usersRef.child(user['.key']).remove() 48 | } 49 | } 50 | }) 51 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | 2 | Contributor Code of Conduct 3 | 4 | As contributors and maintainers of this project, we pledge to respect all people who contribute through reporting issues, posting feature requests, updating documentation, submitting pull requests or patches, and other activities. 5 | 6 | We are committed to making participation in this project a harassment-free experience for everyone, regardless of level of experience, gender, gender identity and expression, sexual orientation, disability, personal appearance, body size, race, age, or religion. 7 | 8 | Examples of unacceptable behavior by participants include the use of sexual language or imagery, derogatory comments or personal attacks, trolling, public or private harassment, insults, or other unprofessional conduct. 9 | 10 | Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct. Project maintainers who do not follow the Code of Conduct may be removed from the project team. 11 | 12 | Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by opening an issue or contacting one or more of the project maintainers. 13 | 14 | This Code of Conduct is adapted from the [Contributor Covenant](http://contributor-covenant.org), version 1.0.0, available at [http://contributor-covenant.org/version/1/0/0/](http://contributor-covenant.org/version/1/0/0/) 15 | -------------------------------------------------------------------------------- /examples/commits/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Vue.js commits example 5 | 21 | 22 | 23 | 24 |
25 |

Latest Vue.js Commits

26 | 34 |

vuejs/vue@{{currentBranch}}

35 |
    36 |
  • 37 | {{record.sha.slice(0, 7)}} 38 | - {{record.commit.message | truncate}}
    39 | by {{record.commit.author.name}} 40 | at {{record.commit.author.date | formatDate}} 41 |
  • 42 |
43 |
44 | 45 | 46 | 47 | -------------------------------------------------------------------------------- /test/e2e/markdown.js: -------------------------------------------------------------------------------- 1 | casper.test.begin('markdown', 5, function (test) { 2 | casper 3 | .start('examples/markdown/index.html') 4 | .then(function () { 5 | test.assertEval(function () { 6 | return document.querySelector('textarea').value === '# hello' 7 | }) 8 | test.assertEval(function () { 9 | return document.querySelector('#editor div') 10 | .innerHTML === '

hello

\n' 11 | }) 12 | }) 13 | .then(function () { 14 | this.sendKeys( 15 | 'textarea', 16 | '## foo\n\n' + 17 | '- bar\n' + 18 | '- baz\n\n', 19 | { keepFocus: true } 20 | ) 21 | // keyUp(13) 22 | }) 23 | .then(function () { 24 | // assert the output is not updated yet because of 25 | // debounce 26 | test.assertEval(function () { 27 | return document.querySelector('#editor div') 28 | .innerHTML === '

hello

\n' 29 | }) 30 | }) 31 | .wait(300) // wait for debounce 32 | .then(function () { 33 | test.assertEval(function () { 34 | return document.querySelector('textarea').value === 35 | '## foo\n\n- bar\n- baz\n\n# hello' 36 | }) 37 | test.assertEval(function () { 38 | return document.querySelector('#editor div') 39 | .innerHTML === 40 | '

foo

\n' + 41 | '
    \n
  • bar
  • \n
  • baz
  • \n
\n' + 42 | '

hello

\n' 43 | }) 44 | }) 45 | // run 46 | .run(function () { 47 | test.done() 48 | }) 49 | }) 50 | -------------------------------------------------------------------------------- /test/e2e/commits.js: -------------------------------------------------------------------------------- 1 | casper.on('remote.message', function (e) { 2 | console.log(e) 3 | }) 4 | 5 | casper.test.begin('commits', 18, function (test) { 6 | casper 7 | .start('examples/commits/index.html') 8 | .then(function () { 9 | // radio inputs & labels 10 | test.assertElementCount('input', 2) 11 | test.assertElementCount('label', 2) 12 | test.assertSelectorHasText('label[for="master"]', 'master') 13 | test.assertSelectorHasText('label[for="dev"]', 'dev') 14 | // initial fetched commits 15 | test.assertField('branch', 'master') 16 | test.assertSelectorHasText('p', 'vuejs/vue@master') 17 | test.assertElementCount('li', 3) 18 | test.assertSelectorHasText('li:first-child a.commit', '1111111') 19 | test.assertSelectorHasText('li:first-child span.message', 'one') 20 | test.assertSelectorHasText('li:first-child span.author', 'Evan') 21 | test.assertSelectorHasText('li:first-child span.date', '2014-10-15 13:52:58') 22 | }) 23 | .thenClick('input[value="dev"]', function () { 24 | test.assertField('branch', 'dev') 25 | test.assertSelectorHasText('p', 'vuejs/vue@dev') 26 | test.assertElementCount('li', 3) 27 | test.assertSelectorHasText('li:first-child a.commit', '2222222') 28 | test.assertSelectorHasText('li:first-child span.message', 'two') 29 | test.assertSelectorHasText('li:first-child span.author', 'Evan') 30 | test.assertSelectorHasText('li:first-child span.date', '2014-10-15 13:52:58') 31 | }) 32 | // run 33 | .run(function () { 34 | test.done() 35 | }) 36 | }) 37 | -------------------------------------------------------------------------------- /test/unit/specs/directives/public/html_spec.js: -------------------------------------------------------------------------------- 1 | var _ = require('src/util') 2 | var def = require('src/directives/public/html') 3 | 4 | describe('v-html', function () { 5 | var el 6 | beforeEach(function () { 7 | el = document.createElement('div') 8 | }) 9 | 10 | it('element', function () { 11 | var dir = { 12 | el: el 13 | } 14 | _.extend(dir, def) 15 | dir.bind() 16 | dir.update('
1234

234

') 17 | expect(el.innerHTML).toBe('
1234

234

') 18 | dir.update('

123

444
') 19 | expect(el.innerHTML).toBe('

123

444
') 20 | dir.update(null) 21 | expect(el.innerHTML).toBe('') 22 | }) 23 | 24 | it('inline', function () { 25 | var node = document.createComment('html-test') 26 | el.appendChild(node) 27 | var dir = { 28 | el: node 29 | } 30 | _.extend(dir, def) 31 | dir.bind() 32 | dir.update('
1234

234

') 33 | expect(el.innerHTML).toBe('
1234

234

') 34 | dir.update('

123

444
') 35 | expect(el.innerHTML).toBe('

123

444
') 36 | dir.update(null) 37 | expect(el.innerHTML).toBe('') 38 | }) 39 | 40 | it('inline keep whitespace', function () { 41 | var node = document.createComment('html-test') 42 | el.appendChild(node) 43 | var dir = { 44 | el: node 45 | } 46 | _.extend(dir, def) 47 | dir.bind() 48 | dir.update('

span

') 49 | expect(el.innerHTML).toBe('

span

') 50 | }) 51 | }) 52 | -------------------------------------------------------------------------------- /examples/grid/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vue.js grid component example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 38 | 39 | 40 |
41 | 44 | 48 | 49 |
50 | 51 | 52 | 53 | 54 | 55 | -------------------------------------------------------------------------------- /test/unit/specs/instance/init_spec.js: -------------------------------------------------------------------------------- 1 | var Vue = require('src') 2 | var init = Vue.prototype._init 3 | 4 | describe('Instance Init', function () { 5 | var stub = { 6 | constructor: { 7 | options: { a: 1, b: 2 } 8 | }, 9 | _updateRef: jasmine.createSpy(), 10 | _initEvents: jasmine.createSpy(), 11 | _callHook: jasmine.createSpy(), 12 | _initState: jasmine.createSpy(), 13 | $mount: jasmine.createSpy() 14 | } 15 | 16 | var options = { 17 | a: 2, 18 | el: {} 19 | } 20 | 21 | init.call(stub, options) 22 | 23 | it('should setup properties', function () { 24 | expect(stub.$el).toBe(null) 25 | expect(stub.$root).toBe(stub) 26 | expect(stub.$refs).toBeTruthy() 27 | expect(stub.$els).toBeTruthy() 28 | expect(stub._watchers).toBeTruthy() 29 | expect(stub._directives).toBeTruthy() 30 | expect(stub._events).toBeTruthy() 31 | expect(stub._eventsCount).toBeTruthy() 32 | }) 33 | 34 | it('should merge options', function () { 35 | expect(stub.$options.a).toBe(2) 36 | expect(stub.$options.b).toBe(2) 37 | }) 38 | 39 | it('should call other init methods', function () { 40 | expect(stub._initEvents).toHaveBeenCalled() 41 | expect(stub._initState).toHaveBeenCalled() 42 | expect(stub._updateRef).toHaveBeenCalled() 43 | }) 44 | 45 | it('should call created hook', function () { 46 | expect(stub._callHook).toHaveBeenCalledWith('created') 47 | }) 48 | 49 | it('should call $mount when options.el is present', function () { 50 | expect(stub.$mount).toHaveBeenCalledWith(stub.$options.el) 51 | }) 52 | }) 53 | -------------------------------------------------------------------------------- /test/unit/specs/directives/public/show_spec.js: -------------------------------------------------------------------------------- 1 | var Vue = require('src') 2 | var transition = require('src/transition') 3 | var def = require('src/directives/public/show') 4 | 5 | describe('v-show', function () { 6 | var el 7 | beforeEach(function () { 8 | el = document.createElement('div') 9 | document.body.appendChild(el) 10 | spyOn(transition, 'applyTransition').and.callThrough() 11 | }) 12 | 13 | afterEach(function () { 14 | document.body.removeChild(el) 15 | }) 16 | 17 | it('should work', function () { 18 | var dir = { 19 | el: el, 20 | update: def.update, 21 | apply: def.apply, 22 | vm: new Vue() 23 | } 24 | dir.update(false) 25 | expect(el.style.display).toBe('none') 26 | dir.update(true) 27 | expect(el.style.display).toBe('') 28 | expect(transition.applyTransition).toHaveBeenCalled() 29 | }) 30 | 31 | it('should work with v-else', function (done) { 32 | var vm = new Vue({ 33 | el: el, 34 | template: 35 | '

YES

' + 36 | '

NO

', 37 | data: { 38 | ok: true 39 | } 40 | }) 41 | expect(el.children[0].style.display).toBe('') 42 | expect(el.children[1].style.display).toBe('none') 43 | expect(transition.applyTransition.calls.count()).toBe(2) 44 | vm.ok = false 45 | Vue.nextTick(function () { 46 | expect(el.children[0].style.display).toBe('none') 47 | expect(el.children[1].style.display).toBe('') 48 | expect(transition.applyTransition.calls.count()).toBe(4) 49 | done() 50 | }) 51 | }) 52 | }) 53 | -------------------------------------------------------------------------------- /examples/tree/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vue.js tree-view demo 6 | 23 | 24 | 25 | 26 | 27 | 28 | 47 | 48 |

(You can double click on an item to turn it into a folder.)

49 | 50 | 51 |
    52 | 55 | 56 |
57 | 58 | 59 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /examples/tree/tree.js: -------------------------------------------------------------------------------- 1 | // demo data 2 | var data = { 3 | name: 'My Tree', 4 | children: [ 5 | { name: 'hello' }, 6 | { name: 'wat' }, 7 | { 8 | name: 'child folder', 9 | children: [ 10 | { 11 | name: 'child folder', 12 | children: [ 13 | { name: 'hello' }, 14 | { name: 'wat' } 15 | ] 16 | }, 17 | { name: 'hello' }, 18 | { name: 'wat' }, 19 | { 20 | name: 'child folder', 21 | children: [ 22 | { name: 'hello' }, 23 | { name: 'wat' } 24 | ] 25 | } 26 | ] 27 | } 28 | ] 29 | } 30 | 31 | // define the item component 32 | Vue.component('item', { 33 | template: '#item-template', 34 | replace: true, 35 | props: { 36 | model: Object 37 | }, 38 | data: function () { 39 | return { 40 | open: false 41 | } 42 | }, 43 | computed: { 44 | isFolder: function () { 45 | return this.model.children && 46 | this.model.children.length 47 | } 48 | }, 49 | methods: { 50 | toggle: function () { 51 | if (this.isFolder) { 52 | this.open = !this.open 53 | } 54 | }, 55 | changeType: function () { 56 | if (!this.isFolder) { 57 | Vue.set(this.model, 'children', []) 58 | this.addChild() 59 | this.open = true 60 | } 61 | }, 62 | addChild: function () { 63 | this.model.children.push({ 64 | name: 'new stuff' 65 | }) 66 | } 67 | } 68 | }) 69 | 70 | // boot up the demo 71 | var demo = new Vue({ 72 | el: '#demo', 73 | data: { 74 | treeData: data 75 | } 76 | }) 77 | -------------------------------------------------------------------------------- /src/directives/public/model/checkbox.js: -------------------------------------------------------------------------------- 1 | import { 2 | toNumber, 3 | isArray, 4 | indexOf, 5 | looseEqual 6 | } from '../../../util/index' 7 | 8 | export default { 9 | 10 | bind () { 11 | var self = this 12 | var el = this.el 13 | 14 | this.getValue = function () { 15 | return el.hasOwnProperty('_value') 16 | ? el._value 17 | : self.params.number 18 | ? toNumber(el.value) 19 | : el.value 20 | } 21 | 22 | function getBooleanValue () { 23 | var val = el.checked 24 | if (val && el.hasOwnProperty('_trueValue')) { 25 | return el._trueValue 26 | } 27 | if (!val && el.hasOwnProperty('_falseValue')) { 28 | return el._falseValue 29 | } 30 | return val 31 | } 32 | 33 | this.listener = function () { 34 | var model = self._watcher.value 35 | if (isArray(model)) { 36 | var val = self.getValue() 37 | if (el.checked) { 38 | if (indexOf(model, val) < 0) { 39 | model.push(val) 40 | } 41 | } else { 42 | model.$remove(val) 43 | } 44 | } else { 45 | self.set(getBooleanValue()) 46 | } 47 | } 48 | 49 | this.on('change', this.listener) 50 | if (el.hasAttribute('checked')) { 51 | this.afterBind = this.listener 52 | } 53 | }, 54 | 55 | update (value) { 56 | var el = this.el 57 | if (isArray(value)) { 58 | el.checked = indexOf(value, this.getValue()) > -1 59 | } else { 60 | if (el.hasOwnProperty('_trueValue')) { 61 | el.checked = looseEqual(value, el._trueValue) 62 | } else { 63 | el.checked = !!value 64 | } 65 | } 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /examples/select2/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vue.js custom directive integration example (select2) 6 | 7 | 8 | 9 | 10 | 15 | 16 | 17 | 18 |
19 |

Selected: {{selected}}

20 | 23 |
24 | 25 | 60 | 61 | 62 | -------------------------------------------------------------------------------- /src/fragment/factory.js: -------------------------------------------------------------------------------- 1 | import { compile } from '../compiler/index' 2 | import { isTemplate, getOuterHTML } from '../util/index' 3 | import { parseTemplate, cloneNode } from '../parsers/template' 4 | import Fragment from './fragment' 5 | import Cache from '../cache' 6 | 7 | const linkerCache = new Cache(5000) 8 | 9 | /** 10 | * A factory that can be used to create instances of a 11 | * fragment. Caches the compiled linker if possible. 12 | * 13 | * @param {Vue} vm 14 | * @param {Element|String} el 15 | */ 16 | 17 | export default function FragmentFactory (vm, el) { 18 | this.vm = vm 19 | var template 20 | var isString = typeof el === 'string' 21 | if (isString || isTemplate(el) && !el.hasAttribute('v-if')) { 22 | template = parseTemplate(el, true) 23 | } else { 24 | template = document.createDocumentFragment() 25 | template.appendChild(el) 26 | } 27 | this.template = template 28 | // linker can be cached, but only for components 29 | var linker 30 | var cid = vm.constructor.cid 31 | if (cid > 0) { 32 | var cacheId = cid + (isString ? el : getOuterHTML(el)) 33 | linker = linkerCache.get(cacheId) 34 | if (!linker) { 35 | linker = compile(template, vm.$options, true) 36 | linkerCache.put(cacheId, linker) 37 | } 38 | } else { 39 | linker = compile(template, vm.$options, true) 40 | } 41 | this.linker = linker 42 | } 43 | 44 | /** 45 | * Create a fragment instance with given host and scope. 46 | * 47 | * @param {Vue} host 48 | * @param {Object} scope 49 | * @param {Fragment} parentFrag 50 | */ 51 | 52 | FragmentFactory.prototype.create = function (host, scope, parentFrag) { 53 | var frag = cloneNode(this.template) 54 | return new Fragment(this.linker, this.vm, frag, host, scope, parentFrag) 55 | } 56 | -------------------------------------------------------------------------------- /examples/svg/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vue.js SVG example 6 | 7 | 8 | 9 | 10 | 11 | 12 | 24 | 25 | 26 | 29 | 30 | 31 |
32 | 33 | 34 | 35 | 36 | 37 |
38 | 39 | 40 | {{stat.value}} 41 | 42 |
43 |
44 | 45 | 46 |
47 |
{{stats | json}}
48 |
49 | 50 |

* input[type="range"] requires IE10 or above.

51 | 52 | 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /src/directives/element/slot.js: -------------------------------------------------------------------------------- 1 | import { SLOT } from '../priorities' 2 | import { 3 | extractContent, 4 | replace, 5 | remove 6 | } from '../../util/index' 7 | 8 | export default { 9 | 10 | priority: SLOT, 11 | params: ['name'], 12 | 13 | bind () { 14 | // this was resolved during component transclusion 15 | var name = this.params.name || 'default' 16 | var content = this.vm._slotContents && this.vm._slotContents[name] 17 | if (!content || !content.hasChildNodes()) { 18 | this.fallback() 19 | } else { 20 | this.compile(content.cloneNode(true), this.vm._context, this.vm) 21 | } 22 | }, 23 | 24 | compile (content, context, host) { 25 | if (content && context) { 26 | if ( 27 | this.el.hasChildNodes() && 28 | content.childNodes.length === 1 && 29 | content.childNodes[0].nodeType === 1 && 30 | content.childNodes[0].hasAttribute('v-if') 31 | ) { 32 | // if the inserted slot has v-if 33 | // inject fallback content as the v-else 34 | const elseBlock = document.createElement('template') 35 | elseBlock.setAttribute('v-else', '') 36 | elseBlock.innerHTML = this.el.innerHTML 37 | // the else block should be compiled in child scope 38 | elseBlock._context = this.vm 39 | content.appendChild(elseBlock) 40 | } 41 | const scope = host 42 | ? host._scope 43 | : this._scope 44 | this.unlink = context.$compile( 45 | content, host, scope, this._frag 46 | ) 47 | } 48 | if (content) { 49 | replace(this.el, content) 50 | } else { 51 | remove(this.el) 52 | } 53 | }, 54 | 55 | fallback () { 56 | this.compile(extractContent(this.el, true), this.vm) 57 | }, 58 | 59 | unbind () { 60 | if (this.unlink) { 61 | this.unlink() 62 | } 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /test/unit/specs/util/component_spec.js: -------------------------------------------------------------------------------- 1 | var _ = require('src/util') 2 | 3 | describe('Util - component', function () { 4 | it('checkComponentAttr', function () { 5 | var el = document.createElement('component') 6 | var mockOptions = { components: { 7 | foo: {} 8 | }} 9 | 10 | // with no is attr 11 | var res = _.checkComponentAttr(el, mockOptions) 12 | expect(res).toBeUndefined() 13 | 14 | // static 15 | el.setAttribute('is', 'foo') 16 | res = _.checkComponentAttr(el, mockOptions) 17 | expect(res.id).toBe('foo') 18 | expect(res.dynamic).toBeFalsy() 19 | 20 | // 21 | el.setAttribute(':is', 'foo') 22 | res = _.checkComponentAttr(el, mockOptions) 23 | expect(res.id).toBe('foo') 24 | expect(res.dynamic).toBe(true) 25 | 26 | // 27 | el = document.createElement('test') 28 | el.setAttribute('is', 'foo') 29 | res = _.checkComponentAttr(el, mockOptions) 30 | expect(res.id).toBe('foo') 31 | expect(res.dynamic).toBeUndefined() 32 | 33 | // 34 | el = document.createElement('test') 35 | el.setAttribute(':is', 'foo') 36 | res = _.checkComponentAttr(el, mockOptions) 37 | expect(res.id).toBe('foo') 38 | expect(res.dynamic).toBe(true) 39 | 40 | // custom element, not defined 41 | el = document.createElement('test') 42 | res = _.checkComponentAttr(el, mockOptions) 43 | expect(res).toBeUndefined() 44 | 45 | // custom element, defined 46 | el = document.createElement('foo') 47 | res = _.checkComponentAttr(el, mockOptions) 48 | expect(res.id).toBe('foo') 49 | 50 | // is on undefined custom element 51 | // should be preserved in case it is a native custom element usage 52 | el = document.createElement('test2') 53 | el.setAttribute('is', 'bar') 54 | res = _.checkComponentAttr(el, mockOptions) 55 | expect(res).toBeUndefined() 56 | }) 57 | }) 58 | -------------------------------------------------------------------------------- /src/instance/api/lifecycle.js: -------------------------------------------------------------------------------- 1 | import { warn, query, inDoc } from '../../util/index' 2 | import { compile } from '../../compiler/index' 3 | 4 | export default function (Vue) { 5 | /** 6 | * Set instance target element and kick off the compilation 7 | * process. The passed in `el` can be a selector string, an 8 | * existing Element, or a DocumentFragment (for block 9 | * instances). 10 | * 11 | * @param {Element|DocumentFragment|string} el 12 | * @public 13 | */ 14 | 15 | Vue.prototype.$mount = function (el) { 16 | if (this._isCompiled) { 17 | process.env.NODE_ENV !== 'production' && warn( 18 | '$mount() should be called only once.', 19 | this 20 | ) 21 | return 22 | } 23 | el = query(el) 24 | if (!el) { 25 | el = document.createElement('div') 26 | } 27 | this._compile(el) 28 | this._initDOMHooks() 29 | if (inDoc(this.$el)) { 30 | this._callHook('attached') 31 | ready.call(this) 32 | } else { 33 | this.$once('hook:attached', ready) 34 | } 35 | return this 36 | } 37 | 38 | /** 39 | * Mark an instance as ready. 40 | */ 41 | 42 | function ready () { 43 | this._isAttached = true 44 | this._isReady = true 45 | this._callHook('ready') 46 | } 47 | 48 | /** 49 | * Teardown the instance, simply delegate to the internal 50 | * _destroy. 51 | * 52 | * @param {Boolean} remove 53 | * @param {Boolean} deferCleanup 54 | */ 55 | 56 | Vue.prototype.$destroy = function (remove, deferCleanup) { 57 | this._destroy(remove, deferCleanup) 58 | } 59 | 60 | /** 61 | * Partially compile a piece of DOM and return a 62 | * decompile function. 63 | * 64 | * @param {Element|DocumentFragment} el 65 | * @param {Vue} [host] 66 | * @param {Object} [scope] 67 | * @param {Fragment} [frag] 68 | * @return {Function} 69 | */ 70 | 71 | Vue.prototype.$compile = function (el, host, scope, frag) { 72 | return compile(el, this.$options, true)( 73 | this, el, host, scope, frag 74 | ) 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /test/unit/specs/cache_spec.js: -------------------------------------------------------------------------------- 1 | var Cache = require('src/cache') 2 | 3 | /** 4 | * Debug function to assert cache state 5 | * 6 | * @param {Cache} cache 7 | */ 8 | 9 | function toString (cache) { 10 | var s = '' 11 | var entry = cache.head 12 | while (entry) { 13 | s += String(entry.key) + ':' + entry.value 14 | entry = entry.newer 15 | if (entry) { 16 | s += ' < ' 17 | } 18 | } 19 | return s 20 | } 21 | 22 | describe('Cache', function () { 23 | var c = new Cache(4) 24 | 25 | it('put', function () { 26 | c.put('adam', 29) 27 | c.put('john', 26) 28 | c.put('angela', 24) 29 | c.put('bob', 48) 30 | expect(c.size).toBe(4) 31 | expect(toString(c)).toBe('adam:29 < john:26 < angela:24 < bob:48') 32 | }) 33 | 34 | it('put with same key', function () { 35 | var same = new Cache(4) 36 | same.put('john', 29) 37 | same.put('john', 26) 38 | same.put('john', 24) 39 | same.put('john', 48) 40 | expect(same.size).toBe(1) 41 | expect(toString(same)).toBe('john:48') 42 | }) 43 | 44 | it('get', function () { 45 | expect(c.get('adam')).toBe(29) 46 | expect(c.get('john')).toBe(26) 47 | expect(c.get('angela')).toBe(24) 48 | expect(c.get('bob')).toBe(48) 49 | expect(toString(c)).toBe('adam:29 < john:26 < angela:24 < bob:48') 50 | 51 | expect(c.get('angela')).toBe(24) 52 | // angela should now be the tail 53 | expect(toString(c)).toBe('adam:29 < john:26 < bob:48 < angela:24') 54 | }) 55 | 56 | it('expire', function () { 57 | c.put('ygwie', 81) 58 | expect(c.size).toBe(4) 59 | expect(toString(c)).toBe('john:26 < bob:48 < angela:24 < ygwie:81') 60 | expect(c.get('adam')).toBeUndefined() 61 | }) 62 | 63 | it('shift', function () { 64 | var shift = new Cache(4) 65 | shift.put('adam', 29) 66 | shift.put('john', 26) 67 | shift.put('angela', 24) 68 | shift.put('bob', 48) 69 | 70 | shift.shift() 71 | shift.shift() 72 | shift.shift() 73 | expect(shift.size).toBe(1) 74 | expect(toString(shift)).toBe('bob:48') 75 | }) 76 | }) 77 | -------------------------------------------------------------------------------- /test/e2e/svg.js: -------------------------------------------------------------------------------- 1 | /* global stats, valueToPoint */ 2 | 3 | casper.test.begin('svg', 18, function (test) { 4 | casper 5 | .start('examples/svg/index.html') 6 | .then(function () { 7 | test.assertElementCount('g', 1) 8 | test.assertElementCount('polygon', 1) 9 | test.assertElementCount('circle', 1) 10 | test.assertElementCount('text', 6) 11 | test.assertElementCount('label', 6) 12 | test.assertElementCount('button', 7) 13 | test.assertElementCount('input[type="range"]', 6) 14 | test.assertEval(function () { 15 | var points = stats.map(function (stat, i) { 16 | var point = valueToPoint(stat.value, i, 6) 17 | return point.x + ',' + point.y 18 | }).join(' ') 19 | return document.querySelector('polygon').attributes[0].value === points 20 | }) 21 | }) 22 | .thenClick('button', function () { 23 | test.assertElementCount('text', 5) 24 | test.assertElementCount('label', 5) 25 | test.assertElementCount('button', 6) 26 | test.assertElementCount('input[type="range"]', 5) 27 | test.assertEval(function () { 28 | var points = stats.map(function (stat, i) { 29 | var point = valueToPoint(stat.value, i, 5) 30 | return point.x + ',' + point.y 31 | }).join(' ') 32 | return document.querySelector('polygon').attributes[0].value === points 33 | }) 34 | }) 35 | .then(function () { 36 | this.fill('#add', { 37 | newlabel: 'foo' 38 | }) 39 | }) 40 | .thenClick('#add > button', function () { 41 | test.assertElementCount('text', 6) 42 | test.assertElementCount('label', 6) 43 | test.assertElementCount('button', 7) 44 | test.assertElementCount('input[type="range"]', 6) 45 | test.assertEval(function () { 46 | var points = stats.map(function (stat, i) { 47 | var point = valueToPoint(stat.value, i, 6) 48 | return point.x + ',' + point.y 49 | }).join(' ') 50 | return document.querySelector('polygon').attributes[0].value === points 51 | }) 52 | }) 53 | // run 54 | .run(function () { 55 | test.done() 56 | }) 57 | }) 58 | -------------------------------------------------------------------------------- /examples/commits/app.js: -------------------------------------------------------------------------------- 1 | var apiURL = 'https://api.github.com/repos/vuejs/vue/commits?per_page=3&sha=' 2 | var isPhantom = navigator.userAgent.indexOf('PhantomJS') > -1 3 | 4 | /** 5 | * Test mocks 6 | */ 7 | 8 | var mocks = { 9 | master: [{sha:'111111111111', commit: {message:'one', author:{name:'Evan',date:'2014-10-15T13:52:58Z'}}},{sha:'111111111111', commit: {message:'hi', author:{name:'Evan',date:'2014-10-15T13:52:58Z'}}},{sha:'111111111111', commit: {message:'hi', author:{name:'Evan',date:'2014-10-15T13:52:58Z'}}}], 10 | dev: [{sha:'222222222222', commit: {message:'two', author:{name:'Evan',date:'2014-10-15T13:52:58Z'}}},{sha:'111111111111', commit: {message:'hi', author:{name:'Evan',date:'2014-10-15T13:52:58Z'}}},{sha:'111111111111', commit: {message:'hi', author:{name:'Evan',date:'2014-10-15T13:52:58Z'}}}] 11 | } 12 | 13 | function mockData () { 14 | this.commits = mocks[this.currentBranch] 15 | } 16 | 17 | /** 18 | * Actual demo 19 | */ 20 | 21 | var demo = new Vue({ 22 | 23 | el: '#demo', 24 | 25 | data: { 26 | branches: ['master', 'dev'], 27 | currentBranch: 'master', 28 | commits: null 29 | }, 30 | 31 | created: function () { 32 | this.fetchData() 33 | }, 34 | 35 | watch: { 36 | currentBranch: 'fetchData' 37 | }, 38 | 39 | filters: { 40 | truncate: function (v) { 41 | var newline = v.indexOf('\n') 42 | return newline > 0 ? v.slice(0, newline) : v 43 | }, 44 | formatDate: function (v) { 45 | return v.replace(/T|Z/g, ' ') 46 | } 47 | }, 48 | 49 | methods: { 50 | fetchData: function () { 51 | // CasperJS fails at cross-domain XHR even with 52 | // --web-security=no, have to mock data here. 53 | if (isPhantom) { 54 | return mockData.call(this) 55 | } 56 | var xhr = new XMLHttpRequest() 57 | var self = this 58 | xhr.open('GET', apiURL + self.currentBranch) 59 | xhr.onload = function () { 60 | self.commits = JSON.parse(xhr.responseText) 61 | console.log(self.commits[0].html_url) 62 | } 63 | xhr.send() 64 | } 65 | } 66 | }) 67 | -------------------------------------------------------------------------------- /src/observer/array.js: -------------------------------------------------------------------------------- 1 | import { def, indexOf } from '../util/index' 2 | 3 | const arrayProto = Array.prototype 4 | export const arrayMethods = Object.create(arrayProto) 5 | 6 | /** 7 | * Intercept mutating methods and emit events 8 | */ 9 | 10 | ;[ 11 | 'push', 12 | 'pop', 13 | 'shift', 14 | 'unshift', 15 | 'splice', 16 | 'sort', 17 | 'reverse' 18 | ] 19 | .forEach(function (method) { 20 | // cache original method 21 | var original = arrayProto[method] 22 | def(arrayMethods, method, function mutator () { 23 | // avoid leaking arguments: 24 | // http://jsperf.com/closure-with-arguments 25 | var i = arguments.length 26 | var args = new Array(i) 27 | while (i--) { 28 | args[i] = arguments[i] 29 | } 30 | var result = original.apply(this, args) 31 | var ob = this.__ob__ 32 | var inserted 33 | switch (method) { 34 | case 'push': 35 | inserted = args 36 | break 37 | case 'unshift': 38 | inserted = args 39 | break 40 | case 'splice': 41 | inserted = args.slice(2) 42 | break 43 | } 44 | if (inserted) ob.observeArray(inserted) 45 | // notify change 46 | ob.dep.notify() 47 | return result 48 | }) 49 | }) 50 | 51 | /** 52 | * Swap the element at the given index with a new value 53 | * and emits corresponding event. 54 | * 55 | * @param {Number} index 56 | * @param {*} val 57 | * @return {*} - replaced element 58 | */ 59 | 60 | def( 61 | arrayProto, 62 | '$set', 63 | function $set (index, val) { 64 | if (index >= this.length) { 65 | this.length = Number(index) + 1 66 | } 67 | return this.splice(index, 1, val)[0] 68 | } 69 | ) 70 | 71 | /** 72 | * Convenience method to remove the element at given index or target element reference. 73 | * 74 | * @param {*} item 75 | */ 76 | 77 | def( 78 | arrayProto, 79 | '$remove', 80 | function $remove (item) { 81 | /* istanbul ignore if */ 82 | if (!this.length) return 83 | var index = indexOf(this, item) 84 | if (index > -1) { 85 | return this.splice(index, 1) 86 | } 87 | } 88 | ) 89 | -------------------------------------------------------------------------------- /src/transition/index.js: -------------------------------------------------------------------------------- 1 | import { 2 | before, 3 | remove, 4 | transitionEndEvent 5 | } from '../util/index' 6 | 7 | /** 8 | * Append with transition. 9 | * 10 | * @param {Element} el 11 | * @param {Element} target 12 | * @param {Vue} vm 13 | * @param {Function} [cb] 14 | */ 15 | 16 | export function appendWithTransition (el, target, vm, cb) { 17 | applyTransition(el, 1, function () { 18 | target.appendChild(el) 19 | }, vm, cb) 20 | } 21 | 22 | /** 23 | * InsertBefore with transition. 24 | * 25 | * @param {Element} el 26 | * @param {Element} target 27 | * @param {Vue} vm 28 | * @param {Function} [cb] 29 | */ 30 | 31 | export function beforeWithTransition (el, target, vm, cb) { 32 | applyTransition(el, 1, function () { 33 | before(el, target) 34 | }, vm, cb) 35 | } 36 | 37 | /** 38 | * Remove with transition. 39 | * 40 | * @param {Element} el 41 | * @param {Vue} vm 42 | * @param {Function} [cb] 43 | */ 44 | 45 | export function removeWithTransition (el, vm, cb) { 46 | applyTransition(el, -1, function () { 47 | remove(el) 48 | }, vm, cb) 49 | } 50 | 51 | /** 52 | * Apply transitions with an operation callback. 53 | * 54 | * @param {Element} el 55 | * @param {Number} direction 56 | * 1: enter 57 | * -1: leave 58 | * @param {Function} op - the actual DOM operation 59 | * @param {Vue} vm 60 | * @param {Function} [cb] 61 | */ 62 | 63 | export function applyTransition (el, direction, op, vm, cb) { 64 | var transition = el.__v_trans 65 | if ( 66 | !transition || 67 | // skip if there are no js hooks and CSS transition is 68 | // not supported 69 | (!transition.hooks && !transitionEndEvent) || 70 | // skip transitions for initial compile 71 | !vm._isCompiled || 72 | // if the vm is being manipulated by a parent directive 73 | // during the parent's compilation phase, skip the 74 | // animation. 75 | (vm.$parent && !vm.$parent._isCompiled) 76 | ) { 77 | op() 78 | if (cb) cb() 79 | return 80 | } 81 | var action = direction > 0 ? 'enter' : 'leave' 82 | transition[action](op, cb) 83 | } 84 | -------------------------------------------------------------------------------- /test/e2e/tree.js: -------------------------------------------------------------------------------- 1 | casper.test.begin('tree', 22, function (test) { 2 | casper 3 | .start('examples/tree/index.html') 4 | .then(function () { 5 | test.assertElementCount('.item', 12) 6 | test.assertElementCount('.item > ul', 4) 7 | test.assertNotVisible('#demo li ul') 8 | test.assertSelectorHasText('#demo li div span', '[+]') 9 | }) 10 | .thenClick('.bold', function () { 11 | test.assertVisible('#demo ul') 12 | test.assertSelectorHasText('#demo li div span', '[-]') 13 | test.assertSelectorHasText('#demo ul > .item:nth-child(1)', 'hello') 14 | test.assertSelectorHasText('#demo ul > .item:nth-child(2)', 'wat') 15 | test.assertSelectorHasText('#demo ul > .item:nth-child(3)', 'child folder') 16 | test.assertSelectorHasText('#demo ul > .item:nth-child(3)', '[+]') 17 | test.assertEval(function () { 18 | return document.querySelector('#demo li ul').children.length === 4 19 | }) 20 | }) 21 | .thenClick('#demo ul .bold', function () { 22 | test.assertVisible('#demo ul ul') 23 | test.assertSelectorHasText('#demo ul > .item:nth-child(3)', '[-]') 24 | test.assertEval(function () { 25 | return document.querySelector('#demo ul ul').children.length === 5 26 | }) 27 | }) 28 | .thenClick('.bold', function () { 29 | test.assertNotVisible('#demo ul') 30 | test.assertSelectorHasText('#demo li div span', '[+]') 31 | }) 32 | .thenClick('.bold', function () { 33 | test.assertVisible('#demo ul') 34 | test.assertSelectorHasText('#demo li div span', '[-]') 35 | }) 36 | .then(function () { 37 | casper.mouseEvent('dblclick', '#demo ul > .item div') 38 | }) 39 | .then(function () { 40 | test.assertElementCount('.item', 13) 41 | test.assertElementCount('.item > ul', 5) 42 | test.assertSelectorHasText('#demo ul > .item:nth-child(1)', '[-]') 43 | test.assertEval(function () { 44 | var firstItem = document.querySelector('#demo ul > .item:nth-child(1)') 45 | var ul = firstItem.querySelector('ul') 46 | return ul.children.length === 2 47 | }) 48 | }) 49 | // run 50 | .run(function () { 51 | test.done() 52 | }) 53 | }) 54 | -------------------------------------------------------------------------------- /test/unit/specs/index.js: -------------------------------------------------------------------------------- 1 | // set some global Vue options 2 | var Vue = require('src') 3 | Vue.options.replace = false 4 | Vue.config.silent = true 5 | 6 | /** 7 | * Because Vue's internal modules reference the warn function 8 | * from different modules (some from util and some from debug), 9 | * we need to normalize the warn check into a few global 10 | * utility functions. 11 | */ 12 | 13 | var _ = require('src/util') 14 | var __ = require('src/util/debug') 15 | var scope = typeof window === 'undefined' 16 | ? global 17 | : window 18 | 19 | scope.getWarnCount = function () { 20 | return _.warn.calls.count() + __.warn.calls.count() 21 | } 22 | 23 | function hasWarned (msg) { 24 | var count = _.warn.calls.count() 25 | var args 26 | while (count--) { 27 | args = _.warn.calls.argsFor(count) 28 | if (args.some(containsMsg)) { 29 | return true 30 | } 31 | } 32 | 33 | count = __.warn.calls.count() 34 | while (count--) { 35 | args = __.warn.calls.argsFor(count) 36 | if (args.some(containsMsg)) { 37 | return true 38 | } 39 | } 40 | 41 | function containsMsg (arg) { 42 | if (arg instanceof Error) throw arg 43 | return typeof arg === 'string' && arg.indexOf(msg) > -1 44 | } 45 | } 46 | 47 | // define custom matcher for warnings 48 | beforeEach(function () { 49 | spyOn(_, 'warn') 50 | spyOn(__, 'warn') 51 | jasmine.addMatchers({ 52 | toHaveBeenWarned: function () { 53 | return { 54 | compare: function (msg) { 55 | var warned = Array.isArray(msg) 56 | ? msg.some(hasWarned) 57 | : hasWarned(msg) 58 | return { 59 | pass: warned, 60 | message: warned 61 | ? 'Expected message "' + msg + '" not to have been warned' 62 | : 'Expected message "' + msg + '" to have been warned' 63 | } 64 | } 65 | } 66 | } 67 | }) 68 | }) 69 | 70 | // shim process 71 | scope.process = { 72 | env: { 73 | NODE_ENV: 'development' 74 | } 75 | } 76 | 77 | // require all test files 78 | var testsContext = require.context('.', true, /_spec$/) 79 | testsContext.keys().forEach(testsContext) 80 | -------------------------------------------------------------------------------- /src/directives/internal/prop.js: -------------------------------------------------------------------------------- 1 | // NOTE: the prop internal directive is compiled and linked 2 | // during _initProps(), before the created hook is called. 3 | // The purpose is to make the initial prop values available 4 | // inside `created` hooks and `data` functions. 5 | 6 | import Watcher from '../../watcher' 7 | import config from '../../config' 8 | import { initProp, updateProp } from '../../compiler/compile-props' 9 | 10 | const bindingModes = config._propBindingModes 11 | 12 | export default { 13 | 14 | bind () { 15 | const child = this.vm 16 | const parent = child._context 17 | // passed in from compiler directly 18 | const prop = this.descriptor.prop 19 | const childKey = prop.path 20 | const parentKey = prop.parentPath 21 | const twoWay = prop.mode === bindingModes.TWO_WAY 22 | 23 | const parentWatcher = this.parentWatcher = new Watcher( 24 | parent, 25 | parentKey, 26 | function (val) { 27 | updateProp(child, prop, val) 28 | }, { 29 | twoWay: twoWay, 30 | filters: prop.filters, 31 | // important: props need to be observed on the 32 | // v-for scope if present 33 | scope: this._scope 34 | } 35 | ) 36 | 37 | // set the child initial value. 38 | initProp(child, prop, parentWatcher.value) 39 | 40 | // setup two-way binding 41 | if (twoWay) { 42 | // important: defer the child watcher creation until 43 | // the created hook (after data observation) 44 | var self = this 45 | child.$once('pre-hook:created', function () { 46 | self.childWatcher = new Watcher( 47 | child, 48 | childKey, 49 | function (val) { 50 | parentWatcher.set(val) 51 | }, { 52 | // ensure sync upward before parent sync down. 53 | // this is necessary in cases e.g. the child 54 | // mutates a prop array, then replaces it. (#1683) 55 | sync: true 56 | } 57 | ) 58 | }) 59 | } 60 | }, 61 | 62 | unbind () { 63 | this.parentWatcher.teardown() 64 | if (this.childWatcher) { 65 | this.childWatcher.teardown() 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/directives/public/if.js: -------------------------------------------------------------------------------- 1 | import FragmentFactory from '../../fragment/factory' 2 | import { IF } from '../priorities' 3 | import { 4 | getAttr, 5 | remove, 6 | replace, 7 | createAnchor, 8 | warn 9 | } from '../../util/index' 10 | 11 | export default { 12 | 13 | priority: IF, 14 | terminal: true, 15 | 16 | bind () { 17 | var el = this.el 18 | if (!el.__vue__) { 19 | // check else block 20 | var next = el.nextElementSibling 21 | if (next && getAttr(next, 'v-else') !== null) { 22 | remove(next) 23 | this.elseEl = next 24 | } 25 | // check main block 26 | this.anchor = createAnchor('v-if') 27 | replace(el, this.anchor) 28 | } else { 29 | process.env.NODE_ENV !== 'production' && warn( 30 | 'v-if="' + this.expression + '" cannot be ' + 31 | 'used on an instance root element.', 32 | this.vm 33 | ) 34 | this.invalid = true 35 | } 36 | }, 37 | 38 | update (value) { 39 | if (this.invalid) return 40 | if (value) { 41 | if (!this.frag) { 42 | this.insert() 43 | } 44 | } else { 45 | this.remove() 46 | } 47 | }, 48 | 49 | insert () { 50 | if (this.elseFrag) { 51 | this.elseFrag.remove() 52 | this.elseFrag = null 53 | } 54 | // lazy init factory 55 | if (!this.factory) { 56 | this.factory = new FragmentFactory(this.vm, this.el) 57 | } 58 | this.frag = this.factory.create(this._host, this._scope, this._frag) 59 | this.frag.before(this.anchor) 60 | }, 61 | 62 | remove () { 63 | if (this.frag) { 64 | this.frag.remove() 65 | this.frag = null 66 | } 67 | if (this.elseEl && !this.elseFrag) { 68 | if (!this.elseFactory) { 69 | this.elseFactory = new FragmentFactory( 70 | this.elseEl._context || this.vm, 71 | this.elseEl 72 | ) 73 | } 74 | this.elseFrag = this.elseFactory.create(this._host, this._scope, this._frag) 75 | this.elseFrag.before(this.anchor) 76 | } 77 | }, 78 | 79 | unbind () { 80 | if (this.frag) { 81 | this.frag.destroy() 82 | } 83 | if (this.elseFrag) { 84 | this.elseFrag.destroy() 85 | } 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/compiler/resolve-slots.js: -------------------------------------------------------------------------------- 1 | import { parseTemplate } from '../parsers/template' 2 | import { 3 | isTemplate, 4 | toArray, 5 | getBindAttr, 6 | warn 7 | } from '../util/index' 8 | 9 | /** 10 | * Scan and determine slot content distribution. 11 | * We do this during transclusion instead at compile time so that 12 | * the distribution is decoupled from the compilation order of 13 | * the slots. 14 | * 15 | * @param {Element|DocumentFragment} template 16 | * @param {Element} content 17 | * @param {Vue} vm 18 | */ 19 | 20 | export function resolveSlots (vm, content) { 21 | if (!content) { 22 | return 23 | } 24 | var contents = vm._slotContents = Object.create(null) 25 | var el, name 26 | for (var i = 0, l = content.children.length; i < l; i++) { 27 | el = content.children[i] 28 | /* eslint-disable no-cond-assign */ 29 | if (name = el.getAttribute('slot')) { 30 | (contents[name] || (contents[name] = [])).push(el) 31 | } 32 | /* eslint-enable no-cond-assign */ 33 | if (process.env.NODE_ENV !== 'production' && getBindAttr(el, 'slot')) { 34 | warn('The "slot" attribute must be static.', vm.$parent) 35 | } 36 | } 37 | for (name in contents) { 38 | contents[name] = extractFragment(contents[name], content) 39 | } 40 | if (content.hasChildNodes()) { 41 | const nodes = content.childNodes 42 | if ( 43 | nodes.length === 1 && 44 | nodes[0].nodeType === 3 && 45 | !nodes[0].data.trim() 46 | ) { 47 | return 48 | } 49 | contents['default'] = extractFragment(content.childNodes, content) 50 | } 51 | } 52 | 53 | /** 54 | * Extract qualified content nodes from a node list. 55 | * 56 | * @param {NodeList} nodes 57 | * @return {DocumentFragment} 58 | */ 59 | 60 | function extractFragment (nodes, parent) { 61 | var frag = document.createDocumentFragment() 62 | nodes = toArray(nodes) 63 | for (var i = 0, l = nodes.length; i < l; i++) { 64 | var node = nodes[i] 65 | if ( 66 | isTemplate(node) && 67 | !node.hasAttribute('v-if') && 68 | !node.hasAttribute('v-for') 69 | ) { 70 | parent.removeChild(node) 71 | node = parseTemplate(node, true) 72 | } 73 | frag.appendChild(node) 74 | } 75 | return frag 76 | } 77 | -------------------------------------------------------------------------------- /test/unit/specs/instance/misc_spec.js: -------------------------------------------------------------------------------- 1 | var Vue = require('src') 2 | 3 | describe('misc', function () { 4 | describe('_applyFilters', function () { 5 | var vm = new Vue({ 6 | data: { 7 | msg: 'bar' 8 | }, 9 | filters: { 10 | read: function (v, arg) { 11 | return v + ' read:' + arg 12 | }, 13 | read2: { 14 | read: function (v, arg) { 15 | return v + ' read2:' + arg 16 | } 17 | }, 18 | write: { 19 | write: function (v, oldV) { 20 | return v + ' ' + oldV 21 | } 22 | }, 23 | duplex1: { 24 | read: function (v) { 25 | return v.split('').reverse().join('') 26 | }, 27 | write: function (v) { 28 | return v.split('').reverse().join('') 29 | } 30 | }, 31 | duplex2: { 32 | read: function (v) { 33 | return v + 'hi' 34 | }, 35 | write: function (v) { 36 | return v.replace('hi', '') 37 | } 38 | } 39 | } 40 | }) 41 | 42 | it('read', function () { 43 | var filters = [ 44 | { name: 'read', args: [{dynamic: false, value: 'foo'}] }, 45 | { name: 'read2', args: [{dynamic: true, value: 'msg'}] } 46 | ] 47 | var val = vm._applyFilters('test', null, filters, false) 48 | expect(val).toBe('test read:foo read2:bar') 49 | }) 50 | 51 | it('write', function () { 52 | var filters = [ 53 | { name: 'write' } 54 | ] 55 | var val = vm._applyFilters('test', 'oldTest', filters, true) 56 | expect(val).toBe('test oldTest') 57 | }) 58 | 59 | it('chained read + write', function () { 60 | var filters = [ 61 | { name: 'duplex1' }, 62 | { name: 'duplex2' } 63 | ] 64 | var val = vm._applyFilters('test', 'oldTest', filters) 65 | expect(val).toBe('tsethi') 66 | val = vm._applyFilters('tsethi', 'oldTest', filters, true) 67 | expect(val).toBe('test') 68 | }) 69 | 70 | it('warn not found', function () { 71 | vm._applyFilters('waldo', null, [{name: 'nemo'}]) 72 | expect('Failed to resolve filter').toHaveBeenWarned() 73 | }) 74 | }) 75 | }) 76 | -------------------------------------------------------------------------------- /examples/modal/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Vue.js Modal Example 6 | 7 | 8 | 9 | 10 | 11 | 41 | 42 | 55 | 56 | 57 |
58 | 59 | 60 | 61 | 65 |

custom header

66 |
67 |
68 | 69 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /examples/svg/svg.js: -------------------------------------------------------------------------------- 1 | // The raw data to observe 2 | var stats = [ 3 | { label: 'A', value: 100 }, 4 | { label: 'B', value: 100 }, 5 | { label: 'C', value: 100 }, 6 | { label: 'D', value: 100 }, 7 | { label: 'E', value: 100 }, 8 | { label: 'F', value: 100 } 9 | ] 10 | 11 | // A resusable polygon graph component 12 | Vue.component('polygraph', { 13 | props: ['stats'], 14 | template: '#polygraph-template', 15 | replace: true, 16 | computed: { 17 | // a computed property for the polygon's points 18 | points: function () { 19 | var total = this.stats.length 20 | return this.stats.map(function (stat, i) { 21 | var point = valueToPoint(stat.value, i, total) 22 | return point.x + ',' + point.y 23 | }).join(' ') 24 | } 25 | }, 26 | components: { 27 | // a sub component for the labels 28 | 'axis-label': { 29 | props: { 30 | stat: Object, 31 | index: Number, 32 | total: Number 33 | }, 34 | template: '#axis-label-template', 35 | replace: true, 36 | computed: { 37 | point: function () { 38 | return valueToPoint( 39 | +this.stat.value + 10, 40 | this.index, 41 | this.total 42 | ) 43 | } 44 | } 45 | } 46 | } 47 | }) 48 | 49 | // math helper... 50 | function valueToPoint (value, index, total) { 51 | var x = 0 52 | var y = -value * 0.8 53 | var angle = Math.PI * 2 / total * index 54 | var cos = Math.cos(angle) 55 | var sin = Math.sin(angle) 56 | var tx = x * cos - y * sin + 100 57 | var ty = x * sin + y * cos + 100 58 | return { 59 | x: tx, 60 | y: ty 61 | } 62 | } 63 | 64 | // bootstrap the demo 65 | new Vue({ 66 | el: '#demo', 67 | data: { 68 | newLabel: '', 69 | stats: stats 70 | }, 71 | methods: { 72 | add: function (e) { 73 | e.preventDefault() 74 | if (!this.newLabel) return 75 | this.stats.push({ 76 | label: this.newLabel, 77 | value: 100 78 | }) 79 | this.newLabel = '' 80 | }, 81 | remove: function (stat) { 82 | if (this.stats.length > 3) { 83 | this.stats.$remove(stat) 84 | } else { 85 | alert('Can\'t delete more!') 86 | } 87 | } 88 | } 89 | }) 90 | -------------------------------------------------------------------------------- /src/config.js: -------------------------------------------------------------------------------- 1 | import { compileRegex } from './parsers/text' 2 | 3 | let delimiters = ['{{', '}}'] 4 | let unsafeDelimiters = ['{{{', '}}}'] 5 | 6 | const config = { 7 | 8 | /** 9 | * Whether to print debug messages. 10 | * Also enables stack trace for warnings. 11 | * 12 | * @type {Boolean} 13 | */ 14 | 15 | debug: false, 16 | 17 | /** 18 | * Whether to suppress warnings. 19 | * 20 | * @type {Boolean} 21 | */ 22 | 23 | silent: false, 24 | 25 | /** 26 | * Whether to use async rendering. 27 | */ 28 | 29 | async: true, 30 | 31 | /** 32 | * Whether to warn against errors caught when evaluating 33 | * expressions. 34 | */ 35 | 36 | warnExpressionErrors: true, 37 | 38 | /** 39 | * Whether to allow devtools inspection. 40 | * Disabled by default in production builds. 41 | */ 42 | 43 | devtools: process.env.NODE_ENV !== 'production', 44 | 45 | /** 46 | * Internal flag to indicate the delimiters have been 47 | * changed. 48 | * 49 | * @type {Boolean} 50 | */ 51 | 52 | _delimitersChanged: true, 53 | 54 | /** 55 | * List of asset types that a component can own. 56 | * 57 | * @type {Array} 58 | */ 59 | 60 | _assetTypes: [ 61 | 'component', 62 | 'directive', 63 | 'elementDirective', 64 | 'filter', 65 | 'transition', 66 | 'partial' 67 | ], 68 | 69 | /** 70 | * prop binding modes 71 | */ 72 | 73 | _propBindingModes: { 74 | ONE_WAY: 0, 75 | TWO_WAY: 1, 76 | ONE_TIME: 2 77 | }, 78 | 79 | /** 80 | * Max circular updates allowed in a batcher flush cycle. 81 | */ 82 | 83 | _maxUpdateCount: 100, 84 | 85 | /** 86 | * Interpolation delimiters. Changing these would trigger 87 | * the text parser to re-compile the regular expressions. 88 | * 89 | * @type {Array} 90 | */ 91 | 92 | get delimiters () { 93 | return delimiters 94 | }, 95 | 96 | set delimiters (val) { 97 | delimiters = val 98 | compileRegex() 99 | }, 100 | 101 | get unsafeDelimiters () { 102 | return unsafeDelimiters 103 | }, 104 | 105 | set unsafeDelimiters (val) { 106 | unsafeDelimiters = val 107 | compileRegex() 108 | } 109 | } 110 | 111 | export default config 112 | -------------------------------------------------------------------------------- /test/unit/specs/directives/public/for/for_ref_spec.js: -------------------------------------------------------------------------------- 1 | var Vue = require('src') 2 | var _ = Vue.util 3 | 4 | describe('v-for + ref', function () { 5 | var el 6 | beforeEach(function () { 7 | el = document.createElement('div') 8 | }) 9 | 10 | it('normal', function (done) { 11 | var vm = new Vue({ 12 | el: el, 13 | data: { items: [1, 2, 3, 4, 5] }, 14 | template: '', 15 | components: { 16 | test: { 17 | props: ['item'] 18 | } 19 | } 20 | }) 21 | expect(vm.$refs.test).toBeTruthy() 22 | expect(Array.isArray(vm.$refs.test)).toBe(true) 23 | expect(vm.$refs.test[0].item).toBe(1) 24 | expect(vm.$refs.test[4].item).toBe(5) 25 | vm.items = [] 26 | _.nextTick(function () { 27 | expect(vm.$refs.test.length).toBe(0) 28 | vm._directives[0].unbind() 29 | expect(vm.$refs.test).toBeNull() 30 | done() 31 | }) 32 | }) 33 | 34 | it('object', function (done) { 35 | var vm = new Vue({ 36 | el: el, 37 | data: { 38 | items: { 39 | a: 1, 40 | b: 2 41 | } 42 | }, 43 | template: '', 44 | components: { 45 | test: { 46 | props: ['item'] 47 | } 48 | } 49 | }) 50 | expect(vm.$refs.test).toBeTruthy() 51 | expect(_.isPlainObject(vm.$refs.test)).toBe(true) 52 | expect(vm.$refs.test.a.item).toBe(1) 53 | expect(vm.$refs.test.b.item).toBe(2) 54 | vm.items = { c: 3 } 55 | _.nextTick(function () { 56 | expect(Object.keys(vm.$refs.test).length).toBe(1) 57 | expect(vm.$refs.test.c.item).toBe(3) 58 | vm._directives[0].unbind() 59 | expect(vm.$refs.test).toBeNull() 60 | done() 61 | }) 62 | }) 63 | 64 | it('nested', function () { 65 | var vm = new Vue({ 66 | el: el, 67 | template: '', 68 | components: { 69 | c1: { 70 | template: '
' 71 | } 72 | } 73 | }) 74 | expect(vm.$refs.c1 instanceof Vue).toBe(true) 75 | expect(vm.$refs.c2).toBeUndefined() 76 | expect(Array.isArray(vm.$refs.c1.$refs.c2)).toBe(true) 77 | expect(vm.$refs.c1.$refs.c2.length).toBe(2) 78 | }) 79 | }) 80 | -------------------------------------------------------------------------------- /src/directives/public/model/index.js: -------------------------------------------------------------------------------- 1 | import { warn, resolveAsset } from '../../../util/index' 2 | import { MODEL } from '../../priorities' 3 | import text from './text' 4 | import radio from './radio' 5 | import select from './select' 6 | import checkbox from './checkbox' 7 | 8 | const handlers = { 9 | text, 10 | radio, 11 | select, 12 | checkbox 13 | } 14 | 15 | export default { 16 | 17 | priority: MODEL, 18 | twoWay: true, 19 | handlers: handlers, 20 | params: ['lazy', 'number', 'debounce'], 21 | 22 | /** 23 | * Possible elements: 24 | *