fluentd-ui 0.3.8 → 0.3.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of fluentd-ui might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/ChangeLog +9 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +4 -1
- data/app/assets/javascripts/vue/fluent_log.js +4 -4
- data/app/assets/javascripts/vue/in_tail_format.js +43 -49
- data/app/assets/javascripts/{alert.js → vue/notification.js} +21 -7
- data/app/assets/javascripts/vue/settings.js +121 -0
- data/app/assets/javascripts/vue/treeview.js +3 -3
- data/app/assets/stylesheets/common.css.scss +7 -1
- data/app/controllers/api/settings_controller.rb +55 -0
- data/app/controllers/api_controller.rb +6 -2
- data/app/controllers/concerns/setting_concern.rb +2 -2
- data/app/controllers/fluentd/settings_controller.rb +17 -2
- data/app/models/fluentd/setting/config.rb +39 -0
- data/app/views/api/settings/_element.json.jbuilder +6 -0
- data/app/views/api/settings/index.json.jbuilder +3 -0
- data/app/views/api/settings/show.json.jbuilder +1 -0
- data/app/views/fluentd/settings/edit.html.haml +3 -0
- data/app/views/fluentd/settings/source_and_output.html.haml +19 -2
- data/app/views/layouts/application.html.erb +1 -30
- data/app/views/shared/vue/_in_tail_format.html.erb +11 -36
- data/app/views/shared/vue/_notification.html.erb +31 -0
- data/app/views/shared/vue/_setting.html.erb +23 -0
- data/app/views/shared/vue/_treeview.html.erb +2 -2
- data/bower.json +1 -1
- data/config/application.rb +1 -0
- data/config/locales/translation_en.yml +11 -7
- data/config/locales/translation_ja.yml +4 -0
- data/config/routes.rb +2 -0
- data/lib/fluentd-ui/version.rb +1 -1
- data/spec/features/fluentd/setting/source_and_output_spec.rb +157 -0
- data/spec/spec_helper.rb +1 -0
- data/spec/support/javascript_macro.rb +21 -0
- data/vendor/assets/javascripts/bower/vue/.bower.json +7 -7
- data/vendor/assets/javascripts/bower/vue/LICENSE +1 -1
- data/vendor/assets/javascripts/bower/vue/dist/vue.js +7822 -4768
- data/vendor/assets/javascripts/bower/vue/dist/vue.min.js +7 -7
- data/vendor/assets/javascripts/bower/vue/src/api/child.js +53 -0
- data/vendor/assets/javascripts/bower/vue/src/api/data.js +161 -0
- data/vendor/assets/javascripts/bower/vue/src/api/dom.js +211 -0
- data/vendor/assets/javascripts/bower/vue/src/api/events.js +176 -0
- data/vendor/assets/javascripts/bower/vue/src/api/global.js +146 -0
- data/vendor/assets/javascripts/bower/vue/src/api/lifecycle.js +144 -0
- data/vendor/assets/javascripts/bower/vue/src/batcher.js +52 -32
- data/vendor/assets/javascripts/bower/vue/src/cache.js +112 -0
- data/vendor/assets/javascripts/bower/vue/src/compiler/compile.js +549 -0
- data/vendor/assets/javascripts/bower/vue/src/compiler/transclude.js +163 -0
- data/vendor/assets/javascripts/bower/vue/src/config.js +74 -14
- data/vendor/assets/javascripts/bower/vue/src/directive.js +179 -219
- data/vendor/assets/javascripts/bower/vue/src/directives/attr.js +32 -0
- data/vendor/assets/javascripts/bower/vue/src/directives/class.js +18 -0
- data/vendor/assets/javascripts/bower/vue/src/directives/cloak.js +12 -0
- data/vendor/assets/javascripts/bower/vue/src/directives/component.js +214 -0
- data/vendor/assets/javascripts/bower/vue/src/directives/el.js +13 -0
- data/vendor/assets/javascripts/bower/vue/src/directives/html.js +30 -34
- data/vendor/assets/javascripts/bower/vue/src/directives/if.js +77 -46
- data/vendor/assets/javascripts/bower/vue/src/directives/index.js +22 -129
- data/vendor/assets/javascripts/bower/vue/src/directives/model/checkbox.js +25 -0
- data/vendor/assets/javascripts/bower/vue/src/directives/model/default.js +123 -0
- data/vendor/assets/javascripts/bower/vue/src/directives/model/index.js +56 -0
- data/vendor/assets/javascripts/bower/vue/src/directives/model/radio.js +26 -0
- data/vendor/assets/javascripts/bower/vue/src/directives/model/select.js +166 -0
- data/vendor/assets/javascripts/bower/vue/src/directives/on.js +51 -50
- data/vendor/assets/javascripts/bower/vue/src/directives/partial.js +36 -42
- data/vendor/assets/javascripts/bower/vue/src/directives/ref.js +24 -0
- data/vendor/assets/javascripts/bower/vue/src/directives/repeat.js +477 -226
- data/vendor/assets/javascripts/bower/vue/src/directives/show.js +8 -0
- data/vendor/assets/javascripts/bower/vue/src/directives/style.js +49 -37
- data/vendor/assets/javascripts/bower/vue/src/directives/text.js +15 -0
- data/vendor/assets/javascripts/bower/vue/src/directives/transition.js +12 -0
- data/vendor/assets/javascripts/bower/vue/src/directives/with.js +38 -41
- data/vendor/assets/javascripts/bower/vue/src/filters/array-filters.js +87 -0
- data/vendor/assets/javascripts/bower/vue/src/filters/index.js +135 -0
- data/vendor/assets/javascripts/bower/vue/src/instance/compile.js +71 -0
- data/vendor/assets/javascripts/bower/vue/src/instance/events.js +122 -0
- data/vendor/assets/javascripts/bower/vue/src/instance/init.js +76 -0
- data/vendor/assets/javascripts/bower/vue/src/instance/scope.js +217 -0
- data/vendor/assets/javascripts/bower/vue/src/observer/array.js +90 -0
- data/vendor/assets/javascripts/bower/vue/src/observer/dep.js +50 -0
- data/vendor/assets/javascripts/bower/vue/src/observer/index.js +235 -0
- data/vendor/assets/javascripts/bower/vue/src/observer/object.js +75 -0
- data/vendor/assets/javascripts/bower/vue/src/parsers/directive.js +159 -0
- data/vendor/assets/javascripts/bower/vue/src/parsers/expression.js +226 -0
- data/vendor/assets/javascripts/bower/vue/src/parsers/path.js +300 -0
- data/vendor/assets/javascripts/bower/vue/src/parsers/template.js +246 -0
- data/vendor/assets/javascripts/bower/vue/src/parsers/text.js +178 -0
- data/vendor/assets/javascripts/bower/vue/src/transition/css.js +189 -0
- data/vendor/assets/javascripts/bower/vue/src/transition/index.js +151 -0
- data/vendor/assets/javascripts/bower/vue/src/transition/js.js +43 -0
- data/vendor/assets/javascripts/bower/vue/src/util/debug.js +50 -0
- data/vendor/assets/javascripts/bower/vue/src/util/dom.js +176 -0
- data/vendor/assets/javascripts/bower/vue/src/util/env.js +74 -0
- data/vendor/assets/javascripts/bower/vue/src/util/filter.js +72 -0
- data/vendor/assets/javascripts/bower/vue/src/util/index.js +8 -0
- data/vendor/assets/javascripts/bower/vue/src/util/lang.js +175 -0
- data/vendor/assets/javascripts/bower/vue/src/util/merge-option.js +258 -0
- data/vendor/assets/javascripts/bower/vue/src/vue.js +84 -0
- data/vendor/assets/javascripts/bower/vue/src/watcher.js +240 -0
- metadata +65 -20
- data/app/assets/javascripts/setting_format.js +0 -15
- data/vendor/assets/javascripts/bower/vue/src/binding.js +0 -103
- data/vendor/assets/javascripts/bower/vue/src/compiler.js +0 -1037
- data/vendor/assets/javascripts/bower/vue/src/deps-parser.js +0 -65
- data/vendor/assets/javascripts/bower/vue/src/directives/model.js +0 -174
- data/vendor/assets/javascripts/bower/vue/src/directives/view.js +0 -56
- data/vendor/assets/javascripts/bower/vue/src/emitter.js +0 -97
- data/vendor/assets/javascripts/bower/vue/src/exp-parser.js +0 -190
- data/vendor/assets/javascripts/bower/vue/src/filters.js +0 -191
- data/vendor/assets/javascripts/bower/vue/src/fragment.js +0 -67
- data/vendor/assets/javascripts/bower/vue/src/main.js +0 -188
- data/vendor/assets/javascripts/bower/vue/src/observer.js +0 -446
- data/vendor/assets/javascripts/bower/vue/src/template-parser.js +0 -46
- data/vendor/assets/javascripts/bower/vue/src/text-parser.js +0 -96
- data/vendor/assets/javascripts/bower/vue/src/transition.js +0 -228
- data/vendor/assets/javascripts/bower/vue/src/utils.js +0 -326
- data/vendor/assets/javascripts/bower/vue/src/viewmodel.js +0 -190
@@ -0,0 +1,159 @@
|
|
1
|
+
var _ = require('../util')
|
2
|
+
var Cache = require('../cache')
|
3
|
+
var cache = new Cache(1000)
|
4
|
+
var argRE = /^[^\{\?]+$|^'[^']*'$|^"[^"]*"$/
|
5
|
+
var filterTokenRE = /[^\s'"]+|'[^']+'|"[^"]+"/g
|
6
|
+
|
7
|
+
/**
|
8
|
+
* Parser state
|
9
|
+
*/
|
10
|
+
|
11
|
+
var str
|
12
|
+
var c, i, l
|
13
|
+
var inSingle
|
14
|
+
var inDouble
|
15
|
+
var curly
|
16
|
+
var square
|
17
|
+
var paren
|
18
|
+
var begin
|
19
|
+
var argIndex
|
20
|
+
var dirs
|
21
|
+
var dir
|
22
|
+
var lastFilterIndex
|
23
|
+
var arg
|
24
|
+
|
25
|
+
/**
|
26
|
+
* Push a directive object into the result Array
|
27
|
+
*/
|
28
|
+
|
29
|
+
function pushDir () {
|
30
|
+
dir.raw = str.slice(begin, i).trim()
|
31
|
+
if (dir.expression === undefined) {
|
32
|
+
dir.expression = str.slice(argIndex, i).trim()
|
33
|
+
} else if (lastFilterIndex !== begin) {
|
34
|
+
pushFilter()
|
35
|
+
}
|
36
|
+
if (i === 0 || dir.expression) {
|
37
|
+
dirs.push(dir)
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
/**
|
42
|
+
* Push a filter to the current directive object
|
43
|
+
*/
|
44
|
+
|
45
|
+
function pushFilter () {
|
46
|
+
var exp = str.slice(lastFilterIndex, i).trim()
|
47
|
+
var filter
|
48
|
+
if (exp) {
|
49
|
+
filter = {}
|
50
|
+
var tokens = exp.match(filterTokenRE)
|
51
|
+
filter.name = tokens[0]
|
52
|
+
filter.args = tokens.length > 1 ? tokens.slice(1) : null
|
53
|
+
}
|
54
|
+
if (filter) {
|
55
|
+
(dir.filters = dir.filters || []).push(filter)
|
56
|
+
}
|
57
|
+
lastFilterIndex = i + 1
|
58
|
+
}
|
59
|
+
|
60
|
+
/**
|
61
|
+
* Parse a directive string into an Array of AST-like
|
62
|
+
* objects representing directives.
|
63
|
+
*
|
64
|
+
* Example:
|
65
|
+
*
|
66
|
+
* "click: a = a + 1 | uppercase" will yield:
|
67
|
+
* {
|
68
|
+
* arg: 'click',
|
69
|
+
* expression: 'a = a + 1',
|
70
|
+
* filters: [
|
71
|
+
* { name: 'uppercase', args: null }
|
72
|
+
* ]
|
73
|
+
* }
|
74
|
+
*
|
75
|
+
* @param {String} str
|
76
|
+
* @return {Array<Object>}
|
77
|
+
*/
|
78
|
+
|
79
|
+
exports.parse = function (s) {
|
80
|
+
|
81
|
+
var hit = cache.get(s)
|
82
|
+
if (hit) {
|
83
|
+
return hit
|
84
|
+
}
|
85
|
+
|
86
|
+
// reset parser state
|
87
|
+
str = s
|
88
|
+
inSingle = inDouble = false
|
89
|
+
curly = square = paren = begin = argIndex = 0
|
90
|
+
lastFilterIndex = 0
|
91
|
+
dirs = []
|
92
|
+
dir = {}
|
93
|
+
arg = null
|
94
|
+
|
95
|
+
for (i = 0, l = str.length; i < l; i++) {
|
96
|
+
c = str.charCodeAt(i)
|
97
|
+
if (inSingle) {
|
98
|
+
// check single quote
|
99
|
+
if (c === 0x27) inSingle = !inSingle
|
100
|
+
} else if (inDouble) {
|
101
|
+
// check double quote
|
102
|
+
if (c === 0x22) inDouble = !inDouble
|
103
|
+
} else if (
|
104
|
+
c === 0x2C && // comma
|
105
|
+
!paren && !curly && !square
|
106
|
+
) {
|
107
|
+
// reached the end of a directive
|
108
|
+
pushDir()
|
109
|
+
// reset & skip the comma
|
110
|
+
dir = {}
|
111
|
+
begin = argIndex = lastFilterIndex = i + 1
|
112
|
+
} else if (
|
113
|
+
c === 0x3A && // colon
|
114
|
+
!dir.expression &&
|
115
|
+
!dir.arg
|
116
|
+
) {
|
117
|
+
// argument
|
118
|
+
arg = str.slice(begin, i).trim()
|
119
|
+
// test for valid argument here
|
120
|
+
// since we may have caught stuff like first half of
|
121
|
+
// an object literal or a ternary expression.
|
122
|
+
if (argRE.test(arg)) {
|
123
|
+
argIndex = i + 1
|
124
|
+
dir.arg = _.stripQuotes(arg) || arg
|
125
|
+
}
|
126
|
+
} else if (
|
127
|
+
c === 0x7C && // pipe
|
128
|
+
str.charCodeAt(i + 1) !== 0x7C &&
|
129
|
+
str.charCodeAt(i - 1) !== 0x7C
|
130
|
+
) {
|
131
|
+
if (dir.expression === undefined) {
|
132
|
+
// first filter, end of expression
|
133
|
+
lastFilterIndex = i + 1
|
134
|
+
dir.expression = str.slice(argIndex, i).trim()
|
135
|
+
} else {
|
136
|
+
// already has filter
|
137
|
+
pushFilter()
|
138
|
+
}
|
139
|
+
} else {
|
140
|
+
switch (c) {
|
141
|
+
case 0x22: inDouble = true; break // "
|
142
|
+
case 0x27: inSingle = true; break // '
|
143
|
+
case 0x28: paren++; break // (
|
144
|
+
case 0x29: paren--; break // )
|
145
|
+
case 0x5B: square++; break // [
|
146
|
+
case 0x5D: square--; break // ]
|
147
|
+
case 0x7B: curly++; break // {
|
148
|
+
case 0x7D: curly--; break // }
|
149
|
+
}
|
150
|
+
}
|
151
|
+
}
|
152
|
+
|
153
|
+
if (i === 0 || begin !== i) {
|
154
|
+
pushDir()
|
155
|
+
}
|
156
|
+
|
157
|
+
cache.put(s, dirs)
|
158
|
+
return dirs
|
159
|
+
}
|
@@ -0,0 +1,226 @@
|
|
1
|
+
var _ = require('../util')
|
2
|
+
var Path = require('./path')
|
3
|
+
var Cache = require('../cache')
|
4
|
+
var expressionCache = new Cache(1000)
|
5
|
+
|
6
|
+
var keywords =
|
7
|
+
'Math,break,case,catch,continue,debugger,default,' +
|
8
|
+
'delete,do,else,false,finally,for,function,if,in,' +
|
9
|
+
'instanceof,new,null,return,switch,this,throw,true,try,' +
|
10
|
+
'typeof,var,void,while,with,undefined,abstract,boolean,' +
|
11
|
+
'byte,char,class,const,double,enum,export,extends,' +
|
12
|
+
'final,float,goto,implements,import,int,interface,long,' +
|
13
|
+
'native,package,private,protected,public,short,static,' +
|
14
|
+
'super,synchronized,throws,transient,volatile,' +
|
15
|
+
'arguments,let,yield'
|
16
|
+
|
17
|
+
var wsRE = /\s/g
|
18
|
+
var newlineRE = /\n/g
|
19
|
+
var saveRE = /[\{,]\s*[\w\$_]+\s*:|'[^']*'|"[^"]*"/g
|
20
|
+
var restoreRE = /"(\d+)"/g
|
21
|
+
var pathTestRE = /^[A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\]|\[\d+\])*$/
|
22
|
+
var pathReplaceRE = /[^\w$\.]([A-Za-z_$][\w$]*(\.[A-Za-z_$][\w$]*|\['.*?'\]|\[".*?"\])*)/g
|
23
|
+
var keywordsRE = new RegExp('^(' + keywords.replace(/,/g, '\\b|') + '\\b)')
|
24
|
+
|
25
|
+
/**
|
26
|
+
* Save / Rewrite / Restore
|
27
|
+
*
|
28
|
+
* When rewriting paths found in an expression, it is
|
29
|
+
* possible for the same letter sequences to be found in
|
30
|
+
* strings and Object literal property keys. Therefore we
|
31
|
+
* remove and store these parts in a temporary array, and
|
32
|
+
* restore them after the path rewrite.
|
33
|
+
*/
|
34
|
+
|
35
|
+
var saved = []
|
36
|
+
|
37
|
+
/**
|
38
|
+
* Save replacer
|
39
|
+
*
|
40
|
+
* @param {String} str
|
41
|
+
* @return {String} - placeholder with index
|
42
|
+
*/
|
43
|
+
|
44
|
+
function save (str) {
|
45
|
+
var i = saved.length
|
46
|
+
saved[i] = str.replace(newlineRE, '\\n')
|
47
|
+
return '"' + i + '"'
|
48
|
+
}
|
49
|
+
|
50
|
+
/**
|
51
|
+
* Path rewrite replacer
|
52
|
+
*
|
53
|
+
* @param {String} raw
|
54
|
+
* @return {String}
|
55
|
+
*/
|
56
|
+
|
57
|
+
function rewrite (raw) {
|
58
|
+
var c = raw.charAt(0)
|
59
|
+
var path = raw.slice(1)
|
60
|
+
if (keywordsRE.test(path)) {
|
61
|
+
return raw
|
62
|
+
} else {
|
63
|
+
path = path.indexOf('"') > -1
|
64
|
+
? path.replace(restoreRE, restore)
|
65
|
+
: path
|
66
|
+
return c + 'scope.' + path
|
67
|
+
}
|
68
|
+
}
|
69
|
+
|
70
|
+
/**
|
71
|
+
* Restore replacer
|
72
|
+
*
|
73
|
+
* @param {String} str
|
74
|
+
* @param {String} i - matched save index
|
75
|
+
* @return {String}
|
76
|
+
*/
|
77
|
+
|
78
|
+
function restore (str, i) {
|
79
|
+
return saved[i]
|
80
|
+
}
|
81
|
+
|
82
|
+
/**
|
83
|
+
* Rewrite an expression, prefixing all path accessors with
|
84
|
+
* `scope.` and generate getter/setter functions.
|
85
|
+
*
|
86
|
+
* @param {String} exp
|
87
|
+
* @param {Boolean} needSet
|
88
|
+
* @return {Function}
|
89
|
+
*/
|
90
|
+
|
91
|
+
function compileExpFns (exp, needSet) {
|
92
|
+
// reset state
|
93
|
+
saved.length = 0
|
94
|
+
// save strings and object literal keys
|
95
|
+
var body = exp
|
96
|
+
.replace(saveRE, save)
|
97
|
+
.replace(wsRE, '')
|
98
|
+
// rewrite all paths
|
99
|
+
// pad 1 space here becaue the regex matches 1 extra char
|
100
|
+
body = (' ' + body)
|
101
|
+
.replace(pathReplaceRE, rewrite)
|
102
|
+
.replace(restoreRE, restore)
|
103
|
+
var getter = makeGetter(body)
|
104
|
+
if (getter) {
|
105
|
+
return {
|
106
|
+
get: getter,
|
107
|
+
body: body,
|
108
|
+
set: needSet
|
109
|
+
? makeSetter(body)
|
110
|
+
: null
|
111
|
+
}
|
112
|
+
}
|
113
|
+
}
|
114
|
+
|
115
|
+
/**
|
116
|
+
* Compile getter setters for a simple path.
|
117
|
+
*
|
118
|
+
* @param {String} exp
|
119
|
+
* @return {Function}
|
120
|
+
*/
|
121
|
+
|
122
|
+
function compilePathFns (exp) {
|
123
|
+
var getter, path
|
124
|
+
if (exp.indexOf('[') < 0) {
|
125
|
+
// really simple path
|
126
|
+
path = exp.split('.')
|
127
|
+
getter = Path.compileGetter(path)
|
128
|
+
} else {
|
129
|
+
// do the real parsing
|
130
|
+
path = Path.parse(exp)
|
131
|
+
getter = path.get
|
132
|
+
}
|
133
|
+
return {
|
134
|
+
get: getter,
|
135
|
+
// always generate setter for simple paths
|
136
|
+
set: function (obj, val) {
|
137
|
+
Path.set(obj, path, val)
|
138
|
+
}
|
139
|
+
}
|
140
|
+
}
|
141
|
+
|
142
|
+
/**
|
143
|
+
* Build a getter function. Requires eval.
|
144
|
+
*
|
145
|
+
* We isolate the try/catch so it doesn't affect the
|
146
|
+
* optimization of the parse function when it is not called.
|
147
|
+
*
|
148
|
+
* @param {String} body
|
149
|
+
* @return {Function|undefined}
|
150
|
+
*/
|
151
|
+
|
152
|
+
function makeGetter (body) {
|
153
|
+
try {
|
154
|
+
return new Function('scope', 'return ' + body + ';')
|
155
|
+
} catch (e) {
|
156
|
+
_.warn(
|
157
|
+
'Invalid expression. ' +
|
158
|
+
'Generated function body: ' + body
|
159
|
+
)
|
160
|
+
}
|
161
|
+
}
|
162
|
+
|
163
|
+
/**
|
164
|
+
* Build a setter function.
|
165
|
+
*
|
166
|
+
* This is only needed in rare situations like "a[b]" where
|
167
|
+
* a settable path requires dynamic evaluation.
|
168
|
+
*
|
169
|
+
* This setter function may throw error when called if the
|
170
|
+
* expression body is not a valid left-hand expression in
|
171
|
+
* assignment.
|
172
|
+
*
|
173
|
+
* @param {String} body
|
174
|
+
* @return {Function|undefined}
|
175
|
+
*/
|
176
|
+
|
177
|
+
function makeSetter (body) {
|
178
|
+
try {
|
179
|
+
return new Function('scope', 'value', body + '=value;')
|
180
|
+
} catch (e) {
|
181
|
+
_.warn('Invalid setter function body: ' + body)
|
182
|
+
}
|
183
|
+
}
|
184
|
+
|
185
|
+
/**
|
186
|
+
* Check for setter existence on a cache hit.
|
187
|
+
*
|
188
|
+
* @param {Function} hit
|
189
|
+
*/
|
190
|
+
|
191
|
+
function checkSetter (hit) {
|
192
|
+
if (!hit.set) {
|
193
|
+
hit.set = makeSetter(hit.body)
|
194
|
+
}
|
195
|
+
}
|
196
|
+
|
197
|
+
/**
|
198
|
+
* Parse an expression into re-written getter/setters.
|
199
|
+
*
|
200
|
+
* @param {String} exp
|
201
|
+
* @param {Boolean} needSet
|
202
|
+
* @return {Function}
|
203
|
+
*/
|
204
|
+
|
205
|
+
exports.parse = function (exp, needSet) {
|
206
|
+
exp = exp.trim()
|
207
|
+
// try cache
|
208
|
+
var hit = expressionCache.get(exp)
|
209
|
+
if (hit) {
|
210
|
+
if (needSet) {
|
211
|
+
checkSetter(hit)
|
212
|
+
}
|
213
|
+
return hit
|
214
|
+
}
|
215
|
+
// we do a simple path check to optimize for them.
|
216
|
+
// the check fails valid paths with unusal whitespaces,
|
217
|
+
// but that's too rare and we don't care.
|
218
|
+
var res = pathTestRE.test(exp)
|
219
|
+
? compilePathFns(exp)
|
220
|
+
: compileExpFns(exp, needSet)
|
221
|
+
expressionCache.put(exp, res)
|
222
|
+
return res
|
223
|
+
}
|
224
|
+
|
225
|
+
// Export the pathRegex for external use
|
226
|
+
exports.pathTestRE = pathTestRE
|
@@ -0,0 +1,300 @@
|
|
1
|
+
var _ = require('../util')
|
2
|
+
var Cache = require('../cache')
|
3
|
+
var pathCache = new Cache(1000)
|
4
|
+
var identRE = /^[$_a-zA-Z]+[\w$]*$/
|
5
|
+
|
6
|
+
/**
|
7
|
+
* Path-parsing algorithm scooped from Polymer/observe-js
|
8
|
+
*/
|
9
|
+
|
10
|
+
var pathStateMachine = {
|
11
|
+
'beforePath': {
|
12
|
+
'ws': ['beforePath'],
|
13
|
+
'ident': ['inIdent', 'append'],
|
14
|
+
'[': ['beforeElement'],
|
15
|
+
'eof': ['afterPath']
|
16
|
+
},
|
17
|
+
|
18
|
+
'inPath': {
|
19
|
+
'ws': ['inPath'],
|
20
|
+
'.': ['beforeIdent'],
|
21
|
+
'[': ['beforeElement'],
|
22
|
+
'eof': ['afterPath']
|
23
|
+
},
|
24
|
+
|
25
|
+
'beforeIdent': {
|
26
|
+
'ws': ['beforeIdent'],
|
27
|
+
'ident': ['inIdent', 'append']
|
28
|
+
},
|
29
|
+
|
30
|
+
'inIdent': {
|
31
|
+
'ident': ['inIdent', 'append'],
|
32
|
+
'0': ['inIdent', 'append'],
|
33
|
+
'number': ['inIdent', 'append'],
|
34
|
+
'ws': ['inPath', 'push'],
|
35
|
+
'.': ['beforeIdent', 'push'],
|
36
|
+
'[': ['beforeElement', 'push'],
|
37
|
+
'eof': ['afterPath', 'push']
|
38
|
+
},
|
39
|
+
|
40
|
+
'beforeElement': {
|
41
|
+
'ws': ['beforeElement'],
|
42
|
+
'0': ['afterZero', 'append'],
|
43
|
+
'number': ['inIndex', 'append'],
|
44
|
+
"'": ['inSingleQuote', 'append', ''],
|
45
|
+
'"': ['inDoubleQuote', 'append', '']
|
46
|
+
},
|
47
|
+
|
48
|
+
'afterZero': {
|
49
|
+
'ws': ['afterElement', 'push'],
|
50
|
+
']': ['inPath', 'push']
|
51
|
+
},
|
52
|
+
|
53
|
+
'inIndex': {
|
54
|
+
'0': ['inIndex', 'append'],
|
55
|
+
'number': ['inIndex', 'append'],
|
56
|
+
'ws': ['afterElement'],
|
57
|
+
']': ['inPath', 'push']
|
58
|
+
},
|
59
|
+
|
60
|
+
'inSingleQuote': {
|
61
|
+
"'": ['afterElement'],
|
62
|
+
'eof': 'error',
|
63
|
+
'else': ['inSingleQuote', 'append']
|
64
|
+
},
|
65
|
+
|
66
|
+
'inDoubleQuote': {
|
67
|
+
'"': ['afterElement'],
|
68
|
+
'eof': 'error',
|
69
|
+
'else': ['inDoubleQuote', 'append']
|
70
|
+
},
|
71
|
+
|
72
|
+
'afterElement': {
|
73
|
+
'ws': ['afterElement'],
|
74
|
+
']': ['inPath', 'push']
|
75
|
+
}
|
76
|
+
}
|
77
|
+
|
78
|
+
function noop () {}
|
79
|
+
|
80
|
+
/**
|
81
|
+
* Determine the type of a character in a keypath.
|
82
|
+
*
|
83
|
+
* @param {Char} char
|
84
|
+
* @return {String} type
|
85
|
+
*/
|
86
|
+
|
87
|
+
function getPathCharType (char) {
|
88
|
+
if (char === undefined) {
|
89
|
+
return 'eof'
|
90
|
+
}
|
91
|
+
|
92
|
+
var code = char.charCodeAt(0)
|
93
|
+
|
94
|
+
switch(code) {
|
95
|
+
case 0x5B: // [
|
96
|
+
case 0x5D: // ]
|
97
|
+
case 0x2E: // .
|
98
|
+
case 0x22: // "
|
99
|
+
case 0x27: // '
|
100
|
+
case 0x30: // 0
|
101
|
+
return char
|
102
|
+
|
103
|
+
case 0x5F: // _
|
104
|
+
case 0x24: // $
|
105
|
+
return 'ident'
|
106
|
+
|
107
|
+
case 0x20: // Space
|
108
|
+
case 0x09: // Tab
|
109
|
+
case 0x0A: // Newline
|
110
|
+
case 0x0D: // Return
|
111
|
+
case 0xA0: // No-break space
|
112
|
+
case 0xFEFF: // Byte Order Mark
|
113
|
+
case 0x2028: // Line Separator
|
114
|
+
case 0x2029: // Paragraph Separator
|
115
|
+
return 'ws'
|
116
|
+
}
|
117
|
+
|
118
|
+
// a-z, A-Z
|
119
|
+
if ((0x61 <= code && code <= 0x7A) ||
|
120
|
+
(0x41 <= code && code <= 0x5A)) {
|
121
|
+
return 'ident'
|
122
|
+
}
|
123
|
+
|
124
|
+
// 1-9
|
125
|
+
if (0x31 <= code && code <= 0x39) {
|
126
|
+
return 'number'
|
127
|
+
}
|
128
|
+
|
129
|
+
return 'else'
|
130
|
+
}
|
131
|
+
|
132
|
+
/**
|
133
|
+
* Parse a string path into an array of segments
|
134
|
+
* Todo implement cache
|
135
|
+
*
|
136
|
+
* @param {String} path
|
137
|
+
* @return {Array|undefined}
|
138
|
+
*/
|
139
|
+
|
140
|
+
function parsePath (path) {
|
141
|
+
var keys = []
|
142
|
+
var index = -1
|
143
|
+
var mode = 'beforePath'
|
144
|
+
var c, newChar, key, type, transition, action, typeMap
|
145
|
+
|
146
|
+
var actions = {
|
147
|
+
push: function() {
|
148
|
+
if (key === undefined) {
|
149
|
+
return
|
150
|
+
}
|
151
|
+
keys.push(key)
|
152
|
+
key = undefined
|
153
|
+
},
|
154
|
+
append: function() {
|
155
|
+
if (key === undefined) {
|
156
|
+
key = newChar
|
157
|
+
} else {
|
158
|
+
key += newChar
|
159
|
+
}
|
160
|
+
}
|
161
|
+
}
|
162
|
+
|
163
|
+
function maybeUnescapeQuote () {
|
164
|
+
var nextChar = path[index + 1]
|
165
|
+
if ((mode === 'inSingleQuote' && nextChar === "'") ||
|
166
|
+
(mode === 'inDoubleQuote' && nextChar === '"')) {
|
167
|
+
index++
|
168
|
+
newChar = nextChar
|
169
|
+
actions.append()
|
170
|
+
return true
|
171
|
+
}
|
172
|
+
}
|
173
|
+
|
174
|
+
while (mode) {
|
175
|
+
index++
|
176
|
+
c = path[index]
|
177
|
+
|
178
|
+
if (c === '\\' && maybeUnescapeQuote()) {
|
179
|
+
continue
|
180
|
+
}
|
181
|
+
|
182
|
+
type = getPathCharType(c)
|
183
|
+
typeMap = pathStateMachine[mode]
|
184
|
+
transition = typeMap[type] || typeMap['else'] || 'error'
|
185
|
+
|
186
|
+
if (transition === 'error') {
|
187
|
+
return // parse error
|
188
|
+
}
|
189
|
+
|
190
|
+
mode = transition[0]
|
191
|
+
action = actions[transition[1]] || noop
|
192
|
+
newChar = transition[2] === undefined
|
193
|
+
? c
|
194
|
+
: transition[2]
|
195
|
+
action()
|
196
|
+
|
197
|
+
if (mode === 'afterPath') {
|
198
|
+
return keys
|
199
|
+
}
|
200
|
+
}
|
201
|
+
}
|
202
|
+
|
203
|
+
/**
|
204
|
+
* Format a accessor segment based on its type.
|
205
|
+
*
|
206
|
+
* @param {String} key
|
207
|
+
* @return {Boolean}
|
208
|
+
*/
|
209
|
+
|
210
|
+
function formatAccessor(key) {
|
211
|
+
if (identRE.test(key)) { // identifier
|
212
|
+
return '.' + key
|
213
|
+
} else if (+key === key >>> 0) { // bracket index
|
214
|
+
return '[' + key + ']'
|
215
|
+
} else { // bracket string
|
216
|
+
return '["' + key.replace(/"/g, '\\"') + '"]'
|
217
|
+
}
|
218
|
+
}
|
219
|
+
|
220
|
+
/**
|
221
|
+
* Compiles a getter function with a fixed path.
|
222
|
+
*
|
223
|
+
* @param {Array} path
|
224
|
+
* @return {Function}
|
225
|
+
*/
|
226
|
+
|
227
|
+
exports.compileGetter = function (path) {
|
228
|
+
var body =
|
229
|
+
'try{return o' +
|
230
|
+
path.map(formatAccessor).join('') +
|
231
|
+
'}catch(e){};'
|
232
|
+
return new Function('o', body)
|
233
|
+
}
|
234
|
+
|
235
|
+
/**
|
236
|
+
* External parse that check for a cache hit first
|
237
|
+
*
|
238
|
+
* @param {String} path
|
239
|
+
* @return {Array|undefined}
|
240
|
+
*/
|
241
|
+
|
242
|
+
exports.parse = function (path) {
|
243
|
+
var hit = pathCache.get(path)
|
244
|
+
if (!hit) {
|
245
|
+
hit = parsePath(path)
|
246
|
+
if (hit) {
|
247
|
+
hit.get = exports.compileGetter(hit)
|
248
|
+
pathCache.put(path, hit)
|
249
|
+
}
|
250
|
+
}
|
251
|
+
return hit
|
252
|
+
}
|
253
|
+
|
254
|
+
/**
|
255
|
+
* Get from an object from a path string
|
256
|
+
*
|
257
|
+
* @param {Object} obj
|
258
|
+
* @param {String} path
|
259
|
+
*/
|
260
|
+
|
261
|
+
exports.get = function (obj, path) {
|
262
|
+
path = exports.parse(path)
|
263
|
+
if (path) {
|
264
|
+
return path.get(obj)
|
265
|
+
}
|
266
|
+
}
|
267
|
+
|
268
|
+
/**
|
269
|
+
* Set on an object from a path
|
270
|
+
*
|
271
|
+
* @param {Object} obj
|
272
|
+
* @param {String | Array} path
|
273
|
+
* @param {*} val
|
274
|
+
*/
|
275
|
+
|
276
|
+
exports.set = function (obj, path, val) {
|
277
|
+
if (typeof path === 'string') {
|
278
|
+
path = exports.parse(path)
|
279
|
+
}
|
280
|
+
if (!path || !_.isObject(obj)) {
|
281
|
+
return false
|
282
|
+
}
|
283
|
+
var last, key
|
284
|
+
for (var i = 0, l = path.length - 1; i < l; i++) {
|
285
|
+
last = obj
|
286
|
+
key = path[i]
|
287
|
+
obj = obj[key]
|
288
|
+
if (!_.isObject(obj)) {
|
289
|
+
obj = {}
|
290
|
+
last.$add(key, obj)
|
291
|
+
}
|
292
|
+
}
|
293
|
+
key = path[i]
|
294
|
+
if (key in obj) {
|
295
|
+
obj[key] = val
|
296
|
+
} else {
|
297
|
+
obj.$add(key, val)
|
298
|
+
}
|
299
|
+
return true
|
300
|
+
}
|