source: SRUAggregator/trunk/src/main/resources/assets/js/pages/aggregatorpage.jsx @ 7221

Last change on this file since 7221 was 7221, checked in by Leif-Jöran, 6 years ago

Updates to aggreagtor page.

File size: 26.1 KB
Line 
1"use strict";
2import classNames from "classnames";
3import CorpusView from "../components/corpusview.jsx";
4import LanguageSelector from "../components/languageselector.jsx"
5import Modal from "../components/modal.jsx";
6import Results from "../components/results.jsx";
7import QueryInput from "../components/queryinput.jsx";
8import ZoomedResult from "../components/zoomedresult.jsx";
9import PropTypes from "prop-types";
10import createReactClass from "create-react-class";
11
12var PT = PropTypes;
13
14window.MyAggregator = window.MyAggregator || {};
15var multipleLanguageCode = window.MyAggregator.multipleLanguageCode = "mul"; // see ISO-693-3
16
17var AggregatorPage = createReactClass({
18// fixme! - class AggregatorPage extends React.Component {
19    propTypes: {
20        ajax: PT.func.isRequired,
21        error: PT.func.isRequired,
22        embedded: PT.bool.isRequired
23    },
24
25        nohits: {
26                results: null,
27        },
28        anyLanguage: [multipleLanguageCode, "Any Language"],
29
30        getInitialState: function () {
31            var aggrContext = getQueryVariable('x-aggregation-context');
32            aggrContext = aggrContext && JSON.parse(aggrContext);
33           
34                return {
35                        corpora: new Corpora([], this.updateCorpora),
36                        languageMap: {},
37                        weblichtLanguages: [],
38            queryTypeId: getQueryVariable('queryType') || 'cql',
39                        cqlQuery: ((getQueryVariable('queryType') || 'cql') === 'cql') && getQueryVariable('query') || '',
40                        fcsQuery: ((getQueryVariable('queryType') || 'cql') === 'fcs') && getQueryVariable('query') || '',
41                        aggregationContext: aggrContext || null,
42                        language: this.anyLanguage,
43                        languageFilter: 'byMeta',
44                        numberOfResults: 10,
45
46                        searchId: null,
47                        timeout: 0,
48                        hits: this.nohits,
49
50                        zoomedCorpusHit: null,
51                };
52        },
53
54        componentDidMount: function() {
55            this._isMounted = true;
56
57                this.props.ajax({
58                        url: 'rest/init',
59                        success: function(json, textStatus, jqXHR) {
60                                if (this._isMounted) {
61                                        var corpora = new Corpora(json.corpora, this.updateCorpora);
62                                       
63                                        // // for testing aggregation context
64                                        // json['x-aggregation-context'] = {
65                                        //      'EKUT': ["http://hdl.handle.net/11858/00-1778-0000-0001-DDAF-D"]
66                                        // };
67
68                    var aggregationContext = json['x-aggregation-context'] || this.state.aggregationContext;
69                                   
70                                    window.MyAggregator.mode = getQueryVariable('mode') || json.mode;
71                                    window.MyAggregator.corpora = json.corpora;
72                        window.MyAggregator.xAggregationContext = aggregationContext;
73                                       
74                                    // Setting visibility, e.g. only corpora
75                    // from v2.0 endpoints for fcs v2.0
76                    corpora.setVisibility(this.state.queryTypeId, this.state.language[0]);
77
78                    if (aggregationContext) {
79                        const contextCorporaInfo = corpora.setAggregationContext(aggregationContext);
80                        const unavailableCorporaHandles = contextCorporaInfo.unavailable; // list of unavailable aggregationContext
81                        if (unavailableCorporaHandles.length > 0) {
82                            this.props.error("Could not find requested collection handles:\n" + unavailableCorporaHandles.join('\n'));
83                        }
84                   
85                        const actuallySelectedCorpora = corpora.getSelectedIds();
86                       
87                        if (contextCorporaInfo.selected.length !== actuallySelectedCorpora.length) {
88                            if (actuallySelectedCorpora.length === 0) {
89                                this.props.error("This search does not support the required collection(s), will search all collections instead"); // TODO give detailed reason its not supported.
90                                corpora.recurse(function(corpus) { corpus.selected = true; });
91                            } else {
92                                var err = "Some required context collections are not supported for this search:\n"
93                                err = err + contextCorpora.filter((c) => {
94                                    if (actuallySelectedCorpora.indexOf(c) === -1) {
95                                        console.warn("Requested corpus but not available for selection", c);
96                                        return true;
97                                    }
98                                    return false;
99                                }).map((c) => c.title).join('\n')
100                                this.props.error(err);
101                            }
102                        }
103                    }
104                    else {
105                        // no context set all visibl to selected as default.
106                        console.log("no context set, selecting all available");
107                        corpora.recurse(c => {c.visible ? c.selected=true : null})
108                    }
109                   
110                    this.setState({
111                            corpora : corpora,
112                            languageMap: json.languages,
113                            weblichtLanguages: json.weblichtLanguages,
114                            aggregationContext: aggregationContext,
115                        }, this.postInit);
116                                }
117                                else {
118                                    console.warn("Got Aggregator init response, but not mounted!");
119                                }
120                        }.bind(this),
121                });
122        },
123       
124        postInit() {
125             if (window.MyAggregator.mode === 'search') {
126            this.search();
127        }
128        },
129
130        updateCorpora: function(corpora) {
131                this.setState({corpora:corpora});
132        },
133       
134        getCurrentQuery() {
135            return  this.state.queryTypeId === 'fcs' ? this.state.fcsQuery : this.state.cqlQuery;
136        },
137
138        search() {
139                var query = this.getCurrentQuery();
140                var queryTypeId = this.state.queryTypeId;
141                if (!query || (this.props.embedded && window.MyAggregator.mode !== 'search')) {
142                        this.setState({ hits: this.nohits, searchId: null });
143                        return;
144                }
145                var selectedIds = this.state.corpora.getSelectedIds();
146                if (!selectedIds.length) {
147                        this.props.error("Please select a collection to search into");
148                        return;
149                }
150
151                // console.log("searching in the following corpora:", selectedIds);
152                // console.log("searching with queryType:", queryTypeId);
153                this.props.ajax({
154                        url: 'rest/search',
155                        type: "POST",
156                        data: {
157                        query: query,
158                queryType: queryTypeId,
159                                language: this.state.language[0],
160                                numberOfResults: this.state.numberOfResults,
161                                corporaIds: selectedIds,
162                        },
163                        success: function(searchId, textStatus, jqXHR) {
164                                // console.log("search ["+query+"] ok: ", searchId, jqXHR);
165                                //Piwik.getAsyncTracker().trackSiteSearch(query, queryTypeId);
166                                // automatic inclusion of piwik in prod
167                                //console.log("location.hostname: " + location.hostname);
168                                if (location.hostname !== "localhost") {
169                                   //console.log("location.host: " + location.host);
170                                   _paq.push(['trackSiteSearch', query, queryTypeId, false]);
171                                }
172
173                                var timeout = 250;
174                                setTimeout(this.refreshSearchResults, timeout);
175                                this.setState({ searchId: searchId, timeout: timeout });
176                        }.bind(this),
177                });
178        },
179       
180        nextResults: function(corpusId) {
181                // console.log("searching next results in corpus:", corpusId);
182                this.props.ajax({
183                        url: 'rest/search/'+this.state.searchId,
184                        type: "POST",
185                        data: {
186                                corpusId: corpusId,
187                                numberOfResults: this.state.numberOfResults,
188                        },
189                        success: function(searchId, textStatus, jqXHR) {
190                                // console.log("search ["+query+"] ok: ", searchId, jqXHR);
191                                var timeout = 250;
192                                setTimeout(this.refreshSearchResults, timeout);
193                                this.setState({ searchId: searchId, timeout: timeout });
194                        }.bind(this),
195                });
196        },
197
198        refreshSearchResults: function() {
199                if (!this.state.searchId || !this._isMounted) {
200                        return;
201                }
202                this.props.ajax({
203                        url: 'rest/search/'+this.state.searchId,
204                        success: function(json, textStatus, jqXHR) {
205                                var timeout = this.state.timeout;
206                                if (json.inProgress) {
207                                        if (timeout < 10000) {
208                                                timeout = 1.5 * timeout;
209                                        }
210                                        setTimeout(this.refreshSearchResults, timeout);
211                                        // console.log("new search in: " + this.timeout + "ms");
212                                } else {
213                                        console.log("search ended; hits:", json);
214                                }
215                                var corpusHit = this.state.zoomedCorpusHit;
216                                if (corpusHit) {
217                                        for (var resi = 0; resi < json.results.length; resi++) {
218                                                var res = json.results[resi];
219                                                if (res.corpus.id === corpusHit.corpus.id) {
220                                                        corpusHit = res;
221                                                        break;
222                                                }
223                                        }
224                                }
225                                this.setState({ hits: json, timeout: timeout, zoomedCorpusHit: corpusHit});
226                        }.bind(this),
227                });
228        },
229
230        getExportParams: function(corpusId, format, filterLanguage) {
231                var params = corpusId ? {corpusId:corpusId}:{};
232                if (format) params.format = format;
233                if (filterLanguage) {
234                        params.filterLanguage = filterLanguage;
235                } else if (this.state.languageFilter === 'byGuess' || this.state.languageFilter === 'byMetaAndGuess') {
236                        params.filterLanguage = this.state.language[0];
237                }
238                return encodeQueryData(params);
239        },
240
241        getDownloadLink: function(corpusId, format) {
242                return 'rest/search/'+this.state.searchId+'/download?' +
243                        this.getExportParams(corpusId, format);
244        },
245
246        getToWeblichtLink: function(corpusId, forceLanguage) {
247                return 'rest/search/'+this.state.searchId+'/toWeblicht?' +
248                        this.getExportParams(corpusId, null, forceLanguage);
249        },
250
251        setLanguageAndFilter: function(languageObj, languageFilter) {
252                this.state.corpora.setVisibility(this.state.queryTypeId,
253                        languageFilter === 'byGuess' ? multipleLanguageCode : languageObj[0]);
254                this.setState({
255                        language: languageObj,
256                        languageFilter: languageFilter,
257                        corpora: this.state.corpora, // === this.state.corpora.update();
258                });
259        },
260
261        setQueryType: function(queryTypeId) {
262                this.state.corpora.setVisibility(queryTypeId, this.state.language[0]);
263                setQueryVariable('queryType', queryTypeId);
264                setQueryVariable('query', queryTypeId === 'cql' ? this.state.cqlQuery : this.state.fcsQuery)
265                this.setState({
266                        queryTypeId: queryTypeId,
267                        hits: this.nohits,
268                        searchId: null,
269                });
270        },
271
272        setNumberOfResults: function(e) {
273                var n = e.target.value;
274                if (n < 10) n = 10;
275                if (n > 250) n = 250;
276                this.setState({numberOfResults: n});
277                e.preventDefault();
278                e.stopPropagation();
279        },
280
281        stop: function(e) {
282                e.stopPropagation();
283        },
284
285        filterResults: function() {
286                var noLangFiltering = this.state.languageFilter === 'byMeta';
287                var langCode = this.state.language[0];
288                var results = null, inProgress = 0, hits = 0;
289                if (this.state.hits.results) {
290                        results = this.state.hits.results.map(function(corpusHit) {
291                                return {
292                                        corpus: corpusHit.corpus,
293                                        inProgress: corpusHit.inProgress,
294                                        exception: corpusHit.exception,
295                                        diagnostics: corpusHit.diagnostics,
296                                        kwics: noLangFiltering ? corpusHit.kwics :
297                                                corpusHit.kwics.filter(function(kwic) {
298                                                        return kwic.language === langCode ||
299                                                               langCode === multipleLanguageCode ||
300                                                               langCode === null;
301                                                }),
302                                        advancedLayers: noLangFiltering ? corpusHit.advancedLayers :
303                                                corpusHit.advancedLayers.filter(function(layer) {
304                                                        return layer.language === langCode ||
305                                                               langCode === multipleLanguageCode ||
306                                                               langCode === null;
307                                                }),
308                                };
309                        });
310                        for (var i = 0; i < results.length; i++) {
311                                var result = results[i];
312                                if (result.inProgress) {
313                                        inProgress++;
314                                }
315                                if (result.kwics.length > 0) {
316                                        hits ++;
317                                }
318                        }
319                }
320                return {
321                        results: results,
322                        hits: hits,
323                        inProgress: inProgress,
324                };
325        },
326
327        toggleLanguageSelection: function(e) {
328            $(ReactDOM.findDOMNode(this.refs.languageModal)).modal();
329                e.preventDefault();
330                e.stopPropagation();
331        },
332
333        toggleCorpusSelection: function(e) {
334            $(ReactDOM.findDOMNode(this.refs.corporaModal)).modal();
335                e.preventDefault();
336                e.stopPropagation();
337        },
338
339        toggleResultModal: function(e, corpusHit) {
340            $(ReactDOM.findDOMNode(this.refs.resultModal)).modal();
341                this.setState({zoomedCorpusHit: corpusHit});
342                e.preventDefault();
343                e.stopPropagation();
344        },
345
346        onQueryChange: function(queryStr) {
347            if (this.state.queryTypeId === 'cql') {
348                this.setState({
349                        cqlQuery: queryStr || '',
350                    });
351            } else {
352                this.setState({
353                        fcsQuery: queryStr || '',
354                    });
355            }
356                setQueryVariable('query', queryStr);
357        },
358
359        handleKey: function(event) {
360                if (event.keyCode==13) {
361                        this.search();
362                }
363        },
364
365        renderZoomedResultTitle: function(corpusHit) {
366                if (!corpusHit) return (<span/>);
367                var corpus = corpusHit.corpus;
368                return (<h3 style={{fontSize:'1em'}}>
369                                        {corpus.title}
370                                        { corpus.landingPage ?
371                                                <a href={corpus.landingPage} onClick={this.stop} style={{fontSize:12}}>
372                                                        <span> – Homepage </span>
373                                                        <i className="glyphicon glyphicon-home"/>
374                                                </a>: false}
375                                </h3>);
376        },
377
378        renderSearchButtonOrLink: function() {
379                if (this.props.embedded) {
380                        var query = this.getCurrentQuery();
381                        var queryTypeId = this.state.queryTypeId;
382                var btnClass = classNames({
383                            'btn': true,
384                            'btn-default': queryTypeId === 'cql',
385                            'btn-primary': true,
386                            'input-lg': true
387                        });
388                        var newurl = !query ? "#" :
389                                (window.MyAggregator.URLROOT + "?" + encodeQueryData({queryType:queryTypeId, query:query, mode:'search'}));
390                        return ( <a className={btnClass} style={{paddingTop:13}}
391                                        type="button" target="_blank" href={newurl}>
392                                        <i className="glyphicon glyphicon-search"></i>
393                                </a>
394                        );
395                }
396                return (
397                    <button className="btn btn-default input-lg image_button" type="button" onClick={this.search}>
398                                <i className="glyphicon glyphicon-search"></i>
399                    </button>
400                );
401        },
402       
403       
404        renderQueryInput() {
405            var queryType = queryTypeMap[this.state.queryTypeId];
406            return (
407                <QueryInput
408                            searchedLanguages={this.state.searchedLanguages || [multipleLanguageCode]}
409                            corpora={this.props.corpora}
410                            queryTypeId={this.state.queryTypeId}
411                            query={this.getCurrentQuery()===undefined ? queryType.searchPlaceholder : this.getCurrentQuery()}
412                            embedded={this.props.embedded}
413                            placeholder={queryType.searchPlaceholder}
414                onQueryChange={this.onQueryChange}
415                            onKeyDown={this.handleKey} />
416                );
417        },
418       
419        renderEmbed () {
420            var queryType = queryTypeMap[this.state.queryTypeId];
421           
422            return <div className="aligncenter" style={{marginLeft:16, marginRight:16}}>
423                <div className={"input-group"}>
424                            <span className="input-group-addon" style={{backgroundColor:queryType.searchLabelBkColor}}>
425                                    {queryType.searchLabel}
426                            </span>
427                       
428                { this.renderQueryInput() }
429                           
430                <div className="input-group-btn">
431                                    {this.renderSearchButtonOrLink()}
432                            </div>
433                    </div>
434                </div>
435        },
436       
437        renderGQB () {
438            var queryType = queryTypeMap[this.state.queryTypeId];
439           
440            return <div style={{marginLeft:16, marginRight:16}}>
441                <div className="panel panel-default">
442                            <div className="panel-heading" style={{backgroundColor:queryType.searchLabelBkColor, fontSize: "120%"}}>
443                                    {queryType.searchLabel}
444                            </div>
445                           
446                            <div className="panel-body">
447                                { this.renderQueryInput() }
448                             </div>
449                         
450                <div className="panel-footer">
451                    <div className="input-group">
452                                   
453                                    <pre className="adv-query-preview aligncenter input-control input-lg">{this.getCurrentQuery()}</pre>
454                                   
455                                        <div className="input-group-btn">
456                                            {this.renderSearchButtonOrLink()}
457                                        </div>
458                                    </div>
459                            </div>
460                    </div>
461                  </div>
462        },
463       
464        renderUnavailableCorporaMessage() {
465            if (!this.state.corpora) {
466                return;
467            }
468        const unavailable = [];
469        this.state.corpora.recurse((c) => {
470            if (c.selected && ! c.visible) {
471                unavailable.push(c);
472            }
473            if (c.selected) {
474                // apparently a selected corpus
475            }
476        });
477       
478        if (unavailable.length) {
479            return <div id="unavailable-corpora-message" className="text-muted">
480                <div id="unavailable-corpora-message-message">
481                    <a role="button" data-toggle="dropdown">{unavailable.length} selected collection{unavailable.length > 1 ? 's are' : ' is'} disabled in this search mode.</a>
482                </div>
483                <ul id="unavailable-corpora-message-list" className="dropdown-menu">
484                                {
485                                    unavailable.map((c) => <li className="unavailable-corpora-message-item">{c.name}</li>)
486                                }
487                                </ul>
488            </div>
489        }
490        },
491
492        render: function() {
493             
494                var queryType = queryTypeMap[this.state.queryTypeId];
495                return  (
496                        <div className="top-gap">
497                                <div className="row">
498                                        { (!this.props.embedded && this.state.queryTypeId == "fcs") ? this.renderGQB() : this.renderEmbed() }
499                                </div>
500
501                                <div className="well" style={{marginTop:20}}>
502                                        <div className="aligncenter" >
503                                        {
504                                            //this.renderUnavailableCorporaMessage()
505                                        }
506                                                <form className="form-inline" role="form">
507
508                                                        <div className="input-group">
509
510                                                                <span className="input-group-addon nobkg" >Search for</span>
511
512                                                                <div className="input-group-btn">
513                                                                        <button className="form-control btn btn-default"
514                                                                                        onClick={this.toggleLanguageSelection}>
515                                                                                {this.state.language[1]} <span className="caret"/>
516                                                                        </button>
517                                                                        <span/>
518                                                                </div>
519                                                                <div className="input-group-btn hidden-xxs">
520                                                                        <ul ref="queryTypeDropdownMenu" className="dropdown-menu">
521                                                                                {       queryTypes.map(function(l) {
522                                                                                                var cls = l.disabled ? 'disabled':'';
523                                                                                                var handler = function() { if (!l.disabled) this.setQueryType(l.id); }.bind(this);
524                                                                                                return (<li key={l.id} className={cls}> <a tabIndex="-1" href="#"
525                                                                                                        onClick={handler}> {l.name} </a></li>);
526                                                                                        }.bind(this))
527                                                                                }
528                                                                        </ul>
529
530                                                                        <button className="form-control btn btn-default"
531                                                                                        aria-expanded="false" data-toggle="dropdown" >
532                                                                                {queryType.name} <span className="caret"/>
533                                                                        </button>
534                                                                </div>
535
536                                                        </div>
537
538                                                        <div className="input-group hidden-xs">
539                                                                <span className="input-group-addon nobkg">in</span>
540                                                                <button type="button" className="btn btn-default" onClick={this.toggleCorpusSelection}>
541                                                                        {this.state.corpora.getSelectedMessage()} <span className="caret"/>
542                                                                </button>
543                                                        </div>
544
545                                                        <div className="input-group hidden-xs hidden-sm">
546                                                                <span className="input-group-addon nobkg">and show up to</span>
547                                                                <div className="input-group-btn">
548                                                                        <input type="number" className="form-control input" min="10" max="250"
549                                                                                style={{width:60}}
550                                                                                onChange={this.setNumberOfResults} value={this.state.numberOfResults}
551                                                                                onKeyPress={this.stop}/>
552                                                                </div>
553                                                                <span className="input-group-addon nobkg">hits per endpoint</span>
554                                                        </div>
555
556                                                </form>
557                                        </div>
558                                </div>
559
560                                <Modal ref="corporaModal" title={<span>Collections <small className="text-muted">{this.props.corpora && this.props.corpora.getSelectedMessage()}</small></span>}>
561                                        <CorpusView corpora={this.state.corpora} languageMap={this.state.languageMap} />
562                                </Modal>
563
564                                <Modal ref="languageModal" title={<span>Select Language</span>}>
565                                        <LanguageSelector anyLanguage={this.anyLanguage}
566                                                                          languageMap={this.state.languageMap}
567                                                                          selectedLanguage={this.state.language}
568                                                                          languageFilter={this.state.languageFilter}
569                                                                          languageChangeHandler={this.setLanguageAndFilter} />
570                                </Modal>
571
572                                <Modal ref="resultModal" title={this.renderZoomedResultTitle(this.state.zoomedCorpusHit)}>
573                                        <ZoomedResult corpusHit={this.state.zoomedCorpusHit}
574                                                                  nextResults={this.nextResults}
575                                                                  getDownloadLink={this.getDownloadLink}
576                                                                  getToWeblichtLink={this.getToWeblichtLink}
577                                                                  searchedLanguage={this.state.language}
578                                                                  weblichtLanguages={this.state.weblichtLanguages}
579                                                                  languageMap={this.state.languageMap}
580                                                                  queryTypeId={this.state.queryTypeId} />
581                                </Modal>
582
583                                <div className="top-gap">
584                                        <Results collhits={this.filterResults()}
585                                                         toggleResultModal={this.toggleResultModal}
586                                                         getDownloadLink={this.getDownloadLink}
587                                                         getToWeblichtLink={this.getToWeblichtLink}
588                                                         searchedLanguage={this.state.language}
589                                                         queryTypeId={this.state.queryTypeId}/>
590                                </div>
591                        </div>
592                        );
593        },
594});
595
596function Corpora(corpora, updateFn) {
597        var that = this;
598        this.corpora = corpora;
599        this.update = function() {
600                updateFn(that);
601        };
602
603        var sortFn = function(x, y) {
604                var r = x.institution.name.localeCompare(y.institution.name);
605                if (r !== 0) {
606                        return r;
607                }
608                return x.title.toLowerCase().localeCompare(y.title.toLowerCase());
609        };
610
611        this.recurse(function(corpus) { corpus.subCorpora.sort(sortFn); });
612        this.corpora.sort(sortFn);
613
614        this.recurse(function(corpus, index) {
615                corpus.visible = true; // visible in the corpus view
616                corpus.selected = false; // not selected in the corpus view, assign later
617                corpus.expanded = false; // not expanded in the corpus view
618                corpus.priority = 1; // used for ordering search results in corpus view
619                corpus.index = index; // original order, used for stable sort
620        });
621}
622
623Corpora.prototype.recurseCorpus = function(corpus, fn) {
624        if (false === fn(corpus)) {
625                // no recursion
626        } else {
627                this.recurseCorpora(corpus.subCorpora, fn);
628        }
629};
630
631Corpora.prototype.recurseCorpora = function(corpora, fn) {
632        var recfn = function(corpus, index){
633                if (false === fn(corpus, index)) {
634                        // no recursion
635                } else {
636                        corpus.subCorpora.forEach(recfn);
637                }
638        };
639        corpora.forEach(recfn);
640};
641
642Corpora.prototype.recurse = function(fn) {
643        this.recurseCorpora(this.corpora, fn);
644};
645
646Corpora.prototype.getLanguageCodes = function() {
647        var languages = {};
648        this.recurse(function(corpus) {
649                corpus.languages.forEach(function(lang) {
650                        languages[lang] = true;
651                });
652                return true;
653        });
654        return languages;
655};
656
657Corpora.prototype.isCorpusVisible = function(corpus, queryTypeId, languageCode) {
658        if (queryTypeId === "fcs" && (corpus.endpoint.protocol === "LEGACY" || corpus.endpoint.protocol === "VERSION_1")) {
659            return false;
660        }
661        // yes for any language
662        if (languageCode === multipleLanguageCode) {
663                return true;
664        }
665        // yes if the corpus is in only that language
666        if (corpus.languages && corpus.languages.length === 1 && corpus.languages[0] === languageCode) {
667                return true;
668        }
669
670        // ? yes if the corpus also contains that language
671        if (corpus.languages && corpus.languages.indexOf(languageCode) >=0) {
672                return true;
673        }
674
675        // ? yes if the corpus has no language
676        // if (!corpus.languages || corpus.languages.length === 0) {
677        //      return true;
678        // }
679        return false;
680};
681
682Corpora.prototype.setVisibility = function(queryTypeId, languageCode) {
683        // top level
684        this.corpora.forEach(function(corpus) {
685                corpus.visible = this.isCorpusVisible(corpus, queryTypeId, languageCode);
686                this.recurseCorpora(corpus.subCorpora, function(c) { c.visible = corpus.visible; });
687        }.bind(this));
688};
689
690Corpora.prototype.setAggregationContext = function(endpoints2handles) {
691        var selectSubTree = function(select, corpus) {
692                corpus.selected = select;
693                this.recurseCorpora(corpus.subCorpora, function(c) { c.selected = corpus.selected; });
694        };
695
696        this.corpora.forEach(selectSubTree.bind(this, false));
697
698    var handlesNotFound = [];
699        var corporaToSelect = [];
700        _.pairs(endpoints2handles).forEach((endp) => {
701                var endpoint = endp[0];
702                var handles = endp[1];
703            console.log('setAggregationContext: endpoint', endpoint);
704            console.log('setAggregationContext: handles', handles);
705                handles.forEach((handle) => {
706                    var found = false;
707                        this.recurse((corpus) => {
708                                if (corpus.handle === handle) {
709                                    found = true;
710                                        corporaToSelect.push(corpus);
711                                }
712                        })
713                        if (!found) {
714                            console.warn("Handle not found in corpora", handle);
715                            handlesNotFound.push(handle);
716                        }
717                })
718        })
719
720        corporaToSelect.forEach(selectSubTree.bind(this, true));
721        return {'selected': corporaToSelect, 'unavailable': handlesNotFound};
722};
723
724Corpora.prototype.getSelectedIds = function() {
725        var ids = [];
726        this.recurse(function(corpus) {
727                if (corpus.visible && corpus.selected) {
728                        ids.push(corpus.id);
729                        //eturn false; // top-most collection in tree, don't delve deeper
730                        // But subcollections are also selectable on their own?...
731                }
732                return true;
733        });
734
735        // console.log("ids: ", ids.length, {ids:ids});
736        return ids;
737};
738
739Corpora.prototype.getSelectedMessage = function() {
740        var selected = this.getSelectedIds().length;
741        if (this.corpora.length === selected) {
742                return "All available collections (" + selected + ")";
743        } else if (selected === 1) {
744                return "1 selected collection";
745        }
746        return selected + " selected collections";
747};
748
749function getQueryVariable(variable) {
750    var query = window.location.search.substring(1);
751    var vars = query.split('&');
752    for (var i = 0; i < vars.length; i++) {
753        var pair = vars[i].split('=');
754        if (decodeURIComponent(pair[0]) == variable) {
755            console.log("variable found: (", variable, ") = ", decodeURIComponent(pair[1]));
756            return decodeURIComponent(pair[1]);
757        }
758    }
759    return null;
760}
761
762/* setter opposite of getQueryVariable*/
763function setQueryVariable(qvar, value) {
764    var query = window.location.search.substring(1);
765    var vars = query.split('&');
766    var d = {};
767    d[qvar] = value;
768    var found = false;
769    for (var i = 0; i < vars.length; i++) {
770        var pair = vars[i].split('=');
771        if (decodeURIComponent(pair[0]) === qvar) {
772           
773            vars[i] = encodeQueryData(d);
774            found=true;
775            break;
776        }
777    }
778   
779    if (!found) {
780        // add to end of url
781        vars.push(encodeQueryData(d));
782    }
783   
784    var searchPart = vars.join('&');
785    var newUrl = window.location.origin + window.location.pathname+'?'+searchPart;
786    console.log("set url", newUrl);
787    window.history.replaceState(window.history.state, null, newUrl);
788}
789
790
791function encodeQueryData(data)
792{
793        var ret = [];
794        for (var d in data) {
795                ret.push(encodeURIComponent(d) + "=" + encodeURIComponent(data[d]));
796        }
797        return ret.join("&");
798}
799
800var queryTypes = [
801        {
802                id: "cql",
803                name: "Text layer Contextual Query Language (CQL)",
804                searchPlaceholder: "Elephant",
805                searchLabel: "Text layer CQL query",
806                searchLabelBkColor: "#fed",
807                className: '',
808        },
809        {
810                id: "fcs",
811                name: "Multi-layer Federated Content Search Query Language (FCS-QL)",
812                searchPlaceholder: "[word = 'annotation'][word = 'focused']",
813                searchLabel: "Multi-layer FCS query",
814                searchLabelBkColor: "#efd",
815                disabled: false,
816        },
817];
818
819var queryTypeMap = {
820        cql: queryTypes[0],
821        fcs: queryTypes[1],
822};
823
824module.exports = AggregatorPage;
Note: See TracBrowser for help on using the repository browser.