├── .gitattributes ├── .gitignore ├── README.md ├── api.php ├── app ├── data │ └── timeline.json ├── js │ ├── dispatcher.js │ ├── react-controls.js │ ├── react-gmaps.js │ ├── react-inputslider.js │ ├── react-itemdetail.js │ ├── react-itemlist.js │ └── react-pagination.js ├── less │ ├── compiled.css │ ├── compiled.less │ ├── lesshat.less │ ├── size_0.less │ ├── size_1024.less │ ├── size_1024_1376.less │ ├── size_1024_up.less │ ├── size_1376.less │ ├── size_1376_1840.less │ ├── size_1376_up.less │ ├── size_1840.less │ ├── size_1840_up.less │ ├── size_640.less │ ├── size_640_800.less │ ├── size_640_up.less │ ├── size_800.less │ ├── size_800_1024.less │ ├── size_800_up.less │ └── style.less ├── main.js └── routes │ └── core-routes.js ├── package.json ├── public ├── bundle.min.js ├── compiled.css ├── functions.js ├── imgs │ ├── Smoke-Element.png │ ├── logo.png │ └── react-logo.svg └── markerwithlabel.js ├── server.js └── views └── index.ejs /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Folder config file 2 | Desktop.ini 3 | 4 | # Recycle Bin used on file shares 5 | $RECYCLE.BIN/ 6 | .DS_Store 7 | 8 | node_modules/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # History of Humanity 2 | 3 | A brief overview of what humanity has been up to since 1750, powered by React.JS and the Wikipedia API 4 | 5 | Demo @ https://wail.es/history-of-humanity/ 6 | 7 | Prototype blog post @ https://builtvisible.com/building-content-in-react/ 8 | 9 | Final result blog post @ https://wail.es/build-seo-friendly-universal-js-apps-reactjs/ 10 | 11 | ## Notes 12 | 13 | Uses httpplease, classie and MarkerWithLabel. And React.js, obviously. -------------------------------------------------------------------------------- /api.php: -------------------------------------------------------------------------------- 1 | 0) 67 | History.pushState(null, 'Page ' + page + ' | Modern History of Humanity', '/history-of-humanity/p/' + page); 68 | else 69 | History.pushState(null, 'Modern History of Humanity', '/history-of-humanity/'); 70 | } 71 | 72 | updateItems(dateType, newDate, newTag) { 73 | var newState = {}; 74 | var items = []; 75 | var n = 0; 76 | 77 | if (dateType === false) { 78 | var sDate = 1750; 79 | var eDate = 2015; 80 | 81 | newState.startDate = sDate; 82 | newState.endDate = eDate; 83 | } 84 | else { 85 | var sDate =(dateType === 'startDate') ? parseInt(newDate) : parseInt(this.state.startDate); 86 | var eDate =(dateType === 'endDate') ? parseInt(newDate) : parseInt(this.state.endDate); 87 | } 88 | 89 | for (var i = sDate; i <= eDate; i++) { 90 | var loopEnd =(isset(this.props.timeline[i])) ? this.props.timeline[i].length : 0; 91 | 92 | for (var j = 0; j < loopEnd; j++) { 93 | if ((newTag !== false && this.props.timeline[i][j].tags.indexOf(newTag) > -1) || newTag === false) { 94 | items[n] = this.props.timeline[i][j]; 95 | items[n].year = i; 96 | items[n].position = j; 97 | n++; 98 | } 99 | } 100 | } 101 | 102 | if (this.state.pointer >= Math.floor(items.length / this.state.nRows)) 103 | newState.pointer = Math.floor(items.length / this.state.nRows); 104 | 105 | newState.tag = newTag; 106 | 107 | newState.selectedItems = items; 108 | newState.nPages = Math.ceil(items.length / this.state.nRows); 109 | 110 | if (dateType !== false) 111 | newState[dateType] = newDate; 112 | 113 | this.setState(newState); 114 | } 115 | 116 | historyUpdate() { 117 | var parts = window.location.pathname.replace('/history-of-humanity/', '').split('/'); 118 | 119 | if (String(window.location.pathname).match(/\/history-of-humanity\/\d+\/\d+\/.+/i) !== null) { 120 | this.setItemDetail({"target": {"dataset": { "year": parts[0], "position": parts[1] }}}, false); 121 | } 122 | else { 123 | this.hideItemDetail({"target": {"id": "hohContainer"}}, false); 124 | } 125 | } 126 | 127 | setItemDetail(e, updateHistory) { 128 | if (typeof e.preventDefault === 'function') 129 | e.preventDefault(); 130 | 131 | if (isset(e.target.dataset.year)) 132 | var year = e.target.dataset.year; 133 | else if (isset(e.target.parentNode.dataset.year)) 134 | var year = e.target.parentNode.dataset.year; 135 | else if (isset(e.target.parentNode.parentNode.dataset.year)) 136 | var year = e.target.parentNode.parentNode.dataset.year; 137 | 138 | if (isset(e.target.dataset.position)) 139 | var position = e.target.dataset.position; 140 | else if (isset(e.target.parentNode.dataset.position)) 141 | var position = e.target.parentNode.dataset.position; 142 | else if (isset(e.target.parentNode.parentNode.dataset.position)) 143 | var position = e.target.parentNode.parentNode.dataset.position; 144 | 145 | var itemData = this.props.timeline[year][position]; 146 | 147 | if (isset(itemData.links.main)) 148 | { 149 | var wikiTitle = itemData.links.main.link.replace('//en.wikipedia.org/wiki/', ''); 150 | this.setWikiData(wikiTitle); 151 | } 152 | else { 153 | this.setState({ wikiData: false, wikiImages: false }) 154 | } 155 | 156 | this.setState({ itemDetail: itemData }); 157 | 158 | if (!isset(updateHistory) || updateHistory !== false) 159 | History.pushState(null, itemData.text + ' | Modern History of Humanity', '/history-of-humanity/' + year + '/' + position + '/' + wikiTitle); 160 | } 161 | 162 | hideItemDetail(e, updateHistory) { 163 | if (e.target.id === 'hohContainer' || e.target.id === 'hideItemDetail' || e.target.parentNode.id === 'hideItemDetail') { 164 | this.setState({ itemDetail: false }); 165 | 166 | var n = parseInt(this.state.pointer) + 1; 167 | 168 | if (!isset(updateHistory) || updateHistory !== false) { 169 | if (this.state.pointer === 0) 170 | History.pushState(null, 'Modern History of Humanity', '/history-of-humanity/'); 171 | else 172 | History.pushState(null, 'Page ' + n + ' | Modern History of Humanity', '/history-of-humanity/p/' + n); 173 | } 174 | } 175 | } 176 | 177 | setWikiData(wikiTitle) { 178 | var self = this; 179 | var wikiApiLink = 'https://en.wikipedia.org/w/api.php?format=json&action=query&prop=extracts|images&exintro=&explaintext=&titles=' + wikiTitle; 180 | var wikidata = axios.get('https://wail.es/history/api.php?url=' + encodeURIComponent(wikiApiLink.replace(/&/g, "&"))) 181 | 182 | wikidata.then(function(res) { 183 | var newState = { wikiData: false, wikiImages: false }; 184 | 185 | if (isset(res.data.query.pages)) { 186 | var pageId = Object.keys(res.data.query.pages); 187 | 188 | newState.wikiData = res.data.query.pages[pageId], 189 | newState.wikiImages = []; 190 | 191 | if (isset(newState.wikiData.images)) { 192 | for (var i = newState.wikiData.images.length - 1; i >= 0; i--) { 193 | var title = newState.wikiData.images[i].title.replace('File:', '').replace(/\s+/g,"_"); 194 | 195 | if (title.indexOf('.jpg') === -1 && title.indexOf('.png') === -1 && i === 0) 196 | newState.wikiData.images.splice(i, 1); 197 | else if (title.indexOf('.jpg') === -1 && title.indexOf('.png') === -1) { 198 | newState.wikiData.images.splice(i, 1); 199 | continue; 200 | } 201 | 202 | var imgLink = 'https://en.wikipedia.org/w/api.php?action=query&titles=Image:' + title + '&prop=imageinfo&iiprop=url&format=json'; 203 | var wikiImgs = axios.get('https://wail.es/history/api.php?url=' + encodeURIComponent(imgLink.replace(/&/g, "&"))); 204 | 205 | if (i === 0) { 206 | wikiImgs.then(function(imgRes) { 207 | if (isset(imgRes.data.query.pages['-1']) && isset(imgRes.data.query.pages['-1'].imageinfo)) 208 | newState.wikiImages.push(imgRes.data.query.pages['-1'].imageinfo[0].url); 209 | 210 | self.setState(newState); 211 | }) 212 | .catch(function(e) { 213 | console.log('error in xhr 2'); 214 | console.log(e); 215 | }); 216 | } 217 | else { 218 | wikiImgs.then(function(imgRes) { 219 | if (isset(imgRes.data.query.pages['-1']) && isset(imgRes.data.query.pages['-1'].imageinfo)) 220 | newState.wikiImages.push(imgRes.data.query.pages['-1'].imageinfo[0].url); 221 | }) 222 | .catch(function(e) { 223 | console.log('error in xhr 3'); 224 | console.log(e); 225 | }); 226 | } 227 | } 228 | } 229 | else 230 | self.setState(newState); 231 | } 232 | }) 233 | .catch(function(e) { 234 | console.log('error in xhr 1'); 235 | console.log(e); 236 | }); 237 | } 238 | 239 | updateDate(e) { 240 | if (!isNaN(e.x)) { 241 | var year = Math.floor(e.x); 242 | 243 | if (e.name === 'startDate' && year !== this.state.startDate) 244 | this.updateItems('startDate', year, this.state.tag); 245 | else if (e.name === 'endDate' && year !== this.state.endDate) 246 | this.updateItems('endDate', year, this.state.tag); 247 | } 248 | } 249 | 250 | updatePointer(e) { 251 | var update = true; 252 | 253 | if (isset(e.target.parentNode.dataset.pointer)) 254 | var pointer = e.target.parentNode.dataset.pointer; 255 | else if (isset(e.target.dataset.pointer)) 256 | var pointer = e.target.dataset.pointer; 257 | 258 | if (pointer === '1' &&(this.state.pointer + 1 < Math.floor(this.state.selectedItems.length / this.state.nRows))) 259 | var newPointer = this.state.pointer + 1 260 | else if (pointer === '1' &&(this.state.pointer + 1 >= Math.floor(this.state.selectedItems.length / this.state.nRows))) 261 | update = false; 262 | else if (pointer === '0' && this.state.pointer > 0) 263 | var newPointer = this.state.pointer - 1; 264 | else 265 | var newPointer = 0; 266 | 267 | if (update) { 268 | this.setState({ pointer: newPointer }); 269 | 270 | if (newPointer === 0) 271 | History.pushState(null, 'Modern History of Humanity', '/history-of-humanity/'); 272 | else 273 | History.pushState(null, 'Modern History of Humanity', '/history-of-humanity/p/' + newPointer); 274 | } 275 | } 276 | 277 | handleMarkerClick(e) { this.setState({ highlightLatLong: e }) } 278 | 279 | updateTag(e) { 280 | e.preventDefault(); 281 | 282 | var newVal = e.target.dataset.value; 283 | 284 | if (newVal === '') 285 | newVal = false; 286 | 287 | this.updateItems('endDate', this.state.endDate, newVal); 288 | } 289 | 290 | renderItemDetail() { 291 | var checkPropsAgainstUrl = true; 292 | 293 | if (typeof window !== 'undefined') { 294 | var parts = window.location.pathname.replace('/history-of-humanity/', '').split('/'); 295 | 296 | checkPropsAgainstUrl =(isset(this.props.initparams) 297 | && this.props.initparams.year === parts[0] 298 | && this.props.initparams.position === parts[1] 299 | && this.props.initparams.name === parts[2]); 300 | } 301 | 302 | var itemDetail = false, 303 | wikiData = false, 304 | wikiImages = false; 305 | 306 | if (this.state.itemDetail !== false) 307 | var itemDetail = this.state.itemDetail; 308 | else if (isset(this.props.initwikidata) && isset(this.props.initwikidata.itemDetail) && checkPropsAgainstUrl) 309 | var itemDetail = this.props.initwikidata.itemDetail; 310 | 311 | if (this.state.wikiData !== false) 312 | var wikiData = this.state.wikiData; 313 | else if (isset(this.props.initwikidata) && isset(this.props.initwikidata.wikiData) && checkPropsAgainstUrl) 314 | var wikiData = this.props.initwikidata.wikiData; 315 | 316 | if (this.state.wikiImages !== false) 317 | var wikiImages = this.state.wikiImages; 318 | else if (isset(this.props.initwikidata) && isset(this.props.initwikidata.wikiImages) && checkPropsAgainstUrl) 319 | var wikiImages = this.props.initwikidata.wikiImages; 320 | 321 | if (itemDetail !== false) { 322 | return ( 323 | React.createElement("div", { key: "itemDetailContainer" }, 324 | React.createElement(ReactCSSTransitionGroup, { 325 | id: 'hideItemDetail', 326 | transitionName: 'itemDetailTransition', 327 | transitionAppear: true, 328 | transitionAppearTimeout: 500, 329 | transitionEnterTimeout: 500, 330 | transitionLeaveTimeout: 500, 331 | onClick: this.hideItemDetail 332 | }, 333 | React.createElement("i", { className: 'fa fa-times' }) 334 | ), 335 | React.createElement(ItemDetail, { 336 | itemDetail: itemDetail, 337 | wikiData: wikiData, 338 | wikiImages: wikiImages 339 | }) 340 | ) 341 | ) 342 | } 343 | else 344 | return []; 345 | } 346 | 347 | render() { 348 | var highchartKey = this.state.startDate.toString() + this.state.endDate.toString(); 349 | var mapsKey = this.state.startDate.toString() + this.state.endDate.toString() + this.state.tag + this.state.pointer; 350 | 351 | var itemDetail =(this.state.itemDetail !== false || isset(this.props.initwikidata)) 352 | ? this.renderItemDetail() 353 | : []; 354 | 355 | return ( 356 | React.createElement("div", { 357 | id: "hohContainer", 358 | onClick: this.hideItemDetail 359 | }, 360 | React.createElement("div", { id: "mapAndControls" }, 361 | React.createElement(GMap, { 362 | initialZoom: 3, 363 | centerLat: 30, 364 | centerLng: 30, 365 | mapsKey: mapsKey, 366 | items: this.state.selectedItems, 367 | pointer: this.state.pointer, 368 | handleMarkerClick: this.handleMarkerClick, 369 | show: this.state.nRows 370 | }), 371 | React.createElement(Controls, { 372 | startDate: this.state.startDate, 373 | endDate: this.state.endDate, 374 | tag: this.state.tag, 375 | inputHandler: this.updateDate, 376 | tagHandler: this.updateTag, 377 | buttonHandler: this.updatePointer 378 | }) 379 | ), 380 | React.createElement(ItemList, { 381 | items: this.state.selectedItems, 382 | highlightLatLong: this.state.highlightLatLong, 383 | pointer: this.state.pointer, 384 | show: this.state.nRows, 385 | itemHandler: this.setItemDetail 386 | }), 387 | itemDetail, 388 | React.createElement(Pagination, { 389 | nPages: this.state.nPages, 390 | maxBlocks: "11", 391 | clickHandler: this.handlePaginatorClicked, 392 | pointer: this.state.pointer 393 | }) 394 | ) 395 | ); 396 | } 397 | } -------------------------------------------------------------------------------- /app/js/react-controls.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import InputSlider from './react-inputslider.js'; 3 | 4 | export default class Controls extends React.Component { 5 | constructor() { 6 | super(); 7 | 8 | this.tags = [ 9 | { value: '', title: 'All' }, 10 | { value: 'art', title: 'Art' }, 11 | { value: 'cold war', title: 'Cold War' }, 12 | { value: 'conflict', title: 'Conflict' }, 13 | { value: 'crime', title: 'Crime' }, 14 | { value: 'death', title: 'Death' }, 15 | { value: 'disaster', title: 'Disaster' }, 16 | { value: 'ecology', title: 'Ecology' }, 17 | { value: 'economy', title: 'Economy' }, 18 | { value: 'equality', title: 'Equality' }, 19 | { value: 'exploration', title: 'Exploration' }, 20 | { value: 'government', title: 'Government' }, 21 | { value: 'history', title: 'History' }, 22 | { value: 'internal conflict', title: 'Internal Conflict' }, 23 | { value: 'natural disaster', title: 'Natural Disaster' }, 24 | { value: 'person', title: 'Person' }, 25 | { value: 'religion', title: 'Religion' }, 26 | { value: 'science', title: 'Science' }, 27 | { value: 'social', title: 'Social' }, 28 | { value: 'space', title: 'Space' }, 29 | { value: 'sport', title: 'Sport' }, 30 | { value: 'technology', title: 'Technology' }, 31 | { value: 'terrorism', title: 'Terrorism' }, 32 | { value: 'toys', title: 'Toys' }, 33 | { value: 'transport', title: 'Transport' }, 34 | { value: 'treaty', title: 'Treaty' }, 35 | { value: 'world population', title: 'World Population' }, 36 | { value: 'ww1', title: 'WW1' }, 37 | { value: 'ww2', title: 'WW2' } 38 | ]; 39 | 40 | this.toggleTags = this.toggleTags.bind(this); 41 | this.hideTags = this.hideTags.bind(this); 42 | this.tagHandler = this.tagHandler.bind(this); 43 | this.renderTag = this.renderTag.bind(this); 44 | 45 | this.state = { showTags: false }; 46 | } 47 | 48 | toggleTags() { this.setState({ showTags: !this.state.showTags }) } 49 | hideTags() { this.setState({ showTags: false }) } 50 | 51 | tagHandler(e) { 52 | this.hideTags(); 53 | this.props.tagHandler(e); 54 | } 55 | 56 | renderTag(tag) { return React.createElement("li", { key: tag.value, 'data-value': tag.value, onClick: this.tagHandler }, tag.title) } 57 | 58 | render() { 59 | var tags = []; 60 | var currentTag = 'All'; 61 | 62 | for (var i = this.tags.length - 1; i >= 0; i--) { 63 | if (this.state.showTags) 64 | tags[i] = this.renderTag(this.tags[i]); 65 | 66 | if (this.props.tag === this.tags[i].value) 67 | currentTag = this.tags[i].title; 68 | } 69 | 70 | var listClass = 'dropdown'; 71 | 72 | if (this.state.showTags) 73 | listClass += ' active'; 74 | 75 | return ( 76 | React.createElement("div", { id: "controls" }, 77 | React.createElement("div", { className: "panel" }, 78 | React.createElement("label", { htmlFor: 'startDate' }, 'Start Date'), 79 | React.createElement("div", { className: 'yearPanel' }, this.props.startDate), 80 | React.createElement(InputSlider, { 81 | axis: 'x', 82 | x: this.props.startDate, 83 | xMin: 1750, 84 | xMax: 2014, 85 | name: 'startDate', 86 | onChange: this.props.inputHandler 87 | }) 88 | ), 89 | React.createElement("div", { className: "panel" }, 90 | React.createElement("label", { htmlFor: 'endDate' }, 'End Date'), 91 | React.createElement("div", { className: 'yearPanel' }, this.props.endDate), 92 | React.createElement(InputSlider, { 93 | axis: 'x', 94 | x: this.props.endDate, 95 | xMin: 1751, 96 | xMax: 2015, 97 | name: 'endDate', 98 | onChange: this.props.inputHandler 99 | }) 100 | ), 101 | React.createElement("div", { className: "panel" }, 102 | React.createElement("label", null, 'Tag'), 103 | React.createElement("p", { id: 'tagHandler', onClick: this.toggleTags }, currentTag), 104 | React.createElement("ul", { id: 'tags', className: listClass }, tags) 105 | ), 106 | React.createElement("span", { className: 'fa_button', id: 'backButton', 'data-pointer': 0, onClick: this.props.buttonHandler }, 107 | React.createElement("i", { className: 'fa fa-angle-double-left' }) 108 | ), 109 | React.createElement("span", { className: 'fa_button', id: 'forwardButton', 'data-pointer': 1, onClick: this.props.buttonHandler }, 110 | React.createElement("i", { className: 'fa fa-angle-double-right' }) 111 | ) 112 | ) 113 | ) 114 | } 115 | } -------------------------------------------------------------------------------- /app/js/react-gmaps.js: -------------------------------------------------------------------------------- 1 | function isset (obj) { return typeof obj !== 'undefined'; } 2 | 3 | import React from 'react'; 4 | 5 | class GMap extends React.Component { 6 | constructor() { 7 | super(); 8 | 9 | // load initial map 10 | this.markers = []; 11 | this.latlongs = []; 12 | 13 | this.componentDidMount = this.componentDidMount.bind(this); 14 | this.componentWillUnmount = this.componentWillUnmount.bind(this); 15 | this.shouldComponentUpdate = this.shouldComponentUpdate.bind(this); 16 | this.componentDidUpdate = this.componentDidUpdate.bind(this); 17 | this.updateDimensions = this.updateDimensions.bind(this); 18 | this.clearMarkers = this.clearMarkers.bind(this); 19 | this.createMarkers = this.createMarkers.bind(this); 20 | this.setMapOnAll = this.setMapOnAll.bind(this); 21 | this.createMap = this.createMap.bind(this); 22 | this.checkBounds = this.checkBounds.bind(this); 23 | } 24 | 25 | componentDidMount(props) { 26 | var self = this; 27 | 28 | if (typeof google === 'object') { 29 | this.map = this.createMap(); 30 | 31 | google.maps.event.addListener(this.map, 'center_changed', function() { 32 | self.checkBounds(self.map); 33 | }); 34 | 35 | google.maps.event.addDomListener(window, "resize", function() { 36 | var center = self.map.getCenter(); 37 | google.maps.event.trigger(self.map, "resize"); 38 | self.map.setCenter(center); 39 | }); 40 | 41 | if (this.props.items.length > 0) 42 | this.createMarkers(); 43 | 44 | window.addEventListener("resize", this.updateDimensions); 45 | } 46 | 47 | this.updateDimensions(); 48 | } 49 | 50 | componentWillUnmount() { if (typeof window !== 'undefined') window.removeEventListener("resize", this.updateDimensions) } 51 | 52 | shouldComponentUpdate(newProps, newState) { 53 | return newProps.mapsKey !== this.props.mapsKey 54 | ||(this.state !== null && isset(this.state.width) && isset(newState.width) && newState.width !== this.state.width) 55 | } 56 | 57 | componentDidUpdate() { 58 | if (typeof google === 'object') { 59 | this.clearMarkers(); 60 | this.createMarkers(); 61 | } 62 | } 63 | 64 | updateDimensions(returnStyles) { 65 | if (typeof window !== 'undefined') { 66 | var dims = getViewportSize(); 67 | 68 | var styles = { 69 | height: '600px', 70 | width: '1300px' 71 | } 72 | 73 | if (dims.width < 640) { 74 | styles.height = '250px'; 75 | styles.width = '285px'; 76 | } 77 | else if (dims.width > 640 && dims.width < 800) { 78 | styles.height = '325px'; 79 | styles.width = '425px'; 80 | } 81 | else if (dims.width > 800 && dims.width < 1024) { 82 | styles.height = '455px'; 83 | styles.width = '625px'; 84 | } 85 | else if (dims.width > 1024 && dims.width < 1376) { 86 | styles.width = '900px'; 87 | } 88 | else if (dims.width > 1376 && dims.width < 1840) { 89 | styles.width = '1200px'; 90 | } 91 | 92 | this.setState(styles); 93 | } 94 | } 95 | 96 | clearMarkers() { 97 | this.setMapOnAll(null); 98 | this.markers = []; 99 | this.latlongs = []; 100 | } 101 | 102 | createMarkers() { 103 | var x = this.props.pointer * this.props.show; 104 | var y = this.props.pointer * this.props.show + this.props.show; 105 | var year = this.props.items[x].year; 106 | 107 | for (var i = x; i < y; i++) { 108 | var currentItem = this.props.items[i]; 109 | 110 | if (isset(currentItem.latlong) && currentItem.latlong.length > 0) 111 | { 112 | for (var j = currentItem.latlong.length - 1; j >= 0; j--) { 113 | var marker = new MarkerWithLabel({ 114 | icon: { 115 | path: google.maps.SymbolPath.CIRCLE, 116 | scale: 0, 117 | }, 118 | labelAnchor: new google.maps.Point(10, 10), 119 | labelClass: "label", 120 | position: new google.maps.LatLng(currentItem.latlong[j][0], currentItem.latlong[j][1]), 121 | map: this.map 122 | }); 123 | 124 | this.markers.push(marker); 125 | this.latlongs.push(currentItem.latlong[j]); 126 | } 127 | } 128 | } 129 | 130 | this.setMapOnAll(this.map); 131 | } 132 | 133 | setMapOnAll(map) { 134 | var self = this; 135 | 136 | for (var i = 0; i < this.markers.length; i++) { 137 | this.markers[i].setMap(map); 138 | google.maps.event.addListener(this.markers[i], "click", function(e) { 139 | self.props.handleMarkerClick({ 140 | lat: this.position.lat().toFixed(1), 141 | long: this.position.lng().toFixed(1) 142 | }) 143 | }) 144 | } 145 | } 146 | 147 | createMap() { 148 | var mapOptions = { 149 | //disableDefaultUI: true, 150 | maxZoom: 10, 151 | minZoom: 2, 152 | scrollwheel: false, 153 | mapTypeId: google.maps.MapTypeId.ROADMAP, 154 | styles: this.style, 155 | streetViewControl: !1, 156 | mapTypeControl: !1, 157 | zoom: this.props.initialZoom, 158 | center: new google.maps.LatLng(this.props.centerLat, this.props.centerLng) 159 | } 160 | 161 | return new google.maps.Map(this.refs.mapCanvas, mapOptions) 162 | } 163 | 164 | // If the map position is out of range, move it back 165 | checkBounds(map) { 166 | var latNorth = map.getBounds().getNorthEast().lat(); 167 | var latSouth = map.getBounds().getSouthWest().lat(); 168 | var newLat; 169 | 170 | var pi = Math.PI; 171 | 172 | var t1 = Math.pow(Math.E, pi); 173 | var t2 = Math.pow(Math.E, 0 - pi); 174 | var sinh =(t1 - t2) / 2; 175 | 176 | var atan = Math.atan(sinh); 177 | 178 | var pos = atan * 180 / pi; 179 | var neg = 0 - pos; 180 | 181 | if (latNorth < pos && latSouth > neg) /* in both side -> it's ok */ 182 | return; 183 | else { 184 | if (latNorth > pos && latSouth < neg) /* out both side -> it's ok */ 185 | return; 186 | else { 187 | if (latNorth > pos) 188 | newLat = map.getCenter().lat() -(latNorth - pos); /* too north, centering */ 189 | if (latSouth < neg) 190 | newLat = map.getCenter().lat() -(latSouth + 85); /* too south, centering */ 191 | } 192 | } 193 | 194 | if (newLat) { 195 | var newCenter = new google.maps.LatLng(newLat, map.getCenter().lng()); 196 | map.setCenter(newCenter); 197 | } 198 | } 199 | 200 | render() { 201 | return ( 202 | React.createElement("div", { id: "gmap" }, 203 | React.createElement("div", { ref: "mapCanvas", id: "mapCanvas" }) 204 | ) 205 | ) 206 | } 207 | } 208 | 209 | GMap.prototype.style = [{ 210 | "featureType": "landscape", 211 | "elementType": "geometry.fill", 212 | "stylers": [{ 213 | "visibility": "on" 214 | }, { 215 | "color": "#DDD4CB" 216 | }] 217 | }, { 218 | "featureType": "poi", 219 | "stylers": [{ 220 | "visibility": "off" 221 | }] 222 | }, { 223 | "featureType": "road", 224 | "stylers": [{ 225 | "visibility": "off" 226 | }] 227 | }, { 228 | "featureType": "transit", 229 | "stylers": [{ 230 | "visibility": "off" 231 | }] 232 | }, { 233 | "featureType": "administrative", 234 | "elementType": "labels", 235 | "stylers": [{ 236 | "color": "#808080" 237 | }, { 238 | "visibility": "on" 239 | }, { 240 | "weight": 0.2 241 | }] 242 | }, { 243 | "featureType": "administrative.locality", 244 | "stylers": [{ 245 | "visibility": "off" 246 | }] 247 | }, { 248 | "featureType": "administrative.neighborhood", 249 | "stylers": [{ 250 | "visibility": "off" 251 | }] 252 | }, { 253 | "featureType": "administrative.land_parcel", 254 | "stylers": [{ 255 | "visibility": "off" 256 | }] 257 | }, { 258 | "featureType": "administrative.country", 259 | "elementType": "labels.text.fill", 260 | "stylers": [{ 261 | "invert_lightness": true 262 | }, { 263 | "visibility": "simplified" 264 | }] 265 | }, { 266 | "featureType": "administrative.country", 267 | "elementType": "geometry.stroke", 268 | "stylers": [{ 269 | "color": "#F7F5F2" 270 | }, { 271 | "visibility": "on" 272 | }, { 273 | "weight": 1.0 274 | }] 275 | }, { 276 | "featureType": "administrative.province", 277 | "stylers": [{ 278 | "visibility": "off" 279 | }] 280 | }, { 281 | "featureType": "water", 282 | "elementType": "geometry.fill", 283 | "stylers": [{ 284 | "color": "#F7F5F2" 285 | }, { 286 | "visibility": "on" 287 | }] 288 | }, { 289 | "featureType": "water", 290 | "elementType": "labels.text.fill", 291 | "stylers": [{ 292 | "visibility": "simplified" 293 | }, { 294 | "invert_lightness": true 295 | }] 296 | }, { 297 | "elementType": "labels.text.fill", 298 | "stylers": [{ 299 | "color": "#808080" 300 | }] 301 | }]; 302 | 303 | export default GMap; -------------------------------------------------------------------------------- /app/js/react-inputslider.js: -------------------------------------------------------------------------------- 1 | // based on //raw.githubusercontent.com/wangzuo/react-input-slider/gh-pages/dist/input-slider.js 2 | import React from 'react'; 3 | import ReactDOM from 'react-dom'; 4 | 5 | export default class InputSlider extends React.Component { 6 | constructor() { 7 | super(); 8 | 9 | this.getPosition = this.getPosition.bind(this); 10 | this.handleSliderClick = this.handleSliderClick.bind(this); 11 | this.handleMoveStart = this.handleMoveStart.bind(this); 12 | this.handleDrag = this.handleDrag.bind(this); 13 | this.handleDragEnd = this.handleDragEnd.bind(this); 14 | this.handleClick = this.handleClick.bind(this); 15 | this.getPos = this.getPos.bind(this); 16 | this.changeValue = this.changeValue.bind(this); 17 | 18 | this.state = { mobile:(typeof window !== 'undefined') ? mobileCheck() : false }; 19 | } 20 | 21 | getPosition() { 22 | if (this.props.axis !== 'x') { 23 | var top = (this.props.y - this.props.yMin) /(this.props.yMax - this.props.yMin) * 100; 24 | 25 | if (top > 100) top = 100; 26 | if (top < 0) top = 0; 27 | } 28 | else 29 | var top = 0; 30 | 31 | if (this.props.axis !== 'y') { 32 | var left = (this.props.x - this.props.xMin) /(this.props.xMax - this.props.xMin) * 100; 33 | 34 | if (left > 100) left = 100; 35 | if (left < 0) left = 0; 36 | } 37 | else 38 | var left = 0; 39 | 40 | return { top: top + '%', left: left + '%' }; 41 | } 42 | 43 | handleSliderClick(e) { 44 | e.stopPropagation(); 45 | e.nativeEvent.stopImmediatePropagation(); 46 | } 47 | 48 | handleMoveStart(e) { 49 | e.preventDefault(); 50 | var dom = this.refs.handle.getDOMNode(); 51 | 52 | this.start = { 53 | x: dom.offsetLeft, 54 | y: dom.offsetTop 55 | }; 56 | 57 | this.offset = { 58 | x: e.clientX, 59 | y: e.clientY 60 | }; 61 | 62 | if (!this.state.mobile) { 63 | document.addEventListener('mousemove', this.handleDrag); 64 | document.addEventListener('mouseup', this.handleDragEnd); 65 | } 66 | else { 67 | document.addEventListener('touchmove', this.handleDrag); 68 | document.addEventListener('touchend', this.handleDragEnd); 69 | } 70 | } 71 | 72 | handleDrag(e) { 73 | e.preventDefault(); 74 | this.changeValue(this.getPos(e)); 75 | } 76 | 77 | handleDragEnd(e) { 78 | e.preventDefault(); 79 | 80 | if (!this.state.mobile) { 81 | document.removeEventListener('mousemove', this.handleDrag); 82 | document.removeEventListener('mouseup', this.handleDragEnd); 83 | } 84 | else { 85 | document.removeEventListener('touchmove', this.handleDrag); 86 | document.removeEventListener('touchend', this.handleDragEnd); 87 | } 88 | 89 | if (this.props.onDragEnd) this.changeValue(this.getPos(e), true); 90 | } 91 | 92 | handleClick(e) { 93 | var rect = this.refs[this.props.name].getBoundingClientRect(); 94 | 95 | this.changeValue({ 96 | left: e.clientX - rect.left, 97 | top: e.clientY - rect.top 98 | }, true); 99 | } 100 | 101 | getPos(e) { 102 | if (!this.state.mobile) { 103 | var posX = e.clientX + this.start.x - this.offset.x; 104 | var posY = e.clientY + this.start.y - this.offset.y; 105 | } 106 | else { 107 | var posX = e.changedTouches[0].screenX - e.changedTouches[0].radiusX; 108 | var posY = e.changedTouches[0].screenY - e.changedTouches[0].radiusY; 109 | } 110 | 111 | return { 112 | left: posX, 113 | top: posY 114 | }; 115 | } 116 | 117 | changeValue(pos, dragEnd) { 118 | if (!this.props.onChange) return; 119 | 120 | var rect = this.refs[this.props.name].getBoundingClientRect(); 121 | var width = rect.width; 122 | var height = rect.height; 123 | var left = pos.left; 124 | var top = pos.top; 125 | var axis = this.props.axis; 126 | 127 | if (left < 0) left = 0; 128 | if (left > width) left = width; 129 | if (top < 0) top = 0; 130 | if (top > height) top = height; 131 | 132 | var x = 0; 133 | var y = 0; 134 | if (axis === 'x' || axis === 'xy') { 135 | x = left / width *(this.props.xMax - this.props.xMin) + this.props.xMin; 136 | } 137 | if (axis === 'y' || axis === 'xy') { 138 | y = top / height *(this.props.yMax - this.props.yMin) + this.props.yMin; 139 | } 140 | 141 | this.props.onChange({ x: x, y: y, name: this.props.name }); 142 | 143 | if (this.props.onDragEnd && dragEnd) this.props.onDragEnd({ x: x, y: y, name: this.props.name }); 144 | } 145 | 146 | render() { 147 | var pos = this.getPosition(); 148 | var axis = this.props.axis; 149 | var valueStyle = {}; 150 | 151 | if (axis === 'x') valueStyle.width = pos.left; 152 | if (axis === 'y') valueStyle.height = pos.top; 153 | 154 | return ( 155 | React.createElement("div", { className: 'slider slider_' + axis, id: this.props.name, ref: this.props.name, onClick: this.handleClick, onTouchStart: this.handleClick }, 156 | React.createElement('div', { className: 'value', style: valueStyle }), 157 | React.createElement('div', { className: 'handle', ref: 'handle', onMouseDown: this.handleMoveStart, onTouchStart: this.handleMoveStart, onClick: this.handleSliderClick, style: pos }) 158 | ) 159 | ) 160 | } 161 | } -------------------------------------------------------------------------------- /app/js/react-itemdetail.js: -------------------------------------------------------------------------------- 1 | function isset (obj) { return typeof obj !== 'undefined'; } 2 | 3 | import React from 'react'; 4 | import ReactCSSTransitionGroup from 'react-addons-css-transition-group'; 5 | 6 | export default class ItemDetail extends React.Component { 7 | constructor() { 8 | super(); 9 | 10 | this.componentWillReceiveProps = this.componentWillReceiveProps.bind(this); 11 | this.renderRelatedLink = this.renderRelatedLink.bind(this); 12 | this.noRelatedLinks = this.noRelatedLinks.bind(this); 13 | this.renderItemLocation = this.renderItemLocation.bind(this); 14 | this.noLocations = this.noLocations.bind(this); 15 | this.renderGalleryImg = this.renderGalleryImg.bind(this); 16 | this.noImages = this.noImages.bind(this); 17 | this.renderMainLink = this.renderMainLink.bind(this); 18 | this.renderControls = this.renderControls.bind(this); 19 | this.buttonHandler = this.buttonHandler.bind(this); 20 | this.renderExtract = this.renderExtract.bind(this); 21 | 22 | this.state = { 23 | galleryPointer: 0, 24 | maxHeight: 600 25 | }; 26 | } 27 | 28 | componentDidMount() { 29 | if (typeof window !== 'undefined') 30 | this.setState({ maxHeight: getViewportSize().height - 20 }) 31 | } 32 | 33 | componentWillReceiveProps() { this.setState({ galleryPointer: 0 }); } 34 | 35 | renderRelatedLink(link) { 36 | return ( 37 | React.createElement("li", { key: 'relatedLink' + link.link }, 38 | React.createElement("a", { href: link.link, target: '_blank' }, link.title) 39 | ) 40 | ) 41 | } 42 | 43 | noRelatedLinks() { return React.createElement("li", { key: 'relatedLink' }, 'No related links') } 44 | 45 | renderItemLocation(location) { return React.createElement("li", { key: 'loc' + location }, location) } 46 | noLocations() { return React.createElement("li", { key: 'locations' }, 'No locations to show') } 47 | 48 | renderGalleryImg(imgUrl) { return React.createElement("img", { key: 'img' + imgUrl, className: 'galleryImg', src: imgUrl }) } 49 | noImages() { return React.createElement("p", { key: 'gallery' }, 'No images') } 50 | 51 | renderMainLink() { 52 | if (isset(this.props.itemDetail.links.main)) { 53 | return ( 54 | React.createElement("p", { key: 'mainLink', id: 'mainLink' }, 55 | React.createElement("span", null, 'Full article: '), 56 | React.createElement("a", { href: this.props.itemDetail.links.main.link, target: '_blank' }, this.props.itemDetail.links.main.title) 57 | ) 58 | ) 59 | } 60 | else 61 | return [] 62 | } 63 | 64 | renderControls() { 65 | return ( 66 | React.createElement("div", { key: 'galleryControls' }, 67 | React.createElement("span", { className: 'fa_button', id: 'galleryBack', onClick: this.buttonHandler }, 68 | React.createElement("i", { className: 'fa fa-angle-double-left' }) 69 | ), 70 | React.createElement("span", { className: 'fa_button', id: 'galleryForward', onClick: this.buttonHandler }, 71 | React.createElement("i", { className: 'fa fa-angle-double-right' }) 72 | ) 73 | ) 74 | ) 75 | } 76 | 77 | buttonHandler(e) { 78 | var id = (isset(e.target.id) &&(e.target.id === 'galleryBack' || e.target.id === 'galleryForward')) ? e.target.id : e.target.parentNode.id; 79 | 80 | if (id === 'galleryForward') 81 | var newPointerPosition =(this.state.galleryPointer + 3 < this.props.wikiImages.length) ? this.state.galleryPointer + 1 : false; 82 | else 83 | var newPointerPosition =(this.state.galleryPointer - 1 < 0) ? false : this.state.galleryPointer - 1; 84 | 85 | if (newPointerPosition) 86 | this.setState({ galleryPointer: newPointerPosition }); 87 | } 88 | 89 | renderExtract(extract, i) { return React.createElement("p", { key: 'extracts_' + i }, extract) } 90 | 91 | render() { 92 | if (this.props.itemDetail !== false) { 93 | if (isset(this.props.itemDetail.links.related)) 94 | var itemLinks = this.props.itemDetail.links.related.map(this.renderRelatedLink); 95 | else 96 | var itemLinks = this.noRelatedLinks(); 97 | 98 | if (isset(this.props.itemDetail.locations[0])) 99 | var itemLocations = this.props.itemDetail.locations.map(this.renderItemLocation); 100 | else 101 | var itemLocations = this.noLocations(); 102 | 103 | var mainLink = this.renderMainLink(); 104 | 105 | if (this.props.wikiImages !== false && this.props.wikiImages.length > 0) { 106 | var gallery = []; 107 | var n =(this.state.galleryPointer + 3 <= this.props.wikiImages.length) ? this.state.galleryPointer + 3 : this.props.wikiImages.length; 108 | 109 | for (var i = this.state.galleryPointer; i < n; i++) { 110 | var img = this.renderGalleryImg(this.props.wikiImages[i]); 111 | gallery.push(img); 112 | } 113 | } 114 | else 115 | var gallery = this.noImages(); 116 | 117 | if (this.props.wikiData !== false && this.props.wikiData.extract !== false && this.props.wikiData.extract !== '') { 118 | var paragraphs = this.props.wikiData.extract.split("\n"); 119 | var extract = paragraphs.map(this.renderExtract); 120 | } 121 | else 122 | extract = []; 123 | 124 | var controls =(gallery.length > 0) ? this.renderControls() : []; 125 | 126 | return ( 127 | React.createElement(ReactCSSTransitionGroup, { id: "itemDetail", transitionName: 'itemDetailTransition', transitionAppear: true, transitionAppearTimeout: 500, transitionEnterTimeout: 500, transitionLeaveTimeout: 500, style: { maxHeight: this.state.maxHeight } }, 128 | React.createElement("h3", null, this.props.itemDetail.text), 129 | React.createElement("div", null, 130 | extract, 131 | mainLink 132 | ), 133 | React.createElement("h4", null, 'Locations'), 134 | React.createElement("ul", null, itemLocations), 135 | React.createElement("h4", null, 'Related Links'), 136 | React.createElement("ul", null, itemLinks), 137 | React.createElement("div", { id: 'galleryTitle' }, 138 | React.createElement("h4", null, 'Images'), 139 | controls 140 | ), 141 | React.createElement("div", { id: 'gallery' }, gallery) 142 | ) 143 | ) 144 | } 145 | } 146 | } -------------------------------------------------------------------------------- /app/js/react-itemlist.js: -------------------------------------------------------------------------------- 1 | function isset (obj) { return typeof obj !== 'undefined'; } 2 | 3 | import React from 'react'; 4 | 5 | export default class ItemList extends React.Component { 6 | constructor() { 7 | super(); 8 | 9 | this.componentDidMount = this.componentDidMount.bind(this); 10 | this.componentWillReceiveProps = this.componentWillReceiveProps.bind(this); 11 | this.scrollAnimate = this.scrollAnimate.bind(this); 12 | this.renderYear = this.renderYear.bind(this); 13 | this.renderItemThumbnail = this.renderItemThumbnail.bind(this); 14 | this.renderItem = this.renderItem.bind(this); 15 | this.itemHandler = this.itemHandler.bind(this); 16 | this.renderItems = this.renderItems.bind(this); 17 | this.loadingItems = this.loadingItems.bind(this); 18 | this.noItems = this.noItems.bind(this); 19 | 20 | this.state = { jumpTo: false }; 21 | } 22 | 23 | componentDidMount() { this.jumping = false } 24 | 25 | componentWillReceiveProps(nextProps) { 26 | if (this.props.highlightLatLong === false && nextProps.highlightLatLong !== false || this.props.highlightLatLong.lat !== nextProps.highlightLatLong.lat) 27 | this.setState({ jumpTo: false }); 28 | } 29 | 30 | //gist.github.com/dezinezync/5487119 31 | scrollAnimate(Y, duration) { 32 | var start = Date.now(), 33 | elem = document.documentElement.scrollTop ? document.documentElement : document.body, 34 | from = elem.scrollTop; 35 | 36 | if (from === Y) 37 | return; // Prevent scrolling to the Y point if already there 38 | 39 | function min(a,b) { return a < b ? a : b; } 40 | 41 | function scroll(timestamp) { 42 | var currentTime = Date.now(), 43 | t = min(1,((currentTime - start) / duration)), 44 | easedT = t < .5 ? 2 * t * t : -1 +(4 - 2 * t) * t; 45 | 46 | elem.scrollTop = (easedT *(Y - from)) + from; 47 | 48 | if (t < 1) 49 | requestAnimationFrame(scroll); 50 | } 51 | 52 | requestAnimationFrame(scroll) 53 | } 54 | 55 | renderYear(x, y, year) { return React.createElement("li", { key: 'year' + x + y + year, className: 'yearTitle' }, year) } 56 | 57 | renderItemThumbnail(item, key) { 58 | var picKey = 'thumb' + key; 59 | 60 | return (isset(item.thumbnail) && item.thumbnail !== '') 61 | ? React.createElement("img", { key: picKey, className: 'thumbnail', src: item.thumbnail }) 62 | : []; 63 | } 64 | 65 | renderItem(item, i) { 66 | var key = 0, i, chr, len; 67 | 68 | for (i = 0, len = item.text.length; i < len; i++) { 69 | chr = item.text.charCodeAt(i); 70 | key =((key << 5) - key) + chr; 71 | key |= 0; // Convert to 32bit integer 72 | } 73 | 74 | var itemThumbnail = this.renderItemThumbnail(item, key); 75 | var className = 'itemPanel'; 76 | 77 | var testPropLatLongSet = isset(this.props.highlightLatLong) && isset(this.props.highlightLatLong.lat) && isset(this.props.highlightLatLong.long); 78 | var testItemLatLongSet = isset(item.latlong) && isset(item.latlong[0]) && isset(item.latlong[0][0]); 79 | 80 | if (testPropLatLongSet && testItemLatLongSet) { 81 | var latFixed = item.latlong[0][0].toFixed(1); 82 | var longFixed = item.latlong[0][1].toFixed(1); 83 | 84 | if (latFixed === this.props.highlightLatLong.lat && longFixed === this.props.highlightLatLong.long) { 85 | className += ' highlightMap'; 86 | 87 | if (this.state.jumpTo === false && this.jumping === false) { 88 | this.jumping = true; 89 | this.scrollAnimate($(key).offsetTop, 1000); 90 | } 91 | } 92 | } 93 | 94 | if (parseInt(this.state.jumpTo) === parseInt(key)) 95 | className += ' highlightClick'; 96 | 97 | 98 | var title =(isset(item.links) && isset(item.links.main) && isset(item.links.main.link)) ? item.links.main.link.replace('//en.wikipedia.org/wiki/', '') : ''; 99 | 100 | return ( 101 | React.createElement("li", { key: key, id: key, 'data-year': item.year, 'data-position': item.position, className: className, onClick: this.itemHandler }, 102 | itemThumbnail, 103 | React.createElement("p", null, 104 | React.createElement("a", { href: '/history-of-humanity/' + item.year + '/' + item.position + '/' + title }, item.text) 105 | ), 106 | React.createElement("div", { className: 'readmore' }, 107 | React.createElement("span", { 'data-year': item.year, 'data-position': item.position }, 'Read more...') 108 | ) 109 | ) 110 | ) 111 | } 112 | 113 | itemHandler(e) { 114 | if (isset(e.target.id) && e.target.id !== '') 115 | var newJumpTo = e.target.id; 116 | else if (isset(e.target.parentNode.id) && e.target.parentNode.id !== '') 117 | var newJumpTo = e.target.parentNode.id; 118 | else if (isset(e.target.parentNode.parentNode.id) && e.target.parentNode.parentNode.id !== '') 119 | var newJumpTo = e.target.parentNode.parentNode.id; 120 | 121 | if (this.state.jumpTo === false || parseInt(this.state.jumpTo) !== parseInt(newJumpTo)) { 122 | this.scrollAnimate($(newJumpTo).offsetTop, 1000); 123 | this.setState({ jumpTo: newJumpTo }); 124 | } 125 | 126 | this.props.itemHandler(e); 127 | } 128 | 129 | renderItems(item) { 130 | var relevantItems = []; 131 | 132 | if (this.props.items.length > 0) 133 | { 134 | var x = this.props.pointer * this.props.show; 135 | var y = this.props.pointer * this.props.show + this.props.show; 136 | var year = this.props.items[x].year; 137 | 138 | relevantItems.push(this.renderYear(x, y, this.props.items[x].year)); 139 | 140 | for (var i = x; i < y; i++) { 141 | if (isset(this.props.items[i])) { 142 | if (this.props.items[i].year !== year) { 143 | relevantItems.push(this.renderYear(i, y, this.props.items[i].year)); 144 | year = this.props.items[i].year; 145 | } 146 | 147 | relevantItems.push(this.renderItem(this.props.items[i], i)); 148 | } 149 | } 150 | } 151 | this.jumping = false; 152 | 153 | return relevantItems; 154 | } 155 | 156 | loadingItems() { return React.createElement("li", { className: 'yearTitle' }, 'Loading data') } 157 | noItems() { return React.createElement("li", { className: 'yearTitle' }, 'No data for your selection') } 158 | 159 | render() { 160 | if (this.props.items === false) 161 | var itemsList = this.loadingItems() 162 | else if (this.props.items.length > 0) 163 | var itemsList = this.renderItems(); 164 | else 165 | var itemsList = this.noItems(); 166 | 167 | return ( 168 | React.createElement("div", { id: "items" }, 169 | React.createElement("ul", null, 170 | itemsList 171 | ) 172 | ) 173 | ) 174 | } 175 | } -------------------------------------------------------------------------------- /app/js/react-pagination.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | export default class Pagination extends React.Component { 4 | constructor() { 5 | super(); 6 | 7 | this.clickHandler = this.clickHandler.bind(this); 8 | this.updateArr = this.updateArr.bind(this); 9 | this.renderPaginationItems = this.renderPaginationItems.bind(this); 10 | this.renderNoPagination = this.renderNoPagination.bind(this); 11 | } 12 | 13 | clickHandler(e) { 14 | e.preventDefault(); 15 | 16 | if (typeof e.target.dataset.pointer !== 'undefined') 17 | this.props.clickHandler(parseInt(e.target.dataset.pointer)); 18 | else 19 | this.props.clickHandler(parseInt(e.target.parentNode.dataset.pointer)); 20 | } 21 | 22 | updateArr() { 23 | var arr = []; 24 | 25 | if (this.props.maxBlocks - this.props.nPages < 2) { 26 | var maxPivotPages = Math.round((this.props.maxBlocks - 5) / 2); 27 | var minPage = Math.max(0, this.props.pointer - maxPivotPages); 28 | var maxPage = Math.min(this.props.nPages - 1, this.props.pointer + maxPivotPages * 2 -(this.props.pointer - minPage)); 29 | var minPage = Math.max(0, minPage - (maxPivotPages * 2 -(maxPage - minPage))); 30 | 31 | var elipses = true; 32 | } 33 | else { 34 | var minPage = 0; 35 | var maxPage = this.props.nPages - 1; 36 | 37 | var elipses = false; 38 | } 39 | 40 | if (elipses && minPage !== 0) { 41 | arr[0] = 'prev'; 42 | arr[1] = 'hellip1'; 43 | } 44 | 45 | for(var i = minPage; i <= maxPage; i++) { 46 | arr.push(i); 47 | } 48 | 49 | if (elipses && this.props.nPages !== maxPage + 1) { 50 | arr.push('hellip2'); 51 | arr.push('next'); 52 | } 53 | 54 | return arr; 55 | } 56 | 57 | renderPaginationItems(i) { 58 | var selectedClass =(this.props.pointer === i) ? "selected clickable" : "clickable"; 59 | 60 | if (i === 'prev') { 61 | return ( 62 | React.createElement("li", { key: i, className: selectedClass, 'data-pointer': 0, onClick: this.clickHandler }, 63 | React.createElement("a", { href: '/history-of-humanity/' }, '1') 64 | ) 65 | ) 66 | } 67 | else if (i === 'next') { 68 | return ( 69 | React.createElement("li", { key: i, className: selectedClass, 'data-pointer': this.props.nPages - 1, onClick: this.clickHandler }, 70 | React.createElement("a", { href: '/history-of-humanity/p/' + this.props.nPages }, this.props.nPages) 71 | ) 72 | ) 73 | } 74 | else if (i === 'hellip1' || i === 'hellip2') 75 | return React.createElement("li", { key: i, className: "more" }, "...") 76 | else { 77 | var n = i + 1; 78 | return React.createElement("li", { key: i, className: selectedClass, 'data-pointer': i, onClick: this.clickHandler }, 79 | React.createElement("a", { href: '/history-of-humanity/p/' + n }, n) 80 | ); 81 | } 82 | } 83 | 84 | renderNoPagination() { return React.createElement("li", { className: 'no_more' }, "No other pages") } 85 | 86 | render() { 87 | var className = 'paginator on'; 88 | 89 | if (this.props.nPages > 1) { 90 | var arr = this.updateArr(); 91 | var pagination = arr.map(this.renderPaginationItems) 92 | } 93 | else if (this.props.nPages === 0) 94 | var pagination = this.renderNoPagination(); 95 | else { 96 | var pagination = []; 97 | className = 'paginator'; 98 | } 99 | 100 | return React.createElement("ul", { className: className }, pagination) 101 | } 102 | } -------------------------------------------------------------------------------- /app/less/compiled.less: -------------------------------------------------------------------------------- 1 | /* 2 | * Open DAWS - Open Source Digital Application Wireframing Styleshee* Version 6.0.0 3 | * Collated by Pete Wailes - pete@watson-wail.es / @petewailes 4 | * 5 | * Includes elements from &| inspired by: 6 | * 7 | * * normalize.css by Nicolas Gallagher and Jonathan Neal - http://necolas.github.com/normalize.css/ 8 | * * Foundation by Zurb - http://foundation.zurb.com/ 9 | * * FramelessGrid by Joni Korpi - http://framelessgrid.com/ 10 | * * Baseline CSS by Stéphane Curzi - http://baselinecss.com/ 11 | * * LESSHat by CSS Hat - http://lesshat.com/ 12 | */ 13 | 14 | /* 15 | Common Mixins 16 | */ 17 | 18 | @import "lesshat.less"; 19 | 20 | /* Boot up LessHat */ 21 | 22 | @font-size: 16; // Base font-size (pixels) 23 | @line-height: 26; // Base font-size (pixels) 24 | @font-line-ratio: 1.6; // Ratio of line height to font size 25 | 26 | @em: @font-size*1em; // Shorthand for outputting ems, e.g. "16/@em" 27 | 28 | @radius: 5px; // Default radius for borders 29 | 30 | @gutter: @line-height; // Gutter-width of your grid (pixels) 31 | @column: 48; // Column-width of your grid (pixels) -- gutter * 1.61803399 rounded up 32 | @column-and-gutter: @column + @gutter; 33 | 34 | 35 | /* 36 | Grid rules & column-widths in variables, in ems 37 | */ 38 | 39 | .od-clearfix () { 40 | &:before, &:after { 41 | content: " "; 42 | display: table; 43 | } 44 | &:after { clear: both; } 45 | } 46 | 47 | .od-row () { 48 | .od-clearfix(); 49 | display: block; 50 | margin: 0 auto; 51 | padding: 0; 52 | *zoom: 1; 53 | 54 | .row { 55 | width: auto; 56 | 57 | &.collapse { margin: 0; } 58 | } 59 | } 60 | 61 | .od-col () { 62 | float: left; 63 | min-height: 1px; 64 | position: relative; 65 | } 66 | 67 | .od-col-inverse () { 68 | float: right; 69 | min-height: 1px; 70 | position: relative; 71 | } 72 | 73 | .od-gutter () { margin: (@gutter / 2) * 1px; } 74 | .od-gutter-horizontal () { 75 | margin-left: (@gutter / 2) * 1px; 76 | margin-right: (@gutter / 2) * 1px; 77 | } 78 | .od-gutter-vertical () { 79 | margin-top: (@gutter / 2) * 1px; 80 | margin-bottom: (@gutter / 2) * 1px; 81 | } 82 | .od-gutter-top () { margin-top: @gutter * 1px; } 83 | .od-gutter-right () { margin-right: @gutter * 1px; } 84 | .od-gutter-bottom () { margin-bottom: @gutter * 1px; } 85 | .od-gutter-left () { margin-left: @gutter * 1px; } 86 | 87 | .od-internal-gutter () { padding: (@gutter / 2) * 1px; } 88 | .od-internal-gutter-horizontal () { 89 | padding-left: (@gutter / 2) * 1px; 90 | padding-right: (@gutter / 2) * 1px; 91 | } 92 | .od-internal-gutter-vertical () { 93 | padding-top: (@gutter / 2) * 1px; 94 | padding-bottom: (@gutter / 2) * 1px; 95 | } 96 | .od-internal-gutter-top () { padding-top: @gutter * 1px; } 97 | .od-internal-gutter-right () { padding-right: @gutter * 1px; } 98 | .od-internal-gutter-bottom () { padding-bottom: @gutter * 1px; } 99 | .od-internal-gutter-left () { padding-left: @gutter * 1px; } 100 | 101 | @1cols: ( 1 * @column-and-gutter - @gutter) * 1px; @1col: @1cols; 102 | @2cols: ( 2 * @column-and-gutter - @gutter) * 1px; 103 | @3cols: ( 3 * @column-and-gutter - @gutter) * 1px; 104 | @4cols: ( 4 * @column-and-gutter - @gutter) * 1px; 105 | @5cols: ( 5 * @column-and-gutter - @gutter) * 1px; 106 | @6cols: ( 6 * @column-and-gutter - @gutter) * 1px; 107 | @7cols: ( 7 * @column-and-gutter - @gutter) * 1px; 108 | @8cols: ( 8 * @column-and-gutter - @gutter) * 1px; 109 | @9cols: ( 9 * @column-and-gutter - @gutter) * 1px; 110 | @10cols: (10 * @column-and-gutter - @gutter) * 1px; 111 | @11cols: (11 * @column-and-gutter - @gutter) * 1px; 112 | @12cols: (12 * @column-and-gutter - @gutter) * 1px; 113 | @13cols: (13 * @column-and-gutter - @gutter) * 1px; 114 | @14cols: (14 * @column-and-gutter - @gutter) * 1px; 115 | @15cols: (15 * @column-and-gutter - @gutter) * 1px; 116 | @16cols: (16 * @column-and-gutter - @gutter) * 1px; 117 | @17cols: (17 * @column-and-gutter - @gutter) * 1px; 118 | @18cols: (18 * @column-and-gutter - @gutter) * 1px; 119 | @19cols: (19 * @column-and-gutter - @gutter) * 1px; 120 | @20cols: (20 * @column-and-gutter - @gutter) * 1px; 121 | @21cols: (21 * @column-and-gutter - @gutter) * 1px; 122 | 123 | .od-width-1 () { width: 8.33333%; } 124 | .od-width-2 () { width: 16.66667%; } 125 | .od-width-3 () { width: 25%; } 126 | .od-width-4 () { width: 33.33333%; } 127 | .od-width-5 () { width: 41.66667%; } 128 | .od-width-6 () { width: 50%; } 129 | .od-width-7 () { width: 58.33333%; } 130 | .od-width-8 () { width: 66.66667%; } 131 | .od-width-9 () { width: 75%; } 132 | .od-width-10 () { width: 83.33333%; } 133 | .od-width-11 () { width: 91.66667%; } 134 | .od-width-12 () { width: 100%; } 135 | .od-width-13 () { width: 108.33333%; } 136 | .od-width-14 () { width: 116.66667%; } 137 | .od-width-15 () { width: 125%; } 138 | .od-width-16 () { width: 133.33333%; } 139 | .od-width-17 () { width: 141.66667%; } 140 | .od-width-18 () { width: 150%; } 141 | .od-width-19 () { width: 158.33333%; } 142 | .od-width-20 () { width: 166.66667%; } 143 | .od-width-21 () { width: 175%; } 144 | .od-width-22 () { width: 183.33333%; } 145 | .od-width-23 () { width: 191.66667%; } 146 | .od-width-24 () { width: 200%; } 147 | 148 | 149 | // Column-widths in a function, in ems 150 | .od-width (@cols: 1) { width: (@cols * @column-and-gutter) * 1px; } 151 | .od-width-mod (@cols: 1, @decrease: 0, @increase: 0) { width: (@cols * @column-and-gutter - (@decrease * @em) + (@increase * @em)) * 1px; } 152 | 153 | .od-max-width (@cols: 1) { max-width: (@cols * @column-and-gutter) * 1px; } 154 | .od-max-width-mod (@cols: 1, @decrease: 0, @increase: 0) { max-width: (@cols * @column-and-gutter - (@decrease * @em) + (@increase * @em)) * 1px; } 155 | 156 | .od-height (@cols: 1) { height: (@cols * @column-and-gutter) * 1px; } 157 | .od-height-mod (@cols: 1, @decrease: 0, @increase: 0) { height: (@cols * @column-and-gutter - (@decrease * @em) + (@increase * @em)) * 1px; } 158 | 159 | .od-max-height (@cols: 1) { max-height: (@cols * @column-and-gutter) * 1px; } 160 | .od-max-height-mod (@cols: 1, @decrease: 0, @increase: 0) { max-height: (@cols * @column-and-gutter - (@decrease * @em) + (@increase * @em)) * 1px; } 161 | 162 | .od-push-col-gutter (@cols: 1) { margin-left: (@cols * @column-and-gutter) * 1px; } 163 | .od-push-col (@cols: 1) { margin-left: (@cols * @column-and-gutter) * 1px; } 164 | 165 | .od-top-positive (@cols: 1) { top: (@cols * @column-and-gutter) * 1px; } 166 | .od-top-negative (@cols: 1) { top: (0 - (@cols * @column-and-gutter)) * 1px; } 167 | 168 | .od-right-positive (@cols: 1) { right: (@cols * @column-and-gutter) * 1px; } 169 | .od-right-negative (@cols: 1) { right: (0 - (@cols * @column-and-gutter)) * 1px; } 170 | 171 | .od-bottom-positive (@cols: 1) { bottom: (@cols * @column-and-gutter) * 1px; } 172 | .od-bottom-negative (@cols: 1) { bottom: (0 - (@cols * @column-and-gutter)) * 1px; } 173 | 174 | .od-left-positive (@cols: 1) { left: (@cols * @column-and-gutter) * 1px; } 175 | .od-left-negative (@cols: 1) { left: (0 - (@cols * @column-and-gutter)) * 1px; } 176 | 177 | .od-clear { clear: both; } 178 | 179 | /* 180 | Resets & normalisation 181 | */ 182 | 183 | html, body, div, span, a, img, h1, h2, h3, h4, h5, h6, 184 | hgroup, p, dl, dialog, dt, dd, ol, ul, li, abbr, acronym, 185 | address, b, big, blockquote, cite, code, 186 | del, dfn, em, i, ins, kbd, pre, q, samp, tt, var, 187 | small, strong, sub, sup, object, iframe, 188 | form, fieldset, label, legend, table, caption, 189 | tbody, tfoot, thead, tr, th, td, 190 | article, aside, footer, header, nav, section, 191 | figure, menu, time, mark, audio, video { 192 | background: transparent; 193 | border: 0; 194 | font-family: inherit; 195 | font-size: 100%; 196 | font-weight: inherit; 197 | font-style: inherit; 198 | margin: 0; 199 | outline: 0; 200 | padding: 0; 201 | text-align: left; 202 | vertical-align: baseline; 203 | white-space: normal; 204 | } 205 | 206 | article, aside, footer, header, nav, section, dialog, figure, hgroup, menu { 207 | display: block; 208 | } 209 | 210 | * { .box-sizing(border-box); } 211 | 212 | /* Deprecated and obsolete elements */ 213 | applet, basefont, dir, font, isindex, menu, s, strike, u { 214 | border: 0; 215 | color: inherit; 216 | font-family: inherit; 217 | font-size: 100%; 218 | font-weight: normal; 219 | font-style: normal; 220 | margin: 0; 221 | outline: 0; 222 | padding: 0; 223 | text-decoration: inherit; 224 | text-align: left; 225 | vertical-align: baseline; 226 | white-space: normal; 227 | } 228 | 229 | dir, menu { list-style: none; } 230 | nobr { white-space: normal; } 231 | blink { text-decoration: none; } 232 | marquee { overflow: visible; } 233 | 234 | html { // Prevent iOS text size adjust after orientation change, without disabling user zoom 235 | font-family: sans-serif; 236 | height: 100%; 237 | -webkit-text-size-adjust: 100%; 238 | -ms-text-size-adjust: 100%; 239 | } 240 | 241 | body { 242 | background: white; 243 | color: #313131; 244 | font-size: 100%; 245 | line-height: 1.5; 246 | min-height: 100%; 247 | -webkit-tap-highlight-color: rgba(255,0,0, 0.62); 248 | } 249 | 250 | 251 | cite, dfn, em, i { font-style: italic; } 252 | 253 | mark { background: rgba(255, 255, 0, 0.4); padding: 0 .25em; } 254 | 255 | article, aside, details, figcaption, header, hgroup, nav, figure, section, footer { display: block; } /* Correct 'block' display not defined in IE 8/9 */ 256 | 257 | audio, canvas, video { display: inline-block; } /* Correct 'inline-block' display not defined in IE 8/9 */ 258 | audio:not([controls]) { 259 | display: none; 260 | height: 0; 261 | } 262 | 263 | /* For debugging */ 264 | 265 | .test { background: #edd; } 266 | 267 | 268 | /* 269 | Typography 270 | */ 271 | 272 | h1 { // 60px / 72px 273 | font-size: 3.75em; 274 | line-height: 1.2em; 275 | margin-bottom: 0.4em 276 | } 277 | 278 | h2 { // 48px / 48px 279 | font-size: 3em; 280 | line-height: 1em; 281 | margin-bottom: 0.5em 282 | } 283 | 284 | h3 { // 36px / 48px 285 | font-size: 2.25em; 286 | line-height: 1.3333333333333333333333333333333em; 287 | margin-bottom: 0.6667em 288 | } 289 | 290 | h4 { // 24px / 24px 291 | font-size: 1.5em; 292 | line-height: 1em; 293 | margin-bottom: 1em 294 | } 295 | 296 | h5 { // 21px / 24px 297 | font-size: 1.3125em; 298 | line-height: 1.1428571428571428571428571428571em; 299 | margin-bottom: 1.1428571428571428571428571428571em 300 | } 301 | 302 | h6 { // 18px / 24px 303 | font-size: 1.125em; 304 | line-height: 1.3333333333333333333333333333333em; 305 | margin-bottom: 1.3333333333333333333333333333333em 306 | } 307 | 308 | p, ul, ol, dl, dialog, blockquote, pre, code, address, td, th, label { // 16px / 24px 309 | display: block; 310 | font-size: 1em; 311 | line-height: @font-line-ratio * 1em; 312 | margin-bottom: @font-line-ratio * 1em; 313 | } 314 | 315 | small, p.small { // 14px / 24px 316 | font-size: 80%; 317 | line-height: 1.2; 318 | margin: 0; 319 | } 320 | 321 | a { 322 | color: #0049cc; 323 | text-decoration: none; 324 | &:hover { color: #0bcdd9; } 325 | &:focus { outline: thin dotted } /* Fix outline inconsistency between Chrome and other browsers */ 326 | &:hover, &:active { outline: none } /* Better outline suppression */ 327 | } 328 | 329 | a img, img { 330 | -ms-interpolation-mode: bicubic; 331 | border: 0; 332 | text-decoration: none; 333 | } 334 | 335 | hr { 336 | margin: 0 0 1.375em; 337 | padding: 0; 338 | } 339 | 340 | /* Addresses styles set to 'bolder' in Firefox 4+, Safari 5, and Chrome, enforces defaults for other browsers */ 341 | b, strong, caption, th, thead, dt, legend { font-weight: 700; } 342 | 343 | /* Addresses styling not present in Safari 5 and Chrome, enforces defaults for other browsers */ 344 | dfn, em, i { font-style: italic } 345 | var, address { font-style: normal } 346 | 347 | /* Code */ 348 | p code, p pre, p var { line-height: @font-line-ratio * 1em * 0.75; } 349 | 350 | pre { // Allow line wrapping of 'pre' 351 | white-space: pre; 352 | white-space: pre-wrap; 353 | word-wrap: break-word 354 | } 355 | 356 | /* Lists */ 357 | ol, ul, dl, dialog { 358 | list-style: none; 359 | list-style-position: inside; 360 | margin-left: @font-line-ratio * 1em; 361 | padding: 0 362 | } 363 | 364 | ul { list-style: disc outside; } 365 | ol { list-style: decimal outside; } 366 | 367 | li { 368 | font-size: 1em; 369 | margin-bottom: 1em; 370 | 371 | &:last-child, 372 | label { margin-bottom: 0; } 373 | 374 | ul, ol { margin: 1em @font-line-ratio * 1em 0; } 375 | ul { list-style-type: circle; } 376 | ol { list-style-type: lower-alpha; } 377 | } 378 | 379 | dl dd { margin-left: @font-line-ratio * 1em } 380 | 381 | nav ul li { margin-left: 0; } 382 | 383 | // Unstyled lists 384 | 385 | .od-unstyled-list () { 386 | list-style-type: none; 387 | margin-left: 0; 388 | 389 | li { 390 | display: block; 391 | list-style: none; 392 | } 393 | } 394 | 395 | .unstyled-list { 396 | .od-unstyled-list(); 397 | padding: 0; 398 | } 399 | 400 | // Inline lists 401 | 402 | .od-inline () { 403 | .od-unstyled-list (); 404 | overflow: hidden; 405 | 406 | &:before, &:after { 407 | content: ""; 408 | display: table; 409 | } 410 | 411 | &:after { clear: both; } 412 | 413 | li { 414 | display: inline-block; 415 | margin: 0; 416 | 417 | &:before { 418 | content: ""; // this can be altered for seperators. See below 419 | margin: 0 0.66667em; 420 | position: relative; 421 | top: 1px; 422 | } 423 | &:first-child:before { margin: 0; } 424 | 425 | form, input { margin-bottom: 0; } 426 | } 427 | } 428 | 429 | .inline { 430 | .od-inline(); 431 | padding: 0; 432 | 433 | &.breadcrumbs li:before { content: "/"; } 434 | &.breadcrumbs li&:first-child:before { 435 | content: ""; 436 | margin: 0; 437 | } 438 | 439 | &.no-margin li:before { margin: 0; } 440 | &.li-margin li { 441 | margin-left: 0.66667em; 442 | 443 | &:first-child { margin: 0; } 444 | &:before { margin: 0; } 445 | } 446 | 447 | .radius > *:first-child, 448 | .radius > *:first-child > a, 449 | .radius > *:first-child > button, 450 | .radius > *:first-child > .button { 451 | .border-bottom-left-radius(@radius); 452 | .border-top-left-radius(@radius); 453 | } 454 | 455 | .radius > *:last-child, 456 | .radius > *:last-child > a, 457 | .radius > *:last-child > button, 458 | .radius > *:last-child > .button { 459 | .border-bottom-right-radius(@radius); 460 | .border-top-right-radius(@radius); 461 | } 462 | } 463 | 464 | .inline-scroll { 465 | .od-inline(); 466 | padding: 0; 467 | overflow-x: auto; 468 | white-space: nowrap; 469 | } 470 | 471 | .pagination { 472 | .od-inline(); 473 | padding: 0; 474 | 475 | li { 476 | margin-left: 0.25em; 477 | 478 | &:hover a { 479 | background: #eee; 480 | } 481 | 482 | a { 483 | .border-radius(@radius); 484 | .transition(background-color 300ms ease-out); 485 | display: block; 486 | padding: 0.1em 0.75rem 0.1em; 487 | 488 | &.selected { 489 | background: #0049cc; 490 | color: #fff; 491 | cursor: default; 492 | font-weight: bold; 493 | } 494 | } 495 | } 496 | } 497 | 498 | /* Quotes */ 499 | blockquote, cite, q { // Sets consistent quote types 500 | font-style: italic 501 | quotes: none; 502 | } 503 | 504 | blockquote:before, blockquote:after, 505 | q:before, q:after { 506 | content: ''; 507 | content: none 508 | } 509 | 510 | blockquote { 511 | border-left: 3px solid #dfdfdf; 512 | padding-left: @font-line-ratio * 1em; 513 | > p { padding: 0 } 514 | } 515 | 516 | /* Abbreviations */ 517 | abbr, acronym, dfn { 518 | font-size: 80%; 519 | letter-spacing: .1em; 520 | line-height: 1.2; 521 | text-transform: uppercase; 522 | } 523 | 524 | abbr[title], acronym[title], dfn[title] { // Addresses styling not present in IE 8/9, Safari 5, and Chrome 525 | border-bottom: 1px dotted black; 526 | cursor: help; 527 | } 528 | 529 | /* Marks, inserts & deletes */ 530 | ins, mark { text-decoration: none } 531 | ins { color: #f00; } 532 | del { text-decoration: line-through } 533 | 534 | mark { // Addresses styling not present in IE 8/9 535 | background: #c47529; 536 | color: #000 537 | } 538 | 539 | sub, sup { // Position 'sub' and 'sup' without affecting line-height 540 | font-size: 75%; 541 | line-height: 0; 542 | position: relative; 543 | vertical-align: baseline 544 | } 545 | 546 | sup { top: -0.7em } /* Move superscripted text up */ 547 | sub { bottom: -0.25em } /* Move subscripted text down */ 548 | 549 | /* Media */ 550 | img, object, embed, video { // Fluid images 551 | max-width: 100%; 552 | } 553 | 554 | img { // Improve IE's resizing of images 555 | border: 0; 556 | -ms-interpolation-mode: bicubic; 557 | &::selection { background: transparent; } 558 | &::-moz-selection { background: transparent; } 559 | 560 | &.no-max { max-width: none; } 561 | } 562 | 563 | figure { 564 | margin: 0 0 1.6em; 565 | max-width: 100%; 566 | 567 | img { 568 | height: auto; 569 | width: 100%; 570 | } 571 | 572 | figcaption { 573 | background: #eee; 574 | border-left: 5px solid #999; 575 | margin-bottom: 0; 576 | padding: .4em .8em; 577 | } 578 | } 579 | svg:not(:root) { overflow: hidden } /* Correct IE9 overflow */ 580 | 581 | ::-moz-selection, 582 | ::selection { // selected text 583 | background: #c47529; 584 | color: #fff; 585 | text-shadow: none 586 | } 587 | 588 | 589 | /* 590 | * Tables 591 | */ 592 | 593 | table { 594 | border-collapse: collapse; 595 | border-spacing: 0; 596 | margin-bottom: @font-line-ratio * 1em; 597 | max-width: 100%; 598 | width: 100%; 599 | 600 | .striped > tbody > tr:nth-child(odd) > td { background: #f0f0f0; } 601 | } 602 | 603 | tr { 604 | display: table-row; 605 | 606 | .success td { background-color: #2ecc71; } 607 | .success-dark td { background-color: #27ad60; } 608 | .info td { background-color: #3498db; } 609 | .info-dark td { background-color: #2383c4; } 610 | .warning td { background-color: #f1c40f; } 611 | .warning-dark td { background-color: #cea70c; } 612 | .danger td { background-color: #e74c3c; } 613 | .danger-dark td { background-color: #dc2d1b; } 614 | } 615 | 616 | td, 617 | th { 618 | display: table-cell; 619 | padding: @font-size / 2 * 1px; 620 | } 621 | 622 | th { 623 | border-bottom: 2px solid #ddd; 624 | border-top: 0; 625 | vertical-align: bottom; 626 | } 627 | 628 | td { 629 | border-top: 1px solid #ddd; 630 | vertical-align: top; 631 | } 632 | 633 | 634 | /* 635 | Forms 636 | */ 637 | 638 | form { 639 | border-bottom: 1px solid rgba(0, 0, 0, 0.1); 640 | margin-bottom: @font-size * 1.25 * 1px; 641 | padding: 0 0 @font-size * 1px; 642 | 643 | fieldset { 644 | border: 0; 645 | margin: 0; 646 | min-width: 0; 647 | padding: 0; 648 | 649 | legend { 650 | border: 0; 651 | border-bottom: 1px solid #e5e5e5; 652 | display: block; 653 | font-size: @font-size * 1.2 * 1px; 654 | padding: 0; 655 | width: 100%; 656 | } 657 | } 658 | 659 | label { 660 | display: inline-block; 661 | font-family: inherit; 662 | font-weight: bold; 663 | margin-bottom: @font-size / 2 * 1px; 664 | max-width: 100%; 665 | 666 | &.firm { color: #1abc9c; } 667 | &.success { color: #11b452; } 668 | &.info { color: #3498db; } 669 | &.warning { color: #f1c40f; } 670 | &.danger { color: #e74c3c; } 671 | &.night { color: #34495e; } 672 | } 673 | 674 | textarea, 675 | input[type=text], 676 | input[type=password], 677 | input[type=email], 678 | input[type=number] { 679 | .box-shadow(inset 0 -1px 0 #ddd); 680 | .transition(border-color ease-in-out .15s, box-shadow ease-in-out .15s); 681 | background: rgba(0, 0, 0, 0); 682 | border: none; 683 | color: #666; 684 | font-size: @font-size*1px; 685 | display: block; 686 | min-height: @line-height * 1px; 687 | line-height: @line-height * 1px; 688 | padding: 0; 689 | width: 100%; 690 | 691 | &:focus { 692 | .box-shadow(inset 0 -2px 0 #2196F3); 693 | border-color: #66AFE9; 694 | outline: 0; 695 | } 696 | 697 | &.firm { border-color: #1abc9c; } 698 | &.success { border-color: #11b452; } 699 | &.info { border-color: #3498db; } 700 | &.warning { border-color: #f1c40f; } 701 | &.danger { border-color: #e74c3c; } 702 | } 703 | 704 | select[multiple], 705 | select[size], 706 | textarea { height: auto; } 707 | 708 | select { 709 | .transition(border-color ease-in-out .15s, box-shadow ease-in-out .15s); 710 | background-color: #fff; 711 | border: 1px solid #ccc; 712 | font-size: @font-size * 1px; 713 | line-height: @line-height * 1px; 714 | margin: 0; 715 | padding: 0.3em 0.2em; 716 | position: relative; 717 | top: -4px; 718 | width: 100%; 719 | 720 | &:focus { 721 | border-color: #66AFE9; 722 | outline: 0; 723 | } 724 | } 725 | 726 | &.no-border { border-bottom: none; } 727 | 728 | .input-row { 729 | .internal-gutter-horizontal(); 730 | margin-bottom: @font-size * 1px; 731 | position: relative; 732 | } 733 | 734 | .input-group { 735 | border-collapse: separate; 736 | display: table; 737 | position: relative; 738 | 739 | .addon, 740 | .input, 741 | .button { display: table-cell; } 742 | 743 | .addon, 744 | .button { 745 | padding: 0 @font-size * 1px; 746 | vertical-align: middle; 747 | white-space: nowrap; 748 | width: 1%; 749 | } 750 | 751 | .button { 752 | font-size: 0; 753 | position: relative; 754 | white-space: nowrap; 755 | } 756 | 757 | .input { 758 | float: left; 759 | margin-bottom: 0; 760 | position: relative; 761 | width: 100%; 762 | z-index: 2; 763 | } 764 | } 765 | } 766 | 767 | form input[type="submit"], 768 | form input[type="reset"], 769 | html input[type="button"], 770 | form button, 771 | a.button { 772 | .border-radius(@radius); 773 | .transition(all 0.2s); 774 | .user-select(none); 775 | background: #fff; 776 | border: none; 777 | color: #666; 778 | cursor: pointer; 779 | display: inline-block; 780 | font-size: @font-size * 0.8 * 1px; 781 | font-weight: normal; 782 | line-height: @line-height * 0.8 * 1px; 783 | letter-spacing: .1px; 784 | margin: 0; 785 | padding: 7px 16px; 786 | position: relative; 787 | text-align: center; 788 | text-rendering: optimizeLegibility; 789 | text-transform: uppercase; 790 | top: -7px; 791 | vertical-align: middle; 792 | white-space: nowrap; 793 | -webkit-font-smoothing: antialiased; 794 | 795 | &:hover { .box-shadow(0 3px 6px rgba(0, 0, 0, 0.2), 0 3px 6px rgba(0, 0, 0, 0.28)); } 796 | 797 | &.success { 798 | background: #11b452; 799 | &:active { background: #27ad60; } 800 | } 801 | 802 | &.info { 803 | background: #3498db; 804 | &:active { background: #2383c4; } 805 | } 806 | 807 | &.warning { 808 | background: #f1c40f; 809 | &:active { background: #cea70c; } 810 | } 811 | 812 | &.danger { 813 | background: #e74c3c; 814 | &:active { background: #dc2d1b; } 815 | } 816 | 817 | &.submit { 818 | background: #2196f3; 819 | color: #fff; 820 | &:active { background: #285e8e; } 821 | } 822 | } 823 | 824 | /* 825 | Addresses styling for 'hidden' attribute not present in IE 8/9 826 | Re-set default cursor for disabled elements 827 | */ 828 | [hidden] { display: none; } 829 | [disabled] { cursor: default; } 830 | 831 | :focus { outline: 0; } 832 | 833 | 834 | /* 835 | Componentry 836 | */ 837 | 838 | // Grid 839 | 840 | // Classes for arbitrary use 841 | .clearfix { .od-clearfix() } 842 | .row { .od-row() } 843 | 844 | .col { 845 | .od-col(); 846 | .od-internal-gutter-horizontal(); 847 | } 848 | 849 | .col-no-gutter { 850 | .od-col(); 851 | } 852 | 853 | .col-left-gutter { 854 | .od-col(); 855 | .od-internal-gutter-left(); 856 | } 857 | 858 | .col-right-gutter { 859 | .od-col(); 860 | .od-internal-gutter-right(); 861 | } 862 | 863 | .col-inverse { 864 | .od-col-inverse(); 865 | .od-internal-gutter-horizontal(); 866 | } 867 | 868 | .col-no-gutter-inverse { 869 | .od-col-inverse(); 870 | } 871 | 872 | .col-left-gutter-inverse { 873 | .od-col-inverse(); 874 | .od-internal-gutter-left(); 875 | } 876 | 877 | .col-right-gutter-inverse { 878 | .od-col-inverse(); 879 | .od-internal-gutter-right(); 880 | } 881 | 882 | .col-inverse-no-gutter.width-1, 883 | .col-inverse.width-1, 884 | .col-no-gutter.width-1, 885 | .col.width-1 { width: 8.33333%; } 886 | 887 | .col-inverse-no-gutter.width-2, 888 | .col-inverse.width-2, 889 | .col-no-gutter.width-2, 890 | .col.width-2 { width: 16.66667%; } 891 | 892 | .col-inverse-no-gutter.width-3, 893 | .col-inverse.width-3, 894 | .col-no-gutter.width-3, 895 | .col.width-3 { width: 25%; } 896 | 897 | .col-inverse-no-gutter.width-4, 898 | .col-inverse.width-4, 899 | .col-no-gutter.width-4, 900 | .col.width-4 { width: 33.33333%; } 901 | 902 | .col-inverse-no-gutter.width-5, 903 | .col-inverse.width-5, 904 | .col-no-gutter.width-5, 905 | .col.width-5 { width: 41.66667%; } 906 | 907 | .col-inverse-no-gutter.width-6, 908 | .col-inverse.width-6, 909 | .col-no-gutter.width-6, 910 | .col.width-6 { width: 50%; } 911 | 912 | .col-inverse-no-gutter.width-7, 913 | .col-inverse.width-7, 914 | .col-no-gutter.width-7, 915 | .col.width-7 { width: 58.33333%; } 916 | 917 | .col-inverse-no-gutter.width-8, 918 | .col-inverse.width-8, 919 | .col-no-gutter.width-8, 920 | .col.width-8 { width: 66.66667%; } 921 | 922 | .col-inverse-no-gutter.width-9, 923 | .col-inverse.width-9, 924 | .col-no-gutter.width-9, 925 | .col.width-9 { width: 75%; } 926 | 927 | .col-inverse-no-gutter.width-10, 928 | .col-inverse.width-10, 929 | .col-no-gutter.width-10, 930 | .col.width-10 { width: 83.33333%; } 931 | 932 | .col-inverse-no-gutter.width-11, 933 | .col-inverse.width-11, 934 | .col-no-gutter.width-11, 935 | .col.width-11 { width: 91.66667%; } 936 | 937 | .col-inverse-no-gutter.width-12, 938 | .col-inverse.width-12, 939 | .col-no-gutter.width-12, 940 | .col.width-12 { width: 100%; } 941 | 942 | 943 | /* Size specific stuff. Stops people screwing themselves up on mobile devices, mostly */ 944 | 945 | @media screen and (max-width: 799px) { 946 | .col-inverse-no-gutter.width-13, 947 | .col-inverse.width-13, 948 | .col-no-gutter.width-13, 949 | .col.width-13, 950 | .col-inverse-no-gutter.width-14, 951 | .col-inverse.width-14, 952 | .col-no-gutter.width-14, 953 | .col.width-14, 954 | .col-inverse-no-gutter.width-15, 955 | .col-inverse.width-15, 956 | .col-no-gutter.width-15, 957 | .col.width-15, 958 | .col-inverse-no-gutter.width-16, 959 | .col-inverse.width-16, 960 | .col-no-gutter.width-16, 961 | .col.width-16, 962 | .col-inverse-no-gutter.width-17, 963 | .col-inverse.width-17, 964 | .col-no-gutter.width-17, 965 | .col.width-17, 966 | .col-inverse-no-gutter.width-18, 967 | .col-inverse.width-18, 968 | .col-no-gutter.width-18, 969 | .col.width-18, 970 | .col-inverse-no-gutter.width-19, 971 | .col-inverse.width-19, 972 | .col-no-gutter.width-19, 973 | .col.width-19, 974 | .col-inverse-no-gutter.width-20, 975 | .col-inverse.width-20, 976 | .col-no-gutter.width-20, 977 | .col.width-20, 978 | .col-inverse-no-gutter.width-21, 979 | .col-inverse.width-21, 980 | .col-no-gutter.width-21, 981 | .col.width-21, 982 | .col-inverse-no-gutter.width-22, 983 | .col-inverse.width-22, 984 | .col-no-gutter.width-22, 985 | .col.width-22, 986 | .col-inverse-no-gutter.width-23, 987 | .col-inverse.width-23, 988 | .col-no-gutter.width-23, 989 | .col.width-23, 990 | .col-inverse-no-gutter.width-24, 991 | .col-inverse.width-24, 992 | .col-no-gutter.width-24, 993 | .col.width-24 { width: 100%; } 994 | } 995 | 996 | @media screen and (min-width: 800px) { 997 | .col-inverse-no-gutter.width-13, 998 | .col-inverse.width-13, 999 | .col-no-gutter.width-13, 1000 | .col.width-13 { width: 108.33333%; } 1001 | 1002 | .col-inverse-no-gutter.width-14, 1003 | .col-inverse.width-14, 1004 | .col-no-gutter.width-14, 1005 | .col.width-14 { width: 116.66667%; } 1006 | 1007 | .col-inverse-no-gutter.width-15, 1008 | .col-inverse.width-15, 1009 | .col-no-gutter.width-15, 1010 | .col.width-15 { width: 125%; } 1011 | 1012 | .col-inverse-no-gutter.width-16, 1013 | .col-inverse.width-16, 1014 | .col-no-gutter.width-16, 1015 | .col.width-16 { width: 133.33333%; } 1016 | 1017 | .col-inverse-no-gutter.width-17, 1018 | .col-inverse.width-17, 1019 | .col-no-gutter.width-17, 1020 | .col.width-17 { width: 141.66667%; } 1021 | 1022 | .col-inverse-no-gutter.width-18, 1023 | .col-inverse.width-18, 1024 | .col-no-gutter.width-18, 1025 | .col.width-18 { width: 150%; } 1026 | 1027 | .col-inverse-no-gutter.width-19, 1028 | .col-inverse.width-19, 1029 | .col-no-gutter.width-19, 1030 | .col.width-19 { width: 158.33333%; } 1031 | 1032 | .col-inverse-no-gutter.width-20, 1033 | .col-inverse.width-20, 1034 | .col-no-gutter.width-20, 1035 | .col.width-20 { width: 166.66667%; } 1036 | 1037 | .col-inverse-no-gutter.width-21, 1038 | .col-inverse.width-21, 1039 | .col-no-gutter.width-21, 1040 | .col.width-21 { width: 175%; } 1041 | 1042 | .col-inverse-no-gutter.width-22, 1043 | .col-inverse.width-22, 1044 | .col-no-gutter.width-22, 1045 | .col.width-22 { width: 183.33333%; } 1046 | 1047 | .col-inverse-no-gutter.width-23, 1048 | .col-inverse.width-23, 1049 | .col-no-gutter.width-23, 1050 | .col.width-23 { width: 191.66667%; } 1051 | 1052 | .col-inverse-no-gutter.width-24, 1053 | .col-inverse.width-24, 1054 | .col-no-gutter.width-24, 1055 | .col.width-24 { width: 200%; } 1056 | 1057 | 1058 | .col-inverse-no-gutter.width-offset-0, 1059 | .col-inverse.width-offset-0, 1060 | .col-no-gutter.width-offset-0, 1061 | .col.width-offset-0 { margin-left: 0%; } 1062 | 1063 | .col-inverse-no-gutter.width-offset-1, 1064 | .col-inverse.width-offset-1, 1065 | .col-no-gutter.width-offset-1, 1066 | .col.width-offset-1 { margin-left: 8.33333%; } 1067 | 1068 | .col-inverse-no-gutter.width-offset-2, 1069 | .col-inverse.width-offset-2, 1070 | .col-no-gutter.width-offset-2, 1071 | .col.width-offset-2 { margin-left: 16.66667%; } 1072 | 1073 | .col-inverse-no-gutter.width-offset-3, 1074 | .col-inverse.width-offset-3, 1075 | .col-no-gutter.width-offset-3, 1076 | .col.width-offset-3 { margin-left: 25%; } 1077 | 1078 | .col-inverse-no-gutter.width-offset-4, 1079 | .col-inverse.width-offset-4, 1080 | .col-no-gutter.width-offset-4, 1081 | .col.width-offset-4 { margin-left: 33.33333%; } 1082 | 1083 | .col-inverse-no-gutter.width-offset-5, 1084 | .col-inverse.width-offset-5, 1085 | .col-no-gutter.width-offset-5, 1086 | .col.width-offset-5 { margin-left: 41.66667%; } 1087 | 1088 | .col-inverse-no-gutter.width-offset-6, 1089 | .col-inverse.width-offset-6, 1090 | .col-no-gutter.width-offset-6, 1091 | .col.width-offset-6 { margin-left: 50%; } 1092 | 1093 | .col-inverse-no-gutter.width-offset-7, 1094 | .col-inverse.width-offset-7, 1095 | .col-no-gutter.width-offset-7, 1096 | .col.width-offset-7 { margin-left: 58.33333%; } 1097 | 1098 | .col-inverse-no-gutter.width-offset-8, 1099 | .col-inverse.width-offset-8, 1100 | .col-no-gutter.width-offset-8, 1101 | .col.width-offset-8 { margin-left: 66.66667%; } 1102 | 1103 | .col-inverse-no-gutter.width-offset-9, 1104 | .col-inverse.width-offset-9, 1105 | .col-no-gutter.width-offset-9, 1106 | .col.width-offset-9 { margin-left: 75%; } 1107 | 1108 | .col-inverse-no-gutter.width-offset-10, 1109 | .col-inverse.width-offset-10, 1110 | .col-no-gutter.width-offset-10, 1111 | .col.width-offset-10 { margin-left: 83.33333%; } 1112 | 1113 | 1114 | .col-inverse-no-gutter.push, 1115 | .col-inverse.push, 1116 | .col-no-gutter.push, 1117 | .col.push { right: auto; } 1118 | 1119 | .col-inverse-no-gutter.pull, 1120 | .col-inverse.pull, 1121 | .col-no-gutter.pull, 1122 | .col.pull { left: auto; } 1123 | 1124 | 1125 | .col-inverse-no-gutter.push-1, 1126 | .col-inverse.push-1, 1127 | .col-no-gutter.push-1, 1128 | .col.push-1 { 1129 | margin-left: -8.33333%; 1130 | left: 8.33333%; 1131 | } 1132 | 1133 | .col-inverse-no-gutter.push-2, 1134 | .col-inverse.push-2, 1135 | .col-no-gutter.push-2, 1136 | .col.push-2 { 1137 | margin-left: -16.66667%; 1138 | left: 16.66667%; 1139 | } 1140 | 1141 | .col-inverse-no-gutter.push-3, 1142 | .col-inverse.push-3, 1143 | .col-no-gutter.push-3, 1144 | .col.push-3 { 1145 | margin-left: -25%; 1146 | left: 25%; 1147 | } 1148 | 1149 | .col-inverse-no-gutter.push-4, 1150 | .col-inverse.push-4, 1151 | .col-no-gutter.push-4, 1152 | .col.push-4 { 1153 | margin-left: -33.33333%; 1154 | left: 33.33333%; 1155 | } 1156 | 1157 | .col-inverse-no-gutter.push-5, 1158 | .col-inverse.push-5, 1159 | .col-no-gutter.push-5, 1160 | .col.push-5 { 1161 | margin-left: -41.66667%; 1162 | left: 41.66667%; 1163 | } 1164 | 1165 | .col-inverse-no-gutter.push-6, 1166 | .col-inverse.push-6, 1167 | .col-no-gutter.push-6, 1168 | .col.push-6 { 1169 | margin-left: -50%; 1170 | left: 50%; 1171 | } 1172 | 1173 | .col-inverse-no-gutter.push-7, 1174 | .col-inverse.push-7, 1175 | .col-no-gutter.push-7, 1176 | .col.push-7 { 1177 | margin-left: -58.33333%; 1178 | left: 58.33333%; 1179 | } 1180 | 1181 | .col-inverse-no-gutter.push-8, 1182 | .col-inverse.push-8, 1183 | .col-no-gutter.push-8, 1184 | .col.push-8 { 1185 | margin-left: -66.66667%; 1186 | left: 66.66667%; 1187 | } 1188 | 1189 | .col-inverse-no-gutter.push-9, 1190 | .col-inverse.push-9, 1191 | .col-no-gutter.push-9, 1192 | .col.push-9 { 1193 | margin-left: -75%; 1194 | left: 75%; 1195 | } 1196 | 1197 | .col-inverse-no-gutter.push-10, 1198 | .col-inverse.push-10, 1199 | .col-no-gutter.push-10, 1200 | .col.push-10 { 1201 | margin-left: -83.33333%; 1202 | left: 83.33333%; 1203 | } 1204 | 1205 | .col-inverse-no-gutter.push-11, 1206 | .col-inverse.push-11, 1207 | .col-no-gutter.push-11, 1208 | .col.push-11 { 1209 | margin-left: -91.66667%; 1210 | left: 91.66667%; 1211 | } 1212 | 1213 | 1214 | .col-inverse-no-gutter.pull-1, 1215 | .col-inverse.pull-1, 1216 | .col-no-gutter.pull-1, 1217 | .col.pull-1 { 1218 | margin-right: -8.33333%; 1219 | right: 8.33333%; 1220 | } 1221 | 1222 | .col-inverse-no-gutter.pull-2, 1223 | .col-inverse.pull-2, 1224 | .col-no-gutter.pull-2, 1225 | .col.pull-2 { 1226 | margin-right: -16.66667%; 1227 | right: 16.66667%; 1228 | } 1229 | 1230 | .col-inverse-no-gutter.pull-3, 1231 | .col-inverse.pull-3, 1232 | .col-no-gutter.pull-3, 1233 | .col.pull-3 { 1234 | margin-right: -25%; 1235 | right: 25%; 1236 | } 1237 | 1238 | .col-inverse-no-gutter.pull-4, 1239 | .col-inverse.pull-4, 1240 | .col-no-gutter.pull-4, 1241 | .col.pull-4 { 1242 | margin-right: -33.33333%; 1243 | right: 33.33333%; 1244 | } 1245 | 1246 | .col-inverse-no-gutter.pull-5, 1247 | .col-inverse.pull-5, 1248 | .col-no-gutter.pull-5, 1249 | .col.pull-5 { 1250 | margin-right: -41.66667%; 1251 | right: 41.66667%; 1252 | } 1253 | 1254 | .col-inverse-no-gutter.pull-6, 1255 | .col-inverse.pull-6, 1256 | .col-no-gutter.pull-6, 1257 | .col.pull-6 { 1258 | margin-right: -50%; 1259 | right: 50%; 1260 | } 1261 | 1262 | .col-inverse-no-gutter.pull-7, 1263 | .col-inverse.pull-7, 1264 | .col-no-gutter.pull-7, 1265 | .col.pull-7 { 1266 | margin-right: -58.33333%; 1267 | right: 58.33333%; 1268 | } 1269 | 1270 | .col-inverse-no-gutter.pull-8, 1271 | .col-inverse.pull-8, 1272 | .col-no-gutter.pull-8, 1273 | .col.pull-8 { 1274 | margin-right: -66.66667%; 1275 | right: 66.66667%; 1276 | } 1277 | 1278 | .col-inverse-no-gutter.pull-9, 1279 | .col-inverse.pull-9, 1280 | .col-no-gutter.pull-9, 1281 | .col.pull-9 { 1282 | margin-right: -75%; 1283 | right: 75%; 1284 | } 1285 | 1286 | .col-inverse-no-gutter.pull-10, 1287 | .col-inverse.pull-10, 1288 | .col-no-gutter.pull-10, 1289 | .col.pull-10 { 1290 | margin-right: -83.33333%; 1291 | right: 83.33333%; 1292 | } 1293 | 1294 | .col-inverse-no-gutter.pull-11, 1295 | .col-inverse.pull-11, 1296 | .col-no-gutter.pull-11, 1297 | .col.pull-11 { 1298 | margin-right: -91.66667%; 1299 | right: 91.66667%; 1300 | } 1301 | 1302 | 1303 | .col-inverse-no-gutter.centered, 1304 | .col-inverse.centered, 1305 | .col-no-gutter.centered, 1306 | .col.centered { 1307 | float: none; 1308 | margin: 0 auto; 1309 | } 1310 | } 1311 | 1312 | .gutter { .od-gutter(); } 1313 | .gutter-horizontal { .od-gutter-horizontal(); } 1314 | .gutter-vertical { .od-gutter-vertical(); } 1315 | 1316 | .gutter-top { .od-gutter-top(); } 1317 | .gutter-right { .od-gutter-right(); } 1318 | .gutter-bottom { .od-gutter-bottom(); } 1319 | .gutter-left { .od-gutter-left(); } 1320 | 1321 | .internal-gutter { .od-internal-gutter(); } 1322 | .internal-gutter-horizontal { .od-internal-gutter-horizontal(); } 1323 | .internal-gutter-vertical { .od-internal-gutter-vertical(); } 1324 | 1325 | .internal-gutter-top { .od-internal-gutter-top(); } 1326 | .internal-gutter-right { .od-internal-gutter-right(); } 1327 | .internal-gutter-bottom { .od-internal-gutter-bottom(); } 1328 | .internal-gutter-left { .od-internal-gutter-left(); } 1329 | 1330 | 1331 | // media styling 1332 | 1333 | figure.col { padding-left: 0; } 1334 | figure.col-inverse { padding-right: 0; } 1335 | 1336 | 1337 | /* 1338 | Font Stacks 1339 | 1340 | Sans font stacks from http://www.awayback.com/revised-font-stack/ 1341 | Other types of font stacks from http://cssfontstack.com/ 1342 | */ 1343 | 1344 | .monospace() { font-family: "Andale Mono", "Courier New", Courier, monospace; } 1345 | .serif() { font-family: "Hoefler Text", Garamond, Baskerville, "Baskerville Old Face", "Times New Roman", serif; } 1346 | .sans() { font-family: "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Verdana, Tahoma, sans-serif; } 1347 | .elegant() { font-family: Baskerville, Garamond, Palatino, "Palatino Linotype", "Hoefler Text", "Times New Roman", serif; } 1348 | .readable() { font-family: "Lucida Grande", geneva, helvetica, sans-serif; } 1349 | 1350 | // Serif 1351 | .baskerville() { font-family: Baskerville, "Baskerville old face", "Hoefler Text", Garamond, "Times New Roman", serif; } 1352 | .caslon() { font-family: "Big Caslon", "Book Antiqua", "Palatino Linotype", Georgia, serif; } 1353 | .bodoni() { font-family: "Bodoni MT", Didot, "Didot LT STD", "Hoefler Text", Garamond, "Times New Roman", serif; } 1354 | .book-antiqua() { font-family: "Book Antiqua", Palatino, "Palatino Linotype", "Palatino LT STD", Georgia, serif; } 1355 | .cambria() { font-family: Cambria, Georgia, serif; } 1356 | .constantia() { font-family: Constantia, Palatino, "Palatino Linotype", "Palatino LT STD", Georgia, serif; } 1357 | .didot() { font-family: Didot, "Didot LT STD", "Hoefler Text", Garamond, "Times New Roman", serif; } 1358 | .garamond() { font-family: Garamond, Baskerville, "Baskerville Old Face", "Hoefler Text", "Times New Roman", serif; } 1359 | .goudy() { font-family: "Goudy Old Style", Garamond, "Big Caslon", "Times New Roman", serif; } 1360 | .hoefler() { font-family: "Hoefler Text", "Baskerville old face", Garamond, "Times New Roman", serif; } 1361 | .lucidia-serif() { font-family: "Lucida Bright", Georgia, serif; } 1362 | .palantino() { font-family: Palatino, "Palatino Linotype", "Palatino LT STD", "Book Antiqua", Georgia, serif; } 1363 | 1364 | // Sans 1365 | .calibri() { font-family: Calibri, Candara, Segoe, "Segoe UI", Optima, Arial, sans-serif; } 1366 | .franklin() { font-family: "Franklin Gothic Medium", Arial, sans-serif; } 1367 | .futura() { font-family: Futura, "Trebuchet MS", Arial, sans-serif; } 1368 | .geneva() { font-family: Geneva, Tahoma, Verdana, sans-serif; } 1369 | .gill() { font-family: "Gill Sans", "Gill Sans MT", Calibri, sans-serif; } 1370 | .helvetica() { font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; } 1371 | .lucidia-sans() { font-family: "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Geneva, Verdana, sans-serif; } 1372 | .optima() { font-family: Optima, Segoe, "Segoe UI", Candara, Calibri, Arial, sans-serif; } 1373 | .segoe() { font-family: Segoe, "Segoe UI", "Helvetica Neue", Arial, sans-serif; } 1374 | .tahoma() { font-family: Tahoma, Geneva, Verdana, sans-serif; } 1375 | .trebuchet() { font-family: "Trebuchet MS", "Lucida Grande", "Lucida Sans Unicode", "Lucida Sans", Tahoma, sans-serif; } 1376 | .verdana() { font-family: Verdana, Geneva, sans-serif; } 1377 | 1378 | // Other 1379 | .avant() { font-family: "Avant Garde", Avantgarde, "Century Gothic", CenturyGothic, "AppleGothic", sans-serif; } 1380 | .brush() { font-family: "Brush Script MT", cursive; } 1381 | .copperplate() { font-family: Copperplate, "Copperplate Gothic Light", fantasy; } 1382 | .impact() { font-family: Impact, Haettenschweiler, "Franklin Gothic Bold", Charcoal, "Helvetica Inserat", "Bitstream Vera Sans Bold", "Arial Black", sans serif; } 1383 | .papyrus() { font-family: Papyrus, fantasy; } 1384 | .rockwell() { font-family: Rockwell, "Courier Bold", Courier, Georgia, Times, "Times New Roman", serif; } 1385 | 1386 | 1387 | /* 1388 | Boxes 1389 | */ 1390 | 1391 | .od-modal-settings (@background, @border-width: 1px, @border-ype: solid, @border-colour, @color) { 1392 | background: @background; 1393 | border: @border-width @border-ype @border-colour; 1394 | color: @color; 1395 | } 1396 | 1397 | .modal, 1398 | .download-modal, 1399 | .info-modal, 1400 | .note-modal, 1401 | .warning-modal { 1402 | .border-radius(@radius); 1403 | .box-shadow(0 5px 15px rgba(0, 0, 0, 0.5)); 1404 | background-clip: padding-box; 1405 | background-color: #fff; 1406 | border: 1px solid rgba(0, 0, 0, 0); 1407 | outline: 0; 1408 | position: relative; 1409 | 1410 | .modal-header, 1411 | .modal-body, 1412 | .modal-footer { padding: @font-size * 1px; } 1413 | 1414 | .modal-header { 1415 | border-bottom: 1px solid rgba(0, 0, 0, 0); 1416 | min-height: @font-size * 1px; 1417 | } 1418 | 1419 | .modal-body { position: relative; } 1420 | 1421 | .modal-footer { 1422 | border-top: 1px solid rgba(0, 0, 0, 0); 1423 | text-align: right; 1424 | font-style: italic; 1425 | } 1426 | } 1427 | 1428 | .box { 1429 | .border-radius(@radius); 1430 | background-clip: padding-box; 1431 | background-color: #fff; 1432 | border: 1px solid rgba(0, 0, 0, 0); 1433 | outline: 0; 1434 | padding: 5px; 1435 | position: relative; 1436 | } 1437 | 1438 | /* 1439 | Colours 1440 | */ 1441 | 1442 | .palette-success { background-color: #2ecc71; } 1443 | .palette-warning { background-color: #f1c40f; } 1444 | .palette-danger { background-color: #e74c3c; } 1445 | .palette-submit { background-color: #2196f3; } 1446 | 1447 | .palette-success-dark { 1448 | background-color: #27ad60; 1449 | color: #fff; 1450 | } 1451 | .palette-info { 1452 | background-color: #dbc034; 1453 | color: #fff; 1454 | } 1455 | .palette-info-dark { 1456 | background-color: #a88f0b; 1457 | color: #fff; 1458 | } 1459 | .palette-warning-dark { 1460 | background-color: #cea70c; 1461 | color: #fff; 1462 | } 1463 | .palette-danger-dark { 1464 | background-color: #dc2d1b; 1465 | color: #fff; 1466 | } 1467 | .palette-submit-dark { 1468 | background-color: #285e8e; 1469 | color: #fff; 1470 | } 1471 | 1472 | // import resets and base units 1473 | @import "style.less"; 1474 | -------------------------------------------------------------------------------- /app/less/size_0.less: -------------------------------------------------------------------------------- 1 | body { 2 | background: #f9f9f9; 3 | overflow-x: hidden; 4 | } 5 | 6 | #bg { 7 | background: #333; 8 | position: absolute; 9 | top: 0; left: 0; 10 | z-index: -1; 11 | 12 | &:after { 13 | border-left: 2000px solid rgba(0, 0, 0, 0); 14 | border-right: 0 solid rgba(0, 0, 0, 0); 15 | border-bottom: 200px solid #F9F9F9; 16 | clear: both; 17 | content: ''; 18 | height: 0; 19 | left: 0; 20 | position: absolute; 21 | bottom: 0; 22 | width: 0; 23 | } 24 | } 25 | 26 | #hohContainer { .od-row(); } 27 | 28 | #heading { 29 | .od-row(); 30 | 31 | #main_header { margin: 0 auto; } 32 | 33 | #logo_container, 34 | #powered_by { 35 | .od-col(); 36 | .od-width-6(); 37 | } 38 | 39 | #logo_container p { 40 | text-align: left; 41 | 42 | a { border-bottom: none; } 43 | } 44 | 45 | #powered_by ul { 46 | text-align: right; 47 | 48 | #reactjs a { 49 | color: #00d8ff; 50 | font-family: "Helvetica Neue", Helvetica, Roboto, Arial, sans-serif; 51 | 52 | img { 53 | height: 25px; 54 | position: relative; 55 | top: 7px; 56 | width: 25px; 57 | } 58 | } 59 | 60 | #wikipedia a { 61 | color: #fff; 62 | font-family: 'Crimson+Text'; 63 | letter-spacing: 0.1em; 64 | text-transform: uppercase; 65 | 66 | small { 67 | font-size: 0.8em 68 | } 69 | } 70 | } 71 | 72 | h1 { 73 | color: #fff; 74 | clear: both; 75 | margin-top: 100px; 76 | margin-bottom: 0; 77 | text-align: center; 78 | 79 | #top_title { 80 | font-size: 32px; 81 | font-family: 'Open Sans'; 82 | font-weight: 300; 83 | letter-spacing: .25em; 84 | text-transform: uppercase; 85 | } 86 | 87 | #lower_title { 88 | font-size: 28px; 89 | font-family: 'Amiri'; 90 | font-style: italic; 91 | position: relative; 92 | top: -1em; 93 | } 94 | } 95 | 96 | p { 97 | color: #fff; 98 | font-family: 'Amiri'; 99 | font-style: italic; 100 | margin-bottom: 1em; 101 | text-align: center; 102 | 103 | &:last-child { margin-bottom: 0 } 104 | 105 | a { 106 | border-bottom: 1px dotted #fff; 107 | color: #fff; 108 | } 109 | } 110 | } 111 | 112 | #socialButtons { 113 | .transition(all 0.5s); 114 | color: #00b5ff; 115 | position: absolute; 116 | right: 0.5em; 117 | 118 | &:hover, 119 | &.active { color: #535f5f; } 120 | 121 | .socialBox { 122 | height: 36px; 123 | position: relative; 124 | width: 41px; 125 | } 126 | 127 | #socialIcons { 128 | .od-row(); 129 | bottom: 37px; 130 | position: absolute; 131 | right: 5px; 132 | } 133 | 134 | #circleBg { 135 | .border-radius(5px); 136 | background-color: #fff; 137 | display: block; 138 | position: relative; 139 | right: 0.9em; 140 | 141 | .fa-share-alt-square { 142 | left: -1px; 143 | top: -4px; 144 | } 145 | } 146 | 147 | .fa { 148 | cursor: pointer; 149 | position: relative; 150 | } 151 | 152 | .fa-share-alt-square { font-size: 2em; } 153 | 154 | .count, 155 | .share { 156 | text-align: center; 157 | text-decoration: none; 158 | } 159 | 160 | .count { 161 | color: #fff; 162 | font-size: 14px; 163 | left: -50px; 164 | position: absolute; 165 | text-align: right; 166 | top: 6px; 167 | width: 42px; 168 | } 169 | 170 | .share { 171 | .border-radius(50%); 172 | color: #fff; 173 | display: inline-block; 174 | height: 30px; 175 | line-height: 28px; 176 | position: absolute; 177 | width: 31px; 178 | 179 | &:hover { 180 | color: #fff; 181 | text-decoration: none; 182 | } 183 | } 184 | 185 | .fa img { 186 | position: relative; 187 | top: 2px; 188 | width: 16px; 189 | } 190 | 191 | .facebook, 192 | .twitter, 193 | .pinterest, 194 | .stumbleupon, 195 | .bufferapp { 196 | height:100%; 197 | text-align:center; 198 | width:100%; 199 | .border-radius(4px); 200 | } 201 | 202 | #twitter .share { 203 | text-shadow: 1px 0 0 #0077be; 204 | .background-image(linear-gradient(top, #26c3eb 0%, #0080d6 100%)); 205 | } 206 | 207 | #facebook .share { 208 | background:#3b5998; 209 | text-shadow:1px 0 0 #26427e; 210 | .background-image(linear-gradient(top, #3B5998 0%, #133783 100%)); 211 | } 212 | 213 | #googleplus .share { 214 | background:#6d6d6d; 215 | text-shadow:1px 0 0 #222; 216 | .background-image(linear-gradient(top, #6d6d6d 0%, #434343 100%)); 217 | } 218 | 219 | #linkedin .share { 220 | background:#6d6d6d; 221 | text-shadow:1px 0 0 #26427e; 222 | .background-image(linear-gradient(top, #74bbdb 0%, #007fb2 100%)); 223 | } 224 | 225 | #stumbleupon .share { 226 | background: #eb4924; 227 | text-shadow: 1px 0 0 #26427e; 228 | .background-image(linear-gradient(top, #eb4924 0%, #b12403 100%)); 229 | } 230 | } 231 | 232 | .socialTransition-appear { 233 | .transition(all 2s); 234 | max-height: 1px; 235 | opacity: 0.01; 236 | 237 | &.socialTransition-appear-active { 238 | max-height: 500px; 239 | opacity: 1; 240 | } 241 | } 242 | 243 | #mapAndControls, 244 | #controls, 245 | #items, 246 | #itemDetail { 247 | .od-row(); 248 | width: 1200px; 249 | } 250 | 251 | #mapAndControls { position: relative; } 252 | 253 | #gmap { .box-shadow(0 10px 55px 0 rgba(0, 0, 0, 0.3)); } 254 | 255 | #mapCanvas { display: block; } 256 | 257 | #controls { 258 | background: rgba(0, 0, 0, 0.5); 259 | bottom: 0; 260 | padding: 11px 11px 18px; 261 | position: absolute; 262 | 263 | p { 264 | .border-radius(3px); 265 | background: #fff; 266 | cursor: pointer; 267 | margin-bottom: 0; 268 | padding: 0.2em 0.5em; 269 | text-transform: capitalize; 270 | } 271 | 272 | label { 273 | color: #fff; 274 | margin-bottom: 0.2em; 275 | } 276 | 277 | .panel { 278 | .od-col(); 279 | .od-width-4(); 280 | .od-internal-gutter-horizontal(); 281 | } 282 | 283 | .yearPanel { 284 | .border-radius(3px); 285 | background-color: #FFF; 286 | display: inline-block; 287 | font-size: 15px; 288 | padding: 3px 9px; 289 | } 290 | 291 | #startDate, 292 | #endDate { 293 | cursor: pointer; 294 | display: inline-block; 295 | margin: 0 1em 0 22px; 296 | width: 280px; 297 | } 298 | } 299 | 300 | .fa_button { 301 | .box-sizing(content-box); 302 | .border-radius(50%); 303 | .box-shadow(1px 1px 1px #CCC); 304 | .transition(background-color 0.5s); 305 | background-color: #00b5ff; 306 | border: none; 307 | color: #fff; 308 | cursor: pointer; 309 | display: block; 310 | height: 30px; 311 | outline: none; 312 | text-align: center; 313 | text-decoration: none; 314 | width: 30px; 315 | zoom: 1; 316 | 317 | &#backButton, 318 | &#forwardButton, 319 | &#galleryBack, 320 | &#galleryForward { 321 | cursor: pointer; 322 | font-size: 18px; 323 | position: absolute; 324 | 325 | .fa { 326 | position: relative; 327 | top: 1px; 328 | } 329 | } 330 | 331 | &#backButton, 332 | &#forwardButton { 333 | bottom: 35px; 334 | } 335 | 336 | &#backButton { left: -50px; } 337 | &#forwardButton { right: -50px; } 338 | 339 | &#backButton .fa, 340 | &#galleryBack .fa { 341 | left: -1px; 342 | } 343 | 344 | &#forwardButton .fa, 345 | &#galleryForward .fa { 346 | left: 1px; 347 | } 348 | 349 | &:hover { 350 | background-color: #535f5f; 351 | color: #fff; 352 | } 353 | } 354 | 355 | #items ul { 356 | list-style-type: none; 357 | margin-left: 0; 358 | 359 | .yearTitle { 360 | border-bottom: 1px solid #e8e8e8; 361 | color: #727272; 362 | margin: 60px 0 30px; 363 | padding-bottom: 30px; 364 | } 365 | 366 | .itemPanel { 367 | .box-shadow(0 2px 4px rgba(0, 0, 0, 0.24)); 368 | background: #fff; 369 | cursor: pointer; 370 | margin-bottom: 30px; 371 | 372 | &.highlightMap { background-color: #d5edf5; } 373 | &.highlightClick { background-color: #f5f4d5; } 374 | 375 | p { 376 | margin-bottom: 0; 377 | padding: 1em; 378 | 379 | a { color: #727272; } 380 | } 381 | 382 | .readmore { 383 | border-top: 1px solid #eee; 384 | color: #00b5ff; 385 | display: block; 386 | font-size: 0.8em; 387 | padding: 1em; 388 | text-align: right; 389 | width: 100%; 390 | } 391 | } 392 | } 393 | 394 | .itemDetailTransition-appear { 395 | opacity: 0.01; 396 | .transition(opacity .5s ease-in); 397 | 398 | &.itemDetailTransition-appear-active { opacity: 1; } 399 | } 400 | 401 | #itemDetail { 402 | .box-shadow(0 4px 10px rgba(0, 0, 0, 0.23)); 403 | background: #fff; 404 | overflow-y: auto; 405 | padding: 1em; 406 | position: fixed; 407 | right: 15px; 408 | top: 10px; 409 | 410 | h3, 411 | h4 { 412 | border-bottom: 1px solid #e8e8e8; 413 | font-family: 'Crimson+Text'; 414 | line-height: 1.5; 415 | padding-bottom: 1em; 416 | } 417 | 418 | h3 { 419 | font-size: 24px; 420 | margin: 0.5em 0 1.5em; 421 | } 422 | 423 | h4 { 424 | font-size: 20px; 425 | margin: 2em 0 1em; 426 | } 427 | 428 | p { 429 | font-size: 15px; 430 | 431 | &#mainLink { 432 | font-weight: bold; 433 | 434 | a { font-weight: normal; } 435 | } 436 | } 437 | 438 | ul { 439 | list-style-type: none; 440 | margin-left: 0; 441 | 442 | li { 443 | font-size: 15px; 444 | } 445 | } 446 | 447 | #galleryTitle { 448 | position: relative; 449 | 450 | #galleryBack, 451 | #galleryForward { top: 3px; } 452 | 453 | #galleryBack { left: 89px } 454 | #galleryForward { left: 130px } 455 | } 456 | 457 | #gallery img { 458 | .od-col(); 459 | .od-width-4(); 460 | .od-internal-gutter-horizontal(); 461 | } 462 | } 463 | 464 | #hideItemDetail { 465 | .border-radius(50%); 466 | .box-shadow(1px 1px 1px #CCC); 467 | .transition(right 0.8s); 468 | background: #00b5ff; 469 | color: #fff; 470 | cursor: pointer; 471 | height: 35px; 472 | line-height: 2; 473 | position: fixed; 474 | right: -100%; 475 | text-align: center; 476 | top: 4px; 477 | width: 35px; 478 | z-index: 200; 479 | } 480 | 481 | // 482 | // pagination 483 | // 484 | 485 | .paginator { 486 | .od-inline(); 487 | text-align: center; 488 | 489 | li { 490 | cursor: pointer; 491 | margin: 0; 492 | padding: 2px 10px; 493 | 494 | a { color: #ccc; } 495 | 496 | &.selected { 497 | padding: 2px 10px; 498 | 499 | a { color: #00b5ff; } 500 | } 501 | 502 | &.no_more { 503 | .border-radius(2px); 504 | background-color: #535f5f; 505 | padding: 2px 10px; 506 | 507 | a { color: #fff; } 508 | } 509 | 510 | &:hover { 511 | padding: 2px 10px; 512 | 513 | a { color: #535f5f; } 514 | } 515 | } 516 | } 517 | 518 | #footer { 519 | background: #222; 520 | color: #ccc; 521 | 522 | p { 523 | border-top: 1px solid #fff; 524 | color: #fff; 525 | margin-bottom: 0; 526 | padding: 2em; 527 | text-align: center; 528 | } 529 | 530 | a { 531 | border-bottom: 1px dotted #ccc; 532 | color: #ccc; 533 | } 534 | } -------------------------------------------------------------------------------- /app/less/size_1024.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwatsonwailes/history-of-humanity/d8867d65a248c4711059dba62b56fab14cf79a13/app/less/size_1024.less -------------------------------------------------------------------------------- /app/less/size_1024_1376.less: -------------------------------------------------------------------------------- 1 | #hohContainer, 2 | #heading { width: 980px; } 3 | 4 | #heading { margin: 75px auto; } 5 | 6 | #circleBg { 7 | height: 30px; 8 | width: 30px; 9 | 10 | .fa-share-alt-square { font-size: 2.3em; } 11 | } 12 | 13 | #mapAndControls, 14 | #controls, 15 | #items, 16 | #itemDetail { width: 900px; } 17 | 18 | #mapCanvas { 19 | height: 600px; 20 | width: 900px; 21 | } 22 | 23 | #controls { 24 | #startDate, 25 | #endDate { width: 200px; } 26 | } 27 | 28 | #hideItemDetail { right: 897px; } -------------------------------------------------------------------------------- /app/less/size_1024_up.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwatsonwailes/history-of-humanity/d8867d65a248c4711059dba62b56fab14cf79a13/app/less/size_1024_up.less -------------------------------------------------------------------------------- /app/less/size_1376.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwatsonwailes/history-of-humanity/d8867d65a248c4711059dba62b56fab14cf79a13/app/less/size_1376.less -------------------------------------------------------------------------------- /app/less/size_1376_1840.less: -------------------------------------------------------------------------------- 1 | #hohContainer, 2 | #heading { width: 1280px; } 3 | 4 | #heading { margin: 100px auto; } 5 | 6 | #circleBg { 7 | height: 30px; 8 | width: 30px; 9 | } 10 | 11 | #mapAndControls, 12 | #controls, 13 | #items, 14 | #itemDetail { width: 1200px; } 15 | 16 | #mapCanvas { 17 | height: 600px; 18 | width: 1200px; 19 | } 20 | 21 | #controls { 22 | #startDate, 23 | #endDate { width: 280px; } 24 | } 25 | 26 | #hideItemDetail { 27 | height: 28px; 28 | line-height: 1.6; 29 | right: 1197px; 30 | width: 28px; 31 | } 32 | -------------------------------------------------------------------------------- /app/less/size_1376_up.less: -------------------------------------------------------------------------------- 1 | body { font-size: (@font-size + 2)/@em; } -------------------------------------------------------------------------------- /app/less/size_1840.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwatsonwailes/history-of-humanity/d8867d65a248c4711059dba62b56fab14cf79a13/app/less/size_1840.less -------------------------------------------------------------------------------- /app/less/size_1840_up.less: -------------------------------------------------------------------------------- 1 | body { font-size: (@font-size + 2)/@em; } 2 | 3 | #hohContainer, 4 | #heading { width: 1440px; } 5 | 6 | #heading { margin: 100px auto; } 7 | 8 | #circleBg { 9 | height: 30px; 10 | width: 30px; 11 | } 12 | 13 | #mapAndControls, 14 | #controls, 15 | #items, 16 | #itemDetail { width: 1300px; } 17 | 18 | #mapCanvas { 19 | height: 600px; 20 | width: 1300px; 21 | } 22 | 23 | #controls { 24 | #startDate, 25 | #endDate { width: 320px; } 26 | } 27 | 28 | #hideItemDetail { right: 1297px; } -------------------------------------------------------------------------------- /app/less/size_640.less: -------------------------------------------------------------------------------- 1 | // Sort form baseline alignment on mobile 2 | input[type="submit"], 3 | input[type="reset"], 4 | html input[type="button"], 5 | button, 6 | a.button { line-height: 1.278; } 7 | 8 | #hohContainer, 9 | #heading { width: 300px; } 10 | 11 | #heading { margin: 40px auto 150px } 12 | 13 | #main_header { 14 | margin: 0 auto; 15 | width: 280px; 16 | } 17 | 18 | #circleBg { 19 | height: 30px; 20 | width: 30px; 21 | 22 | .fa-share-alt-square { font-size: 2.7em; } 23 | } 24 | 25 | #mapAndControls, 26 | #controls, 27 | #items, 28 | #itemDetail { width: 285px; } 29 | 30 | #mapCanvas { 31 | height: 250px; 32 | width: 285px; 33 | } 34 | 35 | #controls { 36 | .panel { padding: 0; } 37 | 38 | #startDate, 39 | #endDate { width: 72px; } 40 | } 41 | 42 | #hideItemDetail { 43 | height: 28px; 44 | right: 283px; 45 | width: 28px; 46 | } -------------------------------------------------------------------------------- /app/less/size_640_800.less: -------------------------------------------------------------------------------- 1 | #hohContainer, 2 | #heading { width: 540px; } 3 | 4 | #heading { margin: 40px auto 80px } 5 | 6 | #main_header { 7 | margin: 0 auto; 8 | width: 480px; 9 | } 10 | 11 | #circleBg { 12 | height: 30px; 13 | width: 30px; 14 | 15 | .fa-share-alt-square { font-size: 2.7em; } 16 | } 17 | 18 | #mapAndControls, 19 | #controls, 20 | #items, 21 | #itemDetail { width: 425px; } 22 | 23 | #mapCanvas { 24 | height: 325px; 25 | width: 425px; 26 | } 27 | 28 | #controls { 29 | #startDate, 30 | #endDate { width: 100px; } 31 | } 32 | 33 | #hideItemDetail { right: 423px; } -------------------------------------------------------------------------------- /app/less/size_640_up.less: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwatsonwailes/history-of-humanity/d8867d65a248c4711059dba62b56fab14cf79a13/app/less/size_640_up.less -------------------------------------------------------------------------------- /app/less/size_800.less: -------------------------------------------------------------------------------- 1 | body { font-size: (@font-size - 2)/@em; } 2 | 3 | #heading h1 { 4 | margin-bottom: 1em; 5 | margin-top: 70px; 6 | 7 | #lower_title { 8 | line-height: 0; 9 | top: 0; 10 | } 11 | } -------------------------------------------------------------------------------- /app/less/size_800_1024.less: -------------------------------------------------------------------------------- 1 | #hohContainer, 2 | #heading { width: 740px; } 3 | 4 | #heading { margin: 40px auto 80px } 5 | 6 | #main_header { 7 | margin: 0 auto; 8 | width: 680px; 9 | } 10 | 11 | #circleBg { 12 | height: 30px; 13 | width: 30px; 14 | 15 | .fa-share-alt-square { font-size: 2.3em; } 16 | } 17 | 18 | #mapAndControls, 19 | #controls, 20 | #items, 21 | #itemDetail { width: 625px; } 22 | 23 | #mapCanvas { 24 | height: 455px; 25 | width: 625px; 26 | } 27 | 28 | #controls { 29 | #startDate, 30 | #endDate { width: 130px; } 31 | } 32 | 33 | #hideItemDetail { 34 | height: 28px; 35 | line-height: 1.6; 36 | right: 623px; 37 | width: 28px; 38 | } -------------------------------------------------------------------------------- /app/less/size_800_up.less: -------------------------------------------------------------------------------- 1 | h1:first-child, h2:first-child, h3:first-child { margin-top: 0; } 2 | 3 | -------------------------------------------------------------------------------- /app/less/style.less: -------------------------------------------------------------------------------- 1 | /* 2 | * Other imports (FontAwesome etc, if used) 3 | */ 4 | 5 | @import url(//fonts.googleapis.com/css?family=Amiri:400italic|Open+Sans:300|Crimson+Text); 6 | @import url(//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.4.0/css/font-awesome.min.css); 7 | 8 | 9 | /* 10 | * Theme mixins 11 | */ 12 | 13 | .odm-dropdown(@top: 0, @right: 0, @left: 0, @overflow-x: 'auto', @overflow-y: 'auto', @after-right: 0, @after-left: 0) { 14 | .transition(opacity 400ms cubic-bezier(0.23, 1, 0.32, 1) 0ms); 15 | left: @left; 16 | right: @right; 17 | top: @top; 18 | background: #fff; 19 | list-style-type: none; 20 | margin: 0; 21 | max-height: 0; 22 | opacity: 0; 23 | overflow: hidden; 24 | padding: 0; 25 | position: absolute; 26 | z-index: 100; 27 | 28 | li { 29 | background: #eaeaea; 30 | cursor: pointer; 31 | font-size: 13px; 32 | margin: 0 0 -1px; 33 | z-index: 101; 34 | 35 | &:hover { background-color: #fefeff; } 36 | &:first-child { border-top: none; } 37 | 38 | span, 39 | a { 40 | color: #aaa; 41 | display: block; 42 | padding: 10px 20px; 43 | z-index: 102; 44 | } 45 | } 46 | 47 | &:after, 48 | &:before { 49 | left: @after-left; 50 | right: @after-right; 51 | } 52 | 53 | &.active { 54 | opacity: 1; 55 | max-height: 400px; 56 | overflow-y: scroll; 57 | 58 | .viewport_container { 59 | height: 100%; 60 | overflow: hidden; 61 | position: relative; 62 | width: 100%; 63 | 64 | .viewport { 65 | bottom: 0; 66 | left: 0; 67 | overflow-x: @overflow-x; 68 | overflow-y: @overflow-y; 69 | position: absolute; 70 | right: -18px; 71 | top: 0; 72 | } 73 | } 74 | } 75 | } 76 | 77 | 78 | /* 79 | * Compiled overrides 80 | */ 81 | 82 | 83 | 84 | 85 | /* 86 | * Main classes 87 | */ 88 | 89 | .label { 90 | .border-radius(50%); 91 | box-sizing: border-box; 92 | background: #d12727; 93 | border: 2px solid #fff; 94 | cursor: pointer; 95 | height: 10px; 96 | width: 10px; 97 | } 98 | 99 | 100 | .slider { 101 | background-color: #b9b9b9; 102 | border-radius: 3px; 103 | display: inline-block; 104 | position: relative; 105 | 106 | .value { 107 | background-color: #00b5ff; 108 | border-radius: 3px; 109 | position: absolute; 110 | } 111 | 112 | .handle { 113 | height: 8px; 114 | position: absolute; 115 | width: 8px; 116 | 117 | &:after { 118 | content: ''; 119 | display: block; 120 | position: relative; 121 | } 122 | } 123 | } 124 | 125 | .slider_x .handle:after, 126 | .slider_y .handle:after { 127 | .border-radius(50%); 128 | background-color: #00b5ff; 129 | border: 1px solid #00b5ff; 130 | height: 12px; 131 | width: 12px; 132 | } 133 | 134 | .slider_x { 135 | height: 3px; 136 | 137 | .handle { 138 | top: 0; 139 | height: 100%; 140 | 141 | &:after { 142 | top: -5px; 143 | left: -6px; 144 | } 145 | } 146 | } 147 | 148 | .slider_y { 149 | width: 3px; 150 | 151 | .handle { 152 | left: 0; 153 | width: 100%; 154 | 155 | &:after { 156 | top: -6px; 157 | left: -2px; 158 | } 159 | } 160 | } 161 | 162 | .slider_xy { 163 | background-color: #00b5ff; 164 | border-radius: 0; 165 | position: relative; 166 | height: 100%; 167 | width: 100%; 168 | 169 | .handle { 170 | position: absolute; 171 | 172 | &:after { 173 | position: relative; 174 | display: block; 175 | top: -4px; 176 | left: -4px; 177 | width: 8px; 178 | height: 8px; 179 | background-color: rgba(0, 0, 0, 0); 180 | border: 2px solid #fff; 181 | border-radius: 50%; 182 | content: ''; 183 | } 184 | } 185 | } 186 | 187 | .dropdown { .odm-dropdown() } 188 | 189 | /* 190 | * Size-agnostic layout rules 191 | */ 192 | 193 | @import "size_0.less"; 194 | 195 | 196 | /* 197 | Small 198 | */ 199 | 200 | @media screen and (max-width: 639px) { @import "size_640.less"; } 201 | @media screen and (min-width: 640px) { @import "size_640_up.less"; } 202 | 203 | @media screen and (max-width: 799px) { @import "size_800.less"; } 204 | @media screen and (min-width: 800px) { @import "size_800_up.less"; } 205 | 206 | @media screen and (min-width: 640px) and (max-width: 799px) { @import "size_640_800.less"; } 207 | 208 | /* 209 | Mid 210 | */ 211 | 212 | @media screen and (max-width: 1023px) { @import "size_1024.less"; } 213 | @media screen and (min-width: 1024px) { @import "size_1024_up.less"; } 214 | 215 | @media screen and (max-width: 1375px) { @import "size_1376.less"; } 216 | @media screen and (min-width: 1376px) { @import "size_1376_up.less"; } 217 | 218 | @media screen and (min-width: 800px) and (max-width: 1023px) { @import "size_800_1024.less"; } 219 | @media screen and (min-width: 1024px) and (max-width: 1375px) { @import "size_1024_1376.less"; } 220 | 221 | /* 222 | Large 223 | */ 224 | 225 | @media screen and (min-width: 1376px) and (max-width: 1839px) { @import "size_1376_1840.less"; } 226 | 227 | @media screen and (max-width: 1839px) { @import "size_1840.less"; } 228 | @media screen and (min-width: 1840px) { @import "size_1840_up.less"; } 229 | -------------------------------------------------------------------------------- /app/main.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import axios from 'axios'; 4 | import TimelineData from './data/timeline.json'; 5 | import HoH from './js/dispatcher.js'; 6 | 7 | window.app = (function() { 8 | var requiredFeatures = { 9 | "JSON decoding": window.JSON, 10 | "the selectors API": document.querySelector, 11 | "DOM level 2 events": window.addEventListener, 12 | "the HTML5 history API": window.history.pushState 13 | }; 14 | 15 | for (var i = requiredFeatures.length - 1; i >= 0; i--) { 16 | if (!requiredFeatures[i]) 17 | return alert("Sorry, but your browser does not support " + feature + " so this app won't work properly."); 18 | }; 19 | 20 | if (String(window.location.pathname).match(/\/history-of-humanity\/\d+\/\d+\/.+/i) !== null) { 21 | var parts = window.location.pathname.replace('/history-of-humanity/', '').split('/'); 22 | var params = { pointer: 0, year: parts[0], position: parts[1], name: parts[2] }; 23 | 24 | var initData = { 25 | itemDetail: TimelineData[params.year][params.position], 26 | wikiData: false, 27 | wikiImages: [] 28 | }; 29 | 30 | var wikiApiLink = 'https://en.wikipedia.org/w/api.php?format=json&action=query&prop=extracts|images&exintro=&explaintext=&titles=' + params.name; 31 | 32 | axios.get('https://wail.es/history/api.php?url=' + encodeURIComponent(wikiApiLink.replace(/&/g, "&"))).then(function (output) { 33 | if (isset(output.data.query.pages)) { 34 | var pageId = Object.keys(output.data.query.pages); 35 | initData.wikiData = output.data.query.pages[pageId]; 36 | 37 | // React.renderToString takes your component and generates rendered markup. SEO friendliness all the way 38 | return ReactDOM.render(React.createElement(HoH, { timeline: TimelineData, initparams: params, initwikidata: initData }), document.getElementById('hoh')); 39 | } 40 | }) 41 | .catch(function (e) { 42 | console.log('error in xhr'); 43 | console.log(e); 44 | }); 45 | } 46 | else if (String(window.location.pathname).match(/\/history-of-humanity\/p\/\d+/i) !== null) { 47 | var parts = window.location.pathname.replace('/history-of-humanity/', '').split('/'); 48 | 49 | if (parseInt(parts[1]) > 0) 50 | return ReactDOM.render(React.createElement(HoH, { timeline: TimelineData, initparams: { pointer: parseInt(parts[1]) - 1, year: false, position: false, name: false } }), document.getElementById('hoh')); 51 | else 52 | window.location.replace("https://wail.es/history-of-humanity/"); 53 | } 54 | else { 55 | return ReactDOM.render(React.createElement(HoH, { timeline: TimelineData, initparams: { pointer: 0, year: false, position: false, name: false } }), document.getElementById('hoh')); 56 | } 57 | })(); -------------------------------------------------------------------------------- /app/routes/core-routes.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOMServer from 'react-dom/server'; 3 | import TimelineData from '../data/timeline.json'; 4 | import axios from 'axios'; 5 | import HoH from '../js/dispatcher.js'; 6 | 7 | // React.renderToString takes your component and generates rendered markup. SEO friendliness all the way 8 | module.exports = function(app) { 9 | app.get('/', function(req, res) { 10 | res.render('index.ejs', { 11 | reactTitle: '', 12 | reactPath: '', 13 | reactOutput: ReactDOMServer.renderToString(React.createElement(HoH, { 14 | timeline: TimelineData, 15 | initparams: { pointer: 0 } 16 | })) 17 | }); 18 | }); 19 | 20 | app.get('/p/:n', function(req, res) { 21 | if (req.params.n > 0) { 22 | res.render('index.ejs', { 23 | reactTitle: 'Page ' + req.params.n + ' | ', 24 | reactPath: 'p/' + req.params.n, 25 | reactOutput: ReactDOMServer.renderToString(React.createElement(HoH, { 26 | timeline: TimelineData, 27 | initparams: { pointer: req.params.n - 1 } 28 | })) 29 | }); 30 | } 31 | else 32 | res.redirect(301, 'https://wail.es/history-of-humanity/') 33 | }); 34 | 35 | app.get('/:year/:position/:name', function(req, res) { 36 | var initData = { 37 | itemDetail: TimelineData[req.params.year][req.params.position], 38 | wikiData: false, 39 | wikiImages: [] 40 | }; 41 | 42 | var wikiApiLink = 'https://en.wikipedia.org/w/api.php?format=json&action=query&prop=extracts|images&exintro=&explaintext=&titles=' + req.params.name; 43 | 44 | axios.get('https://wail.es/history/api.php?url=' + encodeURIComponent(wikiApiLink.replace(/&/g, "&"))).then(function (output) { 45 | if (typeof output.data.query.pages !== 'undefined') { 46 | var pageId = Object.keys(output.data.query.pages); 47 | initData.wikiData = output.data.query.pages[pageId]; 48 | 49 | res.render('index.ejs', { 50 | reactTitle: initData.itemDetail.text + ' | ', 51 | reactPath: req.params.year + '/' + req.params.position + '/' + req.params.name, 52 | reactOutput: ReactDOMServer.renderToString(React.createElement(HoH, { 53 | timeline: TimelineData, 54 | initparams: req.params, 55 | initwikidata: initData 56 | })) 57 | }); 58 | } 59 | }) 60 | .catch(function (e) { 61 | console.log('error in xhr'); 62 | console.log(e); 63 | }) 64 | }) 65 | } -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "history-of-humanity", 3 | "version": "0.1.0", 4 | "description": "A wikipedia powered React.JS demo application", 5 | "main": "server.js", 6 | "scripts": { 7 | "build-dev": "browserify app/main.js -o public/bundle.dev.js -t [ babelify --presets [ es2015 react stage-1 ] ]", 8 | "build-min": "browserify app/main.js -g uglifyify -o public/bundle.min.js -t [ babelify --presets [ es2015 react stage-1 ] ]", 9 | "build-server": "babel server.js -d serer.min.js --presets babel-preset-es2015 babel-preset-react", 10 | "start": "node server.js" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "https://github.com/pwatsonwailes/history-of-humanity.git" 15 | }, 16 | "author": "petewailes", 17 | "license": "BSD-3-Clause", 18 | "bugs": { 19 | "url": "https://github.com/pwatsonwailes/history-of-humanity/issues" 20 | }, 21 | "homepage": "https://github.com/pwatsonwailes/history-of-humanity", 22 | "devDependencies": { 23 | "browserify": "^12.x.x", 24 | "uglifyify": "3.0.x" 25 | }, 26 | "dependencies": { 27 | "axios": "^0.5.4", 28 | "babel": "^6.x.x", 29 | "babel-core": "^6.1.2", 30 | "babel-preset-es2015": "^6.x.x", 31 | "babel-preset-react": "^6.x.x", 32 | "babel-preset-stage-1": "^6.x.x", 33 | "babelify": "^7.2.0", 34 | "body-parser": "^1.13.3", 35 | "ejs": "^2.3.3", 36 | "express": "^4.13.3", 37 | "react": "^0.14.2", 38 | "react-addons-css-transition-group": "^0.14.2", 39 | "react-dom": "^0.14.2" 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /public/compiled.css: -------------------------------------------------------------------------------- 1 | @import url(//fonts.googleapis.com/css?family=Amiri:400italic|Open+Sans:300|Crimson+Text);@import url(//cdnjs.cloudflare.com/ajax/libs/font-awesome/4.4.0/css/font-awesome.min.css);article,aside,details,dialog,figcaption,figure,footer,header,hgroup,menu,nav,section{display:block}a,a img,blink,img,ins,mark{text-decoration:none}a img,img{-ms-interpolation-mode:bicubic}#heading h1 #lower_title,#heading p{font-family:Amiri}b,caption,dt,form label,legend,strong,th,thead{font-weight:700}#controls:after,#heading h1,#heading:after,#hohContainer:after,#itemDetail:after,#items:after,#mapAndControls:after,#socialButtons #socialIcons:after,.clearfix:after,.inline-scroll:after,.inline:after,.od-clear,.pagination:after,.paginator:after,.row:after{clear:both}a,abbr,acronym,address,article,aside,audio,b,big,blockquote,body,caption,cite,code,dd,del,dfn,dialog,div,dl,dt,em,fieldset,figure,footer,form,h1,h2,h3,h4,h5,h6,header,hgroup,html,i,iframe,img,ins,kbd,label,legend,li,mark,menu,nav,object,ol,p,pre,q,samp,section,small,span,strong,sub,sup,table,tbody,td,tfoot,th,thead,time,tr,tt,ul,var,video{background:0 0;border:0;font-family:inherit;font-size:100%;font-weight:inherit;font-style:inherit;margin:0;outline:0;padding:0;text-align:left;vertical-align:baseline;white-space:normal}#heading h1 #lower_title,#heading p,cite,dfn,em,i{font-style:italic}*{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}applet,basefont,dir,font,isindex,menu,s,strike,u{border:0;color:inherit;font-family:inherit;font-size:100%;font-weight:400;font-style:normal;margin:0;outline:0;padding:0;text-decoration:inherit;text-align:left;vertical-align:baseline;white-space:normal}dir,menu{list-style:none}nobr{white-space:normal}marquee{overflow:visible}.inline,.inline-scroll{overflow:hidden;list-style-type:none}html{font-family:sans-serif;height:100%;-webkit-text-size-adjust:100%;-ms-text-size-adjust:100%}body{color:#313131;font-size:100%;line-height:1.5;min-height:100%;-webkit-tap-highlight-color:rgba(255,0,0,.62)}mark{padding:0 .25em}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}.test{background:#edd}h1{font-size:3.75em;line-height:1.2em;margin-bottom:.4em}h2{font-size:3em;line-height:1em;margin-bottom:.5em}h3{font-size:2.25em;line-height:1.3333333333333333333333333333333em;margin-bottom:.6667em}h4{font-size:1.5em;line-height:1em;margin-bottom:1em}h5{font-size:1.3125em;line-height:1.1428571428571428571428571428571em;margin-bottom:1.14285714em}h6{font-size:1.125em;line-height:1.3333333333333333333333333333333em;margin-bottom:1.33333333em}address,blockquote,code,dialog,dl,label,ol,p,pre,td,th,ul{display:block;font-size:1em;line-height:1.6em;margin-bottom:1.6em}p.small,small{font-size:80%;line-height:1.2;margin:0}dialog,dl,dl dd,ol,ul{margin-left:1.6em}a{color:#0049cc}a:hover{color:#0bcdd9}a:focus{outline:dotted thin}a:active,a:hover{outline:0}a img,img{border:0}hr{margin:0 0 1.375em;padding:0}address,var{font-style:normal}p code,p pre,p var{line-height:1.2em}pre{white-space:pre;white-space:pre-wrap;word-wrap:break-word}dialog,dl,ol,ul{list-style:none inside;padding:0}ul{list-style:disc}ol{list-style:decimal}.inline li,.inline-scroll li,.pagination li,.paginator li{list-style:none}li{font-size:1em;margin-bottom:1em}li label,li:last-child{margin-bottom:0}li ol,li ul{margin:1em 1.6em 0}li ul{list-style-type:circle}li ol{list-style-type:lower-alpha}.inline,nav ul li{margin-left:0}.unstyled-list{list-style-type:none;margin-left:0;padding:0}.unstyled-list li{display:block;list-style:none}.inline{padding:0}.inline:after,.inline:before{content:"";display:table}.inline li{display:inline-block;margin:0}.inline li:before{content:"";margin:0 .66667em;position:relative;top:1px}.inline li:first-child:before,.inline.no-margin li:before{margin:0}.inline li form,.inline li input{margin-bottom:0}.inline.breadcrumbs li:before{content:"/"}.inline-scroll li:before,.inline-scroll:after,.inline-scroll:before,.pagination li:before,.pagination:after,.pagination:before,.paginator li:before,.paginator:after,.paginator:before{content:""}.inline.breadcrumbs li.inline:first-child:before{content:"";margin:0}.inline.li-margin li{margin-left:.66667em}.inline.li-margin li:before,.inline.li-margin li:first-child{margin:0}.inline .radius>:first-child,.inline .radius>:first-child>.button,.inline .radius>:first-child>a,.inline .radius>:first-child>button{-webkit-border-bottom-left-radius:5px;-moz-border-radius-bottomleft:5px;border-bottom-left-radius:5px;-webkit-border-top-left-radius:5px;-webkit-background-clip:padding-box;-moz-border-radius-topleft:5px;-moz-background-clip:padding;border-top-left-radius:5px;background-clip:padding-box}.inline .radius>:last-child,.inline .radius>:last-child>.button,.inline .radius>:last-child>a,.inline .radius>:last-child>button{-webkit-border-bottom-right-radius:5px;-moz-border-radius-bottomright:5px;border-bottom-right-radius:5px;-webkit-border-top-right-radius:5px;-webkit-background-clip:padding-box;-moz-border-radius-topright:5px;-moz-background-clip:padding;border-top-right-radius:5px;background-clip:padding-box}.inline-scroll{margin-left:0;padding:0;overflow-x:auto;white-space:nowrap}.dropdown,.pagination,svg:not(:root){overflow:hidden}.inline-scroll:after,.inline-scroll:before{display:table}.inline-scroll li{display:inline-block;margin:0}.inline-scroll li:before{margin:0 .66667em;position:relative;top:1px}.inline-scroll li:first-child:before{margin:0}.inline-scroll li form,.inline-scroll li input{margin-bottom:0}.pagination{list-style-type:none;margin-left:0;padding:0}.pagination:after,.pagination:before{display:table}.pagination li{display:inline-block;margin:0}.pagination li:before{margin:0 .66667em;position:relative;top:1px}.pagination li:first-child:before{margin:0}.pagination li form,.pagination li input{margin-bottom:0}.pagination li{margin-left:.25em}.pagination li:hover a{background:#eee}.pagination li a{-webkit-border-radius:5px;-webkit-background-clip:padding-box;-moz-border-radius:5px;-moz-background-clip:padding;border-radius:5px;background-clip:padding-box;-webkit-transition:background-color .3s ease-out;-moz-transition:background-color .3s ease-out;-o-transition:background-color .3s ease-out;transition:background-color .3s ease-out;display:block;padding:.1em .75rem}.pagination li a.selected{background:#0049cc;color:#fff;cursor:default;font-weight:700}blockquote,cite,q{font-style:italic quotes: none}blockquote:after,blockquote:before,q:after,q:before{content:'';content:none}blockquote{border-left:3px solid #dfdfdf;padding-left:1.6em}blockquote>p{padding:0}abbr,acronym,dfn{font-size:80%;letter-spacing:.1em;line-height:1.2;text-transform:uppercase}abbr[title],acronym[title],dfn[title]{border-bottom:1px dotted #000;cursor:help}ins{color:red}del{text-decoration:line-through}mark{background:#c47529;color:#000}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline}sup{top:-.7em}sub{bottom:-.25em}embed,img,object,video{max-width:100%}img{border:0}img::selection{background:0 0}img::-moz-selection{background:0 0}img.no-max{max-width:none}figure,form label,table{max-width:100%}figure{margin:0 0 1.6em}figure img{height:auto;width:100%}figure figcaption{background:#eee;border-left:5px solid #999;margin-bottom:0;padding:.4em .8em}::-moz-selection,::selection{background:#c47529;color:#fff;text-shadow:none}table{border-collapse:collapse;border-spacing:0;margin-bottom:1.6em;width:100%}table .striped>tbody>tr:nth-child(odd)>td{background:#f0f0f0}tr{display:table-row}tr .success td{background-color:#2ecc71}tr .success-dark td{background-color:#27ad60}tr .info td{background-color:#3498db}tr .info-dark td{background-color:#2383c4}tr .warning td{background-color:#f1c40f}tr .warning-dark td{background-color:#cea70c}tr .danger td{background-color:#e74c3c}tr .danger-dark td{background-color:#dc2d1b}td,th{display:table-cell;padding:8px}th{border-bottom:2px solid #ddd;border-top:0;vertical-align:bottom}td{border-top:1px solid #ddd;vertical-align:top}form{border-bottom:1px solid rgba(0,0,0,.1);margin-bottom:20px;padding:0 0 1pc}form fieldset{border:0;margin:0;min-width:0;padding:0}form fieldset legend{border:0;border-bottom:1px solid #e5e5e5;display:block;font-size:19.2px;padding:0;width:100%}form label{display:inline-block;font-family:inherit;margin-bottom:8px}form label.firm{color:#1abc9c}form label.success{color:#11b452}form label.info{color:#3498db}form label.warning{color:#f1c40f}form label.danger{color:#e74c3c}form label.night{color:#34495e}form input[type=email],form input[type=number],form input[type=password],form input[type=text],form textarea{-webkit-box-shadow:inset 0 -1px 0 #ddd;-moz-box-shadow:inset 0 -1px 0 #ddd;box-shadow:inset 0 -1px 0 #ddd;-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-moz-transition:border-color ease-in-out .15s,-moz-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;background:0 0;border:none;color:#666;font-size:1pc;display:block;min-height:26px;line-height:26px;padding:0;width:100%}form input[type=email]:focus,form input[type=number]:focus,form input[type=password]:focus,form input[type=text]:focus,form textarea:focus{-webkit-box-shadow:inset 0 -2px 0 #2196f3;-moz-box-shadow:inset 0 -2px 0 #2196f3;box-shadow:inset 0 -2px 0 #2196f3;border-color:#66AFE9;outline:0}form input[type=email].firm,form input[type=number].firm,form input[type=password].firm,form input[type=text].firm,form textarea.firm{border-color:#1abc9c}form input[type=email].success,form input[type=number].success,form input[type=password].success,form input[type=text].success,form textarea.success{border-color:#11b452}form input[type=email].info,form input[type=number].info,form input[type=password].info,form input[type=text].info,form textarea.info{border-color:#3498db}form input[type=email].warning,form input[type=number].warning,form input[type=password].warning,form input[type=text].warning,form textarea.warning{border-color:#f1c40f}form input[type=email].danger,form input[type=number].danger,form input[type=password].danger,form input[type=text].danger,form textarea.danger{border-color:#e74c3c}form select[multiple],form select[size],form textarea{height:auto}form select{-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-moz-transition:border-color ease-in-out .15s,-moz-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;background-color:#fff;border:1px solid #ccc;font-size:1pc;line-height:26px;margin:0;padding:.3em .2em;position:relative;top:-4px;width:100%}form select:focus{border-color:#66AFE9;outline:0}form.no-border{border-bottom:none}form .input-row{padding-left:13px;padding-right:13px;margin-bottom:1pc;position:relative}form .input-group{border-collapse:separate;display:table;position:relative}form .input-group .addon,form .input-group .button,form .input-group .input{display:table-cell}form .input-group .addon,form .input-group .button{padding:0 1pc;vertical-align:middle;white-space:nowrap;width:1%}form .input-group .button{font-size:0;position:relative;white-space:nowrap}form .input-group .input{float:left;margin-bottom:0;position:relative;width:100%;z-index:2}a.button,form button,form input[type=submit],form input[type=reset],html input[type=button]{-webkit-border-radius:5px;-webkit-background-clip:padding-box;-moz-border-radius:5px;-moz-background-clip:padding;border-radius:5px;-webkit-transition:all .2s;-moz-transition:all .2s;-o-transition:all .2s;transition:all .2s;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background:#fff;border:none;color:#666;cursor:pointer;display:inline-block;font-size:12.8px;font-weight:400;line-height:20.8px;letter-spacing:.1px;margin:0;padding:7px 1pc;position:relative;text-align:center;text-rendering:optimizeLegibility;text-transform:uppercase;top:-7px;vertical-align:middle;white-space:nowrap;-webkit-font-smoothing:antialiased}a.button:hover,form button:hover,form input[type=submit]:hover,form input[type=reset]:hover,html input[type=button]:hover{-webkit-box-shadow:0 3px 6px rgba(0,0,0,.2),0 3px 6px rgba(0,0,0,.28);-moz-box-shadow:0 3px 6px rgba(0,0,0,.2),0 3px 6px rgba(0,0,0,.28);box-shadow:0 3px 6px rgba(0,0,0,.2),0 3px 6px rgba(0,0,0,.28)}a.button.success,form button.success,form input[type=submit].success,form input[type=reset].success,html input[type=button].success{background:#11b452}a.button.success:active,form button.success:active,form input[type=submit].success:active,form input[type=reset].success:active,html input[type=button].success:active{background:#27ad60}a.button.info,form button.info,form input[type=submit].info,form input[type=reset].info,html input[type=button].info{background:#3498db}a.button.info:active,form button.info:active,form input[type=submit].info:active,form input[type=reset].info:active,html input[type=button].info:active{background:#2383c4}a.button.warning,form button.warning,form input[type=submit].warning,form input[type=reset].warning,html input[type=button].warning{background:#f1c40f}a.button.warning:active,form button.warning:active,form input[type=submit].warning:active,form input[type=reset].warning:active,html input[type=button].warning:active{background:#cea70c}a.button.danger,form button.danger,form input[type=submit].danger,form input[type=reset].danger,html input[type=button].danger{background:#e74c3c}a.button.danger:active,form button.danger:active,form input[type=submit].danger:active,form input[type=reset].danger:active,html input[type=button].danger:active{background:#dc2d1b}a.button.submit,form button.submit,form input[type=submit].submit,form input[type=reset].submit,html input[type=button].submit{background:#2196f3;color:#fff}a.button.submit:active,form button.submit:active,form input[type=submit].submit:active,form input[type=reset].submit:active,html input[type=button].submit:active{background:#285e8e}[hidden]{display:none}[disabled]{cursor:default}:focus{outline:0}.clearfix:after,.clearfix:before{content:" ";display:table}.row{display:block;margin:0 auto;padding:0}.row:after,.row:before{content:" ";display:table}#bg:after,.slider .handle:after,.slider_xy .handle:after{content:''}.row .row{width:auto}.row .row.collapse{margin:0}.col{float:left;min-height:1px;position:relative;padding-left:13px;padding-right:13px}.col-no-gutter{float:left;min-height:1px;position:relative}.col-left-gutter{float:left;min-height:1px;position:relative;padding-left:26px}.col-right-gutter{float:left;min-height:1px;position:relative;padding-right:26px}.col-inverse,.col-left-gutter-inverse,.col-no-gutter-inverse,.col-right-gutter-inverse{float:right;min-height:1px;position:relative}.col-inverse{padding-left:13px;padding-right:13px}.col-left-gutter-inverse{padding-left:26px}.col-right-gutter-inverse{padding-right:26px}.col-inverse-no-gutter.width-1,.col-inverse.width-1,.col-no-gutter.width-1,.col.width-1{width:8.33333%}.col-inverse-no-gutter.width-2,.col-inverse.width-2,.col-no-gutter.width-2,.col.width-2{width:16.66667%}.col-inverse-no-gutter.width-3,.col-inverse.width-3,.col-no-gutter.width-3,.col.width-3{width:25%}.col-inverse-no-gutter.width-4,.col-inverse.width-4,.col-no-gutter.width-4,.col.width-4{width:33.33333%}.col-inverse-no-gutter.width-5,.col-inverse.width-5,.col-no-gutter.width-5,.col.width-5{width:41.66667%}.col-inverse-no-gutter.width-6,.col-inverse.width-6,.col-no-gutter.width-6,.col.width-6{width:50%}.col-inverse-no-gutter.width-7,.col-inverse.width-7,.col-no-gutter.width-7,.col.width-7{width:58.33333%}.col-inverse-no-gutter.width-8,.col-inverse.width-8,.col-no-gutter.width-8,.col.width-8{width:66.66667%}.col-inverse-no-gutter.width-9,.col-inverse.width-9,.col-no-gutter.width-9,.col.width-9{width:75%}.col-inverse-no-gutter.width-10,.col-inverse.width-10,.col-no-gutter.width-10,.col.width-10{width:83.33333%}.col-inverse-no-gutter.width-11,.col-inverse.width-11,.col-no-gutter.width-11,.col.width-11{width:91.66667%}.col-inverse-no-gutter.width-12,.col-inverse.width-12,.col-no-gutter.width-12,.col.width-12{width:100%}@media screen and (max-width:799px){.col-inverse-no-gutter.width-13,.col-inverse-no-gutter.width-14,.col-inverse-no-gutter.width-15,.col-inverse-no-gutter.width-16,.col-inverse-no-gutter.width-17,.col-inverse-no-gutter.width-18,.col-inverse-no-gutter.width-19,.col-inverse-no-gutter.width-20,.col-inverse-no-gutter.width-21,.col-inverse-no-gutter.width-22,.col-inverse-no-gutter.width-23,.col-inverse-no-gutter.width-24,.col-inverse.width-13,.col-inverse.width-14,.col-inverse.width-15,.col-inverse.width-16,.col-inverse.width-17,.col-inverse.width-18,.col-inverse.width-19,.col-inverse.width-20,.col-inverse.width-21,.col-inverse.width-22,.col-inverse.width-23,.col-inverse.width-24,.col-no-gutter.width-13,.col-no-gutter.width-14,.col-no-gutter.width-15,.col-no-gutter.width-16,.col-no-gutter.width-17,.col-no-gutter.width-18,.col-no-gutter.width-19,.col-no-gutter.width-20,.col-no-gutter.width-21,.col-no-gutter.width-22,.col-no-gutter.width-23,.col-no-gutter.width-24,.col.width-13,.col.width-14,.col.width-15,.col.width-16,.col.width-17,.col.width-18,.col.width-19,.col.width-20,.col.width-21,.col.width-22,.col.width-23,.col.width-24{width:100%}}@media screen and (min-width:800px){.col-inverse-no-gutter.width-13,.col-inverse.width-13,.col-no-gutter.width-13,.col.width-13{width:108.33333%}.col-inverse-no-gutter.width-14,.col-inverse.width-14,.col-no-gutter.width-14,.col.width-14{width:116.66667%}.col-inverse-no-gutter.width-15,.col-inverse.width-15,.col-no-gutter.width-15,.col.width-15{width:125%}.col-inverse-no-gutter.width-16,.col-inverse.width-16,.col-no-gutter.width-16,.col.width-16{width:133.33333%}.col-inverse-no-gutter.width-17,.col-inverse.width-17,.col-no-gutter.width-17,.col.width-17{width:141.66667%}.col-inverse-no-gutter.width-18,.col-inverse.width-18,.col-no-gutter.width-18,.col.width-18{width:150%}.col-inverse-no-gutter.width-19,.col-inverse.width-19,.col-no-gutter.width-19,.col.width-19{width:158.33333%}.col-inverse-no-gutter.width-20,.col-inverse.width-20,.col-no-gutter.width-20,.col.width-20{width:166.66667%}.col-inverse-no-gutter.width-21,.col-inverse.width-21,.col-no-gutter.width-21,.col.width-21{width:175%}.col-inverse-no-gutter.width-22,.col-inverse.width-22,.col-no-gutter.width-22,.col.width-22{width:183.33333%}.col-inverse-no-gutter.width-23,.col-inverse.width-23,.col-no-gutter.width-23,.col.width-23{width:191.66667%}.col-inverse-no-gutter.width-24,.col-inverse.width-24,.col-no-gutter.width-24,.col.width-24{width:200%}.col-inverse-no-gutter.width-offset-0,.col-inverse.width-offset-0,.col-no-gutter.width-offset-0,.col.width-offset-0{margin-left:0}.col-inverse-no-gutter.width-offset-1,.col-inverse.width-offset-1,.col-no-gutter.width-offset-1,.col.width-offset-1{margin-left:8.33333%}.col-inverse-no-gutter.width-offset-2,.col-inverse.width-offset-2,.col-no-gutter.width-offset-2,.col.width-offset-2{margin-left:16.66667%}.col-inverse-no-gutter.width-offset-3,.col-inverse.width-offset-3,.col-no-gutter.width-offset-3,.col.width-offset-3{margin-left:25%}.col-inverse-no-gutter.width-offset-4,.col-inverse.width-offset-4,.col-no-gutter.width-offset-4,.col.width-offset-4{margin-left:33.33333%}.col-inverse-no-gutter.width-offset-5,.col-inverse.width-offset-5,.col-no-gutter.width-offset-5,.col.width-offset-5{margin-left:41.66667%}.col-inverse-no-gutter.width-offset-6,.col-inverse.width-offset-6,.col-no-gutter.width-offset-6,.col.width-offset-6{margin-left:50%}.col-inverse-no-gutter.width-offset-7,.col-inverse.width-offset-7,.col-no-gutter.width-offset-7,.col.width-offset-7{margin-left:58.33333%}.col-inverse-no-gutter.width-offset-8,.col-inverse.width-offset-8,.col-no-gutter.width-offset-8,.col.width-offset-8{margin-left:66.66667%}.col-inverse-no-gutter.width-offset-9,.col-inverse.width-offset-9,.col-no-gutter.width-offset-9,.col.width-offset-9{margin-left:75%}.col-inverse-no-gutter.width-offset-10,.col-inverse.width-offset-10,.col-no-gutter.width-offset-10,.col.width-offset-10{margin-left:83.33333%}.col-inverse-no-gutter.push,.col-inverse.push,.col-no-gutter.push,.col.push{right:auto}.col-inverse-no-gutter.pull,.col-inverse.pull,.col-no-gutter.pull,.col.pull{left:auto}.col-inverse-no-gutter.push-1,.col-inverse.push-1,.col-no-gutter.push-1,.col.push-1{margin-left:-8.33333%;left:8.33333%}.col-inverse-no-gutter.push-2,.col-inverse.push-2,.col-no-gutter.push-2,.col.push-2{margin-left:-16.66667%;left:16.66667%}.col-inverse-no-gutter.push-3,.col-inverse.push-3,.col-no-gutter.push-3,.col.push-3{margin-left:-25%;left:25%}.col-inverse-no-gutter.push-4,.col-inverse.push-4,.col-no-gutter.push-4,.col.push-4{margin-left:-33.33333%;left:33.33333%}.col-inverse-no-gutter.push-5,.col-inverse.push-5,.col-no-gutter.push-5,.col.push-5{margin-left:-41.66667%;left:41.66667%}.col-inverse-no-gutter.push-6,.col-inverse.push-6,.col-no-gutter.push-6,.col.push-6{margin-left:-50%;left:50%}.col-inverse-no-gutter.push-7,.col-inverse.push-7,.col-no-gutter.push-7,.col.push-7{margin-left:-58.33333%;left:58.33333%}.col-inverse-no-gutter.push-8,.col-inverse.push-8,.col-no-gutter.push-8,.col.push-8{margin-left:-66.66667%;left:66.66667%}.col-inverse-no-gutter.push-9,.col-inverse.push-9,.col-no-gutter.push-9,.col.push-9{margin-left:-75%;left:75%}.col-inverse-no-gutter.push-10,.col-inverse.push-10,.col-no-gutter.push-10,.col.push-10{margin-left:-83.33333%;left:83.33333%}.col-inverse-no-gutter.push-11,.col-inverse.push-11,.col-no-gutter.push-11,.col.push-11{margin-left:-91.66667%;left:91.66667%}.col-inverse-no-gutter.pull-1,.col-inverse.pull-1,.col-no-gutter.pull-1,.col.pull-1{margin-right:-8.33333%;right:8.33333%}.col-inverse-no-gutter.pull-2,.col-inverse.pull-2,.col-no-gutter.pull-2,.col.pull-2{margin-right:-16.66667%;right:16.66667%}.col-inverse-no-gutter.pull-3,.col-inverse.pull-3,.col-no-gutter.pull-3,.col.pull-3{margin-right:-25%;right:25%}.col-inverse-no-gutter.pull-4,.col-inverse.pull-4,.col-no-gutter.pull-4,.col.pull-4{margin-right:-33.33333%;right:33.33333%}.col-inverse-no-gutter.pull-5,.col-inverse.pull-5,.col-no-gutter.pull-5,.col.pull-5{margin-right:-41.66667%;right:41.66667%}.col-inverse-no-gutter.pull-6,.col-inverse.pull-6,.col-no-gutter.pull-6,.col.pull-6{margin-right:-50%;right:50%}.col-inverse-no-gutter.pull-7,.col-inverse.pull-7,.col-no-gutter.pull-7,.col.pull-7{margin-right:-58.33333%;right:58.33333%}.col-inverse-no-gutter.pull-8,.col-inverse.pull-8,.col-no-gutter.pull-8,.col.pull-8{margin-right:-66.66667%;right:66.66667%}.col-inverse-no-gutter.pull-9,.col-inverse.pull-9,.col-no-gutter.pull-9,.col.pull-9{margin-right:-75%;right:75%}.col-inverse-no-gutter.pull-10,.col-inverse.pull-10,.col-no-gutter.pull-10,.col.pull-10{margin-right:-83.33333%;right:83.33333%}.col-inverse-no-gutter.pull-11,.col-inverse.pull-11,.col-no-gutter.pull-11,.col.pull-11{margin-right:-91.66667%;right:91.66667%}.col-inverse-no-gutter.centered,.col-inverse.centered,.col-no-gutter.centered,.col.centered{float:none;margin:0 auto}}.gutter{margin:13px}.gutter-horizontal{margin-left:13px;margin-right:13px}.gutter-vertical{margin-top:13px;margin-bottom:13px}.gutter-top{margin-top:26px}.gutter-right{margin-right:26px}.gutter-bottom{margin-bottom:26px}.gutter-left{margin-left:26px}.internal-gutter{padding:13px}.internal-gutter-horizontal{padding-left:13px;padding-right:13px}.internal-gutter-vertical{padding-top:13px;padding-bottom:13px}.internal-gutter-top{padding-top:26px}.internal-gutter-right{padding-right:26px}.internal-gutter-bottom{padding-bottom:26px}.internal-gutter-left{padding-left:26px}figure.col{padding-left:0}figure.col-inverse{padding-right:0}.download-modal,.info-modal,.modal,.note-modal,.warning-modal{-webkit-border-radius:5px;-webkit-background-clip:padding-box;-moz-border-radius:5px;-moz-background-clip:padding;border-radius:5px;-webkit-box-shadow:0 5px 15px rgba(0,0,0,.5);-moz-box-shadow:0 5px 15px rgba(0,0,0,.5);box-shadow:0 5px 15px rgba(0,0,0,.5);background-clip:padding-box;background-color:#fff;border:1px solid transparent;outline:0;position:relative}.box,.label{-webkit-background-clip:padding-box;-moz-background-clip:padding}.download-modal .modal-body,.download-modal .modal-footer,.download-modal .modal-header,.info-modal .modal-body,.info-modal .modal-footer,.info-modal .modal-header,.modal .modal-body,.modal .modal-footer,.modal .modal-header,.note-modal .modal-body,.note-modal .modal-footer,.note-modal .modal-header,.warning-modal .modal-body,.warning-modal .modal-footer,.warning-modal .modal-header{padding:1pc}.download-modal .modal-header,.info-modal .modal-header,.modal .modal-header,.note-modal .modal-header,.warning-modal .modal-header{border-bottom:1px solid transparent;min-height:1pc}.download-modal .modal-body,.info-modal .modal-body,.modal .modal-body,.note-modal .modal-body,.warning-modal .modal-body{position:relative}.download-modal .modal-footer,.info-modal .modal-footer,.modal .modal-footer,.note-modal .modal-footer,.warning-modal .modal-footer{border-top:1px solid transparent;text-align:right;font-style:italic}.box{-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;background-clip:padding-box;background-color:#fff;border:1px solid transparent;outline:0;padding:5px;position:relative}.palette-success{background-color:#2ecc71}.palette-warning{background-color:#f1c40f}.palette-danger{background-color:#e74c3c}.palette-submit{background-color:#2196f3}.palette-success-dark{background-color:#27ad60;color:#fff}.palette-info{background-color:#dbc034;color:#fff}.palette-info-dark{background-color:#a88f0b;color:#fff}.palette-warning-dark{background-color:#cea70c;color:#fff}.palette-danger-dark{background-color:#dc2d1b;color:#fff}.palette-submit-dark{background-color:#285e8e;color:#fff}.label{-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%;box-sizing:border-box;background:#d12727;border:2px solid #fff;cursor:pointer;height:10px;width:10px}.slider{background-color:#b9b9b9;border-radius:3px;display:inline-block;position:relative}.slider .value{background-color:#00b5ff;border-radius:3px;position:absolute}.slider .handle{height:8px;position:absolute;width:8px}.slider .handle:after{display:block;position:relative}.slider_x .handle:after,.slider_y .handle:after{-webkit-border-radius:50%;-webkit-background-clip:padding-box;-moz-border-radius:50%;-moz-background-clip:padding;border-radius:50%;background-clip:padding-box;background-color:#00b5ff;border:1px solid #00b5ff;height:9pt;width:9pt}.slider_x{height:3px}.slider_x .handle{top:0;height:100%}.slider_x .handle:after{top:-5px;left:-6px}.slider_y{width:3px}.slider_y .handle{left:0;width:100%}.slider_y .handle:after{top:-6px;left:-2px}.slider_xy{background-color:#00b5ff;border-radius:0;position:relative;height:100%;width:100%}.slider_xy .handle{position:absolute}.slider_xy .handle:after{position:relative;display:block;top:-4px;left:-4px;width:8px;height:8px;background-color:transparent;border:2px solid #fff;border-radius:50%}.dropdown{-webkit-transition:opacity .4s cubic-bezier(.23,1,.32,1) 0s;-moz-transition:opacity .4s cubic-bezier(.23,1,.32,1) 0s;-o-transition:opacity .4s cubic-bezier(.23,1,.32,1) 0s;transition:opacity .4s cubic-bezier(.23,1,.32,1) 0s;left:0;right:0;top:0;background:#fff;list-style-type:none;margin:0;max-height:0;opacity:0;padding:0;position:absolute;z-index:100}.dropdown li{background:#eaeaea;cursor:pointer;font-size:13px;margin:0 0 -1px;z-index:101}#socialButtons #circleBg,#socialButtons .share{-webkit-background-clip:padding-box;-moz-background-clip:padding}#controls p,#heading h1,#heading p:last-child{margin-bottom:0}.dropdown li:hover{background-color:#fefeff}.dropdown li:first-child{border-top:none}.dropdown li a,.dropdown li span{color:#aaa;display:block;padding:10px 20px;z-index:102}#heading:after,#heading:before,#hohContainer:after,#hohContainer:before,#socialButtons #socialIcons:after,#socialButtons #socialIcons:before{display:table;content:" "}.dropdown:after,.dropdown:before{left:0;right:0}.dropdown.active{opacity:1;max-height:25pc;overflow-y:scroll}.dropdown.active .viewport_container{height:100%;overflow:hidden;position:relative;width:100%}#bg,#bg:after,#socialButtons{position:absolute}.dropdown.active .viewport_container .viewport{bottom:0;left:0;overflow-x:'auto';overflow-y:'auto';position:absolute;right:-18px;top:0}body{background:#f9f9f9;overflow-x:hidden}#bg{background:#333;top:0;left:0;z-index:-1}#bg:after{border-left:125pc solid transparent;border-right:0 solid transparent;border-bottom:200px solid #F9F9F9;clear:both;height:0;left:0;bottom:0;width:0}#heading .row,#hohContainer .row{width:auto}#hohContainer{display:block;margin:0 auto;padding:0}#hohContainer .row.collapse{margin:0}#heading{display:block;margin:0 auto;padding:0}#heading .row.collapse{margin:0}#heading #main_header{margin:0 auto}#heading #logo_container,#heading #powered_by{float:left;min-height:1px;position:relative;width:50%}#heading #logo_container p{text-align:left}#heading #logo_container p a{border-bottom:none}#heading #powered_by ul{text-align:right}#heading #powered_by ul #reactjs a{color:#00d8ff;font-family:"Helvetica Neue",Helvetica,Roboto,Arial,sans-serif}#heading #powered_by ul #reactjs a img{height:25px;position:relative;top:7px;width:25px}#heading #powered_by ul #wikipedia a{color:#fff;font-family:'Crimson+Text';letter-spacing:.1em;text-transform:uppercase}#heading #powered_by ul #wikipedia a small{font-size:.8em}#heading h1{color:#fff;margin-top:75pt;text-align:center}#heading h1 #top_title{font-size:2pc;font-family:'Open Sans';font-weight:300;letter-spacing:.25em;text-transform:uppercase}#heading h1 #lower_title{font-size:28px;position:relative;top:-1em}#heading p{color:#fff;margin-bottom:1em;text-align:center}#heading p a{border-bottom:1px dotted #fff;color:#fff}#socialButtons{-webkit-transition:all .5s;-moz-transition:all .5s;-o-transition:all .5s;transition:all .5s;color:#00b5ff;right:.5em}#socialButtons.active,#socialButtons:hover{color:#535f5f}#socialButtons .socialBox{height:36px;position:relative;width:41px}#socialButtons #socialIcons{display:block;margin:0 auto;padding:0;bottom:37px;position:absolute;right:5px}#socialButtons #socialIcons .row{width:auto}#socialButtons #socialIcons .row.collapse{margin:0}#socialButtons #circleBg{-webkit-border-radius:5px;-moz-border-radius:5px;border-radius:5px;background-clip:padding-box;background-color:#fff;display:block;position:relative;right:.9em}#socialButtons #circleBg .fa-share-alt-square{left:-1px;top:-4px}#socialButtons .fa{cursor:pointer;position:relative}#socialButtons .fa-share-alt-square{font-size:2em}#socialButtons .count,#socialButtons .share{text-align:center;text-decoration:none}#socialButtons .count{color:#fff;font-size:14px;left:-50px;position:absolute;text-align:right;top:6px;width:42px}#socialButtons .share{-webkit-border-radius:50%;-moz-border-radius:50%;border-radius:50%;background-clip:padding-box;color:#fff;display:inline-block;height:30px;line-height:28px;position:absolute;width:31px}#socialButtons .share:hover{color:#fff;text-decoration:none}#socialButtons .fa img{position:relative;top:2px;width:1pc}#socialButtons .bufferapp,#socialButtons .facebook,#socialButtons .pinterest,#socialButtons .stumbleupon,#socialButtons .twitter{height:100%;text-align:center;width:100%;-webkit-border-radius:4px;-webkit-background-clip:padding-box;-moz-border-radius:4px;-moz-background-clip:padding;border-radius:4px;background-clip:padding-box}#socialButtons #twitter .share{text-shadow:1px 0 0 #0077be;background-image:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj48bGluZWFyR3JhZGllbnQgaWQ9Imxlc3NoYXQtZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiMyNmMzZWIiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzAwODBkNiIgc3RvcC1vcGFjaXR5PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2xlc3NoYXQtZ2VuZXJhdGVkKSIgLz48L3N2Zz4=);background-image:-webkit-linear-gradient(top,#26c3eb 0,#0080d6 100%);background-image:-moz-linear-gradient(top,#26c3eb 0,#0080d6 100%);background-image:-o-linear-gradient(top,#26c3eb 0,#0080d6 100%);background-image:linear-gradient(to bottom,#26c3eb 0,#0080d6 100%)}#socialButtons #facebook .share{background:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj48bGluZWFyR3JhZGllbnQgaWQ9Imxlc3NoYXQtZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiMzYjU5OTgiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzEzMzc4MyIgc3RvcC1vcGFjaXR5PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2xlc3NoYXQtZ2VuZXJhdGVkKSIgLz48L3N2Zz4=) #3b5998;text-shadow:1px 0 0 #26427e;background-image:-webkit-linear-gradient(top,#3b5998 0,#133783 100%);background-image:-moz-linear-gradient(top,#3b5998 0,#133783 100%);background-image:-o-linear-gradient(top,#3b5998 0,#133783 100%);background-image:linear-gradient(to bottom,#3b5998 0,#133783 100%)}#socialButtons #googleplus .share{background:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj48bGluZWFyR3JhZGllbnQgaWQ9Imxlc3NoYXQtZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiM2ZDZkNmQiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzQzNDM0MyIgc3RvcC1vcGFjaXR5PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2xlc3NoYXQtZ2VuZXJhdGVkKSIgLz48L3N2Zz4=) #6d6d6d;text-shadow:1px 0 0 #222;background-image:-webkit-linear-gradient(top,#6d6d6d 0,#434343 100%);background-image:-moz-linear-gradient(top,#6d6d6d 0,#434343 100%);background-image:-o-linear-gradient(top,#6d6d6d 0,#434343 100%);background-image:linear-gradient(to bottom,#6d6d6d 0,#434343 100%)}#socialButtons #linkedin .share{background:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj48bGluZWFyR3JhZGllbnQgaWQ9Imxlc3NoYXQtZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiM3NGJiZGIiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iIzAwN2ZiMiIgc3RvcC1vcGFjaXR5PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2xlc3NoYXQtZ2VuZXJhdGVkKSIgLz48L3N2Zz4=) #6d6d6d;text-shadow:1px 0 0 #26427e;background-image:-webkit-linear-gradient(top,#74bbdb 0,#007fb2 100%);background-image:-moz-linear-gradient(top,#74bbdb 0,#007fb2 100%);background-image:-o-linear-gradient(top,#74bbdb 0,#007fb2 100%);background-image:linear-gradient(to bottom,#74bbdb 0,#007fb2 100%)}#socialButtons #stumbleupon .share{background:url(data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiA/PjxzdmcgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIiB3aWR0aD0iMTAwJSIgaGVpZ2h0PSIxMDAlIiB2aWV3Qm94PSIwIDAgMSAxIiBwcmVzZXJ2ZUFzcGVjdFJhdGlvPSJub25lIj48bGluZWFyR3JhZGllbnQgaWQ9Imxlc3NoYXQtZ2VuZXJhdGVkIiBncmFkaWVudFVuaXRzPSJ1c2VyU3BhY2VPblVzZSIgeDE9IjAlIiB5MT0iMCUiIHgyPSIwJSIgeTI9IjEwMCUiPjxzdG9wIG9mZnNldD0iMCUiIHN0b3AtY29sb3I9IiNlYjQ5MjQiIHN0b3Atb3BhY2l0eT0iMSIvPjxzdG9wIG9mZnNldD0iMTAwJSIgc3RvcC1jb2xvcj0iI2IxMjQwMyIgc3RvcC1vcGFjaXR5PSIxIi8+PC9saW5lYXJHcmFkaWVudD48cmVjdCB4PSIwIiB5PSIwIiB3aWR0aD0iMSIgaGVpZ2h0PSIxIiBmaWxsPSJ1cmwoI2xlc3NoYXQtZ2VuZXJhdGVkKSIgLz48L3N2Zz4=) #eb4924;text-shadow:1px 0 0 #26427e;background-image:-webkit-linear-gradient(top,#eb4924 0,#b12403 100%);background-image:-moz-linear-gradient(top,#eb4924 0,#b12403 100%);background-image:-o-linear-gradient(top,#eb4924 0,#b12403 100%);background-image:linear-gradient(to bottom,#eb4924 0,#b12403 100%)}#controls .yearPanel,#controls p{-webkit-background-clip:padding-box;-moz-background-clip:padding}.socialTransition-appear{-webkit-transition:all 2s;-moz-transition:all 2s;-o-transition:all 2s;transition:all 2s;max-height:1px;opacity:.01}.socialTransition-appear.socialTransition-appear-active{max-height:500px;opacity:1}#controls,#itemDetail,#items,#mapAndControls{display:block;margin:0 auto;padding:0;width:75pc}#controls:after,#controls:before,#itemDetail:after,#itemDetail:before,#items:after,#items:before,#mapAndControls:after,#mapAndControls:before{content:" ";display:table}#controls .row,#itemDetail .row,#items .row,#mapAndControls .row{width:auto}#controls .row.collapse,#itemDetail .row.collapse,#items .row.collapse,#mapAndControls .row.collapse{margin:0}#mapAndControls{position:relative}#gmap{-webkit-box-shadow:0 10px 55px 0 rgba(0,0,0,.3);-moz-box-shadow:0 10px 55px 0 rgba(0,0,0,.3);box-shadow:0 10px 55px 0 rgba(0,0,0,.3)}#mapCanvas{display:block}#controls{background:rgba(0,0,0,.5);bottom:0;padding:11px 11px 18px;position:absolute}#controls p{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;background:#fff;cursor:pointer;padding:.2em .5em;text-transform:capitalize}#controls label{color:#fff;margin-bottom:.2em}#controls .panel{float:left;min-height:1px;position:relative;width:33.33333%;padding-left:13px;padding-right:13px}#controls .yearPanel{-webkit-border-radius:3px;-moz-border-radius:3px;border-radius:3px;background-clip:padding-box;background-color:#FFF;display:inline-block;font-size:15px;padding:3px 9px}#hideItemDetail,.fa_button{-webkit-border-radius:50%;-webkit-background-clip:padding-box;-moz-border-radius:50%;-moz-background-clip:padding;cursor:pointer}#controls #endDate,#controls #startDate{cursor:pointer;display:inline-block;margin:0 1em 0 22px;width:280px}.fa_button{-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;border-radius:50%;background-clip:padding-box;-webkit-box-shadow:1px 1px 1px #ccc;-moz-box-shadow:1px 1px 1px #ccc;box-shadow:1px 1px 1px #ccc;-webkit-transition:background-color .5s;-moz-transition:background-color .5s;-o-transition:background-color .5s;transition:background-color .5s;background-color:#00b5ff;border:none;color:#fff;display:block;height:30px;outline:0;text-align:center;text-decoration:none;width:30px;zoom:1}.fa_button#backButton,.fa_button#forwardButton,.fa_button#galleryBack,.fa_button#galleryForward{cursor:pointer;font-size:18px;position:absolute}.fa_button#backButton .fa,.fa_button#forwardButton .fa,.fa_button#galleryBack .fa,.fa_button#galleryForward .fa{position:relative;top:1px}.fa_button#backButton,.fa_button#forwardButton{bottom:35px}.fa_button#backButton{left:-50px}.fa_button#forwardButton{right:-50px}.fa_button#backButton .fa,.fa_button#galleryBack .fa{left:-1px}.fa_button#forwardButton .fa,.fa_button#galleryForward .fa{left:1px}.fa_button:hover{background-color:#535f5f;color:#fff}#items ul{list-style-type:none;margin-left:0}#items ul .yearTitle{border-bottom:1px solid #e8e8e8;color:#727272;margin:60px 0 30px;padding-bottom:30px}#items ul .itemPanel{-webkit-box-shadow:0 2px 4px rgba(0,0,0,.24);-moz-box-shadow:0 2px 4px rgba(0,0,0,.24);box-shadow:0 2px 4px rgba(0,0,0,.24);background:#fff;cursor:pointer;margin-bottom:30px}#items ul .itemPanel.highlightMap{background-color:#d5edf5}#items ul .itemPanel.highlightClick{background-color:#f5f4d5}#items ul .itemPanel p{margin-bottom:0;padding:1em}#items ul .itemPanel p a{color:#727272}#items ul .itemPanel .readmore{border-top:1px solid #eee;color:#00b5ff;display:block;font-size:.8em;padding:1em;text-align:right;width:100%}.itemDetailTransition-appear{opacity:.01;-webkit-transition:opacity .5s ease-in;-moz-transition:opacity .5s ease-in;-o-transition:opacity .5s ease-in;transition:opacity .5s ease-in}.itemDetailTransition-appear.itemDetailTransition-appear-active{opacity:1}#itemDetail{-webkit-box-shadow:0 4px 10px rgba(0,0,0,.23);-moz-box-shadow:0 4px 10px rgba(0,0,0,.23);box-shadow:0 4px 10px rgba(0,0,0,.23);background:#fff;overflow-y:auto;padding:1em;position:fixed;right:15px;top:10px}#itemDetail h3,#itemDetail h4{border-bottom:1px solid #e8e8e8;font-family:'Crimson+Text';line-height:1.5;padding-bottom:1em}#itemDetail h3{font-size:24px;margin:.5em 0 1.5em}#itemDetail h4{font-size:20px;margin:2em 0 1em}#itemDetail ul,.paginator{list-style-type:none;margin-left:0}#itemDetail p,#itemDetail ul li{font-size:15px}#itemDetail p#mainLink{font-weight:700}#itemDetail p#mainLink a{font-weight:400}#itemDetail #galleryTitle{position:relative}#itemDetail #galleryTitle #galleryBack,#itemDetail #galleryTitle #galleryForward{top:3px}#itemDetail #galleryTitle #galleryBack{left:89px}#itemDetail #galleryTitle #galleryForward{left:130px}#itemDetail #gallery img{float:left;min-height:1px;position:relative;width:33.33333%;padding-left:13px;padding-right:13px}.paginator li,.paginator li.selected,.paginator li:hover{padding:2px 10px}#hideItemDetail{border-radius:50%;-webkit-box-shadow:1px 1px 1px #ccc;-moz-box-shadow:1px 1px 1px #ccc;box-shadow:1px 1px 1px #ccc;-webkit-transition:right .8s;-moz-transition:right .8s;-o-transition:right .8s;transition:right .8s;background:#00b5ff;color:#fff;height:35px;line-height:2;position:fixed;right:-100%;text-align:center;top:4px;width:35px;z-index:200}.paginator{overflow:hidden;text-align:center}.paginator:after,.paginator:before{display:table}.paginator li:before{margin:0 .66667em;position:relative;top:1px}#footer p,.paginator li form,.paginator li input{margin-bottom:0}.paginator li:first-child:before{margin:0}.paginator li{display:inline-block;cursor:pointer;margin:0}.paginator li a{color:#ccc}.paginator li.selected a{color:#00b5ff}.paginator li.no_more{-webkit-border-radius:2px;-webkit-background-clip:padding-box;-moz-border-radius:2px;-moz-background-clip:padding;border-radius:2px;background-clip:padding-box;background-color:#535f5f;padding:2px 10px}.paginator li.no_more a{color:#fff}.paginator li:hover a{color:#535f5f}#footer{background:#222;color:#ccc}#footer p{border-top:1px solid #fff;color:#fff;padding:2em;text-align:center}#footer a{border-bottom:1px dotted #ccc;color:#ccc}@media screen and (max-width:639px){#controls,#itemDetail,#items,#mapAndControls,#mapCanvas{width:285px}a.button,button,html input[type=button],input[type=submit],input[type=reset]{line-height:1.278}#heading,#hohContainer{width:300px}#heading{margin:40px auto 150px}#main_header{margin:0 auto;width:280px}#circleBg{height:30px;width:30px}#circleBg .fa-share-alt-square{font-size:2.7em}#mapCanvas{height:250px}#controls .panel{padding:0}#controls #endDate,#controls #startDate{width:72px}#hideItemDetail{height:28px;right:283px;width:28px}}@media screen and (max-width:799px){body{font-size:.875em}#heading h1{margin-bottom:1em;margin-top:70px}#heading h1 #lower_title{line-height:0;top:0}}@media screen and (min-width:800px){h1:first-child,h2:first-child,h3:first-child{margin-top:0}}@media screen and (min-width:640px) and (max-width:799px){#controls,#itemDetail,#items,#mapAndControls,#mapCanvas{width:425px}#heading,#hohContainer{width:540px}#heading{margin:40px auto 5pc}#main_header{margin:0 auto;width:5in}#circleBg{height:30px;width:30px}#circleBg .fa-share-alt-square{font-size:2.7em}#mapCanvas{height:325px}#controls #endDate,#controls #startDate{width:75pt}#hideItemDetail{right:423px}}@media screen and (min-width:1376px){body{font-size:1.125em}}@media screen and (min-width:800px) and (max-width:1023px){#controls,#itemDetail,#items,#mapAndControls,#mapCanvas{width:625px}#heading,#hohContainer{width:740px}#heading{margin:40px auto 5pc}#main_header{margin:0 auto;width:680px}#circleBg{height:30px;width:30px}#circleBg .fa-share-alt-square{font-size:2.3em}#mapCanvas{height:455px}#controls #endDate,#controls #startDate{width:130px}#hideItemDetail{height:28px;line-height:1.6;right:623px;width:28px}}@media screen and (min-width:1024px) and (max-width:1375px){#controls,#itemDetail,#items,#mapAndControls,#mapCanvas{width:900px}#heading,#hohContainer{width:980px}#heading{margin:75px auto}#circleBg{height:30px;width:30px}#circleBg .fa-share-alt-square{font-size:2.3em}#mapCanvas{height:600px}#controls #endDate,#controls #startDate{width:200px}#hideItemDetail{right:897px}}@media screen and (min-width:1376px) and (max-width:1839px){#controls,#itemDetail,#items,#mapAndControls,#mapCanvas{width:75pc}#heading,#hohContainer{width:80pc}#heading{margin:75pt auto}#circleBg{height:30px;width:30px}#mapCanvas{height:600px}#controls #endDate,#controls #startDate{width:280px}#hideItemDetail{height:28px;line-height:1.6;right:1197px;width:28px}}@media screen and (min-width:1840px){#controls,#itemDetail,#items,#mapAndControls,#mapCanvas{width:975pt}body{font-size:1.125em}#heading,#hohContainer{width:15in}#heading{margin:75pt auto}#circleBg{height:30px;width:30px}#mapCanvas{height:600px}#controls #endDate,#controls #startDate{width:20pc}#hideItemDetail{right:1297px}} -------------------------------------------------------------------------------- /public/functions.js: -------------------------------------------------------------------------------- 1 | /* 2 | --- 3 | name: Selectors 4 | description: Deals with selecting of things 5 | details: inspired by //product.voxmedia.com/til/2015/2/16/8047537/easy-selector-engine-with-vanilla-javascript 6 | provides: $, $$ 7 | ... 8 | */ 9 | 10 | var $ = document.getElementById.bind(document); 11 | var $$ = document.querySelectorAll.bind(document); 12 | 13 | /* 14 | --- 15 | name: IsSet 16 | description: a tidy function so you don't have to check undefined all the bloody time 17 | provides: isset 18 | ... 19 | */ 20 | 21 | function isset (obj) { return typeof obj !== 'undefined'; } 22 | 23 | /* 24 | --- 25 | name: mobileCheck 26 | description: Checks if we're on a mobile device 27 | details: inspired by //stackoverflow.com/a/11381730/989439 28 | provides: mobileCheck 29 | ... 30 | */ 31 | 32 | function mobileCheck () { 33 | var check = false; 34 | (function(a){if(/(android|ipad|playbook|silk|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows (ce|phone)|xda|xiino/i.test(a)||/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(a.substr(0,4)))check = true})(navigator.userAgent||navigator.vendor||window.opera); 35 | return check; 36 | } 37 | 38 | /* 39 | --- 40 | name: getViewportSize 41 | description: Gets the dimensions of the viewport 42 | ... 43 | */ 44 | 45 | function getViewportSize() { 46 | var dims = {}; 47 | dims.width = 0; 48 | dims.height = 0; 49 | 50 | if( typeof( window.innerWidth ) == 'number' ) { 51 | //Non-IE 52 | dims.width = window.innerWidth; 53 | dims.height = window.innerHeight; 54 | } 55 | else if( document.documentElement && ( document.documentElement.clientWidth || document.documentElement.clientHeight ) ) { 56 | //IE 6+ in 'standards compliant mode' 57 | dims.width = document.documentElement.clientWidth; 58 | dims.height = document.documentElement.clientHeight; 59 | } 60 | else if( document.body && ( document.body.clientWidth || document.body.clientHeight ) ) { 61 | //IE 4 compatible 62 | dims.width = document.body.clientWidth; 63 | dims.height = document.body.clientHeight; 64 | } 65 | 66 | return dims; 67 | } 68 | 69 | /* 70 | --- 71 | name: escapeRegExp 72 | description: escapes regex 73 | ... 74 | */ 75 | 76 | function escapeRegExp (string) { 77 | return string.replace(/([.*+?^=!:${}()|\[\]\/\\])/g, "\\$1"); 78 | } 79 | 80 | 81 | /* 82 | --- 83 | Clouds 84 | --- 85 | */ 86 | 87 | var camera, scene, renderer, 88 | geometry, material, mesh; 89 | 90 | function init () { 91 | clock = new THREE.Clock(); 92 | 93 | renderer = new THREE.WebGLRenderer(); 94 | 95 | scene = new THREE.Scene(); 96 | 97 | camera = new THREE.PerspectiveCamera( 75, window.innerWidth / window.innerHeight, 1, 10000 ); 98 | camera.position.z = 1000; 99 | scene.add( camera ); 100 | 101 | geometry = new THREE.BoxGeometry( 200, 200, 200 ); 102 | material = new THREE.MeshLambertMaterial( { color: 0x93887d, wireframe: false } ); 103 | mesh = new THREE.Mesh( geometry, material ); 104 | //scene.add( mesh ); 105 | cubeSineDriver = 0; 106 | 107 | light = new THREE.DirectionalLight(0xfffadd, 1.5); 108 | light.position.set(-1,0,1); 109 | scene.add(light); 110 | 111 | smokeTexture = THREE.ImageUtils.loadTexture('/history-of-humanity/imgs/Smoke-Element.png'); 112 | smokeMaterial = new THREE.MeshLambertMaterial({ 113 | color: 0x93887d, 114 | map: smokeTexture, 115 | transparent: true 116 | }); 117 | smokeMaterial.map.minFilter = THREE.LinearFilter; 118 | 119 | smokeGeo = new THREE.PlaneBufferGeometry(300,300); 120 | smokeParticles = []; 121 | 122 | for (p = 0; p < 200; p++) { 123 | var particle = new THREE.Mesh(smokeGeo,smokeMaterial); 124 | particle.position.set(Math.random() * 500 - 250, Math.random() * 500 - 250, Math.random() * 1000 - 30); 125 | particle.rotation.z = Math.random() * 360; 126 | scene.add(particle); 127 | smokeParticles.push(particle); 128 | } 129 | 130 | $('bg').appendChild(renderer.domElement); 131 | 132 | window.addEventListener('resize', onWindowResize, false); 133 | } 134 | 135 | function animate () { 136 | // note: three.js includes requestAnimationFrame shim 137 | delta = clock.getDelta(); 138 | requestAnimationFrame( animate ); 139 | evolveSmoke(); 140 | render(); 141 | onWindowResize(); 142 | } 143 | 144 | function evolveSmoke () { 145 | var sp = smokeParticles.length; 146 | while(sp--) { 147 | smokeParticles[sp].rotation.z += (delta * 0.1); 148 | } 149 | } 150 | 151 | function updateLightPosition () { 152 | var time = Date.now() * 0.0002; 153 | 154 | light.position.x = Math.sin(time * 5); 155 | light.position.y = Math.cos(1/time); 156 | } 157 | 158 | function render () { 159 | mesh.rotation.x += 0.005; 160 | mesh.rotation.y += 0.01; 161 | cubeSineDriver += .01; 162 | mesh.position.z = 100 + (Math.sin(cubeSineDriver) * 500); 163 | updateLightPosition(); 164 | renderer.render( scene, camera ); 165 | } 166 | 167 | function onWindowResize () { 168 | var dims = getViewportSize(); 169 | 170 | var cWidth = dims.width; 171 | var cHeight = 670; 172 | 173 | if (!mobileCheck()) 174 | cWidth = cWidth - 17; 175 | 176 | if (cHeight < dims.height) 177 | cHeight = dims.height; 178 | else if (cHeight > 1200) 179 | cHeight = 1200; 180 | 181 | camera.aspect = dims.width / dims.height; 182 | camera.updateProjectionMatrix(); 183 | 184 | renderer.setSize(cWidth, cHeight); 185 | } -------------------------------------------------------------------------------- /public/imgs/Smoke-Element.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwatsonwailes/history-of-humanity/d8867d65a248c4711059dba62b56fab14cf79a13/public/imgs/Smoke-Element.png -------------------------------------------------------------------------------- /public/imgs/logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/pwatsonwailes/history-of-humanity/d8867d65a248c4711059dba62b56fab14cf79a13/public/imgs/logo.png -------------------------------------------------------------------------------- /public/imgs/react-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | 6 | 10 | 15 | 20 | 21 | -------------------------------------------------------------------------------- /public/markerwithlabel.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @name MarkerWithLabel for V3 3 | * @version 1.1.10 [April 8, 2014] 4 | * @author Gary Little (inspired by code from Marc Ridey of Google). 5 | * @copyright Copyright 2012 Gary Little [gary at luxcentral.com] 6 | * @fileoverview MarkerWithLabel extends the Google Maps JavaScript API V3 7 | * google.maps.Marker class. 8 | *

9 | * MarkerWithLabel allows you to define markers with associated labels. As you would expect, 10 | * if the marker is draggable, so too will be the label. In addition, a marker with a label 11 | * responds to all mouse events in the same manner as a regular marker. It also fires mouse 12 | * events and "property changed" events just as a regular marker would. Version 1.1 adds 13 | * support for the raiseOnDrag feature introduced in API V3.3. 14 | *

15 | * If you drag a marker by its label, you can cancel the drag and return the marker to its 16 | * original position by pressing the Esc key. This doesn't work if you drag the marker 17 | * itself because this feature is not (yet) supported in the google.maps.Marker class. 18 | */ 19 | 20 | /*! 21 | * 22 | * Licensed under the Apache License, Version 2.0 (the "License"); 23 | * you may not use this file except in compliance with the License. 24 | * You may obtain a copy of the License at 25 | * 26 | * http://www.apache.org/licenses/LICENSE-2.0 27 | * 28 | * Unless required by applicable law or agreed to in writing, software 29 | * distributed under the License is distributed on an "AS IS" BASIS, 30 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 31 | * See the License for the specific language governing permissions and 32 | * limitations under the License. 33 | */ 34 | 35 | /*jslint browser:true */ 36 | /*global document,google */ 37 | 38 | /** 39 | * @param {Function} childCtor Child class. 40 | * @param {Function} parentCtor Parent class. 41 | * @private 42 | */ 43 | function inherits(childCtor, parentCtor) { 44 | /* @constructor */ 45 | function tempCtor() {} 46 | tempCtor.prototype = parentCtor.prototype; 47 | childCtor.superClass_ = parentCtor.prototype; 48 | childCtor.prototype = new tempCtor(); 49 | /* @override */ 50 | childCtor.prototype.constructor = childCtor; 51 | } 52 | 53 | /** 54 | * This constructor creates a label and associates it with a marker. 55 | * It is for the private use of the MarkerWithLabel class. 56 | * @constructor 57 | * @param {Marker} marker The marker with which the label is to be associated. 58 | * @param {string} crossURL The URL of the cross image =. 59 | * @param {string} handCursor The URL of the hand cursor. 60 | * @private 61 | */ 62 | function MarkerLabel_(marker, crossURL, handCursorURL) { 63 | this.marker_ = marker; 64 | this.handCursorURL_ = marker.handCursorURL; 65 | 66 | this.labelDiv_ = document.createElement("div"); 67 | this.labelDiv_.style.cssText = "position: absolute; overflow: hidden;"; 68 | 69 | // Set up the DIV for handling mouse events in the label. This DIV forms a transparent veil 70 | // in the "overlayMouseTarget" pane, a veil that covers just the label. This is done so that 71 | // events can be captured even if the label is in the shadow of a google.maps.InfoWindow. 72 | // Code is included here to ensure the veil is always exactly the same size as the label. 73 | this.eventDiv_ = document.createElement("div"); 74 | this.eventDiv_.style.cssText = this.labelDiv_.style.cssText; 75 | 76 | // This is needed for proper behavior on MSIE: 77 | this.eventDiv_.setAttribute("onselectstart", "return false;"); 78 | this.eventDiv_.setAttribute("ondragstart", "return false;"); 79 | 80 | // Get the DIV for the "X" to be displayed when the marker is raised. 81 | this.crossDiv_ = MarkerLabel_.getSharedCross(crossURL); 82 | } 83 | 84 | inherits(MarkerLabel_, google.maps.OverlayView); 85 | 86 | /** 87 | * Returns the DIV for the cross used when dragging a marker when the 88 | * raiseOnDrag parameter set to true. One cross is shared with all markers. 89 | * @param {string} crossURL The URL of the cross image =. 90 | * @private 91 | */ 92 | MarkerLabel_.getSharedCross = function (crossURL) { 93 | var div; 94 | if (typeof MarkerLabel_.getSharedCross.crossDiv === "undefined") { 95 | div = document.createElement("img"); 96 | div.style.cssText = "position: absolute; z-index: 1000002; display: none;"; 97 | // Hopefully Google never changes the standard "X" attributes: 98 | div.style.marginLeft = "-8px"; 99 | div.style.marginTop = "-9px"; 100 | div.src = crossURL; 101 | MarkerLabel_.getSharedCross.crossDiv = div; 102 | } 103 | return MarkerLabel_.getSharedCross.crossDiv; 104 | }; 105 | 106 | /** 107 | * Adds the DIV representing the label to the DOM. This method is called 108 | * automatically when the marker's setMap method is called. 109 | * @private 110 | */ 111 | MarkerLabel_.prototype.onAdd = function () { 112 | var me = this; 113 | var cMouseIsDown = false; 114 | var cDraggingLabel = false; 115 | var cSavedZIndex; 116 | var cLatOffset, cLngOffset; 117 | var cIgnoreClick; 118 | var cRaiseEnabled; 119 | var cStartPosition; 120 | var cStartCenter; 121 | // Constants: 122 | var cRaiseOffset = 20; 123 | var cDraggingCursor = "url(" + this.handCursorURL_ + ")"; 124 | 125 | // Stops all processing of an event. 126 | // 127 | var cAbortEvent = function (e) { 128 | if (e.preventDefault) { 129 | e.preventDefault(); 130 | } 131 | e.cancelBubble = true; 132 | if (e.stopPropagation) { 133 | e.stopPropagation(); 134 | } 135 | }; 136 | 137 | var cStopBounce = function () { 138 | me.marker_.setAnimation(null); 139 | }; 140 | 141 | this.getPanes().overlayImage.appendChild(this.labelDiv_); 142 | this.getPanes().overlayMouseTarget.appendChild(this.eventDiv_); 143 | // One cross is shared with all markers, so only add it once: 144 | if (typeof MarkerLabel_.getSharedCross.processed === "undefined") { 145 | this.getPanes().overlayImage.appendChild(this.crossDiv_); 146 | MarkerLabel_.getSharedCross.processed = true; 147 | } 148 | 149 | this.listeners_ = [ 150 | google.maps.event.addDomListener(this.eventDiv_, "mouseover", function (e) { 151 | if (me.marker_.getDraggable() || me.marker_.getClickable()) { 152 | this.style.cursor = "pointer"; 153 | google.maps.event.trigger(me.marker_, "mouseover", e); 154 | } 155 | }), 156 | google.maps.event.addDomListener(this.eventDiv_, "mouseout", function (e) { 157 | if ((me.marker_.getDraggable() || me.marker_.getClickable()) && !cDraggingLabel) { 158 | this.style.cursor = me.marker_.getCursor(); 159 | google.maps.event.trigger(me.marker_, "mouseout", e); 160 | } 161 | }), 162 | google.maps.event.addDomListener(this.eventDiv_, "mousedown", function (e) { 163 | cDraggingLabel = false; 164 | if (me.marker_.getDraggable()) { 165 | cMouseIsDown = true; 166 | this.style.cursor = cDraggingCursor; 167 | } 168 | if (me.marker_.getDraggable() || me.marker_.getClickable()) { 169 | google.maps.event.trigger(me.marker_, "mousedown", e); 170 | cAbortEvent(e); // Prevent map pan when starting a drag on a label 171 | } 172 | }), 173 | google.maps.event.addDomListener(document, "mouseup", function (mEvent) { 174 | var position; 175 | if (cMouseIsDown) { 176 | cMouseIsDown = false; 177 | me.eventDiv_.style.cursor = "pointer"; 178 | google.maps.event.trigger(me.marker_, "mouseup", mEvent); 179 | } 180 | if (cDraggingLabel) { 181 | if (cRaiseEnabled) { // Lower the marker & label 182 | position = me.getProjection().fromLatLngToDivPixel(me.marker_.getPosition()); 183 | position.y += cRaiseOffset; 184 | me.marker_.setPosition(me.getProjection().fromDivPixelToLatLng(position)); 185 | // This is not the same bouncing style as when the marker portion is dragged, 186 | // but it will have to do: 187 | try { // Will fail if running Google Maps API earlier than V3.3 188 | me.marker_.setAnimation(google.maps.Animation.BOUNCE); 189 | setTimeout(cStopBounce, 1406); 190 | } catch (e) {} 191 | } 192 | me.crossDiv_.style.display = "none"; 193 | me.marker_.setZIndex(cSavedZIndex); 194 | cIgnoreClick = true; // Set flag to ignore the click event reported after a label drag 195 | cDraggingLabel = false; 196 | mEvent.latLng = me.marker_.getPosition(); 197 | google.maps.event.trigger(me.marker_, "dragend", mEvent); 198 | } 199 | }), 200 | google.maps.event.addListener(me.marker_.getMap(), "mousemove", function (mEvent) { 201 | var position; 202 | if (cMouseIsDown) { 203 | if (cDraggingLabel) { 204 | // Change the reported location from the mouse position to the marker position: 205 | mEvent.latLng = new google.maps.LatLng(mEvent.latLng.lat() - cLatOffset, mEvent.latLng.lng() - cLngOffset); 206 | position = me.getProjection().fromLatLngToDivPixel(mEvent.latLng); 207 | if (cRaiseEnabled) { 208 | me.crossDiv_.style.left = position.x + "px"; 209 | me.crossDiv_.style.top = position.y + "px"; 210 | me.crossDiv_.style.display = ""; 211 | position.y -= cRaiseOffset; 212 | } 213 | me.marker_.setPosition(me.getProjection().fromDivPixelToLatLng(position)); 214 | if (cRaiseEnabled) { // Don't raise the veil; this hack needed to make MSIE act properly 215 | me.eventDiv_.style.top = (position.y + cRaiseOffset) + "px"; 216 | } 217 | google.maps.event.trigger(me.marker_, "drag", mEvent); 218 | } else { 219 | // Calculate offsets from the click point to the marker position: 220 | cLatOffset = mEvent.latLng.lat() - me.marker_.getPosition().lat(); 221 | cLngOffset = mEvent.latLng.lng() - me.marker_.getPosition().lng(); 222 | cSavedZIndex = me.marker_.getZIndex(); 223 | cStartPosition = me.marker_.getPosition(); 224 | cStartCenter = me.marker_.getMap().getCenter(); 225 | cRaiseEnabled = me.marker_.get("raiseOnDrag"); 226 | cDraggingLabel = true; 227 | me.marker_.setZIndex(1000000); // Moves the marker & label to the foreground during a drag 228 | mEvent.latLng = me.marker_.getPosition(); 229 | google.maps.event.trigger(me.marker_, "dragstart", mEvent); 230 | } 231 | } 232 | }), 233 | google.maps.event.addDomListener(document, "keydown", function (e) { 234 | if (cDraggingLabel) { 235 | if (e.keyCode === 27) { // Esc key 236 | cRaiseEnabled = false; 237 | me.marker_.setPosition(cStartPosition); 238 | me.marker_.getMap().setCenter(cStartCenter); 239 | google.maps.event.trigger(document, "mouseup", e); 240 | } 241 | } 242 | }), 243 | google.maps.event.addDomListener(this.eventDiv_, "click", function (e) { 244 | if (me.marker_.getDraggable() || me.marker_.getClickable()) { 245 | if (cIgnoreClick) { // Ignore the click reported when a label drag ends 246 | cIgnoreClick = false; 247 | } else { 248 | google.maps.event.trigger(me.marker_, "click", e); 249 | cAbortEvent(e); // Prevent click from being passed on to map 250 | } 251 | } 252 | }), 253 | google.maps.event.addDomListener(this.eventDiv_, "dblclick", function (e) { 254 | if (me.marker_.getDraggable() || me.marker_.getClickable()) { 255 | google.maps.event.trigger(me.marker_, "dblclick", e); 256 | cAbortEvent(e); // Prevent map zoom when double-clicking on a label 257 | } 258 | }), 259 | google.maps.event.addListener(this.marker_, "dragstart", function (mEvent) { 260 | if (!cDraggingLabel) { 261 | cRaiseEnabled = this.get("raiseOnDrag"); 262 | } 263 | }), 264 | google.maps.event.addListener(this.marker_, "drag", function (mEvent) { 265 | if (!cDraggingLabel) { 266 | if (cRaiseEnabled) { 267 | me.setPosition(cRaiseOffset); 268 | // During a drag, the marker's z-index is temporarily set to 1000000 to 269 | // ensure it appears above all other markers. Also set the label's z-index 270 | // to 1000000 (plus or minus 1 depending on whether the label is supposed 271 | // to be above or below the marker). 272 | me.labelDiv_.style.zIndex = 1000000 + (this.get("labelInBackground") ? -1 : +1); 273 | } 274 | } 275 | }), 276 | google.maps.event.addListener(this.marker_, "dragend", function (mEvent) { 277 | if (!cDraggingLabel) { 278 | if (cRaiseEnabled) { 279 | me.setPosition(0); // Also restores z-index of label 280 | } 281 | } 282 | }), 283 | google.maps.event.addListener(this.marker_, "position_changed", function () { 284 | me.setPosition(); 285 | }), 286 | google.maps.event.addListener(this.marker_, "zindex_changed", function () { 287 | me.setZIndex(); 288 | }), 289 | google.maps.event.addListener(this.marker_, "visible_changed", function () { 290 | me.setVisible(); 291 | }), 292 | google.maps.event.addListener(this.marker_, "labelvisible_changed", function () { 293 | me.setVisible(); 294 | }), 295 | google.maps.event.addListener(this.marker_, "title_changed", function () { 296 | me.setTitle(); 297 | }), 298 | google.maps.event.addListener(this.marker_, "labelcontent_changed", function () { 299 | me.setContent(); 300 | }), 301 | google.maps.event.addListener(this.marker_, "labelanchor_changed", function () { 302 | me.setAnchor(); 303 | }), 304 | google.maps.event.addListener(this.marker_, "labelclass_changed", function () { 305 | me.setStyles(); 306 | }), 307 | google.maps.event.addListener(this.marker_, "labelstyle_changed", function () { 308 | me.setStyles(); 309 | }) 310 | ]; 311 | }; 312 | 313 | /** 314 | * Removes the DIV for the label from the DOM. It also removes all event handlers. 315 | * This method is called automatically when the marker's setMap(null) 316 | * method is called. 317 | * @private 318 | */ 319 | MarkerLabel_.prototype.onRemove = function () { 320 | var i; 321 | this.labelDiv_.parentNode.removeChild(this.labelDiv_); 322 | this.eventDiv_.parentNode.removeChild(this.eventDiv_); 323 | 324 | // Remove event listeners: 325 | for (i = 0; i < this.listeners_.length; i++) { 326 | google.maps.event.removeListener(this.listeners_[i]); 327 | } 328 | }; 329 | 330 | /** 331 | * Draws the label on the map. 332 | * @private 333 | */ 334 | MarkerLabel_.prototype.draw = function () { 335 | this.setContent(); 336 | this.setTitle(); 337 | this.setStyles(); 338 | }; 339 | 340 | /** 341 | * Sets the content of the label. 342 | * The content can be plain text or an HTML DOM node. 343 | * @private 344 | */ 345 | MarkerLabel_.prototype.setContent = function () { 346 | var content = this.marker_.get("labelContent"); 347 | if (typeof content.nodeType === "undefined") { 348 | this.labelDiv_.innerHTML = content; 349 | this.eventDiv_.innerHTML = this.labelDiv_.innerHTML; 350 | } else { 351 | this.labelDiv_.innerHTML = ""; // Remove current content 352 | this.labelDiv_.appendChild(content); 353 | content = content.cloneNode(true); 354 | this.eventDiv_.innerHTML = ""; // Remove current content 355 | this.eventDiv_.appendChild(content); 356 | } 357 | }; 358 | 359 | /** 360 | * Sets the content of the tool tip for the label. It is 361 | * always set to be the same as for the marker itself. 362 | * @private 363 | */ 364 | MarkerLabel_.prototype.setTitle = function () { 365 | this.eventDiv_.title = this.marker_.getTitle() || ""; 366 | }; 367 | 368 | /** 369 | * Sets the style of the label by setting the style sheet and applying 370 | * other specific styles requested. 371 | * @private 372 | */ 373 | MarkerLabel_.prototype.setStyles = function () { 374 | var i, labelStyle; 375 | 376 | // Apply style values from the style sheet defined in the labelClass parameter: 377 | this.labelDiv_.className = this.marker_.get("labelClass"); 378 | this.eventDiv_.className = this.labelDiv_.className; 379 | 380 | // Clear existing inline style values: 381 | this.labelDiv_.style.cssText = ""; 382 | this.eventDiv_.style.cssText = ""; 383 | // Apply style values defined in the labelStyle parameter: 384 | labelStyle = this.marker_.get("labelStyle"); 385 | for (i in labelStyle) { 386 | if (labelStyle.hasOwnProperty(i)) { 387 | this.labelDiv_.style[i] = labelStyle[i]; 388 | this.eventDiv_.style[i] = labelStyle[i]; 389 | } 390 | } 391 | this.setMandatoryStyles(); 392 | }; 393 | 394 | /** 395 | * Sets the mandatory styles to the DIV representing the label as well as to the 396 | * associated event DIV. This includes setting the DIV position, z-index, and visibility. 397 | * @private 398 | */ 399 | MarkerLabel_.prototype.setMandatoryStyles = function () { 400 | this.labelDiv_.style.position = "absolute"; 401 | this.labelDiv_.style.overflow = "hidden"; 402 | // Make sure the opacity setting causes the desired effect on MSIE: 403 | if (typeof this.labelDiv_.style.opacity !== "undefined" && this.labelDiv_.style.opacity !== "") { 404 | this.labelDiv_.style.MsFilter = "\"progid:DXImageTransform.Microsoft.Alpha(opacity=" + (this.labelDiv_.style.opacity * 100) + ")\""; 405 | this.labelDiv_.style.filter = "alpha(opacity=" + (this.labelDiv_.style.opacity * 100) + ")"; 406 | } 407 | 408 | this.eventDiv_.style.position = this.labelDiv_.style.position; 409 | this.eventDiv_.style.overflow = this.labelDiv_.style.overflow; 410 | this.eventDiv_.style.opacity = 0.01; // Don't use 0; DIV won't be clickable on MSIE 411 | this.eventDiv_.style.MsFilter = "\"progid:DXImageTransform.Microsoft.Alpha(opacity=1)\""; 412 | this.eventDiv_.style.filter = "alpha(opacity=1)"; // For MSIE 413 | 414 | this.setAnchor(); 415 | this.setPosition(); // This also updates z-index, if necessary. 416 | this.setVisible(); 417 | }; 418 | 419 | /** 420 | * Sets the anchor point of the label. 421 | * @private 422 | */ 423 | MarkerLabel_.prototype.setAnchor = function () { 424 | var anchor = this.marker_.get("labelAnchor"); 425 | this.labelDiv_.style.marginLeft = -anchor.x + "px"; 426 | this.labelDiv_.style.marginTop = -anchor.y + "px"; 427 | this.eventDiv_.style.marginLeft = -anchor.x + "px"; 428 | this.eventDiv_.style.marginTop = -anchor.y + "px"; 429 | }; 430 | 431 | /** 432 | * Sets the position of the label. The z-index is also updated, if necessary. 433 | * @private 434 | */ 435 | MarkerLabel_.prototype.setPosition = function (yOffset) { 436 | var position = this.getProjection().fromLatLngToDivPixel(this.marker_.getPosition()); 437 | if (typeof yOffset === "undefined") { 438 | yOffset = 0; 439 | } 440 | this.labelDiv_.style.left = Math.round(position.x) + "px"; 441 | this.labelDiv_.style.top = Math.round(position.y - yOffset) + "px"; 442 | this.eventDiv_.style.left = this.labelDiv_.style.left; 443 | this.eventDiv_.style.top = this.labelDiv_.style.top; 444 | 445 | this.setZIndex(); 446 | }; 447 | 448 | /** 449 | * Sets the z-index of the label. If the marker's z-index property has not been defined, the z-index 450 | * of the label is set to the vertical coordinate of the label. This is in keeping with the default 451 | * stacking order for Google Maps: markers to the south are in front of markers to the north. 452 | * @private 453 | */ 454 | MarkerLabel_.prototype.setZIndex = function () { 455 | var zAdjust = (this.marker_.get("labelInBackground") ? -1 : +1); 456 | if (typeof this.marker_.getZIndex() === "undefined") { 457 | this.labelDiv_.style.zIndex = parseInt(this.labelDiv_.style.top, 10) + zAdjust; 458 | this.eventDiv_.style.zIndex = this.labelDiv_.style.zIndex; 459 | } else { 460 | this.labelDiv_.style.zIndex = this.marker_.getZIndex() + zAdjust; 461 | this.eventDiv_.style.zIndex = this.labelDiv_.style.zIndex; 462 | } 463 | }; 464 | 465 | /** 466 | * Sets the visibility of the label. The label is visible only if the marker itself is 467 | * visible (i.e., its visible property is true) and the labelVisible property is true. 468 | * @private 469 | */ 470 | MarkerLabel_.prototype.setVisible = function () { 471 | if (this.marker_.get("labelVisible")) { 472 | this.labelDiv_.style.display = this.marker_.getVisible() ? "block" : "none"; 473 | } else { 474 | this.labelDiv_.style.display = "none"; 475 | } 476 | this.eventDiv_.style.display = this.labelDiv_.style.display; 477 | }; 478 | 479 | /** 480 | * @name MarkerWithLabelOptions 481 | * @class This class represents the optional parameter passed to the {@link MarkerWithLabel} constructor. 482 | * The properties available are the same as for google.maps.Marker with the addition 483 | * of the properties listed below. To change any of these additional properties after the labeled 484 | * marker has been created, call google.maps.Marker.set(propertyName, propertyValue). 485 | *

486 | * When any of these properties changes, a property changed event is fired. The names of these 487 | * events are derived from the name of the property and are of the form propertyname_changed. 488 | * For example, if the content of the label changes, a labelcontent_changed event 489 | * is fired. 490 | *

491 | * @property {string|Node} [labelContent] The content of the label (plain text or an HTML DOM node). 492 | * @property {Point} [labelAnchor] By default, a label is drawn with its anchor point at (0,0) so 493 | * that its top left corner is positioned at the anchor point of the associated marker. Use this 494 | * property to change the anchor point of the label. For example, to center a 50px-wide label 495 | * beneath a marker, specify a labelAnchor of google.maps.Point(25, 0). 496 | * (Note: x-values increase to the right and y-values increase to the top.) 497 | * @property {string} [labelClass] The name of the CSS class defining the styles for the label. 498 | * Note that style values for position, overflow, top, 499 | * left, zIndex, display, marginLeft, and 500 | * marginTop are ignored; these styles are for internal use only. 501 | * @property {Object} [labelStyle] An object literal whose properties define specific CSS 502 | * style values to be applied to the label. Style values defined here override those that may 503 | * be defined in the labelClass style sheet. If this property is changed after the 504 | * label has been created, all previously set styles (except those defined in the style sheet) 505 | * are removed from the label before the new style values are applied. 506 | * Note that style values for position, overflow, top, 507 | * left, zIndex, display, marginLeft, and 508 | * marginTop are ignored; these styles are for internal use only. 509 | * @property {boolean} [labelInBackground] A flag indicating whether a label that overlaps its 510 | * associated marker should appear in the background (i.e., in a plane below the marker). 511 | * The default is false, which causes the label to appear in the foreground. 512 | * @property {boolean} [labelVisible] A flag indicating whether the label is to be visible. 513 | * The default is true. Note that even if labelVisible is 514 | * true, the label will not be visible unless the associated marker is also 515 | * visible (i.e., unless the marker's visible property is true). 516 | * @property {boolean} [raiseOnDrag] A flag indicating whether the label and marker are to be 517 | * raised when the marker is dragged. The default is true. If a draggable marker is 518 | * being created and a version of Google Maps API earlier than V3.3 is being used, this property 519 | * must be set to false. 520 | * @property {boolean} [optimized] A flag indicating whether rendering is to be optimized for the 521 | * marker. Important: The optimized rendering technique is not supported by MarkerWithLabel, 522 | * so the value of this parameter is always forced to false. 523 | * @property {string} [crossImage="http://maps.gstatic.com/intl/en_us/mapfiles/drag_cross_67_16.png"] 524 | * The URL of the cross image to be displayed while dragging a marker. 525 | * @property {string} [handCursor="http://maps.gstatic.com/intl/en_us/mapfiles/closedhand_8_8.cur"] 526 | * The URL of the cursor to be displayed while dragging a marker. 527 | */ 528 | /** 529 | * Creates a MarkerWithLabel with the options specified in {@link MarkerWithLabelOptions}. 530 | * @constructor 531 | * @param {MarkerWithLabelOptions} [opt_options] The optional parameters. 532 | */ 533 | function MarkerWithLabel(opt_options) { 534 | opt_options = opt_options || {}; 535 | opt_options.labelContent = opt_options.labelContent || ""; 536 | opt_options.labelAnchor = opt_options.labelAnchor || new google.maps.Point(0, 0); 537 | opt_options.labelClass = opt_options.labelClass || "markerLabels"; 538 | opt_options.labelStyle = opt_options.labelStyle || {}; 539 | opt_options.labelInBackground = opt_options.labelInBackground || false; 540 | if (typeof opt_options.labelVisible === "undefined") { 541 | opt_options.labelVisible = true; 542 | } 543 | if (typeof opt_options.raiseOnDrag === "undefined") { 544 | opt_options.raiseOnDrag = true; 545 | } 546 | if (typeof opt_options.clickable === "undefined") { 547 | opt_options.clickable = true; 548 | } 549 | if (typeof opt_options.draggable === "undefined") { 550 | opt_options.draggable = false; 551 | } 552 | if (typeof opt_options.optimized === "undefined") { 553 | opt_options.optimized = false; 554 | } 555 | opt_options.crossImage = opt_options.crossImage || "http" + (document.location.protocol === "https:" ? "s" : "") + "://maps.gstatic.com/intl/en_us/mapfiles/drag_cross_67_16.png"; 556 | opt_options.handCursor = opt_options.handCursor || "http" + (document.location.protocol === "https:" ? "s" : "") + "://maps.gstatic.com/intl/en_us/mapfiles/closedhand_8_8.cur"; 557 | opt_options.optimized = false; // Optimized rendering is not supported 558 | 559 | this.label = new MarkerLabel_(this, opt_options.crossImage, opt_options.handCursor); // Bind the label to the marker 560 | 561 | // Call the parent constructor. It calls Marker.setValues to initialize, so all 562 | // the new parameters are conveniently saved and can be accessed with get/set. 563 | // Marker.set triggers a property changed event (called "propertyname_changed") 564 | // that the marker label listens for in order to react to state changes. 565 | google.maps.Marker.apply(this, arguments); 566 | } 567 | 568 | inherits(MarkerWithLabel, google.maps.Marker); 569 | 570 | /** 571 | * Overrides the standard Marker setMap function. 572 | * @param {Map} theMap The map to which the marker is to be added. 573 | * @private 574 | */ 575 | MarkerWithLabel.prototype.setMap = function (theMap) { 576 | 577 | // Call the inherited function... 578 | google.maps.Marker.prototype.setMap.apply(this, arguments); 579 | 580 | // ... then deal with the label: 581 | this.label.setMap(theMap); 582 | }; -------------------------------------------------------------------------------- /server.js: -------------------------------------------------------------------------------- 1 | require("babel-core/register")({ presets: ['es2015', 'react', 'stage-1'] }) 2 | 3 | var express = require('express'), 4 | path = require('path'), 5 | app = express(), 6 | ip = '0.0.0.0', 7 | port = 3000, 8 | bodyParser = require('body-parser') 9 | 10 | // Include static assets 11 | app.use(express.static(path.join(__dirname, 'public'))); 12 | 13 | app.set('views', path.join(__dirname, 'views')); 14 | app.set('view engine', 'ejs'); 15 | 16 | // Set up routing 17 | require('./app/routes/core-routes.js')(app); 18 | 19 | // Set 404 20 | app.get('*', function(req, res) { 21 | res.json({ 22 | "route": "Sorry this page does not exist!" 23 | }); 24 | }); 25 | 26 | app.listen(port, ip); 27 | 28 | console.log('Server is Up and Running at Port : ' + port); -------------------------------------------------------------------------------- /views/index.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | <%- reactTitle %>Modern History of Humanity 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 39 | 40 | 41 | 42 | 43 |

44 |
45 |
46 |
47 |

48 | 49 | Home 50 | 51 |

52 |
53 |
54 | 58 |
59 |
60 |

A History of Modern Humanity
The Post-Industrial Revolution World

61 |

A demo app to display a list of important events in Human history based on Wikipedia data. Uses Three.js, axios and React.js

62 |

Select a date range below, or read how this was built

63 |
64 |
<%- reactOutput %>
65 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | --------------------------------------------------------------------------------