1 | /** @jsx React.DOM */ |
---|
2 | (function() { |
---|
3 | "use strict"; |
---|
4 | |
---|
5 | var PT = React.PropTypes; |
---|
6 | var ReactCSSTransitionGroup = window.React.addons.CSSTransitionGroup; |
---|
7 | // own components |
---|
8 | var Panel = window.MyReact.Panel; |
---|
9 | |
---|
10 | |
---|
11 | ///////////////////////////////// |
---|
12 | |
---|
13 | var SearchCorpusBox = React.createClass({ |
---|
14 | propTypes: { |
---|
15 | search: PT.func.isRequired, |
---|
16 | }, |
---|
17 | |
---|
18 | getInitialState: function () { |
---|
19 | return { |
---|
20 | query: "" |
---|
21 | }; |
---|
22 | }, |
---|
23 | |
---|
24 | handleChange: function(event) { |
---|
25 | this.setState({query: event.target.value}); |
---|
26 | this.props.search(event.target.value); |
---|
27 | event.stopPropagation(); |
---|
28 | }, |
---|
29 | |
---|
30 | handleKey: function(event) { |
---|
31 | if (event.keyCode==13) { |
---|
32 | this.props.search(event.target.value); |
---|
33 | } |
---|
34 | }, |
---|
35 | |
---|
36 | render: function() { |
---|
37 | return <div className="form-group"> |
---|
38 | <input className="form-control search search-collection" type="text" |
---|
39 | value={this.state.query} placeholder="Search for collection" |
---|
40 | onChange={this.handleChange} /> |
---|
41 | </div>; |
---|
42 | } |
---|
43 | }); |
---|
44 | |
---|
45 | var CorpusView = React.createClass({ |
---|
46 | propTypes: { |
---|
47 | corpora: PT.object.isRequired, |
---|
48 | }, |
---|
49 | |
---|
50 | toggleSelection: function (corpus) { |
---|
51 | var s = !corpus.selected; |
---|
52 | this.props.corpora.recurseCorpus(corpus, function(c) { c.selected = s; }); |
---|
53 | this.props.corpora.update(); |
---|
54 | }, |
---|
55 | |
---|
56 | toggleExpansion: function (corpus) { |
---|
57 | corpus.expanded = !corpus.expanded; |
---|
58 | this.props.corpora.update(); |
---|
59 | }, |
---|
60 | |
---|
61 | selectAll: function(value) { |
---|
62 | this.props.corpora.recurse(function(c) { c.selected = value; }); |
---|
63 | this.props.corpora.update(); |
---|
64 | }, |
---|
65 | |
---|
66 | searchCorpus: function(query) { |
---|
67 | query = query.toLowerCase(); |
---|
68 | var querytokens = query.split(" "); |
---|
69 | if (!query) { |
---|
70 | this.props.corpora.recurse(function(corpus) {corpus.priority = 1; }); |
---|
71 | this.props.corpora.update(); |
---|
72 | return; |
---|
73 | } |
---|
74 | |
---|
75 | // clean up all priorities |
---|
76 | this.props.corpora.recurse(function(corpus) { |
---|
77 | corpus.priority = 0; |
---|
78 | }); |
---|
79 | |
---|
80 | // find priority for each corpus |
---|
81 | this.props.corpora.recurse(function(corpus){ |
---|
82 | querytokens.forEach(function(qtoken){ |
---|
83 | if (corpus.displayName && corpus.displayName.toLowerCase().indexOf(qtoken) >= 0) { |
---|
84 | corpus.priority ++; |
---|
85 | // console.log(corpus.displayName, "name ++"); |
---|
86 | } |
---|
87 | if (corpus.description && corpus.description.toLowerCase().indexOf(qtoken) >= 0) { |
---|
88 | corpus.priority ++; |
---|
89 | // console.log(corpus.displayName, "desc ++"); |
---|
90 | } |
---|
91 | if (corpus.institution && corpus.institution.name && |
---|
92 | corpus.institution.name.toLowerCase().indexOf(qtoken) >= 0) { |
---|
93 | corpus.priority ++; |
---|
94 | // console.log(corpus.displayName, "inst ++"); |
---|
95 | } |
---|
96 | if (corpus.languages){ |
---|
97 | corpus.languages.forEach(function(lang){ |
---|
98 | if (lang.toLowerCase().indexOf(qtoken) >= 0){ |
---|
99 | corpus.priority ++; |
---|
100 | // console.log(corpus.displayName, "lang ++"); |
---|
101 | } |
---|
102 | }); |
---|
103 | } |
---|
104 | }); |
---|
105 | }); |
---|
106 | |
---|
107 | // ensure root corpora have nonnull priority |
---|
108 | this.props.corpora.recurse(function(corpus){ |
---|
109 | if (corpus.subCorpora) { |
---|
110 | corpus.subCorpora.forEach(function(subcorpus){ |
---|
111 | if (subcorpus.priority > 0 && corpus.priority === 0) |
---|
112 | corpus.priority ++; |
---|
113 | }); |
---|
114 | } |
---|
115 | }); |
---|
116 | |
---|
117 | // order (descending priority) |
---|
118 | var sortFn = function(a, b){ |
---|
119 | if (b.priority === a.priority) { |
---|
120 | return b.index - a.index; // make it a stable sort |
---|
121 | } |
---|
122 | return b.priority - a.priority; |
---|
123 | }; |
---|
124 | |
---|
125 | this.props.corpora.recurse(function(corpus){ |
---|
126 | if (corpus.subCorpora) |
---|
127 | corpus.subCorpora.sort(sortFn); |
---|
128 | }); |
---|
129 | this.props.corpora.corpora.sort(sortFn); |
---|
130 | |
---|
131 | // display |
---|
132 | this.props.corpora.update(); |
---|
133 | // console.log("corpus search done", query); |
---|
134 | }, |
---|
135 | |
---|
136 | renderCheckbox: function(corpus) { |
---|
137 | return <button className="btn btn-default"> |
---|
138 | { corpus.selected ? |
---|
139 | <span className="glyphicon glyphicon-check" aria-hidden="true"/> : |
---|
140 | <span className="glyphicon glyphicon-unchecked" aria-hidden="true"/> |
---|
141 | } |
---|
142 | </button>; |
---|
143 | }, |
---|
144 | |
---|
145 | renderExpansion: function(corpus) { |
---|
146 | if (!corpus.subCorpora || corpus.subCorpora.length === 0) { |
---|
147 | return false; |
---|
148 | } |
---|
149 | return <div className="expansion-handle" onClick={this.toggleExpansion.bind(this,corpus)}> |
---|
150 | <a> {corpus.expanded ? |
---|
151 | <span className="glyphicon glyphicon-collapse-down" aria-hidden="true"/>: |
---|
152 | <span className="glyphicon glyphicon-expand" aria-hidden="true"/> |
---|
153 | } |
---|
154 | {corpus.expanded ? " Collapse ":" Expand "} {corpus.subCorpora.length} subcollections |
---|
155 | </a> |
---|
156 | </div>; |
---|
157 | }, |
---|
158 | |
---|
159 | renderCorpus: function(level, corpus) { |
---|
160 | var indent = {marginLeft:level*50}; |
---|
161 | var corpusContainerClass = "corpus-container "+(corpus.priority>0?"":"dimmed"); |
---|
162 | return <div className={corpusContainerClass} key={corpus.displayName}> |
---|
163 | <div className="row corpus"> |
---|
164 | <div className="col-sm-1 vcenter" onClick={this.toggleSelection.bind(this,corpus)}> |
---|
165 | {this.renderCheckbox(corpus)} |
---|
166 | </div> |
---|
167 | <div className="col-sm-8 vcenter"> |
---|
168 | <div style={indent}> |
---|
169 | <h3>{corpus.displayName} :{corpus.priority}</h3> |
---|
170 | <p>{corpus.description}</p> |
---|
171 | {this.renderExpansion(corpus)} |
---|
172 | </div> |
---|
173 | </div> |
---|
174 | <div className="col-sm-3 vcenter"> |
---|
175 | <p><i className="fa fa-institution"/> {corpus.institution.name}</p> |
---|
176 | <p><i className="fa fa-language"/> {corpus.languages.join(" ")}</p> |
---|
177 | </div> |
---|
178 | </div> |
---|
179 | {corpus.expanded ? corpus.subCorpora.map(this.renderCorpus.bind(this,level+1)) : false} |
---|
180 | </div>; |
---|
181 | }, |
---|
182 | |
---|
183 | render: function() { |
---|
184 | return <div style={{margin: "0 30px"}}> |
---|
185 | <div className="row"> |
---|
186 | <div className="float-right"> |
---|
187 | <div className="inline" style={{ marginRight: 20 }} > |
---|
188 | <SearchCorpusBox search={this.searchCorpus}/> |
---|
189 | </div> |
---|
190 | <button className="btn btn-default" style={{ marginRight: 10 }} onClick={this.selectAll.bind(this,true)}> |
---|
191 | {" Select all"}</button> |
---|
192 | <button className="btn btn-default" style={{ marginRight: 20 }} onClick={this.selectAll.bind(this,false)}> |
---|
193 | {" Deselect all"}</button> |
---|
194 | </div> |
---|
195 | </div> |
---|
196 | {this.props.corpora.corpora.map(this.renderCorpus.bind(this,0))} |
---|
197 | </div>; |
---|
198 | } |
---|
199 | }); |
---|
200 | |
---|
201 | ///////////////////////////////// |
---|
202 | |
---|
203 | if (!window.MyAggregator) { |
---|
204 | window.MyAggregator = {}; |
---|
205 | } |
---|
206 | window.MyAggregator.CorpusView = CorpusView; |
---|
207 | })(); |
---|