1 | import AboutPage from "./pages/aboutpage.jsx"; |
---|
2 | import AggregatorPage from "./pages/aggregatorpage.jsx"; |
---|
3 | import HelpPage from "./pages/helppage.jsx"; |
---|
4 | import StatisticsPage from "./pages/statisticspage.jsx"; |
---|
5 | import ErrorPane from "./components/errorpane.jsx"; |
---|
6 | import Footer from "./components/footer.jsx"; |
---|
7 | import EmbeddedFooter from "./components/embeddedfooter.jsx"; |
---|
8 | import PropTypes from "prop-types"; |
---|
9 | import createReactClass from "create-react-class"; |
---|
10 | |
---|
11 | (function() { |
---|
12 | "use strict"; |
---|
13 | |
---|
14 | window.MyAggregator = window.MyAggregator || {}; |
---|
15 | |
---|
16 | var VERSION = window.MyAggregator.VERSION = "v.3.0.0-64"; |
---|
17 | |
---|
18 | var URLROOT = window.MyAggregator.URLROOT = |
---|
19 | window.location.pathname.substring(0, window.location.pathname.indexOf("/", 2)) || |
---|
20 | //window.location.pathname || |
---|
21 | //"/ws/fcs/2.0/aggregator"; |
---|
22 | "/Aggregator"; |
---|
23 | |
---|
24 | |
---|
25 | var PT = PropTypes; |
---|
26 | |
---|
27 | /** |
---|
28 | The FCS Aggregator UI is based on reactjs. |
---|
29 | - index.html: describes the general page structure, with a push-down footer; |
---|
30 | on that structure the Main and Footer components are plugged. |
---|
31 | - main.jsx: composes the simple top components (Main, AggregatorPage, HelpPage, |
---|
32 | AboutPage, StatisticsPage) in pages/ |
---|
33 | - pages/aggregatorpage.jsx: defines |
---|
34 | - the Corpora store of collections |
---|
35 | - the AggregatorPage component which deals with search and displays the search results |
---|
36 | - components/corpusview.jsx: defines the CorpusView, rendered when the user views the available collections |
---|
37 | - plus in components/: various general usage React components |
---|
38 | |
---|
39 | The top-most component, Main, tracks of the window's location URL and, depending on the value, |
---|
40 | renders various components inside its frame: |
---|
41 | - AggregatorPage is the view corresponding to the normal search UI (search bar and all) |
---|
42 | This is the most complex component. |
---|
43 | - HelpPage renders the help page |
---|
44 | - AboutPage renders the about page |
---|
45 | - StatisticsPage renders the stats page |
---|
46 | - another URL, /Aggregator/embed, determines Main and AggregatorPage to render just the search bar. |
---|
47 | The embedded view is supposed to work like a YouTube embedded clip. |
---|
48 | */ |
---|
49 | |
---|
50 | var Main = createReactClass({ |
---|
51 | // fixme! - class Main extends React.Component { |
---|
52 | componentWillMount: function() { |
---|
53 | routeFromLocation.bind(this)(); |
---|
54 | }, |
---|
55 | |
---|
56 | getInitialState: function () { |
---|
57 | return { |
---|
58 | navbarCollapse: false, |
---|
59 | navbarPageFn: this.renderAggregator, |
---|
60 | errorMessages: [], |
---|
61 | }; |
---|
62 | }, |
---|
63 | |
---|
64 | error: function(errObj) { |
---|
65 | var err = ""; |
---|
66 | if (typeof errObj === 'string' || errObj instanceof String) { |
---|
67 | err = errObj; |
---|
68 | } else if (typeof errObj === 'object' && errObj.statusText) { |
---|
69 | console.log("ERROR: jqXHR = ", errObj); |
---|
70 | err = errObj.statusText; |
---|
71 | } else { |
---|
72 | return; |
---|
73 | } |
---|
74 | |
---|
75 | var that = this; |
---|
76 | var errs = this.state.errorMessages.slice(); |
---|
77 | errs.push(err); |
---|
78 | this.setState({errorMessages: errs}); |
---|
79 | |
---|
80 | setTimeout(function() { |
---|
81 | var errs = that.state.errorMessages.slice(); |
---|
82 | errs.shift(); |
---|
83 | that.setState({errorMessages: errs}); |
---|
84 | }, 10000); |
---|
85 | }, |
---|
86 | |
---|
87 | ajax: function(ajaxObject) { |
---|
88 | var that = this; |
---|
89 | if (!ajaxObject.error) { |
---|
90 | ajaxObject.error = function(jqXHR, textStatus, error) { |
---|
91 | if (jqXHR.readyState === 0) { |
---|
92 | that.error("Network error, please check your internet connection"); |
---|
93 | } else if (jqXHR.responseText) { |
---|
94 | that.error(jqXHR.responseText + " ("+error+")"); |
---|
95 | } else { |
---|
96 | that.error(error + " ("+textStatus+")"); |
---|
97 | } |
---|
98 | console.log("ajax error, jqXHR: ", jqXHR); |
---|
99 | }; |
---|
100 | } |
---|
101 | // console.log("ajax", ajaxObject); |
---|
102 | jQuery.ajax(ajaxObject); |
---|
103 | }, |
---|
104 | |
---|
105 | toggleCollapse: function() { |
---|
106 | this.setState({navbarCollapse: !this.state.navbarCollapse}); |
---|
107 | }, |
---|
108 | |
---|
109 | renderAggregator: function() { |
---|
110 | return <AggregatorPage ajax={this.ajax} error={this.error} embedded={false}/>; |
---|
111 | }, |
---|
112 | |
---|
113 | renderHelp: function() { |
---|
114 | return <HelpPage />; |
---|
115 | }, |
---|
116 | |
---|
117 | renderAbout: function() { |
---|
118 | return <AboutPage toStatistics={this.toStatistics} />; |
---|
119 | }, |
---|
120 | |
---|
121 | renderStatistics: function() { |
---|
122 | return <StatisticsPage ajax={this.ajax} />; |
---|
123 | }, |
---|
124 | |
---|
125 | renderEmbedded: function() { |
---|
126 | return <AggregatorPage ajax={this.ajax} error={this.error} embedded={true}/>; |
---|
127 | }, |
---|
128 | |
---|
129 | getPageFns: function() { |
---|
130 | return { |
---|
131 | '': this.renderAggregator, |
---|
132 | 'help': this.renderHelp, |
---|
133 | 'about': this.renderAbout, |
---|
134 | 'stats': this.renderStatistics, |
---|
135 | 'embed': this.renderEmbedded, |
---|
136 | }; |
---|
137 | }, |
---|
138 | |
---|
139 | gotoPage: function(doPushHistory, pageFnName) { |
---|
140 | var pageFn = this.getPageFns()[pageFnName]; |
---|
141 | if (this.state.navbarPageFn !== pageFn) { |
---|
142 | if (doPushHistory) { |
---|
143 | window.history.pushState({page:pageFnName}, '', URLROOT+"/"+pageFnName); |
---|
144 | } |
---|
145 | this.setState({navbarPageFn: pageFn}); |
---|
146 | console.log("new page: " + document.location + ", name: " + pageFnName); |
---|
147 | } |
---|
148 | }, |
---|
149 | |
---|
150 | toAggregator: function(doPushHistory) { this.gotoPage(doPushHistory, ''); }, |
---|
151 | toHelp: function(doPushHistory) { this.gotoPage(doPushHistory, 'help'); }, |
---|
152 | toAbout: function(doPushHistory) { this.gotoPage(doPushHistory, 'about'); }, |
---|
153 | toStatistics: function(doPushHistory) { this.gotoPage(doPushHistory, 'stats'); }, |
---|
154 | toEmbedded: function(doPushHistory) { this.gotoPage(doPushHistory, 'embed'); }, |
---|
155 | |
---|
156 | renderLogin: function() { |
---|
157 | return false; |
---|
158 | // return <li className="unauthenticated"> |
---|
159 | // <a href="login" tabIndex="-1"><span className="glyphicon glyphicon-log-in"></span> LOGIN</a> |
---|
160 | // </li>; |
---|
161 | }, |
---|
162 | |
---|
163 | renderCollapsible: function() { |
---|
164 | var classname = "navbar-collapse collapse " + (this.state.navbarCollapse?"in":""); |
---|
165 | return ( |
---|
166 | <div className={classname}> |
---|
167 | <ul className="nav navbar-nav"> |
---|
168 | <li className={this.state.navbarPageFn === this.renderAggregator ? "active":""}> |
---|
169 | <a className="link" tabIndex="-1" onClick={this.toAggregator.bind(this, true)}>Aggregator</a> |
---|
170 | </li> |
---|
171 | <li className={this.state.navbarPageFn === this.renderHelp ? "active":""}> |
---|
172 | <a className="link" tabIndex="-1" onClick={this.toHelp.bind(this, true)}>Help</a> |
---|
173 | </li> |
---|
174 | </ul> |
---|
175 | <ul className="nav navbar-nav navbar-right"> |
---|
176 | <li> <div id="clarinservices" style={{padding:4}}/> </li> |
---|
177 | {this.renderLogin()} |
---|
178 | </ul> |
---|
179 | </div> |
---|
180 | ); |
---|
181 | }, |
---|
182 | |
---|
183 | renderTop: function() { |
---|
184 | if (this.state.navbarPageFn === this.renderEmbedded) { |
---|
185 | return false; |
---|
186 | } |
---|
187 | return ( |
---|
188 | <div> |
---|
189 | <div className="navbar navbar-default navbar-static-top" role="navigation"> |
---|
190 | <div className="container"> |
---|
191 | <div className="navbar-header"> |
---|
192 | <button type="button" className="navbar-toggle" onClick={this.toggleCollapse}> |
---|
193 | <span className="sr-only">Toggle navigation</span> |
---|
194 | <span className="icon-bar"></span> |
---|
195 | <span className="icon-bar"></span> |
---|
196 | <span className="icon-bar"></span> |
---|
197 | </button> |
---|
198 | <a className="navbar-brand" href={URLROOT + "/"} tabIndex="-1"> |
---|
199 | <img width="28px" height="28px" src="img/magglass1.png"/> |
---|
200 | <header className="inline"> Federated Content Search </header> |
---|
201 | </a> |
---|
202 | </div> |
---|
203 | {this.renderCollapsible()} |
---|
204 | </div> |
---|
205 | </div> |
---|
206 | |
---|
207 | <ErrorPane errorMessages={this.state.errorMessages} /> |
---|
208 | |
---|
209 | </div> |
---|
210 | ); |
---|
211 | }, |
---|
212 | |
---|
213 | render: function() { |
---|
214 | return ( |
---|
215 | <div> |
---|
216 | <div> { this.renderTop() } </div> |
---|
217 | |
---|
218 | <div id="push"> |
---|
219 | <div className="container"> |
---|
220 | {this.state.navbarPageFn()} |
---|
221 | </div> |
---|
222 | <div className="top-gap" /> |
---|
223 | </div> |
---|
224 | </div> |
---|
225 | ); |
---|
226 | } |
---|
227 | }); |
---|
228 | |
---|
229 | // StatisticsPage |
---|
230 | |
---|
231 | // HelpPage |
---|
232 | |
---|
233 | // AboutPage |
---|
234 | |
---|
235 | // Footer |
---|
236 | |
---|
237 | // EmbeddedFooter |
---|
238 | |
---|
239 | function isEmbeddedView() { |
---|
240 | var path = window.location.pathname.split('/'); |
---|
241 | return (path.length >= 3 && path[path.length - 1] === 'embed'); |
---|
242 | } |
---|
243 | |
---|
244 | function endsWith(str, suffix) { |
---|
245 | return str.indexOf(suffix, str.length - suffix.length) !== -1; |
---|
246 | } |
---|
247 | |
---|
248 | var routeFromLocation = function() { |
---|
249 | console.log("routeFromLocation: " + document.location); |
---|
250 | if (!this) throw "routeFromLocation must be bound to main"; |
---|
251 | var path = window.location.pathname.split('/'); |
---|
252 | console.log("path: " + path); |
---|
253 | if (path.length >= 3) { |
---|
254 | var p = path[path.length - 1]; |
---|
255 | if (p === 'help') { |
---|
256 | this.toHelp(false); |
---|
257 | } else if (p === 'about') { |
---|
258 | this.toAbout(false); |
---|
259 | } else if (p === 'stats') { |
---|
260 | this.toStatistics(false); |
---|
261 | } else if (p === 'embed') { |
---|
262 | this.toEmbedded(false); |
---|
263 | } else { |
---|
264 | this.toAggregator(false); |
---|
265 | } |
---|
266 | } else { |
---|
267 | this.toAggregator(false); |
---|
268 | } |
---|
269 | }; |
---|
270 | |
---|
271 | var main = ReactDOM.render(<Main />, document.getElementById('body')); |
---|
272 | if (!isEmbeddedView()) { |
---|
273 | ReactDOM.render(<Footer VERSION={VERSION} toAbout={main.toAbout}/>, document.getElementById('footer') ); |
---|
274 | } else { |
---|
275 | ReactDOM.render(<EmbeddedFooter URLROOT={URLROOT} />, document.getElementById('footer') ); |
---|
276 | if (jQuery) { jQuery('body, #footer').addClass('embedded'); } |
---|
277 | } |
---|
278 | |
---|
279 | window.onpopstate = routeFromLocation.bind(main); |
---|
280 | |
---|
281 | })(); |
---|