Created
June 6, 2017 01:47
-
-
Save gregnb/b2c1ec35d9f19add404c7f1d00d6b7a1 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import React from 'react'; | |
import { apiRequest, apiError, detectAndroid } from './Utils'; | |
let FrontendComponent = ComposedComponent => { | |
class FrontendClass extends ComposedComponent { | |
constructor() { | |
super(); | |
/* global vars needed */ | |
this.metaAttribute = 'data-stylesheet-dynamic'; | |
this.dynamicStyleSheets = []; | |
this.loadedStyleSheets = []; | |
const initialState = { | |
dataRefresh : 0, | |
cssLoaded : false | |
}; | |
this.state = (this.state) ? Object.assign(this.state, initialState) : initialState; | |
} | |
componentDidMount() { | |
if(this.metaConfig) | |
this.updateMetaData(); | |
if(super.componentDidMount) | |
super.componentDidMount(); | |
} | |
shouldComponentUpdate(nextProps, nextState, nextContext) { | |
return (this.context != nextContext || this.props != nextProps || this.state != this.nextState) | |
? true | |
: false; | |
} | |
/* | |
* API CALL | |
*/ | |
dataRequest(url, data, callback) { | |
let self = this; | |
apiRequest(url, data).then(function(result) { | |
if(callback) | |
callback(result); | |
}).catch(function(error) { | |
apiError(error); | |
}); | |
} | |
toggleState(option, toggle) { | |
let newstate = {}; | |
newstate[option] = toggle; | |
this.setState(newstate); | |
} | |
refreshData() { | |
this.setState({ dataRefresh:this.state.dataRefresh+1 }); | |
} | |
render() { | |
/* | |
* FOUC - Flash of Unstyled Content (https://en.wikipedia.org/wiki/Flash_of_unstyled_content) | |
* We tag an onload/onerror event to the dynamic injected stylesheets. If onload/onload count matches total sheets then | |
* we know everything was attempted | |
* | |
* | |
* Onload: Firefox 9+, IE6+, Edge, Chrome 19+, Safari 6.01+ | |
* OnError: Firefox 9+, IE6+, Edge, Chrome 19+, Safari 6.0+, | |
* | |
* No Android support, how do we solve that? Skip for now. | |
* http://www.backalleycoder.com/2011/03/20/link-tag-css-stylesheet-load-event/ | |
* | |
* | |
* Browser Support: https://pie.gd/test/script-link-events/ | |
*/ | |
if(!this.state.cssLoaded && this.metaConfig && !detectAndroid()) | |
return false; | |
return super.render(); | |
} | |
/* | |
* document.head [meta,title,scripts,links] routines | |
*/ | |
getAttributes(el) { | |
let attributes = Array.prototype.slice.call(el.attributes); | |
let data = {}; | |
for(let i = 0; i < attributes.length; i++) { | |
let obj = attributes[i]; | |
data[obj.nodeName] = obj.nodeValue; | |
} | |
return (data) ? data : null; | |
} | |
deleteMetaData() { | |
let metaData = this.metaConfig(); | |
let configSheets = metaData.link | |
if(!configSheets) | |
return; | |
let headEl = document.head || document.querySelector("head"); | |
let styleSheets = this.listStyleSheets(); | |
configSheets.forEach((link, index) => { | |
styleSheets.forEach((sheet, index) => { | |
if(sheet.getAttribute('href') == link.href) | |
headEl.removeChild(sheet); | |
}); | |
}); | |
} | |
listStyleSheets() { | |
let headEl = document.head || document.querySelector("head"); | |
let checkEl = headEl.querySelectorAll('link['+this.metaAttribute+']'); | |
let sheets = []; | |
if(checkEl) | |
for(let i = 0; i < checkEl.length; i++) | |
sheets.push(checkEl[i]); | |
return sheets; | |
} | |
checkStylesheetsLoaded() { | |
if(this.dynamicStyleSheets.length == this.loadedStyleSheets.length) { | |
console.log("stylesheets all loaded!"); | |
//this.setState({ cssLoaded: true }); | |
} else { | |
console.log("still not loaded"); | |
} | |
console.log(this.state); | |
} | |
updateMetaData(data) { | |
let metaData = (this.metaConfig) ? this.metaConfig() : null; | |
let headEl = document.head || document.querySelector("head"); | |
let self = this; | |
/* diffing maybe not needed? lol */ | |
if(data) | |
//metaData = objDiff(data,metaData); | |
metaData = data; | |
document.title = metaData.title || document.title; | |
if(metaData.meta && metaData.meta.length) { | |
let nodes = headEl.querySelectorAll('meta'); | |
let tags = (nodes) ? Array.prototype.slice.call(nodes) : null; | |
let oldTags = []; | |
let newTags = []; | |
metaData.meta.forEach((meta, index) => { | |
let newEl = document.createElement('meta'); | |
for(let prop in meta) | |
if(prop != 'content') | |
newEl.setAttribute(prop, meta[prop]); | |
tags.forEach((tag) => { | |
var tagCheck = tag.cloneNode(); | |
tagCheck.removeAttribute('content'); | |
if(tagCheck.isEqualNode(newEl)) | |
oldTags.push(tag); | |
}); | |
if(meta.content) | |
newEl.setAttribute('content', meta.content); | |
newTags.push(newEl); | |
}); | |
/* delete old/duplicate tags */ | |
oldTags.forEach((tag) => { | |
headEl.removeChild(tag); | |
}); | |
/* set new tags */ | |
newTags.forEach(function(tag) { | |
headEl.appendChild(tag); | |
}); | |
} | |
/* | |
* get list of current stylesheets. | |
* if exists, don't append to <head>. | |
* | |
* if no stylesheets are provided just set cssLoaded : true | |
* | |
*/ | |
if(metaData.link) { | |
let stylesheets = this.listStyleSheets(); | |
let appendCheck = 0; | |
this.loadedStyleSheets = []; | |
this.dynamicStyleSheets = metaData.link; | |
this.setState({ cssLoaded: false }); | |
metaData.link.forEach((link, index) => { | |
let newEl = document.createElement('link'); | |
let append = 1; | |
stylesheets.forEach((sheet, index) => { | |
if(sheet.getAttribute('href') == link.href) | |
append = 0; | |
}); | |
if(!append) { | |
appendCheck++; | |
return; | |
} | |
for(let prop in link) | |
newEl.setAttribute(prop, link[prop]); | |
/* set onLoad/onError for stylesheets */ | |
let cssLoad = () => { | |
let link = newEl.getAttribute('href'); | |
this.loadedStyleSheets.push(link); | |
if(this.dynamicStyleSheets.length == this.loadedStyleSheets.length) | |
self.setState({ cssLoaded: true }); | |
}; | |
newEl.onload = cssLoad; | |
newEl.onerror = cssLoad; | |
newEl.setAttribute(this.metaAttribute, true); | |
headEl.appendChild(newEl); | |
}); | |
/* | |
* incase a developer tries to load in already loaded CSS stylesheets we're going to set cssLoaded to true | |
*/ | |
if(appendCheck == metaData.link.length) | |
this.setState({ cssLoaded: true }); | |
} else { | |
this.setState({ cssLoaded: true }); | |
} | |
} | |
} | |
FrontendClass.contextTypes = { | |
params: React.PropTypes.array, | |
root: React.PropTypes.string, | |
path: React.PropTypes.string, | |
queryString: React.PropTypes.string, | |
changeRoute: React.PropTypes.func, | |
routerHistory: React.PropTypes.func | |
}; | |
FrontendClass.frontendComponent = true; | |
return FrontendClass; | |
} | |
export default FrontendComponent; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment