pcapr-local 0.1.10
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/LICENSE.txt +20 -0
- data/README.md +64 -0
- data/Rakefile +57 -0
- data/VERSION +1 -0
- data/bin/pcap2par +49 -0
- data/bin/startpcapr +40 -0
- data/bin/stoppcapr +33 -0
- data/bin/xtractr +5 -0
- data/lib/environment.rb +106 -0
- data/lib/exe/xtractr +0 -0
- data/lib/mu/pcap.rb +110 -0
- data/lib/mu/pcap/ethernet.rb +148 -0
- data/lib/mu/pcap/header.rb +75 -0
- data/lib/mu/pcap/io_pair.rb +67 -0
- data/lib/mu/pcap/io_wrapper.rb +76 -0
- data/lib/mu/pcap/ip.rb +61 -0
- data/lib/mu/pcap/ipv4.rb +257 -0
- data/lib/mu/pcap/ipv6.rb +148 -0
- data/lib/mu/pcap/packet.rb +104 -0
- data/lib/mu/pcap/pkthdr.rb +155 -0
- data/lib/mu/pcap/reader.rb +61 -0
- data/lib/mu/pcap/reader/http_family.rb +170 -0
- data/lib/mu/pcap/sctp.rb +367 -0
- data/lib/mu/pcap/sctp/chunk.rb +123 -0
- data/lib/mu/pcap/sctp/chunk/data.rb +134 -0
- data/lib/mu/pcap/sctp/chunk/init.rb +100 -0
- data/lib/mu/pcap/sctp/chunk/init_ack.rb +68 -0
- data/lib/mu/pcap/sctp/parameter.rb +110 -0
- data/lib/mu/pcap/sctp/parameter/ip_address.rb +48 -0
- data/lib/mu/pcap/stream_packetizer.rb +72 -0
- data/lib/mu/pcap/tcp.rb +505 -0
- data/lib/mu/pcap/udp.rb +69 -0
- data/lib/mu/scenario/pcap.rb +164 -0
- data/lib/mu/scenario/pcap/fields.rb +50 -0
- data/lib/mu/scenario/pcap/rtp.rb +71 -0
- data/lib/pcapr_local.rb +159 -0
- data/lib/pcapr_local/config.rb +336 -0
- data/lib/pcapr_local/db.rb +197 -0
- data/lib/pcapr_local/scanner.rb +250 -0
- data/lib/pcapr_local/server.rb +178 -0
- data/lib/pcapr_local/www/favicon.ico +0 -0
- data/lib/pcapr_local/www/favicon.png +0 -0
- data/lib/pcapr_local/www/home/index.html +138 -0
- data/lib/pcapr_local/www/static/image/16x16/Cancel.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Cancel.png.1 +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Download.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Folder3.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Full Size.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Minus.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Plus.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/Search.png +0 -0
- data/lib/pcapr_local/www/static/image/16x16/User.png +0 -0
- data/lib/pcapr_local/www/static/image/48x48/Phone.png +0 -0
- data/lib/pcapr_local/www/static/image/48x48/Video.png +0 -0
- data/lib/pcapr_local/www/static/image/bar-orange.gif +0 -0
- data/lib/pcapr_local/www/static/image/beta.png +0 -0
- data/lib/pcapr_local/www/static/image/bg.png +0 -0
- data/lib/pcapr_local/www/static/image/blockquote.png +0 -0
- data/lib/pcapr_local/www/static/image/body-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/body-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl1-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl1-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl1-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl2-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl2-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl2-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl3-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl3-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl3-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl4-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl4-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl4-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl5-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl6-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl7-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-hl8-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/body-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/bottom-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/bottom-l.png +0 -0
- data/lib/pcapr_local/www/static/image/bottom-r.png +0 -0
- data/lib/pcapr_local/www/static/image/btn-search.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-1.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-2.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-3.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-4.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-5.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-6.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-7.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-hl1.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-hl2.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-hl3.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-hl4.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-pathway.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-section1.png +0 -0
- data/lib/pcapr_local/www/static/image/bullet-section2.png +0 -0
- data/lib/pcapr_local/www/static/image/collapsed.gif +0 -0
- data/lib/pcapr_local/www/static/image/crosslink.png +0 -0
- data/lib/pcapr_local/www/static/image/expanded.gif +0 -0
- data/lib/pcapr_local/www/static/image/favicon.ico +0 -0
- data/lib/pcapr_local/www/static/image/favicon.png +0 -0
- data/lib/pcapr_local/www/static/image/icon-author.png +0 -0
- data/lib/pcapr_local/www/static/image/icon-created.png +0 -0
- data/lib/pcapr_local/www/static/image/p-expand.gif +0 -0
- data/lib/pcapr_local/www/static/image/pcapr-logo.png +0 -0
- data/lib/pcapr_local/www/static/image/powered-by.png +0 -0
- data/lib/pcapr_local/www/static/image/section1-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/section1-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/section1-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/section2-bg.png +0 -0
- data/lib/pcapr_local/www/static/image/section2-h3.png +0 -0
- data/lib/pcapr_local/www/static/image/section2-readmore.png +0 -0
- data/lib/pcapr_local/www/static/image/status-alert.png +0 -0
- data/lib/pcapr_local/www/static/image/status-download.png +0 -0
- data/lib/pcapr_local/www/static/image/status-info.png +0 -0
- data/lib/pcapr_local/www/static/image/status-note.png +0 -0
- data/lib/pcapr_local/www/static/image/tab-round.png +0 -0
- data/lib/pcapr_local/www/static/image/throbber.gif +0 -0
- data/lib/pcapr_local/www/static/image/user.jpg +0 -0
- data/lib/pcapr_local/www/static/script/closet/async.js +421 -0
- data/lib/pcapr_local/www/static/script/closet/closet.api.js +241 -0
- data/lib/pcapr_local/www/static/script/closet/closet.folders.js +94 -0
- data/lib/pcapr_local/www/static/script/closet/closet.js +187 -0
- data/lib/pcapr_local/www/static/script/closet/closet.mr.js +219 -0
- data/lib/pcapr_local/www/static/script/closet/closet.options.js +359 -0
- data/lib/pcapr_local/www/static/script/closet/closet.quantity.js +73 -0
- data/lib/pcapr_local/www/static/script/closet/closet.render.js +205 -0
- data/lib/pcapr_local/www/static/script/closet/closet.report.js +86 -0
- data/lib/pcapr_local/www/static/script/closet/closet.reports.http.js +135 -0
- data/lib/pcapr_local/www/static/script/closet/closet.reports.overview.js +163 -0
- data/lib/pcapr_local/www/static/script/closet/closet.reports.sip.js +159 -0
- data/lib/pcapr_local/www/static/script/closet/closet.reports.tcp.js +72 -0
- data/lib/pcapr_local/www/static/script/closet/closet.reports.visualize.js +263 -0
- data/lib/pcapr_local/www/static/script/closet/closet.util.js +40 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery-1.4.2.min.js +154 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery-ui.js +10921 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.flot.js +2123 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.flot.selection.js +184 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.flot.stack.js +184 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.form.js +643 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.jsonp.min.js +3 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.menu.js +142 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.suggest.js +308 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.ui.core.js +203 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.ui.slider.js +629 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.ui.sortable.js +1055 -0
- data/lib/pcapr_local/www/static/script/jquery/jquery.ui.widget.js +236 -0
- data/lib/pcapr_local/www/static/script/json2.js +481 -0
- data/lib/pcapr_local/www/static/script/sammy/plugins/sammy.cache.js +115 -0
- data/lib/pcapr_local/www/static/script/sammy/plugins/sammy.template.js +117 -0
- data/lib/pcapr_local/www/static/script/sammy/sammy.js +1696 -0
- data/lib/pcapr_local/www/static/script/tipsy/jquery.tipsy.js +104 -0
- data/lib/pcapr_local/www/static/style/c3p0.css +116 -0
- data/lib/pcapr_local/www/static/style/jquery.suggest.css +27 -0
- data/lib/pcapr_local/www/static/style/page.css +1113 -0
- data/lib/pcapr_local/www/static/style/tipsy.css +7 -0
- data/lib/pcapr_local/www/templates/browse.services.template +10 -0
- data/lib/pcapr_local/www/templates/browse.template +77 -0
- data/lib/pcapr_local/www/templates/flows.template +38 -0
- data/lib/pcapr_local/www/templates/pcap.template +63 -0
- data/lib/pcapr_local/www/templates/sip.calls.template +35 -0
- data/lib/pcapr_local/www/templates/statistics.template +6 -0
- data/lib/pcapr_local/xtractr.rb +179 -0
- data/lib/pcapr_local/xtractr/instance.rb +172 -0
- data/pcapr-local.gemspec +297 -0
- data/test/mu/pcap/reader/tc_http_family.rb +251 -0
- data/test/mu/pcap/tc_ethernet.rb +71 -0
- data/test/mu/pcap/tc_header.rb +56 -0
- data/test/mu/pcap/tc_ipv4.rb +103 -0
- data/test/mu/pcap/tc_ipv6.rb +83 -0
- data/test/mu/pcap/tc_packet.rb +44 -0
- data/test/mu/pcap/tc_pair.rb +58 -0
- data/test/mu/pcap/tc_pkthdr.rb +33 -0
- data/test/mu/pcap/tc_reader.rb +76 -0
- data/test/mu/pcap/tc_tcp.rb +426 -0
- data/test/mu/pcap/tc_udp.rb +33 -0
- data/test/mu/pcap/tc_wrapper.rb +80 -0
- data/test/mu/scenario/pcap/tc_fields.rb +67 -0
- data/test/mu/scenario/pcap/tc_rtp.rb +135 -0
- data/test/mu/scenario/sip_signalled_call_1.pcap +0 -0
- data/test/mu/scenario/tc_pcap.rb +190 -0
- data/test/mu/scenario/test_data/arp.pcap +0 -0
- data/test/mu/scenario/test_data/dns.pcap +0 -0
- data/test/mu/scenario/test_data/http-v6.pcap +0 -0
- data/test/mu/scenario/test_data/http.pcap +0 -0
- data/test/mu/scenario/test_data/http_chunked.pcap +0 -0
- data/test/mu/scenario/test_data/http_deflate.pcap +0 -0
- data/test/mu/scenario/test_data/httpauth3.pcap +0 -0
- data/test/mu/scenario/test_data/icmp.pcap +0 -0
- data/test/mu/scenario/test_data/sip_signalled_call_1.pcap +0 -0
- data/test/mu/tc_pcap.rb +39 -0
- data/test/mu/testcase.rb +86 -0
- data/test/pcapr_local/arp.pcap +0 -0
- data/test/pcapr_local/data.js +3 -0
- data/test/pcapr_local/http_chunked.pcap +0 -0
- data/test/pcapr_local/tc_api.rb +181 -0
- data/test/pcapr_local/test.tgz +0 -0
- data/test/pcapr_local/test_scanner.rb +241 -0
- data/test/pcapr_local/test_xtractr.rb +219 -0
- data/test/pcapr_local/testcase.rb +107 -0
- data/test/test_export_to_scenario.sh +25 -0
- data/test/test_pcapr_local.rb +29 -0
- metadata +450 -0
@@ -0,0 +1,219 @@
|
|
1
|
+
// ------------------------------------------------------------------------
|
2
|
+
// A set of map/reduce functions that's passed into xtractr for various
|
3
|
+
// types of reporting
|
4
|
+
// ------------------------------------------------------------------------
|
5
|
+
var closet = closet || {};
|
6
|
+
closet.mr = (function() {
|
7
|
+
// Count the unique values of a field
|
8
|
+
// The values of the field are used as keys during map and the reduction
|
9
|
+
// is just the sum of each key
|
10
|
+
function _count(field) {
|
11
|
+
if (!field || typeof(field) !== 'string' || field.length === 0) {
|
12
|
+
throw "Need a field name to count";
|
13
|
+
}
|
14
|
+
|
15
|
+
var source = function(_name) {
|
16
|
+
return {
|
17
|
+
map: function(_pf) {
|
18
|
+
_pf.values(_name, function(_value) {
|
19
|
+
if (_value) {
|
20
|
+
if (typeof(_value) === 'string' && _value.length > 1024) {
|
21
|
+
_value = _value.slice(0,1024);
|
22
|
+
}
|
23
|
+
emit(_value, 1);
|
24
|
+
}
|
25
|
+
});
|
26
|
+
},
|
27
|
+
reduce: function(_key, _values) {
|
28
|
+
return sum(_values);
|
29
|
+
}
|
30
|
+
};
|
31
|
+
};
|
32
|
+
|
33
|
+
source = '(' + source.toString() + ')(' + JSON.stringify(field.replace(/^(pkt|flow)\./, '')) + ')';
|
34
|
+
return source;
|
35
|
+
}
|
36
|
+
|
37
|
+
// Count the matching objects, bucketized by the minute, hour, etc
|
38
|
+
function _count_over_time(bucket) {
|
39
|
+
bucket = Math.max(bucket || 60, 1);
|
40
|
+
|
41
|
+
var source = function(_bucket) {
|
42
|
+
return {
|
43
|
+
map: function(_pf) {
|
44
|
+
var _key = parseInt((_pf.time/_bucket).toFixed(0), 0);
|
45
|
+
emit(_key, 1);
|
46
|
+
},
|
47
|
+
reduce: function(_key, _values) {
|
48
|
+
return sum(_values);
|
49
|
+
}
|
50
|
+
};
|
51
|
+
};
|
52
|
+
|
53
|
+
source = '(' + source.toString() + ')(' + JSON.stringify(bucket) + ')';
|
54
|
+
return source;
|
55
|
+
};
|
56
|
+
|
57
|
+
// Count the matching objects, bucketized over time, except the reduction
|
58
|
+
// is by the specified field (think of stacked charts). For example:
|
59
|
+
// pkt.service:(dns || http) > count_x_over_time('service')
|
60
|
+
// will result in the count of dns, http packets for each of the time
|
61
|
+
// buckets.
|
62
|
+
function _count_x_over_time(field, bucket) {
|
63
|
+
if (!field || typeof(field) !== 'string' || field.length === 0) {
|
64
|
+
throw "Need a field name to count over";
|
65
|
+
}
|
66
|
+
|
67
|
+
bucket = Math.max(bucket || 60, 1);
|
68
|
+
|
69
|
+
var source = function(_field, _bucket) {
|
70
|
+
return {
|
71
|
+
map: function(_pf) {
|
72
|
+
var _key = parseInt((_pf.time/_bucket).toFixed(0), 0);
|
73
|
+
_pf.values(_field, function(_val) {
|
74
|
+
if (_val) {
|
75
|
+
if (typeof(_val) === 'string' && _val.length > 1024) {
|
76
|
+
_val = _val.slice(0,1024);
|
77
|
+
}
|
78
|
+
var _obj = {};
|
79
|
+
_obj[_val] = 1;
|
80
|
+
emit(_key, _obj);
|
81
|
+
}
|
82
|
+
});
|
83
|
+
},
|
84
|
+
reduce: function(_key, _values) {
|
85
|
+
var _rv = {};
|
86
|
+
for (var _i in _values) {
|
87
|
+
var _value = _values[_i];
|
88
|
+
for (var _k in _value) {
|
89
|
+
_rv[_k] = sum([ _rv[_k] || 0, _value[_k]]);
|
90
|
+
}
|
91
|
+
}
|
92
|
+
return _rv;
|
93
|
+
}
|
94
|
+
};
|
95
|
+
};
|
96
|
+
|
97
|
+
source = '(' + source.toString() + ')(' + JSON.stringify(field.replace(/^(pkt|flow)\./, '')) + ',' + JSON.stringify(bucket) + ')';
|
98
|
+
return source;
|
99
|
+
};
|
100
|
+
|
101
|
+
// Compute the min, max count of the values vfield with the values of kfield
|
102
|
+
// being the key used for the reduce
|
103
|
+
function _minmax(kfield, vfield) {
|
104
|
+
if (!kfield || typeof(kfield) !== 'string' || kfield.length === 0) {
|
105
|
+
throw "Need a field name for the key";
|
106
|
+
}
|
107
|
+
|
108
|
+
if (!vfield || typeof(vfield) !== 'string' || vfield.length === 0) {
|
109
|
+
throw "Need a field name for the value";
|
110
|
+
}
|
111
|
+
|
112
|
+
var source = function(_kfield, _vfield) {
|
113
|
+
return {
|
114
|
+
map: function(_pf) {
|
115
|
+
var _key = _pf[_kfield];
|
116
|
+
if (_key) {
|
117
|
+
if (typeof(_key) === 'string' && _key.length > 1024) {
|
118
|
+
_key = _key.slice(0,1024);
|
119
|
+
}
|
120
|
+
_pf.values(_vfield, function(_val) {
|
121
|
+
if (typeof(_val) === 'number') {
|
122
|
+
emit(_key, { min: _val, max: _val, count: 1 });
|
123
|
+
}
|
124
|
+
});
|
125
|
+
}
|
126
|
+
},
|
127
|
+
reduce: function(_key, _values) {
|
128
|
+
var _rv = {};
|
129
|
+
for (var _i in _values) {
|
130
|
+
var _value = _values[_i];
|
131
|
+
_rv.min = Math.min(_value.min, _rv.min || _value.min);
|
132
|
+
_rv.max = Math.max(_value.max, _rv.max || _value.max);
|
133
|
+
_rv.count = sum([ _rv.count || 0, _value.count]);
|
134
|
+
}
|
135
|
+
return _rv;
|
136
|
+
}
|
137
|
+
};
|
138
|
+
};
|
139
|
+
|
140
|
+
var _kfs = JSON.stringify(kfield.replace(/^(pkt|flow)\./, ''));
|
141
|
+
var _vfs = JSON.stringify(vfield.replace(/^(pkt|flow)\./, ''));
|
142
|
+
source = '(' + source.toString() + ')(' + _kfs + ',' + _vfs + ')';
|
143
|
+
return source;
|
144
|
+
};
|
145
|
+
|
146
|
+
// Sum the values of vfield, keyed by kfield
|
147
|
+
function _sum(kfield, vfield) {
|
148
|
+
if (!kfield || typeof(kfield) !== 'string' || kfield.length === 0) {
|
149
|
+
throw "Need a field name for the key";
|
150
|
+
}
|
151
|
+
|
152
|
+
if (!vfield || typeof(vfield) !== 'string' || vfield.length === 0) {
|
153
|
+
throw "Need a field name for the value";
|
154
|
+
}
|
155
|
+
|
156
|
+
var source = function(_kfield, _vfield) {
|
157
|
+
return {
|
158
|
+
map: function(_pf) {
|
159
|
+
var _key = _pf[_kfield];
|
160
|
+
if (_key) {
|
161
|
+
if (typeof(_key) === 'string' && _key.length > 1024) {
|
162
|
+
_key = _key.slice(0,1024);
|
163
|
+
}
|
164
|
+
_pf.values(_vfield, function(_val) {
|
165
|
+
if (typeof(_val) === 'number') {
|
166
|
+
emit(_key, _val);
|
167
|
+
}
|
168
|
+
});
|
169
|
+
}
|
170
|
+
},
|
171
|
+
reduce: function(_key, _values) {
|
172
|
+
return sum(_values);
|
173
|
+
}
|
174
|
+
};
|
175
|
+
};
|
176
|
+
|
177
|
+
var _kfs = JSON.stringify(kfield.replace(/^(pkt|flow)\./, ''));
|
178
|
+
var _vfs = JSON.stringify(vfield.replace(/^(pkt|flow)\./, ''));
|
179
|
+
source = '(' + source.toString() + ')(' + _kfs + ',' + _vfs + ')';
|
180
|
+
return source;
|
181
|
+
};
|
182
|
+
|
183
|
+
// Sum the value of field, bucketized by the minute, hour, etc
|
184
|
+
function _sum_over_time(field, bucket) {
|
185
|
+
if (!field || typeof(field) !== 'string' || field.length === 0) {
|
186
|
+
throw "Need a field name (with a numeric value) to sum over";
|
187
|
+
}
|
188
|
+
|
189
|
+
bucket = Math.max(bucket || 60, 1);
|
190
|
+
|
191
|
+
var source = function(_name, _bucket) {
|
192
|
+
return {
|
193
|
+
map: function(_pf) {
|
194
|
+
_pf.values(_name, function(_val) {
|
195
|
+
if (typeof(_val) === 'number') {
|
196
|
+
var _key = parseInt((_pf.time/_bucket).toFixed(0), 0);
|
197
|
+
emit(_key, _val);
|
198
|
+
}
|
199
|
+
});
|
200
|
+
},
|
201
|
+
reduce: function(_key, _values) {
|
202
|
+
return sum(_values);
|
203
|
+
}
|
204
|
+
};
|
205
|
+
};
|
206
|
+
|
207
|
+
source = '(' + source.toString() + ')(' + JSON.stringify(field.replace(/^(pkt|flow)\./, '')) + ',' + JSON.stringify(bucket) + ')';
|
208
|
+
return source;
|
209
|
+
};
|
210
|
+
|
211
|
+
return {
|
212
|
+
count: _count,
|
213
|
+
count_over_time: _count_over_time,
|
214
|
+
count_x_over_time: _count_x_over_time,
|
215
|
+
minmax: _minmax,
|
216
|
+
sum: _sum,
|
217
|
+
sum_over_time: _sum_over_time
|
218
|
+
};
|
219
|
+
}());
|
@@ -0,0 +1,359 @@
|
|
1
|
+
var closet = closet || {};
|
2
|
+
(function($) {
|
3
|
+
var Maker = function() { this.options = []; };
|
4
|
+
|
5
|
+
|
6
|
+
Maker.Boolean = function(args) { $.extend(this, args); };
|
7
|
+
Maker.Boolean.prototype.build = function(ctx, html) {
|
8
|
+
ctx.add(this, this.name, this.value);
|
9
|
+
html.push('<tr>');
|
10
|
+
html.push('<td><label for="' + this.id + '">' + this.label + '</label></td>');
|
11
|
+
html.push('<td><input id="' + this.id + '" class="boolean" type="checkbox"' + (this.value ? 'checked' : '') + '/></td>');
|
12
|
+
html.push('<tr>');
|
13
|
+
};
|
14
|
+
Maker.Boolean.prototype.coerce = function(value) {
|
15
|
+
this.value = value === 'true' || value === '1' || value === true || value === 1;
|
16
|
+
};
|
17
|
+
Maker.prototype.boolean = function(name, value, args) {
|
18
|
+
args = args || {};
|
19
|
+
this.options.push(new Maker.Boolean({
|
20
|
+
type: 'boolean',
|
21
|
+
name: name,
|
22
|
+
label: args.label || name,
|
23
|
+
value: value
|
24
|
+
}));
|
25
|
+
};
|
26
|
+
|
27
|
+
|
28
|
+
Maker.Choice = function(args) { $.extend(this, args); };
|
29
|
+
Maker.Choice.prototype.build = function(ctx, html) {
|
30
|
+
var self = this;
|
31
|
+
ctx.add(this, this.name, this.value);
|
32
|
+
html.push('<tr>');
|
33
|
+
html.push('<td>' + this.label + '</td>');
|
34
|
+
html.push('<td><select class="choice" id="' + this.id + '">');
|
35
|
+
$.each(this.values, function(i, v) {
|
36
|
+
html.push('<option ' + (v.toString() === self.value.toString() ? 'selected' : '') + '>' + v + '</option>');
|
37
|
+
});
|
38
|
+
html.push('</select></td>');
|
39
|
+
};
|
40
|
+
Maker.Choice.prototype.coerce = function(value) {
|
41
|
+
var self = this;
|
42
|
+
$.each(this.values, function(_, v) {
|
43
|
+
if (typeof v === 'string' && v === value) {
|
44
|
+
self.value = value;
|
45
|
+
} else if (typeof v === 'number' && v.toString() === value) {
|
46
|
+
self.value = parseInt(value, 10);
|
47
|
+
}
|
48
|
+
});
|
49
|
+
};
|
50
|
+
Maker.prototype.choice = function(name, value, args) {
|
51
|
+
args = args || {};
|
52
|
+
this.options.push(new Maker.Choice({
|
53
|
+
type: 'choice',
|
54
|
+
name: name,
|
55
|
+
label: args.label || name,
|
56
|
+
value: value,
|
57
|
+
values: args.values || [ value ]
|
58
|
+
}));
|
59
|
+
};
|
60
|
+
|
61
|
+
|
62
|
+
Maker.Group = function(args) { $.extend(this, args); };
|
63
|
+
Maker.Group.prototype.build = function(ctx, html) {
|
64
|
+
html.push('<tr class="group">');
|
65
|
+
html.push('<td colspan="2">' + this.label + '</td>');
|
66
|
+
html.push('</tr>');
|
67
|
+
|
68
|
+
ctx.push(this.name);
|
69
|
+
$.each(this.options, function(i, v) {
|
70
|
+
v.build(ctx, html);
|
71
|
+
});
|
72
|
+
ctx.pop();
|
73
|
+
};
|
74
|
+
// Intentionally no coerce function here, since group is not a leaf entity
|
75
|
+
Maker.prototype.group = function(name, args, cb) {
|
76
|
+
args = args || {};
|
77
|
+
var group = new Maker.Group({
|
78
|
+
type: 'group',
|
79
|
+
name: name,
|
80
|
+
label: args.label || name,
|
81
|
+
options: []
|
82
|
+
});
|
83
|
+
|
84
|
+
this.options.push(group);
|
85
|
+
|
86
|
+
var _opts = this.options;
|
87
|
+
this.options = group.options;
|
88
|
+
cb(maker);
|
89
|
+
this.options = _opts;
|
90
|
+
};
|
91
|
+
|
92
|
+
|
93
|
+
Maker.Number = function(args) { $.extend(this, args); };
|
94
|
+
Maker.Number.prototype.build = function(ctx, html) {
|
95
|
+
ctx.add(this, this.name, this.value);
|
96
|
+
html.push('<tr>');
|
97
|
+
html.push('<td>' + this.label + '</td>');
|
98
|
+
html.push('<td><input id="' + this.id + '" class="number" type="text" value="' + this.value + '"/></td>');
|
99
|
+
html.push('<tr>');
|
100
|
+
};
|
101
|
+
Maker.Number.prototype.coerce = function(value) {
|
102
|
+
if (/^\d+$/.test(value)) {
|
103
|
+
this.value = parseInt(value, 10);
|
104
|
+
}
|
105
|
+
};
|
106
|
+
Maker.prototype.number = function(name, value, args) {
|
107
|
+
args = args || {};
|
108
|
+
this.options.push(new Maker.Number({
|
109
|
+
type: 'number',
|
110
|
+
name: name,
|
111
|
+
label: args.label || name,
|
112
|
+
value: value,
|
113
|
+
required: args.required || true,
|
114
|
+
min: args.min,
|
115
|
+
max: args.max
|
116
|
+
}));
|
117
|
+
},
|
118
|
+
|
119
|
+
Maker.Select = function(args) { $.extend(this, args); };
|
120
|
+
Maker.Select.prototype.build = function(ctx, html) {
|
121
|
+
var self = this;
|
122
|
+
ctx.push(this.name);
|
123
|
+
|
124
|
+
$.each(this.values, function(i, v) {
|
125
|
+
ctx.add(self, 'select', i);
|
126
|
+
return false;
|
127
|
+
});
|
128
|
+
|
129
|
+
html.push('<tr>');
|
130
|
+
html.push('<td>' + this.label + '</td>');
|
131
|
+
html.push('<td>');
|
132
|
+
html.push('<select id="' + this.id + '" class="select">');
|
133
|
+
$.each(this.values, function(i, v) {
|
134
|
+
html.push('<option>' + i + '</option>');
|
135
|
+
});
|
136
|
+
html.push('</select>');
|
137
|
+
html.push('</td>');
|
138
|
+
html.push('</tr>');
|
139
|
+
html.push('</tbody>');
|
140
|
+
|
141
|
+
var count = 0;
|
142
|
+
$.each(this.values, function(i, v) {
|
143
|
+
html.push('<tbody class="when" select="' + this.id + '" id="' + i + '" style="display:' + ((count++ === 0) ? 'table-row-group' : 'none') + '; padding-left: 20px">');
|
144
|
+
ctx.push(i);
|
145
|
+
$.each(v, function(i_, v_) {
|
146
|
+
v_.build(ctx, html);
|
147
|
+
});
|
148
|
+
ctx.pop();
|
149
|
+
html.push('</tbody>');
|
150
|
+
});
|
151
|
+
ctx.pop();
|
152
|
+
|
153
|
+
html.push('<tbody>');
|
154
|
+
};
|
155
|
+
Maker.Select.prototype.coerce = function(value) {
|
156
|
+
var self = this;
|
157
|
+
$.each(self.values, function(_, v) {
|
158
|
+
if (v === value) {
|
159
|
+
self.value = value;
|
160
|
+
return false;
|
161
|
+
}
|
162
|
+
});
|
163
|
+
};
|
164
|
+
Maker.prototype.select = function(name, values, args) {
|
165
|
+
var self = this;
|
166
|
+
args = args || {};
|
167
|
+
var select = new Maker.Select({
|
168
|
+
type: 'select',
|
169
|
+
name: name,
|
170
|
+
label: args.label || name,
|
171
|
+
values: {}
|
172
|
+
});
|
173
|
+
|
174
|
+
this.options.push(select);
|
175
|
+
$.each(values, function(key, value) {
|
176
|
+
select.values[key] = [];
|
177
|
+
if (value) {
|
178
|
+
var _opts = self.options;
|
179
|
+
self.options = select.values[key];
|
180
|
+
value(self);
|
181
|
+
self.options = _opts;
|
182
|
+
}
|
183
|
+
});
|
184
|
+
};
|
185
|
+
|
186
|
+
|
187
|
+
Maker.String = function(args) { $.extend(this, args); };
|
188
|
+
Maker.String.prototype.build = function(ctx, element) {
|
189
|
+
ctx.add(e, this.name, this.value);
|
190
|
+
html.push('<tr>');
|
191
|
+
html.push('<td>' + this.label + '</td>');
|
192
|
+
html.push('<td><input id="' + this.id + '" class="string" type="text" value="' + this.value + '"/></td>');
|
193
|
+
html.push('<tr>');
|
194
|
+
};
|
195
|
+
Maker.String.prototype.coerce = function(value) {
|
196
|
+
this.value = value;
|
197
|
+
};
|
198
|
+
Maker.prototype.string = function(name, value, args) {
|
199
|
+
args = args || {};
|
200
|
+
this.options.push(new Maker.String({
|
201
|
+
type: 'string',
|
202
|
+
name: name,
|
203
|
+
label: args.label || name,
|
204
|
+
value: value,
|
205
|
+
required: args.required || false
|
206
|
+
}));
|
207
|
+
};
|
208
|
+
|
209
|
+
|
210
|
+
Maker.Text = function(args) { $.extend(this, args); };
|
211
|
+
Maker.String.prototype.build = function(ctx, element) {
|
212
|
+
ctx.add(this, this.name, this.value);
|
213
|
+
html.push('<tr>');
|
214
|
+
html.push('<td>' + this.label + '</td>');
|
215
|
+
html.push('<td><textarea id="' + this.id + '" class="text" rows="' + this.rows + '" cols="' + this.cols + '">' + this.value + '</textarea></td>');
|
216
|
+
html.push('<tr>');
|
217
|
+
};
|
218
|
+
Maker.Text.prototype.coerce = function(value) {
|
219
|
+
this.value = value;
|
220
|
+
};
|
221
|
+
Maker.prototype.text = function(name, value, args) {
|
222
|
+
args = args || {};
|
223
|
+
this.options.push(new Maker.Text({
|
224
|
+
type: 'text',
|
225
|
+
name: name,
|
226
|
+
label: args.label || name,
|
227
|
+
value: value,
|
228
|
+
rows: args.rows || 8,
|
229
|
+
cols: args.cols || 64
|
230
|
+
}));
|
231
|
+
};
|
232
|
+
|
233
|
+
|
234
|
+
Maker.prototype.build = function(kvs, changecb) {
|
235
|
+
this.reconcile(kvs);
|
236
|
+
|
237
|
+
var ctx = (function() {
|
238
|
+
var nextId = 0;
|
239
|
+
var map = {};
|
240
|
+
var opts = {};
|
241
|
+
var stack = [ opts ];
|
242
|
+
return {
|
243
|
+
add: function(e, key, value) {
|
244
|
+
e.id = nextId++;
|
245
|
+
e.opts = stack[stack.length-1];
|
246
|
+
e.opts[key] = value;
|
247
|
+
map[e.id.toString()] = e;
|
248
|
+
},
|
249
|
+
push: function(name) {
|
250
|
+
var _opts = stack[stack.length-1];
|
251
|
+
_opts[name] = {};
|
252
|
+
stack.push(_opts[name]);
|
253
|
+
},
|
254
|
+
pop: function() {
|
255
|
+
stack.pop();
|
256
|
+
},
|
257
|
+
opts: opts,
|
258
|
+
map: map
|
259
|
+
};
|
260
|
+
}());
|
261
|
+
|
262
|
+
var html = [ '<table>' ];
|
263
|
+
html.push('<tbody>');
|
264
|
+
$.each(this.options, function(_, e) {
|
265
|
+
e.build(ctx, html);
|
266
|
+
});
|
267
|
+
html.push('</tbody>');
|
268
|
+
html.push('</table>');
|
269
|
+
|
270
|
+
return $(html.join('')).
|
271
|
+
// opt_boolean
|
272
|
+
find('input.boolean').click(function() {
|
273
|
+
var id = $(this).attr('id');
|
274
|
+
var e = ctx.map[id];
|
275
|
+
e.opts[e.name] = $(this).is(':checked');
|
276
|
+
changecb(ctx.opts);
|
277
|
+
}).end().
|
278
|
+
|
279
|
+
// opt_choice
|
280
|
+
find('select.choice').change(function() {
|
281
|
+
var id = $(this).attr('id');
|
282
|
+
var e = ctx.map[id];
|
283
|
+
e.opts[e.name] = $(this).val();
|
284
|
+
changecb(ctx.opts);
|
285
|
+
}).end().
|
286
|
+
|
287
|
+
// opt_number
|
288
|
+
find('input.number').change(function() {
|
289
|
+
var id = $(this).attr('id');
|
290
|
+
var e = ctx.map[id];
|
291
|
+
var value = parseInt($(this).val(), 0);
|
292
|
+
value = Math.max(e.min || value, value);
|
293
|
+
value = Math.min(e.max || value, value);
|
294
|
+
e.opts[e.name] = value;
|
295
|
+
changecb(ctx.opts);
|
296
|
+
}).end().
|
297
|
+
|
298
|
+
// opt_select
|
299
|
+
find('select.select').change(function() {
|
300
|
+
var id = $(this).attr('id');
|
301
|
+
var when = $(this).find('option:selected').val();
|
302
|
+
ctx.map[id].opts.select = when;
|
303
|
+
$(this).parents('tbody').nextAll('tbody[select=' + id + ']').
|
304
|
+
css('display', 'none').
|
305
|
+
filter('#' + when + '.when').
|
306
|
+
css('display', 'table-row-group');
|
307
|
+
changecb(ctx.opts);
|
308
|
+
}).end().
|
309
|
+
|
310
|
+
// opt_string, opt_text
|
311
|
+
find('input.string,textarea.text').change(function() {
|
312
|
+
var id = $(this).attr('id');
|
313
|
+
var e = ctx.map[id];
|
314
|
+
e.opts[e.name] = $(this).val();
|
315
|
+
changecb(ctx.opts);
|
316
|
+
}).end();
|
317
|
+
};
|
318
|
+
|
319
|
+
|
320
|
+
Maker.prototype.reconcile = function(kvs) {
|
321
|
+
kvs = kvs || {};
|
322
|
+
|
323
|
+
var _flatten = function(obj, prefix, opts) {
|
324
|
+
prefix = (prefix && prefix.length > 0) ? (prefix + '.') : '';
|
325
|
+
$.each(opts, function(_, opt) {
|
326
|
+
if (opt.type === 'group') {
|
327
|
+
_keys(obj, prefix + opt.name, opts);
|
328
|
+
} else {
|
329
|
+
obj[prefix + opt.name] = opt;
|
330
|
+
}
|
331
|
+
});
|
332
|
+
return obj;
|
333
|
+
};
|
334
|
+
|
335
|
+
var _options = _flatten({}, null, this.options);
|
336
|
+
var coerced = {};
|
337
|
+
for (var key in _options) {
|
338
|
+
if (_options.hasOwnProperty(key)) {
|
339
|
+
if (kvs.hasOwnProperty(key)) {
|
340
|
+
_options[key].coerce(kvs[key]);
|
341
|
+
}
|
342
|
+
coerced[key] = _options[key].value;
|
343
|
+
}
|
344
|
+
}
|
345
|
+
|
346
|
+
return coerced;
|
347
|
+
};
|
348
|
+
|
349
|
+
|
350
|
+
closet.options = (function(cb) {
|
351
|
+
return {
|
352
|
+
create: function(cb) {
|
353
|
+
var maker = new Maker();
|
354
|
+
cb(maker);
|
355
|
+
return maker;
|
356
|
+
}
|
357
|
+
};
|
358
|
+
}());
|
359
|
+
}(jQuery));
|