File: development/modules/bgchanger/js/bgchanger.js
import $ from 'jquery';
import DOMModule from '../../__base/dommodule';
import {isString} from '../../../js/utils/type';
import {uuid} from '../../../js/utils/uuid';
import {tag} from '../../../js/utils/helpers';
import {each} from '../../../js/utils/enums';
/**
* @module Modules
*/
const _getStylesheet = Symbol('_getStylesheet');
const _insertStyles = Symbol('_insertStyles');
// The RuleParser is used to transform a string from the data-rbg attribute into
// a rule which can then be inserted into an embedded stylesheet
const RuleParser = {
defaults() {
return { url: '', media: null, position: '50% 50%' };
},
make(rule) { // creates css rules from parsed atoms
if (rule.media) {
return RuleParser.complete(rule)
}
return RuleParser.partial(rule);
},
parse(atoms) { // parse atoms into basic rules
return atoms.reduce((rule, atom) => {
if (isString(atom)) {
if (/\([\w\-]+?:\s?\d{1,}?(px|em)\)/g.test(atom)) {
rule.media = atom;
} else if (/^\d{1,3}?%\s+?\d{1,3}?%/g.test(atom)) {
rule.position = atom;
} else {
rule.url = atom;
}
}
return rule;
}, RuleParser.defaults());
},
complete({media, selector, url, position}) {
return `@media only screen and ${media} {
${selector} {
background-image: url(${url});
background-position: ${position};
}
}`;
},
partial({selector, url, position}) {
return `${selector} {
background-image: url(${url});
background-position: ${position};
}`;
}
};
/**
* BgChanger module
* @class Modules.BgChanger
* @constructor
* @extends Modules._DOMModule
*/
class BgChanger extends DOMModule {
constructor(options) {
super(options);
this.id = this.$rootNode.attr('id');
if (!this.id) {
this.id = 'rbg-container_' + uuid();
this.$rootNode.attr('id', this.id);
}
}
/**
* Creates a new BgChanger instance without the "new" keyword but does not
* initialize it
* @method of
* @static
* @for Modules.BgChanger
* @param {node|string|configs} node Node, CSS selector or configuration object
* @return {BgChanger} A new instance
*
* @example
* import BgChanger from './modules/bgchanger/js/bgchanger';
*
* const myChanger = BgChanger.of('[data-widget=BgChanger]');
* myChanger.init();
*/
static of(node) {
return new BgChanger(node);
}
/**
* Finds and initializes all BgChangers on a page at once
* @method findAll
* @static
* @for Modules.BgChanger
* @param {string} selector CSS selector to match nodes against
* @return {array} A list of instances
*
* @example
* import BgChanger from './modules/bgchanger/js/bgchanger';
*
* BgChanger.findAll('[data-widget=BgChanger]');
*/
static findAll(selector) {
return $(selector).toArray().map((node) => {
const changer = BgChanger.of(node);
changer.init();
return changer;
});
}
/**
* Initializes the instance, thereby creating an embedded stylesheet which
* is placed into the <head> element of the document if not already
* present
* @method init
* @for Modules.BgChanger
* @return {undefined} Nothing
*
* @example
* import BgChanger from './modules/bgchanger/js/bgchanger';
*
* const myChanger = BgChanger.of('[data-widget=BgChanger]');
* myChanger.init();
*/
init() {
this[_getStylesheet]();
this[_insertStyles]();
}
// === PRIVATE STUFF ===
[_getStylesheet]() {
if (BgChanger.embeddedCSS == null) {
let style = tag('style', {
type: 'text/css',
data: {bgchangerStyle: ''},
innerHTML: ''
});
this.$head.append(style);
BgChanger.embeddedCSS = style.sheet;
}
return BgChanger.embeddedCSS;
}
[_insertStyles]() {
each(this.$data('rbg'), (cssRule, idx) => {
const parsed = RuleParser.parse(cssRule.split('|'));
parsed.selector = `#${this.id}`;
const rule = RuleParser.make(parsed);
BgChanger.embeddedCSS.insertRule(rule, idx);
});
}
}
export default BgChanger;