source: SRUAggregator/trunk/src/main/resources/assets/js/main.js @ 6081

Last change on this file since 6081 was 6081, checked in by emanuel.dima@uni-tuebingen.de, 9 years ago
  1. alpha 26: zoom in result, other UI changes
File size: 20.2 KB
Line 
1/** @jsx React.DOM */
2(function() {
3"use strict";
4
5var VERSION = "VERSION 2.0.0.α26";
6var URLROOT = "/Aggregator-testing";
7
8var PT = React.PropTypes;
9
10var ErrorPane = window.MyReact.ErrorPane;
11var AggregatorPage = window.MyAggregator.AggregatorPage;
12
13var Main = React.createClass({displayName: 'Main',
14        componentWillMount: function() {
15                routeFromLocation.bind(this);
16        },
17
18        getInitialState: function () {
19                return {
20                        navbarCollapse: false,
21                        navbarPageFn: this.renderAggregator,
22                        // navbarPageFn: this.renderStatistics,
23                        errorMessages: [],
24                };
25        },
26
27        error: function(errObj) {
28                var err = "";
29                if (typeof errObj === 'string' || errObj instanceof String) {
30                        err = errObj;
31                } else if (typeof errObj === 'object' && errObj.statusText) {
32                        console.log("ERROR: jqXHR = ", errObj);
33                        err = errObj.statusText;
34                } else {
35                        return;
36                }
37
38                var that = this;
39                var errs = this.state.errorMessages.slice();
40                errs.push(err);
41                this.setState({errorMessages: errs});
42
43                setTimeout(function() {
44                        var errs = that.state.errorMessages.slice();
45                        errs.shift();
46                        that.setState({errorMessages: errs});
47                }, 10000);
48        },
49       
50        ajax: function(ajaxObject) {
51                var that = this;
52                if (!ajaxObject.error) {
53                        ajaxObject.error = function(jqXHR, textStatus, error) {
54                                if (jqXHR.readyState === 0) {
55                                        that.error("Network error, please check your internet connection");
56                                } else if (jqXHR.responseText) {
57                                        that.error(jqXHR.responseText + " ("+error+")");
58                                } else  {
59                                        that.error(error + " ("+textStatus+")");
60                                }
61                                console.log("ajax error, jqXHR: ", jqXHR);
62                        };
63                }
64                // console.log("ajax", ajaxObject);
65                jQuery.ajax(ajaxObject);
66        },
67
68        toggleCollapse: function() {
69                this.setState({navbarCollapse: !this.state.navbarCollapse});
70        },
71
72        renderAggregator: function() {
73                return React.createElement(AggregatorPage, {ajax: this.ajax, corpora: this.state.corpora, languageMap: this.state.languageMap});
74        },
75
76        renderHelp: function() {
77                return React.createElement(HelpPage, null);
78        },
79
80        renderAbout: function() {
81                return React.createElement(AboutPage, null);
82        },
83
84        renderStatistics: function() {
85                return React.createElement(StatisticsPage, {ajax: this.ajax});
86        },
87
88        getPageFns: function() { 
89                return {
90                        '': this.renderAggregator,
91                        'help': this.renderHelp,
92                        'about': this.renderAbout,
93                        'stats': this.renderStatistics,
94                };
95        },
96
97        gotoPage: function(doPushHistory, pageFnName) {
98                var pageFn = this.getPageFns()[pageFnName];
99                if (this.state.navbarPageFn !== pageFn) {
100                        if (doPushHistory) {
101                                window.history.pushState({page:pageFnName}, '', URLROOT+"/"+pageFnName);
102                        }
103                        this.setState({navbarPageFn: pageFn});
104                        // console.log("new page: " + document.location + ", name: " + pageFnName);
105                }
106        },
107
108        toAggregator: function(doPushHistory) { this.gotoPage(doPushHistory, ''); },
109        toHelp: function(doPushHistory) { this.gotoPage(doPushHistory, 'help'); },
110        toAbout: function(doPushHistory) { this.gotoPage(doPushHistory, 'about'); },
111        toStatistics: function(doPushHistory) { this.gotoPage(doPushHistory, 'stats'); },
112
113        renderCollapsible: function() {
114                var classname = "navbar-collapse collapse " + (this.state.navbarCollapse?"in":"");
115                return (
116                        React.createElement("div", {className: classname}, 
117                                React.createElement("ul", {className: "nav navbar-nav"}, 
118                                        React.createElement("li", {className: this.state.navbarPageFn === this.renderAggregator ? "active":""}, 
119                                                React.createElement("a", {className: "link", tabIndex: "-1", onClick: this.toAggregator.bind(this, true)}, "Aggregator")
120                                        ), 
121                                        React.createElement("li", {className: this.state.navbarPageFn === this.renderHelp ? "active":""}, 
122                                                React.createElement("a", {className: "link", tabIndex: "-1", onClick: this.toHelp.bind(this, true)}, "Help")
123                                        )
124                                ), 
125                                React.createElement("ul", {id: "CLARIN_header_right", className: "nav navbar-nav navbar-right"}, 
126                                        React.createElement("li", {className: "unauthenticated"}, 
127                                                React.createElement("a", {href: "login", tabIndex: "-1"}, React.createElement("span", {className: "glyphicon glyphicon-log-in"}), " LOGIN")
128                                        )
129                                )
130                        )
131                );
132        },
133
134        render: function() {
135                return  (
136                        React.createElement("div", null, 
137                                React.createElement("div", {className: "container"}, 
138                                        React.createElement("div", {className: "beta-tag"}, 
139                                                React.createElement("span", null, "ALPHA")
140                                        )
141                                ), 
142                       
143                                React.createElement("div", {className: "navbar navbar-default navbar-static-top", role: "navigation"}, 
144                                        React.createElement("div", {className: "container"}, 
145                                                React.createElement("div", {className: "navbar-header"}, 
146                                                        React.createElement("button", {type: "button", className: "navbar-toggle", onClick: this.toggleCollapse}, 
147                                                                React.createElement("span", {className: "sr-only"}, "Toggle navigation"), 
148                                                                React.createElement("span", {className: "icon-bar"}), 
149                                                                React.createElement("span", {className: "icon-bar"}), 
150                                                                React.createElement("span", {className: "icon-bar"})
151                                                        ), 
152                                                        React.createElement("a", {className: "navbar-brand", href: URLROOT, tabIndex: "-1"}, React.createElement("header", null, "Federated Content Search"))
153                                                ), 
154                                                this.renderCollapsible()
155                                        )
156                                ), 
157
158                                React.createElement(ErrorPane, {errorMessages: this.state.errorMessages}), 
159
160                                React.createElement("div", {id: "push"}, 
161                                        React.createElement("div", {className: "container"}, 
162                                                this.state.navbarPageFn()
163                                        ), 
164                                        React.createElement("div", {className: "top-gap"})
165                                )
166                        )
167                );
168        }
169});
170
171
172var StatisticsPage = React.createClass({displayName: 'StatisticsPage',
173        propTypes: {
174                ajax: PT.func.isRequired,
175        },
176
177        getInitialState: function () {
178                return {
179                        stats: {},
180                        activeTab: 0,
181                        // searchStats: {},
182                        // lastScanStats: {},
183                };
184        },
185
186        componentDidMount: function() {
187                this.refreshStats();
188        },
189
190        refreshStats: function() {
191                this.props.ajax({
192                        url: 'rest/statistics',
193                        success: function(json, textStatus, jqXHR) {
194                                this.setState({stats: json});
195                                // console.log("stats:", json);
196                        }.bind(this),
197                });
198        },
199
200        renderWaitTimeSecs: function(t) {
201                var hue = t * 4;
202                if (hue > 120) {
203                        hue = 120;
204                }
205                var a = hue/120;
206                hue = 120 - hue;
207                var shue = "hsla("+hue+",100%,80%,"+a+")";
208                return  React.createElement("span", {className: "badge", style: {backgroundColor:shue, color:"black"}}, 
209                                        t.toFixed(3), "s"
210                                );
211        },
212
213        renderCollections: function(colls) {
214                return  React.createElement("div", {style: {marginLeft:40}}, 
215                                         colls.length === 0 ? 
216                                                React.createElement("div", {style: {color:"#a94442"}}, "NO collections found")
217                                                : 
218                                                React.createElement("div", null, 
219                                                        colls.length, " root collection(s):", 
220                                                        React.createElement("ul", {className: "list-unstyled", style: {marginLeft:40}}, 
221                                                                 colls.map(function(name, i) { return React.createElement("div", {key: i}, name); }) 
222                                                        )
223                                                )
224                                       
225                                );
226        },
227
228        renderDiagnostic: function(d) {
229                return  React.createElement("div", {key: d.diagnostic.uri}, 
230                                        React.createElement("div", {className: "inline alert alert-warning"}, 
231                                                React.createElement("div", null, 
232                                                         d.counter <= 1 ? false : 
233                                                                React.createElement("div", {className: "inline", style: {margin:"5px 5px 5px 5px"}}, 
234                                                                        React.createElement("span", {className: "badge", style: {backgroundColor:'#ae7241'}}, "x ", d.counter)
235                                                                ), 
236                                                       
237                                                        "Diagnostic: ", d.diagnostic.message, ": ", d.diagnostic.diagnostic
238                                                ), 
239                                                React.createElement("div", null, "Context: ", React.createElement("a", {href: d.context}, d.context))
240                                        )
241                                ); 
242        },
243
244        renderError: function(e) {
245                var xc = e.exception;
246                return  React.createElement("div", {key: xc.message}, 
247                                        React.createElement("div", {className: "inline alert alert-danger", role: "alert"}, 
248                                                React.createElement("div", null, 
249                                                         e.counter <= 1 ? false : 
250                                                                React.createElement("div", {className: "inline", style: {margin:"5px 5px 5px 5px"}}, 
251                                                                        React.createElement("span", {className: "badge", style: {backgroundColor:'#c94442'}}, "x ", e.counter, " ")
252                                                                ), 
253                                                       
254                                                        "Exception: ", xc.message
255                                                ), 
256                                                React.createElement("div", null, "Context: ", React.createElement("a", {href: e.context}, e.context)), 
257                                                 xc.cause ? React.createElement("div", null, "Caused by: ", xc.cause) : false
258                                        )
259                                ); 
260        },
261
262        renderEndpoint: function(isScan, endpoint) {
263                var stat = endpoint[1];
264                var errors = _.values(stat.errors);
265                var diagnostics = _.values(stat.diagnostics);
266                return React.createElement("div", {style: {marginTop:10}, key: endpoint[0]}, 
267                                        React.createElement("ul", {className: "list-inline list-unstyled", style: {marginBottom:0}}, 
268                                                React.createElement("li", null, 
269                                                         stat.version == "LEGACY" ? 
270                                                                React.createElement("span", {style: {color:'#a94442'}}, "legacy ", React.createElement("i", {className: "glyphicon glyphicon-thumbs-down"}), " ") 
271                                                                : React.createElement("span", {style: {color:'#3c763d'}}, React.createElement("i", {className: "glyphicon glyphicon-thumbs-up"}), " "), 
272                                                       
273                                                         " "+endpoint[0], ":" 
274                                                ), 
275                                                React.createElement("li", null, 
276                                                        React.createElement("span", null, stat.numberOfRequests), " request(s)," + ' ' +
277                                                        "average:", this.renderWaitTimeSecs(stat.avgExecutionTime), "," + ' ' + 
278                                                        "max: ", this.renderWaitTimeSecs(stat.maxExecutionTime)
279                                                )
280                                        ), 
281                                         isScan ? this.renderCollections(stat.rootCollections) : false, 
282                                                (errors && errors.length) ? 
283                                                React.createElement("div", {className: "inline", style: {marginLeft:40}}, 
284                                                         errors.map(this.renderError) 
285                                                ) : false, 
286                                       
287                                                (diagnostics && diagnostics.length) ? 
288                                                React.createElement("div", {className: "inline", style: {marginLeft:40}}, 
289                                                         diagnostics.map(this.renderDiagnostic) 
290                                                ) : false
291                                       
292                                );
293        },
294
295        renderInstitution: function(isScan, inst) {
296                return  React.createElement("div", {style: {marginTop:30}, key: inst[0]}, 
297                                        React.createElement("h4", null, inst[0]), 
298                                        React.createElement("div", {style: {marginLeft:20}}, " ", _.pairs(inst[1]).map(this.renderEndpoint.bind(this, isScan)) )
299                                );
300        },
301
302        renderStatistics: function(stats) {
303                return  React.createElement("div", {className: "container statistics", style: {marginTop:20}}, 
304                                        React.createElement("div", null, 
305                                                React.createElement("div", null, "Start date: ", new Date(stats.date).toLocaleString()), 
306                                                 stats.isScan ? 
307                                                        React.createElement("div", null, "Max concurrent scan requests per endpoint:", " ", 
308                                                                React.createElement("kbd", null, stats.maxConcurrentScanRequestsPerEndpoint)
309                                                        ) 
310                                                        : 
311                                                        React.createElement("div", null, "Max concurrent search requests per endpoint:", " ", 
312                                                                React.createElement("kbd", null, stats.maxConcurrentSearchRequestsPerEndpoint)
313                                                        ), 
314                                               
315                                                React.createElement("div", null, "Timeout: ", " ", React.createElement("kbd", null, stats.timeout, " seconds"))
316                                        ), 
317                                        React.createElement("div", null, " ",  _.pairs(stats.institutions).map(this.renderInstitution.bind(this, stats.isScan)), " ")
318                                )
319                                 ;
320        },
321
322        setTab: function(idx) {
323                this.setState({activeTab:idx});
324        },
325
326        render: function() {
327                return  (
328                        React.createElement("div", null, 
329                                React.createElement("div", {className: "top-gap"}, 
330                                        React.createElement("h1", null, "Statistics"), 
331                                        React.createElement("p", null), 
332                                        React.createElement("div", {role: "tabpanel"}, 
333                                                React.createElement("ul", {className: "nav nav-tabs", role: "tablist"}, 
334                                                         _.pairs(this.state.stats).map(function(st, idx){
335                                                                        var classname = idx === this.state.activeTab ? "active":"";
336                                                                        return  React.createElement("li", {role: "presentation", className: classname, key: st[0]}, 
337                                                                                                React.createElement("a", {href: "#", role: "tab", onClick: this.setTab.bind(this, idx)}, st[0])
338                                                                                        );
339                                                                }.bind(this))
340                                                       
341                                                ), 
342
343                                                React.createElement("div", {className: "tab-content"}, 
344                                                         _.pairs(this.state.stats).map(function(st, idx){
345                                                                        var classname = idx === this.state.activeTab ? "tab-pane active" : "tab-pane";
346                                                                        return  React.createElement("div", {role: "tabpanel", className: classname, key: st[0]}, 
347                                                                                                this.renderStatistics(st[1])
348                                                                                        );
349                                                                }.bind(this))
350                                                       
351                                                )
352                                        )
353                                )
354                        )
355                        );
356        },
357});
358
359var HelpPage = React.createClass({displayName: 'HelpPage',
360        openHelpDesk: function() {
361                window.open('http://support.clarin-d.de/mail/form.php?queue=Aggregator&lang=en', 
362                        '_blank', 'height=560,width=370');
363        },
364
365        render: function() {
366                return  (
367                        React.createElement("div", null, 
368                                React.createElement("div", {className: "top-gap"}, 
369                                        React.createElement("h1", null, "Help"), 
370                                        React.createElement("h3", null, "Performing search in FCS corpora"), 
371                                        React.createElement("p", null, "To perform simple keyword search in all CLARIN-D Federated Content Search centers" + ' ' + 
372                                        "and their corpora, go to the search field at the top of the page," + ' ' + 
373                                        "enter your query, and click 'search' button or press the 'Enter' key."), 
374                                       
375                                        React.createElement("h3", null, "Search Options - adjusting search criteria"), 
376                                        React.createElement("p", null, "To select specific corpora based on their name or language and to specify" + ' ' + 
377                                        "number of search results (hits) per corpus per page, click on the 'Search options'" + ' ' +
378                                        "link. Here, you can filter resources based on the language, select specific resources," + ' ' + 
379                                        "set the maximum number of hits."), 
380
381                                        React.createElement("h3", null, "Search Results - inspecting search results"), 
382                                        React.createElement("p", null, "When the search starts, the 'Search results' page is displayed" + ' ' + 
383                                        "and its content starts to get filled with the corpora responses." + ' ' + 
384                                        "To save or process the displayed search result, in the 'Search results' page," + ' ' + 
385                                        "go to the menu and select either 'Export to Personal Workspace'," + ' ' + 
386                                        "'Download' or 'Use WebLicht' menu item. This menu appears only after" + ' ' + 
387                                        "all the results on the page have been loaded. To get the next hits from each corpus," + ' ' + 
388                                        "click the 'next' arrow at the bottom of 'Search results' page."), 
389
390
391                                        React.createElement("h3", null, "More help"), 
392                                        React.createElement("p", null, "More detailed information on using FCS Aggregator is available" + ' ' + 
393                                        "at the Aggegator wiki page. If you still cannot find an answer to your question," + ' ' + 
394                                        "or if want to send a feedback, you can write to Clarin-D helpdesk: "), 
395                                        React.createElement("button", {type: "button", className: "btn btn-default btn-lg", onClick: this.openHelpDesk}, 
396                                                React.createElement("span", {className: "glyphicon glyphicon-question-sign", 'aria-hidden': "true"}), 
397                                                " HelpDesk"
398                                        )                                       
399                                )
400                        )
401                );
402        }
403});
404
405var AboutPage = React.createClass({displayName: 'AboutPage',
406        render: function() {
407                return  React.createElement("div", null, 
408                                        React.createElement("div", {className: "top-gap"}, 
409                                                React.createElement("h1", null, "About"), 
410                                                React.createElement("h3", null, "Technology"), 
411
412                                                React.createElement("p", null, "The Aggregator uses the following software components:"), 
413
414                                                React.createElement("ul", null, 
415                                                        React.createElement("li", null, 
416                                                                React.createElement("a", {href: "http://dropwizard.io/"}, "Dropwizard"), " ", 
417                                                                "(", React.createElement("a", {href: "http://www.apache.org/licenses/LICENSE-2.0"}, "Apache License 2.0"), ")"
418                                                        ), 
419                                                        React.createElement("li", null, 
420                                                                React.createElement("a", {href: "http://eclipse.org/jetty/"}, "Jetty"), " ", 
421                                                                "(", React.createElement("a", {href: "http://www.apache.org/licenses/LICENSE-2.0"}, "Apache License 2.0"), ")"
422                                                        ), 
423                                                        React.createElement("li", null, 
424                                                                React.createElement("a", {href: "http://jackson.codehaus.org/"}, "Jackson"), " ", 
425                                                                "(", React.createElement("a", {href: "http://www.apache.org/licenses/LICENSE-2.0"}, "Apache License 2.0"), ")"
426                                                        ), 
427                                                        React.createElement("li", null, 
428                                                                React.createElement("a", {href: "https://jersey.java.net/"}, "Jersey"), " ", 
429                                                                "(", React.createElement("a", {href: "https://jersey.java.net/license.html#/cddl"}, "CCDL 1.1"), ")"
430                                                        ), 
431                                                        React.createElement("li", null, 
432                                                                React.createElement("a", {href: "https://github.com/optimaize/language-detector"}, "Optimaize Language Detector"), " ", 
433                                                                "(", React.createElement("a", {href: "http://www.apache.org/licenses/LICENSE-2.0"}, "Apache License 2.0"), ")"
434                                                        ), 
435                                                        React.createElement("li", null, 
436                                                                React.createElement("a", {href: "http://poi.apache.org/"}, "Apache POI"), " ", 
437                                                                "(", React.createElement("a", {href: "http://www.apache.org/licenses/LICENSE-2.0"}, "Apache License 2.0"), ")"
438                                                        )
439                                                ), 
440
441                                                React.createElement("ul", null, 
442                                                        React.createElement("li", null, 
443                                                                React.createElement("a", {href: "http://facebook.github.io/react/"}, "React"), " ", 
444                                                                "(", React.createElement("a", {href: "https://github.com/facebook/react/blob/master/LICENSE"}, "BSD license"), ")"
445                                                        ), 
446                                                        React.createElement("li", null, 
447                                                                React.createElement("a", {href: "http://getbootstrap.com/"}, "Bootstrap"), " ", 
448                                                                "(", React.createElement("a", {href: "http://opensource.org/licenses/mit-license.html"}, "MIT license"), ")"
449                                                        ), 
450                                                        React.createElement("li", null, 
451                                                                React.createElement("a", {href: "http://jquery.com/"}, "jQuery"), " ", 
452                                                                "(", React.createElement("a", {href: "http://opensource.org/licenses/mit-license.html"}, "MIT license"), ")"
453                                                        ), 
454                                                        React.createElement("li", null, 
455                                                                React.createElement("a", {href: "http://glyphicons.com/"}, "GLYPHICONS free"), " ", 
456                                                                "(", React.createElement("a", {href: "https://creativecommons.org/licenses/by/3.0/"}, "CC-BY 3.0"), ")"
457                                                        ), 
458                                                        React.createElement("li", null, 
459                                                                React.createElement("a", {href: "http://fortawesome.github.io/Font-Awesome/"}, "FontAwesome"), " ", 
460                                                                "(", React.createElement("a", {href: "http://opensource.org/licenses/mit-license.html"}, "MIT"), ", ", React.createElement("a", {href: "http://scripts.sil.org/OFL"}, "SIL Open Font License"), ")"
461                                                        )
462                                                ), 
463
464                                                React.createElement("h3", null, "Statistics"), 
465                                                React.createElement("button", {type: "button", className: "btn btn-default btn-lg", onClick: function() {main.toStatistics(true);}}, 
466                                                        React.createElement("span", {className: "glyphicon glyphicon-cog", 'aria-hidden': "true"}, " "), 
467                                                        "View server log"
468                                                )
469                                        )
470                                );
471        }
472});
473
474var Footer = React.createClass({displayName: 'Footer',
475        toAbout: function(e) {
476                main.toAbout(true);
477                e.preventDefault();
478                e.stopPropagation();
479        },
480
481        render: function() {
482                return  (
483                        React.createElement("div", {className: "container"}, 
484                                React.createElement("div", {id: "CLARIN_footer_left"}, 
485                                                React.createElement("a", {title: "about", href: "about", onClick: this.toAbout}, 
486                                                React.createElement("span", {className: "glyphicon glyphicon-info-sign"}), 
487                                                React.createElement("span", null, VERSION)
488                                        )
489                                ), 
490                                React.createElement("div", {id: "CLARIN_footer_middle"}, 
491                                        React.createElement("a", {title: "CLARIN ERIC", href: "https://www.clarin.eu/"}, 
492                                        React.createElement("img", {src: "img/clarindLogo.png", alt: "CLARIN ERIC logo", style: {height:80}})
493                                        )
494                                ), 
495                                React.createElement("div", {id: "CLARIN_footer_right"}, 
496                                        React.createElement("a", {title: "contact", href: "mailto:fcs@clarin.eu"}, 
497                                                React.createElement("span", {className: "glyphicon glyphicon-envelope"}), 
498                                                React.createElement("span", null, " CONTACT")
499                                        )
500                                )
501                        )
502                );
503        }
504});
505
506function endsWith(str, suffix) {
507    return str.indexOf(suffix, str.length - suffix.length) !== -1;
508}
509
510var routeFromLocation = function() {
511        // console.log("routeFromLocation: " + document.location);
512        if (!this) throw "routeFromLocation must be bound to main";
513        var path = window.location.pathname.split('/');
514        if (path.length === 3) {
515                var p = path[2];
516                if (p === 'help') {
517                        this.toHelp(false);
518                } else if (p === 'about') {
519                        this.toAbout(false);
520                } else if (p === 'stats') {
521                        this.toStatistics(false);
522                } else {
523                        this.toAggregator(false);
524                }
525        } else {
526                this.toAggregator(false);
527        }
528};
529
530var main = React.render(React.createElement(Main, null),  document.getElementById('body'));
531React.render(React.createElement(Footer, null), document.getElementById('footer') );
532
533window.onpopstate = routeFromLocation.bind(main);
534
535routeFromLocation.bind(main)();
536
537})();
538
539
540
541
542
543
544
Note: See TracBrowser for help on using the repository browser.