Changeset 5931 for SRUAggregator
- Timestamp:
- 01/16/15 19:24:55 (9 years ago)
- Location:
- SRUAggregator/trunk
- Files:
-
- 16 edited
Legend:
- Unmodified
- Added
- Removed
-
SRUAggregator/trunk/pom.xml
r5919 r5931 8 8 <groupId>eu.clarin.sru.fcs</groupId> 9 9 <artifactId>Aggregator2</artifactId> 10 <version>2.0.0-alpha-1 1</version>10 <version>2.0.0-alpha-12</version> 11 11 <name>FCS Aggregator</name> 12 12 -
SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/app/Aggregator.java
r5919 r5931 86 86 * @author edima 87 87 * 88 * TODO: try to refine results by language using a language library, with UI89 * element90 *91 * TODO: condensed list of corpora92 *93 * TODO: group the list of corpora by institution?94 *95 88 * TODO: fix ordering of corpora in corpora view 96 89 * 97 90 * TODO: corpora search should not indicate the ones that don't match 98 *99 * TODO: Collections view: home link (make a single consistent text for it)100 91 * 101 92 * TODO: tri-state for parent collections; search + message implications … … 267 258 268 259 // this function should be thread-safe 269 public Search startSearch(SRUVersion version, List<Corpus> corpora, String searchString, String searchLang, int maxRecords) throws Exception { 260 public Search startSearch(SRUVersion version, List<Corpus> corpora, 261 String searchString, String searchLang, int maxRecords) throws Exception { 270 262 if (corpora.isEmpty()) { 271 263 // No corpora -
SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/lang/LanguagesISO693_3.java
r5900 r5931 28 28 public static class Language { 29 29 30 String code, name; 30 // code is ISO-639-3 (3 letters) while code_2 is ISO-639-2 (2 letters) 31 String code, code_2, name; 31 32 32 public Language(String code, String name) {33 public Language(String code, String code_2, String name) { 33 34 this.code = code; 35 this.code_2 = code_2; 34 36 this.name = name; 35 37 } 36 38 } 37 39 38 private Map<String, Language> code2Lang = new HashMap<String, Language>(); 39 private Map<String, Language> name2Lang = new HashMap<String, Language>(); 40 private Map<String, Language> codeToLang = new HashMap<String, Language>(); 41 private Map<String, Language> nameToLang = new HashMap<String, Language>(); 42 private Map<String, Language> code_2ToLang = new HashMap<String, Language>(); 40 43 41 44 private LanguagesISO693_3() { 42 45 InputStream is = LanguagesISO693_3.class.getResourceAsStream(LANGUAGES_FILE_PATH); 43 46 try (BufferedReader br = new BufferedReader(new InputStreamReader(is, LANGUAGES_FILE_ENCODING))) { 44 String line ;47 String line = br.readLine(); // ignore first line 45 48 while ((line = br.readLine()) != null) { 46 49 if (line.length() > 0) { … … 50 53 continue; 51 54 } 52 String code = toks[0]; 53 String name = toks[6]; 54 Language l = new Language(code, name); 55 code2Lang.put(code, l); 56 name2Lang.put(name, l); 55 String code = toks[0].trim(); 56 String code_2 = toks[3].trim().isEmpty() ? null : toks[3].trim(); 57 if (code_2 != null && code_2.length() != 2) { 58 throw new RuntimeException("bad code_2 code: " + code_2); 59 } 60 String name = toks[6].trim(); 61 Language l = new Language(code, code_2, name); 62 codeToLang.put(code, l); 63 if (code_2 != null) { 64 code_2ToLang.put(code_2, l); 65 } 66 nameToLang.put(name, l); 57 67 } 58 68 } … … 63 73 ObjectWriter ow = new ObjectMapper().writerWithDefaultPrettyPrinter(); 64 74 try { 65 System.out.println(ow.writeValueAsString(code 2Lang));75 System.out.println(ow.writeValueAsString(codeToLang)); 66 76 } catch (JsonProcessingException ex) { 67 77 } … … 76 86 77 87 public Set<String> getCodes() { 78 return code2Lang.keySet(); 88 return codeToLang.keySet(); 89 } 90 91 public String codeForCode639_2(String code639_2) { 92 if (code639_2 == null) { 93 return null; 94 } 95 Language l = code_2ToLang.get(code639_2); 96 if (l == null) { 97 log.error("Unknown 639-2 code: " + code639_2); 98 return null; 99 } 100 return l.code; 79 101 } 80 102 81 103 public String codeForName(String name) { 82 Language l = name 2Lang.get(name);104 Language l = nameToLang.get(name); 83 105 if (l == null) { 84 106 log.error("Unknown language name: " + name); … … 89 111 90 112 public String nameForCode(String code) { 91 Language l = code 2Lang.get(code);113 Language l = codeToLang.get(code); 92 114 if (l == null) { 93 115 log.error("Unknown language code: " + code); -
SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/rest/RestService.java
r5901 r5931 120 120 @FormParam("numberOfResults") Integer numberOfResults, 121 121 @FormParam("language") String language, 122 @FormParam("useLanguageGuesser") boolean useLanguageGuesser, 122 123 @FormParam("corporaIds[]") List<String> corporaIds) throws Exception { 123 124 if (query == null || query.isEmpty()) { … … 137 138 if (numberOfResults > 250) { 138 139 numberOfResults = 250; 139 }140 for (String c : corporaIds) {141 142 140 } 143 141 Search search = Aggregator.getInstance().startSearch(SRUVersion.VERSION_1_2, corpora, query, language, numberOfResults); -
SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/search/Kwic.java
r5919 r5931 3 3 import eu.clarin.sru.client.fcs.DataViewHits; 4 4 import eu.clarin.sru.fcs.aggregator.app.Aggregator; 5 import eu.clarin.sru.fcs.aggregator.lang.LanguagesISO693_3; 5 6 import java.util.ArrayList; 6 7 import java.util.List; … … 63 64 } 64 65 65 language = Aggregator.getInstance().detectLanguage(hits.getText()); 66 String code_iso639_2 = Aggregator.getInstance().detectLanguage(hits.getText()); 67 language = code_iso639_2 == null ? null 68 : LanguagesISO693_3.getInstance().codeForCode639_2(code_iso639_2); 66 69 } 67 70 -
SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/search/Result.java
r5893 r5931 37 37 } 38 38 39 public Result(Request request, SRUSearchRetrieveResponse response, SRUClientException xc) { 39 public Result(Request request, SRUSearchRetrieveResponse response, 40 SRUClientException xc) { 40 41 this.request = request; 41 42 this.exception = xc; -
SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/search/Search.java
r5900 r5931 41 41 42 42 public Search(ThrottledClient searchClient, SRUVersion version, 43 Statistics statistics, List<Corpus> corpora, 44 String search String, String searchLanguage, int startRecord, int maxRecords43 Statistics statistics, List<Corpus> corpora, String searchString, 44 String searchLanguage, int startRecord, int maxRecords 45 45 ) { 46 46 this.id = counter.getAndIncrement(); -
SRUAggregator/trunk/src/main/resources/assets/CLARIN.css
r5784 r5931 23 23 } 24 24 a:visited { 25 color: #c0a37c;25 /*color: #c0a37c;*/ 26 26 } 27 27 -
SRUAggregator/trunk/src/main/resources/assets/base.css
r5919 r5931 13 13 14 14 .unselectable { 15 -webkit-touch-callout: none; 15 -ms-touch-action: manipulation; 16 touch-action: manipulation; 17 cursor: pointer; 16 18 -webkit-user-select: none; 17 -khtml-user-select: none;18 19 -moz-user-select: none; 19 20 -ms-user-select: none; 20 21 user-select: none; 22 -webkit-touch-callout: none; 23 -khtml-user-select: none; 21 24 } 22 25 … … 181 184 182 185 div.corpus-container { 183 margin: 10px 0; 186 margin: 5px 0; 187 border-bottom: 1px solid #eee; 184 188 } 185 189 div.corpus-container:hover { … … 196 200 197 201 div.corpus h3 { 198 margin -top: 10px;202 margin: 0px 0 5px 0; 199 203 font-size: 18px; 204 } 205 206 div.corpus p { 207 margin: 0; 200 208 } 201 209 202 210 div.corpus button { 203 211 /*border: none;*/ 212 } 213 214 div.corpus .glyphicon { 215 font-size: 12px; 216 color: rgb(128,107,82); /*gold*/ 217 218 -ms-touch-action: manipulation; 219 touch-action: manipulation; 220 cursor: pointer; 221 -webkit-user-select: none; 222 -moz-user-select: none; 223 -ms-user-select: none; 224 user-select: none; 204 225 } 205 226 … … 222 243 223 244 div.corpus div.expansion-handle { 224 margin: 5px 0;245 margin: 0 0 5px 0; 225 246 cursor: pointer; 226 247 } … … 278 299 color: #00406F; 279 300 font-weight: bold; 280 font-size: 12px;281 301 font-weight: bold; 282 302 white-space: nowrap; -
SRUAggregator/trunk/src/main/resources/assets/index.html
r5919 r5931 46 46 <a title="about" id="aboutlink" href="about"> 47 47 <span class="glyphicon glyphicon-info-sign"></span> 48 <span>VERSION 2.0.0.α1 1</span>48 <span>VERSION 2.0.0.α12</span> 49 49 </a> 50 50 </div> -
SRUAggregator/trunk/src/main/resources/assets/js/components.js
r5897 r5931 109 109 110 110 111 var PopoverMixin = {111 var PopoverMixin = window.MyReact.PopoverMixin = { 112 112 getDefaultProps: function(){ 113 113 return {hasPopover: true}; … … 115 115 116 116 componentDidMount: function() { 117 this.refresh(); 118 }, 119 componentDidUpdate: function() { 120 this.refresh(); 121 }, 122 123 refresh: function() { 124 $(this.getDOMNode()).popover('destroy'); 125 117 126 var content; 118 127 if (Array.isArray(this.props.children)) … … 120 129 else 121 130 content = React.renderToString(this.props.children); 131 // console.log("children: ", this.props.children); 132 // console.log("content: ", content); 122 133 $(this.getDOMNode()).popover({ 123 134 content: content, … … 128 139 html: true, 129 140 }); 130 } 141 }, 142 143 componentWillUnmount: function() { 144 $(this.getDOMNode()).popover('destroy'); 145 }, 131 146 }; 147 148 window.MyReact.Popover = React.createClass({displayName: 'Popover', 149 propTypes: { 150 placement: PT.string, 151 title: PT.string, 152 triggerButtonClass: PT.string, 153 triggerButtonContent: PT.element.isRequired 154 }, 155 mixins: [PopoverMixin], 156 157 handleClick: function(e) { 158 e.stopPropagation(); 159 }, 160 161 render: function() { 162 return React.createElement("button", {className: this.props.triggerButtonClass, onClick: this.handleClick}, 163 this.props.triggerButtonContent 164 ); 165 } 166 }); 132 167 133 168 window.MyReact.InfoPopover = React.createClass({displayName: 'InfoPopover', -
SRUAggregator/trunk/src/main/resources/assets/js/components.jsx
r5897 r5931 109 109 110 110 111 var PopoverMixin = {111 var PopoverMixin = window.MyReact.PopoverMixin = { 112 112 getDefaultProps: function(){ 113 113 return {hasPopover: true}; … … 115 115 116 116 componentDidMount: function() { 117 this.refresh(); 118 }, 119 componentDidUpdate: function() { 120 this.refresh(); 121 }, 122 123 refresh: function() { 124 $(this.getDOMNode()).popover('destroy'); 125 117 126 var content; 118 127 if (Array.isArray(this.props.children)) … … 120 129 else 121 130 content = React.renderToString(this.props.children); 131 // console.log("children: ", this.props.children); 132 // console.log("content: ", content); 122 133 $(this.getDOMNode()).popover({ 123 134 content: content, … … 128 139 html: true, 129 140 }); 130 } 141 }, 142 143 componentWillUnmount: function() { 144 $(this.getDOMNode()).popover('destroy'); 145 }, 131 146 }; 147 148 window.MyReact.Popover = React.createClass({ 149 propTypes: { 150 placement: PT.string, 151 title: PT.string, 152 triggerButtonClass: PT.string, 153 triggerButtonContent: PT.element.isRequired 154 }, 155 mixins: [PopoverMixin], 156 157 handleClick: function(e) { 158 e.stopPropagation(); 159 }, 160 161 render: function() { 162 return <button className={this.props.triggerButtonClass} onClick={this.handleClick}> 163 {this.props.triggerButtonContent} 164 </button>; 165 } 166 }); 132 167 133 168 window.MyReact.InfoPopover = React.createClass({ -
SRUAggregator/trunk/src/main/resources/assets/js/corpora.js
r5919 r5931 161 161 return false; 162 162 } 163 return React.createElement("div", {className: "expansion-handle", onClick: this.toggleExpansion.bind(this,corpus)}, 164 React.createElement("a", null, " ", corpus.expanded ? 165 React.createElement("span", {className: "glyphicon glyphicon-collapse-down", 'aria-hidden': "true"}): 166 React.createElement("span", {className: "glyphicon glyphicon-expand", 'aria-hidden': "true"}), 163 return React.createElement("div", {className: "expansion-handle", style: {}}, 164 React.createElement("a", null, 165 corpus.expanded ? 166 React.createElement("span", {className: "glyphicon glyphicon-minus", 'aria-hidden': "true"}): 167 React.createElement("span", {className: "glyphicon glyphicon-plus", 'aria-hidden': "true"}), 167 168 168 169 corpus.expanded ? " Collapse ":" Expand ", " ", corpus.subCorpora.length, " subcollections" … … 175 176 .map(function(l) { return this.props.languageMap[l]; }.bind(this)) 176 177 .sort() 177 .join(" ");178 .join(", "); 178 179 }, 179 180 … … 190 191 var color = minmaxp[0] === minmaxp[1] ? 'transparent' : 'hsl('+hue+', 50%, 50%)'; 191 192 var priorityStyle = {paddingBottom: 4, paddingLeft: 2, borderBottom: '2px solid '+color }; 193 var expansive = corpus.expanded ? {} 194 : {whiteSpace:'nowrap', overflow:'hidden', textOverflow: 'ellipsis'}; 195 var title = corpus.title || corpus.displayName; 192 196 return React.createElement("div", {className: corpusContainerClass, key: corpus.displayName}, 193 React.createElement("div", {className: "row corpus" },194 React.createElement("div", {className: "col-sm-1 vcenter" , onClick: this.toggleSelection.bind(this,corpus)},195 React.createElement("div", {style: priorityStyle},196 this.renderCheckbox(corpus)197 )197 React.createElement("div", {className: "row corpus", onClick: this.toggleExpansion.bind(this, corpus)}, 198 React.createElement("div", {className: "col-sm-1 vcenter"}, 199 React.createElement("div", {className: "inline", style: priorityStyle, onClick: this.toggleSelection.bind(this,corpus)}, 200 this.renderCheckbox(corpus) 201 ) 198 202 ), 199 203 React.createElement("div", {className: "col-sm-8 vcenter"}, 200 204 React.createElement("div", {style: indent}, 201 React.createElement("h3", null, corpus.title ? corpus.title : corpus.displayName, " "), 202 React.createElement("p", null, corpus.description), 203 this.renderExpansion(corpus) 204 ) 205 React.createElement("h3", {style: expansive}, 206 corpus.landingPage ? React.createElement("a", {href: corpus.landingPage}, title): title 207 ), 208 209 React.createElement("p", {style: expansive}, corpus.description) 210 ), 211 this.renderExpansion(corpus) 205 212 ), 206 213 React.createElement("div", {className: "col-sm-3 vcenter"}, 207 React.createElement("p", null, React.createElement("i", {className: "fa fa-institution"}), " ", corpus.institution.name), 208 React.createElement("p", null, React.createElement("i", {className: "fa fa-language"}), " ", this.renderLanguages(corpus.languages)), 209 corpus.landingPage ? 210 React.createElement("p", null, React.createElement("i", {className: "fa fa-home"}), " ", React.createElement("a", {href: corpus.landingPage}, corpus.landingPage)) : 211 false 214 React.createElement("p", {style: expansive}, 215 React.createElement("i", {className: "fa fa-institution"}), " ", corpus.institution.name 216 ), 217 React.createElement("p", {style: expansive}, 218 React.createElement("i", {className: "fa fa-language"}), " ", this.renderLanguages(corpus.languages) 219 ) 212 220 ) 213 221 ), -
SRUAggregator/trunk/src/main/resources/assets/js/corpora.jsx
r5919 r5931 161 161 return false; 162 162 } 163 return <div className="expansion-handle" onClick={this.toggleExpansion.bind(this,corpus)}> 164 <a> {corpus.expanded ? 165 <span className="glyphicon glyphicon-collapse-down" aria-hidden="true"/>: 166 <span className="glyphicon glyphicon-expand" aria-hidden="true"/> 163 return <div className="expansion-handle" style={{}}> 164 <a> 165 {corpus.expanded ? 166 <span className="glyphicon glyphicon-minus" aria-hidden="true"/>: 167 <span className="glyphicon glyphicon-plus" aria-hidden="true"/> 167 168 } 168 169 {corpus.expanded ? " Collapse ":" Expand "} {corpus.subCorpora.length} subcollections … … 175 176 .map(function(l) { return this.props.languageMap[l]; }.bind(this)) 176 177 .sort() 177 .join(" ");178 .join(", "); 178 179 }, 179 180 … … 190 191 var color = minmaxp[0] === minmaxp[1] ? 'transparent' : 'hsl('+hue+', 50%, 50%)'; 191 192 var priorityStyle = {paddingBottom: 4, paddingLeft: 2, borderBottom: '2px solid '+color }; 193 var expansive = corpus.expanded ? {} 194 : {whiteSpace:'nowrap', overflow:'hidden', textOverflow: 'ellipsis'}; 195 var title = corpus.title || corpus.displayName; 192 196 return <div className={corpusContainerClass} key={corpus.displayName}> 193 <div className="row corpus" >194 <div className="col-sm-1 vcenter" onClick={this.toggleSelection.bind(this,corpus)}>195 <div style={priorityStyle}>196 {this.renderCheckbox(corpus)}197 </div>197 <div className="row corpus" onClick={this.toggleExpansion.bind(this, corpus)}> 198 <div className="col-sm-1 vcenter"> 199 <div className="inline" style={priorityStyle} onClick={this.toggleSelection.bind(this,corpus)}> 200 {this.renderCheckbox(corpus)} 201 </div> 198 202 </div> 199 203 <div className="col-sm-8 vcenter"> 200 204 <div style={indent}> 201 <h3>{corpus.title ? corpus.title : corpus.displayName} </h3> 202 <p>{corpus.description}</p> 203 {this.renderExpansion(corpus)} 205 <h3 style={expansive}> 206 { corpus.landingPage ? <a href={corpus.landingPage}>{title}</a>: title } 207 </h3> 208 209 <p style={expansive}>{corpus.description}</p> 204 210 </div> 211 {this.renderExpansion(corpus)} 205 212 </div> 206 213 <div className="col-sm-3 vcenter"> 207 <p><i className="fa fa-institution"/> {corpus.institution.name}</p> 208 <p><i className="fa fa-language"/> {this.renderLanguages(corpus.languages)}</p> 209 { corpus.landingPage ? 210 <p><i className="fa fa-home"/> <a href={corpus.landingPage}>{corpus.landingPage}</a></p> : 211 false } 214 <p style={expansive}> 215 <i className="fa fa-institution"/> {corpus.institution.name} 216 </p> 217 <p style={expansive}> 218 <i className="fa fa-language"/> {this.renderLanguages(corpus.languages)} 219 </p> 212 220 </div> 213 221 </div> -
SRUAggregator/trunk/src/main/resources/assets/js/search.js
r5919 r5931 12 12 var HitNumber = window.MyAggregator.HitNumber; 13 13 var CorpusView = window.MyAggregator.CorpusView; 14 var Popover = window.MyReact.Popover; 14 15 var InfoPopover = window.MyReact.InfoPopover; 15 16 var Panel = window.MyReact.Panel; … … 165 166 }, 166 167 167 mixins: [React.addons.LinkedStateMixin],168 168 timeout: 0, 169 169 nohits: { … … 178 178 languageMap: {}, 179 179 language: this.anyLanguage, 180 languageFilter: 'byMeta', 180 181 searchLayerId: "text", 181 182 numberOfResults: 10, … … 260 261 }, 261 262 262 setLanguage: function(languageObj) { 263 this.state.corpora.setVisibility(this.state.searchLayerId, languageObj[0]); 264 this.setState({language: languageObj}); 263 setLanguageAndFilter: function(languageObj, languageFilter) { 264 this.state.corpora.setVisibility(this.state.searchLayerId, 265 languageFilter === 'byGuess' ? multipleLanguageCode : languageObj[0]); 266 this.setState({language: languageObj, languageFilter: languageFilter}); 265 267 this.state.corpora.update(); 266 268 }, … … 282 284 283 285 stop: function(e) { 286 e.preventDefault(); 287 e.stopPropagation(); 288 }, 289 290 filterResults: function() { 291 var langCode = this.state.language[0]; 292 return this.state.hits.results.map(function(corpusHit) { 293 return { 294 corpus: corpusHit.corpus, 295 startRecord: corpusHit.startRecord, 296 endRecord: corpusHit.endRecord, 297 exception: corpusHit.exception, 298 searchString: corpusHit.searchString, 299 kwics: corpusHit.kwics.filter(function(kwic){ 300 return kwic.language === langCode || langCode === multipleLanguageCode || langCode === null; 301 }), 302 }; 303 }); 304 }, 305 306 toggleLanguageSelection: function(e) { 307 $(this.refs.languageModal.getDOMNode()).modal(); 284 308 e.preventDefault(); 285 309 e.stopPropagation(); … … 323 347 React.createElement("div", {className: "input-group-btn"}, 324 348 React.createElement("button", {className: "form-control btn btn-default", 325 'aria-expanded': "false", 'data-toggle': "dropdown"},349 onClick: this.toggleLanguageSelection}, 326 350 this.state.language[1], " ", React.createElement("span", {className: "caret"}) 327 351 ), 328 React.createElement("ul", {ref: "languageDropdownMenu", className: "dropdown-menu"}, 329 React.createElement("li", {key: this.anyLanguage[0]}, " ", React.createElement("a", {tabIndex: "-1", href: "#", 330 onClick: this.setLanguage.bind(this, this.anyLanguage)}, 331 this.anyLanguage[1]) 332 ), 333 _.pairs(this.state.languageMap).sort(function(l1, l2){ 334 return l1[1].localeCompare(l2[1]); 335 }).map(function(l) { 336 var desc = l[1] + " [" + l[0] + "]"; 337 return React.createElement("li", {key: l[0]}, " ", React.createElement("a", {tabIndex: "-1", href: "#", 338 onClick: this.setLanguage.bind(this, l)}, desc)); 339 }.bind(this)) 340 341 ) 352 React.createElement("span", null) 342 353 ), 343 354 … … 360 371 React.createElement("div", {className: "input-group"}, 361 372 React.createElement("span", {className: "input-group-addon nobkg"}, "in"), 362 363 364 373 React.createElement("button", {type: "button", className: "btn btn-default", onClick: this.toggleCorpusSelection}, 374 this.state.corpora.getSelectedMessage(), " ", React.createElement("span", {className: "caret"}) 375 ) 365 376 ), 366 377 … … 383 394 ), 384 395 396 React.createElement(Modal, {ref: "languageModal", title: "Select Language"}, 397 React.createElement(LanguageSelector, {anyLanguage: this.anyLanguage, 398 languageMap: this.state.languageMap, 399 selectedLanguage: this.state.language, 400 languageFilter: this.state.languageFilter, 401 languageChangeHandler: this.setLanguageAndFilter}) 402 ), 403 385 404 React.createElement("div", {className: "top-gap"}, 386 React.createElement(Results, {requests: this.state.hits.requests, results: this.state.hits.results}) 405 React.createElement(Results, {requests: this.state.hits.requests, 406 results: this.filterResults(), 407 searchedLanguage: this.state.language}) 387 408 ) 388 409 ) … … 396 417 397 418 419 ///////////////////////////////// 420 421 var LanguageSelector = React.createClass({displayName: 'LanguageSelector', 422 propTypes: { 423 anyLanguage: PT.array.isRequired, 424 languageMap: PT.object.isRequired, 425 selectedLanguage: PT.array.isRequired, 426 languageFilter: PT.string.isRequired, 427 languageChangeHandler: PT.func.isRequired, 428 }, 429 mixins: [React.addons.LinkedStateMixin], 430 431 selectLang: function(language) { 432 this.props.languageChangeHandler(language, this.props.languageFilter); 433 }, 434 435 setFilter: function(filter) { 436 this.props.languageChangeHandler(this.props.selectedLanguage, filter); 437 }, 438 439 renderLanguageObject: function(lang) { 440 var desc = lang[1] + " [" + lang[0] + "]"; 441 var style = { 442 whiteSpace: "nowrap", 443 fontWeight: lang[0] === this.props.selectedLanguage[0] ? "bold":"normal", 444 }; 445 return React.createElement("div", {key: lang[0]}, 446 React.createElement("a", {tabIndex: "-1", href: "#", style: style, onClick: this.selectLang.bind(this, lang)}, desc) 447 ); 448 }, 449 450 renderRadio: function(option) { 451 return this.props.languageFilter === option ? 452 React.createElement("input", {type: "radio", name: "filterOpts", value: option, checked: true, onChange: this.setFilter.bind(this, option)}) 453 : React.createElement("input", {type: "radio", name: "filterOpts", value: option, onChange: this.setFilter.bind(this, option)}); 454 }, 455 456 render: function() { 457 var languages = _.pairs(this.props.languageMap) 458 .sort(function(l1, l2){return l1[1].localeCompare(l2[1]); }); 459 languages.unshift(this.props.anyLanguage); 460 languages = languages.map(this.renderLanguageObject); 461 var third = Math.round(languages.length/3); 462 var l1 = languages.slice(0, third); 463 var l2 = languages.slice(third, 2*third); 464 var l3 = languages.slice(2*third, languages.length); 465 466 return React.createElement("div", null, 467 React.createElement("div", {className: "row"}, 468 React.createElement("div", {className: "col-sm-4"}, l1), 469 React.createElement("div", {className: "col-sm-4"}, l2), 470 React.createElement("div", {className: "col-sm-4"}, l3), 471 React.createElement("div", {className: "col-sm-12", style: {marginTop:10, marginBottom:10, borderBottom:"1px solid #eee"}}) 472 ), 473 React.createElement("form", {className: "form", role: "form"}, 474 React.createElement("div", {className: "input-group"}, 475 React.createElement("div", null, 476 React.createElement("label", {style: {color:'black'}}, 477 this.renderRadio('byMeta'), " ", 478 "Use the collections", "'", " specified language to filter results" 479 ) 480 ), 481 React.createElement("div", null, 482 React.createElement("label", {style: {color:'black'}}, 483 this.renderRadio('byGuess'), " ", 484 "Filter results by using a language detector" 485 ) 486 ), 487 React.createElement("div", null, 488 React.createElement("label", {style: {color:'black'}}, 489 this.renderRadio('byMetaAndGuess'), " ", 490 "First use the collections", "'", " specified language then also use a language detector" 491 ) 492 ) 493 ) 494 ) 495 ); 496 } 497 }); 398 498 ///////////////////////////////// 399 499 … … 442 542 requests: PT.array.isRequired, 443 543 results: PT.array.isRequired, 544 searchedLanguage: PT.array.isRequired, 444 545 }, 445 546 446 547 getInitialState: function () { 447 return { displayKwic: false }; 548 return { 549 displayKwic: false, 550 }; 448 551 }, 449 552 … … 453 556 454 557 renderRowLanguage: function(hit) { 455 return React.createElement("span", {style: {fontFace:"Courier",color:"black"}}, hit.language );558 return React.createElement("span", {style: {fontFace:"Courier",color:"black"}}, hit.language, " ") ; 456 559 }, 457 560 … … 467 570 468 571 renderRowsAsKwic: function(hit,i) { 469 var sleft={textAlign:"left", verticalAlign:" middle", width:"50%"};470 var scenter={textAlign:"center", verticalAlign:" middle", maxWidth:"50%"};471 var sright={textAlign:"right", verticalAlign:" middle", maxWidth:"50%"};572 var sleft={textAlign:"left", verticalAlign:"top", width:"50%"}; 573 var scenter={textAlign:"center", verticalAlign:"top", maxWidth:"50%"}; 574 var sright={textAlign:"right", verticalAlign:"top", maxWidth:"50%"}; 472 575 return React.createElement("tr", {key: i, className: "hitrow"}, 473 576 React.createElement("td", null, this.renderRowLanguage(hit)), … … 565 668 566 669 renderKwicCheckbox: function() { 567 return React.createElement("div", {key: "-option-KWIC-", className: "row"}, 568 React.createElement("div", {className: "float-right", style: {marginRight:17}}, 569 React.createElement("div", {className: "btn-group", style: {display:"inline-block"}}, 570 React.createElement("label", {forHtml: "inputKwic", className: "btn-default"}, 571 this.state.displayKwic ? 572 React.createElement("input", {id: "inputKwic", type: "checkbox", value: "kwic", checked: true, onChange: this.toggleKwic}) : 573 React.createElement("input", {id: "inputKwic", type: "checkbox", value: "kwic", onChange: this.toggleKwic}), 574 575 "Â " + ' ' + 576 "Display as Key Word In Context" 577 ) 670 return React.createElement("div", {className: "float-right", style: {marginRight:17}}, 671 React.createElement("div", {className: "btn-group", style: {display:"inline-block"}}, 672 React.createElement("label", {forHtml: "inputKwic", className: "btn-default"}, 673 this.state.displayKwic ? 674 React.createElement("input", {id: "inputKwic", type: "checkbox", value: "kwic", checked: true, onChange: this.toggleKwic}) : 675 React.createElement("input", {id: "inputKwic", type: "checkbox", value: "kwic", onChange: this.toggleKwic}), 676 677 "Â " + ' ' + 678 "Display as Key Word In Context" 578 679 ) 579 680 ) … … 592 693 React.createElement("div", {key: "-found-message-", style: margintop}, this.renderFoundMessage(hits), " "), 593 694 React.createElement("div", {key: "-progress-", style: margintop}, this.renderProgressBar()), 594 hits > 0 ? this.renderKwicCheckbox() : false, 695 hits > 0 ? 696 React.createElement("div", {key: "-option-KWIC-", className: "row"}, 697 this.renderKwicCheckbox() 698 ) 699 : false, 595 700 this.props.results.map(this.renderResultPanels) 596 701 ) -
SRUAggregator/trunk/src/main/resources/assets/js/search.jsx
r5919 r5931 12 12 var HitNumber = window.MyAggregator.HitNumber; 13 13 var CorpusView = window.MyAggregator.CorpusView; 14 var Popover = window.MyReact.Popover; 14 15 var InfoPopover = window.MyReact.InfoPopover; 15 16 var Panel = window.MyReact.Panel; … … 165 166 }, 166 167 167 mixins: [React.addons.LinkedStateMixin],168 168 timeout: 0, 169 169 nohits: { … … 178 178 languageMap: {}, 179 179 language: this.anyLanguage, 180 languageFilter: 'byMeta', 180 181 searchLayerId: "text", 181 182 numberOfResults: 10, … … 260 261 }, 261 262 262 setLanguage: function(languageObj) { 263 this.state.corpora.setVisibility(this.state.searchLayerId, languageObj[0]); 264 this.setState({language: languageObj}); 263 setLanguageAndFilter: function(languageObj, languageFilter) { 264 this.state.corpora.setVisibility(this.state.searchLayerId, 265 languageFilter === 'byGuess' ? multipleLanguageCode : languageObj[0]); 266 this.setState({language: languageObj, languageFilter: languageFilter}); 265 267 this.state.corpora.update(); 266 268 }, … … 282 284 283 285 stop: function(e) { 286 e.preventDefault(); 287 e.stopPropagation(); 288 }, 289 290 filterResults: function() { 291 var langCode = this.state.language[0]; 292 return this.state.hits.results.map(function(corpusHit) { 293 return { 294 corpus: corpusHit.corpus, 295 startRecord: corpusHit.startRecord, 296 endRecord: corpusHit.endRecord, 297 exception: corpusHit.exception, 298 searchString: corpusHit.searchString, 299 kwics: corpusHit.kwics.filter(function(kwic){ 300 return kwic.language === langCode || langCode === multipleLanguageCode || langCode === null; 301 }), 302 }; 303 }); 304 }, 305 306 toggleLanguageSelection: function(e) { 307 $(this.refs.languageModal.getDOMNode()).modal(); 284 308 e.preventDefault(); 285 309 e.stopPropagation(); … … 323 347 <div className="input-group-btn"> 324 348 <button className="form-control btn btn-default" 325 aria-expanded="false" data-toggle="dropdown">349 onClick={this.toggleLanguageSelection}> 326 350 {this.state.language[1]} <span className="caret"/> 327 351 </button> 328 <ul ref="languageDropdownMenu" className="dropdown-menu"> 329 <li key={this.anyLanguage[0]}> <a tabIndex="-1" href="#" 330 onClick={this.setLanguage.bind(this, this.anyLanguage)}> 331 {this.anyLanguage[1]}</a> 332 </li> 333 { _.pairs(this.state.languageMap).sort(function(l1, l2){ 334 return l1[1].localeCompare(l2[1]); 335 }).map(function(l) { 336 var desc = l[1] + " [" + l[0] + "]"; 337 return <li key={l[0]}> <a tabIndex="-1" href="#" 338 onClick={this.setLanguage.bind(this, l)}>{desc}</a></li>; 339 }.bind(this)) 340 } 341 </ul> 352 <span/> 342 353 </div> 343 354 … … 360 371 <div className="input-group"> 361 372 <span className="input-group-addon nobkg">in</span> 362 363 364 373 <button type="button" className="btn btn-default" onClick={this.toggleCorpusSelection}> 374 {this.state.corpora.getSelectedMessage()} <span className="caret"/> 375 </button> 365 376 </div> 366 377 … … 383 394 </Modal> 384 395 396 <Modal ref="languageModal" title="Select Language"> 397 <LanguageSelector anyLanguage={this.anyLanguage} 398 languageMap={this.state.languageMap} 399 selectedLanguage={this.state.language} 400 languageFilter={this.state.languageFilter} 401 languageChangeHandler={this.setLanguageAndFilter} /> 402 </Modal> 403 385 404 <div className="top-gap"> 386 <Results requests={this.state.hits.requests} results={this.state.hits.results} /> 405 <Results requests={this.state.hits.requests} 406 results={this.filterResults()} 407 searchedLanguage={this.state.language}/> 387 408 </div> 388 409 </div> … … 396 417 397 418 419 ///////////////////////////////// 420 421 var LanguageSelector = React.createClass({ 422 propTypes: { 423 anyLanguage: PT.array.isRequired, 424 languageMap: PT.object.isRequired, 425 selectedLanguage: PT.array.isRequired, 426 languageFilter: PT.string.isRequired, 427 languageChangeHandler: PT.func.isRequired, 428 }, 429 mixins: [React.addons.LinkedStateMixin], 430 431 selectLang: function(language) { 432 this.props.languageChangeHandler(language, this.props.languageFilter); 433 }, 434 435 setFilter: function(filter) { 436 this.props.languageChangeHandler(this.props.selectedLanguage, filter); 437 }, 438 439 renderLanguageObject: function(lang) { 440 var desc = lang[1] + " [" + lang[0] + "]"; 441 var style = { 442 whiteSpace: "nowrap", 443 fontWeight: lang[0] === this.props.selectedLanguage[0] ? "bold":"normal", 444 }; 445 return <div key={lang[0]}> 446 <a tabIndex="-1" href="#" style={style} onClick={this.selectLang.bind(this, lang)}>{desc}</a> 447 </div>; 448 }, 449 450 renderRadio: function(option) { 451 return this.props.languageFilter === option ? 452 <input type="radio" name="filterOpts" value={option} checked onChange={this.setFilter.bind(this, option)}/> 453 : <input type="radio" name="filterOpts" value={option} onChange={this.setFilter.bind(this, option)} />; 454 }, 455 456 render: function() { 457 var languages = _.pairs(this.props.languageMap) 458 .sort(function(l1, l2){return l1[1].localeCompare(l2[1]); }); 459 languages.unshift(this.props.anyLanguage); 460 languages = languages.map(this.renderLanguageObject); 461 var third = Math.round(languages.length/3); 462 var l1 = languages.slice(0, third); 463 var l2 = languages.slice(third, 2*third); 464 var l3 = languages.slice(2*third, languages.length); 465 466 return <div> 467 <div className="row"> 468 <div className="col-sm-4">{l1}</div> 469 <div className="col-sm-4">{l2}</div> 470 <div className="col-sm-4">{l3}</div> 471 <div className="col-sm-12" style={{marginTop:10, marginBottom:10, borderBottom:"1px solid #eee"}}/> 472 </div> 473 <form className="form" role="form"> 474 <div className="input-group"> 475 <div> 476 <label style={{color:'black'}}> 477 { this.renderRadio('byMeta') }{" "} 478 Use the collections{"'"} specified language to filter results 479 </label> 480 </div> 481 <div> 482 <label style={{color:'black'}}> 483 { this.renderRadio('byGuess') }{" "} 484 Filter results by using a language detector 485 </label> 486 </div> 487 <div> 488 <label style={{color:'black'}}> 489 { this.renderRadio('byMetaAndGuess') }{" "} 490 First use the collections{"'"} specified language then also use a language detector 491 </label> 492 </div> 493 </div> 494 </form> 495 </div>; 496 } 497 }); 398 498 ///////////////////////////////// 399 499 … … 442 542 requests: PT.array.isRequired, 443 543 results: PT.array.isRequired, 544 searchedLanguage: PT.array.isRequired, 444 545 }, 445 546 446 547 getInitialState: function () { 447 return { displayKwic: false }; 548 return { 549 displayKwic: false, 550 }; 448 551 }, 449 552 … … 453 556 454 557 renderRowLanguage: function(hit) { 455 return <span style={{fontFace:"Courier",color:"black"}}>{hit.language} </span>;558 return <span style={{fontFace:"Courier",color:"black"}}>{hit.language} </span> ; 456 559 }, 457 560 … … 467 570 468 571 renderRowsAsKwic: function(hit,i) { 469 var sleft={textAlign:"left", verticalAlign:" middle", width:"50%"};470 var scenter={textAlign:"center", verticalAlign:" middle", maxWidth:"50%"};471 var sright={textAlign:"right", verticalAlign:" middle", maxWidth:"50%"};572 var sleft={textAlign:"left", verticalAlign:"top", width:"50%"}; 573 var scenter={textAlign:"center", verticalAlign:"top", maxWidth:"50%"}; 574 var sright={textAlign:"right", verticalAlign:"top", maxWidth:"50%"}; 472 575 return <tr key={i} className="hitrow"> 473 576 <td>{this.renderRowLanguage(hit)}</td> … … 565 668 566 669 renderKwicCheckbox: function() { 567 return <div key="-option-KWIC-" className="row"> 568 <div className="float-right" style={{marginRight:17}}> 569 <div className="btn-group" style={{display:"inline-block"}}> 570 <label forHtml="inputKwic" className="btn-default"> 571 { this.state.displayKwic ? 572 <input id="inputKwic" type="checkbox" value="kwic" checked onChange={this.toggleKwic} /> : 573 <input id="inputKwic" type="checkbox" value="kwic" onChange={this.toggleKwic} /> 574 } 575 576 Display as Key Word In Context 577 </label> 578 </div> 670 return <div className="float-right" style={{marginRight:17}}> 671 <div className="btn-group" style={{display:"inline-block"}}> 672 <label forHtml="inputKwic" className="btn-default"> 673 { this.state.displayKwic ? 674 <input id="inputKwic" type="checkbox" value="kwic" checked onChange={this.toggleKwic} /> : 675 <input id="inputKwic" type="checkbox" value="kwic" onChange={this.toggleKwic} /> 676 } 677 678 Display as Key Word In Context 679 </label> 579 680 </div> 580 681 </div>; … … 592 693 <div key="-found-message-" style={margintop}>{this.renderFoundMessage(hits)} </div> 593 694 <div key="-progress-" style={margintop}>{this.renderProgressBar()}</div> 594 {hits > 0 ? this.renderKwicCheckbox() : false} 695 {hits > 0 ? 696 <div key="-option-KWIC-" className="row"> 697 {this.renderKwicCheckbox()} 698 </div> 699 : false } 595 700 {this.props.results.map(this.renderResultPanels)} 596 701 </ReactCSSTransitionGroup>
Note: See TracChangeset
for help on using the changeset viewer.