123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610 |
- import jquery from 'jquery';
- const $ = jquery || window.jQuery || window.$;
- class BootstrapSwitch {
- constructor(element, options = {}) {
- this.$element = $(element);
- this.options = $.extend(
- {},
- $.fn.bootstrapSwitch.defaults,
- this._getElementOptions(),
- options,
- );
- this.prevOptions = {};
- this.$wrapper = $('<div>', {
- class: () => {
- const classes = [];
- classes.push(this.options.state ? 'on' : 'off');
- if (this.options.size) {
- classes.push(this.options.size);
- }
- if (this.options.disabled) {
- classes.push('disabled');
- }
- if (this.options.readonly) {
- classes.push('readonly');
- }
- if (this.options.indeterminate) {
- classes.push('indeterminate');
- }
- if (this.options.inverse) {
- classes.push('inverse');
- }
- if (this.$element.attr('id')) {
- classes.push(`id-${this.$element.attr('id')}`);
- }
- return classes
- .map(this._getClass.bind(this))
- .concat([this.options.baseClass], this._getClasses(this.options.wrapperClass))
- .join(' ');
- },
- });
- this.$container = $('<div>', { class: this._getClass('container') });
- this.$on = $('<span>', {
- html: this.options.onText,
- class: `${this._getClass('handle-on')} ${this._getClass(this.options.onColor)}`,
- });
- this.$off = $('<span>', {
- html: this.options.offText,
- class: `${this._getClass('handle-off')} ${this._getClass(this.options.offColor)}`,
- });
- this.$label = $('<span>', {
- html: this.options.labelText,
- class: this._getClass('label'),
- });
- this.$element.on('init.bootstrapSwitch', this.options.onInit.bind(this, element));
- this.$element.on('switchChange.bootstrapSwitch', (...args) => {
- if (this.options.onSwitchChange.apply(element, args) === false) {
- if (this.$element.is(':radio')) {
- $(`[name="${this.$element.attr('name')}"]`).trigger('previousState.bootstrapSwitch', true);
- } else {
- this.$element.trigger('previousState.bootstrapSwitch', true);
- }
- }
- });
- this.$container = this.$element.wrap(this.$container).parent();
- this.$wrapper = this.$container.wrap(this.$wrapper).parent();
- this.$element
- .before(this.options.inverse ? this.$off : this.$on)
- .before(this.$label)
- .before(this.options.inverse ? this.$on : this.$off);
- if (this.options.indeterminate) {
- this.$element.prop('indeterminate', true);
- }
- this._init();
- this._elementHandlers();
- this._handleHandlers();
- this._labelHandlers();
- this._formHandler();
- this._externalLabelHandler();
- this.$element.trigger('init.bootstrapSwitch', this.options.state);
- }
- setPrevOptions() {
- this.prevOptions = { ...this.options };
- }
- state(value, skip) {
- if (typeof value === 'undefined') { return this.options.state; }
- if (
- (this.options.disabled || this.options.readonly) ||
- (this.options.state && !this.options.radioAllOff && this.$element.is(':radio'))
- ) { return this.$element; }
- if (this.$element.is(':radio')) {
- $(`[name="${this.$element.attr('name')}"]`).trigger('setPreviousOptions.bootstrapSwitch');
- } else {
- this.$element.trigger('setPreviousOptions.bootstrapSwitch');
- }
- if (this.options.indeterminate) {
- this.indeterminate(false);
- }
- this.$element
- .prop('checked', Boolean(value))
- .trigger('change.bootstrapSwitch', skip);
- return this.$element;
- }
- toggleState(skip) {
- if (this.options.disabled || this.options.readonly) { return this.$element; }
- if (this.options.indeterminate) {
- this.indeterminate(false);
- return this.state(true);
- }
- return this.$element.prop('checked', !this.options.state).trigger('change.bootstrapSwitch', skip);
- }
- size(value) {
- if (typeof value === 'undefined') { return this.options.size; }
- if (this.options.size != null) {
- this.$wrapper.removeClass(this._getClass(this.options.size));
- }
- if (value) {
- this.$wrapper.addClass(this._getClass(value));
- }
- this._width();
- this._containerPosition();
- this.options.size = value;
- return this.$element;
- }
- animate(value) {
- if (typeof value === 'undefined') { return this.options.animate; }
- if (this.options.animate === Boolean(value)) { return this.$element; }
- return this.toggleAnimate();
- }
- toggleAnimate() {
- this.options.animate = !this.options.animate;
- this.$wrapper.toggleClass(this._getClass('animate'));
- return this.$element;
- }
- disabled(value) {
- if (typeof value === 'undefined') { return this.options.disabled; }
- if (this.options.disabled === Boolean(value)) { return this.$element; }
- return this.toggleDisabled();
- }
- toggleDisabled() {
- this.options.disabled = !this.options.disabled;
- this.$element.prop('disabled', this.options.disabled);
- this.$wrapper.toggleClass(this._getClass('disabled'));
- return this.$element;
- }
- readonly(value) {
- if (typeof value === 'undefined') { return this.options.readonly; }
- if (this.options.readonly === Boolean(value)) { return this.$element; }
- return this.toggleReadonly();
- }
- toggleReadonly() {
- this.options.readonly = !this.options.readonly;
- this.$element.prop('readonly', this.options.readonly);
- this.$wrapper.toggleClass(this._getClass('readonly'));
- return this.$element;
- }
- indeterminate(value) {
- if (typeof value === 'undefined') { return this.options.indeterminate; }
- if (this.options.indeterminate === Boolean(value)) { return this.$element; }
- return this.toggleIndeterminate();
- }
- toggleIndeterminate() {
- this.options.indeterminate = !this.options.indeterminate;
- this.$element.prop('indeterminate', this.options.indeterminate);
- this.$wrapper.toggleClass(this._getClass('indeterminate'));
- this._containerPosition();
- return this.$element;
- }
- inverse(value) {
- if (typeof value === 'undefined') { return this.options.inverse; }
- if (this.options.inverse === Boolean(value)) { return this.$element; }
- return this.toggleInverse();
- }
- toggleInverse() {
- this.$wrapper.toggleClass(this._getClass('inverse'));
- const $on = this.$on.clone(true);
- const $off = this.$off.clone(true);
- this.$on.replaceWith($off);
- this.$off.replaceWith($on);
- this.$on = $off;
- this.$off = $on;
- this.options.inverse = !this.options.inverse;
- return this.$element;
- }
- onColor(value) {
- if (typeof value === 'undefined') { return this.options.onColor; }
- if (this.options.onColor) {
- this.$on.removeClass(this._getClass(this.options.onColor));
- }
- this.$on.addClass(this._getClass(value));
- this.options.onColor = value;
- return this.$element;
- }
- offColor(value) {
- if (typeof value === 'undefined') { return this.options.offColor; }
- if (this.options.offColor) {
- this.$off.removeClass(this._getClass(this.options.offColor));
- }
- this.$off.addClass(this._getClass(value));
- this.options.offColor = value;
- return this.$element;
- }
- onText(value) {
- if (typeof value === 'undefined') { return this.options.onText; }
- this.$on.html(value);
- this._width();
- this._containerPosition();
- this.options.onText = value;
- return this.$element;
- }
- offText(value) {
- if (typeof value === 'undefined') { return this.options.offText; }
- this.$off.html(value);
- this._width();
- this._containerPosition();
- this.options.offText = value;
- return this.$element;
- }
- labelText(value) {
- if (typeof value === 'undefined') { return this.options.labelText; }
- this.$label.html(value);
- this._width();
- this.options.labelText = value;
- return this.$element;
- }
- handleWidth(value) {
- if (typeof value === 'undefined') { return this.options.handleWidth; }
- this.options.handleWidth = value;
- this._width();
- this._containerPosition();
- return this.$element;
- }
- labelWidth(value) {
- if (typeof value === 'undefined') { return this.options.labelWidth; }
- this.options.labelWidth = value;
- this._width();
- this._containerPosition();
- return this.$element;
- }
- baseClass(value) {
- return this.options.baseClass;
- }
- wrapperClass(value) {
- if (typeof value === 'undefined') { return this.options.wrapperClass; }
- if (!value) {
- value = $.fn.bootstrapSwitch.defaults.wrapperClass;
- }
- this.$wrapper.removeClass(this._getClasses(this.options.wrapperClass).join(' '));
- this.$wrapper.addClass(this._getClasses(value).join(' '));
- this.options.wrapperClass = value;
- return this.$element;
- }
- radioAllOff(value) {
- if (typeof value === 'undefined') { return this.options.radioAllOff; }
- const val = Boolean(value);
- if (this.options.radioAllOff === val) { return this.$element; }
- this.options.radioAllOff = val;
- return this.$element;
- }
- onInit(value) {
- if (typeof value === 'undefined') { return this.options.onInit; }
- if (!value) {
- value = $.fn.bootstrapSwitch.defaults.onInit;
- }
- this.options.onInit = value;
- return this.$element;
- }
- onSwitchChange(value) {
- if (typeof value === 'undefined') {
- return this.options.onSwitchChange;
- }
- if (!value) {
- value = $.fn.bootstrapSwitch.defaults.onSwitchChange;
- }
- this.options.onSwitchChange = value;
- return this.$element;
- }
- destroy() {
- const $form = this.$element.closest('form');
- if ($form.length) {
- $form.off('reset.bootstrapSwitch').removeData('bootstrap-switch');
- }
- this.$container
- .children()
- .not(this.$element)
- .remove();
- this.$element
- .unwrap()
- .unwrap()
- .off('.bootstrapSwitch')
- .removeData('bootstrap-switch');
- return this.$element;
- }
- _getElementOptions() {
- return {
- state: this.$element.is(':checked'),
- size: this.$element.data('size'),
- animate: this.$element.data('animate'),
- disabled: this.$element.is(':disabled'),
- readonly: this.$element.is('[readonly]'),
- indeterminate: this.$element.data('indeterminate'),
- inverse: this.$element.data('inverse'),
- radioAllOff: this.$element.data('radio-all-off'),
- onColor: this.$element.data('on-color'),
- offColor: this.$element.data('off-color'),
- onText: this.$element.data('on-text'),
- offText: this.$element.data('off-text'),
- labelText: this.$element.data('label-text'),
- handleWidth: this.$element.data('handle-width'),
- labelWidth: this.$element.data('label-width'),
- baseClass: this.$element.data('base-class'),
- wrapperClass: this.$element.data('wrapper-class'),
- };
- }
- _width() {
- const $handles = this.$on
- .add(this.$off)
- .add(this.$label)
- .css('width', '');
- const handleWidth = this.options.handleWidth === 'auto'
- ? Math.round(Math.max(this.$on.width(), this.$off.width()))
- : this.options.handleWidth;
- $handles.width(handleWidth);
- this.$label.width((index, width) => {
- if (this.options.labelWidth !== 'auto') { return this.options.labelWidth; }
- if (width < handleWidth) { return handleWidth; }
- return width;
- });
- this._handleWidth = this.$on.outerWidth();
- this._labelWidth = this.$label.outerWidth();
- this.$container.width((this._handleWidth * 2) + this._labelWidth);
- return this.$wrapper.width(this._handleWidth + this._labelWidth);
- }
- _containerPosition(state = this.options.state, callback) {
- this.$container.css('margin-left', () => {
- const values = [0, `-${this._handleWidth}px`];
- if (this.options.indeterminate) {
- return `-${this._handleWidth / 2}px`;
- }
- if (state) {
- if (this.options.inverse) {
- return values[1];
- }
- return values[0];
- }
- if (this.options.inverse) {
- return values[0];
- }
- return values[1];
- });
- }
- _init() {
- const init = () => {
- this.setPrevOptions();
- this._width();
- this._containerPosition();
- setTimeout(() => {
- if (this.options.animate) {
- return this.$wrapper.addClass(this._getClass('animate'));
- }
- }, 50);
- };
- if (this.$wrapper.is(':visible')) {
- init();
- return;
- }
- const initInterval = window.setInterval(() => {
- if (this.$wrapper.is(':visible')) {
- init();
- return window.clearInterval(initInterval);
- }
- }, 50);
- }
- _elementHandlers() {
- return this.$element.on({
- 'setPreviousOptions.bootstrapSwitch': this.setPrevOptions.bind(this),
- 'previousState.bootstrapSwitch': () => {
- this.options = this.prevOptions;
- if (this.options.indeterminate) {
- this.$wrapper.addClass(this._getClass('indeterminate'));
- }
- this.$element
- .prop('checked', this.options.state)
- .trigger('change.bootstrapSwitch', true);
- },
- 'change.bootstrapSwitch': (event, skip) => {
- event.preventDefault();
- event.stopImmediatePropagation();
- const state = this.$element.is(':checked');
- this._containerPosition(state);
- if (state === this.options.state) {
- return;
- }
- this.options.state = state;
- this.$wrapper
- .toggleClass(this._getClass('off'))
- .toggleClass(this._getClass('on'));
- if (!skip) {
- if (this.$element.is(':radio')) {
- $(`[name="${this.$element.attr('name')}"]`)
- .not(this.$element)
- .prop('checked', false)
- .trigger('change.bootstrapSwitch', true);
- }
- this.$element.trigger('switchChange.bootstrapSwitch', [state]);
- }
- },
- 'focus.bootstrapSwitch': (event) => {
- event.preventDefault();
- this.$wrapper.addClass(this._getClass('focused'));
- },
- 'blur.bootstrapSwitch': (event) => {
- event.preventDefault();
- this.$wrapper.removeClass(this._getClass('focused'));
- },
- 'keydown.bootstrapSwitch': (event) => {
- if (!event.which || this.options.disabled || this.options.readonly) {
- return;
- }
- if (event.which === 37 || event.which === 39) {
- event.preventDefault();
- event.stopImmediatePropagation();
- this.state(event.which === 39);
- }
- },
- });
- }
- _handleHandlers() {
- this.$on.on('click.bootstrapSwitch', (event) => {
- event.preventDefault();
- event.stopPropagation();
- this.state(false);
- return this.$element.trigger('focus.bootstrapSwitch');
- });
- return this.$off.on('click.bootstrapSwitch', (event) => {
- event.preventDefault();
- event.stopPropagation();
- this.state(true);
- return this.$element.trigger('focus.bootstrapSwitch');
- });
- }
- _labelHandlers() {
- const handlers = {
- click(event) { event.stopPropagation(); },
- 'mousedown.bootstrapSwitch touchstart.bootstrapSwitch': (event) => {
- if (this._dragStart || this.options.disabled || this.options.readonly) {
- return;
- }
- event.preventDefault();
- event.stopPropagation();
- this._dragStart = (event.pageX || event.originalEvent.touches[0].pageX) - parseInt(this.$container.css('margin-left'), 10);
- if (this.options.animate) {
- this.$wrapper.removeClass(this._getClass('animate'));
- }
- this.$element.trigger('focus.bootstrapSwitch');
- },
- 'mousemove.bootstrapSwitch touchmove.bootstrapSwitch': (event) => {
- if (this._dragStart == null) { return; }
- const difference = (event.pageX || event.originalEvent.touches[0].pageX) - this._dragStart;
- event.preventDefault();
- if (difference < -this._handleWidth || difference > 0) { return; }
- this._dragEnd = difference;
- this.$container.css('margin-left', `${this._dragEnd}px`);
- },
- 'mouseup.bootstrapSwitch touchend.bootstrapSwitch': (event) => {
- if (!this._dragStart) { return; }
- event.preventDefault();
- if (this.options.animate) {
- this.$wrapper.addClass(this._getClass('animate'));
- }
- if (this._dragEnd) {
- const state = this._dragEnd > -(this._handleWidth / 2);
- this._dragEnd = false;
- this.state(this.options.inverse ? !state : state);
- } else {
- this.state(!this.options.state);
- }
- this._dragStart = false;
- },
- 'mouseleave.bootstrapSwitch': () => {
- this.$label.trigger('mouseup.bootstrapSwitch');
- },
- };
- this.$label.on(handlers);
- }
- _externalLabelHandler() {
- const $externalLabel = this.$element.closest('label');
- $externalLabel.on('click', (event) => {
- event.preventDefault();
- event.stopImmediatePropagation();
- if (event.target === $externalLabel[0]) {
- this.toggleState();
- }
- });
- }
- _formHandler() {
- const $form = this.$element.closest('form');
- if ($form.data('bootstrap-switch')) {
- return;
- }
- $form
- .on('reset.bootstrapSwitch', () => {
- window.setTimeout(() => {
- $form.find('input')
- .filter(function () { return $(this).data('bootstrap-switch'); })
- .each(function () { return $(this).bootstrapSwitch('state', this.checked); });
- }, 1);
- })
- .data('bootstrap-switch', true);
- }
- _getClass(name) {
- return `${this.options.baseClass}-${name}`;
- }
- _getClasses(classes) {
- if (!$.isArray(classes)) {
- return [this._getClass(classes)];
- }
- return classes.map(this._getClass.bind(this));
- }
- }
- $.fn.bootstrapSwitch = function (option, ...args) {
- function reducer(ret, next) {
- const $this = $(next);
- const existingData = $this.data('bootstrap-switch');
- const data = existingData || new BootstrapSwitch(next, option);
- if (!existingData) {
- $this.data('bootstrap-switch', data);
- }
- if (typeof option === 'string') {
- return data[option](...args);
- }
- return ret;
- }
- return Array.prototype.reduce.call(this, reducer, this);
- };
- $.fn.bootstrapSwitch.Constructor = BootstrapSwitch;
- $.fn.bootstrapSwitch.defaults = {
- state: true,
- size: null,
- animate: true,
- disabled: false,
- readonly: false,
- indeterminate: false,
- inverse: false,
- radioAllOff: false,
- onColor: 'primary',
- offColor: 'default',
- onText: 'ON',
- offText: 'OFF',
- labelText: ' ',
- handleWidth: 'auto',
- labelWidth: 'auto',
- baseClass: 'bootstrap-switch',
- wrapperClass: 'wrapper',
- onInit: () => {},
- onSwitchChange: () => {},
- };
|