mirror of
https://github.com/ascribe/onion.git
synced 2024-12-22 09:23:13 +01:00
Merge pull request #26 from ascribe/AD-56-add-social-share-functionality
Add social sharing for Facebook and Twitter
This commit is contained in:
commit
9a706ebb7d
@ -30,6 +30,7 @@ import GoogleAnalyticsHandler from './third_party/ga';
|
||||
import RavenHandler from './third_party/raven';
|
||||
import IntercomHandler from './third_party/intercom';
|
||||
import NotificationsHandler from './third_party/notifications';
|
||||
import FacebookHandler from './third_party/facebook';
|
||||
/* eslint-enable */
|
||||
|
||||
initLogging();
|
||||
|
@ -21,13 +21,13 @@ let CollapsibleButton = React.createClass({
|
||||
this.setState({expanded: !this.state.expanded});
|
||||
},
|
||||
render() {
|
||||
let isVisible = (this.state.expanded) ? '' : 'invisible';
|
||||
let isHidden = (this.state.expanded) ? '' : 'hidden';
|
||||
return (
|
||||
<span>
|
||||
<span onClick={this.handleToggle}>
|
||||
{this.props.button}
|
||||
</span>
|
||||
<div ref='panel' className={isVisible}>
|
||||
<div ref='panel' className={isHidden}>
|
||||
{this.props.panel}
|
||||
</div>
|
||||
</span>
|
||||
|
@ -7,10 +7,18 @@ import Glyphicon from 'react-bootstrap/lib/Glyphicon';
|
||||
|
||||
import MediaPlayer from './../ascribe_media/media_player';
|
||||
|
||||
import FacebookShareButton from '../ascribe_social_share/facebook_share_button';
|
||||
import TwitterShareButton from '../ascribe_social_share/twitter_share_button';
|
||||
|
||||
import CollapsibleButton from './../ascribe_collapsible/collapsible_button';
|
||||
|
||||
import AclProxy from '../acl_proxy';
|
||||
|
||||
import UserActions from '../../actions/user_actions';
|
||||
import UserStore from '../../stores/user_store';
|
||||
|
||||
import { mergeOptions } from '../../utils/general_utils.js';
|
||||
import { getLangText } from '../../utils/lang_utils.js';
|
||||
|
||||
const EMBED_IFRAME_HEIGHT = {
|
||||
video: 315,
|
||||
@ -24,10 +32,17 @@ let MediaContainer = React.createClass({
|
||||
},
|
||||
|
||||
getInitialState() {
|
||||
return {timerId: null};
|
||||
return mergeOptions(
|
||||
UserStore.getState(),
|
||||
{
|
||||
timerId: null
|
||||
});
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
UserStore.listen(this.onChange);
|
||||
UserActions.fetchCurrentUser();
|
||||
|
||||
if (!this.props.content.digital_work) {
|
||||
return;
|
||||
}
|
||||
@ -45,19 +60,32 @@ let MediaContainer = React.createClass({
|
||||
},
|
||||
|
||||
componentWillUnmount() {
|
||||
UserStore.unlisten(this.onChange);
|
||||
|
||||
window.clearInterval(this.state.timerId);
|
||||
},
|
||||
|
||||
onChange(state) {
|
||||
this.setState(state);
|
||||
},
|
||||
|
||||
render() {
|
||||
let thumbnail = this.props.content.thumbnail.thumbnail_sizes && this.props.content.thumbnail.thumbnail_sizes['600x600'] ?
|
||||
this.props.content.thumbnail.thumbnail_sizes['600x600'] : this.props.content.thumbnail.url_safe;
|
||||
let mimetype = this.props.content.digital_work.mime;
|
||||
const { content } = this.props;
|
||||
// Pieces and editions are joined to the user by a foreign key in the database, so
|
||||
// the information in content will be updated if a user updates their username.
|
||||
// We also force uniqueness of usernames, so this check is safe to dtermine if the
|
||||
// content was registered by the current user.
|
||||
const didUserRegisterContent = this.state.currentUser && (this.state.currentUser.username === content.user_registered);
|
||||
|
||||
let thumbnail = content.thumbnail.thumbnail_sizes && content.thumbnail.thumbnail_sizes['600x600'] ?
|
||||
content.thumbnail.thumbnail_sizes['600x600'] : content.thumbnail.url_safe;
|
||||
let mimetype = content.digital_work.mime;
|
||||
let embed = null;
|
||||
let extraData = null;
|
||||
let isEmbedDisabled = mimetype === 'video' && this.props.content.digital_work.isEncoding !== undefined && this.props.content.digital_work.isEncoding !== 100;
|
||||
let isEmbedDisabled = mimetype === 'video' && content.digital_work.isEncoding !== undefined && content.digital_work.isEncoding !== 100;
|
||||
|
||||
if (this.props.content.digital_work.encoding_urls) {
|
||||
extraData = this.props.content.digital_work.encoding_urls.map(e => { return { url: e.url, type: e.label }; });
|
||||
if (content.digital_work.encoding_urls) {
|
||||
extraData = content.digital_work.encoding_urls.map(e => { return { url: e.url, type: e.label }; });
|
||||
}
|
||||
|
||||
if (['video', 'audio'].indexOf(mimetype) > -1) {
|
||||
@ -73,7 +101,7 @@ let MediaContainer = React.createClass({
|
||||
panel={
|
||||
<pre className="">
|
||||
{'<iframe width="560" height="' + height + '" src="https://embed.ascribe.io/content/'
|
||||
+ this.props.content.bitcoin_id + '" frameborder="0" allowfullscreen></iframe>'}
|
||||
+ content.bitcoin_id + '" frameborder="0" allowfullscreen></iframe>'}
|
||||
</pre>
|
||||
}/>
|
||||
);
|
||||
@ -83,13 +111,19 @@ let MediaContainer = React.createClass({
|
||||
<MediaPlayer
|
||||
mimetype={mimetype}
|
||||
preview={thumbnail}
|
||||
url={this.props.content.digital_work.url}
|
||||
url={content.digital_work.url}
|
||||
extraData={extraData}
|
||||
encodingStatus={this.props.content.digital_work.isEncoding} />
|
||||
encodingStatus={content.digital_work.isEncoding} />
|
||||
<p className="text-center">
|
||||
<span className="ascribe-social-button-list">
|
||||
<FacebookShareButton />
|
||||
<TwitterShareButton
|
||||
text={getLangText('Check out %s ascribed piece', didUserRegisterContent ? 'my latest' : 'this' )} />
|
||||
</span>
|
||||
|
||||
<AclProxy
|
||||
show={['video', 'audio', 'image'].indexOf(mimetype) === -1 || this.props.content.acl.acl_download}
|
||||
aclObject={this.props.content.acl}
|
||||
show={['video', 'audio', 'image'].indexOf(mimetype) === -1 || content.acl.acl_download}
|
||||
aclObject={content.acl}
|
||||
aclName="acl_download">
|
||||
<Button bsSize="xsmall" className="ascribe-margin-1px" href={this.props.content.digital_work.url} target="_blank">
|
||||
Download .{mimetype} <Glyphicon glyph="cloud-download"/>
|
||||
|
@ -3,12 +3,13 @@
|
||||
import React from 'react';
|
||||
import Q from 'q';
|
||||
|
||||
import { escapeHTML } from '../../utils/general_utils';
|
||||
|
||||
import InjectInHeadMixin from '../../mixins/inject_in_head_mixin';
|
||||
import Panel from 'react-bootstrap/lib/Panel';
|
||||
import ProgressBar from 'react-bootstrap/lib/ProgressBar';
|
||||
import AppConstants from '../../constants/application_constants.js';
|
||||
|
||||
import AppConstants from '../../constants/application_constants';
|
||||
|
||||
import { escapeHTML } from '../../utils/general_utils';
|
||||
import { InjectInHeadUtils } from '../../utils/inject_utils';
|
||||
|
||||
/**
|
||||
* This is the component that implements display-specific functionality.
|
||||
@ -54,15 +55,13 @@ let Image = React.createClass({
|
||||
preview: React.PropTypes.string.isRequired
|
||||
},
|
||||
|
||||
mixins: [InjectInHeadMixin],
|
||||
|
||||
componentDidMount() {
|
||||
if(this.props.url) {
|
||||
this.inject('https://code.jquery.com/jquery-2.1.4.min.js')
|
||||
InjectInHeadUtils.inject(AppConstants.jquery.sdkUrl)
|
||||
.then(() =>
|
||||
Q.all([
|
||||
this.inject(AppConstants.baseUrl + 'static/thirdparty/shmui/shmui.css'),
|
||||
this.inject(AppConstants.baseUrl + 'static/thirdparty/shmui/jquery.shmui.js')
|
||||
InjectInHeadUtils.inject(AppConstants.shmui.cssUrl),
|
||||
InjectInHeadUtils.inject(AppConstants.shmui.sdkUrl)
|
||||
]).then(() => { window.jQuery('.shmui-ascribe').shmui(); }));
|
||||
}
|
||||
},
|
||||
@ -87,10 +86,8 @@ let Audio = React.createClass({
|
||||
url: React.PropTypes.string.isRequired
|
||||
},
|
||||
|
||||
mixins: [InjectInHeadMixin],
|
||||
|
||||
componentDidMount() {
|
||||
this.inject(AppConstants.baseUrl + 'static/thirdparty/audiojs/audiojs/audio.min.js').then(this.ready);
|
||||
InjectInHeadUtils.inject(AppConstants.audiojs.sdkUrl).then(this.ready);
|
||||
},
|
||||
|
||||
ready() {
|
||||
@ -121,7 +118,7 @@ let Video = React.createClass({
|
||||
* `false` if we failed to load the external library)
|
||||
* 2) render the cover using the `<Image />` component (because libraryLoaded is null)
|
||||
* 3) on `componentDidMount`, we load the external `css` and `js` resources using
|
||||
* the `InjectInHeadMixin`, attaching a function to `Promise.then` to change
|
||||
* the `InjectInHeadUtils`, attaching a function to `Promise.then` to change
|
||||
* `state.libraryLoaded` to true
|
||||
* 4) when the promise is succesfully resolved, we change `state.libraryLoaded` triggering
|
||||
* a re-render
|
||||
@ -139,20 +136,22 @@ let Video = React.createClass({
|
||||
encodingStatus: React.PropTypes.number
|
||||
},
|
||||
|
||||
mixins: [InjectInHeadMixin],
|
||||
|
||||
getInitialState() {
|
||||
return { libraryLoaded: null, videoMounted: false };
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
Q.all([
|
||||
this.inject('//vjs.zencdn.net/4.12/video-js.css'),
|
||||
this.inject('//vjs.zencdn.net/4.12/video.js')])
|
||||
InjectInHeadUtils.inject(AppConstants.videojs.cssUrl),
|
||||
InjectInHeadUtils.inject(AppConstants.videojs.sdkUrl)])
|
||||
.then(() => this.setState({libraryLoaded: true}))
|
||||
.fail(() => this.setState({libraryLoaded: false}));
|
||||
},
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return nextState.videoMounted === false;
|
||||
},
|
||||
|
||||
componentDidUpdate() {
|
||||
if (this.state.libraryLoaded && !this.state.videoMounted) {
|
||||
window.videojs('#mainvideo');
|
||||
@ -178,10 +177,6 @@ let Video = React.createClass({
|
||||
return html.join('\n');
|
||||
},
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return nextState.videoMounted === false;
|
||||
},
|
||||
|
||||
render() {
|
||||
if (this.state.libraryLoaded !== null) {
|
||||
return (
|
||||
|
51
js/components/ascribe_social_share/facebook_share_button.js
Normal file
51
js/components/ascribe_social_share/facebook_share_button.js
Normal file
@ -0,0 +1,51 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import AppConstants from '../../constants/application_constants';
|
||||
|
||||
import { InjectInHeadUtils } from '../../utils/inject_utils';
|
||||
|
||||
let FacebookShareButton = React.createClass({
|
||||
propTypes: {
|
||||
url: React.PropTypes.string,
|
||||
type: React.PropTypes.string
|
||||
},
|
||||
|
||||
getDefaultProps() {
|
||||
return {
|
||||
type: 'button'
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
/**
|
||||
* Ideally we would only use FB.XFBML.parse() on the component that we're
|
||||
* mounting, but doing this when we first load the FB sdk causes unpredictable behaviour.
|
||||
* The button sometimes doesn't get initialized, likely because FB hasn't properly
|
||||
* been initialized yet.
|
||||
*
|
||||
* To circumvent this, we always have the sdk parse the entire DOM on the initial load
|
||||
* (see FacebookHandler) and then use FB.XFBML.parse() on the mounting component later.
|
||||
*/
|
||||
if (!InjectInHeadUtils.isPresent('script', AppConstants.facebook.sdkUrl)) {
|
||||
InjectInHeadUtils.inject(AppConstants.facebook.sdkUrl);
|
||||
} else {
|
||||
// Parse() searches the children of the element we give it, not the element itself.
|
||||
FB.XFBML.parse(this.refs.fbShareButton.getDOMNode().parentElement);
|
||||
}
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<span
|
||||
ref="fbShareButton"
|
||||
className="fb-share-button btn btn-ascribe-social"
|
||||
data-href={this.props.url}
|
||||
data-layout={this.props.type}>
|
||||
</span>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default FacebookShareButton;
|
55
js/components/ascribe_social_share/twitter_share_button.js
Normal file
55
js/components/ascribe_social_share/twitter_share_button.js
Normal file
@ -0,0 +1,55 @@
|
||||
'use strict';
|
||||
|
||||
import React from 'react';
|
||||
|
||||
import AppConstants from '../../constants/application_constants';
|
||||
|
||||
import { InjectInHeadUtils } from '../../utils/inject_utils';
|
||||
|
||||
let TwitterShareButton = React.createClass({
|
||||
propTypes: {
|
||||
count: React.PropTypes.string,
|
||||
counturl: React.PropTypes.string,
|
||||
hashtags: React.PropTypes.string,
|
||||
size: React.PropTypes.string,
|
||||
text: React.PropTypes.string,
|
||||
url: React.PropTypes.string,
|
||||
via: React.PropTypes.string
|
||||
},
|
||||
|
||||
getDefaultProps() {
|
||||
return {
|
||||
count: 'none',
|
||||
via: 'ascribeIO'
|
||||
};
|
||||
},
|
||||
|
||||
componentDidMount() {
|
||||
InjectInHeadUtils.inject(AppConstants.twitter.sdkUrl).then(this.loadTwitterButton);
|
||||
},
|
||||
|
||||
loadTwitterButton() {
|
||||
const { count, counturl, hashtags, size, text, url, via } = this.props;
|
||||
|
||||
twttr.widgets.createShareButton(url, this.refs.twitterShareButton.getDOMNode(), {
|
||||
count,
|
||||
counturl,
|
||||
hashtags,
|
||||
size,
|
||||
text,
|
||||
via,
|
||||
dnt: true // Do not track
|
||||
});
|
||||
},
|
||||
|
||||
render() {
|
||||
return (
|
||||
<span
|
||||
ref="twitterShareButton"
|
||||
className="btn btn-ascribe-social">
|
||||
</span>
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
export default TwitterShareButton;
|
@ -1,15 +1,19 @@
|
||||
'use strict';
|
||||
|
||||
let constants = {
|
||||
//'baseUrl': 'http://localhost:8000/api/',
|
||||
//const baseUrl = 'http://localhost:8000/api/';
|
||||
|
||||
//FIXME: referring to a global variable in `window` is not
|
||||
// super pro. What if we render stuff on the server?
|
||||
// - super-bro - Senor Developer, 14th July 2015
|
||||
//'baseUrl': window.BASE_URL,
|
||||
'apiEndpoint': window.API_ENDPOINT,
|
||||
'serverUrl': window.SERVER_URL,
|
||||
'baseUrl': window.BASE_URL,
|
||||
//FIXME: referring to a global variable in `window` is not
|
||||
// super pro. What if we render stuff on the server?
|
||||
// - super-bro - Senor Developer, 14th July 2015
|
||||
//const baseUrl = window.BASE_URL;
|
||||
const apiEndpoint = window.API_ENDPOINT;
|
||||
const serverUrl = window.SERVER_URL;
|
||||
const baseUrl = window.BASE_URL;
|
||||
|
||||
const constants = {
|
||||
apiEndpoint,
|
||||
serverUrl,
|
||||
baseUrl,
|
||||
'aclList': ['acl_coa', 'acl_consign', 'acl_delete', 'acl_download', 'acl_edit', 'acl_create_editions', 'acl_view_editions',
|
||||
'acl_loan', 'acl_loan_request', 'acl_share', 'acl_transfer', 'acl_unconsign', 'acl_unshare', 'acl_view',
|
||||
'acl_withdraw_transfer', 'acl_wallet_submit'],
|
||||
@ -77,16 +81,40 @@ let constants = {
|
||||
}
|
||||
},
|
||||
|
||||
// in case of whitelabel customization, we store stuff here
|
||||
'whitelabel': {},
|
||||
'raven': {
|
||||
'url': 'https://0955da3388c64ab29bd32c2a429f9ef4@app.getsentry.com/48351'
|
||||
},
|
||||
'copyrightAssociations': ['ARS', 'DACS', 'Bildkunst', 'Pictoright', 'SODRAC', 'Copyright Agency/Viscopy', 'SAVA',
|
||||
'Bildrecht GmbH', 'SABAM', 'AUTVIS', 'CREAIMAGEN', 'SONECA', 'Copydan', 'EAU', 'Kuvasto', 'GCA', 'HUNGART',
|
||||
'IVARO', 'SIAE', 'JASPAR-SPDA', 'AKKA/LAA', 'LATGA-A', 'SOMAAP', 'ARTEGESTION', 'CARIER', 'BONO', 'APSAV',
|
||||
'SPA', 'GESTOR', 'VISaRTA', 'RAO', 'LITA', 'DALRO', 'VeGaP', 'BUS', 'ProLitteris', 'AGADU', 'AUTORARTE', 'BUBEDRA', 'BBDA', 'BCDA', 'BURIDA', 'ADAVIS', 'BSDA'],
|
||||
'searchThreshold': 500
|
||||
'searchThreshold': 500,
|
||||
|
||||
// in case of whitelabel customization, we store stuff here
|
||||
'whitelabel': {},
|
||||
|
||||
// 3rd party integrations
|
||||
'jquery': {
|
||||
'sdkUrl': 'https://code.jquery.com/jquery-2.1.4.min.js'
|
||||
},
|
||||
'shmui': {
|
||||
'sdkUrl': baseUrl + 'static/thirdparty/shmui/jquery.shmui.js',
|
||||
'cssUrl': baseUrl + 'static/thirdparty/shmui/shmui.css'
|
||||
},
|
||||
'audiojs': {
|
||||
'sdkUrl': baseUrl + 'static/thirdparty/audiojs/audiojs/audio.min.js'
|
||||
},
|
||||
'videojs': {
|
||||
'sdkUrl': '//vjs.zencdn.net/4.12/video.js',
|
||||
'cssUrl': '//vjs.zencdn.net/4.12/video-js.css'
|
||||
},
|
||||
'raven': {
|
||||
'url': 'https://0955da3388c64ab29bd32c2a429f9ef4@app.getsentry.com/48351'
|
||||
},
|
||||
'facebook': {
|
||||
'appId': '420813844732240',
|
||||
'sdkUrl': '//connect.facebook.net/en_US/sdk.js'
|
||||
},
|
||||
'twitter': {
|
||||
'sdkUrl': 'https://platform.twitter.com/widgets.js'
|
||||
}
|
||||
};
|
||||
|
||||
export default constants;
|
||||
|
@ -1,71 +0,0 @@
|
||||
'use strict';
|
||||
|
||||
import Q from 'q';
|
||||
|
||||
let mapAttr = {
|
||||
link: 'href',
|
||||
script: 'src'
|
||||
};
|
||||
|
||||
let mapTag = {
|
||||
js: 'script',
|
||||
css: 'link'
|
||||
};
|
||||
|
||||
|
||||
let InjectInHeadMixin = {
|
||||
/**
|
||||
* Provide functions to inject `<script>` and `<link>` in `<head>`.
|
||||
* Useful when you have to load a huge external library and
|
||||
* you don't want to embed everything inside the build file.
|
||||
*/
|
||||
|
||||
isPresent(tag, src) {
|
||||
let attr = mapAttr[tag];
|
||||
let query = `head > ${tag}[${attr}="${src}"]`;
|
||||
return document.querySelector(query);
|
||||
},
|
||||
|
||||
injectTag(tag, src) {
|
||||
return Q.Promise((resolve, reject) => {
|
||||
if (InjectInHeadMixin.isPresent(tag, src)) {
|
||||
resolve();
|
||||
} else {
|
||||
let attr = mapAttr[tag];
|
||||
let element = document.createElement(tag);
|
||||
if (tag === 'script') {
|
||||
element.onload = () => resolve();
|
||||
element.onerror = () => reject();
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
document.head.appendChild(element);
|
||||
element[attr] = src;
|
||||
if (tag === 'link') {
|
||||
element.rel = 'stylesheet';
|
||||
}
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
injectStylesheet(src) {
|
||||
return InjectInHeadMixin.injectTag('link', src);
|
||||
},
|
||||
|
||||
injectScript(src) {
|
||||
return InjectInHeadMixin.injectTag('source', src);
|
||||
},
|
||||
|
||||
inject(src) {
|
||||
let ext = src.split('.').pop();
|
||||
let tag = mapTag[ext];
|
||||
if (!tag) {
|
||||
throw new Error(`Cannot inject ${src} in the DOM, cannot guess the tag name from extension "${ext}". Valid extensions are "js" and "css".`);
|
||||
}
|
||||
|
||||
return InjectInHeadMixin.injectTag(tag, src);
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
export default InjectInHeadMixin;
|
30
js/third_party/facebook.js
vendored
Normal file
30
js/third_party/facebook.js
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
'use strict';
|
||||
|
||||
import { altThirdParty } from '../alt';
|
||||
import EventActions from '../actions/event_actions';
|
||||
|
||||
import AppConstants from '../constants/application_constants'
|
||||
|
||||
class FacebookHandler {
|
||||
constructor() {
|
||||
this.bindActions(EventActions);
|
||||
}
|
||||
|
||||
onApplicationWillBoot(settings) {
|
||||
// Callback function that FB's sdk will call when it's finished loading
|
||||
// See https://developers.facebook.com/docs/javascript/quickstart/v2.5
|
||||
window.fbAsyncInit = () => {
|
||||
FB.init({
|
||||
appId: AppConstants.facebook.appId,
|
||||
// Force FB to parse everything on first load to make sure all the XFBML components are initialized.
|
||||
// If we don't do this, we can run into issues with components on the first load who are not be
|
||||
// initialized.
|
||||
xfbml: true,
|
||||
version: 'v2.5',
|
||||
cookie: false
|
||||
});
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export default altThirdParty.createStore(FacebookHandler, 'FacebookHandler');
|
72
js/utils/inject_utils.js
Normal file
72
js/utils/inject_utils.js
Normal file
@ -0,0 +1,72 @@
|
||||
'use strict';
|
||||
|
||||
import Q from 'q';
|
||||
|
||||
let mapAttr = {
|
||||
link: 'href',
|
||||
script: 'src'
|
||||
};
|
||||
|
||||
let mapTag = {
|
||||
js: 'script',
|
||||
css: 'link'
|
||||
};
|
||||
|
||||
function injectTag(tag, src) {
|
||||
return Q.Promise((resolve, reject) => {
|
||||
if (isPresent(tag, src)) {
|
||||
resolve();
|
||||
} else {
|
||||
let attr = mapAttr[tag];
|
||||
let element = document.createElement(tag);
|
||||
if (tag === 'script') {
|
||||
element.onload = () => resolve();
|
||||
element.onerror = () => reject();
|
||||
} else {
|
||||
resolve();
|
||||
}
|
||||
document.head.appendChild(element);
|
||||
element[attr] = src;
|
||||
if (tag === 'link') {
|
||||
element.rel = 'stylesheet';
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function isPresent(tag, src) {
|
||||
let attr = mapAttr[tag];
|
||||
let query = `head > ${tag}[${attr}="${src}"]`;
|
||||
return document.querySelector(query);
|
||||
}
|
||||
|
||||
function injectStylesheet(src) {
|
||||
return injectTag('link', src);
|
||||
}
|
||||
|
||||
function injectScript(src) {
|
||||
return injectTag('source', src);
|
||||
}
|
||||
|
||||
function inject(src) {
|
||||
let ext = src.split('.').pop();
|
||||
let tag = mapTag[ext];
|
||||
if (!tag) {
|
||||
throw new Error(`Cannot inject ${src} in the DOM, cannot guess the tag name from extension "${ext}". Valid extensions are "js" and "css".`);
|
||||
}
|
||||
|
||||
return injectTag(tag, src);
|
||||
}
|
||||
|
||||
export const InjectInHeadUtils = {
|
||||
/**
|
||||
* Provide functions to inject `<script>` and `<link>` in `<head>`.
|
||||
* Useful when you have to load a huge external library and
|
||||
* you don't want to embed everything inside the build file.
|
||||
*/
|
||||
|
||||
isPresent,
|
||||
injectStylesheet,
|
||||
injectScript,
|
||||
inject
|
||||
};
|
10
sass/ascribe_social_share.scss
Normal file
10
sass/ascribe_social_share.scss
Normal file
@ -0,0 +1,10 @@
|
||||
.ascribe-social-button-list {
|
||||
margin-right: 10px;
|
||||
}
|
||||
|
||||
.btn-ascribe-social {
|
||||
height: 20px;
|
||||
width: 56px;
|
||||
box-sizing: content-box; /* We want to ignore padding in these calculations as we use the sdk buttons' height and width */
|
||||
padding: 1px 0;
|
||||
}
|
@ -31,6 +31,7 @@ $BASE_URL: '<%= BASE_URL %>';
|
||||
@import 'offset_right';
|
||||
@import 'ascribe_settings';
|
||||
@import 'ascribe_slides_container';
|
||||
@import 'ascribe_social_share';
|
||||
@import 'ascribe_property';
|
||||
@import 'ascribe_form';
|
||||
@import 'ascribe_panel';
|
||||
|
Loading…
Reference in New Issue
Block a user