- Timestamp:
- 01/15/15 16:01:26 (9 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
SRUAggregator/trunk/src/main/resources/assets/js/search.jsx
r5900 r5919 3 3 "use strict"; 4 4 5 window.MyAggregator = window.MyAggregator || {}; 6 5 7 var React = window.React; 6 8 var PT = React.PropTypes; 7 var ReactCSSTransitionGroup = window.React.addons.CSSTransitionGroup; 8 // own components 9 var ReactCSSTransitionGroup = React.addons.CSSTransitionGroup; 10 11 var CorpusSelection = window.MyAggregator.CorpusSelection; 12 var HitNumber = window.MyAggregator.HitNumber; 13 var CorpusView = window.MyAggregator.CorpusView; 9 14 var InfoPopover = window.MyReact.InfoPopover; 10 15 var Panel = window.MyReact.Panel; 16 var Modal = window.MyReact.Modal; 17 18 var multipleLanguageCode = "mul"; // see ISO-693-3 19 20 var layers = [ 21 { 22 id: "sampa", 23 name: "Phonetic Transcriptions", 24 searchPlaceholder: "stA:z", 25 searchLabel: "SAMPA query", 26 searchLabelBkColor: "#eef", 27 }, 28 { 29 id: "text", 30 name: "Text Resources", 31 searchPlaceholder: "Elephant", 32 searchLabel: "Search text", 33 searchLabelBkColor: "#fed", 34 }, 35 ]; 36 var layerMap = { 37 sampa: layers[0], 38 text: layers[1], 39 }; 40 41 function Corpora(corpora, updateFn) { 42 var that = this; 43 this.corpora = corpora; 44 this.update = function() { 45 updateFn(that); 46 }; 47 48 var sortFn = function(x, y) { 49 var r = x.institution.name.localeCompare(y.institution.name); 50 if (r !== 0) { 51 return r; 52 } 53 var t1 = x.title ? x.title : x.displayName; 54 var t2 = y.title ? y.title : y.displayName; 55 return t1.toLowerCase().localeCompare(t2.toLowerCase()); 56 }; 57 58 this.recurse(function(corpus) { corpus.subCorpora.sort(sortFn); }); 59 this.corpora.sort(sortFn); 60 61 this.recurse(function(corpus, index) { 62 corpus.visible = true; // visible in the corpus view 63 corpus.selected = true; // selected in the corpus view 64 corpus.expanded = false; // not expanded in the corpus view 65 corpus.priority = 1; // priority in corpus view 66 corpus.index = index; 67 }); 68 } 69 70 Corpora.prototype.recurseCorpus = function(corpus, fn) { 71 if (false === fn(corpus)) { 72 // no recursion 73 } else { 74 this.recurseCorpora(corpus.subCorpora, fn); 75 } 76 }; 77 78 Corpora.prototype.recurseCorpora = function(corpora, fn) { 79 var recfn = function(corpus, index){ 80 if (false === fn(corpus)) { 81 // no recursion 82 } else { 83 corpus.subCorpora.forEach(recfn); 84 } 85 }; 86 corpora.forEach(recfn); 87 }; 88 89 Corpora.prototype.recurse = function(fn) { 90 this.recurseCorpora(this.corpora, fn); 91 }; 92 93 Corpora.prototype.getLanguageCodes = function() { 94 var languages = {}; 95 this.recurse(function(corpus) { 96 corpus.languages.forEach(function(lang) { 97 languages[lang] = true; 98 }); 99 return true; 100 }); 101 return languages; 102 }; 103 104 Corpora.prototype.isCorpusVisible = function(corpus, layerId, languageCode) { 105 if (layerId !== "text") { 106 return false; 107 } 108 // yes for any language 109 if (languageCode === multipleLanguageCode) { 110 return true; 111 } 112 // yes if the corpus is in only that language 113 if (corpus.languages && corpus.languages.length === 1 && corpus.languages[0] === languageCode) { 114 return true; 115 } 116 117 // ? yes if the corpus also contains that language 118 if (corpus.languages && corpus.languages.indexOf(languageCode) >=0) { 119 return true; 120 } 121 122 // ? yes if the corpus has no language 123 // if (!corpus.languages || corpus.languages.length === 0) { 124 // return true; 125 // } 126 return false; 127 }; 128 129 Corpora.prototype.setVisibility = function(layerId, languageCode) { 130 // top level 131 this.corpora.forEach(function(corpus) { 132 corpus.visible = this.isCorpusVisible(corpus, layerId, languageCode); 133 this.recurseCorpora(corpus.subCorpora, function(c) { c.visible = corpus.visible; }); 134 }.bind(this)); 135 }; 136 137 Corpora.prototype.getSelectedIds = function() { 138 var ids = []; 139 this.recurse(function(corpus) { 140 if (corpus.visible && corpus.selected) { 141 ids.push(corpus.id); 142 return false; // top-most collection in tree, don't delve deeper 143 } 144 return true; 145 }); 146 147 // console.log("ids: ", ids.length, {ids:ids}); 148 return ids; 149 }; 150 151 Corpora.prototype.getSelectedMessage = function() { 152 var selected = this.getSelectedIds().length; 153 if (this.corpora.length === selected) { 154 return "All available collections"; 155 } else if (selected === 1) { 156 return "1 selected collection"; 157 } 158 return selected+" selected collections"; 159 }; 160 161 162 var AggregatorPage = window.MyAggregator.AggregatorPage = React.createClass({ 163 propTypes: { 164 ajax: PT.func.isRequired 165 }, 166 167 mixins: [React.addons.LinkedStateMixin], 168 timeout: 0, 169 nohits: { 170 requests: [], 171 results: [], 172 }, 173 anyLanguage: [multipleLanguageCode, "Any Language"], 174 175 getInitialState: function () { 176 return { 177 corpora: new Corpora([], this.updateCorpora), 178 languageMap: {}, 179 language: this.anyLanguage, 180 searchLayerId: "text", 181 numberOfResults: 10, 182 183 searchId: null, 184 hits: this.nohits, 185 }; 186 }, 187 188 componentDidMount: function() { 189 this.refreshCorpora(); 190 this.refreshLanguages(); 191 }, 192 193 refreshCorpora: function() { 194 this.props.ajax({ 195 url: 'rest/corpora', 196 success: function(json, textStatus, jqXHR) { 197 this.setState({corpora : new Corpora(json, this.updateCorpora)}); 198 }.bind(this), 199 }); 200 }, 201 202 refreshLanguages: function() { 203 this.props.ajax({ 204 url: 'rest/languages', 205 success: function(json, textStatus, jqXHR) { 206 this.setState({languageMap : json}); 207 }.bind(this), 208 }); 209 }, 210 211 updateCorpora: function(corpora) { 212 this.setState({corpora:corpora}); 213 }, 214 215 search: function(query) { 216 // console.log(query); 217 if (!query) { 218 this.setState({ hits: this.nohits, searchId: null }); 219 return; 220 } 221 this.props.ajax({ 222 url: 'rest/search', 223 type: "POST", 224 data: { 225 layer: this.state.searchLayerId, 226 language: this.state.language[0], 227 query: query, 228 numberOfResults: this.state.numberOfResults, 229 corporaIds: this.state.corpora.getSelectedIds(), 230 }, 231 success: function(searchId, textStatus, jqXHR) { 232 // console.log("search ["+query+"] ok: ", searchId, jqXHR); 233 this.setState({searchId : searchId}); 234 this.timeout = 250; 235 setTimeout(this.refreshSearchResults, this.timeout); 236 }.bind(this), 237 }); 238 }, 239 240 refreshSearchResults: function() { 241 if (!this.state.searchId) { 242 return; 243 } 244 this.props.ajax({ 245 url: 'rest/search/'+this.state.searchId, 246 success: function(json, textStatus, jqXHR) { 247 if (json.requests.length > 0) { 248 if (this.timeout < 10000) { 249 this.timeout = 1.5 * this.timeout; 250 } 251 setTimeout(this.refreshSearchResults, this.timeout); 252 // console.log("new search in: " + this.timeout+ "ms"); 253 } else { 254 // console.log("search ended"); 255 } 256 this.setState({hits:json}); 257 // console.log("hits:", json); 258 }.bind(this), 259 }); 260 }, 261 262 setLanguage: function(languageObj) { 263 this.state.corpora.setVisibility(this.state.searchLayerId, languageObj[0]); 264 this.setState({language: languageObj}); 265 this.state.corpora.update(); 266 }, 267 268 setLayer: function(layerId) { 269 this.state.corpora.setVisibility(layerId, this.state.language[0]); 270 this.state.corpora.update(); 271 this.setState({searchLayerId: layerId}); 272 }, 273 274 setNumberOfResults: function(e) { 275 var n = e.target.value; 276 if (n < 10) n = 10; 277 if (n > 250) n = 250; 278 this.setState({numberOfResults: n}); 279 e.preventDefault(); 280 e.stopPropagation(); 281 }, 282 283 stop: function(e) { 284 e.preventDefault(); 285 e.stopPropagation(); 286 }, 287 288 toggleCorpusSelection: function(e) { 289 $(this.refs.corporaModal.getDOMNode()).modal(); 290 e.preventDefault(); 291 e.stopPropagation(); 292 }, 293 294 renderAggregator: function() { 295 var layer = layerMap[this.state.searchLayerId]; 296 return ( 297 <div className="top-gap"> 298 <div className="row"> 299 <div className="aligncenter" style={{marginLeft:16, marginRight:16}}> 300 <div className="input-group"> 301 <span className="input-group-addon" style={{backgroundColor:layer.searchLabelBkColor}}> 302 {layer.searchLabel} 303 </span> 304 305 <SearchBox search={this.search} placeholder={layer.searchPlaceholder} /> 306 <div className="input-group-btn"> 307 <button className="btn btn-default input-lg" type="button" onClick={this.search}> 308 <i className="glyphicon glyphicon-search"></i> 309 </button> 310 </div> 311 </div> 312 </div> 313 </div> 314 315 <div className="wel" style={{marginTop:20}}> 316 <div className="aligncenter" > 317 <form className="form-inline" role="form"> 318 319 <div className="input-group"> 320 321 <span className="input-group-addon nobkg" >Search for</span> 322 323 <div className="input-group-btn"> 324 <button className="form-control btn btn-default" 325 aria-expanded="false" data-toggle="dropdown"> 326 {this.state.language[1]} <span className="caret"/> 327 </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> 342 </div> 343 344 <div className="input-group-btn"> 345 <ul ref="layerDropdownMenu" className="dropdown-menu"> 346 { layers.map(function(l) { 347 return <li key={l.id}> <a tabIndex="-1" href="#" 348 onClick={this.setLayer.bind(this, l.id)}> {l.name} </a></li>; 349 }.bind(this)) 350 } 351 </ul> 352 <button className="form-control btn btn-default" 353 aria-expanded="false" data-toggle="dropdown" > 354 {layer.name} <span className="caret"/> 355 </button> 356 </div> 357 358 </div> 359 360 <div className="input-group"> 361 <span className="input-group-addon nobkg">in</span> 362 <button type="button" className="btn btn-default" onClick={this.toggleCorpusSelection}> 363 {this.state.corpora.getSelectedMessage()} <span className="caret"/> 364 </button> 365 </div> 366 367 <div className="input-group"> 368 <span className="input-group-addon nobkg">and show up to</span> 369 <div className="input-group-btn"> 370 <input type="number" className="form-control input" min="10" max="250" step="5" 371 style={{width:54}} 372 onChange={this.setNumberOfResults} value={this.state.numberOfResults} 373 onKeyPress={this.stop}/> 374 </div> 375 <span className="input-group-addon nobkg">hits</span> 376 </div> 377 </form> 378 </div> 379 </div> 380 381 <Modal ref="corporaModal" title="Collections"> 382 <CorpusView corpora={this.state.corpora} languageMap={this.state.languageMap} /> 383 </Modal> 384 385 <div className="top-gap"> 386 <Results requests={this.state.hits.requests} results={this.state.hits.results} /> 387 </div> 388 </div> 389 ); 390 }, 391 render: function() { 392 return this.renderAggregator(); 393 } 394 }); 395 11 396 12 397 … … 67 452 }, 68 453 454 renderRowLanguage: function(hit) { 455 return <span style={{fontFace:"Courier",color:"black"}}>{hit.language}</span>; 456 }, 457 69 458 renderRowsAsHits: function(hit,i) { 70 459 function renderTextFragments(tf, idx) { … … 72 461 } 73 462 return <p key={i} className="hitrow"> 463 {this.renderRowLanguage(hit)} 74 464 {hit.fragments.map(renderTextFragments)} 75 465 </p>; … … 81 471 var sright={textAlign:"right", verticalAlign:"middle", maxWidth:"50%"}; 82 472 return <tr key={i} className="hitrow"> 473 <td>{this.renderRowLanguage(hit)}</td> 83 474 <td style={sright}>{hit.left}</td> 84 475 <td style={scenter} className="keyword">{hit.keyword}</td> … … 208 599 }); 209 600 210 if (!window.MyAggregator) { 211 window.MyAggregator = {}; 212 } 213 window.MyAggregator.SearchBox = SearchBox; 214 window.MyAggregator.Results = Results; 601 var _ = window._ = window._ || { 602 keys: function() { 603 var ret = []; 604 for (var x in o) { 605 if (o.hasOwnProperty(x)) { 606 ret.push(x); 607 } 608 } 609 return ret; 610 }, 611 612 pairs: function(o){ 613 var ret = []; 614 for (var x in o) { 615 if (o.hasOwnProperty(x)) { 616 ret.push([x, o[x]]); 617 } 618 } 619 return ret; 620 }, 621 }; 622 215 623 })();
Note: See TracChangeset
for help on using the changeset viewer.