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.

Files changed (117) hide show
  1. checksums.yaml +4 -4
  2. data/ChangeLog +9 -0
  3. data/Gemfile +1 -0
  4. data/Gemfile.lock +4 -1
  5. data/app/assets/javascripts/vue/fluent_log.js +4 -4
  6. data/app/assets/javascripts/vue/in_tail_format.js +43 -49
  7. data/app/assets/javascripts/{alert.js → vue/notification.js} +21 -7
  8. data/app/assets/javascripts/vue/settings.js +121 -0
  9. data/app/assets/javascripts/vue/treeview.js +3 -3
  10. data/app/assets/stylesheets/common.css.scss +7 -1
  11. data/app/controllers/api/settings_controller.rb +55 -0
  12. data/app/controllers/api_controller.rb +6 -2
  13. data/app/controllers/concerns/setting_concern.rb +2 -2
  14. data/app/controllers/fluentd/settings_controller.rb +17 -2
  15. data/app/models/fluentd/setting/config.rb +39 -0
  16. data/app/views/api/settings/_element.json.jbuilder +6 -0
  17. data/app/views/api/settings/index.json.jbuilder +3 -0
  18. data/app/views/api/settings/show.json.jbuilder +1 -0
  19. data/app/views/fluentd/settings/edit.html.haml +3 -0
  20. data/app/views/fluentd/settings/source_and_output.html.haml +19 -2
  21. data/app/views/layouts/application.html.erb +1 -30
  22. data/app/views/shared/vue/_in_tail_format.html.erb +11 -36
  23. data/app/views/shared/vue/_notification.html.erb +31 -0
  24. data/app/views/shared/vue/_setting.html.erb +23 -0
  25. data/app/views/shared/vue/_treeview.html.erb +2 -2
  26. data/bower.json +1 -1
  27. data/config/application.rb +1 -0
  28. data/config/locales/translation_en.yml +11 -7
  29. data/config/locales/translation_ja.yml +4 -0
  30. data/config/routes.rb +2 -0
  31. data/lib/fluentd-ui/version.rb +1 -1
  32. data/spec/features/fluentd/setting/source_and_output_spec.rb +157 -0
  33. data/spec/spec_helper.rb +1 -0
  34. data/spec/support/javascript_macro.rb +21 -0
  35. data/vendor/assets/javascripts/bower/vue/.bower.json +7 -7
  36. data/vendor/assets/javascripts/bower/vue/LICENSE +1 -1
  37. data/vendor/assets/javascripts/bower/vue/dist/vue.js +7822 -4768
  38. data/vendor/assets/javascripts/bower/vue/dist/vue.min.js +7 -7
  39. data/vendor/assets/javascripts/bower/vue/src/api/child.js +53 -0
  40. data/vendor/assets/javascripts/bower/vue/src/api/data.js +161 -0
  41. data/vendor/assets/javascripts/bower/vue/src/api/dom.js +211 -0
  42. data/vendor/assets/javascripts/bower/vue/src/api/events.js +176 -0
  43. data/vendor/assets/javascripts/bower/vue/src/api/global.js +146 -0
  44. data/vendor/assets/javascripts/bower/vue/src/api/lifecycle.js +144 -0
  45. data/vendor/assets/javascripts/bower/vue/src/batcher.js +52 -32
  46. data/vendor/assets/javascripts/bower/vue/src/cache.js +112 -0
  47. data/vendor/assets/javascripts/bower/vue/src/compiler/compile.js +549 -0
  48. data/vendor/assets/javascripts/bower/vue/src/compiler/transclude.js +163 -0
  49. data/vendor/assets/javascripts/bower/vue/src/config.js +74 -14
  50. data/vendor/assets/javascripts/bower/vue/src/directive.js +179 -219
  51. data/vendor/assets/javascripts/bower/vue/src/directives/attr.js +32 -0
  52. data/vendor/assets/javascripts/bower/vue/src/directives/class.js +18 -0
  53. data/vendor/assets/javascripts/bower/vue/src/directives/cloak.js +12 -0
  54. data/vendor/assets/javascripts/bower/vue/src/directives/component.js +214 -0
  55. data/vendor/assets/javascripts/bower/vue/src/directives/el.js +13 -0
  56. data/vendor/assets/javascripts/bower/vue/src/directives/html.js +30 -34
  57. data/vendor/assets/javascripts/bower/vue/src/directives/if.js +77 -46
  58. data/vendor/assets/javascripts/bower/vue/src/directives/index.js +22 -129
  59. data/vendor/assets/javascripts/bower/vue/src/directives/model/checkbox.js +25 -0
  60. data/vendor/assets/javascripts/bower/vue/src/directives/model/default.js +123 -0
  61. data/vendor/assets/javascripts/bower/vue/src/directives/model/index.js +56 -0
  62. data/vendor/assets/javascripts/bower/vue/src/directives/model/radio.js +26 -0
  63. data/vendor/assets/javascripts/bower/vue/src/directives/model/select.js +166 -0
  64. data/vendor/assets/javascripts/bower/vue/src/directives/on.js +51 -50
  65. data/vendor/assets/javascripts/bower/vue/src/directives/partial.js +36 -42
  66. data/vendor/assets/javascripts/bower/vue/src/directives/ref.js +24 -0
  67. data/vendor/assets/javascripts/bower/vue/src/directives/repeat.js +477 -226
  68. data/vendor/assets/javascripts/bower/vue/src/directives/show.js +8 -0
  69. data/vendor/assets/javascripts/bower/vue/src/directives/style.js +49 -37
  70. data/vendor/assets/javascripts/bower/vue/src/directives/text.js +15 -0
  71. data/vendor/assets/javascripts/bower/vue/src/directives/transition.js +12 -0
  72. data/vendor/assets/javascripts/bower/vue/src/directives/with.js +38 -41
  73. data/vendor/assets/javascripts/bower/vue/src/filters/array-filters.js +87 -0
  74. data/vendor/assets/javascripts/bower/vue/src/filters/index.js +135 -0
  75. data/vendor/assets/javascripts/bower/vue/src/instance/compile.js +71 -0
  76. data/vendor/assets/javascripts/bower/vue/src/instance/events.js +122 -0
  77. data/vendor/assets/javascripts/bower/vue/src/instance/init.js +76 -0
  78. data/vendor/assets/javascripts/bower/vue/src/instance/scope.js +217 -0
  79. data/vendor/assets/javascripts/bower/vue/src/observer/array.js +90 -0
  80. data/vendor/assets/javascripts/bower/vue/src/observer/dep.js +50 -0
  81. data/vendor/assets/javascripts/bower/vue/src/observer/index.js +235 -0
  82. data/vendor/assets/javascripts/bower/vue/src/observer/object.js +75 -0
  83. data/vendor/assets/javascripts/bower/vue/src/parsers/directive.js +159 -0
  84. data/vendor/assets/javascripts/bower/vue/src/parsers/expression.js +226 -0
  85. data/vendor/assets/javascripts/bower/vue/src/parsers/path.js +300 -0
  86. data/vendor/assets/javascripts/bower/vue/src/parsers/template.js +246 -0
  87. data/vendor/assets/javascripts/bower/vue/src/parsers/text.js +178 -0
  88. data/vendor/assets/javascripts/bower/vue/src/transition/css.js +189 -0
  89. data/vendor/assets/javascripts/bower/vue/src/transition/index.js +151 -0
  90. data/vendor/assets/javascripts/bower/vue/src/transition/js.js +43 -0
  91. data/vendor/assets/javascripts/bower/vue/src/util/debug.js +50 -0
  92. data/vendor/assets/javascripts/bower/vue/src/util/dom.js +176 -0
  93. data/vendor/assets/javascripts/bower/vue/src/util/env.js +74 -0
  94. data/vendor/assets/javascripts/bower/vue/src/util/filter.js +72 -0
  95. data/vendor/assets/javascripts/bower/vue/src/util/index.js +8 -0
  96. data/vendor/assets/javascripts/bower/vue/src/util/lang.js +175 -0
  97. data/vendor/assets/javascripts/bower/vue/src/util/merge-option.js +258 -0
  98. data/vendor/assets/javascripts/bower/vue/src/vue.js +84 -0
  99. data/vendor/assets/javascripts/bower/vue/src/watcher.js +240 -0
  100. metadata +65 -20
  101. data/app/assets/javascripts/setting_format.js +0 -15
  102. data/vendor/assets/javascripts/bower/vue/src/binding.js +0 -103
  103. data/vendor/assets/javascripts/bower/vue/src/compiler.js +0 -1037
  104. data/vendor/assets/javascripts/bower/vue/src/deps-parser.js +0 -65
  105. data/vendor/assets/javascripts/bower/vue/src/directives/model.js +0 -174
  106. data/vendor/assets/javascripts/bower/vue/src/directives/view.js +0 -56
  107. data/vendor/assets/javascripts/bower/vue/src/emitter.js +0 -97
  108. data/vendor/assets/javascripts/bower/vue/src/exp-parser.js +0 -190
  109. data/vendor/assets/javascripts/bower/vue/src/filters.js +0 -191
  110. data/vendor/assets/javascripts/bower/vue/src/fragment.js +0 -67
  111. data/vendor/assets/javascripts/bower/vue/src/main.js +0 -188
  112. data/vendor/assets/javascripts/bower/vue/src/observer.js +0 -446
  113. data/vendor/assets/javascripts/bower/vue/src/template-parser.js +0 -46
  114. data/vendor/assets/javascripts/bower/vue/src/text-parser.js +0 -96
  115. data/vendor/assets/javascripts/bower/vue/src/transition.js +0 -228
  116. data/vendor/assets/javascripts/bower/vue/src/utils.js +0 -326
  117. 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
+ }