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
@@ -1,191 +0,0 @@
1
- var utils = require('./utils'),
2
- get = utils.get,
3
- slice = [].slice,
4
- QUOTE_RE = /^'.*'$/,
5
- filters = module.exports = utils.hash()
6
-
7
- /**
8
- * 'abc' => 'Abc'
9
- */
10
- filters.capitalize = function (value) {
11
- if (!value && value !== 0) return ''
12
- value = value.toString()
13
- return value.charAt(0).toUpperCase() + value.slice(1)
14
- }
15
-
16
- /**
17
- * 'abc' => 'ABC'
18
- */
19
- filters.uppercase = function (value) {
20
- return (value || value === 0)
21
- ? value.toString().toUpperCase()
22
- : ''
23
- }
24
-
25
- /**
26
- * 'AbC' => 'abc'
27
- */
28
- filters.lowercase = function (value) {
29
- return (value || value === 0)
30
- ? value.toString().toLowerCase()
31
- : ''
32
- }
33
-
34
- /**
35
- * 12345 => $12,345.00
36
- */
37
- filters.currency = function (value, sign) {
38
- value = parseFloat(value)
39
- if (!value && value !== 0) return ''
40
- sign = sign || '$'
41
- var s = Math.floor(value).toString(),
42
- i = s.length % 3,
43
- h = i > 0 ? (s.slice(0, i) + (s.length > 3 ? ',' : '')) : '',
44
- f = '.' + value.toFixed(2).slice(-2)
45
- return sign + h + s.slice(i).replace(/(\d{3})(?=\d)/g, '$1,') + f
46
- }
47
-
48
- /**
49
- * args: an array of strings corresponding to
50
- * the single, double, triple ... forms of the word to
51
- * be pluralized. When the number to be pluralized
52
- * exceeds the length of the args, it will use the last
53
- * entry in the array.
54
- *
55
- * e.g. ['single', 'double', 'triple', 'multiple']
56
- */
57
- filters.pluralize = function (value) {
58
- var args = slice.call(arguments, 1)
59
- return args.length > 1
60
- ? (args[value - 1] || args[args.length - 1])
61
- : (args[value - 1] || args[0] + 's')
62
- }
63
-
64
- /**
65
- * A special filter that takes a handler function,
66
- * wraps it so it only gets triggered on specific keypresses.
67
- *
68
- * v-on only
69
- */
70
-
71
- var keyCodes = {
72
- enter : 13,
73
- tab : 9,
74
- 'delete' : 46,
75
- up : 38,
76
- left : 37,
77
- right : 39,
78
- down : 40,
79
- esc : 27
80
- }
81
-
82
- filters.key = function (handler, key) {
83
- if (!handler) return
84
- var code = keyCodes[key]
85
- if (!code) {
86
- code = parseInt(key, 10)
87
- }
88
- return function (e) {
89
- if (e.keyCode === code) {
90
- return handler.call(this, e)
91
- }
92
- }
93
- }
94
-
95
- /**
96
- * Filter filter for v-repeat
97
- */
98
- filters.filterBy = function (arr, searchKey, delimiter, dataKey) {
99
-
100
- // allow optional `in` delimiter
101
- // because why not
102
- if (delimiter && delimiter !== 'in') {
103
- dataKey = delimiter
104
- }
105
-
106
- // get the search string
107
- var search = stripQuotes(searchKey) || this.$get(searchKey)
108
- if (!search) return arr
109
- search = search.toLowerCase()
110
-
111
- // get the optional dataKey
112
- dataKey = dataKey && (stripQuotes(dataKey) || this.$get(dataKey))
113
-
114
- // convert object to array
115
- if (!Array.isArray(arr)) {
116
- arr = utils.objectToArray(arr)
117
- }
118
-
119
- return arr.filter(function (item) {
120
- return dataKey
121
- ? contains(get(item, dataKey), search)
122
- : contains(item, search)
123
- })
124
-
125
- }
126
-
127
- filters.filterBy.computed = true
128
-
129
- /**
130
- * Sort fitler for v-repeat
131
- */
132
- filters.orderBy = function (arr, sortKey, reverseKey) {
133
-
134
- var key = stripQuotes(sortKey) || this.$get(sortKey)
135
- if (!key) return arr
136
-
137
- // convert object to array
138
- if (!Array.isArray(arr)) {
139
- arr = utils.objectToArray(arr)
140
- }
141
-
142
- var order = 1
143
- if (reverseKey) {
144
- if (reverseKey === '-1') {
145
- order = -1
146
- } else if (reverseKey.charAt(0) === '!') {
147
- reverseKey = reverseKey.slice(1)
148
- order = this.$get(reverseKey) ? 1 : -1
149
- } else {
150
- order = this.$get(reverseKey) ? -1 : 1
151
- }
152
- }
153
-
154
- // sort on a copy to avoid mutating original array
155
- return arr.slice().sort(function (a, b) {
156
- a = get(a, key)
157
- b = get(b, key)
158
- return a === b ? 0 : a > b ? order : -order
159
- })
160
-
161
- }
162
-
163
- filters.orderBy.computed = true
164
-
165
- // Array filter helpers -------------------------------------------------------
166
-
167
- /**
168
- * String contain helper
169
- */
170
- function contains (val, search) {
171
- /* jshint eqeqeq: false */
172
- if (utils.isObject(val)) {
173
- for (var key in val) {
174
- if (contains(val[key], search)) {
175
- return true
176
- }
177
- }
178
- } else if (val != null) {
179
- return val.toString().toLowerCase().indexOf(search) > -1
180
- }
181
- }
182
-
183
- /**
184
- * Test whether a string is in quotes,
185
- * if yes return stripped string
186
- */
187
- function stripQuotes (str) {
188
- if (QUOTE_RE.test(str)) {
189
- return str.slice(1, -1)
190
- }
191
- }
@@ -1,67 +0,0 @@
1
- // string -> DOM conversion
2
- // wrappers originally from jQuery, scooped from component/domify
3
- var map = {
4
- legend : [1, '<fieldset>', '</fieldset>'],
5
- tr : [2, '<table><tbody>', '</tbody></table>'],
6
- col : [2, '<table><tbody></tbody><colgroup>', '</colgroup></table>'],
7
- _default : [0, '', '']
8
- }
9
-
10
- map.td =
11
- map.th = [3, '<table><tbody><tr>', '</tr></tbody></table>']
12
-
13
- map.option =
14
- map.optgroup = [1, '<select multiple="multiple">', '</select>']
15
-
16
- map.thead =
17
- map.tbody =
18
- map.colgroup =
19
- map.caption =
20
- map.tfoot = [1, '<table>', '</table>']
21
-
22
- map.text =
23
- map.circle =
24
- map.ellipse =
25
- map.line =
26
- map.path =
27
- map.polygon =
28
- map.polyline =
29
- map.rect = [1, '<svg xmlns="http://www.w3.org/2000/svg" version="1.1">','</svg>']
30
-
31
- var TAG_RE = /<([\w:]+)/
32
-
33
- module.exports = function (templateString) {
34
- var frag = document.createDocumentFragment(),
35
- m = TAG_RE.exec(templateString)
36
- // text only
37
- if (!m) {
38
- frag.appendChild(document.createTextNode(templateString))
39
- return frag
40
- }
41
-
42
- var tag = m[1],
43
- wrap = map[tag] || map._default,
44
- depth = wrap[0],
45
- prefix = wrap[1],
46
- suffix = wrap[2],
47
- node = document.createElement('div')
48
-
49
- node.innerHTML = prefix + templateString.trim() + suffix
50
- while (depth--) node = node.lastChild
51
-
52
- // one element
53
- if (node.firstChild === node.lastChild) {
54
- frag.appendChild(node.firstChild)
55
- return frag
56
- }
57
-
58
- // multiple nodes, return a fragment
59
- var child
60
- /* jshint boss: true */
61
- while (child = node.firstChild) {
62
- if (node.nodeType === 1) {
63
- frag.appendChild(child)
64
- }
65
- }
66
- return frag
67
- }
@@ -1,188 +0,0 @@
1
- var config = require('./config'),
2
- ViewModel = require('./viewmodel'),
3
- utils = require('./utils'),
4
- makeHash = utils.hash,
5
- assetTypes = ['directive', 'filter', 'partial', 'effect', 'component'],
6
- // Internal modules that are exposed for plugins
7
- pluginAPI = {
8
- utils: utils,
9
- config: config,
10
- transition: require('./transition'),
11
- observer: require('./observer')
12
- }
13
-
14
- ViewModel.options = config.globalAssets = {
15
- directives : require('./directives'),
16
- filters : require('./filters'),
17
- partials : makeHash(),
18
- effects : makeHash(),
19
- components : makeHash()
20
- }
21
-
22
- /**
23
- * Expose asset registration methods
24
- */
25
- assetTypes.forEach(function (type) {
26
- ViewModel[type] = function (id, value) {
27
- var hash = this.options[type + 's']
28
- if (!hash) {
29
- hash = this.options[type + 's'] = makeHash()
30
- }
31
- if (!value) return hash[id]
32
- if (type === 'partial') {
33
- value = utils.parseTemplateOption(value)
34
- } else if (type === 'component') {
35
- value = utils.toConstructor(value)
36
- } else if (type === 'filter') {
37
- utils.checkFilter(value)
38
- }
39
- hash[id] = value
40
- return this
41
- }
42
- })
43
-
44
- /**
45
- * Set config options
46
- */
47
- ViewModel.config = function (opts, val) {
48
- if (typeof opts === 'string') {
49
- if (val === undefined) {
50
- return config[opts]
51
- } else {
52
- config[opts] = val
53
- }
54
- } else {
55
- utils.extend(config, opts)
56
- }
57
- return this
58
- }
59
-
60
- /**
61
- * Expose an interface for plugins
62
- */
63
- ViewModel.use = function (plugin) {
64
- if (typeof plugin === 'string') {
65
- try {
66
- plugin = require(plugin)
67
- } catch (e) {
68
- utils.warn('Cannot find plugin: ' + plugin)
69
- return
70
- }
71
- }
72
-
73
- // additional parameters
74
- var args = [].slice.call(arguments, 1)
75
- args.unshift(this)
76
-
77
- if (typeof plugin.install === 'function') {
78
- plugin.install.apply(plugin, args)
79
- } else {
80
- plugin.apply(null, args)
81
- }
82
- return this
83
- }
84
-
85
- /**
86
- * Expose internal modules for plugins
87
- */
88
- ViewModel.require = function (module) {
89
- return pluginAPI[module]
90
- }
91
-
92
- ViewModel.extend = extend
93
- ViewModel.nextTick = utils.nextTick
94
-
95
- /**
96
- * Expose the main ViewModel class
97
- * and add extend method
98
- */
99
- function extend (options) {
100
-
101
- var ParentVM = this
102
-
103
- // extend data options need to be copied
104
- // on instantiation
105
- if (options.data) {
106
- options.defaultData = options.data
107
- delete options.data
108
- }
109
-
110
- // inherit options
111
- // but only when the super class is not the native Vue.
112
- if (ParentVM !== ViewModel) {
113
- options = inheritOptions(options, ParentVM.options, true)
114
- }
115
- utils.processOptions(options)
116
-
117
- var ExtendedVM = function (opts, asParent) {
118
- if (!asParent) {
119
- opts = inheritOptions(opts, options, true)
120
- }
121
- ParentVM.call(this, opts, true)
122
- }
123
-
124
- // inherit prototype props
125
- var proto = ExtendedVM.prototype = Object.create(ParentVM.prototype)
126
- utils.defProtected(proto, 'constructor', ExtendedVM)
127
-
128
- // allow extended VM to be further extended
129
- ExtendedVM.extend = extend
130
- ExtendedVM.super = ParentVM
131
- ExtendedVM.options = options
132
-
133
- // allow extended VM to add its own assets
134
- assetTypes.forEach(function (type) {
135
- ExtendedVM[type] = ViewModel[type]
136
- })
137
-
138
- // allow extended VM to use plugins
139
- ExtendedVM.use = ViewModel.use
140
- ExtendedVM.require = ViewModel.require
141
-
142
- return ExtendedVM
143
- }
144
-
145
- /**
146
- * Inherit options
147
- *
148
- * For options such as `data`, `vms`, `directives`, 'partials',
149
- * they should be further extended. However extending should only
150
- * be done at top level.
151
- *
152
- * `proto` is an exception because it's handled directly on the
153
- * prototype.
154
- *
155
- * `el` is an exception because it's not allowed as an
156
- * extension option, but only as an instance option.
157
- */
158
- function inheritOptions (child, parent, topLevel) {
159
- child = child || {}
160
- if (!parent) return child
161
- for (var key in parent) {
162
- if (key === 'el') continue
163
- var val = child[key],
164
- parentVal = parent[key]
165
- if (topLevel && typeof val === 'function' && parentVal) {
166
- // merge hook functions into an array
167
- child[key] = [val]
168
- if (Array.isArray(parentVal)) {
169
- child[key] = child[key].concat(parentVal)
170
- } else {
171
- child[key].push(parentVal)
172
- }
173
- } else if (
174
- topLevel &&
175
- (utils.isTrueObject(val) || utils.isTrueObject(parentVal))
176
- && !(parentVal instanceof ViewModel)
177
- ) {
178
- // merge toplevel object options
179
- child[key] = inheritOptions(val, parentVal)
180
- } else if (val === undefined) {
181
- // inherit if child doesn't override
182
- child[key] = parentVal
183
- }
184
- }
185
- return child
186
- }
187
-
188
- module.exports = ViewModel
@@ -1,446 +0,0 @@
1
- /* jshint proto:true */
2
-
3
- var Emitter = require('./emitter'),
4
- utils = require('./utils'),
5
- // cache methods
6
- def = utils.defProtected,
7
- isObject = utils.isObject,
8
- isArray = Array.isArray,
9
- hasOwn = ({}).hasOwnProperty,
10
- oDef = Object.defineProperty,
11
- slice = [].slice,
12
- // fix for IE + __proto__ problem
13
- // define methods as inenumerable if __proto__ is present,
14
- // otherwise enumerable so we can loop through and manually
15
- // attach to array instances
16
- hasProto = ({}).__proto__
17
-
18
- // Array Mutation Handlers & Augmentations ------------------------------------
19
-
20
- // The proxy prototype to replace the __proto__ of
21
- // an observed array
22
- var ArrayProxy = Object.create(Array.prototype)
23
-
24
- // intercept mutation methods
25
- ;[
26
- 'push',
27
- 'pop',
28
- 'shift',
29
- 'unshift',
30
- 'splice',
31
- 'sort',
32
- 'reverse'
33
- ].forEach(watchMutation)
34
-
35
- // Augment the ArrayProxy with convenience methods
36
- def(ArrayProxy, '$set', function (index, data) {
37
- return this.splice(index, 1, data)[0]
38
- }, !hasProto)
39
-
40
- def(ArrayProxy, '$remove', function (index) {
41
- if (typeof index !== 'number') {
42
- index = this.indexOf(index)
43
- }
44
- if (index > -1) {
45
- return this.splice(index, 1)[0]
46
- }
47
- }, !hasProto)
48
-
49
- /**
50
- * Intercep a mutation event so we can emit the mutation info.
51
- * we also analyze what elements are added/removed and link/unlink
52
- * them with the parent Array.
53
- */
54
- function watchMutation (method) {
55
- def(ArrayProxy, method, function () {
56
-
57
- var args = slice.call(arguments),
58
- result = Array.prototype[method].apply(this, args),
59
- inserted, removed
60
-
61
- // determine new / removed elements
62
- if (method === 'push' || method === 'unshift') {
63
- inserted = args
64
- } else if (method === 'pop' || method === 'shift') {
65
- removed = [result]
66
- } else if (method === 'splice') {
67
- inserted = args.slice(2)
68
- removed = result
69
- }
70
-
71
- // link & unlink
72
- linkArrayElements(this, inserted)
73
- unlinkArrayElements(this, removed)
74
-
75
- // emit the mutation event
76
- this.__emitter__.emit('mutate', '', this, {
77
- method : method,
78
- args : args,
79
- result : result,
80
- inserted : inserted,
81
- removed : removed
82
- })
83
-
84
- return result
85
-
86
- }, !hasProto)
87
- }
88
-
89
- /**
90
- * Link new elements to an Array, so when they change
91
- * and emit events, the owner Array can be notified.
92
- */
93
- function linkArrayElements (arr, items) {
94
- if (items) {
95
- var i = items.length, item, owners
96
- while (i--) {
97
- item = items[i]
98
- if (isWatchable(item)) {
99
- // if object is not converted for observing
100
- // convert it...
101
- if (!item.__emitter__) {
102
- convert(item)
103
- watch(item)
104
- }
105
- owners = item.__emitter__.owners
106
- if (owners.indexOf(arr) < 0) {
107
- owners.push(arr)
108
- }
109
- }
110
- }
111
- }
112
- }
113
-
114
- /**
115
- * Unlink removed elements from the ex-owner Array.
116
- */
117
- function unlinkArrayElements (arr, items) {
118
- if (items) {
119
- var i = items.length, item
120
- while (i--) {
121
- item = items[i]
122
- if (item && item.__emitter__) {
123
- var owners = item.__emitter__.owners
124
- if (owners) owners.splice(owners.indexOf(arr))
125
- }
126
- }
127
- }
128
- }
129
-
130
- // Object add/delete key augmentation -----------------------------------------
131
-
132
- var ObjProxy = Object.create(Object.prototype)
133
-
134
- def(ObjProxy, '$add', function (key, val) {
135
- if (hasOwn.call(this, key)) return
136
- this[key] = val
137
- convertKey(this, key, true)
138
- }, !hasProto)
139
-
140
- def(ObjProxy, '$delete', function (key) {
141
- if (!(hasOwn.call(this, key))) return
142
- // trigger set events
143
- this[key] = undefined
144
- delete this[key]
145
- this.__emitter__.emit('delete', key)
146
- }, !hasProto)
147
-
148
- // Watch Helpers --------------------------------------------------------------
149
-
150
- /**
151
- * Check if a value is watchable
152
- */
153
- function isWatchable (obj) {
154
- return typeof obj === 'object' && obj && !obj.$compiler
155
- }
156
-
157
- /**
158
- * Convert an Object/Array to give it a change emitter.
159
- */
160
- function convert (obj) {
161
- if (obj.__emitter__) return true
162
- var emitter = new Emitter()
163
- def(obj, '__emitter__', emitter)
164
- emitter
165
- .on('set', function (key, val, propagate) {
166
- if (propagate) propagateChange(obj)
167
- })
168
- .on('mutate', function () {
169
- propagateChange(obj)
170
- })
171
- emitter.values = utils.hash()
172
- emitter.owners = []
173
- return false
174
- }
175
-
176
- /**
177
- * Propagate an array element's change to its owner arrays
178
- */
179
- function propagateChange (obj) {
180
- var owners = obj.__emitter__.owners,
181
- i = owners.length
182
- while (i--) {
183
- owners[i].__emitter__.emit('set', '', '', true)
184
- }
185
- }
186
-
187
- /**
188
- * Watch target based on its type
189
- */
190
- function watch (obj) {
191
- if (isArray(obj)) {
192
- watchArray(obj)
193
- } else {
194
- watchObject(obj)
195
- }
196
- }
197
-
198
- /**
199
- * Augment target objects with modified
200
- * methods
201
- */
202
- function augment (target, src) {
203
- if (hasProto) {
204
- target.__proto__ = src
205
- } else {
206
- for (var key in src) {
207
- def(target, key, src[key])
208
- }
209
- }
210
- }
211
-
212
- /**
213
- * Watch an Object, recursive.
214
- */
215
- function watchObject (obj) {
216
- augment(obj, ObjProxy)
217
- for (var key in obj) {
218
- convertKey(obj, key)
219
- }
220
- }
221
-
222
- /**
223
- * Watch an Array, overload mutation methods
224
- * and add augmentations by intercepting the prototype chain
225
- */
226
- function watchArray (arr) {
227
- augment(arr, ArrayProxy)
228
- linkArrayElements(arr, arr)
229
- }
230
-
231
- /**
232
- * Define accessors for a property on an Object
233
- * so it emits get/set events.
234
- * Then watch the value itself.
235
- */
236
- function convertKey (obj, key, propagate) {
237
- var keyPrefix = key.charAt(0)
238
- if (keyPrefix === '$' || keyPrefix === '_') {
239
- return
240
- }
241
- // emit set on bind
242
- // this means when an object is observed it will emit
243
- // a first batch of set events.
244
- var emitter = obj.__emitter__,
245
- values = emitter.values
246
-
247
- init(obj[key], propagate)
248
-
249
- oDef(obj, key, {
250
- enumerable: true,
251
- configurable: true,
252
- get: function () {
253
- var value = values[key]
254
- // only emit get on tip values
255
- if (pub.shouldGet) {
256
- emitter.emit('get', key)
257
- }
258
- return value
259
- },
260
- set: function (newVal) {
261
- var oldVal = values[key]
262
- unobserve(oldVal, key, emitter)
263
- copyPaths(newVal, oldVal)
264
- // an immediate property should notify its parent
265
- // to emit set for itself too
266
- init(newVal, true)
267
- }
268
- })
269
-
270
- function init (val, propagate) {
271
- values[key] = val
272
- emitter.emit('set', key, val, propagate)
273
- if (isArray(val)) {
274
- emitter.emit('set', key + '.length', val.length, propagate)
275
- }
276
- observe(val, key, emitter)
277
- }
278
- }
279
-
280
- /**
281
- * When a value that is already converted is
282
- * observed again by another observer, we can skip
283
- * the watch conversion and simply emit set event for
284
- * all of its properties.
285
- */
286
- function emitSet (obj) {
287
- var emitter = obj && obj.__emitter__
288
- if (!emitter) return
289
- if (isArray(obj)) {
290
- emitter.emit('set', 'length', obj.length)
291
- } else {
292
- var key, val
293
- for (key in obj) {
294
- val = obj[key]
295
- emitter.emit('set', key, val)
296
- emitSet(val)
297
- }
298
- }
299
- }
300
-
301
- /**
302
- * Make sure all the paths in an old object exists
303
- * in a new object.
304
- * So when an object changes, all missing keys will
305
- * emit a set event with undefined value.
306
- */
307
- function copyPaths (newObj, oldObj) {
308
- if (!isObject(newObj) || !isObject(oldObj)) {
309
- return
310
- }
311
- var path, oldVal, newVal
312
- for (path in oldObj) {
313
- if (!(hasOwn.call(newObj, path))) {
314
- oldVal = oldObj[path]
315
- if (isArray(oldVal)) {
316
- newObj[path] = []
317
- } else if (isObject(oldVal)) {
318
- newVal = newObj[path] = {}
319
- copyPaths(newVal, oldVal)
320
- } else {
321
- newObj[path] = undefined
322
- }
323
- }
324
- }
325
- }
326
-
327
- /**
328
- * walk along a path and make sure it can be accessed
329
- * and enumerated in that object
330
- */
331
- function ensurePath (obj, key) {
332
- var path = key.split('.'), sec
333
- for (var i = 0, d = path.length - 1; i < d; i++) {
334
- sec = path[i]
335
- if (!obj[sec]) {
336
- obj[sec] = {}
337
- if (obj.__emitter__) convertKey(obj, sec)
338
- }
339
- obj = obj[sec]
340
- }
341
- if (isObject(obj)) {
342
- sec = path[i]
343
- if (!(hasOwn.call(obj, sec))) {
344
- obj[sec] = undefined
345
- if (obj.__emitter__) convertKey(obj, sec)
346
- }
347
- }
348
- }
349
-
350
- // Main API Methods -----------------------------------------------------------
351
-
352
- /**
353
- * Observe an object with a given path,
354
- * and proxy get/set/mutate events to the provided observer.
355
- */
356
- function observe (obj, rawPath, observer) {
357
-
358
- if (!isWatchable(obj)) return
359
-
360
- var path = rawPath ? rawPath + '.' : '',
361
- alreadyConverted = convert(obj),
362
- emitter = obj.__emitter__
363
-
364
- // setup proxy listeners on the parent observer.
365
- // we need to keep reference to them so that they
366
- // can be removed when the object is un-observed.
367
- observer.proxies = observer.proxies || {}
368
- var proxies = observer.proxies[path] = {
369
- get: function (key) {
370
- observer.emit('get', path + key)
371
- },
372
- set: function (key, val, propagate) {
373
- if (key) observer.emit('set', path + key, val)
374
- // also notify observer that the object itself changed
375
- // but only do so when it's a immediate property. this
376
- // avoids duplicate event firing.
377
- if (rawPath && propagate) {
378
- observer.emit('set', rawPath, obj, true)
379
- }
380
- },
381
- mutate: function (key, val, mutation) {
382
- // if the Array is a root value
383
- // the key will be null
384
- var fixedPath = key ? path + key : rawPath
385
- observer.emit('mutate', fixedPath, val, mutation)
386
- // also emit set for Array's length when it mutates
387
- var m = mutation.method
388
- if (m !== 'sort' && m !== 'reverse') {
389
- observer.emit('set', fixedPath + '.length', val.length)
390
- }
391
- }
392
- }
393
-
394
- // attach the listeners to the child observer.
395
- // now all the events will propagate upwards.
396
- emitter
397
- .on('get', proxies.get)
398
- .on('set', proxies.set)
399
- .on('mutate', proxies.mutate)
400
-
401
- if (alreadyConverted) {
402
- // for objects that have already been converted,
403
- // emit set events for everything inside
404
- emitSet(obj)
405
- } else {
406
- watch(obj)
407
- }
408
- }
409
-
410
- /**
411
- * Cancel observation, turn off the listeners.
412
- */
413
- function unobserve (obj, path, observer) {
414
-
415
- if (!obj || !obj.__emitter__) return
416
-
417
- path = path ? path + '.' : ''
418
- var proxies = observer.proxies[path]
419
- if (!proxies) return
420
-
421
- // turn off listeners
422
- obj.__emitter__
423
- .off('get', proxies.get)
424
- .off('set', proxies.set)
425
- .off('mutate', proxies.mutate)
426
-
427
- // remove reference
428
- observer.proxies[path] = null
429
- }
430
-
431
- // Expose API -----------------------------------------------------------------
432
-
433
- var pub = module.exports = {
434
-
435
- // whether to emit get events
436
- // only enabled during dependency parsing
437
- shouldGet : false,
438
-
439
- observe : observe,
440
- unobserve : unobserve,
441
- ensurePath : ensurePath,
442
- copyPaths : copyPaths,
443
- watch : watch,
444
- convert : convert,
445
- convertKey : convertKey
446
- }