source: SRUAggregator/trunk/src/main/resources/assets/js/corpora.jsx @ 5957

Last change on this file since 5957 was 5957, checked in by emanuel.dima@uni-tuebingen.de, 9 years ago
  1. alpha15: added support for new spec explains/search; misc UI corrections
File size: 7.9 KB
Line 
1/** @jsx React.DOM */
2(function() {
3"use strict";
4
5window.MyAggregator = window.MyAggregator || {};
6
7var PT = React.PropTypes;
8var ReactCSSTransitionGroup = window.React.addons.CSSTransitionGroup;
9// own components
10var Panel = window.MyReact.Panel;
11
12
13/////////////////////////////////
14
15var SearchCorpusBox = React.createClass({
16        propTypes: {
17                search: PT.func.isRequired,
18        },
19
20        getInitialState: function () {
21                return {
22                        query: ""
23                };
24        },
25
26        handleChange: function(event) {
27                var query = event.target.value;
28                this.setState({query: query});
29
30                if (query.length === 0 || 2 <= query.length) {
31                        this.props.search(query);
32                }
33                event.stopPropagation();
34        },
35
36        handleKey: function(event) {
37                if (event.keyCode==13) {
38                        this.props.search(event.target.value);
39                }
40        },
41
42        render: function() {
43                return  <div className="form-group">
44                                        <input className="form-control search search-collection" type="text"
45                                                value={this.state.query} placeholder="Search for collection"
46                                                onChange={this.handleChange} />
47                                </div>;
48        }
49});
50
51var CorpusView = window.MyAggregator.CorpusView = React.createClass({
52        propTypes: {
53                corpora: PT.object.isRequired,
54                languageMap: PT.object.isRequired,
55        },
56
57        toggleSelection: function (corpus, e) {
58                var s = !corpus.selected;
59                this.props.corpora.recurseCorpus(corpus, function(c) { c.selected = s; });
60                this.props.corpora.update();
61                this.stop(e);
62        },
63
64        toggleExpansion: function (corpus) {
65                corpus.expanded = !corpus.expanded;
66                this.props.corpora.update();
67        },
68
69        selectAll: function(value) {
70                this.props.corpora.recurse(function(c) { c.selected = value; });
71                this.props.corpora.update();
72        },
73
74        searchCorpus: function(query) {
75                // sort fn: descending priority, stable sort
76                var sortFn = function(a, b){
77                        if (b.priority === a.priority) {
78                                return b.index - a.index; // stable sort
79                        }
80                        return b.priority - a.priority;
81                };
82
83                query = query.toLowerCase();
84                if (!query) {
85                        this.props.corpora.recurse(function(corpus) {corpus.priority = 1; });
86                        this.props.corpora.update();
87                        return;
88                }
89
90                // clean up all priorities
91                this.props.corpora.recurse(function(corpus) {
92                        corpus.priority = 0;
93                });
94
95                // find priority for each corpus
96                var querytokens = query.split(" ").filter(function(x){ return x.length > 0; });
97                this.props.corpora.recurse(function(corpus){
98                        var title = corpus.title;
99                        querytokens.forEach(function(qtoken){
100                                if (title && title.toLowerCase().indexOf(qtoken) >= 0) {
101                                        corpus.priority ++;
102                                }
103                                if (corpus.description && corpus.description.toLowerCase().indexOf(qtoken) >= 0) {
104                                        corpus.priority ++;
105                                }
106                                if (corpus.institution && corpus.institution.name &&
107                                                corpus.institution.name.toLowerCase().indexOf(qtoken) >= 0) {
108                                        corpus.priority ++;
109                                }
110                                if (corpus.languages){
111                                        corpus.languages.forEach(function(lang){
112                                                if (lang.toLowerCase().indexOf(qtoken) >= 0){
113                                                        corpus.priority ++;
114                                                }
115                                        });
116                                        corpus.languages.forEach(function(lang){
117                                                if (this.props.languageMap[lang].toLowerCase().indexOf(qtoken) >= 0){
118                                                        corpus.priority ++;
119                                                }
120                                        }.bind(this));
121                                }
122                        }.bind(this));
123                }.bind(this));
124
125                // ensure parents of visible corpora are also visible; maximum depth = 3
126                var isVisibleFn = function(corpus){ return corpus.priority > 0; };
127                var parentBooster = function(corpus){
128                        if (corpus.priority <= 0 && corpus.subCorpora) {
129                                if (corpus.subCorpora.some(isVisibleFn)) {
130                                        corpus.priority = 0.5;
131                                }
132                        }
133                };
134                for (var i = 3; i > 0; i --) {
135                        this.props.corpora.recurse(parentBooster);
136                }
137
138                this.props.corpora.recurse(function(corpus) { corpus.subCorpora.sort(sortFn); });
139                this.props.corpora.corpora.sort(sortFn);
140
141                // display
142                this.props.corpora.update();
143        },
144
145        stop: function(e) {
146                e.stopPropagation();
147        },
148
149        getMinMaxPriority: function() {
150                var min = 1, max = 0;
151                this.props.corpora.recurse(function(c) {
152                        if (c.priority < min) min = c.priority;
153                        if (max < c.priority) max = c.priority;
154                });
155                return [min, max];
156        },
157
158        renderCheckbox: function(corpus) {
159                return  <button className="btn btn-default">
160                                        { corpus.selected ?
161                                                <span className="glyphicon glyphicon-check" aria-hidden="true"/> :
162                                                <span className="glyphicon glyphicon-unchecked" aria-hidden="true"/>
163                                        }
164                                </button>;
165        },
166
167        renderExpansion: function(corpus) {
168                if (!corpus.subCorpora || corpus.subCorpora.length === 0) {
169                        return false;
170                }
171                return  <div className="expansion-handle" style={{}}>
172                                        <a>
173                                                {corpus.expanded ?
174                                                        <span className="glyphicon glyphicon-minus" aria-hidden="true"/>:
175                                                        <span className="glyphicon glyphicon-plus" aria-hidden="true"/>
176                                                }
177                                                {corpus.expanded ? " Collapse ":" Expand "} ({corpus.subCorpora.length} subcollections)
178                                        </a>
179                                </div>;
180        },
181
182        renderLanguages: function(languages) {
183                return languages
184                                .map(function(l) { return this.props.languageMap[l]; }.bind(this))
185                                .sort()
186                                .join(", ");
187        },
188
189        renderFilteredMessage: function() {
190                var total = 0;
191                var visible = 0;
192                this.props.corpora.recurse(function(corpus){
193                        if (corpus.visible) {
194                                total ++;
195                                if (corpus.priority > 0) {
196                                        visible++;
197                                }
198                        }
199                });
200                if (visible === total) {
201                        return false;
202                }
203                return  <div> Showing {visible} out of {total} (sub)collections. </div>;
204        },
205
206        renderCorpus: function(level, minmaxp, corpus) {
207                if (!corpus.visible || corpus.priority <= 0) {
208                        return false;
209                }
210
211                var indent = {marginLeft:level*50};
212                var corpusContainerClass = "corpus-container "+(corpus.priority>0?"":"dimmed");
213
214                var hue = 120 * corpus.priority / minmaxp[1];
215                var color = minmaxp[0] === minmaxp[1] ? 'transparent' : 'hsl('+hue+', 50%, 50%)';
216                var priorityStyle = {paddingBottom: 4, paddingLeft: 2, borderBottom: '3px solid '+color };
217                var expansive = corpus.expanded ? {overflow:'hidden'}
218                        : {whiteSpace:'nowrap', overflow:'hidden', textOverflow: 'ellipsis'};
219                return  <div className={corpusContainerClass} key={corpus.title}>
220                                        <div className="row corpus" onClick={this.toggleExpansion.bind(this, corpus)}>
221                                                <div className="col-sm-1 vcenter">
222                                                                <div className="inline" style={priorityStyle} onClick={this.toggleSelection.bind(this,corpus)}>
223                                                                        {this.renderCheckbox(corpus)}
224                                                                </div>
225                                                </div>
226                                                <div className="col-sm-8 vcenter">
227                                                        <div style={indent}>
228                                                                <h3 style={expansive}>
229                                                                        {corpus.title}
230                                                                        { corpus.landingPage ?
231                                                                                <a href={corpus.landingPage} onClick={this.stop}>
232                                                                                        <span style={{fontSize:12}}> – Homepage </span><i className="fa fa-home"/>
233                                                                                </a>: false}
234                                                                </h3>
235
236
237                                                                <p style={expansive}>{corpus.description}</p>
238                                                                {this.renderExpansion(corpus)}
239                                                        </div>
240                                                </div>
241                                                <div className="col-sm-3 vcenter">
242                                                        <p style={expansive}>
243                                                                <i className="fa fa-institution"/> {corpus.institution.name}
244                                                        </p>
245                                                        <p style={expansive}>
246                                                                <i className="fa fa-language"/> {this.renderLanguages(corpus.languages)}
247                                                        </p>
248                                                </div>
249                                        </div>
250                                        {corpus.expanded ? corpus.subCorpora.map(this.renderCorpus.bind(this, level+1, minmaxp)) : false}
251                                </div>;
252        },
253
254        render: function() {
255                var minmaxp = this.getMinMaxPriority();
256                return  <div style={{margin: "0 30px"}}>
257                                        <div className="row">
258                                                <div className="float-left inline">
259                                                        <h3 style={{marginTop:10}}>
260                                                                {this.props.corpora.getSelectedMessage()}
261                                                        </h3>
262                                                </div>
263                                                <div className="float-right inline">
264                                                        <button className="btn btn-default" style={{ marginRight: 10 }} onClick={this.selectAll.bind(this,true)}>
265                                                                {" Select all"}</button>
266                                                        <button className="btn btn-default" style={{ marginRight: 20 }} onClick={this.selectAll.bind(this,false)}>
267                                                                {" Deselect all"}</button>
268                                                </div>
269                                                <div className="float-right inline">
270                                                        <div className="inline" style={{ marginRight: 20 }} >
271                                                                <SearchCorpusBox search={this.searchCorpus}/>
272                                                                {this.renderFilteredMessage()}
273                                                        </div>
274                                                </div>
275                                        </div>
276                                       
277                                        {this.props.corpora.corpora.map(this.renderCorpus.bind(this, 0, minmaxp))}
278                                </div>;
279        }
280});
281
282})();
Note: See TracBrowser for help on using the repository browser.