"use strict"; import classNames from "classnames"; import CorpusView from "../components/corpusview.jsx"; import LanguageSelector from "../components/languageselector.jsx" import Modal from "../components/modal.jsx"; import Results from "../components/results.jsx"; import QueryInput from "../components/queryinput.jsx"; import ZoomedResult from "../components/zoomedresult.jsx"; import PropTypes from "prop-types"; import createReactClass from "create-react-class"; var PT = PropTypes; window.MyAggregator = window.MyAggregator || {}; var multipleLanguageCode = window.MyAggregator.multipleLanguageCode = "mul"; // see ISO-693-3 var AggregatorPage = createReactClass({ // fixme! - class AggregatorPage extends React.Component { propTypes: { ajax: PT.func.isRequired, error: PT.func.isRequired, embedded: PT.bool.isRequired }, nohits: { results: null, }, anyLanguage: [multipleLanguageCode, "Any Language"], getInitialState: function () { var aggrContext = getQueryVariable('x-aggregation-context'); aggrContext = aggrContext && JSON.parse(aggrContext); return { corpora: new Corpora([], this.updateCorpora), languageMap: {}, weblichtLanguages: [], queryTypeId: getQueryVariable('queryType') || 'cql', cqlQuery: ((getQueryVariable('queryType') || 'cql') === 'cql') && getQueryVariable('query') || '', fcsQuery: ((getQueryVariable('queryType') || 'cql') === 'fcs') && getQueryVariable('query') || '', aggregationContext: aggrContext || null, language: this.anyLanguage, languageFilter: 'byMeta', numberOfResults: 10, searchId: null, timeout: 0, hits: this.nohits, zoomedCorpusHit: null, }; }, componentDidMount: function() { this._isMounted = true; this.props.ajax({ url: 'rest/init', success: function(json, textStatus, jqXHR) { if (this._isMounted) { var corpora = new Corpora(json.corpora, this.updateCorpora); // // for testing aggregation context // json['x-aggregation-context'] = { // 'EKUT': ["http://hdl.handle.net/11858/00-1778-0000-0001-DDAF-D"] // }; var aggregationContext = json['x-aggregation-context'] || this.state.aggregationContext; window.MyAggregator.mode = getQueryVariable('mode') || json.mode; window.MyAggregator.corpora = json.corpora; window.MyAggregator.xAggregationContext = aggregationContext; // Setting visibility, e.g. only corpora // from v2.0 endpoints for fcs v2.0 corpora.setVisibility(this.state.queryTypeId, this.state.language[0]); if (aggregationContext) { const contextCorporaInfo = corpora.setAggregationContext(aggregationContext); const unavailableCorporaHandles = contextCorporaInfo.unavailable; // list of unavailable aggregationContext if (unavailableCorporaHandles.length > 0) { this.props.error("Could not find requested collection handles:\n" + unavailableCorporaHandles.join('\n')); } const actuallySelectedCorpora = corpora.getSelectedIds(); if (contextCorporaInfo.selected.length !== actuallySelectedCorpora.length) { if (actuallySelectedCorpora.length === 0) { this.props.error("This search does not support the required collection(s), will search all collections instead"); // TODO give detailed reason its not supported. corpora.recurse(function(corpus) { corpus.selected = true; }); } else { var err = "Some required context collections are not supported for this search:\n" err = err + contextCorpora.filter((c) => { if (actuallySelectedCorpora.indexOf(c) === -1) { console.warn("Requested corpus but not available for selection", c); return true; } return false; }).map((c) => c.title).join('\n') this.props.error(err); } } } else { // no context set all visibl to selected as default. console.log("no context set, selecting all available"); corpora.recurse(c => {c.visible ? c.selected=true : null}) } this.setState({ corpora : corpora, languageMap: json.languages, weblichtLanguages: json.weblichtLanguages, aggregationContext: aggregationContext, }, this.postInit); } else { console.warn("Got Aggregator init response, but not mounted!"); } }.bind(this), }); }, postInit() { if (window.MyAggregator.mode === 'search') { this.search(); } }, updateCorpora: function(corpora) { this.setState({corpora:corpora}); }, getCurrentQuery() { return this.state.queryTypeId === 'fcs' ? this.state.fcsQuery : this.state.cqlQuery; }, search() { var query = this.getCurrentQuery(); var queryTypeId = this.state.queryTypeId; if (!query || (this.props.embedded && window.MyAggregator.mode !== 'search')) { this.setState({ hits: this.nohits, searchId: null }); return; } var selectedIds = this.state.corpora.getSelectedIds(); if (!selectedIds.length) { this.props.error("Please select a collection to search into"); return; } // console.log("searching in the following corpora:", selectedIds); // console.log("searching with queryType:", queryTypeId); this.props.ajax({ url: 'rest/search', type: "POST", data: { query: query, queryType: queryTypeId, language: this.state.language[0], numberOfResults: this.state.numberOfResults, corporaIds: selectedIds, }, success: function(searchId, textStatus, jqXHR) { // console.log("search ["+query+"] ok: ", searchId, jqXHR); //Piwik.getAsyncTracker().trackSiteSearch(query, queryTypeId); // automatic inclusion of piwik in prod //console.log("location.hostname: " + location.hostname); if (location.hostname !== "localhost") { //console.log("location.host: " + location.host); _paq.push(['trackSiteSearch', query, queryTypeId, false]); } var timeout = 250; setTimeout(this.refreshSearchResults, timeout); this.setState({ searchId: searchId, timeout: timeout }); }.bind(this), }); }, nextResults: function(corpusId) { // console.log("searching next results in corpus:", corpusId); this.props.ajax({ url: 'rest/search/'+this.state.searchId, type: "POST", data: { corpusId: corpusId, numberOfResults: this.state.numberOfResults, }, success: function(searchId, textStatus, jqXHR) { // console.log("search ["+query+"] ok: ", searchId, jqXHR); var timeout = 250; setTimeout(this.refreshSearchResults, timeout); this.setState({ searchId: searchId, timeout: timeout }); }.bind(this), }); }, refreshSearchResults: function() { if (!this.state.searchId || !this._isMounted) { return; } this.props.ajax({ url: 'rest/search/'+this.state.searchId, success: function(json, textStatus, jqXHR) { var timeout = this.state.timeout; if (json.inProgress) { if (timeout < 10000) { timeout = 1.5 * timeout; } setTimeout(this.refreshSearchResults, timeout); // console.log("new search in: " + this.timeout + "ms"); } else { console.log("search ended; hits:", json); } var corpusHit = this.state.zoomedCorpusHit; if (corpusHit) { for (var resi = 0; resi < json.results.length; resi++) { var res = json.results[resi]; if (res.corpus.id === corpusHit.corpus.id) { corpusHit = res; break; } } } this.setState({ hits: json, timeout: timeout, zoomedCorpusHit: corpusHit}); }.bind(this), }); }, getExportParams: function(corpusId, format, filterLanguage) { var params = corpusId ? {corpusId:corpusId}:{}; if (format) params.format = format; if (filterLanguage) { params.filterLanguage = filterLanguage; } else if (this.state.languageFilter === 'byGuess' || this.state.languageFilter === 'byMetaAndGuess') { params.filterLanguage = this.state.language[0]; } return encodeQueryData(params); }, getDownloadLink: function(corpusId, format) { return 'rest/search/'+this.state.searchId+'/download?' + this.getExportParams(corpusId, format); }, getToWeblichtLink: function(corpusId, forceLanguage) { return 'rest/search/'+this.state.searchId+'/toWeblicht?' + this.getExportParams(corpusId, null, forceLanguage); }, setLanguageAndFilter: function(languageObj, languageFilter) { this.state.corpora.setVisibility(this.state.queryTypeId, languageFilter === 'byGuess' ? multipleLanguageCode : languageObj[0]); this.setState({ language: languageObj, languageFilter: languageFilter, corpora: this.state.corpora, // === this.state.corpora.update(); }); }, setQueryType: function(queryTypeId) { this.state.corpora.setVisibility(queryTypeId, this.state.language[0]); setQueryVariable('queryType', queryTypeId); setQueryVariable('query', queryTypeId === 'cql' ? this.state.cqlQuery : this.state.fcsQuery) this.setState({ queryTypeId: queryTypeId, hits: this.nohits, searchId: null, }); }, setNumberOfResults: function(e) { var n = e.target.value; if (n < 10) n = 10; if (n > 250) n = 250; this.setState({numberOfResults: n}); e.preventDefault(); e.stopPropagation(); }, stop: function(e) { e.stopPropagation(); }, filterResults: function() { var noLangFiltering = this.state.languageFilter === 'byMeta'; var langCode = this.state.language[0]; var results = null, inProgress = 0, hits = 0; if (this.state.hits.results) { results = this.state.hits.results.map(function(corpusHit) { return { corpus: corpusHit.corpus, inProgress: corpusHit.inProgress, exception: corpusHit.exception, diagnostics: corpusHit.diagnostics, kwics: noLangFiltering ? corpusHit.kwics : corpusHit.kwics.filter(function(kwic) { return kwic.language === langCode || langCode === multipleLanguageCode || langCode === null; }), advancedLayers: noLangFiltering ? corpusHit.advancedLayers : corpusHit.advancedLayers.filter(function(layer) { return layer.language === langCode || langCode === multipleLanguageCode || langCode === null; }), }; }); for (var i = 0; i < results.length; i++) { var result = results[i]; if (result.inProgress) { inProgress++; } if (result.kwics.length > 0) { hits ++; } } } return { results: results, hits: hits, inProgress: inProgress, }; }, toggleLanguageSelection: function(e) { $(ReactDOM.findDOMNode(this.refs.languageModal)).modal(); e.preventDefault(); e.stopPropagation(); }, toggleCorpusSelection: function(e) { $(ReactDOM.findDOMNode(this.refs.corporaModal)).modal(); e.preventDefault(); e.stopPropagation(); }, toggleResultModal: function(e, corpusHit) { $(ReactDOM.findDOMNode(this.refs.resultModal)).modal(); this.setState({zoomedCorpusHit: corpusHit}); e.preventDefault(); e.stopPropagation(); }, onQueryChange: function(queryStr) { if (this.state.queryTypeId === 'cql') { this.setState({ cqlQuery: queryStr || '', }); } else { this.setState({ fcsQuery: queryStr || '', }); } setQueryVariable('query', queryStr); }, handleKey: function(event) { if (event.keyCode==13) { this.search(); } }, renderZoomedResultTitle: function(corpusHit) { if (!corpusHit) return (); var corpus = corpusHit.corpus; return (

