text parser started, features, optional oneway binding for input

This commit is contained in:
Evan You 2013-08-08 16:24:28 -04:00
parent f9077cfa6a
commit 7fd557ccc8
13 changed files with 71 additions and 19 deletions

View File

@ -1,5 +1,11 @@
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
### Controller

View File

@ -1,4 +1,4 @@
- parse textNodes
- more directives / filters
- sd-with
- sd-with
- standarized way to reuse components (sd-component?)
- nested properties in scope (kinda hard, maybe later)

View File

@ -8,7 +8,7 @@
"src/seed.js",
"src/binding.js",
"src/directive.js",
"src/textnode-parser.js",
"src/text-parser.js",
"src/filters.js",
"src/directives/index.js",
"src/directives/each.js",

View File

@ -6,7 +6,8 @@
<script src="../dist/seed.js"></script>
</head>
<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>
var Seed = require('seed')
Seed.controller('hello', function (scope) {

View File

@ -15,7 +15,6 @@ Seed.controller('Todos', function (scope) {
scope.remaining = todos.reduce(function (count, todo) {
return count + (todo.done ? 0 : 1)
}, 0)
scope.allDone = scope.remaining === 0
// computed properties ----------------------------------------------------
scope.total = {get: function () {
@ -30,6 +29,10 @@ Seed.controller('Todos', function (scope) {
return scope.remaining > 1 ? 'items' : 'item'
}}
scope.allDone = {get: function () {
return scope.remaining === 0
}}
// event handlers ---------------------------------------------------------
scope.addTodo = function (e) {
var val = e.el.value
@ -47,7 +50,6 @@ Seed.controller('Todos', function (scope) {
scope.updateCount = function (e) {
scope.remaining += e.scope.done ? -1 : 1
scope.allDone = scope.remaining === 0
}
scope.edit = function (e) {

View File

@ -24,7 +24,7 @@
<input
id="toggle-all"
type="checkbox"
sd-checked="allDone"
sd-checked-oneway="allDone"
sd-on="change:toggleAll"
>
<ul id="todo-list">

View File

@ -1,5 +1,9 @@
module.exports = {
prefix: 'sd',
interpolateTags: {
open: '{{',
close: '}}'
},
controllers: {},
datum: {}
}

View File

@ -7,13 +7,14 @@ var KEY_RE = /^[^\|<]+/,
FILTERS_RE = /\|[^\|<]+/g,
FILTER_TOKEN_RE = /[^\s']+|'[^']+'/g,
INVERSE_RE = /^!/,
NESTING_RE = /^\^+/
NESTING_RE = /^\^+/,
ONEWAY_RE = /-oneway$/
/*
* Directive class
* represents a single directive instance in the DOM
*/
function Directive (directiveName, expression) {
function Directive (directiveName, expression, oneway) {
var prop, directive = directives[directiveName]
if (typeof directive === 'function') {
@ -28,6 +29,7 @@ function Directive (directiveName, expression) {
}
}
this.oneway = !!oneway
this.directiveName = directiveName
this.expression = expression.trim()
this.rawKey = expression.match(KEY_RE)[0]
@ -160,6 +162,11 @@ module.exports = {
if (dirname.indexOf(prefix) === -1) return null
dirname = dirname.slice(prefix.length + 1)
var oneway = ONEWAY_RE.test(dirname)
if (oneway) {
dirname = dirname.slice(0, -7)
}
var dir = directives[dirname],
valid = KEY_RE.test(expression)
@ -167,7 +174,7 @@ module.exports = {
if (!valid) console.warn('invalid directive expression: ' + expression)
return dir && valid
? new Directive(dirname, expression)
? new Directive(dirname, expression, oneway)
: null
}
}

View File

@ -55,6 +55,7 @@ module.exports = {
value: {
bind: function () {
if (this.oneway) return
var el = this.el, self = this
this.change = function () {
self.seed.scope[self.key] = el.value
@ -65,12 +66,14 @@ module.exports = {
this.el.value = value
},
unbind: function () {
if (this.oneway) return
this.el.removeEventListener('change', this.change)
}
},
checked: {
bind: function () {
if (this.oneway) return
var el = this.el, self = this
this.change = function () {
self.seed.scope[self.key] = el.checked
@ -81,6 +84,7 @@ module.exports = {
this.el.checked = !!value
},
unbind: function () {
if (this.oneway) return
this.el.removeEventListener('change', this.change)
}
},
@ -88,7 +92,13 @@ module.exports = {
'if': {
bind: function () {
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) {
if (!value) {

View File

@ -1,7 +1,8 @@
var config = require('./config'),
Seed = require('./seed'),
directives = require('./directives'),
filters = require('./filters')
filters = require('./filters'),
textParser = require('./text-parser')
var controllers = config.controllers,
datum = config.datum,
@ -61,6 +62,7 @@ api.bootstrap = function (opts) {
}
}
}
textParser.buildRegex()
var el,
ctrlSlt = '[' + config.prefix + '-controller]',
dataSlt = '[' + config.prefix + '-data]'

View File

@ -2,7 +2,7 @@ var config = require('./config'),
Emitter = require('emitter'),
Binding = require('./binding'),
Directive = require('./directive'),
TextNodeParser = require('./textnode-parser')
TextParser = require('./text-parser')
var slice = Array.prototype.slice,
ctrlAttr = config.prefix + '-controller',
@ -122,6 +122,8 @@ Seed.prototype._compileNode = function (node, root) {
// parse if has attributes
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) {
if (attr.name === ctrlAttr) return
var valid = false
@ -151,7 +153,7 @@ Seed.prototype._compileNode = function (node, root) {
* Compile a text node
*/
Seed.prototype._compileTextNode = function (node) {
return TextNodeParser.parse(node)
return TextParser.parse(node)
}
/*

23
src/text-parser.js Normal file
View File

@ -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)
}
}

View File

@ -1,5 +0,0 @@
module.exports = {
parse: function (node) {
return node
}
}