[7148] | 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 | |
---|
[5897] | 11 | (function() { |
---|
| 12 | "use strict"; |
---|
[5720] | 13 | |
---|
[7148] | 14 | window.MyAggregator = window.MyAggregator || {}; |
---|
[6144] | 15 | |
---|
[7223] | 16 | var VERSION = window.MyAggregator.VERSION = "v.3.0.0-64"; |
---|
[7148] | 17 | |
---|
[6144] | 18 | var URLROOT = window.MyAggregator.URLROOT = |
---|
[7223] | 19 | window.location.pathname.substring(0, window.location.pathname.indexOf("/", 2)) || |
---|
| 20 | //window.location.pathname || |
---|
[7228] | 21 | //"/ws/fcs/2.0/aggregator"; |
---|
[6129] | 22 | "/Aggregator"; |
---|
[5971] | 23 | |
---|
[7228] | 24 | |
---|
[7148] | 25 | var PT = PropTypes; |
---|
[5758] | 26 | |
---|
[6172] | 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. |
---|
[7148] | 31 | - main.jsx: composes the simple top components (Main, AggregatorPage, HelpPage, |
---|
| 32 | AboutPage, StatisticsPage) in pages/ |
---|
| 33 | - pages/aggregatorpage.jsx: defines |
---|
[6172] | 34 | - the Corpora store of collections |
---|
| 35 | - the AggregatorPage component which deals with search and displays the search results |
---|
[7148] | 36 | - components/corpusview.jsx: defines the CorpusView, rendered when the user views the available collections |
---|
| 37 | - plus in components/: various general usage React components |
---|
[6172] | 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 |
---|
[7148] | 44 | - AboutPage renders the about page |
---|
| 45 | - StatisticsPage renders the stats page |
---|
[6172] | 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 | |
---|
[7148] | 50 | var Main = createReactClass({ |
---|
| 51 | // fixme! - class Main extends React.Component { |
---|
[5976] | 52 | componentWillMount: function() { |
---|
[6123] | 53 | routeFromLocation.bind(this)(); |
---|
[5976] | 54 | }, |
---|
| 55 | |
---|
[5720] | 56 | getInitialState: function () { |
---|
| 57 | return { |
---|
[5890] | 58 | navbarCollapse: false, |
---|
| 59 | navbarPageFn: this.renderAggregator, |
---|
| 60 | errorMessages: [], |
---|
[5720] | 61 | }; |
---|
| 62 | }, |
---|
| 63 | |
---|
[5890] | 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 | |
---|
[5720] | 75 | var that = this; |
---|
[5890] | 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}); |
---|
[5897] | 84 | }, 10000); |
---|
[5890] | 85 | }, |
---|
[6144] | 86 | |
---|
[5890] | 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"); |
---|
[5897] | 93 | } else if (jqXHR.responseText) { |
---|
| 94 | that.error(jqXHR.responseText + " ("+error+")"); |
---|
| 95 | } else { |
---|
[5890] | 96 | that.error(error + " ("+textStatus+")"); |
---|
| 97 | } |
---|
| 98 | console.log("ajax error, jqXHR: ", jqXHR); |
---|
| 99 | }; |
---|
| 100 | } |
---|
[5957] | 101 | // console.log("ajax", ajaxObject); |
---|
[5890] | 102 | jQuery.ajax(ajaxObject); |
---|
| 103 | }, |
---|
| 104 | |
---|
[5976] | 105 | toggleCollapse: function() { |
---|
| 106 | this.setState({navbarCollapse: !this.state.navbarCollapse}); |
---|
| 107 | }, |
---|
| 108 | |
---|
[5890] | 109 | renderAggregator: function() { |
---|
[7053] | 110 | return <AggregatorPage ajax={this.ajax} error={this.error} embedded={false}/>; |
---|
[5890] | 111 | }, |
---|
| 112 | |
---|
[5959] | 113 | renderHelp: function() { |
---|
| 114 | return <HelpPage />; |
---|
| 115 | }, |
---|
| 116 | |
---|
| 117 | renderAbout: function() { |
---|
[7148] | 118 | return <AboutPage toStatistics={this.toStatistics} />; |
---|
[5959] | 119 | }, |
---|
| 120 | |
---|
[5890] | 121 | renderStatistics: function() { |
---|
| 122 | return <StatisticsPage ajax={this.ajax} />; |
---|
| 123 | }, |
---|
| 124 | |
---|
[6111] | 125 | renderEmbedded: function() { |
---|
[6197] | 126 | return <AggregatorPage ajax={this.ajax} error={this.error} embedded={true}/>; |
---|
[6111] | 127 | }, |
---|
| 128 | |
---|
[6144] | 129 | getPageFns: function() { |
---|
[5976] | 130 | return { |
---|
| 131 | '': this.renderAggregator, |
---|
| 132 | 'help': this.renderHelp, |
---|
| 133 | 'about': this.renderAbout, |
---|
| 134 | 'stats': this.renderStatistics, |
---|
[6111] | 135 | 'embed': this.renderEmbedded, |
---|
[5976] | 136 | }; |
---|
[5890] | 137 | }, |
---|
| 138 | |
---|
[5976] | 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}); |
---|
[7148] | 146 | console.log("new page: " + document.location + ", name: " + pageFnName); |
---|
[5976] | 147 | } |
---|
[5959] | 148 | }, |
---|
| 149 | |
---|
[5976] | 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'); }, |
---|
[6111] | 154 | toEmbedded: function(doPushHistory) { this.gotoPage(doPushHistory, 'embed'); }, |
---|
[5959] | 155 | |
---|
[6132] | 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 | |
---|
[5890] | 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":""}> |
---|
[5976] | 169 | <a className="link" tabIndex="-1" onClick={this.toAggregator.bind(this, true)}>Aggregator</a> |
---|
[5890] | 170 | </li> |
---|
| 171 | <li className={this.state.navbarPageFn === this.renderHelp ? "active":""}> |
---|
[5976] | 172 | <a className="link" tabIndex="-1" onClick={this.toHelp.bind(this, true)}>Help</a> |
---|
[5890] | 173 | </li> |
---|
| 174 | </ul> |
---|
[6171] | 175 | <ul className="nav navbar-nav navbar-right"> |
---|
[6180] | 176 | <li> <div id="clarinservices" style={{padding:4}}/> </li> |
---|
[6144] | 177 | {this.renderLogin()} |
---|
[5890] | 178 | </ul> |
---|
| 179 | </div> |
---|
| 180 | ); |
---|
| 181 | }, |
---|
| 182 | |
---|
[6111] | 183 | renderTop: function() { |
---|
| 184 | if (this.state.navbarPageFn === this.renderEmbedded) { |
---|
| 185 | return false; |
---|
| 186 | } |
---|
[5890] | 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> |
---|
[7228] | 198 | <a className="navbar-brand" href={URLROOT + "/"} tabIndex="-1"> |
---|
[6172] | 199 | <img width="28px" height="28px" src="img/magglass1.png"/> |
---|
[7228] | 200 | <header className="inline"> Federated Content Search </header> |
---|
[6171] | 201 | </a> |
---|
[5890] | 202 | </div> |
---|
| 203 | {this.renderCollapsible()} |
---|
| 204 | </div> |
---|
| 205 | </div> |
---|
| 206 | |
---|
| 207 | <ErrorPane errorMessages={this.state.errorMessages} /> |
---|
[6171] | 208 | |
---|
[6111] | 209 | </div> |
---|
| 210 | ); |
---|
| 211 | }, |
---|
[5890] | 212 | |
---|
[6111] | 213 | render: function() { |
---|
| 214 | return ( |
---|
| 215 | <div> |
---|
| 216 | <div> { this.renderTop() } </div> |
---|
| 217 | |
---|
[5890] | 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 | |
---|
[7148] | 229 | // StatisticsPage |
---|
[5890] | 230 | |
---|
[7148] | 231 | // HelpPage |
---|
[5890] | 232 | |
---|
[7148] | 233 | // AboutPage |
---|
[5894] | 234 | |
---|
[7148] | 235 | // Footer |
---|
[5894] | 236 | |
---|
[7148] | 237 | // EmbeddedFooter |
---|
[5894] | 238 | |
---|
[6172] | 239 | function isEmbeddedView() { |
---|
| 240 | var path = window.location.pathname.split('/'); |
---|
[7054] | 241 | return (path.length >= 3 && path[path.length - 1] === 'embed'); |
---|
[6172] | 242 | } |
---|
| 243 | |
---|
[5976] | 244 | function endsWith(str, suffix) { |
---|
| 245 | return str.indexOf(suffix, str.length - suffix.length) !== -1; |
---|
| 246 | } |
---|
| 247 | |
---|
[6043] | 248 | var routeFromLocation = function() { |
---|
[7148] | 249 | console.log("routeFromLocation: " + document.location); |
---|
[6043] | 250 | if (!this) throw "routeFromLocation must be bound to main"; |
---|
[5976] | 251 | var path = window.location.pathname.split('/'); |
---|
[7148] | 252 | console.log("path: " + path); |
---|
[7054] | 253 | if (path.length >= 3) { |
---|
| 254 | var p = path[path.length - 1]; |
---|
[5976] | 255 | if (p === 'help') { |
---|
[6043] | 256 | this.toHelp(false); |
---|
[5976] | 257 | } else if (p === 'about') { |
---|
[6043] | 258 | this.toAbout(false); |
---|
[5976] | 259 | } else if (p === 'stats') { |
---|
[6043] | 260 | this.toStatistics(false); |
---|
[6111] | 261 | } else if (p === 'embed') { |
---|
| 262 | this.toEmbedded(false); |
---|
[5976] | 263 | } else { |
---|
[6043] | 264 | this.toAggregator(false); |
---|
[5976] | 265 | } |
---|
| 266 | } else { |
---|
[6043] | 267 | this.toAggregator(false); |
---|
[5976] | 268 | } |
---|
| 269 | }; |
---|
| 270 | |
---|
[7036] | 271 | var main = ReactDOM.render(<Main />, document.getElementById('body')); |
---|
[6172] | 272 | if (!isEmbeddedView()) { |
---|
[7157] | 273 | ReactDOM.render(<Footer VERSION={VERSION} toAbout={main.toAbout}/>, document.getElementById('footer') ); |
---|
[6306] | 274 | } else { |
---|
[7148] | 275 | ReactDOM.render(<EmbeddedFooter URLROOT={URLROOT} />, document.getElementById('footer') ); |
---|
[6306] | 276 | if (jQuery) { jQuery('body, #footer').addClass('embedded'); } |
---|
[6172] | 277 | } |
---|
[5957] | 278 | |
---|
[6043] | 279 | window.onpopstate = routeFromLocation.bind(main); |
---|
[5976] | 280 | |
---|
[5897] | 281 | })(); |
---|