{corpus.title} { corpus.landingPage ? – Homepage : false}

); }, renderSearchButtonOrLink: function() { if (this.props.embedded) { var query = this.getCurrentQuery(); var queryTypeId = this.state.queryTypeId; var btnClass = classNames({ 'btn': true, 'btn-default': queryTypeId === 'cql', 'btn-primary': true, 'input-lg': true }); var newurl = !query ? "#" : (window.MyAggregator.URLROOT + (this.props.embedded ? "/embed" : "/") + "?" + encodeQueryData({queryType:queryTypeId, query:query, mode:'search'})); return ( ); } return ( ); }, renderQueryInput() { var queryType = queryTypeMap[this.state.queryTypeId]; return ( ); }, renderEmbed () { var queryType = queryTypeMap[this.state.queryTypeId]; return
{queryType.searchLabel} { this.renderQueryInput() }
{this.renderSearchButtonOrLink()}
}, renderGQB () { var queryType = queryTypeMap[this.state.queryTypeId]; return
{queryType.searchLabel}
{ this.renderQueryInput() }
{this.getCurrentQuery()}
{this.renderSearchButtonOrLink()}
}, renderUnavailableCorporaMessage() { if (!this.state.corpora) { return; } const unavailable = []; this.state.corpora.recurse((c) => { if (c.selected && ! c.visible) { unavailable.push(c); } if (c.selected) { // apparently a selected corpus } }); if (unavailable.length) { return } }, render: function() { var queryType = queryTypeMap[this.state.queryTypeId]; return (
{ (!this.props.embedded && this.state.queryTypeId == "fcs") ? this.renderGQB() : this.renderEmbed() }
{ //this.renderUnavailableCorporaMessage() }
Search for
    { queryTypes.map(function(l) { var cls = l.disabled ? 'disabled':''; var handler = function() { if (!l.disabled) this.setQueryType(l.id); }.bind(this); return (
  • {l.name}
  • ); }.bind(this)) }
in
and show up to
hits per endpoint
Collections {this.props.corpora && this.props.corpora.getSelectedMessage()}}> Select Language}>
); }, }); function Corpora(corpora, updateFn) { var that = this; this.corpora = corpora; this.update = function() { updateFn(that); }; var sortFn = function(x, y) { var r = x.institution.name.localeCompare(y.institution.name); if (r !== 0) { return r; } return x.title.toLowerCase().localeCompare(y.title.toLowerCase()); }; this.recurse(function(corpus) { corpus.subCorpora.sort(sortFn); }); this.corpora.sort(sortFn); this.recurse(function(corpus, index) { corpus.visible = true; // visible in the corpus view corpus.selected = false; // not selected in the corpus view, assign later corpus.expanded = false; // not expanded in the corpus view corpus.priority = 1; // used for ordering search results in corpus view corpus.index = index; // original order, used for stable sort }); } Corpora.prototype.recurseCorpus = function(corpus, fn) { if (false === fn(corpus)) { // no recursion } else { this.recurseCorpora(corpus.subCorpora, fn); } }; Corpora.prototype.recurseCorpora = function(corpora, fn) { var recfn = function(corpus, index){ if (false === fn(corpus, index)) { // no recursion } else { corpus.subCorpora.forEach(recfn); } }; corpora.forEach(recfn); }; Corpora.prototype.recurse = function(fn) { this.recurseCorpora(this.corpora, fn); }; Corpora.prototype.getLanguageCodes = function() { var languages = {}; this.recurse(function(corpus) { corpus.languages.forEach(function(lang) { languages[lang] = true; }); return true; }); return languages; }; Corpora.prototype.isCorpusVisible = function(corpus, queryTypeId, languageCode) { if (queryTypeId === "fcs" && (corpus.endpoint.protocol === "LEGACY" || corpus.endpoint.protocol === "VERSION_1")) { return false; } // yes for any language if (languageCode === multipleLanguageCode) { return true; } // yes if the corpus is in only that language if (corpus.languages && corpus.languages.length === 1 && corpus.languages[0] === languageCode) { return true; } // ? yes if the corpus also contains that language if (corpus.languages && corpus.languages.indexOf(languageCode) >=0) { return true; } // ? yes if the corpus has no language // if (!corpus.languages || corpus.languages.length === 0) { // return true; // } return false; }; Corpora.prototype.setVisibility = function(queryTypeId, languageCode) { // top level this.corpora.forEach(function(corpus) { corpus.visible = this.isCorpusVisible(corpus, queryTypeId, languageCode); this.recurseCorpora(corpus.subCorpora, function(c) { c.visible = corpus.visible; }); }.bind(this)); }; Corpora.prototype.setAggregationContext = function(endpoints2handles) { var selectSubTree = function(select, corpus) { corpus.selected = select; this.recurseCorpora(corpus.subCorpora, function(c) { c.selected = corpus.selected; }); }; this.corpora.forEach(selectSubTree.bind(this, false)); var handlesNotFound = []; var corporaToSelect = []; _.pairs(endpoints2handles).forEach((endp) => { var endpoint = endp[0]; var handles = endp[1]; console.log('setAggregationContext: endpoint', endpoint); console.log('setAggregationContext: handles', handles); handles.forEach((handle) => { var found = false; this.recurse((corpus) => { if (corpus.handle === handle) { found = true; corporaToSelect.push(corpus); } }) if (!found) { console.warn("Handle not found in corpora", handle); handlesNotFound.push(handle); } }) }) corporaToSelect.forEach(selectSubTree.bind(this, true)); return {'selected': corporaToSelect, 'unavailable': handlesNotFound}; }; Corpora.prototype.getSelectedIds = function() { var ids = []; this.recurse(function(corpus) { if (corpus.visible && corpus.selected) { ids.push(corpus.id); //eturn false; // top-most collection in tree, don't delve deeper // But subcollections are also selectable on their own?... } return true; }); // console.log("ids: ", ids.length, {ids:ids}); return ids; }; Corpora.prototype.getSelectedMessage = function() { var selected = this.getSelectedIds().length; if (this.corpora.length === selected) { return "All available collections (" + selected + ")"; } else if (selected === 1) { return "1 selected collection"; } return selected + " selected collections"; }; function getQueryVariable(variable) { var query = window.location.search.substring(1); var vars = query.split('&'); for (var i = 0; i < vars.length; i++) { var pair = vars[i].split('='); if (decodeURIComponent(pair[0]) == variable) { console.log("variable found: (", variable, ") = ", decodeURIComponent(pair[1])); return decodeURIComponent(pair[1]); } } return null; } /* setter opposite of getQueryVariable*/ function setQueryVariable(qvar, value) { var query = window.location.search.substring(1); var vars = query.split('&'); var d = {}; d[qvar] = value; var found = false; for (var i = 0; i < vars.length; i++) { var pair = vars[i].split('='); if (decodeURIComponent(pair[0]) === qvar) { vars[i] = encodeQueryData(d); found=true; break; } } if (!found) { // add to end of url vars.push(encodeQueryData(d)); } var searchPart = vars.join('&'); var newUrl = window.location.origin + window.location.pathname+'?'+searchPart; console.log("set url", newUrl); window.history.replaceState(window.history.state, null, newUrl); } function encodeQueryData(data) { var ret = []; for (var d in data) { ret.push(encodeURIComponent(d) + "=" + encodeURIComponent(data[d])); } return ret.join("&"); } var queryTypes = [ { id: "cql", name: "Text layer Contextual Query Language (CQL)", searchPlaceholder: "Elephant", searchLabel: "Text layer CQL query", searchLabelBkColor: "#fed", className: '', }, { id: "fcs", name: "Multi-layer Federated Content Search Query Language (FCS-QL)", searchPlaceholder: "[word = 'annotation'][word = 'focused']", searchLabel: "Multi-layer FCS query", searchLabelBkColor: "#efd", disabled: false, }, ]; var queryTypeMap = { cql: queryTypes[0], fcs: queryTypes[1], }; module.exports = AggregatorPage;