mirror of https://github.com/vuejs/vue.git
text parser started, features, optional oneway binding for input
This commit is contained in:
parent
f9077cfa6a
commit
7fd557ccc8
|
|
@ -1,5 +1,11 @@
|
||||||
WIP, playing with data binding
|
WIP, playing with data binding
|
||||||
|
|
||||||
|
- DOM based templates with precise and efficient manipulation
|
||||||
|
- Keep logic expressions outside of the templates.
|
||||||
|
- POJSO (plain old javascript objects) FTW.
|
||||||
|
- Auto dependency extraction for computed properties.
|
||||||
|
- Auto event delegation on repeated items.
|
||||||
|
|
||||||
### Template
|
### Template
|
||||||
|
|
||||||
### Controller
|
### Controller
|
||||||
|
|
|
||||||
4
TODO.md
4
TODO.md
|
|
@ -1,4 +1,4 @@
|
||||||
- parse textNodes
|
- parse textNodes
|
||||||
- more directives / filters
|
- sd-with
|
||||||
- sd-with
|
- standarized way to reuse components (sd-component?)
|
||||||
- nested properties in scope (kinda hard, maybe later)
|
- nested properties in scope (kinda hard, maybe later)
|
||||||
|
|
@ -8,7 +8,7 @@
|
||||||
"src/seed.js",
|
"src/seed.js",
|
||||||
"src/binding.js",
|
"src/binding.js",
|
||||||
"src/directive.js",
|
"src/directive.js",
|
||||||
"src/textnode-parser.js",
|
"src/text-parser.js",
|
||||||
"src/filters.js",
|
"src/filters.js",
|
||||||
"src/directives/index.js",
|
"src/directives/index.js",
|
||||||
"src/directives/each.js",
|
"src/directives/each.js",
|
||||||
|
|
|
||||||
|
|
@ -6,7 +6,8 @@
|
||||||
<script src="../dist/seed.js"></script>
|
<script src="../dist/seed.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body sd-controller="hello">
|
<body sd-controller="hello">
|
||||||
<span sd-text="hello" sd-style="text-transform:format"></span>
|
<input type="checkbox" sd-checked="ok" checked>
|
||||||
|
<span sd-text="hello" sd-style="text-transform:format" sd-if="ok"></span>
|
||||||
<script>
|
<script>
|
||||||
var Seed = require('seed')
|
var Seed = require('seed')
|
||||||
Seed.controller('hello', function (scope) {
|
Seed.controller('hello', function (scope) {
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,6 @@ Seed.controller('Todos', function (scope) {
|
||||||
scope.remaining = todos.reduce(function (count, todo) {
|
scope.remaining = todos.reduce(function (count, todo) {
|
||||||
return count + (todo.done ? 0 : 1)
|
return count + (todo.done ? 0 : 1)
|
||||||
}, 0)
|
}, 0)
|
||||||
scope.allDone = scope.remaining === 0
|
|
||||||
|
|
||||||
// computed properties ----------------------------------------------------
|
// computed properties ----------------------------------------------------
|
||||||
scope.total = {get: function () {
|
scope.total = {get: function () {
|
||||||
|
|
@ -30,6 +29,10 @@ Seed.controller('Todos', function (scope) {
|
||||||
return scope.remaining > 1 ? 'items' : 'item'
|
return scope.remaining > 1 ? 'items' : 'item'
|
||||||
}}
|
}}
|
||||||
|
|
||||||
|
scope.allDone = {get: function () {
|
||||||
|
return scope.remaining === 0
|
||||||
|
}}
|
||||||
|
|
||||||
// event handlers ---------------------------------------------------------
|
// event handlers ---------------------------------------------------------
|
||||||
scope.addTodo = function (e) {
|
scope.addTodo = function (e) {
|
||||||
var val = e.el.value
|
var val = e.el.value
|
||||||
|
|
@ -47,7 +50,6 @@ Seed.controller('Todos', function (scope) {
|
||||||
|
|
||||||
scope.updateCount = function (e) {
|
scope.updateCount = function (e) {
|
||||||
scope.remaining += e.scope.done ? -1 : 1
|
scope.remaining += e.scope.done ? -1 : 1
|
||||||
scope.allDone = scope.remaining === 0
|
|
||||||
}
|
}
|
||||||
|
|
||||||
scope.edit = function (e) {
|
scope.edit = function (e) {
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@
|
||||||
<input
|
<input
|
||||||
id="toggle-all"
|
id="toggle-all"
|
||||||
type="checkbox"
|
type="checkbox"
|
||||||
sd-checked="allDone"
|
sd-checked-oneway="allDone"
|
||||||
sd-on="change:toggleAll"
|
sd-on="change:toggleAll"
|
||||||
>
|
>
|
||||||
<ul id="todo-list">
|
<ul id="todo-list">
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,9 @@
|
||||||
module.exports = {
|
module.exports = {
|
||||||
prefix: 'sd',
|
prefix: 'sd',
|
||||||
|
interpolateTags: {
|
||||||
|
open: '{{',
|
||||||
|
close: '}}'
|
||||||
|
},
|
||||||
controllers: {},
|
controllers: {},
|
||||||
datum: {}
|
datum: {}
|
||||||
}
|
}
|
||||||
|
|
@ -7,13 +7,14 @@ var KEY_RE = /^[^\|<]+/,
|
||||||
FILTERS_RE = /\|[^\|<]+/g,
|
FILTERS_RE = /\|[^\|<]+/g,
|
||||||
FILTER_TOKEN_RE = /[^\s']+|'[^']+'/g,
|
FILTER_TOKEN_RE = /[^\s']+|'[^']+'/g,
|
||||||
INVERSE_RE = /^!/,
|
INVERSE_RE = /^!/,
|
||||||
NESTING_RE = /^\^+/
|
NESTING_RE = /^\^+/,
|
||||||
|
ONEWAY_RE = /-oneway$/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Directive class
|
* Directive class
|
||||||
* represents a single directive instance in the DOM
|
* represents a single directive instance in the DOM
|
||||||
*/
|
*/
|
||||||
function Directive (directiveName, expression) {
|
function Directive (directiveName, expression, oneway) {
|
||||||
|
|
||||||
var prop, directive = directives[directiveName]
|
var prop, directive = directives[directiveName]
|
||||||
if (typeof directive === 'function') {
|
if (typeof directive === 'function') {
|
||||||
|
|
@ -28,6 +29,7 @@ function Directive (directiveName, expression) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
this.oneway = !!oneway
|
||||||
this.directiveName = directiveName
|
this.directiveName = directiveName
|
||||||
this.expression = expression.trim()
|
this.expression = expression.trim()
|
||||||
this.rawKey = expression.match(KEY_RE)[0]
|
this.rawKey = expression.match(KEY_RE)[0]
|
||||||
|
|
@ -160,6 +162,11 @@ module.exports = {
|
||||||
if (dirname.indexOf(prefix) === -1) return null
|
if (dirname.indexOf(prefix) === -1) return null
|
||||||
dirname = dirname.slice(prefix.length + 1)
|
dirname = dirname.slice(prefix.length + 1)
|
||||||
|
|
||||||
|
var oneway = ONEWAY_RE.test(dirname)
|
||||||
|
if (oneway) {
|
||||||
|
dirname = dirname.slice(0, -7)
|
||||||
|
}
|
||||||
|
|
||||||
var dir = directives[dirname],
|
var dir = directives[dirname],
|
||||||
valid = KEY_RE.test(expression)
|
valid = KEY_RE.test(expression)
|
||||||
|
|
||||||
|
|
@ -167,7 +174,7 @@ module.exports = {
|
||||||
if (!valid) console.warn('invalid directive expression: ' + expression)
|
if (!valid) console.warn('invalid directive expression: ' + expression)
|
||||||
|
|
||||||
return dir && valid
|
return dir && valid
|
||||||
? new Directive(dirname, expression)
|
? new Directive(dirname, expression, oneway)
|
||||||
: null
|
: null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -55,6 +55,7 @@ module.exports = {
|
||||||
|
|
||||||
value: {
|
value: {
|
||||||
bind: function () {
|
bind: function () {
|
||||||
|
if (this.oneway) return
|
||||||
var el = this.el, self = this
|
var el = this.el, self = this
|
||||||
this.change = function () {
|
this.change = function () {
|
||||||
self.seed.scope[self.key] = el.value
|
self.seed.scope[self.key] = el.value
|
||||||
|
|
@ -65,12 +66,14 @@ module.exports = {
|
||||||
this.el.value = value
|
this.el.value = value
|
||||||
},
|
},
|
||||||
unbind: function () {
|
unbind: function () {
|
||||||
|
if (this.oneway) return
|
||||||
this.el.removeEventListener('change', this.change)
|
this.el.removeEventListener('change', this.change)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
checked: {
|
checked: {
|
||||||
bind: function () {
|
bind: function () {
|
||||||
|
if (this.oneway) return
|
||||||
var el = this.el, self = this
|
var el = this.el, self = this
|
||||||
this.change = function () {
|
this.change = function () {
|
||||||
self.seed.scope[self.key] = el.checked
|
self.seed.scope[self.key] = el.checked
|
||||||
|
|
@ -81,6 +84,7 @@ module.exports = {
|
||||||
this.el.checked = !!value
|
this.el.checked = !!value
|
||||||
},
|
},
|
||||||
unbind: function () {
|
unbind: function () {
|
||||||
|
if (this.oneway) return
|
||||||
this.el.removeEventListener('change', this.change)
|
this.el.removeEventListener('change', this.change)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
@ -88,7 +92,13 @@ module.exports = {
|
||||||
'if': {
|
'if': {
|
||||||
bind: function () {
|
bind: function () {
|
||||||
this.parent = this.el.parentNode
|
this.parent = this.el.parentNode
|
||||||
this.ref = this.el.nextSibling
|
this.ref = document.createComment('sd-if-' + this.key)
|
||||||
|
var next = this.el.nextSibling
|
||||||
|
if (next) {
|
||||||
|
this.parent.insertBefore(this.ref, next)
|
||||||
|
} else {
|
||||||
|
this.parent.appendChild(this.ref)
|
||||||
|
}
|
||||||
},
|
},
|
||||||
update: function (value) {
|
update: function (value) {
|
||||||
if (!value) {
|
if (!value) {
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,8 @@
|
||||||
var config = require('./config'),
|
var config = require('./config'),
|
||||||
Seed = require('./seed'),
|
Seed = require('./seed'),
|
||||||
directives = require('./directives'),
|
directives = require('./directives'),
|
||||||
filters = require('./filters')
|
filters = require('./filters'),
|
||||||
|
textParser = require('./text-parser')
|
||||||
|
|
||||||
var controllers = config.controllers,
|
var controllers = config.controllers,
|
||||||
datum = config.datum,
|
datum = config.datum,
|
||||||
|
|
@ -61,6 +62,7 @@ api.bootstrap = function (opts) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
textParser.buildRegex()
|
||||||
var el,
|
var el,
|
||||||
ctrlSlt = '[' + config.prefix + '-controller]',
|
ctrlSlt = '[' + config.prefix + '-controller]',
|
||||||
dataSlt = '[' + config.prefix + '-data]'
|
dataSlt = '[' + config.prefix + '-data]'
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,7 @@ var config = require('./config'),
|
||||||
Emitter = require('emitter'),
|
Emitter = require('emitter'),
|
||||||
Binding = require('./binding'),
|
Binding = require('./binding'),
|
||||||
Directive = require('./directive'),
|
Directive = require('./directive'),
|
||||||
TextNodeParser = require('./textnode-parser')
|
TextParser = require('./text-parser')
|
||||||
|
|
||||||
var slice = Array.prototype.slice,
|
var slice = Array.prototype.slice,
|
||||||
ctrlAttr = config.prefix + '-controller',
|
ctrlAttr = config.prefix + '-controller',
|
||||||
|
|
@ -122,6 +122,8 @@ Seed.prototype._compileNode = function (node, root) {
|
||||||
|
|
||||||
// parse if has attributes
|
// parse if has attributes
|
||||||
if (node.attributes && node.attributes.length) {
|
if (node.attributes && node.attributes.length) {
|
||||||
|
// forEach vs for loop perf comparison: http://jsperf.com/for-vs-foreach-case
|
||||||
|
// takeaway: not worth it to wrtie manual loops.
|
||||||
slice.call(node.attributes).forEach(function (attr) {
|
slice.call(node.attributes).forEach(function (attr) {
|
||||||
if (attr.name === ctrlAttr) return
|
if (attr.name === ctrlAttr) return
|
||||||
var valid = false
|
var valid = false
|
||||||
|
|
@ -151,7 +153,7 @@ Seed.prototype._compileNode = function (node, root) {
|
||||||
* Compile a text node
|
* Compile a text node
|
||||||
*/
|
*/
|
||||||
Seed.prototype._compileTextNode = function (node) {
|
Seed.prototype._compileTextNode = function (node) {
|
||||||
return TextNodeParser.parse(node)
|
return TextParser.parse(node)
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
var config = require('./config')
|
||||||
|
|
||||||
|
var ESCAPE_RE = /[-.*+?^${}()|[\]\/\\]/g,
|
||||||
|
BINDING_RE = undefined
|
||||||
|
|
||||||
|
function escapeRegex (val) {
|
||||||
|
return val.replace(ESCAPE_RE, '\\$&')
|
||||||
|
}
|
||||||
|
|
||||||
|
"this is {{cool}} hahah {{todo.but}} 123 {{total}}"
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
|
||||||
|
parse: function (node) {
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
buildRegex: function () {
|
||||||
|
var open = escapeRegex(config.interpolateTags.open),
|
||||||
|
close = escapeRegex(config.interpolateTags.close)
|
||||||
|
BINDING_RE = new RegExp(open + '(.*?)' + close)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,5 +0,0 @@
|
||||||
module.exports = {
|
|
||||||
parse: function (node) {
|
|
||||||
return node
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Loading…
Reference in New Issue