Changeset 5784 for SRUAggregator
- Timestamp:
- 11/06/14 18:36:44 (10 years ago)
- Location:
- SRUAggregator/trunk
- Files:
-
- 1 added
- 1 deleted
- 21 edited
Legend:
- Unmodified
- Added
- Removed
-
SRUAggregator/trunk/build.sh
r5771 r5784 6 6 node_modules/bower/bin/bower install jquery bootstrap react react-addons react-bootstrap 7 7 8 cp bower_components/bootstrap/dist/css/bootstrap.min.css src/main/webapp/lib/bootstrap.min.css 9 cp bower_components/jquery/dist/jquery.min.js src/main/webapp/lib/jquery.min.js 10 cp bower_components/react/react-with-addons.js src/main/webapp/lib/react-with-addons.js 11 cp bower_components/react/react-with-addons.min.js src/main/webapp/lib/react-with-addons.min.js 12 cp bower_components/react-bootstrap/react-bootstrap.min.js src/main/webapp/lib/react-bootstrap.min.js 8 cp bower_components/bootstrap/dist/css/bootstrap.min.css src/main/webapp/lib/ 9 cp bower_components/bootstrap/dist/js/bootstrap.min.js src/main/webapp/lib/ 10 cp bower_components/jquery/dist/jquery.min.js src/main/webapp/lib/ 11 cp bower_components/react/react-with-addons.js src/main/webapp/lib/ 12 cp bower_components/react/react-with-addons.min.js src/main/webapp/lib/ 13 cp bower_components/react-bootstrap/react-bootstrap.min.js src/main/webapp/lib/ 13 14 fi 14 15 -
SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/app/Aggregator.java
r5771 r5784 76 76 * @author edima 77 77 * 78 * TODO: result panes with animation and more info79 *80 * TODO: highlighted/kwic hits: toggle for now81 *82 * TODO: show multiple hits on the same result in multiple rows, linked visually83 *84 * TODO: new UI element to specify layer we search in85 *86 78 * TODO: good UI for tree view corpus selection, with instant search form 87 79 * … … 95 87 * TODO: atomic replace of cached corpora (file) 96 88 * 89 * TODO: show multiple hits on the same result in multiple rows, linked visually 90 * 91 * TODO: new UI element to specify layer we search in 92 * 97 93 * TODO: test json deserialization 98 94 * … … 102 98 private static final org.slf4j.Logger log = LoggerFactory.getLogger(Aggregator.class); 103 99 104 public static final int WAITING_TIME_FOR_SHUTDOWN_MS = 2000;105 100 public static final String DE_TOK_MODEL = "/tokenizer/de-tuebadz-8.0-token.bin"; 106 private static final String DEFAULT_DATA_LOCATION = "/data";107 101 108 102 private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); 109 103 private static Aggregator instance; 104 105 public static int ENDPOINTS_TIMEOUT_MS = 50 * 1000; 106 public static int EXECUTOR_SHUTDOWN_TIMEOUT_MS = (50 + 10) * 1000; 107 private final EndpointUrlFilter filter = new EndpointUrlFilter(); 110 108 111 109 private AtomicReference<Corpora> scanCacheAtom = new AtomicReference<Corpora>(new Corpora()); … … 129 127 @Override 130 128 public void contextInitialized(ServletContextEvent servletContextEvent) { 131 log.info("Aggregator i s starting now.");129 log.info("Aggregator initialization started."); 132 130 instance = this; 133 131 try { 134 132 params = new Params(); 133 detectAndConfigureEnvironment(); 135 134 136 135 sruClient = new ClarinFCSClientBuilder() 137 .setConnectTimeout( 5000)138 .setSocketTimeout( 5000)136 .setConnectTimeout(ENDPOINTS_TIMEOUT_MS) 137 .setSocketTimeout(ENDPOINTS_TIMEOUT_MS) 139 138 .addDefaultDataViewParsers() 140 139 .enableLegacySupport() … … 152 151 model = setUpTokenizers(); 153 152 154 EndpointUrlFilter filter = new EndpointUrlFilter();//.deny("leipzig"); // ~5k corpora155 153 ScanCrawlTask task = new ScanCrawlTask(sruClient, params.centerRegistryUrl, 156 154 params.cacheMaxDepth, filter, scanCacheAtom, corporaCacheFile); … … 206 204 sruClient.shutdown(); 207 205 scheduler.shutdown(); 208 Thread.sleep( WAITING_TIME_FOR_SHUTDOWN_MS);206 Thread.sleep(EXECUTOR_SHUTDOWN_TIMEOUT_MS); 209 207 sruClient.shutdownNow(); 210 208 scheduler.shutdownNow(); 211 Thread.sleep( WAITING_TIME_FOR_SHUTDOWN_MS);209 Thread.sleep(EXECUTOR_SHUTDOWN_TIMEOUT_MS); 212 210 } catch (InterruptedException ie) { 213 211 sruClient.shutdownNow(); … … 228 226 return model; 229 227 } 228 229 private void detectAndConfigureEnvironment() { 230 if (!"Development".equals(System.getProperty("Environment"))) { 231 log.info(" *** Production Environment detected, using default settings *** "); 232 return; 233 } 234 235 log.warn(" *** Development Environment detected, using custom settings *** "); 236 237 ENDPOINTS_TIMEOUT_MS = 10 * 1000; 238 EXECUTOR_SHUTDOWN_TIMEOUT_MS = 1000; 239 240 filter.deny("leipzig"); // ~5k corpora 241 params.aggregatorFilePath = System.getProperty("user.home") + File.separator + "fcsAggregatorCorpora.json"; 242 params.cacheMaxDepth = 1; 243 } 230 244 } -
SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/app/Params.java
r5771 r5784 15 15 16 16 public final String centerRegistryUrl; 17 public final int cacheMaxDepth;18 17 public final TimeUnit cacheUpdateIntervalUnit; 19 18 public final int cacheUpdateInterval; 20 public final String aggregatorFilePath; 19 public int cacheMaxDepth; 20 public String aggregatorFilePath; 21 21 22 22 public Params() throws NamingException { -
SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/cache/Corpora.java
r5771 r5784 4 4 import eu.clarin.sru.fcs.aggregator.registry.Institution; 5 5 import java.util.ArrayList; 6 import java.util.Collections; 6 7 import java.util.HashMap; 7 8 import java.util.HashSet; … … 29 30 30 31 public List<Institution> getInstitutions() { 31 return institutions;32 return Collections.unmodifiableList(institutions); 32 33 } 33 34 34 35 public List<Corpus> getCorpora() { 35 return corpora;36 return Collections.unmodifiableList(corpora); 36 37 } 37 38 38 public void addInstitution(Institution institution) {39 public synchronized void addInstitution(Institution institution) { 39 40 institutions.add(institution); 40 41 } 41 42 42 public synchronized boolean add RootCorpus(final Corpus c) {43 public synchronized boolean addCorpus(Corpus c, Corpus parentCorpus) { 43 44 if (findByHandle(c.getHandle()) != null) { 44 45 return false; 45 46 } 46 corpora.add(c); 47 for (String lang : c.getLanguages()) { 48 if (!langToRootCorpora.containsKey(lang)) { 49 langToRootCorpora.put(lang, new HashSet<Corpus>()); 47 if (parentCorpus == null) { //i.e it's a root corpus 48 corpora.add(c); 49 for (String lang : c.getLanguages()) { 50 if (!langToRootCorpora.containsKey(lang)) { 51 langToRootCorpora.put(lang, new HashSet<Corpus>()); 52 } 53 langToRootCorpora.get(lang).add(c); 50 54 } 51 langToRootCorpora.get(lang).add(c);52 }53 return true;54 }55 56 public synchronized boolean addSubCorpus(Corpus c, Corpus parentCorpus) {57 if (findByHandle(c.getHandle()) != null) {58 return false;59 }60 61 if (parentCorpus == null) { //i.e it's a root corpus62 addRootCorpus(c);63 55 } else { 64 56 parentCorpus.addCorpus(c); -
SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/cache/EndpointFilter.java
r5720 r5784 1 1 package eu.clarin.sru.fcs.aggregator.cache; 2 3 import eu.clarin.sru.fcs.aggregator.registry.Endpoint;4 2 5 3 /** … … 12 10 public interface EndpointFilter { 13 11 14 Iterable<Endpoint> filter(Iterable<Endpoint> endpoints);12 Iterable<String> filter(Iterable<String> endpoints); 15 13 16 14 } -
SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/cache/EndpointUrlFilter.java
r5758 r5784 1 1 package eu.clarin.sru.fcs.aggregator.cache; 2 2 3 import eu.clarin.sru.fcs.aggregator.registry.Endpoint;4 3 import java.util.ArrayList; 5 4 import java.util.Collections; … … 31 30 32 31 @Override 33 public Iterable< Endpoint> filter(Iterable<Endpoint> endpoints) {34 List< Endpoint> filtered = new ArrayList<Endpoint>();32 public Iterable<String> filter(Iterable<String> endpoints) { 33 List<String> filtered = new ArrayList<String>(); 35 34 36 for ( Endpointendp : endpoints) {35 for (String endp : endpoints) { 37 36 if (allow.isEmpty()) { 38 37 filtered.add(endp); 39 38 } 40 39 for (String urlSubstring : allow) { 41 if (endp. getUrl().contains(urlSubstring)) {40 if (endp.contains(urlSubstring)) { 42 41 filtered.add(endp); 43 42 break; … … 45 44 } 46 45 for (String urlSubstring : deny) { 47 if (endp. getUrl().contains(urlSubstring)) {46 if (endp.contains(urlSubstring)) { 48 47 filtered.remove(endp); 49 48 } -
SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/cache/ScanCrawler.java
r5771 r5784 10 10 import eu.clarin.sru.fcs.aggregator.registry.CenterRegistry; 11 11 import eu.clarin.sru.fcs.aggregator.registry.Corpus; 12 import eu.clarin.sru.fcs.aggregator.registry.Endpoint;13 12 import eu.clarin.sru.fcs.aggregator.registry.Institution; 14 13 import eu.clarin.sru.fcs.aggregator.util.SRUCQL; … … 48 47 for (Institution institution : centerRegistry.getCQLInstitutions()) { 49 48 cache.addInstitution(institution); 50 Iterable< Endpoint> endpoints = institution.getEndpoints();49 Iterable<String> endpoints = institution.getEndpoints(); 51 50 if (filter != null) { 52 51 endpoints = filter.filter(endpoints); 53 52 } 54 for ( Endpointendp : endpoints) {55 addCorpora( endp.getUrl(), institution, null, cache, 0);53 for (String endp : endpoints) { 54 addCorpora(institution, endp, null, cache, 0); 56 55 } 57 56 } … … 68 67 } 69 68 70 private void addCorpora(final String endpointUrl, final Institution institution,69 private void addCorpora(final Institution institution, final String endpointUrl, 71 70 final Corpus parentCorpus, final Corpora corpora, final int depth) { 72 71 if (depth > maxDepth) { … … 77 76 try { 78 77 scanRequest = new SRUScanRequest(endpointUrl); 79 StringBuilder scanClause = new StringBuilder(SRUCQL.SCAN_RESOURCE_PARAMETER); 80 scanClause.append("="); 81 String normalizedHandle = normalizeHandle(parentCorpus, parentCorpus == null); 82 scanClause.append(normalizedHandle); 83 scanRequest.setScanClause(scanClause.toString()); 78 scanRequest.setScanClause(SRUCQL.SCAN_RESOURCE_PARAMETER 79 + "=" + normalizeHandle(parentCorpus)); 84 80 scanRequest.setExtraRequestData(SRUCQL.SCAN_RESOURCE_INFO_PARAMETER, 85 81 SRUCQL.SCAN_RESOURCE_INFO_PARAMETER_DEFAULT_VALUE); … … 101 97 for (SRUTerm term : response.getTerms()) { 102 98 Corpus c = createCorpus(institution, endpointUrl, term); 103 checkedAdd(corpora, parentCorpus, c, depth); 99 if (corpora.addCorpus(c, parentCorpus)) { 100 addCorpora(institution, endpointUrl, c, corpora, depth + 1); 101 } 104 102 } 105 103 } else if (parentCorpus == null) { 106 // create default root corpus 107 Corpus c = new Corpus(institution, endpointUrl); 108 checkedAdd(corpora, parentCorpus, c, depth); 104 Corpus c = createCorpus(institution, endpointUrl, null); 105 if (corpora.addCorpus(c, parentCorpus)) { 106 addCorpora(institution, endpointUrl, c, corpora, depth + 1); 107 } 109 108 } 110 109 … … 119 118 120 119 @Override 120 121 121 public void onError(SRUScanRequest request, SRUClientException error) { 122 122 latch.decrement(); 123 123 log.error("{} Error while scanning {}: {} : {}", latch.get(), endpointUrl, error, error.getCause()); 124 124 } 125 }); 125 } 126 ); 126 127 } catch (SRUClientException ex) { 127 128 latch.decrement(); … … 130 131 } 131 132 132 private void checkedAdd(Corpora corpora, Corpus parentCorpus, Corpus c, int depth) { 133 if (corpora.addSubCorpus(c, parentCorpus)) { 134 addCorpora(c.getEndpointUrl(), c.getInstitution(), c, corpora, depth + 1); 135 } else { 136 // log.warn("Cyclic reference in corpus " + c.getHandle() + " of endpoint " + c.getEndpointUrl()); 137 } 138 } 139 140 private static String normalizeHandle(Corpus corpus, boolean root) { 141 if (root) { 133 private static String normalizeHandle(Corpus corpus) { 134 if (corpus == null) { 142 135 return Corpus.ROOT_HANDLE; 143 136 } … … 152 145 private static Corpus createCorpus(Institution institution, String endpointUrl, SRUTerm term) { 153 146 Corpus c = new Corpus(institution, endpointUrl); 154 c.setHandle(term.getValue()); 155 c.setDisplayName(term.getDisplayTerm()); 156 if (term.getNumberOfRecords() > 0) { 157 c.setNumberOfRecords(term.getNumberOfRecords()); 158 } 159 addExtraInfo(c, term); 147 if (term == null) { 148 c.setDisplayName("[" + endpointUrl + "]"); 149 } else { 150 c.setDisplayName(term.getDisplayTerm()); 151 c.setHandle(term.getValue()); 152 if (term.getNumberOfRecords() > 0) { 153 c.setNumberOfRecords(term.getNumberOfRecords()); 154 } 155 addExtraInfo(c, term); 156 } 160 157 return c; 161 158 } -
SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/registry/Corpus.java
r5771 r5784 23 23 private String handle; 24 24 private Integer numberOfRecords; 25 private String display Term;25 private String displayName; 26 26 private Set<String> languages = new HashSet<String>(); 27 27 private String landingPage; 28 28 private String title; 29 29 private String description; 30 boolean temp = false;31 30 public List<Corpus> subCorpora = Collections.synchronizedList(new ArrayList<Corpus>()); 32 31 33 32 public Corpus() { 34 temp = true;35 33 } 36 34 … … 48 46 } 49 47 50 public boolean isTemporary() {51 return temp;48 public void setSubCorpora(List<Corpus> subCorpora) { 49 this.subCorpora = subCorpora; 52 50 } 51 53 52 54 53 public String getHandle() { … … 60 59 } 61 60 62 public void setNumberOfRecords(int numberOfRecords) {63 this.numberOfRecords = numberOfRecords;64 }65 66 61 public Integer getNumberOfRecords() { 67 62 return numberOfRecords; 68 63 } 69 64 65 public void setNumberOfRecords(Integer numberOfRecords) { 66 this.numberOfRecords = numberOfRecords; 67 } 68 70 69 public String getDisplayName() { 71 return display Term;70 return displayName; 72 71 } 73 72 74 73 public void setDisplayName(String displayName) { 75 this.display Term= displayName;74 this.displayName = displayName; 76 75 } 77 76 … … 80 79 } 81 80 81 public void setEndpointUrl(String endpointUrl) { 82 this.endpointUrl = endpointUrl; 83 } 84 82 85 public Institution getInstitution() { 83 86 return institution; 87 } 88 89 public void setInstitution(Institution institution) { 90 this.institution = institution; 84 91 } 85 92 -
SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/registry/Institution.java
r5771 r5784 13 13 private String name; 14 14 private String link; 15 private ArrayList<Endpoint> endpoints;15 private Set<String> endpoints; 16 16 17 17 // for JSON deserialization … … 22 22 this.name = name; 23 23 this.link = link; 24 this.endpoints = new ArrayList<Endpoint>();24 this.endpoints = new LinkedHashSet<String>(); 25 25 } 26 26 27 public Endpoint add(String endpointUrl) { 28 Endpoint ep = getEndpoint(endpointUrl); 29 if (ep == null) { 30 ep = new Endpoint(endpointUrl, this); 31 endpoints.add(ep); 32 } 33 return ep; 27 public String add(String endpointUrl) { 28 endpoints.add(endpointUrl); 29 return endpointUrl; 34 30 } 35 31 … … 42 38 } 43 39 44 public List<Endpoint> getEndpoints() {40 public Set<String> getEndpoints() { 45 41 return this.endpoints; 46 }47 48 public Endpoint getEndpoint(int index) {49 if (index >= endpoints.size()) {50 return null;51 }52 return endpoints.get(index);53 }54 55 public Endpoint getEndpoint(String endpointUrl) {56 for (Endpoint ep : endpoints) {57 if (ep.getUrl().equals(endpointUrl)) {58 return ep;59 }60 }61 return null;62 42 } 63 43 -
SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/rest/RestService.java
r5771 r5784 94 94 @POST 95 95 @Path("search") 96 public Response postSearch(@FormParam("query") String query) throws Exception { 96 public Response postSearch( 97 @FormParam("query") String query, 98 @FormParam("language") String language, 99 @FormParam("numHits") Integer numHits) throws Exception { 97 100 if (query == null || query.isEmpty()) { 98 101 return Response.status(400).entity("'query' parameter expected").build(); 99 102 } 100 103 List<Corpus> corpora = Aggregator.getInstance().getCorpora().getCorpora(); 101 Search search = Aggregator.getInstance().startSearch(SRUVersion.VERSION_1_2, corpora, query, "eng", 10); 104 if (corpora == null || corpora.isEmpty()) { 105 return Response.status(503).entity("No corpora, please wait for the server to finish scanning").build(); 106 } 107 if (numHits == null || numHits < 10) { 108 numHits = 10; 109 } 110 Search search = Aggregator.getInstance().startSearch(SRUVersion.VERSION_1_2, corpora, query, language, numHits); 111 if (search == null) { 112 return Response.status(500).entity("Initiating search failed").build(); 113 } 102 114 URI uri = URI.create("" + search.getId()); 103 115 return Response.created(uri).entity(uri).build(); -
SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/search/Request.java
r5718 r5784 40 40 41 41 public boolean hasCorpusHandler() { 42 if (corpus != null && corpus.getHandle() != null) { 43 return true; 44 } 45 return false; 42 return corpus != null && corpus.getHandle() != null; 46 43 } 47 44 } -
SRUAggregator/trunk/src/main/java/eu/clarin/sru/fcs/aggregator/search/Search.java
r5758 r5784 18 18 import java.util.logging.Logger; 19 19 import opennlp.tools.tokenize.TokenizerModel; 20 import org.slf4j.LoggerFactory; 20 21 21 22 /** … … 27 28 public class Search { 28 29 29 private static final Logger LOGGER = Logger.getLogger(Search.class.getName());30 private static final org.slf4j.Logger log = LoggerFactory.getLogger(Search.class); 30 31 31 32 private static final String SEARCH_RESULTS_ENCODING = "UTF-8"; … … 50 51 private Request executeSearch(SRUThreadedClient searchClient, SRUVersion version, Corpus corpus, String searchString, int startRecord, int maxRecords) { 51 52 final Request request = new Request(corpus, searchString, startRecord, startRecord + maxRecords - 1); 52 LOGGER.log(Level.INFO, "Executing search for {0} query={1} maxRecords={2}", 53 new Object[]{corpus.toString(), searchString, maxRecords}); 53 log.info("Executing search for {0} query={1} maxRecords={2}", corpus, searchString, maxRecords); 54 54 55 55 SRUSearchRetrieveRequest searchRequest = new SRUSearchRetrieveRequest(corpus.getEndpointUrl()); … … 79 79 }); 80 80 } catch (SRUClientException ex) { 81 LOGGER.log(Level.SEVERE, "SearchRetrieve failed for {0} {1} {2}", 82 new String[]{corpus.getEndpointUrl(), ex.getClass().getName(), ex.getMessage()}); 81 log.error("SearchRetrieve exception for " + corpus.getEndpointUrl(), ex); 82 } catch (Throwable xc) { 83 log.error("SearchRetrieve error for " + corpus.getEndpointUrl(), xc); 83 84 } 84 85 return request; -
SRUAggregator/trunk/src/main/webapp/CLARIN.css
r5720 r5784 11 11 -moz-osx-font-smoothing: grayscale; 12 12 color: #222; 13 /*font-size: 1 3px;*/13 /*font-size: 12px;*/ 14 14 /*word-wrap: break-word;*/ 15 15 } -
SRUAggregator/trunk/src/main/webapp/base.css
r5758 r5784 131 131 } 132 132 133 div.panel-title { 134 font-size:16px; 135 } 136 133 137 .unselectable { 134 138 -webkit-touch-callout: none; … … 145 149 146 150 147 .panel span {151 .panel-body span { 148 152 color: #222; 149 153 } 150 154 151 .panel span.keyword { 155 .panel-body span.keyword { 156 /*color: #00406F;*/ 157 /*font-weight: bold*/ 158 font-size: 12px; 159 color: #fff; 160 background-color: #00406F; 161 } 162 163 .panel-body td.keyword { 152 164 color: #00406F; 165 font-weight: bold 153 166 } 154 167 155 168 .bs-callout { 156 169 padding: 0 20px; 157 margin: 20px 0;170 margin: 0 0 20px 0; 158 171 border: 1px solid #ddd; 159 172 /*border-left: 5px solid rgb(17, 65, 111);*/ … … 162 175 } 163 176 .bs-callout .panel-heading { 177 margin-top: 0; 178 margin-bottom: 5px; 179 } 180 .bs-callout .panel-title { 164 181 color: #806a52; 165 182 /*color: #222;*/ 166 margin-top: 0;167 margin-bottom: 5px;168 183 } 169 184 .panel-body { 170 185 border-top: 1px solid #ddd; 186 } 187 188 div.popover { 189 max-width:552px; 171 190 } 172 191 -
SRUAggregator/trunk/src/main/webapp/index.html
r5771 r5784 63 63 <a title="about" id="aboutlink"> 64 64 <span class="glyphicon glyphicon-info-sign"></span> 65 <!-- <span>VERSION ${pom.version} </span> --> 66 <span>VERSION 2.0.0-ALPHA2 </span> 65 <span>VERSION 2.0.0.α3 </span> 67 66 </a> 68 67 </div> … … 87 86 <![endif]--> 88 87 <script src="lib/react-with-addons.js"></script> 88 <script src="lib/bootstrap.min.js"></script> 89 89 <script src="js/components.js"></script> 90 90 <script src="js/search.js"></script> -
SRUAggregator/trunk/src/main/webapp/js/components.js
r5771 r5784 5 5 var ReactTransitionGroup = React.addons.TransitionGroup; 6 6 7 var PopoverMixin = { 8 getDefaultProps: function(){ 9 return {hasPopover: true}; 10 }, 11 12 componentDidMount: function() { 13 var content; 14 if (Array.isArray(this.props.children)) 15 content = this.props.children.map(React.renderToString).join(" "); 16 else 17 content = React.renderToString(this.props.children); 18 $(this.getDOMNode()).popover({ 19 content: content, 20 animation: this.props.animation, 21 placement: this.props.placement, 22 title: this.props.title, 23 trigger: 'click', 24 html: true, 25 }); 26 } 27 }; 28 29 var InfoPopover = React.createClass({displayName: 'InfoPopover', 30 propTypes: { 31 title: PT.string.isRequired, 32 }, 33 mixins: [PopoverMixin], 34 35 handleClick: function(e) { 36 e.stopPropagation(); 37 }, 38 39 render: function() { 40 return React.createElement("button", {className: "btn btn-default btn-xs", onClick: this.handleClick}, 41 React.createElement("span", {className: "glyphicon glyphicon-info-sign"}) 42 ); 43 } 44 }); 45 7 46 window.MyReact = {}; 8 47 window.MyReact.Panel = React.createClass({displayName: 'Panel', 9 48 propTypes: { 10 key: PT.oneOfType([PT.string, PT.number]).isRequired, 11 header: PT.string.isRequired, 49 corpus:PT.object.isRequired, 12 50 }, 13 51 14 52 getInitialState: function() { 15 53 return { … … 22 60 }, 23 61 62 renderBody: function() { 63 return this.state.open ? 64 React.createElement("div", {className: "panel-body"}, this.props.children) : 65 false; 66 }, 67 68 renderInfo: function() { 69 return React.createElement("dl", {className: "dl-horizontal"}, 70 React.createElement("dt", null, "Institution"), 71 React.createElement("dd", null, this.props.corpus.institution.name), 72 73 this.props.corpus.description ? React.createElement("dt", null, "Description"):false, 74 this.props.corpus.description ? React.createElement("dd", null, this.props.corpus.description): false, 75 76 this.props.corpus.landingPage ? React.createElement("dt", null, "Landing Page") : false, 77 this.props.corpus.landingPage ? 78 React.createElement("dd", null, React.createElement("a", {href: this.props.corpus.landingPage}, this.props.corpus.landingPage)): 79 false, 80 81 React.createElement("dt", null, "Languages"), 82 React.createElement("dd", null, this.props.corpus.languages.join(", ")) 83 ); 84 }, 85 24 86 render: function() { 25 87 var chevron = "glyphicon glyphicon-chevron-" + (this.state.open ? "down":"right"); 26 88 var chevronStyle={fontSize:12}; 27 return React.createElement("div", {key: this.props.key, className: "bs-callout bs-callout-info"}, 89 var right={float:"right"}; 90 return React.createElement("div", {className: "bs-callout bs-callout-info"}, 28 91 React.createElement("div", {className: "panel"}, 29 React.createElement("div", {className: "panel-heading unselectable ", onClick: this.toggleState},30 React.createElement(" p", {className: "panel-title unselectable"},92 React.createElement("div", {className: "panel-heading unselectable row", onClick: this.toggleState}, 93 React.createElement("div", {className: "panel-title unselectable col-sm-11"}, 31 94 React.createElement("span", {className: chevron, style: chevronStyle}), "Â ", 32 this.props.header 95 this.props.corpus.displayName 96 ), 97 React.createElement("div", {style: right}, 98 React.createElement(InfoPopover, {placement: "left", title: this.props.corpus.displayName}, 99 this.renderInfo() 100 ) 33 101 ) 34 102 ), 35 React.createElement("div", {className: "panel-body"}, 36 React.createElement(ReactTransitionGroup, {transitionName: "display"}, 37 this.state.open ? this.props.children : false 38 ) 39 ) 103 this.renderBody() 40 104 ) 41 105 ); -
SRUAggregator/trunk/src/main/webapp/js/components.jsx
r5771 r5784 5 5 var ReactTransitionGroup = React.addons.TransitionGroup; 6 6 7 var PopoverMixin = { 8 getDefaultProps: function(){ 9 return {hasPopover: true}; 10 }, 11 12 componentDidMount: function() { 13 var content; 14 if (Array.isArray(this.props.children)) 15 content = this.props.children.map(React.renderToString).join(" "); 16 else 17 content = React.renderToString(this.props.children); 18 $(this.getDOMNode()).popover({ 19 content: content, 20 animation: this.props.animation, 21 placement: this.props.placement, 22 title: this.props.title, 23 trigger: 'click', 24 html: true, 25 }); 26 } 27 }; 28 29 var InfoPopover = React.createClass({ 30 propTypes: { 31 title: PT.string.isRequired, 32 }, 33 mixins: [PopoverMixin], 34 35 handleClick: function(e) { 36 e.stopPropagation(); 37 }, 38 39 render: function() { 40 return <button className="btn btn-default btn-xs" onClick={this.handleClick}> 41 <span className="glyphicon glyphicon-info-sign"/> 42 </button>; 43 } 44 }); 45 7 46 window.MyReact = {}; 8 47 window.MyReact.Panel = React.createClass({ 9 48 propTypes: { 10 key: PT.oneOfType([PT.string, PT.number]).isRequired, 11 header: PT.string.isRequired, 49 corpus:PT.object.isRequired, 12 50 }, 13 51 14 52 getInitialState: function() { 15 53 return { … … 22 60 }, 23 61 62 renderBody: function() { 63 return this.state.open ? 64 <div className="panel-body">{this.props.children}</div> : 65 false; 66 }, 67 68 renderInfo: function() { 69 return <dl className="dl-horizontal"> 70 <dt>Institution</dt> 71 <dd>{this.props.corpus.institution.name}</dd> 72 73 {this.props.corpus.description ? <dt>Description</dt>:false} 74 {this.props.corpus.description ? <dd>{this.props.corpus.description}</dd>: false} 75 76 {this.props.corpus.landingPage ? <dt>Landing Page</dt> : false } 77 {this.props.corpus.landingPage ? 78 <dd><a href={this.props.corpus.landingPage}>{this.props.corpus.landingPage}</a></dd>: 79 false} 80 81 <dt>Languages</dt> 82 <dd>{this.props.corpus.languages.join(", ")}</dd> 83 </dl>; 84 }, 85 24 86 render: function() { 25 87 var chevron = "glyphicon glyphicon-chevron-" + (this.state.open ? "down":"right"); 26 88 var chevronStyle={fontSize:12}; 27 return <div key={this.props.key} className="bs-callout bs-callout-info"> 89 var right={float:"right"}; 90 return <div className="bs-callout bs-callout-info"> 28 91 <div className="panel"> 29 <div className="panel-heading unselectable" onClick={this.toggleState}> 30 <p className="panel-title unselectable"> 31 <span className={chevron} style={chevronStyle}></span> 32 {this.props.header} 33 </p> 92 <div className="panel-heading unselectable row" onClick={this.toggleState}> 93 <div className="panel-title unselectable col-sm-11"> 94 <span className={chevron} style={chevronStyle} /> 95 {this.props.corpus.displayName} 96 </div> 97 <div style={right}> 98 <InfoPopover placement="left" title={this.props.corpus.displayName}> 99 {this.renderInfo()} 100 </InfoPopover> 101 </div> 34 102 </div> 35 <div className="panel-body"> 36 <ReactTransitionGroup transitionName="display"> 37 {this.state.open ? this.props.children : false} 38 </ReactTransitionGroup> 39 </div> 103 {this.renderBody()} 40 104 </div> 41 105 </div>; -
SRUAggregator/trunk/src/main/webapp/js/main.js
r5771 r5784 8 8 var HitNumber = window.MyAggregator.HitNumber; 9 9 var Results = window.MyAggregator.Results; 10 11 var globals = {}; 10 12 11 13 var Main = React.createClass({displayName: 'Main', … … 30 32 success: function(json, textStatus, jqXHR) { 31 33 that.setState({corpora:json}); 34 console.log("corpora", json); 32 35 }, 33 36 error: function(jqXHR, textStatus, error) { … … 61 64 console.log("search ["+query+"] ok: ", searchId); 62 65 that.setState({searchId : searchId}); 63 that.setState({timerId : setInterval(that.refreshSearchResults, 1000)}); 66 globals.timeout = 250; 67 setTimeout(that.refreshSearchResults, globals.timeout); 64 68 }, 65 69 error: function(jqXHR, textStatus, error) { … … 79 83 success: function(json, textStatus, jqXHR) { 80 84 if (json.requests.length === 0) { 81 clearInterval(that.state.timerId); 82 console.log("cleaned up timer"); 85 console.log("search ended"); 86 } else { 87 globals.timeout = 1.5 * globals.timeout; 88 setTimeout(that.refreshSearchResults, globals.timeout); 89 // console.log("new search in: " + globals.timeout+ "ms"); 83 90 } 84 91 that.setState({hits:json}); 92 console.log("hits:", json); 85 93 }, 86 94 error: function(jqXHR, textStatus, error) { … … 114 122 React.createElement("div", {style: margin}, 115 123 React.createElement("form", {className: "form-inline", role: "form"}, 116 React.createElement("label", { htmlFor: "dropdownCorpus",className: "muted"}, "search in "),124 React.createElement("label", {className: "muted"}, "search in "), 117 125 React.createElement("div", {id: "corpusSelection", style: inlinew}, 118 126 this.renderCorpusSelection() 119 127 ), 120 React.createElement("label", { htmlFor: "dropdownLanguage",className: "muted"}, " for results in "),128 React.createElement("label", {className: "muted"}, " for results in "), 121 129 React.createElement("div", {id: "languageSelection", style: inlinew}, 122 130 React.createElement(LanguageSelection, {languages: this.state.languages}) … … 126 134 React.createElement(HitNumber, {onChange: this.setNumberOfResults, numberOfResults: this.state.numberOfResults}) 127 135 ), 128 React.createElement("label", { htmlFor: "hits",className: "muted"}, " hits")136 React.createElement("label", {className: "muted"}, " hits") 129 137 ) 130 138 ) -
SRUAggregator/trunk/src/main/webapp/js/main.jsx
r5771 r5784 8 8 var HitNumber = window.MyAggregator.HitNumber; 9 9 var Results = window.MyAggregator.Results; 10 11 var globals = {}; 10 12 11 13 var Main = React.createClass({ … … 30 32 success: function(json, textStatus, jqXHR) { 31 33 that.setState({corpora:json}); 34 console.log("corpora", json); 32 35 }, 33 36 error: function(jqXHR, textStatus, error) { … … 61 64 console.log("search ["+query+"] ok: ", searchId); 62 65 that.setState({searchId : searchId}); 63 that.setState({timerId : setInterval(that.refreshSearchResults, 1000)}); 66 globals.timeout = 250; 67 setTimeout(that.refreshSearchResults, globals.timeout); 64 68 }, 65 69 error: function(jqXHR, textStatus, error) { … … 79 83 success: function(json, textStatus, jqXHR) { 80 84 if (json.requests.length === 0) { 81 clearInterval(that.state.timerId); 82 console.log("cleaned up timer"); 85 console.log("search ended"); 86 } else { 87 globals.timeout = 1.5 * globals.timeout; 88 setTimeout(that.refreshSearchResults, globals.timeout); 89 // console.log("new search in: " + globals.timeout+ "ms"); 83 90 } 84 91 that.setState({hits:json}); 92 console.log("hits:", json); 85 93 }, 86 94 error: function(jqXHR, textStatus, error) { … … 114 122 <div style={margin}> 115 123 <form className="form-inline" role="form"> 116 <label htmlFor="dropdownCorpus"className="muted">search in </label>124 <label className="muted">search in </label> 117 125 <div id="corpusSelection" style={inlinew}> 118 126 {this.renderCorpusSelection()} 119 127 </div> 120 <label htmlFor="dropdownLanguage"className="muted"> for results in </label>128 <label className="muted"> for results in </label> 121 129 <div id="languageSelection" style={inlinew}> 122 130 <LanguageSelection languages={this.state.languages} /> … … 126 134 <HitNumber onChange={this.setNumberOfResults} numberOfResults={this.state.numberOfResults} /> 127 135 </div> 128 <label htmlFor="hits"className="muted"> hits</label>136 <label className="muted"> hits</label> 129 137 </form> 130 138 </div> -
SRUAggregator/trunk/src/main/webapp/js/search.js
r5771 r5784 68 68 }, 69 69 70 handleKey: function(event) { 71 if (event.keyCode==13) { 72 this.search(); 73 } 74 }, 75 70 76 search: function() { 71 77 this.props.search(this.state.query); … … 76 82 React.createElement("input", {name: "query", type: "text", className: "form-control input-lg search", 77 83 value: this.state.query, placeholder: "Search", tabIndex: "1", 78 onChange: this.handleChange}), 84 onChange: this.handleChange, 85 onKeyDown: this.handleKey 86 }), 79 87 React.createElement("div", {className: "input-group-btn"}, 80 88 React.createElement("button", {className: "btn btn-default input-lg search", type: "submit", tabIndex: "2", onClick: this.search}, … … 105 113 }, 106 114 107 renderCorp ora: function() {115 renderCorpus: function(level, corpus) { 108 116 var that = this; 109 return this.props.corpora.map(function(corpus) { 110 if (corpus.subCorpora.length > 0) { 111 console.log("big corpus: ", corpus); 112 } 113 function toggle() { 114 corpus.checked = !corpus.checked; 115 that.setState({corpora : corpora}); 116 } 117 var bold = {fontWeight:"bold"}; 118 var spaced = {marginRight:"20px"}; 119 var topline = {borderTop:"1px solid #ddd", paddingTop:10}; 120 return React.createElement("div", {className: "row", style: topline, key: corpus.displayTerm}, 121 React.createElement("div", {className: "col-sm-2"}, that.renderCheckbox(corpus.checked, corpus.displayTerm, toggle)), 117 118 if (corpus.subCorpora.length > 0) { 119 console.log("big corpus: ", corpus); 120 } 121 function toggle() { 122 corpus.checked = !corpus.checked; 123 that.setState({corpora : corpora}); 124 } 125 var bold = {fontWeight:"bold"}; 126 var spaced = {marginRight:"20px"}; 127 var topline = {borderTop:"1px solid #ddd", paddingTop:10}; 128 return React.createElement("div", {style: topline, key: corpus.displayName}, 129 React.createElement("div", {className: "row"}, 130 React.createElement("div", {className: "col-sm-2"}, that.renderCheckbox(corpus.checked, corpus.displayName, toggle)), 122 131 React.createElement("div", {className: "col-sm-6"}, 123 React.createElement("p", null, corpus.description)132 React.createElement("p", null, level, " ", corpus.description) 124 133 ), 125 134 React.createElement("div", {className: "col-sm-4"}, … … 128 137 React.createElement("p", null, " ", React.createElement("span", null, corpus.numberOfRecords ? (corpus.numberOfRecords+" records") : "")) 129 138 ) 130 ); 131 }); 139 ), 140 corpus.subCorpora.map(this.renderCorpus.bind(this,level+1)) 141 ); 132 142 }, 133 143 … … 138 148 React.createElement("div", {className: "col-sm-10"}, React.createElement("h3", null, "Description")) 139 149 ), 140 this. renderCorpora()150 this.props.corpora.map(this.renderCorpus.bind(this,0)) 141 151 ); 142 152 } … … 151 161 }, 152 162 163 getInitialState: function () { 164 return { displayKwic: false }; 165 }, 166 167 toggleKwic: function() { 168 this.setState({displayKwic:!this.state.displayKwic}); 169 }, 170 153 171 renderResultPanels: function(corpusHit) { 154 function renderRows (hit,i) {172 function renderRowsAsHits(hit,i) { 155 173 function renderTextFragments(tf, idx) { 156 return React.createElement("span", {key: idx, className: tf.hit?"keyword ":""}, tf.text);174 return React.createElement("span", {key: idx, className: tf.hit?"keyword label label-primary":""}, tf.text); 157 175 } 158 176 return React.createElement("p", {key: i}, … … 161 179 } 162 180 163 console.log(corpusHit); 181 function renderRowsAsKwic(hit,i) { 182 var sleft={textAlign:"left", verticalAlign:"middle", width:"50%"}; 183 var scenter={textAlign:"center", verticalAlign:"middle", maxWidth:"50%"}; 184 var sright={textAlign:"right", verticalAlign:"middle", maxWidth:"50%"}; 185 return React.createElement("tr", {key: i}, 186 React.createElement("td", {style: sright}, hit.left), 187 React.createElement("td", {style: scenter, className: "keyword"}, hit.keyword), 188 React.createElement("td", {style: sleft}, hit.right) 189 ); 190 } 191 164 192 if (corpusHit.kwics.length === 0) { 165 return React.createElement("span", {key: corpusHit.corpus.displayName}); 166 } 167 return React.createElement(Panel, {header: corpusHit.corpus.displayName, key: corpusHit.corpus.displayName}, 168 corpusHit.kwics.map(renderRows) 193 return false; 194 } 195 var fulllength = {width:"100%"}; 196 var body = this.state.displayKwic ? 197 React.createElement("table", {className: "table table-condensed table-hover", style: fulllength}, 198 React.createElement("tbody", null, corpusHit.kwics.map(renderRowsAsKwic)) 199 ) : 200 React.createElement("div", null, corpusHit.kwics.map(renderRowsAsHits)); 201 return React.createElement(Panel, {corpus: corpusHit.corpus, key: corpusHit.corpus.displayName}, 202 body 169 203 ); 170 204 }, … … 178 212 React.createElement("div", {className: "progress-bar progress-bar-striped active", role: "progressbar", 179 213 'aria-valuenow': sperc, 'aria-valuemin': "0", 'aria-valuemax': "100", style: styleperc}) 180 ) : React.createElement("span", null); 214 ) : 215 React.createElement("span", null); 181 216 }, 182 217 183 218 renderMessage: function() { 184 219 var noHits = this.props.results.filter(function(corpusHit) { return corpusHit.kwics.length === 0; }); 185 return noHits.length > 0 ? (noHits.length + " other collections returned no results") : ""; 220 return noHits.length > 0 ? (noHits.length + " other collections did not return any results") : ""; 221 }, 222 223 renderKwicCheckbox: function() { 224 var inline = {display:"inline-block"}; 225 return React.createElement("div", {className: "row"}, 226 React.createElement("div", {className: "col-sm-3 col-sm-offset-9"}, 227 React.createElement("div", {className: "btn-group", style: inline}, 228 React.createElement("label", {forHtml: "inputKwic", className: "btn-default"}, 229 this.state.displayKwic ? 230 React.createElement("input", {id: "inputKwic", type: "checkbox", value: "kwic", checked: true, onChange: this.toggleKwic}) : 231 React.createElement("input", {id: "inputKwic", type: "checkbox", value: "kwic", onChange: this.toggleKwic}), 232 233 "Â " + ' ' + 234 "Display as Key Word In Context" 235 ) 236 ) 237 ) 238 ); 186 239 }, 187 240 188 241 render: function() { 189 242 var margintop = {marginTop:"10px"}; 243 var margin = {marginTop:"0", padding:"20px"}; 244 var inlinew = {display:"inline-block", margin:"0 5px 0 0", width:"240px;"}; 245 var right= {float:"right"}; 190 246 return React.createElement("div", null, 247 this.props.results.length > 0 ? this.renderKwicCheckbox() : false, 191 248 React.createElement(ReactCSSTransitionGroup, {transitionName: "fade"}, 192 249 this.props.results.map(this.renderResultPanels), -
SRUAggregator/trunk/src/main/webapp/js/search.jsx
r5771 r5784 68 68 }, 69 69 70 handleKey: function(event) { 71 if (event.keyCode==13) { 72 this.search(); 73 } 74 }, 75 70 76 search: function() { 71 77 this.props.search(this.state.query); … … 76 82 <input name="query" type="text" className="form-control input-lg search" 77 83 value={this.state.query} placeholder="Search" tabIndex="1" 78 onChange={this.handleChange}></input> 84 onChange={this.handleChange} 85 onKeyDown={this.handleKey} 86 ></input> 79 87 <div className="input-group-btn"> 80 88 <button className="btn btn-default input-lg search" type="submit" tabIndex="2" onClick={this.search}> … … 105 113 }, 106 114 107 renderCorp ora: function() {115 renderCorpus: function(level, corpus) { 108 116 var that = this; 109 return this.props.corpora.map(function(corpus) { 110 if (corpus.subCorpora.length > 0) { 111 console.log("big corpus: ", corpus); 112 } 113 function toggle() { 114 corpus.checked = !corpus.checked; 115 that.setState({corpora : corpora}); 116 } 117 var bold = {fontWeight:"bold"}; 118 var spaced = {marginRight:"20px"}; 119 var topline = {borderTop:"1px solid #ddd", paddingTop:10}; 120 return <div className="row" style={topline} key={corpus.displayTerm}> 121 <div className="col-sm-2">{that.renderCheckbox(corpus.checked, corpus.displayTerm, toggle)}</div> 117 118 if (corpus.subCorpora.length > 0) { 119 console.log("big corpus: ", corpus); 120 } 121 function toggle() { 122 corpus.checked = !corpus.checked; 123 that.setState({corpora : corpora}); 124 } 125 var bold = {fontWeight:"bold"}; 126 var spaced = {marginRight:"20px"}; 127 var topline = {borderTop:"1px solid #ddd", paddingTop:10}; 128 return <div style={topline} key={corpus.displayName}> 129 <div className="row"> 130 <div className="col-sm-2">{that.renderCheckbox(corpus.checked, corpus.displayName, toggle)}</div> 122 131 <div className="col-sm-6"> 123 <p>{ corpus.description}</p>132 <p>{level} {corpus.description}</p> 124 133 </div> 125 134 <div className="col-sm-4"> … … 128 137 <p> <span>{corpus.numberOfRecords ? (corpus.numberOfRecords+" records") : ""}</span></p> 129 138 </div> 130 </div>; 131 }); 139 </div> 140 {corpus.subCorpora.map(this.renderCorpus.bind(this,level+1))} 141 </div>; 132 142 }, 133 143 … … 138 148 <div className="col-sm-10"><h3>Description</h3></div> 139 149 </div> 140 {this. renderCorpora()}150 {this.props.corpora.map(this.renderCorpus.bind(this,0))} 141 151 </div>; 142 152 } … … 151 161 }, 152 162 163 getInitialState: function () { 164 return { displayKwic: false }; 165 }, 166 167 toggleKwic: function() { 168 this.setState({displayKwic:!this.state.displayKwic}); 169 }, 170 153 171 renderResultPanels: function(corpusHit) { 154 function renderRows (hit,i) {172 function renderRowsAsHits(hit,i) { 155 173 function renderTextFragments(tf, idx) { 156 return <span key={idx} className={tf.hit?"keyword ":""}>{tf.text}</span>;174 return <span key={idx} className={tf.hit?"keyword label label-primary":""}>{tf.text}</span>; 157 175 } 158 176 return <p key={i}> … … 161 179 } 162 180 163 console.log(corpusHit); 181 function renderRowsAsKwic(hit,i) { 182 var sleft={textAlign:"left", verticalAlign:"middle", width:"50%"}; 183 var scenter={textAlign:"center", verticalAlign:"middle", maxWidth:"50%"}; 184 var sright={textAlign:"right", verticalAlign:"middle", maxWidth:"50%"}; 185 return <tr key={i}> 186 <td style={sright}>{hit.left}</td> 187 <td style={scenter} className="keyword">{hit.keyword}</td> 188 <td style={sleft}>{hit.right}</td> 189 </tr>; 190 } 191 164 192 if (corpusHit.kwics.length === 0) { 165 return <span key={corpusHit.corpus.displayName}></span>; 166 } 167 return <Panel header={corpusHit.corpus.displayName} key={corpusHit.corpus.displayName}> 168 {corpusHit.kwics.map(renderRows)} 193 return false; 194 } 195 var fulllength = {width:"100%"}; 196 var body = this.state.displayKwic ? 197 <table className="table table-condensed table-hover" style={fulllength}> 198 <tbody>{corpusHit.kwics.map(renderRowsAsKwic)}</tbody> 199 </table> : 200 <div>{corpusHit.kwics.map(renderRowsAsHits)}</div>; 201 return <Panel corpus={corpusHit.corpus} key={corpusHit.corpus.displayName}> 202 {body} 169 203 </Panel>; 170 204 }, … … 178 212 <div className="progress-bar progress-bar-striped active" role="progressbar" 179 213 aria-valuenow={sperc} aria-valuemin="0" aria-valuemax="100" style={styleperc} /> 180 </div> : <span />; 214 </div> : 215 <span />; 181 216 }, 182 217 183 218 renderMessage: function() { 184 219 var noHits = this.props.results.filter(function(corpusHit) { return corpusHit.kwics.length === 0; }); 185 return noHits.length > 0 ? (noHits.length + " other collections returned no results") : ""; 220 return noHits.length > 0 ? (noHits.length + " other collections did not return any results") : ""; 221 }, 222 223 renderKwicCheckbox: function() { 224 var inline = {display:"inline-block"}; 225 return <div className="row"> 226 <div className="col-sm-3 col-sm-offset-9"> 227 <div className="btn-group" style={inline}> 228 <label forHtml="inputKwic" className="btn-default"> 229 { this.state.displayKwic ? 230 <input id="inputKwic" type="checkbox" value="kwic" checked onChange={this.toggleKwic} /> : 231 <input id="inputKwic" type="checkbox" value="kwic" onChange={this.toggleKwic} /> 232 } 233 234 Display as Key Word In Context 235 </label> 236 </div> 237 </div> 238 </div>; 186 239 }, 187 240 188 241 render: function() { 189 242 var margintop = {marginTop:"10px"}; 243 var margin = {marginTop:"0", padding:"20px"}; 244 var inlinew = {display:"inline-block", margin:"0 5px 0 0", width:"240px;"}; 245 var right= {float:"right"}; 190 246 return <div> 247 {this.props.results.length > 0 ? this.renderKwicCheckbox() : false} 191 248 <ReactCSSTransitionGroup transitionName="fade"> 192 249 {this.props.results.map(this.renderResultPanels)}
Note: See TracChangeset
for help on using the changeset viewer.