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

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