├── .gitignore
├── main.go
├── model
├── camel.go
├── config.go
├── graphite.go
├── metric.go
├── route.go
├── service.go
└── utils.go
├── public
├── css
│ ├── AdminLTE.min.css
│ ├── bootstrap.css.map
│ ├── bootstrap.min.css
│ ├── flow.css
│ ├── font-awesome.min.css
│ ├── ionicons.min.css
│ ├── skin-blue.min.css
│ └── vis.min.css
├── favicon.ico
├── fonts
│ ├── FontAwesome.otf
│ ├── fontawesome-webfont.eot
│ ├── fontawesome-webfont.svg
│ ├── fontawesome-webfont.ttf
│ ├── fontawesome-webfont.woff
│ └── fontawesome-webfont.woff2
├── img
│ ├── camel-graph.png
│ ├── grafana_icon.svg
│ └── graphite-icon-57x57.png
├── index.html
├── info.json
├── js
│ ├── app.js
│ ├── controllers
│ │ ├── environment.js
│ │ ├── environments.js
│ │ └── optionsDialog.js
│ ├── providers
│ │ └── local-timeout.js
│ ├── services
│ │ └── utils.js
│ └── vendor
│ │ ├── angular-relative-date.min.js
│ │ ├── angular-relative-date.min.js.map
│ │ ├── angular-route.js
│ │ ├── angular-sanitize.js
│ │ ├── angular-ui-router.js
│ │ ├── angular.js
│ │ ├── jquery-1.11.1.min.js
│ │ ├── moment.min.js
│ │ ├── ui-bootstrap-tpls-0.14.3.min.js
│ │ ├── vis.min.js
│ │ └── xml2json.min.js
└── views
│ ├── environment.html
│ ├── environments.html
│ └── options.html
├── readme.md
└── services.json
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
2 | *.iml
3 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | _ "net/http/pprof"
5 | "net/http"
6 | "encoding/json"
7 | "fmt"
8 | "log"
9 | "flag"
10 | "github.com/avvero/camel-graph/model"
11 | )
12 |
13 | var (
14 | httpPort = flag.String("httpPort", "8080", "http server port")
15 | serviceUpdateIntervalSeconds = flag.Int("serviceUpdateIntervalSeconds", 60, "update interval for infos")
16 | routeUpdateIntervalSeconds = flag.Int("routeUpdateIntervalSeconds", 60, "update interval for infos")
17 | graphiteUrl = flag.String("graphiteUrl", "", "host and port to send plaint text metrics to graphite")
18 | graphiteRepeatSendOnFail = flag.Bool("graphiteRepeatSendOnFail", false, "repeat send metrcis to graphite on fail")
19 | )
20 |
21 | func main() {
22 | flag.Parse()
23 |
24 | config, err := model.ReadConfig("services.json")
25 | config.ServiceUpdateIntervalSeconds = *serviceUpdateIntervalSeconds
26 | config.RouteUpdateIntervalSeconds = *routeUpdateIntervalSeconds
27 | if err != nil {
28 | panic(fmt.Sprintf("Error during configuration %v", err))
29 | }
30 |
31 | var metricConsumer model.MetricConsumer
32 | if *graphiteUrl != "" {
33 | metricConsumer = model.NewGraphite(*graphiteUrl, *graphiteRepeatSendOnFail)
34 | log.Println("Metrics will be passed to graphite: " + *graphiteUrl)
35 | } else {
36 | metricConsumer = &model.MetricConsumerStub{}
37 | }
38 |
39 | instance, err := model.NewInstance(config, &metricConsumer)
40 | if err != nil {
41 | panic(fmt.Sprintf("Error during configuration %v", err))
42 | }
43 |
44 | // proxy stuff
45 | http.Handle("/", http.FileServer(http.Dir("public")))
46 | http.HandleFunc("/data", func(w http.ResponseWriter, r *http.Request) {
47 | var js []byte
48 | var err error
49 | envName := r.URL.Query().Get("env")
50 | var environmentToReturn *model.Environment
51 | if envName != "" {
52 | for _, environment := range instance.Environments {
53 | if environment.Name == envName {
54 | environmentToReturn = environment
55 | break
56 | }
57 | }
58 | }
59 | // marshal
60 | if envName != "" && environmentToReturn != nil {
61 | js, err = json.Marshal(environmentToReturn)
62 | } else {
63 | js, err = json.Marshal(instance)
64 | }
65 | if err != nil {
66 | http.Error(w, err.Error(), http.StatusInternalServerError)
67 | return
68 | }
69 | w.Header().Set("Content-Type", "application/json")
70 | w.Write(js)
71 | })
72 |
73 | log.Println("Http server started on port " + *httpPort)
74 | http.ListenAndServe(":" + *httpPort, nil)
75 | }
76 |
--------------------------------------------------------------------------------
/model/camel.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | type ReadRouteResponse struct {
4 | Value map[string]ReadRouteEntry `json:"value,omitempty"`
5 | Status int `json:"status,omitempty"`
6 | }
7 |
8 | type ReadRouteEntry struct {
9 | EndpointUri string
10 | CamelManagementName string
11 | Uptime string
12 | CamelId string
13 | RouteId string
14 | State string
15 | ExchangesTotal int
16 | ExchangesCompleted int
17 | ExchangesFailed int
18 | ExchangesInflight int
19 | MaxProcessingTime int
20 | MinProcessingTime int
21 | LastProcessingTime int
22 | MeanProcessingTime int
23 | TotalProcessingTime int
24 | FailuresHandled int
25 | Redeliveries int
26 | StartTimestamp string
27 | }
28 |
29 | type ReadResponse struct {
30 | Value string `json:"value,omitempty"`
31 | Error string `json:"error,omitempty"`
32 | Status int `json:"status,omitempty"`
33 | }
34 |
35 | type ReadRoutesEndpointsEntry struct {
36 | Routes *map[string]*ReadRouteEndpointsEntry `json:"routes,omitempty"`
37 | }
38 |
39 | type ReadRouteEndpointsEntry struct {
40 | Inputs []*RouteEndpointEntry `json:"inputs,omitempty"`
41 | Outputs []*RouteEndpointEntry `json:"outputs,omitempty"`
42 | }
43 |
44 | type RouteEndpointEntry struct {
45 | Uri string `json:"uri,omitempty"`
46 | }
47 |
--------------------------------------------------------------------------------
/model/config.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "os"
5 | "encoding/json"
6 | "time"
7 | )
8 |
9 | type InstanceConfig struct {
10 | Created time.Time
11 | Environments []*EnvironmentConfig
12 | ServiceUpdateIntervalSeconds int
13 | RouteUpdateIntervalSeconds int
14 | }
15 |
16 | type EnvironmentConfig struct {
17 | Name string
18 | Services []*ServiceConfig
19 | }
20 |
21 | type ServiceConfig struct {
22 | Name string
23 | Url string
24 | Color string
25 | Authorization *Authorization
26 | }
27 |
28 | type Authorization struct {
29 | Login string
30 | Pass string
31 | }
32 |
33 | // Read config
34 | func ReadConfig(fileName string) (*InstanceConfig, error) {
35 | rootConfig := InstanceConfig{}
36 | file, err := os.Open(fileName)
37 | if err != nil {
38 | return nil, err
39 | }
40 | decoder := json.NewDecoder(file)
41 | err = decoder.Decode(&rootConfig)
42 | if err != nil {
43 | return nil, err
44 | }
45 | return &rootConfig, nil
46 | }
47 |
--------------------------------------------------------------------------------
/model/graphite.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "log"
5 | "fmt"
6 | "net"
7 | "time"
8 | "github.com/fatih/pool"
9 | )
10 |
11 | type Graphite struct {
12 | pool *pool.Pool
13 | metrics chan *Metric
14 | repeatSendOnFail bool
15 | }
16 |
17 | const (
18 | StopCharacter = "\r\n\r\n"
19 | SleepDuration = 5
20 | )
21 |
22 | func NewGraphite(url string, repeatSendOnFail bool) *Graphite {
23 | //pool
24 | factory := func() (net.Conn, error) { return net.Dial("tcp", url) }
25 | pool, err := pool.NewChannelPool(5, 30, factory)
26 | if err != nil {
27 | panic(fmt.Sprintf("Error during creating new pool for graphite %v", err))
28 | }
29 |
30 | graphite := &Graphite{pool: &pool, metrics: make(chan *Metric, 1000), repeatSendOnFail: repeatSendOnFail}
31 | go func() {
32 | for {
33 | select {
34 | case metric := <-graphite.metrics:
35 | times := 1
36 | for err := graphite.send(metric); err != nil && times < 4; {
37 | sleepTime := SleepDuration * time.Duration(times)
38 | log.Println(fmt.Sprintf("Will sleep for %v (%v attempt of 3)", sleepTime, times))
39 | time.Sleep(sleepTime * time.Second)
40 | times++
41 | }
42 | }
43 | }
44 | }()
45 | return graphite
46 | }
47 | func (graphite *Graphite) send(metric *Metric) error {
48 | message := fmt.Sprintf("%s %v %v", metric.name, metric.value, metric.time.Unix())
49 |
50 | conn, err := (*graphite.pool).Get()
51 | if err != nil {
52 | if graphite.repeatSendOnFail {
53 | log.Println(fmt.Sprintf("Could not connected to graphite"))
54 | return err
55 | } else {
56 | log.Println(fmt.Sprintf("Could not connected to graphite, metrics will be lost"))
57 | return nil
58 | }
59 | }
60 | defer conn.Close()
61 |
62 | _, err = conn.Write([]byte(message + StopCharacter))
63 | if err != nil {
64 | log.Println(fmt.Sprintf("Something wrong with the connection it will be closed"))
65 | if pc, ok := conn.(*pool.PoolConn); ok {
66 | pc.MarkUnusable()
67 | pc.Close()
68 | }
69 | }
70 | //log.Println(fmt.Sprintf("Do send metrics: %s", message))
71 | //log.Println(fmt.Sprintf("available connections in the pool: %s", (*graphite.pool).Len()))
72 | return nil
73 | }
74 |
75 | func (graphite *Graphite) consumeMetric(metric *Metric) {
76 | graphite.metrics <- metric
77 | }
78 |
--------------------------------------------------------------------------------
/model/metric.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "time"
5 | "log"
6 | "fmt"
7 | )
8 |
9 | type Metric struct {
10 | name string
11 | time time.Time
12 | value interface{}
13 | }
14 |
15 | type MetricConsumer interface {
16 | consumeMetric(metric *Metric)
17 | }
18 |
19 | type MetricConsumerStub struct {
20 | }
21 |
22 | func (it *MetricConsumerStub) consumeMetric(metric *Metric) {
23 | message := fmt.Sprintf("%s %v %v", metric.name, metric.value, metric.time.Unix())
24 | if false {
25 | log.Println(fmt.Sprintf("log metrics: %s", message))
26 | }
27 | }
28 |
29 | func NewMetric(metricName string, value interface{}, t time.Time) *Metric {
30 | return &Metric{name: metricName, value: value, time: t}
31 | }
--------------------------------------------------------------------------------
/model/route.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import "time"
4 |
5 | type Route struct {
6 | Context string `json:"context,omitempty"`
7 | Name string `json:"name,omitempty"`
8 | Error string `json:"error,omitempty"`
9 | LastUpdated JsonTime `json:"lastUpdated"`
10 | State string `json:"state,omitempty"`
11 | Uptime string `json:"uptime,omitempty"`
12 | Schema string `json:"schema,omitempty"`
13 | EndpointUri string `json:"endpointUri,omitempty"`
14 | Endpoints *Endpoints `json:"endpoints,omitempty"`
15 | // metrics
16 | ExchangesTotal int `json:"exchangesTotal,omitempty"`
17 | ExchangesCompleted int `json:"exchangesCompleted,omitempty"`
18 | ExchangesFailed int `json:"exchangesFailed,omitempty"`
19 | ExchangesInflight int `json:"exchangesInflight,omitempty"`
20 | MaxProcessingTime int `json:"maxProcessingTime,omitempty"`
21 | MinProcessingTime int `json:"minProcessingTime,omitempty"`
22 | LastProcessingTime int `json:"lastProcessingTime,omitempty"`
23 | MeanProcessingTime int `json:"meanProcessingTime,omitempty"`
24 | TotalProcessingTime int `json:"totalProcessingTime,omitempty"`
25 | FailuresHandled int `json:"failuresHandled,omitempty"`
26 | Redeliveries int `json:"redeliveries,omitempty"`
27 | StartTimestamp string`json:"startTimestamp,omitempty"`
28 | // meta
29 |
30 | // DONE, FAILED
31 | UpdatingState string `json:"updatingState,omitempty"`
32 | metrics chan *Metric
33 | service *Service
34 | upd chan time.Time
35 | }
36 |
37 | type Context struct {
38 | Name string `json:"name,omitempty"`
39 | Routes []*Route `json:"routes,omitempty"`
40 | }
41 |
42 | type Endpoints struct {
43 | Inputs []string `json:"inputs,omitempty"`
44 | Outputs []string `json:"outputs,omitempty"`
45 | }
46 |
47 |
48 |
--------------------------------------------------------------------------------
/model/service.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "time"
5 | "sync"
6 | "errors"
7 | "encoding/json"
8 | "log"
9 | "fmt"
10 | "strings"
11 | "github.com/antchfx/xquery/xml"
12 | )
13 |
14 | const (
15 | UPDATE_STATE_IN_PROCESS = "in_process"
16 | UPDATE_STATE_DONE = "done"
17 | UPDATE_STATE_FAILED = "failed"
18 |
19 | NONE = "None"
20 | )
21 |
22 | const (
23 | GetRouteSchemaPath = "%s/jolokia/exec/org.apache.camel:context=%s,type=routes,name=\"%s\"/dumpRouteAsXml(boolean)/true"
24 | GetRouteEndpointsPath = "%s/jolokia/exec/org.apache.camel:context=%s,type=routes,name=\"%s\"/createRouteStaticEndpointJson(boolean)/true"
25 | GetRoutesPath = "/jolokia/read/org.apache.camel:type=routes,*"
26 | )
27 |
28 | type Instance struct {
29 | Environments []*Environment `json:"environments,omitempty"`
30 | }
31 |
32 | type Environment struct {
33 | Name string `json:"name,omitempty"`
34 | ServiceMap map[string]*Service `json:"serviceMap,omitempty"`
35 | }
36 |
37 | type JsonTime time.Time
38 |
39 | func (t JsonTime)MarshalJSON() ([]byte, error) {
40 | //do your serializing here
41 | stamp := fmt.Sprintf("\"%s\"", time.Time(t).Format(time.RFC3339))
42 | return []byte(stamp), nil
43 | }
44 |
45 | type Service struct {
46 | Name string `json:"name,omitempty"`
47 | Url string `json:"url,omitempty"`
48 | RouteMap map[string]*Route `json:"routeMap,omitempty"`
49 | LastUpdated JsonTime `json:"lastUpdated,"`
50 | Error string `json:"error,omitempty"`
51 | Color string `json:"color,omitempty"`
52 | // IN_PROCESS, DONE, FAILED
53 | UpdatingState string `json:"updatingState,omitempty"`
54 |
55 | updateMutex sync.Mutex
56 | metricConsumer *MetricConsumer
57 | config *ServiceConfig
58 | environment *Environment
59 | upd chan time.Time
60 | }
61 |
62 | func NewInstance(config *InstanceConfig, metricConsumer *MetricConsumer) (*Instance, error) {
63 | instance := &Instance{Environments: make([]*Environment, len(config.Environments))}
64 | for i, environmentConfig := range config.Environments {
65 | environment, err := NewEnvironment(config, environmentConfig, metricConsumer)
66 | if err != nil {
67 | return nil, err
68 | }
69 | instance.Environments[i] = environment
70 | }
71 | return instance, nil
72 | }
73 |
74 | func NewEnvironment(instanceConfig *InstanceConfig, envConfig *EnvironmentConfig, metricConsumer *MetricConsumer) (*Environment, error) {
75 | if envConfig.Name == "" {
76 | return nil, errors.New("environment name must not be empty")
77 | }
78 | environment := &Environment{
79 | Name: envConfig.Name,
80 | ServiceMap: make(map[string]*Service)}
81 | for _, serviceConfig := range envConfig.Services {
82 | service, err := NewService(instanceConfig, serviceConfig, environment, metricConsumer)
83 | if err != nil {
84 | return nil, err
85 | }
86 | environment.ServiceMap[service.Name] = service
87 | }
88 | return environment, nil
89 | }
90 |
91 | //Creates new service from config
92 | func NewService(instanceConfig *InstanceConfig, config *ServiceConfig, environment *Environment, metricConsumer *MetricConsumer) (*Service, error) {
93 | if config.Name == "" {
94 | return nil, errors.New("service name must not be empty")
95 | }
96 | if config.Url == "" {
97 | return nil, errors.New("service url must not be empty")
98 | }
99 | service := &Service{
100 | config: config,
101 | Name: config.Name,
102 | Url: config.Url,
103 | Color: config.Color,
104 | upd: make(chan time.Time, 100),
105 | RouteMap: make(map[string]*Route),
106 | UpdatingState: UPDATE_STATE_IN_PROCESS,
107 | environment: environment,
108 | metricConsumer: metricConsumer}
109 | go service.doUpdate(instanceConfig.ServiceUpdateIntervalSeconds, instanceConfig.RouteUpdateIntervalSeconds)
110 | return service, nil
111 | }
112 |
113 |
114 | func (service *Service) doUpdate(serviceUpdateIntervalSeconds int, routeUpdateIntervalSeconds int) {
115 | ticker := time.NewTicker(time.Duration(serviceUpdateIntervalSeconds) * time.Second)
116 | service.upd <- time.Now()
117 |
118 | for {
119 | select {
120 | case <-ticker.C:
121 | if service.UpdatingState == UPDATE_STATE_IN_PROCESS {
122 | //log.Printf("info: %s:%s is still has been updating", service.environment.Name, service.Name)
123 | } else {
124 | service.upd <- time.Now()
125 | }
126 | case t := <-service.upd:
127 | service.UpdatingState = UPDATE_STATE_IN_PROCESS
128 | err := service.update(t, routeUpdateIntervalSeconds)
129 | if err != nil {
130 | service.UpdatingState = UPDATE_STATE_FAILED
131 | service.Error = fmt.Sprintf("%s", err)
132 | } else {
133 | service.Error = ""
134 | service.UpdatingState = UPDATE_STATE_DONE
135 | service.LastUpdated = JsonTime(t)
136 | }
137 | }
138 | }
139 | }
140 |
141 | func (service *Service) update(t time.Time, routeUpdateIntervalSeconds int) error {
142 | url := service.config.Url + GetRoutesPath
143 | body, err := callEndpoint(url, service.config.Authorization)
144 | if err != nil {
145 | log.Printf("error: %s:%s error during getting routes from %s: %s", service.environment.Name, service.Name,
146 | service.config.Url, err)
147 | return err
148 | } else {
149 | for _, r := range service.RouteMap {
150 | r.State = NONE
151 | }
152 | // Merge from object
153 | response := &ReadRouteResponse{}
154 | json.Unmarshal(body, response)
155 | //log.Printf("info: %s:%s read route response %v", service.environment.Name, service.Name, response)
156 | for _, v := range response.Value {
157 | properContext := strings.Replace(v.CamelManagementName, " ", "_", -1)
158 | properRouteId := strings.Replace(v.RouteId, " ", "_", -1)
159 | routeName := fmt.Sprintf("%s.%s", properContext, properRouteId)
160 | route, exists := service.RouteMap[routeName]
161 | if !exists {
162 | route = &Route{
163 | Context: v.CamelManagementName,
164 | Name: v.RouteId,
165 | EndpointUri: v.EndpointUri,
166 | Endpoints: &Endpoints{
167 | Inputs: make([]string, 0),
168 | Outputs: make([]string, 0),
169 | },
170 | service: service,
171 | UpdatingState: UPDATE_STATE_IN_PROCESS,
172 |
173 | upd: make(chan time.Time, 100),
174 | metrics: make(chan *Metric, 1000)}
175 | service.RouteMap[routeName] = route
176 | // Add first input
177 | route.Endpoints.Inputs = append(route.Endpoints.Inputs, cleanEndpoint(route, v.EndpointUri))
178 | go route.doUpdate(routeUpdateIntervalSeconds)
179 | go route.sendMetrics()
180 | }
181 | route.State = v.State
182 | route.Uptime = v.Uptime
183 | // Metrics
184 | route.ExchangesTotal = v.ExchangesTotal
185 | route.ExchangesCompleted = v.ExchangesCompleted
186 | route.ExchangesFailed = v.ExchangesFailed
187 | route.ExchangesInflight = v.ExchangesInflight
188 | route.MaxProcessingTime = v.MaxProcessingTime
189 | route.MinProcessingTime = v.MinProcessingTime
190 | route.LastProcessingTime = v.LastProcessingTime
191 | route.MeanProcessingTime = v.MeanProcessingTime
192 | route.TotalProcessingTime = v.TotalProcessingTime
193 | route.FailuresHandled = v.FailuresHandled
194 | route.Redeliveries = v.Redeliveries
195 |
196 | route.collectMetrics(&v, t)
197 | }
198 | // stop update
199 | return nil
200 | }
201 | }
202 |
203 | func (route *Route) doUpdate(routeUpdateIntervalSeconds int) {
204 | ticker := time.NewTicker(time.Duration(routeUpdateIntervalSeconds) * time.Second)
205 | route.upd <- time.Now()
206 |
207 | for {
208 | select {
209 | case <-ticker.C:
210 | if route.State == NONE {
211 | log.Printf("info: %s:%s:%s route is outed of service", route.service.environment.Name,
212 | route.service.Name, route.Name)
213 | return
214 | }
215 | if route.UpdatingState == UPDATE_STATE_IN_PROCESS {
216 | //log.Printf("info: %s:%s:%s route is still has been updating", route.service.environment.Name,
217 | // route.service.Name, route.Name)
218 | } else {
219 | //log.Printf("info: %s:%s:%s route is updating", route.service.environment.Name,
220 | // route.service.Name, route.Name)
221 | route.upd <- time.Now()
222 | }
223 | case t := <-route.upd:
224 | route.UpdatingState = UPDATE_STATE_IN_PROCESS
225 | err:= route.update()
226 | if err != nil {
227 | route.UpdatingState = UPDATE_STATE_FAILED
228 | route.Error = fmt.Sprintf("%s", err)
229 | if route.State == NONE {
230 | //close(route.upd)
231 | //close(route.metrics)
232 | }
233 | } else {
234 | route.Error = ""
235 | route.UpdatingState = UPDATE_STATE_DONE
236 | route.LastUpdated = JsonTime(t)
237 | }
238 | }
239 | }
240 | }
241 |
242 | func (route *Route) update() error {
243 | // Endpoints
244 | url := fmt.Sprintf(GetRouteEndpointsPath, route.service.config.Url, route.Context, route.Name)
245 |
246 | //log.Printf("indo: %s:%s:%s getting route endoints from %s",
247 | // route.service.environment.Name, route.service.Name, route.Name, url)
248 |
249 | body, err := callEndpoint(url, route.service.config.Authorization)
250 | if err != nil {
251 | log.Printf("error: %s:%s:%s error during getting route endoints from %s: %s",
252 | route.service.environment.Name, route.service.Name, route.Name, route.service.config.Url, err)
253 | return err
254 | } else {
255 | response := &ReadResponse{}
256 | json.Unmarshal(body, response)
257 | if response.Status != 200 {
258 | log.Printf("error: %s:%s:%s error during getting route endoints from %s: %s",
259 | route.service.environment.Name, route.service.Name, route.Name, route.service.config.Url, response.Error)
260 | return errors.New(response.Error)
261 | } else {
262 | endpointsEntry := &ReadRoutesEndpointsEntry{}
263 | json.Unmarshal([]byte(response.Value), endpointsEntry)
264 | if endpointsEntry != nil && *endpointsEntry.Routes != nil{
265 | for _, v := range *endpointsEntry.Routes {
266 | if v.Outputs == nil {
267 | continue
268 | }
269 | for _, o := range v.Outputs {
270 |
271 | // skip configured and not filled endpoints
272 | if len(o.Uri) == 0 && strings.Contains(o.Uri, "{{") {
273 | continue
274 | }
275 | candidate := cleanEndpoint(route, o.Uri)
276 | if !contains(route.Endpoints.Outputs, candidate) {
277 | route.Endpoints.Outputs = append(route.Endpoints.Outputs, candidate)
278 | }
279 | }
280 | }
281 | }
282 | }
283 | }
284 | // Schema
285 | url = fmt.Sprintf(GetRouteSchemaPath, route.service.config.Url, route.Context, route.Name)
286 | //log.Printf("info: %s:%s:%s getting schema %s", route.service.environment.Name, route.service.Name, route.Name, url)
287 | body, err = callEndpoint(url, route.service.config.Authorization)
288 | if err != nil {
289 | log.Printf("error: %s:%s:%s error during getting schema from %s: %s", route.service.environment.Name,
290 | route.service.Name, route.Name, route.service.config.Url, err)
291 | return err
292 | } else {
293 | response := &ReadResponse{}
294 | json.Unmarshal(body, response)
295 | if response != nil && len(response.Value) > 0 {
296 | route.Schema = response.Value
297 | xmlNode, _ := xmlquery.Parse(strings.NewReader(route.Schema))
298 | // to endpoints
299 | toEndpoints := xmlquery.Find(xmlNode, "//to")
300 | for _, toEndpoint := range toEndpoints {
301 | uri := toEndpoint.SelectAttr("uri")
302 | if len(uri) > 0 {
303 | candidate := cleanEndpoint(route, uri)
304 | if !contains(route.Endpoints.Outputs, candidate) {
305 | route.Endpoints.Outputs = append(route.Endpoints.Outputs, candidate)
306 | }
307 | }
308 | }
309 | // from endpoints
310 | fromEndpoints := xmlquery.Find(xmlNode, "//from")
311 | for _, fromEndpoint := range fromEndpoints {
312 | uri := fromEndpoint.SelectAttr("uri")
313 | if len(uri) > 0 {
314 | candidate := cleanEndpoint(route, uri)
315 | if !contains(route.Endpoints.Inputs, candidate) {
316 | route.Endpoints.Inputs = append(route.Endpoints.Inputs, candidate)
317 | }
318 | }
319 | }
320 | }
321 | return nil
322 | }
323 | }
324 |
325 | func contains(list []string, entry string) bool {
326 | if list == nil || len(list) == 0 {
327 | return false
328 | }
329 | for _, v := range list {
330 | if v == entry {
331 | return true
332 | }
333 | }
334 | return false
335 | }
336 |
337 | func cleanEndpoints(route *Route, v []*RouteEndpointEntry) (inputs []string) {
338 | inputs = make([]string, 0)
339 | for _, i := range v {
340 | url := i.Uri
341 | inputs = append(inputs, cleanEndpoint(route, url))
342 | }
343 | return
344 | }
345 |
346 | func cleanEndpoint(route *Route, endpoint string) (result string) {
347 | endpoint = strings.Replace(endpoint, "%7B", "{", -1)
348 | endpoint = strings.Replace(endpoint, "%7D", "}", -1)
349 | endpoint = strings.Replace(endpoint, "://", ":", -1)
350 | endpoint = strings.Replace(endpoint, "activemq:", "jms:", -1)
351 | //take left part before ?
352 | endpoint = strings.Split(endpoint, "?")[0]
353 | if strings.Contains(endpoint, "VirtualTopic") {
354 | topicName := strings.Split(endpoint, "VirtualTopic")[1]
355 | endpoint = "VirtualTopic" + topicName
356 | }
357 | if strings.HasPrefix(endpoint, "direct") {
358 | endpoint = route.service.Name + ":" + endpoint
359 | }
360 | if strings.HasPrefix(endpoint, "timer") {
361 | endpoint = route.service.Name + ":" + endpoint
362 | }
363 | return endpoint
364 | }
365 |
366 | func (route *Route) collectMetrics(e *ReadRouteEntry, t time.Time) {
367 | properContext := strings.Replace(e.CamelManagementName, " ", "_", -1)
368 | properRouteId := strings.Replace(e.RouteId, " ", "_", -1)
369 | routeName := fmt.Sprintf("%s_%s", properContext, properRouteId)
370 | properRouteName := strings.Replace(routeName, ".", "_", -1)
371 |
372 | serviceName := fmt.Sprintf("camel-graph.%s.%s.%s", route.service.environment.Name, route.service.Name, properRouteName)
373 | route.metrics <- NewMetric(fmt.Sprintf("%s.%s", serviceName, "exchanges_total"), e.ExchangesTotal, t)
374 | route.metrics <- NewMetric(fmt.Sprintf("%s.%s", serviceName, "exchanges_completed"), e.ExchangesCompleted, t)
375 | route.metrics <- NewMetric(fmt.Sprintf("%s.%s", serviceName, "exchanges_failed"), e.ExchangesFailed, t)
376 | route.metrics <- NewMetric(fmt.Sprintf("%s.%s", serviceName, "exchanges_inflight"), e.ExchangesInflight, t)
377 | route.metrics <- NewMetric(fmt.Sprintf("%s.%s", serviceName, "max_processing_time"), e.MaxProcessingTime, t)
378 | route.metrics <- NewMetric(fmt.Sprintf("%s.%s", serviceName, "min_processing_time"), e.MinProcessingTime, t)
379 | route.metrics <- NewMetric(fmt.Sprintf("%s.%s", serviceName, "last_processing_time"), e.LastProcessingTime, t)
380 | route.metrics <- NewMetric(fmt.Sprintf("%s.%s", serviceName, "mean_processing_time"), e.MeanProcessingTime, t)
381 | route.metrics <- NewMetric(fmt.Sprintf("%s.%s", serviceName, "total_processing_time"), e.TotalProcessingTime, t)
382 | route.metrics <- NewMetric(fmt.Sprintf("%s.%s", serviceName, "failures_handled"), e.FailuresHandled, t)
383 | route.metrics <- NewMetric(fmt.Sprintf("%s.%s", serviceName, "redeliveries"), e.Redeliveries, t)
384 | }
385 |
386 | func (route *Route) sendMetrics() {
387 | for {
388 | select {
389 | case metric := <-route.metrics:
390 | (*route.service.metricConsumer).consumeMetric(metric)
391 | }
392 | }
393 | }
394 |
--------------------------------------------------------------------------------
/model/utils.go:
--------------------------------------------------------------------------------
1 | package model
2 |
3 | import (
4 | "net/http"
5 | "io/ioutil"
6 | "time"
7 | "errors"
8 | )
9 |
10 | func callEndpoint(url string, auth *Authorization) ([]byte, error) {
11 | client := &http.Client{
12 | Timeout: time.Duration(60 * time.Second),
13 | }
14 | req, err := http.NewRequest("GET", url, nil)
15 | if auth != nil {
16 | req.SetBasicAuth(auth.Login, auth.Pass)
17 | }
18 | resp, err := client.Do(req)
19 | if err != nil {
20 | return nil, err
21 | }
22 | if resp.StatusCode != 200 {
23 | return nil, errors.New("Status " + resp.Status)
24 | }
25 | defer resp.Body.Close()
26 | //return json.NewDecoder(resp.Body).Decode(target)
27 | return ioutil.ReadAll(resp.Body)
28 | }
29 |
--------------------------------------------------------------------------------
/public/css/flow.css:
--------------------------------------------------------------------------------
1 | .entry-log.level-ERROR,
2 | .entry-log .log-level.level-ERROR {
3 | color: #AC2925;
4 | }
5 |
6 | .entry-log .log-level.level-ERROR {
7 | font-weight: bold;
8 | }
9 |
10 | .entry-log .log-level.level-WARN {
11 | color: darkorange;
12 | font-weight: bold;
13 | }
14 |
15 | .entry-log .log-level.level-INFO {
16 | color: blue;
17 | font-weight: bold;
18 | }
19 |
20 | .entry-log .log-level.level-DEBUG {
21 | color: green;
22 | font-weight: bold;
23 | font-weight: bold;
24 | }
25 |
26 | .entry-log {
27 | /*display: none;*/
28 | word-wrap: break-word;
29 | }
30 |
31 | .entry-log.selected {
32 | /*display: none;*/
33 | background-color: #FFE485;
34 | }
35 |
36 | .show-level-ERROR .entry-log.level-ERROR {
37 | display: block
38 | }
39 |
40 | .show-level-WARN .entry-log.level-WARN {
41 | display: block
42 | }
43 |
44 | .show-level-INFO .entry-log.level-INFO {
45 | display: block
46 | }
47 |
48 | .show-level-DEBUG .entry-log.level-DEBUG {
49 | display: block
50 | }
51 |
52 | .show-level-TRACE .entry-log.level-TRACE {
53 | display: block
54 | }
55 |
56 | .entry-log span.log-level {
57 | width: 50px;
58 | display: inline-block;
59 | text-align: right;
60 | }
61 |
62 | .entry-log span.log-thread {
63 | /*min-width: 150px;*/
64 | display: inline-block;
65 | /*float: right;*/
66 | }
67 |
68 | .entry-log span.log-mdc {
69 | /*float: right;*/
70 | /*padding-right: 5px;*/
71 | }
72 |
73 | .entry-log div.log-msg-multiline {
74 | margin-left: 50px;
75 | padding-left: 5px;
76 | }
77 |
78 | .entry-log .log-msg-error-stack {
79 | margin-left: 50px;
80 | padding-left: 5px;
81 | }
82 |
83 | .flow {
84 | padding-left: 3px;
85 | padding-right: 33px;
86 | overflow: auto;
87 | position: fixed !important;
88 | position: absolute;
89 | top: 5px;
90 | right: 0;
91 | bottom: 0px;
92 | left: 155px;
93 | }
94 |
95 | .log-list {
96 | font-size: 12px;
97 | font-family: 'Helvetica Neue', Helvetica, Arial, sans-serif;
98 | word-wrap: break-word;
99 | }
100 |
101 | .searchable .flow {
102 | top: 54px;
103 | }
104 |
105 | .log-msg-error-stack.callout.callout-danger {
106 | background-color: white !important;
107 | color: #AC2925 !important;
108 | }
109 |
110 | @-webkit-keyframes pulse {
111 | 0% {
112 | -webkit-transform: scale(0);
113 | opacity: 0;
114 | }
115 | 8% {
116 | -webkit-transform: scale(0);
117 | opacity: 0;
118 | }
119 | 15% {
120 | -webkit-transform: scale(0.1);
121 | opacity: 1;
122 | }
123 | 30% {
124 | -webkit-transform: scale(0.5);
125 | opacity: 1;
126 | }
127 | 100% {
128 | opacity: 0;
129 | -webkit-transform: scale(1);
130 | }
131 | }
132 |
133 | @-moz-keyframes pulse {
134 | 0% {
135 | -moz-transform: scale(0);
136 | opacity: 0;
137 | }
138 | 8% {
139 | -moz-transform: scale(0);
140 | opacity: 0;
141 | }
142 | 15% {
143 | -moz-transform: scale(0.1);
144 | opacity: 1;
145 | }
146 | 30% {
147 | -moz-transform: scale(0.5);
148 | opacity: 1;
149 | }
150 | 100% {
151 | opacity: 0;
152 | -moz-transform: scale(1);
153 | }
154 | }
155 |
156 | .pulse_holder {
157 | display: block;
158 | z-index: 1030;
159 | position: relative;
160 | left: -32px;
161 | top: -20px;
162 | width: 16px;
163 | }
164 |
165 | .pulse_holder .pulse_marker {
166 | width: 16px;
167 | height: 16px;
168 | background: #969099;
169 | border-radius: 28px;
170 | }
171 |
172 | .pulse_holder.connected .pulse_marker {
173 | background: rgb(60, 141, 188);
174 | }
175 |
176 | .pulse_holder .pulse_rays {
177 | margin: 0 auto;
178 | border-radius: 100px;
179 | position: relative;
180 | right: 24px;
181 | top: -24px;
182 | z-index: 10;
183 | background-color: transparent;
184 | opacity: 0.1;
185 | width: 64px;
186 | height: 64px;
187 | border: 2px solid rgb(128, 123, 122);
188 | -webkit-border-radius: 100px;
189 | -moz-border-radius: 100px;
190 | -o-border-radius: 100px;
191 | -ms-border-radius: 100px;
192 | border-radius: 100px;
193 | /* Giving Animation Function */
194 | -webkit-animation: pulse 2s linear infinite;
195 | -moz-animation: pulse 2s linear infinite;
196 | border-image: initial;
197 | }
198 |
199 | .pulse_holder.connected .pulse_rays {
200 | border: 2px solid rgb(60, 141, 188);
201 | }
202 |
203 | .babies_container {
204 | width: auto;
205 | margin-right: auto;
206 | margin-left: auto;
207 | max-width: 520px;
208 | padding: 0 15px;
209 | }
210 |
211 | .babies_container.full_width {
212 | max-width: inherit;
213 | }
214 |
215 | .babies_container .endpoints pre {
216 | border-radius: 0px;
217 | }
218 |
219 | .pointer {
220 | cursor: pointer;
221 | }
222 |
223 | .scroll_to_top,
224 | .scroll_to_bottom {
225 | float: right;
226 | display: block;
227 | z-index: 1031;
228 | position: fixed;
229 | right: 5px;
230 | font-size: 24px;
231 | cursor: pointer;
232 | }
233 |
234 | .scroll_to_top {
235 | top: 5px;
236 | }
237 |
238 | .scroll_to_bottom {
239 | bottom: 0px;
240 | }
241 |
242 | .scroll_to_top .fa,
243 | .scroll_to_bottom .fa {
244 | font-size: 30px;
245 | }
246 |
247 | .help {
248 | z-index: 1035;
249 | position: fixed;
250 | display: none;
251 | font-size: 14px;
252 | }
253 |
254 | .help_back, .help_side-menu {
255 | z-index: 809;
256 | position: fixed;
257 | width: 100%;
258 | height: 100%;
259 | background-color: black;
260 | opacity: 0.3;
261 | top: 0px;
262 | }
263 |
264 | .help_side-menu {
265 | width: 150px;
266 | z-index: 1031;
267 | }
268 |
269 | .help.controls {
270 | left: 177px;
271 | margin-top: -34px;
272 | }
273 |
274 | .help.markers {
275 | left: 177px;
276 | margin-top: 8px;
277 | }
278 |
279 | .help.levels {
280 | left: 177px;
281 | margin-top: 24px;
282 | }
283 |
284 | .help.description {
285 | width: 250px;
286 | margin: auto;
287 | margin-top: 100px;
288 | position: relative;
289 | }
290 |
291 | .help.server_pulse {
292 | /* width: 250px; */
293 | top: 3px;
294 | left: 177px;
295 | position: fixed;
296 | }
297 |
298 | .show-help .help.markers,
299 | .show-help .help.server_pulse,
300 | .show-help .help.description,
301 | .show-help .help.controls,
302 | .show-help .help.levels {
303 | display: block;
304 | }
305 |
306 | .settings {
307 | height: 200px
308 | }
309 |
310 | .setting {
311 | padding-bottom: 5px;
312 | }
313 |
314 | .flow.with-settings {
315 | bottom: 250px;
316 | }
317 |
318 | .help.with-settings {
319 | bottom: 200px;
320 | }
321 |
322 | .container.with-settings .scroll_to_top {
323 | bottom: 263px;
324 | }
325 |
326 | .log-search-field {
327 | width: 400px;
328 | }
329 |
330 | /******
331 | * LTE Template
332 | */
333 |
334 | .content-wrapper {
335 | background-color: white;
336 | }
337 |
338 | .skin-blue .sidebar-form input[type="text"].filled, .skin-blue .sidebar-form input[type="text"].filled + .input-group-btn .btn {
339 | background-color: #fff;
340 | color: #666;
341 | }
342 |
343 | .github_link {
344 | bottom: 12px;
345 | right: 10px;
346 | position: fixed;
347 | z-index: 1030;
348 | }
349 |
350 | .sidebar-menu > li.github-menu-link {
351 | bottom: 0px;
352 | position: absolute;
353 | width: 150px;
354 | }
355 |
356 | .main-sidebar {
357 | width: 150px;
358 | }
359 |
360 | .main-header > .navbar {
361 | margin-left: 150px;
362 | margin-right: 36px;
363 | }
364 |
365 | .main-header .logo {
366 | width: 150px;
367 | }
368 |
369 | .content-wrapper {
370 | margin-left: 150px;
371 | }
372 |
373 | .skin-blue .sidebar-menu > li.active-red > a {
374 | color: #fff;
375 | background: #1e282c;
376 | border-left-color: #dd4b39;
377 | }
378 |
379 | .skin-blue .sidebar-menu > li.active-orange > a {
380 | color: #fff;
381 | background: #1e282c;
382 | border-left-color: #f39c12;
383 | }
384 |
385 | .skin-blue .sidebar-menu > li.deactivated > a {
386 | text-decoration: line-through;
387 | }
388 |
389 | .log-list .callout {
390 | border-radius: 3px;
391 | margin: 0;
392 | padding: 0;
393 | margin-left: 50px;
394 | padding-left: 5px;
395 | }
396 |
397 | .log-list .callout.callout-info {
398 | background-color: white !important;
399 | color: black !important;
400 | }
401 |
402 | .main-header {
403 | display: none;
404 | }
405 |
406 | .searchable .main-header {
407 | display: block;
408 | }
409 |
410 | .chart-box {
411 | margin-left: 150px;
412 | bottom: 0px;
413 | position: fixed;
414 | right: 22px;
415 | }
416 |
417 | .debug-box {
418 | margin-left: 150px;
419 | bottom: 100px;
420 | position: fixed;
421 | right: 22px;
422 | background-color: #001a35;
423 | color: white;
424 | z-index: 2000;
425 | }
426 |
427 | .main-sidebar .logo {
428 | color: #fff;
429 | border-bottom: 0 solid transparent;
430 | width: 150px;
431 | -webkit-transition: width .3s ease-in-out;
432 | -o-transition: width .3s ease-in-out;
433 | transition: width .3s ease-in-out;
434 | display: block;
435 | height: 50px;
436 | font-size: 20px;
437 | line-height: 50px;
438 | text-align: center;
439 | width: 150px;
440 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
441 | padding: 0 15px;
442 | font-weight: 300;
443 | overflow: hidden;
444 | }
445 |
446 | .main-sidebar, .left-side {
447 | padding-top: 0px;
448 | }
449 |
450 | .tooltip {
451 | position: fixed;
452 | }
453 |
454 | .search-result {
455 | position: fixed;
456 | width: inherit;
457 | padding-right: 118px;
458 | padding-left: 10px;
459 | padding-bottom: 10px;
460 | margin-bottom: 10px;
461 | }
462 |
463 | .search-result .log-list {
464 | max-height: 305px;
465 | overflow: auto;
466 | }
467 |
468 | .search-result .callout {
469 | padding: 3px;
470 | }
471 |
472 | .search-result .log-list {
473 | font-size: 10px;
474 | }
475 |
476 | .callout-navy {
477 | background-color: #FFFFFF;
478 | color: black;
479 | border-color: #1A2226;
480 | border: 1px solid #374850;
481 | border-left: 5px solid;
482 | }
483 |
484 | #flow-slider-container {
485 | position: fixed;
486 | right: 8px;
487 | height: 100%;
488 | z-index: 99;
489 | }
490 |
491 | .with-slider {
492 | overflow: hidden;
493 | }
494 |
495 | #flow-slider {
496 | height: 100%;
497 | padding-top: 46px;
498 | padding-bottom: 46px;
499 | }
500 |
501 | .log-level-buttons .btn {
502 | font-size: 21px;
503 | padding: 5px 2px;
504 | }
505 |
506 | .vertical {
507 | /*writing-mode:tb-rl;*/
508 | -webkit-transform: rotate(90deg);
509 | -moz-transform: rotate(90deg);
510 | -o-transform: rotate(90deg);
511 | -ms-transform: rotate(90deg);
512 | transform: rotate(90deg);
513 | white-space: nowrap;
514 | display: block;
515 | bottom: 0;
516 | /* width:20px; */
517 | /* height:20px; */
518 | }
519 |
520 | .btn-group.select-buttons .btn {
521 | font-size: 14px;
522 | padding: 5px 11px;
523 | }
524 |
525 | .select-next-prev-block {
526 | position: fixed;
527 | right: 31px;
528 | top: 4px;
529 | }
530 |
531 | .select-next-prev-block .fa {
532 | color: #ff851b;
533 | font-size: 19px;
534 | }
535 |
536 | .select-next-prev-block .fa:hover {
537 | color: #3C8DBC;
538 | }
539 |
540 | .searchable .select-next-prev-block {
541 | top: 54px;
542 | }
543 |
544 | .fade_in {
545 | animation: fadein 1s;
546 | -moz-animation: fadein 1s; /* Firefox */
547 | -webkit-animation: fadein 1s; /* Safari and Chrome */
548 | -o-animation: fadein 1s; /* Opera */
549 | }
550 |
551 | .fade_out {
552 | animation: fadeout 2s;
553 | -moz-animation: fadeout 2s; /* Firefox */
554 | -webkit-animation: fadeout 2s; /* Safari and Chrome */
555 | -o-animation: fadeout 2s; /* Opera */
556 | }
557 |
558 | @keyframes fadein {
559 | from {
560 | opacity: 0;
561 | }
562 | to {
563 | opacity: 1;
564 | }
565 | }
566 |
567 | @-moz-keyframes fadein { /* Firefox */
568 | from {
569 | opacity: 0;
570 | }
571 | to {
572 | opacity: 1;
573 | }
574 | }
575 |
576 | @-webkit-keyframes fadein { /* Safari and Chrome */
577 | from {
578 | opacity: 0;
579 | }
580 | to {
581 | opacity: 1;
582 | }
583 | }
584 |
585 | @-o-keyframes fadein { /* Opera */
586 | from {
587 | opacity: 0;
588 | }
589 | to {
590 | opacity: 1;
591 | }
592 | }
593 |
594 | .babys_list {
595 | left: 10px;
596 | }
597 |
598 | code.version {
599 | float: right;
600 | font-size: small;
601 | position: relative;
602 | top: -37px;
603 | }
604 |
605 | .babies {
606 | padding-left: 17px;
607 | padding-right: 17px;
608 | }
609 |
610 | .babies .components {
611 | padding-left: 5px;
612 | font-size: small;
613 | font-weight: 500;
614 | color: #666666;
615 | }
616 |
617 | .components code.version {
618 | float: none;
619 | font-size: small;
620 | position: inherit;
621 | padding: 2px 4px;
622 | font-size: 90%;
623 | border-radius: 4px;
624 | }
625 |
626 | code.tag {
627 | font-size: small;
628 | color: #4d6bc7;
629 | }
630 |
631 | code.tag.url {
632 | position: absolute;
633 | margin-top: 26px;
634 | color: #bbbbbb;
635 | background-color: inherit;
636 | }
637 |
638 | code.place {
639 | color: #40c73c;
640 | }
641 |
642 | .babies_container .place {
643 | float: right;
644 | }
645 |
646 | @keyframes shake {
647 | 0% {
648 | transform: translateX(0);
649 | }
650 | 12.5% {
651 | transform: translateX(-8px) rotateY(-5deg)
652 | }
653 | 37.5% {
654 | transform: translateX(7px) rotateY(4deg)
655 | }
656 | 62.5% {
657 | transform: translateX(-5px) rotateY(-2deg)
658 | }
659 | 87.5% {
660 | transform: translateX(4px) rotateY(1deg)
661 | }
662 | 100% {
663 | transform: translateX(0)
664 | }
665 | }
666 |
667 | .shake {
668 | animation: shake 1500ms ease-in-out;
669 | }
670 |
671 | .last_updated {
672 | position: relative;
673 | float: right;
674 | font-size: small;
675 | color: #bbbbbb;
676 | top: -10px;
677 | }
678 |
679 | .connection_error {
680 | color: #c7254e;
681 | font-size: initial;
682 | float: left;
683 | position: relative;
684 | top: -14px;
685 | }
686 |
687 | .crop {
688 | overflow: hidden;
689 | white-space: nowrap;
690 | text-overflow: ellipsis;
691 | }
692 |
693 | .tag.crop {
694 | width: 500px;
695 | }
696 |
697 | .app_name.crop {
698 | width: 460px;
699 | }
700 |
701 | .nav > li.link > a {
702 | position: relative;
703 | display: block;
704 | padding: 3px;
705 | }
706 |
707 | .nav > li.link > a > img {
708 | max-width: 42px;
709 | }
710 |
711 | #camel-map {
712 | width: 100%;
713 | margin: 0;
714 | padding: 0;
715 | height: 100%;
716 | }
717 |
718 | .camel-map-page-header {
719 | position: absolute;
720 | width: 100%;
721 | padding: 0px 40px 0px 0;
722 | margin: 0;
723 | }
724 |
725 | .vis-network {
726 | border: 1px dashed #eeeeee;
727 | }
728 |
729 | html, body {
730 | height: 100%;
731 | margin: 0px;
732 | padding: 0px;
733 | }
734 |
735 | .full_height {
736 | height: 100%;
737 | }
738 |
739 | .camel-graph-form {
740 | padding-top: 78px;
741 | padding-bottom: 50px;
742 | }
743 |
744 | .camel-graph-tab {
745 | padding-top: 5px;
746 | display: none;
747 | padding-left: 69px;
748 | padding-right: 15px;
749 | }
750 |
751 | .camel-graph-tab.visible {
752 | display: block;
753 | }
754 |
755 | .camel-graph-tab-data {
756 | overflow: auto;
757 | }
758 |
759 | .endpoint-list {
760 | overflow: auto;
761 | height: 100%;
762 | bottom: 0;
763 | }
764 |
765 | .endpoint-list ol, ul {
766 | padding-left: 0px;
767 | }
768 |
769 | .endpoint-list-entry {
770 | cursor: pointer;
771 | padding-left: 5px;
772 | }
773 |
774 | .endpoint-list-entry:hover {
775 | background-color: #c4e3f3;
776 | }
777 |
778 | .endpoint-list-entry.selected {
779 | background-color: #c9dec9;
780 | border-left: 3px solid #9bb19a;
781 | margin-left: 0px;
782 | padding-left: 2px;
783 | }
784 |
785 | .endpoint-list-header {
786 | color: #9bb19a;
787 | }
788 |
789 | .humpway-graph-legend {
790 | position: fixed;
791 | padding-top: 20px;
792 | padding-left: 20px;
793 | z-index: 100;
794 | }
795 |
796 | .humpway-graph-legend ul {
797 | margin: 0px;
798 | padding: 0px;
799 | list-style-type: none;
800 | }
801 |
802 | .humpway-graph-legend ul li {
803 | margin: 0px;
804 | padding: 0px;
805 | padding-bottom: 5px;
806 | }
807 |
808 | .humpway-graph-legend .circle {
809 | border-radius: 50%;
810 | width: 20px;
811 | height: 20px;
812 | display: inline-block;
813 | background: green;
814 | }
815 |
816 | .humpway-graph-legend ul li.service.failed .circle{
817 | background-color: #f0f0f0 !important;
818 | color: #b4b4b4 !important;
819 | }
820 |
821 | .humpway-graph-legend ul li.service.in_process .circle{
822 | -webkit-animation: blinking 1s infinite; /* Safari 4+ */
823 | -moz-animation: blinking 1s infinite; /* Fx 5+ */
824 | -o-animation: blinking 1s infinite; /* Opera 12+ */
825 | animation: blinking 1s infinite; /* IE 10+, Fx 29+ */
826 | }
827 |
828 | @-webkit-keyframes blinking {
829 | 0%, 49% {
830 | /*background-color: rgb(117, 209, 63);*/
831 | /*border: 3px solid #e50000;*/
832 | }
833 | 50%, 100% {
834 | background-color: #acacac;
835 | /*border: 3px solid rgb(117, 209, 63);*/
836 | }
837 | }
838 |
839 | .service-name {
840 | display: inline-block;
841 | padding-top: 0px;
842 | padding-left: 39px;
843 | }
844 |
845 | .camel-logo {
846 | padding-left: 110px;
847 | }
848 |
849 | .page-header.camel-map-page-header .camel-logo {
850 | padding-left: 0px;
851 | padding-top: 14px;
852 | position: absolute;
853 | }
854 |
855 | .page-header.camel-map-page-header .camel-logo img {
856 | max-height: 51px;
857 | }
858 |
859 | .page-header.camel-map-page-header h1 {
860 | margin-left: 57px;
861 | }
862 |
863 | .modal-body.scrollable {
864 | max-height: calc(100vh - 238px);
865 | overflow-y: auto;
866 | }
867 |
868 | .btn-flat {
869 | color: #333;
870 | background-color: #fff;
871 | border-color: #ccc;
872 | }
873 |
874 | .tabs-left {
875 | margin-top: 3rem;
876 | }
877 |
878 | .nav-tabs {
879 | float: left;
880 | border-bottom: 0;
881 | }
882 |
883 | .list-group {
884 | width: 100%;
885 | }
886 |
887 | .list-group .list-group-item {
888 | height: 42px;
889 | }
890 |
891 | .list-group .list-group-item h4, .list-group .list-group-item span {
892 | line-height: 11px;
893 | }
894 |
895 | .list-group-item.active, .list-group-item.active:focus, .list-group-item.active:hover {
896 | z-index: 2;
897 | color: #555;
898 | background-color: #e7e7e7;
899 | border: 1px solid #ddd;
900 | }
--------------------------------------------------------------------------------
/public/css/skin-blue.min.css:
--------------------------------------------------------------------------------
1 | .skin-blue .main-header .navbar {
2 | background-color: white;
3 | }
4 |
5 | .skin-blue .main-header .navbar .nav > li > a {
6 | color: #fff
7 | }
8 |
9 | .skin-blue .main-header .navbar .nav > li > a:hover, .skin-blue .main-header .navbar .nav > li > a:active, .skin-blue .main-header .navbar .nav > li > a:focus, .skin-blue .main-header .navbar .nav .open > a, .skin-blue .main-header .navbar .nav .open > a:hover, .skin-blue .main-header .navbar .nav .open > a:focus, .skin-blue .main-header .navbar .nav > .active > a {
10 | background: rgba(0, 0, 0, 0.1);
11 | color: #f6f6f6
12 | }
13 |
14 | .skin-blue .main-header .navbar .sidebar-toggle {
15 | color: #fff
16 | }
17 |
18 | .skin-blue .main-header .navbar .sidebar-toggle:hover {
19 | color: #f6f6f6;
20 | background: rgba(0, 0, 0, 0.1)
21 | }
22 |
23 | .skin-blue .main-header .navbar .sidebar-toggle {
24 | color: #fff
25 | }
26 |
27 | .skin-blue .main-header .navbar .sidebar-toggle:hover {
28 | background-color: #367fa9
29 | }
30 |
31 | @media (max-width: 767px) {
32 | .skin-blue .main-header .navbar .dropdown-menu li.divider {
33 | background-color: rgba(255, 255, 255, 0.1)
34 | }
35 |
36 | .skin-blue .main-header .navbar .dropdown-menu li a {
37 | color: #fff
38 | }
39 |
40 | .skin-blue .main-header .navbar .dropdown-menu li a:hover {
41 | background: #367fa9
42 | }
43 | }
44 |
45 | .skin-blue .main-header .logo {
46 | /*background-color: #367fa9;*/
47 | color: #fff;
48 | border-bottom: 0 solid transparent
49 | }
50 |
51 | .skin-blue .main-header .logo:hover {
52 | background-color: #357ca5
53 | }
54 |
55 | .skin-blue .main-header li.user-header {
56 | background-color: #3c8dbc
57 | }
58 |
59 | .skin-blue .content-header {
60 | background: transparent
61 | }
62 |
63 | .skin-blue .wrapper, .skin-blue .main-sidebar, .skin-blue .left-side {
64 | background-color: #222d32
65 | }
66 |
67 | .skin-blue .user-panel > .info, .skin-blue .user-panel > .info > a {
68 | color: #fff
69 | }
70 |
71 | .skin-blue .sidebar-menu > li.header {
72 | color: #4b646f;
73 | background: #1a2226
74 | }
75 |
76 | .skin-blue .sidebar-menu > li > a {
77 | border-left: 3px solid transparent
78 | }
79 |
80 | .skin-blue .sidebar-menu > li:hover > a, .skin-blue .sidebar-menu > li.active > a {
81 | color: #fff;
82 | background: #1e282c;
83 | border-left-color: #3c8dbc
84 | }
85 |
86 | .skin-blue .sidebar-menu > li > .treeview-menu {
87 | margin: 0 1px;
88 | background: #2c3b41
89 | }
90 |
91 | .skin-blue .sidebar a {
92 | color: #b8c7ce
93 | }
94 |
95 | .skin-blue .sidebar a:hover {
96 | text-decoration: none
97 | }
98 |
99 | .skin-blue .treeview-menu > li > a {
100 | color: #8aa4af
101 | }
102 |
103 | .skin-blue .treeview-menu > li.active > a, .skin-blue .treeview-menu > li > a:hover {
104 | color: #fff
105 | }
106 |
107 | .skin-blue .sidebar-form {
108 | border-radius: 3px;
109 | border: 1px solid #374850;
110 | margin: 10px 10px
111 | }
112 |
113 | .skin-blue .sidebar-form input[type="text"], .skin-blue .sidebar-form .btn {
114 | box-shadow: none;
115 | background-color: #374850;
116 | border: 1px solid transparent;
117 | height: 35px;
118 | -webkit-transition: all .3s ease-in-out;
119 | -o-transition: all .3s ease-in-out;
120 | transition: all .3s ease-in-out
121 | }
122 |
123 | .skin-blue .sidebar-form input[type="text"] {
124 | color: #666;
125 | border-top-left-radius: 2px;
126 | border-top-right-radius: 0;
127 | border-bottom-right-radius: 0;
128 | border-bottom-left-radius: 2px
129 | }
130 |
131 | .skin-blue .sidebar-form input[type="text"]:focus, .skin-blue .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
132 | background-color: #fff;
133 | color: #666
134 | }
135 |
136 | .skin-blue .sidebar-form input[type="text"]:focus + .input-group-btn .btn {
137 | border-left-color: #fff
138 | }
139 |
140 | .skin-blue .sidebar-form .btn {
141 | color: #999;
142 | border-top-left-radius: 0;
143 | border-top-right-radius: 2px;
144 | border-bottom-right-radius: 2px;
145 | border-bottom-left-radius: 0
146 | }
147 |
148 | .skin-blue.layout-top-nav .main-header > .logo {
149 | background-color: #3c8dbc;
150 | color: #fff;
151 | border-bottom: 0 solid transparent
152 | }
153 |
154 | .skin-blue.layout-top-nav .main-header > .logo:hover {
155 | background-color: #3b8ab8
156 | }
--------------------------------------------------------------------------------
/public/css/vis.min.css:
--------------------------------------------------------------------------------
1 | .vis .overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-active{box-shadow:0 0 10px #86d5f8}.vis [class*=span]{min-height:0;width:auto}div.vis-configuration{position:relative;display:block;float:left;font-size:12px}div.vis-configuration-wrapper{display:block;width:700px}div.vis-configuration-wrapper::after{clear:both;content:"";display:block}div.vis-configuration.vis-config-option-container{display:block;width:495px;background-color:#fff;border:2px solid #f7f8fa;border-radius:4px;margin-top:20px;left:10px;padding-left:5px}div.vis-configuration.vis-config-button{display:block;width:495px;height:25px;vertical-align:middle;line-height:25px;background-color:#f7f8fa;border:2px solid #ceced0;border-radius:4px;margin-top:20px;left:10px;padding-left:5px;cursor:pointer;margin-bottom:30px}div.vis-configuration.vis-config-button.hover{background-color:#4588e6;border:2px solid #214373;color:#fff}div.vis-configuration.vis-config-item{display:block;float:left;width:495px;height:25px;vertical-align:middle;line-height:25px}div.vis-configuration.vis-config-item.vis-config-s2{left:10px;background-color:#f7f8fa;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s3{left:20px;background-color:#e4e9f0;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-item.vis-config-s4{left:30px;background-color:#cfd8e6;padding-left:5px;border-radius:3px}div.vis-configuration.vis-config-header{font-size:18px;font-weight:700}div.vis-configuration.vis-config-label{width:120px;height:25px;line-height:25px}div.vis-configuration.vis-config-label.vis-config-s3{width:110px}div.vis-configuration.vis-config-label.vis-config-s4{width:100px}div.vis-configuration.vis-config-colorBlock{top:1px;width:30px;height:19px;border:1px solid #444;border-radius:2px;padding:0;margin:0;cursor:pointer}input.vis-configuration.vis-config-checkbox{left:-5px}input.vis-configuration.vis-config-rangeinput{position:relative;top:-5px;width:60px;padding:1px;margin:0;pointer-events:none}input.vis-configuration.vis-config-range{-webkit-appearance:none;border:0 solid #fff;background-color:rgba(0,0,0,0);width:300px;height:20px}input.vis-configuration.vis-config-range::-webkit-slider-runnable-track{width:300px;height:5px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-webkit-slider-thumb{-webkit-appearance:none;border:1px solid #14334b;height:17px;width:17px;border-radius:50%;background:#3876c2;background:-moz-linear-gradient(top,#3876c2 0,#385380 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#3876c2),color-stop(100%,#385380));background:-webkit-linear-gradient(top,#3876c2 0,#385380 100%);background:-o-linear-gradient(top,#3876c2 0,#385380 100%);background:-ms-linear-gradient(top,#3876c2 0,#385380 100%);background:linear-gradient(to bottom,#3876c2 0,#385380 100%);box-shadow:#111927 0 0 1px 0;margin-top:-7px}input.vis-configuration.vis-config-range:focus{outline:0}input.vis-configuration.vis-config-range:focus::-webkit-slider-runnable-track{background:#9d9d9d;background:-moz-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#9d9d9d),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-o-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#9d9d9d 0,#c8c8c8 99%);background:linear-gradient(to bottom,#9d9d9d 0,#c8c8c8 99%)}input.vis-configuration.vis-config-range::-moz-range-track{width:300px;height:10px;background:#dedede;background:-moz-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#dedede),color-stop(99%,#c8c8c8));background:-webkit-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-o-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:-ms-linear-gradient(top,#dedede 0,#c8c8c8 99%);background:linear-gradient(to bottom,#dedede 0,#c8c8c8 99%);border:1px solid #999;box-shadow:#aaa 0 0 3px 0;border-radius:3px}input.vis-configuration.vis-config-range::-moz-range-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:-moz-focusring{outline:1px solid #fff;outline-offset:-1px}input.vis-configuration.vis-config-range::-ms-track{width:300px;height:5px;background:0 0;border-color:transparent;border-width:6px 0;color:transparent}input.vis-configuration.vis-config-range::-ms-fill-lower{background:#777;border-radius:10px}input.vis-configuration.vis-config-range::-ms-fill-upper{background:#ddd;border-radius:10px}input.vis-configuration.vis-config-range::-ms-thumb{border:none;height:16px;width:16px;border-radius:50%;background:#385380}input.vis-configuration.vis-config-range:focus::-ms-fill-lower{background:#888}input.vis-configuration.vis-config-range:focus::-ms-fill-upper{background:#ccc}.vis-configuration-popup{position:absolute;background:rgba(57,76,89,.85);border:2px solid #f2faff;line-height:30px;height:30px;width:150px;text-align:center;color:#fff;font-size:14px;border-radius:4px;-webkit-transition:opacity .3s ease-in-out;-moz-transition:opacity .3s ease-in-out;transition:opacity .3s ease-in-out}.vis-configuration-popup:after,.vis-configuration-popup:before{left:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}.vis-configuration-popup:after{border-color:rgba(136,183,213,0);border-left-color:rgba(57,76,89,.85);border-width:8px;margin-top:-8px}.vis-configuration-popup:before{border-color:rgba(194,225,245,0);border-left-color:#f2faff;border-width:12px;margin-top:-12px}div.vis-tooltip{position:absolute;visibility:hidden;padding:5px;white-space:nowrap;font-family:verdana;font-size:14px;color:#000;background-color:#f5f4ed;-moz-border-radius:3px;-webkit-border-radius:3px;border-radius:3px;border:1px solid #808074;box-shadow:3px 3px 10px rgba(0,0,0,.2);pointer-events:none;z-index:5}div.vis-color-picker{position:absolute;top:0;left:30px;margin-top:-140px;margin-left:30px;width:310px;height:444px;z-index:1;padding:10px;border-radius:15px;background-color:#fff;display:none;box-shadow:rgba(0,0,0,.5) 0 0 10px 0}div.vis-color-picker div.vis-arrow{position:absolute;top:147px;left:5px}div.vis-color-picker div.vis-arrow::after,div.vis-color-picker div.vis-arrow::before{right:100%;top:50%;border:solid transparent;content:" ";height:0;width:0;position:absolute;pointer-events:none}div.vis-color-picker div.vis-arrow:after{border-color:rgba(255,255,255,0);border-right-color:#fff;border-width:30px;margin-top:-30px}div.vis-color-picker div.vis-color{position:absolute;width:289px;height:289px;cursor:pointer}div.vis-color-picker div.vis-brightness{position:absolute;top:313px}div.vis-color-picker div.vis-opacity{position:absolute;top:350px}div.vis-color-picker div.vis-selector{position:absolute;top:137px;left:137px;width:15px;height:15px;border-radius:15px;border:1px solid #fff;background:#4c4c4c;background:-moz-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#4c4c4c),color-stop(12%,#595959),color-stop(25%,#666),color-stop(39%,#474747),color-stop(50%,#2c2c2c),color-stop(51%,#000),color-stop(60%,#111),color-stop(76%,#2b2b2b),color-stop(91%,#1c1c1c),color-stop(100%,#131313));background:-webkit-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-o-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:-ms-linear-gradient(top,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%);background:linear-gradient(to bottom,#4c4c4c 0,#595959 12%,#666 25%,#474747 39%,#2c2c2c 50%,#000 51%,#111 60%,#2b2b2b 76%,#1c1c1c 91%,#131313 100%)}div.vis-color-picker div.vis-new-color{position:absolute;width:140px;height:20px;border:1px solid rgba(0,0,0,.1);border-radius:5px;top:380px;left:159px;text-align:right;padding-right:2px;font-size:10px;color:rgba(0,0,0,.4);vertical-align:middle;line-height:20px}div.vis-color-picker div.vis-initial-color{position:absolute;width:140px;height:20px;border:1px solid rgba(0,0,0,.1);border-radius:5px;top:380px;left:10px;text-align:left;padding-left:2px;font-size:10px;color:rgba(0,0,0,.4);vertical-align:middle;line-height:20px}div.vis-color-picker div.vis-label{position:absolute;width:300px;left:10px}div.vis-color-picker div.vis-label.vis-brightness{top:300px}div.vis-color-picker div.vis-label.vis-opacity{top:338px}div.vis-color-picker div.vis-button{position:absolute;width:68px;height:25px;border-radius:10px;vertical-align:middle;text-align:center;line-height:25px;top:410px;border:2px solid #d9d9d9;background-color:#f7f7f7;cursor:pointer}div.vis-color-picker div.vis-button.vis-cancel{left:5px}div.vis-color-picker div.vis-button.vis-load{left:82px}div.vis-color-picker div.vis-button.vis-apply{left:159px}div.vis-color-picker div.vis-button.vis-save{left:236px}div.vis-color-picker input.vis-range{width:290px;height:20px}div.vis-network div.vis-manipulation{box-sizing:content-box;border-width:0;border-bottom:1px;border-style:solid;border-color:#d6d9d8;background:#fff;background:-moz-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-webkit-gradient(linear,left top,left bottom,color-stop(0,#fff),color-stop(48%,#fcfcfc),color-stop(50%,#fafafa),color-stop(100%,#fcfcfc));background:-webkit-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-o-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:-ms-linear-gradient(top,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);background:linear-gradient(to bottom,#fff 0,#fcfcfc 48%,#fafafa 50%,#fcfcfc 100%);padding-top:4px;position:absolute;left:0;top:0;width:100%;height:28px}div.vis-network div.vis-edit-mode{position:absolute;left:0;top:5px;height:30px}div.vis-network div.vis-close{position:absolute;right:0;top:0;width:30px;height:30px;background-position:20px 3px;background-repeat:no-repeat;background-image:url(img/network/cross.png);cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.vis-network div.vis-close:hover{opacity:.6}div.vis-network div.vis-edit-mode div.vis-button,div.vis-network div.vis-manipulation div.vis-button{float:left;font-family:verdana;font-size:12px;-moz-border-radius:15px;border-radius:15px;display:inline-block;background-position:0 0;background-repeat:no-repeat;height:24px;margin-left:10px;cursor:pointer;padding:0 8px 0 8px;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.vis-network div.vis-manipulation div.vis-button:hover{box-shadow:1px 1px 8px rgba(0,0,0,.2)}div.vis-network div.vis-manipulation div.vis-button:active{box-shadow:1px 1px 8px rgba(0,0,0,.5)}div.vis-network div.vis-manipulation div.vis-button.vis-back{background-image:url(img/network/backIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-none:hover{box-shadow:1px 1px 8px transparent;cursor:default}div.vis-network div.vis-manipulation div.vis-button.vis-none:active{box-shadow:1px 1px 8px transparent}div.vis-network div.vis-manipulation div.vis-button.vis-none{padding:0}div.vis-network div.vis-manipulation div.notification{margin:2px;font-weight:700}div.vis-network div.vis-manipulation div.vis-button.vis-add{background-image:url(img/network/addNodeIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit,div.vis-network div.vis-manipulation div.vis-button.vis-edit{background-image:url(img/network/editIcon.png)}div.vis-network div.vis-edit-mode div.vis-button.vis-edit.vis-edit-mode{background-color:#fcfcfc;border:1px solid #ccc}div.vis-network div.vis-manipulation div.vis-button.vis-connect{background-image:url(img/network/connectIcon.png)}div.vis-network div.vis-manipulation div.vis-button.vis-delete{background-image:url(img/network/deleteIcon.png)}div.vis-network div.vis-edit-mode div.vis-label,div.vis-network div.vis-manipulation div.vis-label{margin:0 0 0 23px;line-height:25px}div.vis-network div.vis-manipulation div.vis-separator-line{float:left;display:inline-block;width:1px;height:21px;background-color:#bdbdbd;margin:0 7px 0 15px}div.vis-network div.vis-navigation div.vis-button{width:34px;height:34px;-moz-border-radius:17px;border-radius:17px;position:absolute;display:inline-block;background-position:2px 2px;background-repeat:no-repeat;cursor:pointer;-webkit-touch-callout:none;-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}div.vis-network div.vis-navigation div.vis-button:hover{box-shadow:0 0 3px 3px rgba(56,207,21,.3)}div.vis-network div.vis-navigation div.vis-button:active{box-shadow:0 0 1px 3px rgba(56,207,21,.95)}div.vis-network div.vis-navigation div.vis-button.vis-up{background-image:url(img/network/upArrow.png);bottom:50px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-down{background-image:url(img/network/downArrow.png);bottom:10px;left:55px}div.vis-network div.vis-navigation div.vis-button.vis-left{background-image:url(img/network/leftArrow.png);bottom:10px;left:15px}div.vis-network div.vis-navigation div.vis-button.vis-right{background-image:url(img/network/rightArrow.png);bottom:10px;left:95px}div.vis-network div.vis-navigation div.vis-button.vis-zoomIn{background-image:url(img/network/plus.png);bottom:10px;right:15px}div.vis-network div.vis-navigation div.vis-button.vis-zoomOut{background-image:url(img/network/minus.png);bottom:10px;right:55px}div.vis-network div.vis-navigation div.vis-button.vis-zoomExtends{background-image:url(img/network/zoomExtends.png);bottom:50px;right:15px}.vis-current-time{background-color:#ff7f6e;width:2px;z-index:1;pointer-events:none}.vis-rolling-mode-btn{height:40px;width:40px;position:absolute;top:7px;right:20px;border-radius:50%;font-size:28px;cursor:pointer;opacity:.8;color:#fff;font-weight:700;text-align:center;background:#3876c2}.vis-rolling-mode-btn:before{content:"\26F6"}.vis-rolling-mode-btn:hover{opacity:1}.vis-custom-time{background-color:#6e94ff;width:2px;cursor:move;z-index:1}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-horizontal{position:absolute;width:100%;height:0;border-bottom:1px solid}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-minor{border-color:#e5e5e5}.vis-panel.vis-background.vis-horizontal .vis-grid.vis-major{border-color:#bfbfbf}.vis-data-axis .vis-y-axis.vis-major{width:100%;position:absolute;color:#4d4d4d;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-major.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-minor{position:absolute;width:100%;color:#bebebe;white-space:nowrap}.vis-data-axis .vis-y-axis.vis-minor.vis-measure{padding:0;margin:0;border:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title{position:absolute;color:#4d4d4d;white-space:nowrap;bottom:20px;text-align:center}.vis-data-axis .vis-y-axis.vis-title.vis-measure{padding:0;margin:0;visibility:hidden;width:auto}.vis-data-axis .vis-y-axis.vis-title.vis-left{bottom:0;-webkit-transform-origin:left top;-moz-transform-origin:left top;-ms-transform-origin:left top;-o-transform-origin:left top;transform-origin:left bottom;-webkit-transform:rotate(-90deg);-moz-transform:rotate(-90deg);-ms-transform:rotate(-90deg);-o-transform:rotate(-90deg);transform:rotate(-90deg)}.vis-data-axis .vis-y-axis.vis-title.vis-right{bottom:0;-webkit-transform-origin:right bottom;-moz-transform-origin:right bottom;-ms-transform-origin:right bottom;-o-transform-origin:right bottom;transform-origin:right bottom;-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.vis-legend{background-color:rgba(247,252,255,.65);padding:5px;border:1px solid #b3b3b3;box-shadow:2px 2px 10px rgba(154,154,154,.55)}.vis-legend-text{white-space:nowrap;display:inline-block}.vis-item{position:absolute;color:#1a1a1a;border-color:#97b0f8;border-width:1px;background-color:#d5ddf6;display:inline-block;z-index:1}.vis-item.vis-selected{border-color:#ffc200;background-color:#fff785;z-index:2}.vis-editable.vis-selected{cursor:move}.vis-item.vis-point.vis-selected{background-color:#fff785}.vis-item.vis-box{text-align:center;border-style:solid;border-radius:2px}.vis-item.vis-point{background:0 0}.vis-item.vis-dot{position:absolute;padding:0;border-width:4px;border-style:solid;border-radius:4px}.vis-item.vis-range{border-style:solid;border-radius:2px;box-sizing:border-box}.vis-item.vis-background{border:none;background-color:rgba(213,221,246,.4);box-sizing:border-box;padding:0;margin:0}.vis-item .vis-item-overflow{position:relative;width:100%;height:100%;padding:0;margin:0;overflow:hidden}.vis-item-visible-frame{white-space:nowrap}.vis-item.vis-range .vis-item-content{position:relative;display:inline-block}.vis-item.vis-background .vis-item-content{position:absolute;display:inline-block}.vis-item.vis-line{padding:0;position:absolute;width:0;border-left-width:1px;border-left-style:solid}.vis-item .vis-item-content{white-space:nowrap;box-sizing:border-box;padding:5px}.vis-item .vis-onUpdateTime-tooltip{position:absolute;background:#4f81bd;color:#fff;width:200px;text-align:center;white-space:nowrap;padding:5px;border-radius:1px;transition:.4s;-o-transition:.4s;-moz-transition:.4s;-webkit-transition:.4s}.vis-item .vis-delete,.vis-item .vis-delete-rtl{position:absolute;top:0;width:24px;height:24px;box-sizing:border-box;padding:0 5px;cursor:pointer;-webkit-transition:background .2s linear;-moz-transition:background .2s linear;-ms-transition:background .2s linear;-o-transition:background .2s linear;transition:background .2s linear}.vis-item .vis-delete{right:-24px}.vis-item .vis-delete-rtl{left:-24px}.vis-item .vis-delete-rtl:after,.vis-item .vis-delete:after{content:"\00D7";color:red;font-family:arial,sans-serif;font-size:22px;font-weight:700;-webkit-transition:color .2s linear;-moz-transition:color .2s linear;-ms-transition:color .2s linear;-o-transition:color .2s linear;transition:color .2s linear}.vis-item .vis-delete-rtl:hover,.vis-item .vis-delete:hover{background:red}.vis-item .vis-delete-rtl:hover:after,.vis-item .vis-delete:hover:after{color:#fff}.vis-item .vis-drag-center{position:absolute;width:100%;height:100%;top:0;left:0;cursor:move}.vis-item.vis-range .vis-drag-left{position:absolute;width:24px;max-width:20%;min-width:2px;height:100%;top:0;left:-4px;cursor:w-resize}.vis-item.vis-range .vis-drag-right{position:absolute;width:24px;max-width:20%;min-width:2px;height:100%;top:0;right:-4px;cursor:e-resize}.vis-range.vis-item.vis-readonly .vis-drag-left,.vis-range.vis-item.vis-readonly .vis-drag-right{cursor:auto}.vis-itemset{position:relative;padding:0;margin:0;box-sizing:border-box}.vis-itemset .vis-background,.vis-itemset .vis-foreground{position:absolute;width:100%;height:100%;overflow:visible}.vis-axis{position:absolute;width:100%;height:0;left:0;z-index:1}.vis-foreground .vis-group{position:relative;box-sizing:border-box;border-bottom:1px solid #bfbfbf}.vis-foreground .vis-group:last-child{border-bottom:none}.vis-nesting-group{cursor:pointer}.vis-nested-group{background:#f5f5f5}.vis-label.vis-nesting-group.expanded:before{content:"\25BC"}.vis-label.vis-nesting-group.collapsed-rtl:before{content:"\25C0"}.vis-label.vis-nesting-group.collapsed:before{content:"\25B6"}.vis-overlay{position:absolute;top:0;left:0;width:100%;height:100%;z-index:10}.vis-labelset{position:relative;overflow:hidden;box-sizing:border-box}.vis-labelset .vis-label{position:relative;left:0;top:0;width:100%;color:#4d4d4d;box-sizing:border-box}.vis-labelset .vis-label{border-bottom:1px solid #bfbfbf}.vis-labelset .vis-label.draggable{cursor:pointer}.vis-labelset .vis-label:last-child{border-bottom:none}.vis-labelset .vis-label .vis-inner{display:inline-block;padding:5px}.vis-labelset .vis-label .vis-inner.vis-hidden{padding:0}.vis-panel{position:absolute;padding:0;margin:0;box-sizing:border-box}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right,.vis-panel.vis-top{border:1px #bfbfbf}.vis-panel.vis-center,.vis-panel.vis-left,.vis-panel.vis-right{border-top-style:solid;border-bottom-style:solid;overflow:hidden}.vis-left.vis-panel.vis-vertical-scroll,.vis-right.vis-panel.vis-vertical-scroll{height:100%;overflow-x:hidden;overflow-y:scroll}.vis-left.vis-panel.vis-vertical-scroll{direction:rtl}.vis-left.vis-panel.vis-vertical-scroll .vis-content{direction:ltr}.vis-right.vis-panel.vis-vertical-scroll{direction:ltr}.vis-right.vis-panel.vis-vertical-scroll .vis-content{direction:rtl}.vis-panel.vis-bottom,.vis-panel.vis-center,.vis-panel.vis-top{border-left-style:solid;border-right-style:solid}.vis-background{overflow:hidden}.vis-panel>.vis-content{position:relative}.vis-panel .vis-shadow{position:absolute;width:100%;height:1px;box-shadow:0 0 10px rgba(0,0,0,.8)}.vis-panel .vis-shadow.vis-top{top:-1px;left:0}.vis-panel .vis-shadow.vis-bottom{bottom:-1px;left:0}.vis-graph-group0{fill:#4f81bd;fill-opacity:0;stroke-width:2px;stroke:#4f81bd}.vis-graph-group1{fill:#f79646;fill-opacity:0;stroke-width:2px;stroke:#f79646}.vis-graph-group2{fill:#8c51cf;fill-opacity:0;stroke-width:2px;stroke:#8c51cf}.vis-graph-group3{fill:#75c841;fill-opacity:0;stroke-width:2px;stroke:#75c841}.vis-graph-group4{fill:#ff0100;fill-opacity:0;stroke-width:2px;stroke:#ff0100}.vis-graph-group5{fill:#37d8e6;fill-opacity:0;stroke-width:2px;stroke:#37d8e6}.vis-graph-group6{fill:#042662;fill-opacity:0;stroke-width:2px;stroke:#042662}.vis-graph-group7{fill:#00ff26;fill-opacity:0;stroke-width:2px;stroke:#00ff26}.vis-graph-group8{fill:#f0f;fill-opacity:0;stroke-width:2px;stroke:#f0f}.vis-graph-group9{fill:#8f3938;fill-opacity:0;stroke-width:2px;stroke:#8f3938}.vis-timeline .vis-fill{fill-opacity:.1;stroke:none}.vis-timeline .vis-bar{fill-opacity:.5;stroke-width:1px}.vis-timeline .vis-point{stroke-width:2px;fill-opacity:1}.vis-timeline .vis-legend-background{stroke-width:1px;fill-opacity:.9;fill:#fff;stroke:#c2c2c2}.vis-timeline .vis-outline{stroke-width:1px;fill-opacity:1;fill:#fff;stroke:#e5e5e5}.vis-timeline .vis-icon-fill{fill-opacity:.3;stroke:none}.vis-time-axis{position:relative;overflow:hidden}.vis-time-axis.vis-foreground{top:0;left:0;width:100%}.vis-time-axis.vis-background{position:absolute;top:0;left:0;width:100%;height:100%}.vis-time-axis .vis-text{position:absolute;color:#4d4d4d;padding:3px;overflow:hidden;box-sizing:border-box;white-space:nowrap}.vis-time-axis .vis-text.vis-measure{position:absolute;padding-left:0;padding-right:0;margin-left:0;margin-right:0;visibility:hidden}.vis-time-axis .vis-grid.vis-vertical{position:absolute;border-left:1px solid}.vis-time-axis .vis-grid.vis-vertical-rtl{position:absolute;border-right:1px solid}.vis-time-axis .vis-grid.vis-minor{border-color:#e5e5e5}.vis-time-axis .vis-grid.vis-major{border-color:#bfbfbf}.vis-timeline{position:relative;border:1px solid #bfbfbf;overflow:hidden;padding:0;margin:0;box-sizing:border-box}
--------------------------------------------------------------------------------
/public/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/avvero/camel-graph/f6bf8c9262c3111ae242cd8a75241c8d8f65049a/public/favicon.ico
--------------------------------------------------------------------------------
/public/fonts/FontAwesome.otf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/avvero/camel-graph/f6bf8c9262c3111ae242cd8a75241c8d8f65049a/public/fonts/FontAwesome.otf
--------------------------------------------------------------------------------
/public/fonts/fontawesome-webfont.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/avvero/camel-graph/f6bf8c9262c3111ae242cd8a75241c8d8f65049a/public/fonts/fontawesome-webfont.eot
--------------------------------------------------------------------------------
/public/fonts/fontawesome-webfont.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/avvero/camel-graph/f6bf8c9262c3111ae242cd8a75241c8d8f65049a/public/fonts/fontawesome-webfont.ttf
--------------------------------------------------------------------------------
/public/fonts/fontawesome-webfont.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/avvero/camel-graph/f6bf8c9262c3111ae242cd8a75241c8d8f65049a/public/fonts/fontawesome-webfont.woff
--------------------------------------------------------------------------------
/public/fonts/fontawesome-webfont.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/avvero/camel-graph/f6bf8c9262c3111ae242cd8a75241c8d8f65049a/public/fonts/fontawesome-webfont.woff2
--------------------------------------------------------------------------------
/public/img/camel-graph.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/avvero/camel-graph/f6bf8c9262c3111ae242cd8a75241c8d8f65049a/public/img/camel-graph.png
--------------------------------------------------------------------------------
/public/img/grafana_icon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
59 |
--------------------------------------------------------------------------------
/public/img/graphite-icon-57x57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/avvero/camel-graph/f6bf8c9262c3111ae242cd8a75241c8d8f65049a/public/img/graphite-icon-57x57.png
--------------------------------------------------------------------------------
/public/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 | Camel-graph
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/public/info.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "camel-graph",
3 | "version": "1.0"
4 | }
5 |
--------------------------------------------------------------------------------
/public/js/app.js:
--------------------------------------------------------------------------------
1 | angular.module("flow", [
2 | 'ngRoute',
3 | 'ui.router',
4 | 'ngSanitize',
5 | 'ui.bootstrap',
6 | 'relativeDate'
7 | ])
8 | angular.module("flow").constant('constants', {
9 | version: "1.0.2"
10 | })
11 | // configure our routes
12 | angular.module("flow").config(function ($routeProvider, $stateProvider, $urlRouterProvider) {
13 |
14 | $urlRouterProvider.otherwise("/")
15 |
16 | $stateProvider
17 | .state('index', {
18 | url: "/",
19 | views: {
20 | "single": {
21 | templateUrl: 'views/environments.html',
22 | controller: placesController,
23 | resolve: placesController.resolve
24 | }
25 | }
26 | })
27 | .state('component', {
28 | url: "/listen/:component",
29 | views: {
30 | "single": {
31 | templateUrl: 'views/environment.html',
32 | controller: placeController,
33 | resolve: placesController.resolve
34 | }
35 | }
36 | })
37 | })
38 | angular.module("flow").run(function ($rootScope) {
39 |
40 | })
41 |
42 | angular.module("flow").controller('mainController', function ($scope) {
43 |
44 | })
45 |
46 | angular.module("flow").filter('toArray', function() { return function(obj) {
47 | if (!(obj instanceof Object)) return obj;
48 | var arr = [];
49 | for (var key in obj) {
50 | arr.push({ key: key, value: obj[key] });
51 | }
52 | return arr;
53 | }});
--------------------------------------------------------------------------------
/public/js/controllers/environment.js:
--------------------------------------------------------------------------------
1 | function placeController($scope, data, $timeout, $http, $stateParams, $location, $anchorScroll, $uibModal, utils) {
2 | $scope.$stateParams = $stateParams
3 | $scope.showSearchBox = true
4 | $scope.selected = null
5 | $scope.connection = {}
6 | $scope.shaked = null
7 | $scope.$on('$destroy', function () {
8 | $scope.isDestroed = true
9 | });
10 |
11 | $scope.dataUpdateIntervalSeconds = 60
12 |
13 | $scope.merge = function (oldData, newData) {
14 | var result = {}
15 | result.name = newData.name
16 | result.lastUpdated = newData.lastUpdated
17 | result.serviceMap = {}
18 |
19 | //merging
20 | for (var serviceName in newData.serviceMap) {
21 | if (newData.serviceMap.hasOwnProperty(serviceName)) {
22 | if (!result.serviceMap[serviceName]) {
23 | result.serviceMap[serviceName] = {}
24 | }
25 | if (oldData.serviceMap && oldData.serviceMap.hasOwnProperty(serviceName)) {
26 | result.serviceMap[serviceName].error = newData.serviceMap[serviceName].error
27 | result.serviceMap[serviceName].lastUpdated = newData.serviceMap[serviceName].lastUpdated
28 | // merge
29 | } else {
30 | result.serviceMap[serviceName] = newData.serviceMap[serviceName]
31 | }
32 | }
33 | }
34 |
35 | // flatting services
36 | result.services = []
37 | for (var serviceName in result.serviceMap) {
38 | if (result.serviceMap.hasOwnProperty(serviceName)) {
39 | result.services.push(result.serviceMap[serviceName])
40 | }
41 | }
42 | // flatting routes
43 | for (var i = 0; i < result.services.length; i++) {
44 | var service = result.services[i]
45 | service.routes = []
46 | for (var routeEntry in service.routeMap) {
47 | if (service.routeMap.hasOwnProperty(routeEntry)) {
48 | service.routes.push(service.routeMap[routeEntry])
49 | }
50 | }
51 | }
52 | $scope.connection.lastUpdated = result.lastUpdated
53 | return result
54 | }
55 |
56 | $scope.data = $scope.merge({}, data)
57 | utils.processSchema($scope.data)
58 | $scope.graph = utils.buildGraphFromSchema($scope.data)
59 |
60 | $scope.listen = function (delay) {
61 | $timeout(function () {
62 | $http({
63 | method: 'GET',
64 | url: '/data?env=' + $stateParams.component + "&t=" + new Date().getTime(),
65 | headers: {'Content-Type': 'application/json;charset=UTF-8'}
66 | })
67 | .success(function (newData) {
68 | console.info("Has come new data: " + newData)
69 | $scope.connection.error = null
70 | var data = $scope.merge({}, newData)
71 | utils.processSchema(data)
72 | utils.updateGraph($scope.graph, data)
73 | })
74 | .error(function (error, error2, error3) {
75 | $scope.connection.error = "Connection with server is lost"
76 | });
77 | if (!!$scope.isDestroed) return
78 | $scope.listen($scope.dataUpdateIntervalSeconds * 1000)
79 | }, delay, true);
80 | }
81 | $scope.listen($scope.dataUpdateIntervalSeconds * 1000)
82 |
83 | $scope.drawGraph = function (nodes, edges) {
84 | console.info("Start draw")
85 | var data = {
86 | nodes: nodes,
87 | edges: edges
88 | };
89 | var options = {
90 | nodes: {
91 | // shadow: true,
92 | shape: 'dot',
93 | size: 12,
94 | font: {
95 | size: 20
96 | }
97 | },
98 | edges: {
99 | // shadow: true,
100 | smooth: true,
101 | arrows: {to: true},
102 | scaling: {
103 | customScalingFunction: function (min,max,total,value) {
104 | return value/total;
105 | },
106 | min:1,
107 | max: 10
108 | },
109 | arrowStrikethrough: false,
110 | font: {size: 8, color: 'grey', face: 'arial'}
111 | }
112 | };
113 | var container = document.getElementById('camel-map');
114 | var graphNetwork = new vis.Network(container, data, options);
115 | graphNetwork.on("click", function (params) {
116 | if (params.edges.length > 0 || params.nodes.length > 0 ) {
117 | var data = {
118 | nodes: nodes.get(params.nodes),
119 | edges: edges.get(params.edges)
120 | }
121 | //prepare
122 | if (data.nodes.length > 0) {
123 | data.endpoint = data.nodes[0].label
124 | } else {
125 | data.route = data.edges[0].route
126 | }
127 | $scope.$apply(function () {
128 | $scope.selectGraphElement(data)
129 | });
130 | }
131 | });
132 |
133 | if (false) {
134 | var updateFrameVar = setInterval(function() { updateFrameTimer(); }, 60);
135 | function updateFrameTimer() {
136 | graphNetwork.redraw();
137 | currentRadius += 0.05;
138 | }
139 | var currentRadius = 0;
140 | graphNetwork.on("beforeDrawing", function(ctx) {
141 | var inode;
142 | var nodePosition = graphNetwork.getPositions();
143 | var arrayLength = nodes.length;
144 | for (inode = 0; inode < arrayLength; inode++) {
145 | var node = nodes.get(inode)
146 | if (node && node.strike) {
147 | ctx.strokeStyle = node.color;
148 | ctx.fillStyle = '#fffbfb';
149 |
150 | var radius = Math.abs(50 * Math.sin(currentRadius + inode / 50.0));
151 | ctx.circle(nodePosition[node.id].x, nodePosition[node.id].y, radius);
152 | ctx.fill();
153 | ctx.stroke();
154 | }
155 | }
156 | });
157 | }
158 | console.info("Finish draw")
159 | return graphNetwork
160 | }
161 | $scope.graphNetwork = $scope.drawGraph($scope.graph.nodesDataSet, $scope.graph.edgesDataSet)
162 |
163 | // -----
164 | // ----- VIEW
165 | // -----
166 | $scope.goToPlaces = function () {
167 | $location.path('#')
168 | }
169 |
170 | //Select who must be selected
171 | if ($location.search().select) {
172 | if (!$scope.data) return
173 | if (!$scope.data.environments) return
174 |
175 |
176 | for (var i = 0; i < $scope.info.app.components.length; i++) {
177 | var url = window.decodeURIComponent($location.search().select)
178 | if ($scope.info.app.components[i].url == url) {
179 | // $scope.selected = $scope.babies[i]
180 | $anchorScroll();
181 | $scope.shaked = $scope.info.app.components[i]
182 | $location.hash('component_' + url);
183 | break
184 | }
185 | }
186 | }
187 |
188 | $scope.selectedTab = 'graph'
189 | $scope.setTab = function (v) {
190 | $scope.selectedTab = v
191 | }
192 | $scope.showHideSearchBox = function() {
193 | $scope.showSearchBox = !$scope.showSearchBox
194 | if ($scope.showSearchBox) {
195 | $scope.setTab('graph')
196 | }
197 | }
198 |
199 | //Selected graph endpoint
200 | $scope.endpointSearchValue = ''
201 | $scope.selectedGraphEndpoint = null
202 | $scope.selectGraphEndpoint = function (entry) {
203 | $scope.selectedGraphEndpoint = $scope.selectedGraphEndpoint == entry ? null : entry
204 | }
205 | $scope.$watch('selectedGraphEndpoint', function (newValue, oldValue) {
206 | var options = {
207 | // scale: scale,
208 | // offset: {x:offsetx,y:offsety},
209 | animation: {
210 | duration: 1000,
211 | easingFunction: 'easeOutQuart'
212 | }
213 | };
214 |
215 | if (newValue) {
216 | var id = $scope.graph.endpoints[newValue]
217 | $scope.graph.nodesDataSet.update([{id: id, size: 20}]);
218 | $scope.graphNetwork.focus(id, options);
219 | } else {
220 | // fly away
221 | // $scope.graphNetwork.fit({animation: options});
222 | }
223 | if (oldValue) {
224 | var id = $scope.graph.endpoints[oldValue]
225 | $scope.graph.nodesDataSet.update([{id: id, size: 10}]);
226 | }
227 | });
228 |
229 | //Selected graph element
230 | $scope.selectedGraphElement = null
231 | $scope.selectGraphElement = function (entry) {
232 | console.info(entry)
233 | if (entry.endpoint) {
234 | $scope.selectGraphEndpoint(entry.endpoint)
235 | } else {
236 | $scope.selectGraphEndpoint(null)
237 | }
238 | $scope.selectedGraphElement = $scope.selectedGraphElement == entry ? null : entry
239 | $scope.showOptionsDialog(entry)
240 | }
241 |
242 | $scope.showOptionsDialog = function (data) {
243 | var options = {}
244 | var modalInstance = $uibModal.open({
245 | templateUrl: 'views/options.html',
246 | controller: optionsDialogController,
247 | size: "lg",
248 | resolve: {
249 | data: function ($q, $http) {
250 | var deferred = $q.defer();
251 | deferred.resolve(data)
252 | return deferred.promise;
253 | }
254 | }
255 | });
256 | modalInstance.result.then(function (params) {}, function () {});
257 | }
258 | }
--------------------------------------------------------------------------------
/public/js/controllers/environments.js:
--------------------------------------------------------------------------------
1 | function placesController($scope, data, $timeout, $http) {
2 | $scope.data = data
3 | }
4 |
5 | placesController.resolve = {
6 | data: function ($q, $http, $stateParams) {
7 | var deferred = $q.defer();
8 |
9 | $http({
10 | method: 'GET',
11 | url: '/data?env=' + $stateParams.component + "&t=" + new Date().getTime(),
12 | headers: {'Content-Type': 'application/json;charset=UTF-8'}
13 | })
14 | .success(function (data) {
15 | deferred.resolve(data)
16 | })
17 | .error(function (data) {
18 | deferred.reject("error value");
19 | });
20 |
21 | return deferred.promise;
22 | }
23 | }
--------------------------------------------------------------------------------
/public/js/controllers/optionsDialog.js:
--------------------------------------------------------------------------------
1 | function optionsDialogController(data, $scope, $uibModalInstance) {
2 | $scope.data = data
3 | $scope.ok = function () {
4 | $uibModalInstance.close(data);
5 | }
6 | }
--------------------------------------------------------------------------------
/public/js/providers/local-timeout.js:
--------------------------------------------------------------------------------
1 | angular.module('flow').provider('localTimeout', function LocalTimeoutProvider() {
2 |
3 | this.$get = ['$rootScope', '$browser', '$q', '$exceptionHandler',
4 | function($rootScope, $browser, $q, $exceptionHandler) {
5 | var deferreds = {};
6 |
7 | function isDefined(value){return typeof value !== 'undefined';}
8 |
9 | /**
10 | * @ngdoc function
11 | * @name ng.$timeout
12 | * @requires $browser
13 | *
14 | * @description
15 | * Angular's wrapper for `window.setTimeout`. The `fn` function is wrapped into a try/catch
16 | * block and delegates any exceptions to
17 | * {@link ng.$exceptionHandler $exceptionHandler} service.
18 | *
19 | * The return value of registering a timeout function is a promise, which will be resolved when
20 | * the timeout is reached and the timeout function is executed.
21 | *
22 | * To cancel a timeout request, call `$timeout.cancel(promise)`.
23 | *
24 | * In tests you can use {@link ngMock.$timeout `$timeout.flush()`} to
25 | * synchronously flush the queue of deferred functions.
26 | *
27 | * @param {function()} fn A function, whose execution should be delayed.
28 | * @param {number=} [delay=0] Delay in milliseconds.
29 | * @param {boolean=} [invokeApply=true] If set to `false` skips model dirty checking, otherwise
30 | * will invoke `fn` within the {@link ng.$rootScope.Scope#methods_$apply $apply} block.
31 | * @returns {Promise} Promise that will be resolved when the timeout is reached. The value this
32 | * promise will be resolved with is the return value of the `fn` function.
33 | *
34 | */
35 | function timeout(fn, $scope, delay, invokeApply) {
36 | var deferred = $q.defer(),
37 | promise = deferred.promise,
38 | skipApply = (isDefined(invokeApply) && !invokeApply),
39 | timeoutId;
40 |
41 | timeoutId = $browser.defer(function() {
42 | try {
43 | deferred.resolve(fn());
44 | } catch(e) {
45 | deferred.reject(e);
46 | $exceptionHandler(e);
47 | }
48 | finally {
49 | delete deferreds[promise.$$timeoutId];
50 | }
51 |
52 | if (!skipApply) $scope.$digest();
53 | }, delay);
54 |
55 | promise.$$timeoutId = timeoutId;
56 | deferreds[timeoutId] = deferred;
57 |
58 | return promise;
59 | }
60 |
61 |
62 | /**
63 | * @ngdoc function
64 | * @name ng.$timeout#cancel
65 | * @methodOf ng.$timeout
66 | *
67 | * @description
68 | * Cancels a task associated with the `promise`. As a result of this, the promise will be
69 | * resolved with a rejection.
70 | *
71 | * @param {Promise=} promise Promise returned by the `$timeout` function.
72 | * @returns {boolean} Returns `true` if the task hasn't executed yet and was successfully
73 | * canceled.
74 | */
75 | timeout.cancel = function(promise) {
76 | if (promise && promise.$$timeoutId in deferreds) {
77 | deferreds[promise.$$timeoutId].reject('canceled');
78 | delete deferreds[promise.$$timeoutId];
79 | return $browser.defer.cancel(promise.$$timeoutId);
80 | }
81 | return false;
82 | };
83 |
84 | return timeout;
85 | }];
86 | })
--------------------------------------------------------------------------------
/public/js/services/utils.js:
--------------------------------------------------------------------------------
1 | angular.module('flow').service('utils', function () {
2 | var getRandomColor = function () {
3 | var letters = '0123456789ABCDEF';
4 | var color = '#';
5 | for (var i = 0; i < 6; i++) {
6 | color += letters[Math.floor(Math.random() * 16)];
7 | }
8 | return color;
9 | }
10 | return {
11 | getRandomColor: getRandomColor,
12 | processSchema: function (data) {
13 | // Set color
14 | for (var i = 0; i < data.services.length; i++) {
15 | var service = data.services[i]
16 | service.color = service.color || this.getRandomColor()
17 | for (var j = 0; j < service.routes.length; j++) {
18 | service.routes[j].color = this.getRandomColor()
19 | }
20 | }
21 | },
22 | buildGraphFromSchema: function (data) {
23 | var graph = {
24 | edgeMap: {},
25 | endpoints: {},
26 | endpointsKeys: [],
27 | nodesDataSet: null,
28 | edgesDataSet: null,
29 |
30 | edgeExists: function (from, to) {
31 | var k = from + "_" + to
32 | return !!this.edgeMap[k]
33 | },
34 | addEndpoint: function (endpoint, service) {
35 | var e = this.endpoints[endpoint]
36 | if (typeof e == "undefined") {
37 | e = this.nodesDataSet.length
38 | this.endpoints[endpoint] = e
39 | // console.info(service.name + ": add endpoint " + endpoint)
40 | this.addNode(e, endpoint, service)
41 | } else {
42 | this.updateNode(e, endpoint, service)
43 | }
44 | },
45 | addNode: function (id, endpoint, service) {
46 | this.nodesDataSet.add({
47 | id: id,
48 | label: endpoint,
49 | color: service.color
50 | })
51 | },
52 | updateNode: function (id, endpoint, service) {
53 | // do nothing
54 | },
55 | getRouteTitle: function (route) {
56 | var title = '' + route.name + ''
57 | + '
State: ' + (route.state || 'none')
58 | + '
Uptime: ' + (route.uptime || '-')
59 | + '
LastUpdated: ' + (route.lastUpdated ? moment(route.lastUpdated).fromNow(): "-")
60 | + '
----'
61 | + '
exchangesTotal: ' + (route.exchangesTotal || 0)
62 | + '
exchangesCompleted: ' + (route.exchangesCompleted || 0)
63 | + '
exchangesFailed: ' + (route.exchangesFailed || 0)
64 | + '
exchangesInflight: ' + (route.exchangesInflight || 0)
65 | + '
maxProcessingTime: ' + (route.maxProcessingTime || 0)
66 | + '
minProcessingTime: ' + (route.minProcessingTime || 0)
67 | + '
lastProcessingTime: ' + (route.lastProcessingTime || 0)
68 | + '
meanProcessingTime: ' + (route.meanProcessingTime || 0)
69 | + '
totalProcessingTime: ' + (route.totalProcessingTime || 0)
70 | + '
failuresHandled: ' + (route.failuresHandled || 0)
71 | + '
redeliveries: ' + (route.redeliveries || 0)
72 | + '
startTimestamp: ' + (route.startTimestamp || '-')
73 | return title
74 | },
75 | addEdge: function (route, from, to, service) {
76 | var id = from + "_" + to
77 | // console.info(service.name + ": add edge " + id + ": " + route.name)
78 | var routeRepresentation = this.getRouteRepresentation(route, service)
79 | this.edgesDataSet.add({
80 | id: id,
81 | from: from,
82 | to: to,
83 | route: route,
84 | color: routeRepresentation.color,
85 | title: this.getRouteTitle(route),
86 | value: routeRepresentation.weight,
87 | dashes: routeRepresentation.dashes,
88 | label: route.exchangesTotal ? '' + route.exchangesTotal : null,
89 | font: {align: 'top'}
90 | })
91 | this.edgeMap[id] = 1
92 | },
93 | updateEdge: function (route, from, to, service) {
94 | var id = from + "_" + to
95 | var existedEdge = this.edgesDataSet.get(id)
96 | // console.info(service.name + ": update edge " + id + ": " + route.name)
97 | if (typeof existedEdge != "undefined" && typeof route != "undefined") {
98 | this.nodesDataSet.update({
99 | id: to,
100 | strike: false
101 | })
102 | if (this.isNeedUpdate(existedEdge, route)) {
103 | // console.info(route.name + " " + existedEdge.route.exchangesTotal + ' -> ' + route.exchangesTotal)
104 | var routeRepresentation = this.getRouteRepresentation(route, service)
105 | this.edgesDataSet.update([{
106 | id: id,
107 | from: from,
108 | to: to,
109 | route: route,
110 | color: routeRepresentation.color,
111 | title: this.getRouteTitle(route),
112 | value: routeRepresentation.weight,
113 | dashes: routeRepresentation.dashes,
114 | label: route.exchangesTotal ? '' + route.exchangesTotal : null,
115 | font: {align: 'top'}
116 | }]);
117 | this.nodesDataSet.update({
118 | id: to,
119 | strike: true
120 | })
121 | }
122 | }
123 | },
124 | getRouteRepresentation: function (route, service) {
125 | var color = service.color
126 | var dashes = false
127 | var weight = 1 //route.exchangesTotal,
128 | if (route.state == 'None') {
129 | color = "#b2b2b2"
130 | dashes = true
131 | } else if (route.state != 'Started') {
132 | color = "#ff251e"
133 | dashes = true
134 | }
135 | return {
136 | color: {color: color},
137 | dashes: dashes,
138 | weight: weight
139 | }
140 | },
141 | isNeedUpdate: function (edge, route) {
142 | if (typeof route.exchangesTotal == "undefined"
143 | || typeof edge.route == "undefined"
144 | || typeof edge.route.exchangesTotal == "undefined") {
145 | return false
146 | }
147 | return route.exchangesTotal != edge.route.exchangesTotal
148 | || route.state != edge.route.state
149 | || route.uptime != edge.route.uptime
150 | },
151 | build: function (data) {
152 | this.nodesDataSet = new vis.DataSet([])
153 | this.edgesDataSet = new vis.DataSet([])
154 |
155 | // Created elements from endpoints
156 | for (var i = 0; i < data.services.length; i++) {
157 | var service = data.services[i]
158 | for (var j = 0; j < service.routes.length; j++) {
159 | var route = service.routes[j]
160 | if (route.endpoints && route.endpoints.inputs) {
161 | for (var n = 0; n < route.endpoints.inputs.length; n++) {
162 | var endpoint = route.endpoints.inputs[n]
163 | this.addEndpoint(endpoint, service)
164 | }
165 | }
166 | if (route.endpoints && route.endpoints.outputs) {
167 | for (var n = 0; n < route.endpoints.outputs.length; n++) {
168 | var endpoint = route.endpoints.outputs[n]
169 | //TODO duplicates
170 | if (endpoint.indexOf("{{") !== -1) {
171 | continue
172 | }
173 | this.addEndpoint(endpoint, service)
174 | }
175 | }
176 | }
177 | }
178 | // Create edges
179 | for (var i = 0; i < data.services.length; i++) {
180 | var service = data.services[i]
181 | for (var j = 0; j < service.routes.length; j++) {
182 | var route = service.routes[j]
183 | if (route.endpoints && route.endpoints.inputs) {
184 | for (var n = 0; n < route.endpoints.inputs.length; n++) {
185 | var input = route.endpoints.inputs[n]
186 | if (route.endpoints.outputs) {
187 | for (var m = 0; m < route.endpoints.outputs.length; m++) {
188 | var output = route.endpoints.outputs[m]
189 | var from = this.endpoints[input]
190 | var to = this.endpoints[output]
191 | // console.info(service.name + ": add " + input + ' (' + from + ')' +
192 | // " to " + output + ' (' + to + ')' + ' route ' + route.name + ' exists ' + this.edgeExists(from, to))
193 | // console.info("pre done " + (from && to && !this.edgeExists(from, to)))
194 | // console.info("pre done " + (from && to && !this.edgeMap[from + '_' + to]))
195 | if (typeof from != "undefined"
196 | && typeof to != "undefined"
197 | && !this.edgeExists(from, to)) {
198 | // console.info("done")
199 | this.addEdge(route, from, to, service)
200 | }
201 | }
202 | }
203 | }
204 | }
205 | }
206 | }
207 | //TODO
208 | for (var key in this.endpoints) {
209 | this.endpointsKeys.push(key);
210 | }
211 | }
212 | }
213 | graph.build(data)
214 | return graph
215 | },
216 | updateGraph: function (graph, data) {
217 | // nodes
218 | for (var i = 0; i < data.services.length; i++) {
219 | var service = data.services[i]
220 | for (var j = 0; j < service.routes.length; j++) {
221 | var route = service.routes[j]
222 | if (route.endpoints && route.endpoints.inputs) {
223 | for (var n = 0; n < route.endpoints.inputs.length; n++) {
224 | var endpoint = route.endpoints.inputs[n]
225 | var id = graph.endpoints[endpoint]
226 | if (typeof id == "undefined") {
227 | graph.addEndpoint(endpoint, service)
228 | }
229 | }
230 | }
231 | if (route.endpoints && route.endpoints.outputs) {
232 | for (var n = 0; n < route.endpoints.outputs.length; n++) {
233 | var endpoint = route.endpoints.outputs[n]
234 | //TODO duplicates
235 | if (endpoint.indexOf("{{") !== -1) {
236 | continue
237 | }
238 | var id = graph.endpoints[endpoint]
239 | if (typeof id == "undefined") {
240 | graph.addEndpoint(endpoint, service)
241 | }
242 | }
243 | }
244 | }
245 | }
246 | // edges
247 | for (var i = 0; i < data.services.length; i++) {
248 | var service = data.services[i]
249 | for (var j = 0; j < service.routes.length; j++) {
250 | var route = service.routes[j]
251 | if (route.endpoints && route.endpoints.inputs) {
252 | for (var n = 0; n < route.endpoints.inputs.length; n++) {
253 | var input = route.endpoints.inputs[n]
254 | if (route.endpoints.outputs) {
255 | for (var m = 0; m < route.endpoints.outputs.length; m++) {
256 | var output = route.endpoints.outputs[m]
257 | var from = graph.endpoints[input]
258 | var to = graph.endpoints[output]
259 | //TODO duplicates
260 | if (typeof from != "undefined"
261 | && typeof to != "undefined") {
262 | if (graph.edgeExists(from, to)) {
263 | graph.updateEdge(route, from, to, service)
264 | } else {
265 | graph.addEdge(route, from, to, service)
266 | }
267 | }
268 | }
269 | }
270 | }
271 | }
272 | }
273 | }
274 | }
275 | }
276 | })
--------------------------------------------------------------------------------
/public/js/vendor/angular-relative-date.min.js:
--------------------------------------------------------------------------------
1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var o;o="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,o.relativeDate=e()}}(function(){return function e(o,r,t){function a(u,f){if(!r[u]){if(!o[u]){var s="function"==typeof require&&require;if(!f&&s)return s(u,!0);if(n)return n(u,!0);var i=new Error("Cannot find module '"+u+"'");throw i.code="MODULE_NOT_FOUND",i}var _=r[u]={exports:{}};o[u][0].call(_.exports,function(e){var r=o[u][1][e];return a(r?r:e)},_,_.exports,e,o,r,t)}return r[u].exports}for(var n="function"==typeof require&&require,u=0;us&&i>a&&(e=new Date(e.getFullYear(),e.getMonth(),e.getDate(),0,0,0),a=n(r,e));var l=function(o,a){var n;return n="just_now"===o?o:r>=e?o+"_ago":o+"_from_now",t.instant(n,{time:a})};switch(!1){case!(30>a):return l("just_now");case!(u>a):return l("seconds",a);case!(2*u>a):return l("a_minute");case!(f>a):return l("minutes",Math.floor(a/u));case 1!==Math.floor(a/f):return l("an_hour");case!(s>a):return l("hours",Math.floor(a/f));case!(2*s>a):return l("a_day");case!(i>a):return l("days",Math.floor(a/s));case 1!==Math.floor(a/i):return l("a_week");case!(_>a):return l("weeks",Math.floor(a/i));case 1!==Math.floor(a/_):return l("a_month");case!(m>a):return l("months",Math.floor(a/_));case 1!==Math.floor(a/m):return l("a_year");default:return l("over_a_year")}}}Object.defineProperty(r,"__esModule",{value:!0}),r["default"]=t;var a=function(e,o){return e.has("$translate")?e.get("$translate"):{instant:function(e,r){return o[e].replace("{{time}}",r.time)}}},n=function(e,o){return Math.round(Math.abs(e-o)/1e3)}},{}],2:[function(e,o,r){"use strict";function t(e){return e&&e.__esModule?e:{"default":e}}Object.defineProperty(r,"__esModule",{value:!0});var a=e("./translations"),n=t(a),u=e("./filter"),f=t(u),s=angular.module("relativeDate",[]);s.value("now",null),s.value("relativeDateTranslations",n["default"]),s.filter("relativeDate",["$injector","now","relativeDateTranslations",f["default"]]),r["default"]=s},{"./filter":1,"./translations":3}],3:[function(e,o,r){"use strict";Object.defineProperty(r,"__esModule",{value:!0}),r["default"]={just_now:"just now",seconds_ago:"{{time}} seconds ago",a_minute_ago:"a minute ago",minutes_ago:"{{time}} minutes ago",an_hour_ago:"an hour ago",hours_ago:"{{time}} hours ago",a_day_ago:"yesterday",days_ago:"{{time}} days ago",a_week_ago:"a week ago",weeks_ago:"{{time}} weeks ago",a_month_ago:"a month ago",months_ago:"{{time}} months ago",a_year_ago:"a year ago",years_ago:"{{time}} years ago",over_a_year_ago:"over a year ago",seconds_from_now:"{{time}} seconds from now",a_minute_from_now:"a minute from now",minutes_from_now:"{{time}} minutes from now",an_hour_from_now:"an hour from now",hours_from_now:"{{time}} hours from now",a_day_from_now:"tomorrow",days_from_now:"{{time}} days from now",a_week_from_now:"a week from now",weeks_from_now:"{{time}} weeks from now",a_month_from_now:"a month from now",months_from_now:"{{time}} months from now",a_year_from_now:"a year from now",years_from_now:"{{time}} years from now",over_a_year_from_now:"over a year from now"}},{}]},{},[2])(2)});
2 | //# sourceMappingURL=angular-relative-date.min.js.map
3 |
--------------------------------------------------------------------------------
/public/js/vendor/angular-relative-date.min.js.map:
--------------------------------------------------------------------------------
1 | {"version":3,"sources":["angular-relative-date.min.js"],"names":["f","exports","module","define","amd","g","window","global","self","this","relativeDate","e","t","n","r","s","o","u","a","require","i","Error","code","l","call","length","1","_dereq_","relativeDateFilter","$injector","_now","relativeDateTranslations","$translate","getTranslate","date","now","Date","delta","minute","hour","day","week","month","year","calculateDelta","getFullYear","getMonth","getDate","translate","translatePhrase","timeValue","translateKey","instant","time","Math","floor","Object","defineProperty","value","injector","translations","has","get","id","params","replace","round","abs","2","_interopRequireDefault","obj","__esModule","default","_translations","_translations2","_filter","_filter2","angular","filter","./filter","./translations","3","just_now","seconds_ago","a_minute_ago","minutes_ago","an_hour_ago","hours_ago","a_day_ago","days_ago","a_week_ago","weeks_ago","a_month_ago","months_ago","a_year_ago","years_ago","over_a_year_ago","seconds_from_now","a_minute_from_now","minutes_from_now","an_hour_from_now","hours_from_now","a_day_from_now","days_from_now","a_week_from_now","weeks_from_now","a_month_from_now","months_from_now","a_year_from_now","years_from_now","over_a_year_from_now"],"mappings":"CAAA,SAAUA,GAAG,GAAoB,gBAAVC,UAAoC,mBAATC,QAAsBA,OAAOD,QAAQD,QAAS,IAAmB,kBAATG,SAAqBA,OAAOC,IAAKD,UAAUH,OAAO,CAAC,GAAIK,EAAkCA,GAAb,mBAATC,QAAwBA,OAA+B,mBAATC,QAAwBA,OAA6B,mBAAPC,MAAsBA,KAAYC,KAAKJ,EAAEK,aAAeV,MAAO,WAAqC,MAAO,SAAUW,GAAEC,EAAEC,EAAEC,GAAG,QAASC,GAAEC,EAAEC,GAAG,IAAIJ,EAAEG,GAAG,CAAC,IAAIJ,EAAEI,GAAG,CAAC,GAAIE,GAAkB,kBAATC,UAAqBA,OAAQ,KAAIF,GAAGC,EAAE,MAAOA,GAAEF,GAAE,EAAI,IAAGI,EAAE,MAAOA,GAAEJ,GAAE,EAAI,IAAIhB,GAAE,GAAIqB,OAAM,uBAAuBL,EAAE,IAAK,MAAMhB,GAAEsB,KAAK,mBAAmBtB,EAAE,GAAIuB,GAAEV,EAAEG,IAAIf,WAAYW,GAAEI,GAAG,GAAGQ,KAAKD,EAAEtB,QAAQ,SAASU,GAAG,GAAIE,GAAED,EAAEI,GAAG,GAAGL,EAAG,OAAOI,GAAEF,EAAEA,EAAEF,IAAIY,EAAEA,EAAEtB,QAAQU,EAAEC,EAAEC,EAAEC,GAAG,MAAOD,GAAEG,GAAGf,QAAkD,IAAI,GAA1CmB,GAAkB,kBAATD,UAAqBA,QAAgBH,EAAE,EAAEA,EAAEF,EAAEW,OAAOT,IAAID,EAAED,EAAEE,GAAI,OAAOD,KAAKW,GAAG,SAASC,EAAQzB,EAAOD,GAC30B,YAsBA,SAAS2B,GAAmBC,EAAWC,EAAMC,GAC3C,GAAIC,GAAaC,EAAaJ,EAAWE,EAEzC,OAAO,UAAUG,GACf,GAAIC,GAAML,GAAQ,GAAIM,KAEhBF,aAAgBE,QACpBF,EAAO,GAAIE,MAAKF,GAGlB,IAAIG,GAAQ,KAERC,EAAS,GACTC,EAAgB,GAATD,EACPE,EAAa,GAAPD,EACNE,EAAa,EAAND,EACPE,EAAc,GAANF,EACRG,EAAa,IAANH,CAEXH,GAAQO,EAAeT,EAAKD,GAExBG,EAAQG,GAAeC,EAARJ,IACjBH,EAAO,GAAIE,MAAKF,EAAKW,cAAeX,EAAKY,WAAYZ,EAAKa,UAAW,EAAG,EAAG,GAC3EV,EAAQO,EAAeT,EAAKD,GAG9B,IAAIc,GAAY,SAAmBC,EAAiBC,GAClD,GAAIC,EAUJ,OAPEA,GADsB,aAApBF,EACaA,EACNd,GAAOD,EACDe,EAAkB,OAElBA,EAAkB,YAG5BjB,EAAWoB,QAAQD,GACxBE,KAAMH,IAIV,SAAQ,GACN,MAAe,GAARb,GACL,MAAOW,GAAU,WAEnB,OAAeV,EAARD,GACL,MAAOW,GAAU,UAAWX,EAE9B,OAAe,EAAIC,EAAZD,GACL,MAAOW,GAAU,WAEnB,OAAeT,EAARF,GACL,MAAOW,GAAU,UAAWM,KAAKC,MAAMlB,EAAQC,GAEjD,KAAkC,KAA7BgB,KAAKC,MAAMlB,EAAQE,GACtB,MAAOS,GAAU,UAEnB,OAAeR,EAARH,GACL,MAAOW,GAAU,QAASM,KAAKC,MAAMlB,EAAQE,GAE/C,OAAqB,EAANC,EAARH,GACL,MAAOW,GAAU,QAEnB,OAAeP,EAARJ,GACL,MAAOW,GAAU,OAAQM,KAAKC,MAAMlB,EAAQG,GAE9C,KAAkC,KAA7Bc,KAAKC,MAAMlB,EAAQI,GACtB,MAAOO,GAAU,SAEnB,OAAeN,EAARL,GACL,MAAOW,GAAU,QAASM,KAAKC,MAAMlB,EAAQI,GAE/C,KAAmC,KAA9Ba,KAAKC,MAAMlB,EAAQK,GACtB,MAAOM,GAAU,UAEnB,OAAeL,EAARN,GACL,MAAOW,GAAU,SAAUM,KAAKC,MAAMlB,EAAQK,GAEhD,KAAkC,KAA7BY,KAAKC,MAAMlB,EAAQM,GACtB,MAAOK,GAAU,SAEnB,SACE,MAAOA,GAAU,iBAvGzBQ,OAAOC,eAAexD,EAAS,cAC7ByD,OAAO,IAETzD,EAAAA,WAAkB2B,CAClB,IAAIK,GAAe,SAAsB0B,EAAUC,GACjD,MAAID,GAASE,IAAI,cACRF,EAASG,IAAI,eAGlBV,QAAS,SAAiBW,EAAIC,GAC5B,MAAOJ,GAAaG,GAAIE,QAAQ,WAAYD,EAAOX,SAMvDT,EAAiB,SAAwBT,EAAKD,GAChD,MAAOoB,MAAKY,MAAMZ,KAAKa,IAAIhC,EAAMD,GAAQ,WA2FrCkC,GAAG,SAASzC,EAAQzB,EAAOD,GACjC,YAcA,SAASoE,GAAuBC,GAAO,MAAOA,IAAOA,EAAIC,WAAaD,GAAQE,UAASF,GAZvFd,OAAOC,eAAexD,EAAS,cAC7ByD,OAAO,GAGT,IAAIe,GAAgB9C,EAAQ,kBAExB+C,EAAiBL,EAAuBI,GAExCE,EAAUhD,EAAQ,YAElBiD,EAAWP,EAAuBM,GAMlCjE,EAAemE,QAAQ3E,OAAO,kBAElCQ,GAAagD,MAAM,MAAO,MAC1BhD,EAAagD,MAAM,2BAA4BgB,EAAAA,YAE/ChE,EAAaoE,OAAO,gBAAiB,YAAa,MAAO,2BAA4BF,EAAAA,aAErF3E,EAAAA,WAAkBS,IAEfqE,WAAW,EAAEC,iBAAiB,IAAIC,GAAG,SAAStD,EAAQzB,EAAOD,GAChE,YAEAuD,QAAOC,eAAexD,EAAS,cAC7ByD,OAAO,IAETzD,EAAAA,YACEiF,SAAU,WACVC,YAAa,uBACbC,aAAc,eACdC,YAAa,uBACbC,YAAa,cACbC,UAAW,qBACXC,UAAW,YACXC,SAAU,oBACVC,WAAY,aACZC,UAAW,qBACXC,YAAa,cACbC,WAAY,sBACZC,WAAY,aACZC,UAAW,qBACXC,gBAAiB,kBACjBC,iBAAkB,4BAClBC,kBAAmB,oBACnBC,iBAAkB,4BAClBC,iBAAkB,mBAClBC,eAAgB,0BAChBC,eAAgB,WAChBC,cAAe,yBACfC,gBAAiB,kBACjBC,eAAgB,0BAChBC,iBAAkB,mBAClBC,gBAAiB,2BACjBC,gBAAiB,kBACjBC,eAAgB,0BAChBC,qBAAsB,kCAGb,IAAI","file":"angular-relative-date.min.js","sourcesContent":["(function(f){if(typeof exports===\"object\"&&typeof module!==\"undefined\"){module.exports=f()}else if(typeof define===\"function\"&&define.amd){define([],f)}else{var g;if(typeof window!==\"undefined\"){g=window}else if(typeof global!==\"undefined\"){g=global}else if(typeof self!==\"undefined\"){g=self}else{g=this}g.relativeDate = f()}})(function(){var define,module,exports;return (function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error(\"Cannot find module '\"+o+\"'\");throw f.code=\"MODULE_NOT_FOUND\",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require==\"function\"&&require;for(var o=0;o day && delta < week) {\n date = new Date(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);\n delta = calculateDelta(now, date);\n }\n\n var translate = function translate(translatePhrase, timeValue) {\n var translateKey;\n\n if (translatePhrase === 'just_now') {\n translateKey = translatePhrase;\n } else if (now >= date) {\n translateKey = translatePhrase + '_ago';\n } else {\n translateKey = translatePhrase + '_from_now';\n }\n\n return $translate.instant(translateKey, {\n time: timeValue\n });\n };\n\n switch (false) {\n case !(delta < 30):\n return translate('just_now');\n\n case !(delta < minute):\n return translate('seconds', delta);\n\n case !(delta < 2 * minute):\n return translate('a_minute');\n\n case !(delta < hour):\n return translate('minutes', Math.floor(delta / minute));\n\n case Math.floor(delta / hour) !== 1:\n return translate('an_hour');\n\n case !(delta < day):\n return translate('hours', Math.floor(delta / hour));\n\n case !(delta < day * 2):\n return translate('a_day');\n\n case !(delta < week):\n return translate('days', Math.floor(delta / day));\n\n case Math.floor(delta / week) !== 1:\n return translate('a_week');\n\n case !(delta < month):\n return translate('weeks', Math.floor(delta / week));\n\n case Math.floor(delta / month) !== 1:\n return translate('a_month');\n\n case !(delta < year):\n return translate('months', Math.floor(delta / month));\n\n case Math.floor(delta / year) !== 1:\n return translate('a_year');\n\n default:\n return translate('over_a_year');\n }\n };\n}\n\n},{}],2:[function(_dereq_,module,exports){\n'use strict';\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\n\nvar _translations = _dereq_('./translations');\n\nvar _translations2 = _interopRequireDefault(_translations);\n\nvar _filter = _dereq_('./filter');\n\nvar _filter2 = _interopRequireDefault(_filter);\n\nfunction _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }\n\n/* global angular */\n\nvar relativeDate = angular.module('relativeDate', []);\n\nrelativeDate.value('now', null);\nrelativeDate.value('relativeDateTranslations', _translations2.default);\n\nrelativeDate.filter('relativeDate', ['$injector', 'now', 'relativeDateTranslations', _filter2.default]);\n\nexports.default = relativeDate;\n\n},{\"./filter\":1,\"./translations\":3}],3:[function(_dereq_,module,exports){\n'use strict';\n\nObject.defineProperty(exports, \"__esModule\", {\n value: true\n});\nexports.default = {\n just_now: 'just now',\n seconds_ago: '{{time}} seconds ago',\n a_minute_ago: 'a minute ago',\n minutes_ago: '{{time}} minutes ago',\n an_hour_ago: 'an hour ago',\n hours_ago: '{{time}} hours ago',\n a_day_ago: 'yesterday',\n days_ago: '{{time}} days ago',\n a_week_ago: 'a week ago',\n weeks_ago: '{{time}} weeks ago',\n a_month_ago: 'a month ago',\n months_ago: '{{time}} months ago',\n a_year_ago: 'a year ago',\n years_ago: '{{time}} years ago',\n over_a_year_ago: 'over a year ago',\n seconds_from_now: '{{time}} seconds from now',\n a_minute_from_now: 'a minute from now',\n minutes_from_now: '{{time}} minutes from now',\n an_hour_from_now: 'an hour from now',\n hours_from_now: '{{time}} hours from now',\n a_day_from_now: 'tomorrow',\n days_from_now: '{{time}} days from now',\n a_week_from_now: 'a week from now',\n weeks_from_now: '{{time}} weeks from now',\n a_month_from_now: 'a month from now',\n months_from_now: '{{time}} months from now',\n a_year_from_now: 'a year from now',\n years_from_now: '{{time}} years from now',\n over_a_year_from_now: 'over a year from now'\n};\n\n},{}]},{},[2])(2)\n});\n\n"],"sourceRoot":"/source/"}
--------------------------------------------------------------------------------
/public/js/vendor/angular-sanitize.js:
--------------------------------------------------------------------------------
1 | /**
2 | * @license AngularJS v1.3.16
3 | * (c) 2010-2014 Google, Inc. http://angularjs.org
4 | * License: MIT
5 | */
6 | (function(window, angular, undefined) {'use strict';
7 |
8 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
9 | * Any commits to this file should be reviewed with security in mind. *
10 | * Changes to this file can potentially create security vulnerabilities. *
11 | * An approval from 2 Core members with history of modifying *
12 | * this file is required. *
13 | * *
14 | * Does the change somehow allow for arbitrary javascript to be executed? *
15 | * Or allows for someone to change the prototype of built-in objects? *
16 | * Or gives undesired access to variables likes document or window? *
17 | * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
18 |
19 | var $sanitizeMinErr = angular.$$minErr('$sanitize');
20 |
21 | /**
22 | * @ngdoc module
23 | * @name ngSanitize
24 | * @description
25 | *
26 | * # ngSanitize
27 | *
28 | * The `ngSanitize` module provides functionality to sanitize HTML.
29 | *
30 | *
31 | *
32 | *
33 | * See {@link ngSanitize.$sanitize `$sanitize`} for usage.
34 | */
35 |
36 | /*
37 | * HTML Parser By Misko Hevery (misko@hevery.com)
38 | * based on: HTML Parser By John Resig (ejohn.org)
39 | * Original code by Erik Arvidsson, Mozilla Public License
40 | * http://erik.eae.net/simplehtmlparser/simplehtmlparser.js
41 | *
42 | * // Use like so:
43 | * htmlParser(htmlString, {
44 | * start: function(tag, attrs, unary) {},
45 | * end: function(tag) {},
46 | * chars: function(text) {},
47 | * comment: function(text) {}
48 | * });
49 | *
50 | */
51 |
52 |
53 | /**
54 | * @ngdoc service
55 | * @name $sanitize
56 | * @kind function
57 | *
58 | * @description
59 | * The input is sanitized by parsing the HTML into tokens. All safe tokens (from a whitelist) are
60 | * then serialized back to properly escaped html string. This means that no unsafe input can make
61 | * it into the returned string, however, since our parser is more strict than a typical browser
62 | * parser, it's possible that some obscure input, which would be recognized as valid HTML by a
63 | * browser, won't make it through the sanitizer. The input may also contain SVG markup.
64 | * The whitelist is configured using the functions `aHrefSanitizationWhitelist` and
65 | * `imgSrcSanitizationWhitelist` of {@link ng.$compileProvider `$compileProvider`}.
66 | *
67 | * @param {string} html HTML input.
68 | * @returns {string} Sanitized HTML.
69 | *
70 | * @example
71 |
72 |
73 |
85 |
86 | Snippet:
87 |
88 |
89 | Directive |
90 | How |
91 | Source |
92 | Rendered |
93 |
94 |
95 | ng-bind-html |
96 | Automatically uses $sanitize |
97 | <div ng-bind-html="snippet"> </div> |
98 | |
99 |
100 |
101 | ng-bind-html |
102 | Bypass $sanitize by explicitly trusting the dangerous value |
103 |
104 | <div ng-bind-html="deliberatelyTrustDangerousSnippet()">
105 | </div>
106 | |
107 | |
108 |
109 |
110 | ng-bind |
111 | Automatically escapes |
112 | <div ng-bind="snippet"> </div> |
113 | |
114 |
115 |
116 |
117 |
118 |
119 | it('should sanitize the html snippet by default', function() {
120 | expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
121 | toBe('an html\nclick here\nsnippet
');
122 | });
123 |
124 | it('should inline raw snippet if bound to a trusted value', function() {
125 | expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).
126 | toBe("an html\n" +
127 | "click here\n" +
128 | "snippet
");
129 | });
130 |
131 | it('should escape snippet without any filter', function() {
132 | expect(element(by.css('#bind-default div')).getInnerHtml()).
133 | toBe("<p style=\"color:blue\">an html\n" +
134 | "<em onmouseover=\"this.textContent='PWN3D!'\">click here</em>\n" +
135 | "snippet</p>");
136 | });
137 |
138 | it('should update', function() {
139 | element(by.model('snippet')).clear();
140 | element(by.model('snippet')).sendKeys('new text');
141 | expect(element(by.css('#bind-html-with-sanitize div')).getInnerHtml()).
142 | toBe('new text');
143 | expect(element(by.css('#bind-html-with-trust div')).getInnerHtml()).toBe(
144 | 'new text');
145 | expect(element(by.css('#bind-default div')).getInnerHtml()).toBe(
146 | "new <b onclick=\"alert(1)\">text</b>");
147 | });
148 |
149 |
150 | */
151 | function $SanitizeProvider() {
152 | this.$get = ['$$sanitizeUri', function($$sanitizeUri) {
153 | return function(html) {
154 | var buf = [];
155 | htmlParser(html, htmlSanitizeWriter(buf, function(uri, isImage) {
156 | return !/^unsafe/.test($$sanitizeUri(uri, isImage));
157 | }));
158 | return buf.join('');
159 | };
160 | }];
161 | }
162 |
163 | function sanitizeText(chars) {
164 | var buf = [];
165 | var writer = htmlSanitizeWriter(buf, angular.noop);
166 | writer.chars(chars);
167 | return buf.join('');
168 | }
169 |
170 |
171 | // Regular Expressions for parsing tags and attributes
172 | var START_TAG_REGEXP =
173 | /^<((?:[a-zA-Z])[\w:-]*)((?:\s+[\w:-]+(?:\s*=\s*(?:(?:"[^"]*")|(?:'[^']*')|[^>\s]+))?)*)\s*(\/?)\s*(>?)/,
174 | END_TAG_REGEXP = /^<\/\s*([\w:-]+)[^>]*>/,
175 | ATTR_REGEXP = /([\w:-]+)(?:\s*=\s*(?:(?:"((?:[^"])*)")|(?:'((?:[^'])*)')|([^>\s]+)))?/g,
176 | BEGIN_TAG_REGEXP = /^,
177 | BEGING_END_TAGE_REGEXP = /^<\//,
178 | COMMENT_REGEXP = //g,
179 | DOCTYPE_REGEXP = /]*?)>/i,
180 | CDATA_REGEXP = //g,
181 | SURROGATE_PAIR_REGEXP = /[\uD800-\uDBFF][\uDC00-\uDFFF]/g,
182 | // Match everything outside of normal chars and " (quote character)
183 | NON_ALPHANUMERIC_REGEXP = /([^\#-~| |!])/g;
184 |
185 |
186 | // Good source of info about elements and attributes
187 | // http://dev.w3.org/html5/spec/Overview.html#semantics
188 | // http://simon.html5.org/html-elements
189 |
190 | // Safe Void Elements - HTML5
191 | // http://dev.w3.org/html5/spec/Overview.html#void-elements
192 | var voidElements = makeMap("area,br,col,hr,img,wbr");
193 |
194 | // Elements that you can, intentionally, leave open (and which close themselves)
195 | // http://dev.w3.org/html5/spec/Overview.html#optional-tags
196 | var optionalEndTagBlockElements = makeMap("colgroup,dd,dt,li,p,tbody,td,tfoot,th,thead,tr"),
197 | optionalEndTagInlineElements = makeMap("rp,rt"),
198 | optionalEndTagElements = angular.extend({},
199 | optionalEndTagInlineElements,
200 | optionalEndTagBlockElements);
201 |
202 | // Safe Block Elements - HTML5
203 | var blockElements = angular.extend({}, optionalEndTagBlockElements, makeMap("address,article," +
204 | "aside,blockquote,caption,center,del,dir,div,dl,figure,figcaption,footer,h1,h2,h3,h4,h5," +
205 | "h6,header,hgroup,hr,ins,map,menu,nav,ol,pre,script,section,table,ul"));
206 |
207 | // Inline Elements - HTML5
208 | var inlineElements = angular.extend({}, optionalEndTagInlineElements, makeMap("a,abbr,acronym,b," +
209 | "bdi,bdo,big,br,cite,code,del,dfn,em,font,i,img,ins,kbd,label,map,mark,q,ruby,rp,rt,s," +
210 | "samp,small,span,strike,strong,sub,sup,time,tt,u,var"));
211 |
212 | // SVG Elements
213 | // https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Elements
214 | var svgElements = makeMap("animate,animateColor,animateMotion,animateTransform,circle,defs," +
215 | "desc,ellipse,font-face,font-face-name,font-face-src,g,glyph,hkern,image,linearGradient," +
216 | "line,marker,metadata,missing-glyph,mpath,path,polygon,polyline,radialGradient,rect,set," +
217 | "stop,svg,switch,text,title,tspan,use");
218 |
219 | // Special Elements (can contain anything)
220 | var specialElements = makeMap("script,style");
221 |
222 | var validElements = angular.extend({},
223 | voidElements,
224 | blockElements,
225 | inlineElements,
226 | optionalEndTagElements,
227 | svgElements);
228 |
229 | //Attributes that have href and hence need to be sanitized
230 | var uriAttrs = makeMap("background,cite,href,longdesc,src,usemap,xlink:href");
231 |
232 | var htmlAttrs = makeMap('abbr,align,alt,axis,bgcolor,border,cellpadding,cellspacing,class,clear,' +
233 | 'color,cols,colspan,compact,coords,dir,face,headers,height,hreflang,hspace,' +
234 | 'ismap,lang,language,nohref,nowrap,rel,rev,rows,rowspan,rules,' +
235 | 'scope,scrolling,shape,size,span,start,summary,target,title,type,' +
236 | 'valign,value,vspace,width');
237 |
238 | // SVG attributes (without "id" and "name" attributes)
239 | // https://wiki.whatwg.org/wiki/Sanitization_rules#svg_Attributes
240 | var svgAttrs = makeMap('accent-height,accumulate,additive,alphabetic,arabic-form,ascent,' +
241 | 'attributeName,attributeType,baseProfile,bbox,begin,by,calcMode,cap-height,class,color,' +
242 | 'color-rendering,content,cx,cy,d,dx,dy,descent,display,dur,end,fill,fill-rule,font-family,' +
243 | 'font-size,font-stretch,font-style,font-variant,font-weight,from,fx,fy,g1,g2,glyph-name,' +
244 | 'gradientUnits,hanging,height,horiz-adv-x,horiz-origin-x,ideographic,k,keyPoints,' +
245 | 'keySplines,keyTimes,lang,marker-end,marker-mid,marker-start,markerHeight,markerUnits,' +
246 | 'markerWidth,mathematical,max,min,offset,opacity,orient,origin,overline-position,' +
247 | 'overline-thickness,panose-1,path,pathLength,points,preserveAspectRatio,r,refX,refY,' +
248 | 'repeatCount,repeatDur,requiredExtensions,requiredFeatures,restart,rotate,rx,ry,slope,stemh,' +
249 | 'stemv,stop-color,stop-opacity,strikethrough-position,strikethrough-thickness,stroke,' +
250 | 'stroke-dasharray,stroke-dashoffset,stroke-linecap,stroke-linejoin,stroke-miterlimit,' +
251 | 'stroke-opacity,stroke-width,systemLanguage,target,text-anchor,to,transform,type,u1,u2,' +
252 | 'underline-position,underline-thickness,unicode,unicode-range,units-per-em,values,version,' +
253 | 'viewBox,visibility,width,widths,x,x-height,x1,x2,xlink:actuate,xlink:arcrole,xlink:role,' +
254 | 'xlink:show,xlink:title,xlink:type,xml:base,xml:lang,xml:space,xmlns,xmlns:xlink,y,y1,y2,' +
255 | 'zoomAndPan');
256 |
257 | var validAttrs = angular.extend({},
258 | uriAttrs,
259 | svgAttrs,
260 | htmlAttrs);
261 |
262 | function makeMap(str) {
263 | var obj = {}, items = str.split(','), i;
264 | for (i = 0; i < items.length; i++) obj[items[i]] = true;
265 | return obj;
266 | }
267 |
268 |
269 | /**
270 | * @example
271 | * htmlParser(htmlString, {
272 | * start: function(tag, attrs, unary) {},
273 | * end: function(tag) {},
274 | * chars: function(text) {},
275 | * comment: function(text) {}
276 | * });
277 | *
278 | * @param {string} html string
279 | * @param {object} handler
280 | */
281 | function htmlParser(html, handler) {
282 | if (typeof html !== 'string') {
283 | if (html === null || typeof html === 'undefined') {
284 | html = '';
285 | } else {
286 | html = '' + html;
287 | }
288 | }
289 | var index, chars, match, stack = [], last = html, text;
290 | stack.last = function() { return stack[stack.length - 1]; };
291 |
292 | while (html) {
293 | text = '';
294 | chars = true;
295 |
296 | // Make sure we're not in a script or style element
297 | if (!stack.last() || !specialElements[stack.last()]) {
298 |
299 | // Comment
300 | if (html.indexOf("", index) === index) {
305 | if (handler.comment) handler.comment(html.substring(4, index));
306 | html = html.substring(index + 3);
307 | chars = false;
308 | }
309 | // DOCTYPE
310 | } else if (DOCTYPE_REGEXP.test(html)) {
311 | match = html.match(DOCTYPE_REGEXP);
312 |
313 | if (match) {
314 | html = html.replace(match[0], '');
315 | chars = false;
316 | }
317 | // end tag
318 | } else if (BEGING_END_TAGE_REGEXP.test(html)) {
319 | match = html.match(END_TAG_REGEXP);
320 |
321 | if (match) {
322 | html = html.substring(match[0].length);
323 | match[0].replace(END_TAG_REGEXP, parseEndTag);
324 | chars = false;
325 | }
326 |
327 | // start tag
328 | } else if (BEGIN_TAG_REGEXP.test(html)) {
329 | match = html.match(START_TAG_REGEXP);
330 |
331 | if (match) {
332 | // We only have a valid start-tag if there is a '>'.
333 | if (match[4]) {
334 | html = html.substring(match[0].length);
335 | match[0].replace(START_TAG_REGEXP, parseStartTag);
336 | }
337 | chars = false;
338 | } else {
339 | // no ending tag found --- this piece should be encoded as an entity.
340 | text += '<';
341 | html = html.substring(1);
342 | }
343 | }
344 |
345 | if (chars) {
346 | index = html.indexOf("<");
347 |
348 | text += index < 0 ? html : html.substring(0, index);
349 | html = index < 0 ? "" : html.substring(index);
350 |
351 | if (handler.chars) handler.chars(decodeEntities(text));
352 | }
353 |
354 | } else {
355 | // IE versions 9 and 10 do not understand the regex '[^]', so using a workaround with [\W\w].
356 | html = html.replace(new RegExp("([\\W\\w]*)<\\s*\\/\\s*" + stack.last() + "[^>]*>", 'i'),
357 | function(all, text) {
358 | text = text.replace(COMMENT_REGEXP, "$1").replace(CDATA_REGEXP, "$1");
359 |
360 | if (handler.chars) handler.chars(decodeEntities(text));
361 |
362 | return "";
363 | });
364 |
365 | parseEndTag("", stack.last());
366 | }
367 |
368 | if (html == last) {
369 | throw $sanitizeMinErr('badparse', "The sanitizer was unable to parse the following block " +
370 | "of html: {0}", html);
371 | }
372 | last = html;
373 | }
374 |
375 | // Clean up any remaining tags
376 | parseEndTag();
377 |
378 | function parseStartTag(tag, tagName, rest, unary) {
379 | tagName = angular.lowercase(tagName);
380 | if (blockElements[tagName]) {
381 | while (stack.last() && inlineElements[stack.last()]) {
382 | parseEndTag("", stack.last());
383 | }
384 | }
385 |
386 | if (optionalEndTagElements[tagName] && stack.last() == tagName) {
387 | parseEndTag("", tagName);
388 | }
389 |
390 | unary = voidElements[tagName] || !!unary;
391 |
392 | if (!unary)
393 | stack.push(tagName);
394 |
395 | var attrs = {};
396 |
397 | rest.replace(ATTR_REGEXP,
398 | function(match, name, doubleQuotedValue, singleQuotedValue, unquotedValue) {
399 | var value = doubleQuotedValue
400 | || singleQuotedValue
401 | || unquotedValue
402 | || '';
403 |
404 | attrs[name] = decodeEntities(value);
405 | });
406 | if (handler.start) handler.start(tagName, attrs, unary);
407 | }
408 |
409 | function parseEndTag(tag, tagName) {
410 | var pos = 0, i;
411 | tagName = angular.lowercase(tagName);
412 | if (tagName)
413 | // Find the closest opened tag of the same type
414 | for (pos = stack.length - 1; pos >= 0; pos--)
415 | if (stack[pos] == tagName)
416 | break;
417 |
418 | if (pos >= 0) {
419 | // Close all the open elements, up the stack
420 | for (i = stack.length - 1; i >= pos; i--)
421 | if (handler.end) handler.end(stack[i]);
422 |
423 | // Remove the open elements from the stack
424 | stack.length = pos;
425 | }
426 | }
427 | }
428 |
429 | var hiddenPre=document.createElement("pre");
430 | /**
431 | * decodes all entities into regular string
432 | * @param value
433 | * @returns {string} A string with decoded entities.
434 | */
435 | function decodeEntities(value) {
436 | if (!value) { return ''; }
437 |
438 | hiddenPre.innerHTML = value.replace(//g, '>');
464 | }
465 |
466 | /**
467 | * create an HTML/XML writer which writes to buffer
468 | * @param {Array} buf use buf.jain('') to get out sanitized html string
469 | * @returns {object} in the form of {
470 | * start: function(tag, attrs, unary) {},
471 | * end: function(tag) {},
472 | * chars: function(text) {},
473 | * comment: function(text) {}
474 | * }
475 | */
476 | function htmlSanitizeWriter(buf, uriValidator) {
477 | var ignore = false;
478 | var out = angular.bind(buf, buf.push);
479 | return {
480 | start: function(tag, attrs, unary) {
481 | tag = angular.lowercase(tag);
482 | if (!ignore && specialElements[tag]) {
483 | ignore = tag;
484 | }
485 | if (!ignore && validElements[tag] === true) {
486 | out('<');
487 | out(tag);
488 | angular.forEach(attrs, function(value, key) {
489 | var lkey=angular.lowercase(key);
490 | var isImage = (tag === 'img' && lkey === 'src') || (lkey === 'background');
491 | if (validAttrs[lkey] === true &&
492 | (uriAttrs[lkey] !== true || uriValidator(value, isImage))) {
493 | out(' ');
494 | out(key);
495 | out('="');
496 | out(encodeEntities(value));
497 | out('"');
498 | }
499 | });
500 | out(unary ? '/>' : '>');
501 | }
502 | },
503 | end: function(tag) {
504 | tag = angular.lowercase(tag);
505 | if (!ignore && validElements[tag] === true) {
506 | out('');
507 | out(tag);
508 | out('>');
509 | }
510 | if (tag == ignore) {
511 | ignore = false;
512 | }
513 | },
514 | chars: function(chars) {
515 | if (!ignore) {
516 | out(encodeEntities(chars));
517 | }
518 | }
519 | };
520 | }
521 |
522 |
523 | // define ngSanitize module and register $sanitize service
524 | angular.module('ngSanitize', []).provider('$sanitize', $SanitizeProvider);
525 |
526 | /* global sanitizeText: false */
527 |
528 | /**
529 | * @ngdoc filter
530 | * @name linky
531 | * @kind function
532 | *
533 | * @description
534 | * Finds links in text input and turns them into html links. Supports http/https/ftp/mailto and
535 | * plain email address links.
536 | *
537 | * Requires the {@link ngSanitize `ngSanitize`} module to be installed.
538 | *
539 | * @param {string} text Input text.
540 | * @param {string} target Window (_blank|_self|_parent|_top) or named frame to open links in.
541 | * @returns {string} Html-linkified text.
542 | *
543 | * @usage
544 |
545 | *
546 | * @example
547 |
548 |
549 |
561 |
562 | Snippet:
563 |
564 |
565 | Filter |
566 | Source |
567 | Rendered |
568 |
569 |
570 | linky filter |
571 |
572 | <div ng-bind-html="snippet | linky"> </div>
573 | |
574 |
575 |
576 | |
577 |
578 |
579 | linky target |
580 |
581 | <div ng-bind-html="snippetWithTarget | linky:'_blank'"> </div>
582 | |
583 |
584 |
585 | |
586 |
587 |
588 | no filter |
589 | <div ng-bind="snippet"> </div> |
590 | |
591 |
592 |
593 |
594 |
595 | it('should linkify the snippet with urls', function() {
596 | expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
597 | toBe('Pretty text with some links: http://angularjs.org/, us@somewhere.org, ' +
598 | 'another@somewhere.org, and one more: ftp://127.0.0.1/.');
599 | expect(element.all(by.css('#linky-filter a')).count()).toEqual(4);
600 | });
601 |
602 | it('should not linkify snippet without the linky filter', function() {
603 | expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText()).
604 | toBe('Pretty text with some links: http://angularjs.org/, mailto:us@somewhere.org, ' +
605 | 'another@somewhere.org, and one more: ftp://127.0.0.1/.');
606 | expect(element.all(by.css('#escaped-html a')).count()).toEqual(0);
607 | });
608 |
609 | it('should update', function() {
610 | element(by.model('snippet')).clear();
611 | element(by.model('snippet')).sendKeys('new http://link.');
612 | expect(element(by.id('linky-filter')).element(by.binding('snippet | linky')).getText()).
613 | toBe('new http://link.');
614 | expect(element.all(by.css('#linky-filter a')).count()).toEqual(1);
615 | expect(element(by.id('escaped-html')).element(by.binding('snippet')).getText())
616 | .toBe('new http://link.');
617 | });
618 |
619 | it('should work with the target property', function() {
620 | expect(element(by.id('linky-target')).
621 | element(by.binding("snippetWithTarget | linky:'_blank'")).getText()).
622 | toBe('http://angularjs.org/');
623 | expect(element(by.css('#linky-target a')).getAttribute('target')).toEqual('_blank');
624 | });
625 |
626 |
627 | */
628 | angular.module('ngSanitize').filter('linky', ['$sanitize', function($sanitize) {
629 | var LINKY_URL_REGEXP =
630 | /((ftp|https?):\/\/|(www\.)|(mailto:)?[A-Za-z0-9._%+-]+@)\S*[^\s.;,(){}<>"”’]/,
631 | MAILTO_REGEXP = /^mailto:/;
632 |
633 | return function(text, target) {
634 | if (!text) return text;
635 | var match;
636 | var raw = text;
637 | var html = [];
638 | var url;
639 | var i;
640 | while ((match = raw.match(LINKY_URL_REGEXP))) {
641 | // We can not end in these as they are sometimes found at the end of the sentence
642 | url = match[0];
643 | // if we did not match ftp/http/www/mailto then assume mailto
644 | if (!match[2] && !match[4]) {
645 | url = (match[3] ? 'http://' : 'mailto:') + url;
646 | }
647 | i = match.index;
648 | addText(raw.substr(0, i));
649 | addLink(url, match[0].replace(MAILTO_REGEXP, ''));
650 | raw = raw.substring(i + match[0].length);
651 | }
652 | addText(raw);
653 | return $sanitize(html.join(''));
654 |
655 | function addText(text) {
656 | if (!text) {
657 | return;
658 | }
659 | html.push(sanitizeText(text));
660 | }
661 |
662 | function addLink(url, text) {
663 | html.push('
');
672 | addText(text);
673 | html.push('');
674 | }
675 | };
676 | }]);
677 |
678 |
679 | })(window, window.angular);
680 |
--------------------------------------------------------------------------------
/public/js/vendor/moment.min.js:
--------------------------------------------------------------------------------
1 | //! moment.js
2 | //! version : 2.4.0
3 | //! authors : Tim Wood, Iskren Chernev, Moment.js contributors
4 | //! license : MIT
5 | //! momentjs.com
6 | (function(a){function b(a,b){return function(c){return i(a.call(this,c),b)}}function c(a,b){return function(c){return this.lang().ordinal(a.call(this,c),b)}}function d(){}function e(a){u(a),g(this,a)}function f(a){var b=o(a),c=b.year||0,d=b.month||0,e=b.week||0,f=b.day||0,g=b.hour||0,h=b.minute||0,i=b.second||0,j=b.millisecond||0;this._input=a,this._milliseconds=+j+1e3*i+6e4*h+36e5*g,this._days=+f+7*e,this._months=+d+12*c,this._data={},this._bubble()}function g(a,b){for(var c in b)b.hasOwnProperty(c)&&(a[c]=b[c]);return b.hasOwnProperty("toString")&&(a.toString=b.toString),b.hasOwnProperty("valueOf")&&(a.valueOf=b.valueOf),a}function h(a){return 0>a?Math.ceil(a):Math.floor(a)}function i(a,b){for(var c=a+"";c.length
d;d++)(c&&a[d]!==b[d]||!c&&q(a[d])!==q(b[d]))&&g++;return g+f}function n(a){if(a){var b=a.toLowerCase().replace(/(.)s$/,"$1");a=Kb[a]||Lb[b]||b}return a}function o(a){var b,c,d={};for(c in a)a.hasOwnProperty(c)&&(b=n(c),b&&(d[b]=a[c]));return d}function p(b){var c,d;if(0===b.indexOf("week"))c=7,d="day";else{if(0!==b.indexOf("month"))return;c=12,d="month"}bb[b]=function(e,f){var g,h,i=bb.fn._lang[b],j=[];if("number"==typeof e&&(f=e,e=a),h=function(a){var b=bb().utc().set(d,a);return i.call(bb.fn._lang,b,e||"")},null!=f)return h(f);for(g=0;c>g;g++)j.push(h(g));return j}}function q(a){var b=+a,c=0;return 0!==b&&isFinite(b)&&(c=b>=0?Math.floor(b):Math.ceil(b)),c}function r(a,b){return new Date(Date.UTC(a,b+1,0)).getUTCDate()}function s(a){return t(a)?366:365}function t(a){return 0===a%4&&0!==a%100||0===a%400}function u(a){var b;a._a&&-2===a._pf.overflow&&(b=a._a[gb]<0||a._a[gb]>11?gb:a._a[hb]<1||a._a[hb]>r(a._a[fb],a._a[gb])?hb:a._a[ib]<0||a._a[ib]>23?ib:a._a[jb]<0||a._a[jb]>59?jb:a._a[kb]<0||a._a[kb]>59?kb:a._a[lb]<0||a._a[lb]>999?lb:-1,a._pf._overflowDayOfYear&&(fb>b||b>hb)&&(b=hb),a._pf.overflow=b)}function v(a){a._pf={empty:!1,unusedTokens:[],unusedInput:[],overflow:-2,charsLeftOver:0,nullInput:!1,invalidMonth:null,invalidFormat:!1,userInvalidated:!1,iso:!1}}function w(a){return null==a._isValid&&(a._isValid=!isNaN(a._d.getTime())&&a._pf.overflow<0&&!a._pf.empty&&!a._pf.invalidMonth&&!a._pf.nullInput&&!a._pf.invalidFormat&&!a._pf.userInvalidated,a._strict&&(a._isValid=a._isValid&&0===a._pf.charsLeftOver&&0===a._pf.unusedTokens.length)),a._isValid}function x(a){return a?a.toLowerCase().replace("_","-"):a}function y(a,b){return b.abbr=a,mb[a]||(mb[a]=new d),mb[a].set(b),mb[a]}function z(a){delete mb[a]}function A(a){var b,c,d,e,f=0,g=function(a){if(!mb[a]&&nb)try{require("./lang/"+a)}catch(b){}return mb[a]};if(!a)return bb.fn._lang;if(!k(a)){if(c=g(a))return c;a=[a]}for(;f0;){if(c=g(e.slice(0,b).join("-")))return c;if(d&&d.length>=b&&m(e,d,!0)>=b-1)break;b--}f++}return bb.fn._lang}function B(a){return a.match(/\[[\s\S]/)?a.replace(/^\[|\]$/g,""):a.replace(/\\/g,"")}function C(a){var b,c,d=a.match(rb);for(b=0,c=d.length;c>b;b++)d[b]=Pb[d[b]]?Pb[d[b]]:B(d[b]);return function(e){var f="";for(b=0;c>b;b++)f+=d[b]instanceof Function?d[b].call(e,a):d[b];return f}}function D(a,b){return a.isValid()?(b=E(b,a.lang()),Mb[b]||(Mb[b]=C(b)),Mb[b](a)):a.lang().invalidDate()}function E(a,b){function c(a){return b.longDateFormat(a)||a}var d=5;for(sb.lastIndex=0;d>=0&&sb.test(a);)a=a.replace(sb,c),sb.lastIndex=0,d-=1;return a}function F(a,b){var c;switch(a){case"DDDD":return vb;case"YYYY":case"GGGG":case"gggg":return wb;case"YYYYY":case"GGGGG":case"ggggg":return xb;case"S":case"SS":case"SSS":case"DDD":return ub;case"MMM":case"MMMM":case"dd":case"ddd":case"dddd":return zb;case"a":case"A":return A(b._l)._meridiemParse;case"X":return Cb;case"Z":case"ZZ":return Ab;case"T":return Bb;case"SSSS":return yb;case"MM":case"DD":case"YY":case"GG":case"gg":case"HH":case"hh":case"mm":case"ss":case"M":case"D":case"d":case"H":case"h":case"m":case"s":case"w":case"ww":case"W":case"WW":case"e":case"E":return tb;default:return c=new RegExp(N(M(a.replace("\\","")),"i"))}}function G(a){var b=(Ab.exec(a)||[])[0],c=(b+"").match(Hb)||["-",0,0],d=+(60*c[1])+q(c[2]);return"+"===c[0]?-d:d}function H(a,b,c){var d,e=c._a;switch(a){case"M":case"MM":null!=b&&(e[gb]=q(b)-1);break;case"MMM":case"MMMM":d=A(c._l).monthsParse(b),null!=d?e[gb]=d:c._pf.invalidMonth=b;break;case"D":case"DD":null!=b&&(e[hb]=q(b));break;case"DDD":case"DDDD":null!=b&&(c._dayOfYear=q(b));break;case"YY":e[fb]=q(b)+(q(b)>68?1900:2e3);break;case"YYYY":case"YYYYY":e[fb]=q(b);break;case"a":case"A":c._isPm=A(c._l).isPM(b);break;case"H":case"HH":case"h":case"hh":e[ib]=q(b);break;case"m":case"mm":e[jb]=q(b);break;case"s":case"ss":e[kb]=q(b);break;case"S":case"SS":case"SSS":case"SSSS":e[lb]=q(1e3*("0."+b));break;case"X":c._d=new Date(1e3*parseFloat(b));break;case"Z":case"ZZ":c._useUTC=!0,c._tzm=G(b);break;case"w":case"ww":case"W":case"WW":case"d":case"dd":case"ddd":case"dddd":case"e":case"E":a=a.substr(0,1);case"gg":case"gggg":case"GG":case"GGGG":case"GGGGG":a=a.substr(0,2),b&&(c._w=c._w||{},c._w[a]=b)}}function I(a){var b,c,d,e,f,g,h,i,j,k,l=[];if(!a._d){for(d=K(a),a._w&&null==a._a[hb]&&null==a._a[gb]&&(f=function(b){return b?b.length<3?parseInt(b,10)>68?"19"+b:"20"+b:b:null==a._a[fb]?bb().weekYear():a._a[fb]},g=a._w,null!=g.GG||null!=g.W||null!=g.E?h=X(f(g.GG),g.W||1,g.E,4,1):(i=A(a._l),j=null!=g.d?T(g.d,i):null!=g.e?parseInt(g.e,10)+i._week.dow:0,k=parseInt(g.w,10)||1,null!=g.d&&js(e)&&(a._pf._overflowDayOfYear=!0),c=S(e,0,a._dayOfYear),a._a[gb]=c.getUTCMonth(),a._a[hb]=c.getUTCDate()),b=0;3>b&&null==a._a[b];++b)a._a[b]=l[b]=d[b];for(;7>b;b++)a._a[b]=l[b]=null==a._a[b]?2===b?1:0:a._a[b];l[ib]+=q((a._tzm||0)/60),l[jb]+=q((a._tzm||0)%60),a._d=(a._useUTC?S:R).apply(null,l)}}function J(a){var b;a._d||(b=o(a._i),a._a=[b.year,b.month,b.day,b.hour,b.minute,b.second,b.millisecond],I(a))}function K(a){var b=new Date;return a._useUTC?[b.getUTCFullYear(),b.getUTCMonth(),b.getUTCDate()]:[b.getFullYear(),b.getMonth(),b.getDate()]}function L(a){a._a=[],a._pf.empty=!0;var b,c,d,e,f,g=A(a._l),h=""+a._i,i=h.length,j=0;for(d=E(a._f,g).match(rb)||[],b=0;b0&&a._pf.unusedInput.push(f),h=h.slice(h.indexOf(c)+c.length),j+=c.length),Pb[e]?(c?a._pf.empty=!1:a._pf.unusedTokens.push(e),H(e,c,a)):a._strict&&!c&&a._pf.unusedTokens.push(e);a._pf.charsLeftOver=i-j,h.length>0&&a._pf.unusedInput.push(h),a._isPm&&a._a[ib]<12&&(a._a[ib]+=12),a._isPm===!1&&12===a._a[ib]&&(a._a[ib]=0),I(a),u(a)}function M(a){return a.replace(/\\(\[)|\\(\])|\[([^\]\[]*)\]|\\(.)/g,function(a,b,c,d,e){return b||c||d||e})}function N(a){return a.replace(/[-\/\\^$*+?.()|[\]{}]/g,"\\$&")}function O(a){var b,c,d,e,f;if(0===a._f.length)return a._pf.invalidFormat=!0,a._d=new Date(0/0),void 0;for(e=0;ef)&&(d=f,c=b));g(a,c||b)}function P(a){var b,c=a._i,d=Db.exec(c);if(d){for(a._pf.iso=!0,b=4;b>0;b--)if(d[b]){a._f=Fb[b-1]+(d[6]||" ");break}for(b=0;4>b;b++)if(Gb[b][1].exec(c)){a._f+=Gb[b][0];break}Ab.exec(c)&&(a._f+="Z"),L(a)}else a._d=new Date(c)}function Q(b){var c=b._i,d=ob.exec(c);c===a?b._d=new Date:d?b._d=new Date(+d[1]):"string"==typeof c?P(b):k(c)?(b._a=c.slice(0),I(b)):l(c)?b._d=new Date(+c):"object"==typeof c?J(b):b._d=new Date(c)}function R(a,b,c,d,e,f,g){var h=new Date(a,b,c,d,e,f,g);return 1970>a&&h.setFullYear(a),h}function S(a){var b=new Date(Date.UTC.apply(null,arguments));return 1970>a&&b.setUTCFullYear(a),b}function T(a,b){if("string"==typeof a)if(isNaN(a)){if(a=b.weekdaysParse(a),"number"!=typeof a)return null}else a=parseInt(a,10);return a}function U(a,b,c,d,e){return e.relativeTime(b||1,!!c,a,d)}function V(a,b,c){var d=eb(Math.abs(a)/1e3),e=eb(d/60),f=eb(e/60),g=eb(f/24),h=eb(g/365),i=45>d&&["s",d]||1===e&&["m"]||45>e&&["mm",e]||1===f&&["h"]||22>f&&["hh",f]||1===g&&["d"]||25>=g&&["dd",g]||45>=g&&["M"]||345>g&&["MM",eb(g/30)]||1===h&&["y"]||["yy",h];return i[2]=b,i[3]=a>0,i[4]=c,U.apply({},i)}function W(a,b,c){var d,e=c-b,f=c-a.day();return f>e&&(f-=7),e-7>f&&(f+=7),d=bb(a).add("d",f),{week:Math.ceil(d.dayOfYear()/7),year:d.year()}}function X(a,b,c,d,e){var f,g,h=new Date(Date.UTC(a,0)).getUTCDay();return c=null!=c?c:e,f=e-h+(h>d?7:0),g=7*(b-1)+(c-e)+f+1,{year:g>0?a:a-1,dayOfYear:g>0?g:s(a-1)+g}}function Y(a){var b=a._i,c=a._f;return"undefined"==typeof a._pf&&v(a),null===b?bb.invalid({nullInput:!0}):("string"==typeof b&&(a._i=b=A().preparse(b)),bb.isMoment(b)?(a=g({},b),a._d=new Date(+b._d)):c?k(c)?O(a):L(a):Q(a),new e(a))}function Z(a,b){bb.fn[a]=bb.fn[a+"s"]=function(a){var c=this._isUTC?"UTC":"";return null!=a?(this._d["set"+c+b](a),bb.updateOffset(this),this):this._d["get"+c+b]()}}function $(a){bb.duration.fn[a]=function(){return this._data[a]}}function _(a,b){bb.duration.fn["as"+a]=function(){return+this/b}}function ab(a){var b=!1,c=bb;"undefined"==typeof ender&&(this.moment=a?function(){return!b&&console&&console.warn&&(b=!0,console.warn("Accessing Moment through the global scope is deprecated, and will be removed in an upcoming release.")),c.apply(null,arguments)}:bb)}for(var bb,cb,db="2.4.0",eb=Math.round,fb=0,gb=1,hb=2,ib=3,jb=4,kb=5,lb=6,mb={},nb="undefined"!=typeof module&&module.exports,ob=/^\/?Date\((\-?\d+)/i,pb=/(\-)?(?:(\d*)\.)?(\d+)\:(\d+)(?:\:(\d+)\.?(\d{3})?)?/,qb=/^(-)?P(?:(?:([0-9,.]*)Y)?(?:([0-9,.]*)M)?(?:([0-9,.]*)D)?(?:T(?:([0-9,.]*)H)?(?:([0-9,.]*)M)?(?:([0-9,.]*)S)?)?|([0-9,.]*)W)$/,rb=/(\[[^\[]*\])|(\\)?(Mo|MM?M?M?|Do|DDDo|DD?D?D?|ddd?d?|do?|w[o|w]?|W[o|W]?|YYYYY|YYYY|YY|gg(ggg?)?|GG(GGG?)?|e|E|a|A|hh?|HH?|mm?|ss?|S{1,4}|X|zz?|ZZ?|.)/g,sb=/(\[[^\[]*\])|(\\)?(LT|LL?L?L?|l{1,4})/g,tb=/\d\d?/,ub=/\d{1,3}/,vb=/\d{3}/,wb=/\d{1,4}/,xb=/[+\-]?\d{1,6}/,yb=/\d+/,zb=/[0-9]*['a-z\u00A0-\u05FF\u0700-\uD7FF\uF900-\uFDCF\uFDF0-\uFFEF]+|[\u0600-\u06FF\/]+(\s*?[\u0600-\u06FF]+){1,2}/i,Ab=/Z|[\+\-]\d\d:?\d\d/i,Bb=/T/i,Cb=/[\+\-]?\d+(\.\d{1,3})?/,Db=/^\s*\d{4}-(?:(\d\d-\d\d)|(W\d\d$)|(W\d\d-\d)|(\d\d\d))((T| )(\d\d(:\d\d(:\d\d(\.\d+)?)?)?)?([\+\-]\d\d:?\d\d|Z)?)?$/,Eb="YYYY-MM-DDTHH:mm:ssZ",Fb=["YYYY-MM-DD","GGGG-[W]WW","GGGG-[W]WW-E","YYYY-DDD"],Gb=[["HH:mm:ss.SSSS",/(T| )\d\d:\d\d:\d\d\.\d{1,3}/],["HH:mm:ss",/(T| )\d\d:\d\d:\d\d/],["HH:mm",/(T| )\d\d:\d\d/],["HH",/(T| )\d\d/]],Hb=/([\+\-]|\d\d)/gi,Ib="Date|Hours|Minutes|Seconds|Milliseconds".split("|"),Jb={Milliseconds:1,Seconds:1e3,Minutes:6e4,Hours:36e5,Days:864e5,Months:2592e6,Years:31536e6},Kb={ms:"millisecond",s:"second",m:"minute",h:"hour",d:"day",D:"date",w:"week",W:"isoWeek",M:"month",y:"year",DDD:"dayOfYear",e:"weekday",E:"isoWeekday",gg:"weekYear",GG:"isoWeekYear"},Lb={dayofyear:"dayOfYear",isoweekday:"isoWeekday",isoweek:"isoWeek",weekyear:"weekYear",isoweekyear:"isoWeekYear"},Mb={},Nb="DDD w W M D d".split(" "),Ob="M D H h m s w W".split(" "),Pb={M:function(){return this.month()+1},MMM:function(a){return this.lang().monthsShort(this,a)},MMMM:function(a){return this.lang().months(this,a)},D:function(){return this.date()},DDD:function(){return this.dayOfYear()},d:function(){return this.day()},dd:function(a){return this.lang().weekdaysMin(this,a)},ddd:function(a){return this.lang().weekdaysShort(this,a)},dddd:function(a){return this.lang().weekdays(this,a)},w:function(){return this.week()},W:function(){return this.isoWeek()},YY:function(){return i(this.year()%100,2)},YYYY:function(){return i(this.year(),4)},YYYYY:function(){return i(this.year(),5)},gg:function(){return i(this.weekYear()%100,2)},gggg:function(){return this.weekYear()},ggggg:function(){return i(this.weekYear(),5)},GG:function(){return i(this.isoWeekYear()%100,2)},GGGG:function(){return this.isoWeekYear()},GGGGG:function(){return i(this.isoWeekYear(),5)},e:function(){return this.weekday()},E:function(){return this.isoWeekday()},a:function(){return this.lang().meridiem(this.hours(),this.minutes(),!0)},A:function(){return this.lang().meridiem(this.hours(),this.minutes(),!1)},H:function(){return this.hours()},h:function(){return this.hours()%12||12},m:function(){return this.minutes()},s:function(){return this.seconds()},S:function(){return q(this.milliseconds()/100)},SS:function(){return i(q(this.milliseconds()/10),2)},SSS:function(){return i(this.milliseconds(),3)},SSSS:function(){return i(this.milliseconds(),3)},Z:function(){var a=-this.zone(),b="+";return 0>a&&(a=-a,b="-"),b+i(q(a/60),2)+":"+i(q(a)%60,2)},ZZ:function(){var a=-this.zone(),b="+";return 0>a&&(a=-a,b="-"),b+i(q(10*a/6),4)},z:function(){return this.zoneAbbr()},zz:function(){return this.zoneName()},X:function(){return this.unix()}},Qb=["months","monthsShort","weekdays","weekdaysShort","weekdaysMin"];Nb.length;)cb=Nb.pop(),Pb[cb+"o"]=c(Pb[cb],cb);for(;Ob.length;)cb=Ob.pop(),Pb[cb+cb]=b(Pb[cb],2);for(Pb.DDDD=b(Pb.DDD,3),g(d.prototype,{set:function(a){var b,c;for(c in a)b=a[c],"function"==typeof b?this[c]=b:this["_"+c]=b},_months:"January_February_March_April_May_June_July_August_September_October_November_December".split("_"),months:function(a){return this._months[a.month()]},_monthsShort:"Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec".split("_"),monthsShort:function(a){return this._monthsShort[a.month()]},monthsParse:function(a){var b,c,d;for(this._monthsParse||(this._monthsParse=[]),b=0;12>b;b++)if(this._monthsParse[b]||(c=bb.utc([2e3,b]),d="^"+this.months(c,"")+"|^"+this.monthsShort(c,""),this._monthsParse[b]=new RegExp(d.replace(".",""),"i")),this._monthsParse[b].test(a))return b},_weekdays:"Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday".split("_"),weekdays:function(a){return this._weekdays[a.day()]},_weekdaysShort:"Sun_Mon_Tue_Wed_Thu_Fri_Sat".split("_"),weekdaysShort:function(a){return this._weekdaysShort[a.day()]},_weekdaysMin:"Su_Mo_Tu_We_Th_Fr_Sa".split("_"),weekdaysMin:function(a){return this._weekdaysMin[a.day()]},weekdaysParse:function(a){var b,c,d;for(this._weekdaysParse||(this._weekdaysParse=[]),b=0;7>b;b++)if(this._weekdaysParse[b]||(c=bb([2e3,1]).day(b),d="^"+this.weekdays(c,"")+"|^"+this.weekdaysShort(c,"")+"|^"+this.weekdaysMin(c,""),this._weekdaysParse[b]=new RegExp(d.replace(".",""),"i")),this._weekdaysParse[b].test(a))return b},_longDateFormat:{LT:"h:mm A",L:"MM/DD/YYYY",LL:"MMMM D YYYY",LLL:"MMMM D YYYY LT",LLLL:"dddd, MMMM D YYYY LT"},longDateFormat:function(a){var b=this._longDateFormat[a];return!b&&this._longDateFormat[a.toUpperCase()]&&(b=this._longDateFormat[a.toUpperCase()].replace(/MMMM|MM|DD|dddd/g,function(a){return a.slice(1)}),this._longDateFormat[a]=b),b},isPM:function(a){return"p"===(a+"").toLowerCase().charAt(0)},_meridiemParse:/[ap]\.?m?\.?/i,meridiem:function(a,b,c){return a>11?c?"pm":"PM":c?"am":"AM"},_calendar:{sameDay:"[Today at] LT",nextDay:"[Tomorrow at] LT",nextWeek:"dddd [at] LT",lastDay:"[Yesterday at] LT",lastWeek:"[Last] dddd [at] LT",sameElse:"L"},calendar:function(a,b){var c=this._calendar[a];return"function"==typeof c?c.apply(b):c},_relativeTime:{future:"in %s",past:"%s ago",s:"a few seconds",m:"a minute",mm:"%d minutes",h:"an hour",hh:"%d hours",d:"a day",dd:"%d days",M:"a month",MM:"%d months",y:"a year",yy:"%d years"},relativeTime:function(a,b,c,d){var e=this._relativeTime[c];return"function"==typeof e?e(a,b,c,d):e.replace(/%d/i,a)},pastFuture:function(a,b){var c=this._relativeTime[a>0?"future":"past"];return"function"==typeof c?c(b):c.replace(/%s/i,b)},ordinal:function(a){return this._ordinal.replace("%d",a)},_ordinal:"%d",preparse:function(a){return a},postformat:function(a){return a},week:function(a){return W(a,this._week.dow,this._week.doy).week},_week:{dow:0,doy:6},_invalidDate:"Invalid date",invalidDate:function(){return this._invalidDate}}),bb=function(b,c,d,e){return"boolean"==typeof d&&(e=d,d=a),Y({_i:b,_f:c,_l:d,_strict:e,_isUTC:!1})},bb.utc=function(b,c,d,e){var f;return"boolean"==typeof d&&(e=d,d=a),f=Y({_useUTC:!0,_isUTC:!0,_l:d,_i:b,_f:c,_strict:e}).utc()},bb.unix=function(a){return bb(1e3*a)},bb.duration=function(a,b){var c,d,e,g=bb.isDuration(a),h="number"==typeof a,i=g?a._input:h?{}:a,j=null;return h?b?i[b]=a:i.milliseconds=a:(j=pb.exec(a))?(c="-"===j[1]?-1:1,i={y:0,d:q(j[hb])*c,h:q(j[ib])*c,m:q(j[jb])*c,s:q(j[kb])*c,ms:q(j[lb])*c}):(j=qb.exec(a))&&(c="-"===j[1]?-1:1,e=function(a){var b=a&&parseFloat(a.replace(",","."));return(isNaN(b)?0:b)*c},i={y:e(j[2]),M:e(j[3]),d:e(j[4]),h:e(j[5]),m:e(j[6]),s:e(j[7]),w:e(j[8])}),d=new f(i),g&&a.hasOwnProperty("_lang")&&(d._lang=a._lang),d},bb.version=db,bb.defaultFormat=Eb,bb.updateOffset=function(){},bb.lang=function(a,b){var c;return a?(b?y(x(a),b):null===b?(z(a),a="en"):mb[a]||A(a),c=bb.duration.fn._lang=bb.fn._lang=A(a),c._abbr):bb.fn._lang._abbr},bb.langData=function(a){return a&&a._lang&&a._lang._abbr&&(a=a._lang._abbr),A(a)},bb.isMoment=function(a){return a instanceof e},bb.isDuration=function(a){return a instanceof f},cb=Qb.length-1;cb>=0;--cb)p(Qb[cb]);for(bb.normalizeUnits=function(a){return n(a)},bb.invalid=function(a){var b=bb.utc(0/0);return null!=a?g(b._pf,a):b._pf.userInvalidated=!0,b},bb.parseZone=function(a){return bb(a).parseZone()},g(bb.fn=e.prototype,{clone:function(){return bb(this)},valueOf:function(){return+this._d+6e4*(this._offset||0)},unix:function(){return Math.floor(+this/1e3)},toString:function(){return this.clone().lang("en").format("ddd MMM DD YYYY HH:mm:ss [GMT]ZZ")},toDate:function(){return this._offset?new Date(+this):this._d},toISOString:function(){return D(bb(this).utc(),"YYYY-MM-DD[T]HH:mm:ss.SSS[Z]")},toArray:function(){var a=this;return[a.year(),a.month(),a.date(),a.hours(),a.minutes(),a.seconds(),a.milliseconds()]},isValid:function(){return w(this)},isDSTShifted:function(){return this._a?this.isValid()&&m(this._a,(this._isUTC?bb.utc(this._a):bb(this._a)).toArray())>0:!1},parsingFlags:function(){return g({},this._pf)},invalidAt:function(){return this._pf.overflow},utc:function(){return this.zone(0)},local:function(){return this.zone(0),this._isUTC=!1,this},format:function(a){var b=D(this,a||bb.defaultFormat);return this.lang().postformat(b)},add:function(a,b){var c;return c="string"==typeof a?bb.duration(+b,a):bb.duration(a,b),j(this,c,1),this},subtract:function(a,b){var c;return c="string"==typeof a?bb.duration(+b,a):bb.duration(a,b),j(this,c,-1),this},diff:function(a,b,c){var d,e,f=this._isUTC?bb(a).zone(this._offset||0):bb(a).local(),g=6e4*(this.zone()-f.zone());return b=n(b),"year"===b||"month"===b?(d=432e5*(this.daysInMonth()+f.daysInMonth()),e=12*(this.year()-f.year())+(this.month()-f.month()),e+=(this-bb(this).startOf("month")-(f-bb(f).startOf("month")))/d,e-=6e4*(this.zone()-bb(this).startOf("month").zone()-(f.zone()-bb(f).startOf("month").zone()))/d,"year"===b&&(e/=12)):(d=this-f,e="second"===b?d/1e3:"minute"===b?d/6e4:"hour"===b?d/36e5:"day"===b?(d-g)/864e5:"week"===b?(d-g)/6048e5:d),c?e:h(e)},from:function(a,b){return bb.duration(this.diff(a)).lang(this.lang()._abbr).humanize(!b)},fromNow:function(a){return this.from(bb(),a)},calendar:function(){var a=this.diff(bb().zone(this.zone()).startOf("day"),"days",!0),b=-6>a?"sameElse":-1>a?"lastWeek":0>a?"lastDay":1>a?"sameDay":2>a?"nextDay":7>a?"nextWeek":"sameElse";return this.format(this.lang().calendar(b,this))},isLeapYear:function(){return t(this.year())},isDST:function(){return this.zone()+bb(a).startOf(b)},isBefore:function(a,b){return b="undefined"!=typeof b?b:"millisecond",+this.clone().startOf(b)<+bb(a).startOf(b)},isSame:function(a,b){return b="undefined"!=typeof b?b:"millisecond",+this.clone().startOf(b)===+bb(a).startOf(b)},min:function(a){return a=bb.apply(null,arguments),this>a?this:a},max:function(a){return a=bb.apply(null,arguments),a>this?this:a},zone:function(a){var b=this._offset||0;return null==a?this._isUTC?b:this._d.getTimezoneOffset():("string"==typeof a&&(a=G(a)),Math.abs(a)<16&&(a=60*a),this._offset=a,this._isUTC=!0,b!==a&&j(this,bb.duration(b-a,"m"),1,!0),this)},zoneAbbr:function(){return this._isUTC?"UTC":""},zoneName:function(){return this._isUTC?"Coordinated Universal Time":""},parseZone:function(){return"string"==typeof this._i&&this.zone(this._i),this},hasAlignedHourOffset:function(a){return a=a?bb(a).zone():0,0===(this.zone()-a)%60},daysInMonth:function(){return r(this.year(),this.month())},dayOfYear:function(a){var b=eb((bb(this).startOf("day")-bb(this).startOf("year"))/864e5)+1;return null==a?b:this.add("d",a-b)},weekYear:function(a){var b=W(this,this.lang()._week.dow,this.lang()._week.doy).year;return null==a?b:this.add("y",a-b)},isoWeekYear:function(a){var b=W(this,1,4).year;return null==a?b:this.add("y",a-b)},week:function(a){var b=this.lang().week(this);return null==a?b:this.add("d",7*(a-b))},isoWeek:function(a){var b=W(this,1,4).week;return null==a?b:this.add("d",7*(a-b))},weekday:function(a){var b=(this.day()+7-this.lang()._week.dow)%7;return null==a?b:this.add("d",a-b)},isoWeekday:function(a){return null==a?this.day()||7:this.day(this.day()%7?a:a-7)},get:function(a){return a=n(a),this[a]()},set:function(a,b){return a=n(a),"function"==typeof this[a]&&this[a](b),this},lang:function(b){return b===a?this._lang:(this._lang=A(b),this)}}),cb=0;cb/g,">").replace(/"/g,""").replace(/'/g,"'");}else{return B;}}function k(B){return B.replace(/</g,"<").replace(/>/g,">").replace(/"/g,'"').replace(/'/g,"'").replace(/&/g,"&");}function w(C,F,D,E){var B=0;for(;B0){if(w(z.arrayAccessFormPaths,D,B,C)){D[B]=[D[B]];}}}function a(G){var E=G.split(/[-T:+Z]/g);var F=new Date(E[0],E[1]-1,E[2]);var D=E[5].split(".");F.setHours(E[3],E[4],D[0]);if(D.length>1){F.setMilliseconds(D[1]);}if(E[6]&&E[7]){var C=E[6]*60+Number(E[7]);var B=/\d\d-\d\d:\d\d$/.test(G)?"-":"+";C=0+(B=="-"?-1*C:C);F.setMinutes(F.getMinutes()-C-F.getTimezoneOffset());}else{if(G.indexOf("Z",G.length-1)!==-1){F=new Date(Date.UTC(F.getFullYear(),F.getMonth(),F.getDate(),F.getHours(),F.getMinutes(),F.getSeconds(),F.getMilliseconds()));}}return F;}function q(D,B,C){if(z.datetimeAccessFormPaths.length>0){var E=C.split(".#")[0];if(w(z.datetimeAccessFormPaths,D,B,E)){return a(D);}else{return D;}}else{return D;}}function b(E,C,B,D){if(C==h.ELEMENT_NODE&&z.xmlElementsFilter.length>0){return w(z.xmlElementsFilter,E,B,D);}else{return true;}}function A(D,J){if(D.nodeType==h.DOCUMENT_NODE){var K=new Object;var B=D.childNodes;for(var L=0;L1&&K.__text!=null&&z.skipEmptyTextNodesForObj){if((z.stripWhitespaces&&K.__text=="")||(K.__text.trim()=="")){delete K.__text;}}}}}delete K.__cnt;if(z.enableToStringFunc&&(K.__text!=null||K.__cdata!=null)){K.toString=function(){return(this.__text!=null?this.__text:"")+(this.__cdata!=null?this.__cdata:"");};}return K;}else{if(D.nodeType==h.TEXT_NODE||D.nodeType==h.CDATA_SECTION_NODE){return D.nodeValue;}}}}function o(I,F,H,C){var E="<"+((I!=null&&I.__prefix!=null)?(I.__prefix+":"):"")+F;if(H!=null){for(var G=0;G";}else{E+="/>";}return E;}function j(C,B){return""+(C.__prefix!=null?(C.__prefix+":"):"")+B+">";}function v(C,B){return C.indexOf(B,C.length-B.length)!==-1;}function y(C,B){if((z.arrayAccessForm=="property"&&v(B.toString(),("_asArray")))||B.toString().indexOf(z.attributePrefix)==0||B.toString().indexOf("__")==0||(C[B] instanceof Function)){return true;}else{return false;}}function m(D){var C=0;if(D instanceof Object){for(var B in D){if(y(D,B)){continue;}C++;}}return C;}function l(D,B,C){return z.jsonPropertiesFilter.length==0||C==""||w(z.jsonPropertiesFilter,D,B,C);}function c(D){var C=[];if(D instanceof Object){for(var B in D){if(B.toString().indexOf("__")==-1&&B.toString().indexOf(z.attributePrefix)==0){C.push(B);}}}return C;}function g(C){var B="";if(C.__cdata!=null){B+="";}if(C.__text!=null){if(z.escapeMode){B+=s(C.__text);}else{B+=C.__text;}}return B;}function d(C){var B="";if(C instanceof Object){B+=g(C);}else{if(C!=null){if(z.escapeMode){B+=s(C);}else{B+=C;}}}return B;}function p(C,B){if(C===""){return B;}else{return C+"."+B;}}function f(D,G,F,E){var B="";if(D.length==0){B+=o(D,G,F,true);}else{for(var C=0;C0){for(var E in I){if(y(I,E)||(H!=""&&!l(I,E,p(H,E)))){continue;}var D=I[E];var G=c(D);if(D==null||D==undefined){B+=o(D,E,G,true);}else{if(D instanceof Object){if(D instanceof Array){B+=f(D,E,G,H);}else{if(D instanceof Date){B+=o(D,E,G,false);B+=D.toISOString();B+=j(D,E);}else{var C=m(D);if(C>0||D.__text!=null||D.__cdata!=null){B+=o(D,E,G,false);B+=e(D,p(H,E));B+=j(D,E);}else{B+=o(D,E,G,true);}}}}else{B+=o(D,E,G,false);B+=d(D);B+=j(D,E);}}}}B+=d(I);return B;}this.parseXmlString=function(D){var F=window.ActiveXObject||"ActiveXObject" in window;if(D===undefined){return null;}var E;if(window.DOMParser){var G=new window.DOMParser();var B=null;if(!F){try{B=G.parseFromString("INVALID","text/xml").getElementsByTagName("parsererror")[0].namespaceURI;}catch(C){B=null;}}try{E=G.parseFromString(D,"text/xml");if(B!=null&&E.getElementsByTagNameNS(B,"parsererror").length>0){E=null;}}catch(C){E=null;}}else{if(D.indexOf("")==0){D=D.substr(D.indexOf("?>")+2);}E=new ActiveXObject("Microsoft.XMLDOM");E.async="false";E.loadXML(D);}return E;};this.asArray=function(B){if(B===undefined||B==null){return[];}else{if(B instanceof Array){return B;}else{return[B];}}};this.toXmlDateTime=function(B){if(B instanceof Date){return B.toISOString();}else{if(typeof(B)==="number"){return new Date(B).toISOString();}else{return null;}}};this.asDateTime=function(B){if(typeof(B)=="string"){return a(B);}else{return B;}};this.xml2json=function(B){return A(B);};this.xml_str2json=function(B){var C=this.parseXmlString(B);if(C!=null){return this.xml2json(C);}else{return null;}};this.json2xml_str=function(B){return e(B,"");};this.json2xml=function(C){var B=this.json2xml_str(C);return this.parseXmlString(B);};this.getVersion=function(){return t;};};}));
--------------------------------------------------------------------------------
/public/views/environment.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
84 |
85 |
--------------------------------------------------------------------------------
/public/views/environments.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
10 |
17 |
18 |
19 | No environments :(
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/public/views/options.html:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
8 | Connection is defined in route:
9 |
10 |
{{edge.route.name}}
11 |
{{edge.route.schema}}
12 |
13 |
14 |
15 |
{{data.nodes[0].label}} is mentioned in routes:
16 |
17 |
{{edge.route.name}}
18 |
{{edge.route.schema}}
19 |
20 |
21 |
24 |
25 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | ### Overview
2 | **Problem**: Managing hundreds of complex bus routes manually has become impractical\
3 | **Solution**: Automatic route graph generation\
4 | **Status**: Finished, article is published
5 |
6 | ----
7 |
8 | ## Camel-graph
9 | 
10 |
11 | Camel-graph is the viewer for routes in servicemix and camel applications. [Article with intention and result explanation (Russian)](https://habr.com/ru/post/435594/)
12 | ## Requirenment
13 | Viewer work over JMS with help of jolokia, so jolokia is required to be in your application or in servicemix.
14 | ## Example
15 | 
16 | ## Configuration - services.json
17 | ```json
18 | {
19 | "environments": [
20 | {
21 | "name": "dev",
22 | "services": [
23 | {
24 | "name": "smx",
25 | "url": "http://localhost:8181",
26 | "color": "#62aa34",
27 | "authorization": {
28 | "login": "smx",
29 | "pass": "smx"
30 | }
31 | }
32 | ]
33 | }
34 | ]
35 | }
36 |
37 | ```
38 | ## Launch
39 | ```
40 | go build
41 | ./camel-graph -httpPort=8080
42 | ```
43 |
--------------------------------------------------------------------------------
/services.json:
--------------------------------------------------------------------------------
1 | {
2 | "environments": [
3 | {
4 | "name": "dev",
5 | "services": [
6 | {
7 | "name": "smx",
8 | "url": "http://localhost:8181",
9 | "color": "#62aa34",
10 | "authorization": {
11 | "login": "smx",
12 | "pass": "smx"
13 | }
14 | },
15 | {
16 | "name": "spring_service",
17 | "url": "http://localhost:10002",
18 | "color": "#272dc5"
19 | }
20 | ]
21 | }
22 | ]
23 | }
--------------------------------------------------------------------------------