init
This commit is contained in:
32
user/plugins/admin/themes/grav/app/utils/2fa.js
Normal file
32
user/plugins/admin/themes/grav/app/utils/2fa.js
Normal file
@@ -0,0 +1,32 @@
|
||||
import $ from 'jquery';
|
||||
import { config } from 'grav-config';
|
||||
import request from '../utils/request';
|
||||
|
||||
const body = $('body');
|
||||
|
||||
// Dashboard update and Grav update
|
||||
body.on('click', '[data-2fa-regenerate]', function(event) {
|
||||
event.preventDefault();
|
||||
let element = $(this);
|
||||
let url = `${config.base_url_relative}/ajax.json/task${config.param_sep}regenerate2FASecret`;
|
||||
|
||||
element.attr('disabled', 'disabled').find('> .fa').addClass('fa-spin');
|
||||
|
||||
request(url, { method: 'post' }, (response) => {
|
||||
$('[data-2fa-image]').attr('src', response.image);
|
||||
$('[data-2fa-secret]').text(response.secret);
|
||||
$('[data-2fa-value]').val(response.secret.replace(' ', ''));
|
||||
|
||||
element.removeAttr('disabled').find('> .fa').removeClass('fa-spin');
|
||||
});
|
||||
});
|
||||
|
||||
const toggleSecret = () => {
|
||||
const toggle = $('#toggle_twofa_enabled1');
|
||||
const secret = $('.twofa-secret');
|
||||
|
||||
secret[toggle.is(':checked') ? 'addClass' : 'removeClass']('show');
|
||||
};
|
||||
|
||||
body.on('click', '.twofa-toggle input', toggleSecret);
|
||||
toggleSecret();
|
||||
210
user/plugins/admin/themes/grav/app/utils/bootstrap-collapse.js
vendored
Normal file
210
user/plugins/admin/themes/grav/app/utils/bootstrap-collapse.js
vendored
Normal file
@@ -0,0 +1,210 @@
|
||||
import jQuery from 'jquery';
|
||||
|
||||
/* ========================================================================
|
||||
* Bootstrap: collapse.js v3.4.0
|
||||
* http://getbootstrap.com/javascript/#collapse
|
||||
* ========================================================================
|
||||
* Copyright 2011-2016 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
/* jshint latedef: false */
|
||||
|
||||
+(function($) {
|
||||
'use strict';
|
||||
|
||||
// COLLAPSE PUBLIC CLASS DEFINITION
|
||||
// ================================
|
||||
|
||||
var Collapse = function(element, options) {
|
||||
this.$element = $(element);
|
||||
this.options = $.extend({}, Collapse.DEFAULTS, options);
|
||||
this.$trigger = $('[data-toggle="collapse"][href="#' + element.id + '"],' +
|
||||
'[data-toggle="collapse"][data-target="#' + element.id + '"]');
|
||||
this.transitioning = null;
|
||||
|
||||
if (this.options.parent) {
|
||||
this.$parent = this.getParent();
|
||||
} else {
|
||||
this.addAriaAndCollapsedClass(this.$element, this.$trigger);
|
||||
}
|
||||
|
||||
if (this.options.toggle) this.toggle();
|
||||
};
|
||||
|
||||
Collapse.VERSION = '3.4.0';
|
||||
|
||||
Collapse.TRANSITION_DURATION = 350;
|
||||
|
||||
Collapse.DEFAULTS = {
|
||||
toggle: true
|
||||
};
|
||||
|
||||
Collapse.prototype.dimension = function() {
|
||||
var hasWidth = this.$element.hasClass('width');
|
||||
return hasWidth ? 'width' : 'height';
|
||||
};
|
||||
|
||||
Collapse.prototype.show = function() {
|
||||
if (this.transitioning || this.$element.hasClass('in')) return;
|
||||
|
||||
var activesData;
|
||||
var actives = this.$parent && this.$parent.children('.panel').children('.in, .collapsing');
|
||||
|
||||
if (actives && actives.length) {
|
||||
activesData = actives.data('bs.collapse');
|
||||
if (activesData && activesData.transitioning) return;
|
||||
}
|
||||
|
||||
var startEvent = $.Event('show.bs.collapse');
|
||||
this.$element.trigger(startEvent);
|
||||
if (startEvent.isDefaultPrevented()) return;
|
||||
|
||||
if (actives && actives.length) {
|
||||
Plugin.call(actives, 'hide');
|
||||
activesData || actives.data('bs.collapse', null);
|
||||
}
|
||||
|
||||
var dimension = this.dimension();
|
||||
|
||||
this.$element
|
||||
.removeClass('collapse')
|
||||
.addClass('collapsing')[dimension](0)
|
||||
.attr('aria-expanded', true);
|
||||
|
||||
this.$trigger
|
||||
.removeClass('collapsed')
|
||||
.attr('aria-expanded', true);
|
||||
|
||||
this.transitioning = 1;
|
||||
|
||||
var complete = function() {
|
||||
this.$element
|
||||
.removeClass('collapsing')
|
||||
.addClass('collapse in')[dimension]('');
|
||||
this.transitioning = 0;
|
||||
this.$element
|
||||
.trigger('shown.bs.collapse');
|
||||
};
|
||||
|
||||
if (!$.support.transition) return complete.call(this);
|
||||
|
||||
var scrollSize = $.camelCase(['scroll', dimension].join('-'));
|
||||
|
||||
this.$element
|
||||
.one('bsTransitionEnd', $.proxy(complete, this))
|
||||
.emulateTransitionEnd(Collapse.TRANSITION_DURATION)[dimension](this.$element[0][scrollSize]);
|
||||
};
|
||||
|
||||
Collapse.prototype.hide = function() {
|
||||
if (this.transitioning || !this.$element.hasClass('in')) return;
|
||||
|
||||
var startEvent = $.Event('hide.bs.collapse');
|
||||
this.$element.trigger(startEvent);
|
||||
if (startEvent.isDefaultPrevented()) return;
|
||||
|
||||
var dimension = this.dimension();
|
||||
|
||||
this.$element[dimension](this.$element[dimension]())[0].offsetHeight;
|
||||
|
||||
this.$element
|
||||
.addClass('collapsing')
|
||||
.removeClass('collapse in')
|
||||
.attr('aria-expanded', false);
|
||||
|
||||
this.$trigger
|
||||
.addClass('collapsed')
|
||||
.attr('aria-expanded', false);
|
||||
|
||||
this.transitioning = 1;
|
||||
|
||||
var complete = function() {
|
||||
this.transitioning = 0;
|
||||
this.$element
|
||||
.removeClass('collapsing')
|
||||
.addClass('collapse')
|
||||
.trigger('hidden.bs.collapse');
|
||||
};
|
||||
|
||||
if (!$.support.transition) return complete.call(this);
|
||||
|
||||
this.$element[dimension](0)
|
||||
.one('bsTransitionEnd', $.proxy(complete, this))
|
||||
.emulateTransitionEnd(Collapse.TRANSITION_DURATION);
|
||||
};
|
||||
|
||||
Collapse.prototype.toggle = function() {
|
||||
this[this.$element.hasClass('in') ? 'hide' : 'show']();
|
||||
};
|
||||
|
||||
Collapse.prototype.getParent = function() {
|
||||
return $(this.options.parent)
|
||||
.find('[data-toggle="collapse"][data-parent="' + this.options.parent + '"]')
|
||||
.each($.proxy(function(i, element) {
|
||||
var $element = $(element);
|
||||
this.addAriaAndCollapsedClass(getTargetFromTrigger($element), $element);
|
||||
}, this))
|
||||
.end();
|
||||
};
|
||||
|
||||
Collapse.prototype.addAriaAndCollapsedClass = function($element, $trigger) {
|
||||
var isOpen = $element.hasClass('in');
|
||||
|
||||
$element.attr('aria-expanded', isOpen);
|
||||
$trigger
|
||||
.toggleClass('collapsed', !isOpen)
|
||||
.attr('aria-expanded', isOpen);
|
||||
};
|
||||
|
||||
function getTargetFromTrigger($trigger) {
|
||||
var href;
|
||||
var target = $trigger.attr('data-target') ||
|
||||
(href = $trigger.attr('href')) && href.replace(/.*(?=#[^\s]+$)/, ''); // strip for ie7
|
||||
|
||||
return $(target);
|
||||
}
|
||||
|
||||
// COLLAPSE PLUGIN DEFINITION
|
||||
// ==========================
|
||||
|
||||
function Plugin(option) {
|
||||
return this.each(function() {
|
||||
var $this = $(this);
|
||||
var data = $this.data('bs.collapse');
|
||||
var options = $.extend({}, Collapse.DEFAULTS, $this.data(), typeof option === 'object' && option);
|
||||
|
||||
if (!data && options.toggle && /show|hide/.test(option)) options.toggle = false;
|
||||
if (!data) $this.data('bs.collapse', (data = new Collapse(this, options)));
|
||||
if (typeof option === 'string') data[option]();
|
||||
});
|
||||
}
|
||||
|
||||
var old = $.fn.collapse;
|
||||
|
||||
$.fn.collapse = Plugin;
|
||||
$.fn.collapse.Constructor = Collapse;
|
||||
|
||||
// COLLAPSE NO CONFLICT
|
||||
// ====================
|
||||
|
||||
$.fn.collapse.noConflict = function() {
|
||||
$.fn.collapse = old;
|
||||
return this;
|
||||
};
|
||||
|
||||
// COLLAPSE DATA-API
|
||||
// =================
|
||||
|
||||
$(document).on('click.bs.collapse.data-api', '[data-toggle="collapse"]', function(e) {
|
||||
var $this = $(this);
|
||||
|
||||
if (!$this.attr('data-target')) e.preventDefault();
|
||||
|
||||
var $target = getTargetFromTrigger($this);
|
||||
var data = $target.data('bs.collapse');
|
||||
var option = data ? 'toggle' : $this.data();
|
||||
|
||||
Plugin.call($target, option);
|
||||
});
|
||||
|
||||
}(jQuery));
|
||||
2632
user/plugins/admin/themes/grav/app/utils/bootstrap-datetimepicker.js
vendored
Normal file
2632
user/plugins/admin/themes/grav/app/utils/bootstrap-datetimepicker.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
169
user/plugins/admin/themes/grav/app/utils/bootstrap-dropdown.js
vendored
Normal file
169
user/plugins/admin/themes/grav/app/utils/bootstrap-dropdown.js
vendored
Normal file
@@ -0,0 +1,169 @@
|
||||
import jQuery from 'jquery';
|
||||
|
||||
/* ========================================================================
|
||||
* Bootstrap: dropdown.js v3.4.1
|
||||
* https://getbootstrap.com/docs/3.4/javascript/#dropdowns
|
||||
* ========================================================================
|
||||
* Copyright 2011-2019 Twitter, Inc.
|
||||
* Licensed under MIT (https://github.com/twbs/bootstrap/blob/v3-dev/LICENSE)
|
||||
* ======================================================================== */
|
||||
|
||||
+(function($) {
|
||||
'use strict';
|
||||
|
||||
// DROPDOWN CLASS DEFINITION
|
||||
// =========================
|
||||
|
||||
const backdrop = '.dropdown-backdrop';
|
||||
const toggle = '[data-toggle="dropdown"]';
|
||||
const Dropdown = function(element) {
|
||||
$(element).on('click.bs.dropdown', this.toggle);
|
||||
};
|
||||
|
||||
Dropdown.VERSION = '3.4.1';
|
||||
|
||||
function getParent($this) {
|
||||
let selector = $this.attr('data-target');
|
||||
|
||||
if (!selector) {
|
||||
selector = $this.attr('href');
|
||||
selector = selector && /#[A-Za-z]/.test(selector) && selector.replace(/.*(?=#[^\s]*$)/, ''); // strip for ie7
|
||||
}
|
||||
|
||||
const $parent = selector !== '#' ? $(document).find(selector) : null;
|
||||
|
||||
return $parent && $parent.length ? $parent : $this.parent();
|
||||
}
|
||||
|
||||
function clearMenus(e) {
|
||||
if (e && e.which === 3) { return; }
|
||||
$(backdrop).remove();
|
||||
$(toggle).each(function() {
|
||||
const $this = $(this);
|
||||
const $parent = getParent($this);
|
||||
const relatedTarget = { relatedTarget: this };
|
||||
|
||||
if (!$parent.hasClass('open')) { return; }
|
||||
|
||||
if (e && e.type === 'click' && /input|textarea/i.test(e.target.tagName) && $.contains($parent[0], e.target)) { return; }
|
||||
|
||||
$parent.trigger(e = $.Event('hide.bs.dropdown', relatedTarget));
|
||||
|
||||
if (e.isDefaultPrevented()) { return; }
|
||||
|
||||
$this.attr('aria-expanded', 'false');
|
||||
$parent.removeClass('open').trigger($.Event('hidden.bs.dropdown', relatedTarget));
|
||||
});
|
||||
}
|
||||
|
||||
Dropdown.prototype.toggle = function(e) {
|
||||
const $this = $(this);
|
||||
|
||||
if ($this.is('.disabled, :disabled')) { return; }
|
||||
|
||||
const $parent = getParent($this);
|
||||
const isActive = $parent.hasClass('open');
|
||||
|
||||
clearMenus();
|
||||
|
||||
if (!isActive) {
|
||||
if ('ontouchstart' in document.documentElement && !$parent.closest('.navbar-nav').length) {
|
||||
// if mobile we use a backdrop because click events don't delegate
|
||||
$(document.createElement('div'))
|
||||
.addClass('dropdown-backdrop')
|
||||
.insertAfter($(this))
|
||||
.on('click', clearMenus);
|
||||
}
|
||||
|
||||
const relatedTarget = { relatedTarget: this };
|
||||
$parent.trigger(e = $.Event('show.bs.dropdown', relatedTarget));
|
||||
|
||||
if (e.isDefaultPrevented()) { return; }
|
||||
|
||||
$this
|
||||
.trigger('focus')
|
||||
.attr('aria-expanded', 'true');
|
||||
|
||||
$parent
|
||||
.toggleClass('open')
|
||||
.trigger($.Event('shown.bs.dropdown', relatedTarget));
|
||||
}
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
Dropdown.prototype.keydown = function(e) {
|
||||
if (!/(38|40|27|32)/.test(e.which) || /input|textarea/i.test(e.target.tagName)) return;
|
||||
|
||||
const $this = $(this);
|
||||
|
||||
e.preventDefault();
|
||||
e.stopPropagation();
|
||||
|
||||
if ($this.is('.disabled, :disabled')) {
|
||||
return;
|
||||
}
|
||||
|
||||
const $parent = getParent($this);
|
||||
const isActive = $parent.hasClass('open');
|
||||
|
||||
if (!isActive && e.which !== 27 || isActive && e.which === 27) {
|
||||
if (e.which === 27) {
|
||||
$parent.find(toggle).trigger('focus');
|
||||
}
|
||||
|
||||
return $this.trigger('click');
|
||||
}
|
||||
|
||||
const desc = ' li:not(.disabled):visible a';
|
||||
const $items = $parent.find('.dropdown-menu' + desc);
|
||||
|
||||
if (!$items.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
let index = $items.index(e.target);
|
||||
|
||||
if (e.which === 38 && index > 0) { index--; } // up
|
||||
if (e.which === 40 && index < $items.length - 1) { index++; } // down
|
||||
if (!~index) { index = 0; }
|
||||
|
||||
$items.eq(index).trigger('focus');
|
||||
};
|
||||
|
||||
// DROPDOWN PLUGIN DEFINITION
|
||||
// ==========================
|
||||
|
||||
function Plugin(option) {
|
||||
return this.each(function() {
|
||||
const $this = $(this);
|
||||
let data = $this.data('bs.dropdown');
|
||||
|
||||
if (!data) $this.data('bs.dropdown', (data = new Dropdown(this)));
|
||||
if (typeof option === 'string') data[option].call($this);
|
||||
});
|
||||
}
|
||||
|
||||
const old = $.fn.dropdown;
|
||||
|
||||
$.fn.dropdown = Plugin;
|
||||
$.fn.dropdown.Constructor = Dropdown;
|
||||
|
||||
// DROPDOWN NO CONFLICT
|
||||
// ====================
|
||||
|
||||
$.fn.dropdown.noConflict = function() {
|
||||
$.fn.dropdown = old;
|
||||
return this;
|
||||
};
|
||||
|
||||
// APPLY TO STANDARD DROPDOWN ELEMENTS
|
||||
// ===================================
|
||||
|
||||
$(document)
|
||||
.on('click.bs.dropdown.data-api', clearMenus)
|
||||
.on('click.bs.dropdown.data-api', '.dropdown form', function(e) { e.stopPropagation(); })
|
||||
.on('click.bs.dropdown.data-api', toggle, Dropdown.prototype.toggle)
|
||||
.on('keydown.bs.dropdown.data-api', toggle, Dropdown.prototype.keydown)
|
||||
.on('keydown.bs.dropdown.data-api', '.dropdown-menu', Dropdown.prototype.keydown);
|
||||
}(jQuery));
|
||||
52
user/plugins/admin/themes/grav/app/utils/bootstrap-transition.js
vendored
Normal file
52
user/plugins/admin/themes/grav/app/utils/bootstrap-transition.js
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
import jQuery from 'jquery';
|
||||
|
||||
+(function($) {
|
||||
'use strict';
|
||||
|
||||
// CSS TRANSITION SUPPORT (Shoutout: http://www.modernizr.com/)
|
||||
// ============================================================
|
||||
|
||||
function transitionEnd() {
|
||||
var el = document.createElement('bootstrap');
|
||||
|
||||
var transEndEventNames = {
|
||||
WebkitTransition: 'webkitTransitionEnd',
|
||||
MozTransition: 'transitionend',
|
||||
OTransition: 'oTransitionEnd otransitionend',
|
||||
transition: 'transitionend'
|
||||
};
|
||||
|
||||
for (var name in transEndEventNames) {
|
||||
if (el.style[name] !== undefined) {
|
||||
return { end: transEndEventNames[name] };
|
||||
}
|
||||
}
|
||||
|
||||
return false; // explicit for ie8 ( ._.)
|
||||
}
|
||||
|
||||
// http://blog.alexmaccaw.com/css-transitions
|
||||
$.fn.emulateTransitionEnd = function(duration) {
|
||||
var called = false;
|
||||
var $el = this;
|
||||
$(this).one('bsTransitionEnd', function() { called = true; });
|
||||
var callback = function() { if (!called) $($el).trigger($.support.transition.end); };
|
||||
setTimeout(callback, duration);
|
||||
return this;
|
||||
};
|
||||
|
||||
$(function() {
|
||||
$.support.transition = transitionEnd();
|
||||
|
||||
if (!$.support.transition) return;
|
||||
|
||||
$.event.special.bsTransitionEnd = {
|
||||
bindType: $.support.transition.end,
|
||||
delegateType: $.support.transition.end,
|
||||
handle: function(e) {
|
||||
if ($(e.target).is(this)) return e.handleObj.handler.apply(this, arguments);
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
}(jQuery));
|
||||
25
user/plugins/admin/themes/grav/app/utils/changelog.js
Normal file
25
user/plugins/admin/themes/grav/app/utils/changelog.js
Normal file
@@ -0,0 +1,25 @@
|
||||
/* eslint-disable */
|
||||
import $ from 'jquery';
|
||||
|
||||
let TRIGGER = null;
|
||||
|
||||
$(document).on('click', '[data-remodal-changelog]', (event) => {
|
||||
TRIGGER = event.currentTarget;
|
||||
});
|
||||
|
||||
$(document).on('opened', '[data-remodal-id="changelog"]', () => {
|
||||
const instance = $.remodal.lookup[$('[data-remodal-id=changelog]').data('remodal')];
|
||||
instance.$modal.html('<div class="changelog-overflow center" style="padding:5rem 0;text-align:center;"><i class="fa fa-spinner fa-spin fa-3x fa-fw"></i></div>');
|
||||
if (!TRIGGER) { return true; }
|
||||
|
||||
const url = $(TRIGGER).data('remodalChangelog');
|
||||
|
||||
$.ajax({url: url}).done(function(data) {
|
||||
instance.$modal.html(data);
|
||||
});
|
||||
});
|
||||
|
||||
$(document).on('closed', '[data-remodal-id="changelog"]', () => {
|
||||
const instance = $.remodal.lookup[$('[data-remodal-id=changelog]').data('remodal')];
|
||||
instance.$modal.html('');
|
||||
});
|
||||
152
user/plugins/admin/themes/grav/app/utils/colors.js
Normal file
152
user/plugins/admin/themes/grav/app/utils/colors.js
Normal file
@@ -0,0 +1,152 @@
|
||||
// Parses a string and returns a valid hex string when possible
|
||||
// parseHex('#fff') => '#ffffff'
|
||||
export const parseHex = (string) => {
|
||||
string = string.replace(/[^A-F0-9]/ig, '');
|
||||
if (string.length !== 3 && string.length !== 6) return '';
|
||||
if (string.length === 3) {
|
||||
string = string[0] + string[0] + string[1] + string[1] + string[2] + string[2];
|
||||
}
|
||||
|
||||
return '#' + string.toLowerCase();
|
||||
};
|
||||
|
||||
// Converts an HSB object to an RGB object
|
||||
// hsb2rgb({h: 0, s: 0, b: 100}) => {r: 255, g: 255, b: 255}
|
||||
export const hsb2rgb = (hsb) => {
|
||||
let rgb = {};
|
||||
let h = Math.round(hsb.h);
|
||||
let s = Math.round(hsb.s * 255 / 100);
|
||||
let v = Math.round(hsb.b * 255 / 100);
|
||||
if (s === 0) {
|
||||
rgb.r = rgb.g = rgb.b = v;
|
||||
} else {
|
||||
var t1 = v;
|
||||
var t2 = (255 - s) * v / 255;
|
||||
var t3 = (t1 - t2) * (h % 60) / 60;
|
||||
if (h === 360) h = 0;
|
||||
if (h < 60) {
|
||||
rgb.r = t1;
|
||||
rgb.b = t2;
|
||||
rgb.g = t2 + t3;
|
||||
} else if (h < 120) {
|
||||
rgb.g = t1;
|
||||
rgb.b = t2;
|
||||
rgb.r = t1 - t3;
|
||||
} else if (h < 180) {
|
||||
rgb.g = t1;
|
||||
rgb.r = t2;
|
||||
rgb.b = t2 + t3;
|
||||
} else if (h < 240) {
|
||||
rgb.b = t1;
|
||||
rgb.r = t2;
|
||||
rgb.g = t1 - t3;
|
||||
} else if (h < 300) {
|
||||
rgb.b = t1;
|
||||
rgb.g = t2;
|
||||
rgb.r = t2 + t3;
|
||||
} else if (h < 360) {
|
||||
rgb.r = t1;
|
||||
rgb.g = t2;
|
||||
rgb.b = t1 - t3;
|
||||
} else {
|
||||
rgb.r = 0;
|
||||
rgb.g = 0;
|
||||
rgb.b = 0;
|
||||
}
|
||||
}
|
||||
return {
|
||||
r: Math.round(rgb.r),
|
||||
g: Math.round(rgb.g),
|
||||
b: Math.round(rgb.b)
|
||||
};
|
||||
};
|
||||
|
||||
// Converts an RGB object to a HEX string
|
||||
// rgb2hex({r: 255, g: 255, b: 255}) => #ffffff
|
||||
export const rgb2hex = (rgb) => {
|
||||
var hex = [
|
||||
rgb.r.toString(16),
|
||||
rgb.g.toString(16),
|
||||
rgb.b.toString(16)
|
||||
];
|
||||
|
||||
hex.forEach((val, nr) => {
|
||||
if (val.length === 1) hex[nr] = '0' + val;
|
||||
});
|
||||
|
||||
return '#' + hex.join('');
|
||||
};
|
||||
|
||||
// Converts and RGB(a) string to a HEX string
|
||||
// rgbstr2hex('rgba(255, 255, 255, 0.5)') => #ffffff
|
||||
export const rgbstr2hex = (rgb) => {
|
||||
rgb = rgb.match(/^rgba?[\s+]?\([\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?,[\s+]?(\d+)[\s+]?/i);
|
||||
|
||||
return (rgb && rgb.length === 4) ? '#' +
|
||||
('0' + parseInt(rgb[1], 10).toString(16)).slice(-2) +
|
||||
('0' + parseInt(rgb[2], 10).toString(16)).slice(-2) +
|
||||
('0' + parseInt(rgb[3], 10).toString(16)).slice(-2) : '';
|
||||
};
|
||||
|
||||
// Converts an HSB object to a HEX string
|
||||
// hsb2hex({h: 0, s: 0, b: 100}) => #ffffff
|
||||
export const hsb2hex = (hsb) => {
|
||||
return rgb2hex(hsb2rgb(hsb));
|
||||
};
|
||||
|
||||
// Converts a HEX string to an HSB object
|
||||
// hex2hsb('#ffffff') => {h: 0, s: 0, b: 100}
|
||||
export const hex2hsb = (hex) => {
|
||||
let hsb = rgb2hsb(hex2rgb(hex));
|
||||
if (hsb.s === 0) hsb.h = 360;
|
||||
|
||||
return hsb;
|
||||
};
|
||||
|
||||
// Converts an RGB object to an HSB object
|
||||
// rgb2hsb({r: 255, g: 255, b: 255}) => {h: 0, s: 0, b: 100}
|
||||
export const rgb2hsb = (rgb) => {
|
||||
let hsb = {
|
||||
h: 0,
|
||||
s: 0,
|
||||
b: 0
|
||||
};
|
||||
let min = Math.min(rgb.r, rgb.g, rgb.b);
|
||||
let max = Math.max(rgb.r, rgb.g, rgb.b);
|
||||
let delta = max - min;
|
||||
hsb.b = max;
|
||||
hsb.s = max !== 0 ? 255 * delta / max : 0;
|
||||
if (hsb.s !== 0) {
|
||||
if (rgb.r === max) {
|
||||
hsb.h = (rgb.g - rgb.b) / delta;
|
||||
} else if (rgb.g === max) {
|
||||
hsb.h = 2 + (rgb.b - rgb.r) / delta;
|
||||
} else {
|
||||
hsb.h = 4 + (rgb.r - rgb.g) / delta;
|
||||
}
|
||||
} else {
|
||||
hsb.h = -1;
|
||||
}
|
||||
hsb.h *= 60;
|
||||
if (hsb.h < 0) {
|
||||
hsb.h += 360;
|
||||
}
|
||||
hsb.s *= 100 / 255;
|
||||
hsb.b *= 100 / 255;
|
||||
|
||||
return hsb;
|
||||
};
|
||||
|
||||
// Converts a HEX string to an RGB object
|
||||
// hex2rgb('#ffffff') => {r: 255, g: 255, b: 255}
|
||||
export const hex2rgb = (hex) => {
|
||||
hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16);
|
||||
|
||||
return {
|
||||
/* jshint ignore:start */
|
||||
r: hex >> 16,
|
||||
g: (hex & 0x00FF00) >> 8,
|
||||
b: (hex & 0x0000FF)
|
||||
/* jshint ignore:end */
|
||||
};
|
||||
};
|
||||
164
user/plugins/admin/themes/grav/app/utils/cookies.js
Normal file
164
user/plugins/admin/themes/grav/app/utils/cookies.js
Normal file
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* Cookies.js - 1.2.3-grav
|
||||
* https://github.com/ScottHamper/Cookies
|
||||
*
|
||||
* With SameSite support by Grav
|
||||
*
|
||||
* This is free and unencumbered software released into the public domain.
|
||||
*/
|
||||
|
||||
const factory = function(window) {
|
||||
if (typeof window.document !== 'object') {
|
||||
throw new Error('Cookies.js requires a `window` with a `document` object');
|
||||
}
|
||||
|
||||
const Cookies = (key, value, options) => {
|
||||
return arguments.length === 1
|
||||
? Cookies.get(key)
|
||||
: Cookies.set(key, value, options);
|
||||
};
|
||||
|
||||
// Allows for setter injection in unit tests
|
||||
Cookies._document = window.document;
|
||||
|
||||
// Used to ensure cookie keys do not collide with
|
||||
// built-in `Object` properties
|
||||
Cookies._cacheKeyPrefix = 'cookey.'; // Hurr hurr, :)
|
||||
|
||||
Cookies._maxExpireDate = new Date('Fri, 31 Dec 9999 23:59:59 UTC');
|
||||
|
||||
Cookies.defaults = {
|
||||
path: '/',
|
||||
secure: false,
|
||||
sameSite: 'Lax'
|
||||
};
|
||||
|
||||
Cookies.get = (key) => {
|
||||
if (Cookies._cachedDocumentCookie !== Cookies._document.cookie) {
|
||||
Cookies._renewCache();
|
||||
}
|
||||
|
||||
const value = Cookies._cache[Cookies._cacheKeyPrefix + key];
|
||||
|
||||
return value === undefined ? undefined : decodeURIComponent(value);
|
||||
};
|
||||
|
||||
Cookies.set = (key, value, options) => {
|
||||
options = Cookies._getExtendedOptions(options);
|
||||
options.expires = Cookies._getExpiresDate(value === undefined ? -1 : options.expires);
|
||||
|
||||
Cookies._document.cookie = Cookies._generateCookieString(key, value, options);
|
||||
|
||||
return Cookies;
|
||||
};
|
||||
|
||||
Cookies.expire = (key, options) => {
|
||||
return Cookies.set(key, undefined, options);
|
||||
};
|
||||
|
||||
Cookies._getExtendedOptions = (options) => {
|
||||
return {
|
||||
path: options && options.path || Cookies.defaults.path,
|
||||
domain: options && options.domain || Cookies.defaults.domain,
|
||||
expires: options && options.expires || Cookies.defaults.expires,
|
||||
secure: options && options.secure !== undefined ? options.secure : Cookies.defaults.secure,
|
||||
sameSite: options && options.sameSite || Cookies.defaults.sameSite
|
||||
};
|
||||
};
|
||||
|
||||
Cookies._isValidDate = (date) => {
|
||||
return Object.prototype.toString.call(date) === '[object Date]' && !isNaN(date.getTime());
|
||||
};
|
||||
|
||||
Cookies._getExpiresDate = (expires, now) => {
|
||||
now = now || new Date();
|
||||
|
||||
if (typeof expires === 'number') {
|
||||
expires = expires === Infinity
|
||||
? Cookies._maxExpireDate
|
||||
: new Date(now.getTime() + expires * 1000);
|
||||
} else if (typeof expires === 'string') {
|
||||
expires = new Date(expires);
|
||||
}
|
||||
|
||||
if (expires && !Cookies._isValidDate(expires)) {
|
||||
throw new Error('`expires` parameter cannot be converted to a valid Date instance');
|
||||
}
|
||||
|
||||
return expires;
|
||||
};
|
||||
|
||||
Cookies._generateCookieString = (key, value, options) => {
|
||||
key = key.replace(/[^#$&+\^`|]/g, encodeURIComponent);
|
||||
key = key.replace(/\(/g, '%28').replace(/\)/g, '%29');
|
||||
value = (value + '').replace(/[^!#$&-+\--:<-\[\]-~]/g, encodeURIComponent);
|
||||
options = options || {};
|
||||
|
||||
let cookieString = key + '=' + value;
|
||||
cookieString += options.path ? ';path=' + options.path : '';
|
||||
cookieString += options.domain ? ';domain=' + options.domain : '';
|
||||
cookieString += options.expires ? ';expires=' + options.expires.toUTCString() : '';
|
||||
cookieString += options.secure ? ';secure' : '';
|
||||
cookieString += options.sameSite ? ';SameSite=' + options.sameSite : '';
|
||||
|
||||
return cookieString;
|
||||
};
|
||||
|
||||
Cookies._getCacheFromString = (documentCookie) => {
|
||||
let cookieCache = {};
|
||||
const cookiesArray = documentCookie ? documentCookie.split('; ') : [];
|
||||
|
||||
for (let i = 0; i < cookiesArray.length; i++) {
|
||||
const cookieKvp = Cookies._getKeyValuePairFromCookieString(cookiesArray[i]);
|
||||
|
||||
if (cookieCache[Cookies._cacheKeyPrefix + cookieKvp.key] === undefined) {
|
||||
cookieCache[Cookies._cacheKeyPrefix + cookieKvp.key] = cookieKvp.value;
|
||||
}
|
||||
}
|
||||
|
||||
return cookieCache;
|
||||
};
|
||||
|
||||
Cookies._getKeyValuePairFromCookieString = (cookieString) => {
|
||||
// "=" is a valid character in a cookie value according to RFC6265, so cannot `split('=')`
|
||||
let separatorIndex = cookieString.indexOf('=');
|
||||
|
||||
// IE omits the "=" when the cookie value is an empty string
|
||||
separatorIndex = separatorIndex < 0 ? cookieString.length : separatorIndex;
|
||||
|
||||
const key = cookieString.substr(0, separatorIndex);
|
||||
let decodedKey;
|
||||
try {
|
||||
decodedKey = decodeURIComponent(key);
|
||||
} catch (e) {
|
||||
if (console && typeof console.error === 'function') {
|
||||
console.error('Could not decode cookie with key "' + key + '"', e);
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
key: decodedKey,
|
||||
value: cookieString.substr(separatorIndex + 1) // Defer decoding value until accessed
|
||||
};
|
||||
};
|
||||
|
||||
Cookies._renewCache = () => {
|
||||
Cookies._cache = Cookies._getCacheFromString(Cookies._document.cookie);
|
||||
Cookies._cachedDocumentCookie = Cookies._document.cookie;
|
||||
};
|
||||
|
||||
Cookies._areEnabled = () => {
|
||||
const testKey = 'cookies.js';
|
||||
const areEnabled = Cookies.set(testKey, 1).get(testKey) === '1';
|
||||
Cookies.expire(testKey);
|
||||
return areEnabled;
|
||||
};
|
||||
|
||||
Cookies.enabled = Cookies._areEnabled();
|
||||
|
||||
return Cookies;
|
||||
};
|
||||
|
||||
global.Cookies = (global && typeof global.document === 'object') ? factory(global) : factory;
|
||||
|
||||
export default global.Cookies;
|
||||
864
user/plugins/admin/themes/grav/app/utils/cron-ui.js
Normal file
864
user/plugins/admin/themes/grav/app/utils/cron-ui.js
Normal file
@@ -0,0 +1,864 @@
|
||||
/* eslint-disable */
|
||||
import $ from 'jquery';
|
||||
/*
|
||||
* This file is part of the Arnapou jqCron package.
|
||||
*
|
||||
* (c) Arnaud Buathier <arnaud@arnapou.net>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Default settings
|
||||
*/
|
||||
var jqCronDefaultSettings = {
|
||||
texts: {},
|
||||
monthdays: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31],
|
||||
hours: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23],
|
||||
hour_labels: ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12", "13", "14", "15", "16", "17", "18", "19", "20", "21", "22", "23"],
|
||||
minutes: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
|
||||
lang: 'en',
|
||||
enabled_minute: false,
|
||||
enabled_hour: true,
|
||||
enabled_day: true,
|
||||
enabled_week: true,
|
||||
enabled_month: true,
|
||||
enabled_year: true,
|
||||
multiple_dom: false,
|
||||
multiple_month: false,
|
||||
multiple_mins: false,
|
||||
multiple_dow: false,
|
||||
multiple_time_hours: false,
|
||||
multiple_time_minutes: false,
|
||||
numeric_zero_pad: false,
|
||||
default_period: 'day',
|
||||
default_value: '',
|
||||
no_reset_button: true,
|
||||
disabled: false,
|
||||
bind_to: null,
|
||||
bind_method: {
|
||||
set: function($element, value) {
|
||||
$element.is(':input') ? $element.val(value) : $element.data('jqCronValue', value);
|
||||
},
|
||||
get: function($element) {
|
||||
return $element.is(':input') ? $element.val() : $element.data('jqCronValue');
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Custom extend of json for jqCron settings.
|
||||
* We don't use jQuery.extend because simple extend does not fit our needs, and deep extend has a bad
|
||||
* feature for us : it replaces keys of "Arrays" instead of replacing the full array.
|
||||
*/
|
||||
(function($){
|
||||
var extend = function(dst, src) {
|
||||
for(var i in src) {
|
||||
if($.isPlainObject(src[i])) {
|
||||
dst[i] = extend(dst[i] && $.isPlainObject(dst[i]) ? dst[i] : {}, src[i]);
|
||||
}
|
||||
else if($.isArray(src[i])) {
|
||||
dst[i] = src[i].slice(0);
|
||||
}
|
||||
else if(src[i] !== undefined) {
|
||||
dst[i] = src[i];
|
||||
}
|
||||
}
|
||||
return dst;
|
||||
};
|
||||
this.jqCronMergeSettings = function(obj) {
|
||||
return extend(extend({}, jqCronDefaultSettings), obj || {});
|
||||
};
|
||||
}).call(window, $);
|
||||
|
||||
/**
|
||||
* Shortcut to get the instance of jqCron instance from one jquery object
|
||||
*/
|
||||
(function($){
|
||||
$.fn.jqCronGetInstance = function() {
|
||||
return this.data('jqCron');
|
||||
};
|
||||
}).call(window, $);
|
||||
|
||||
/**
|
||||
* Main plugin
|
||||
*/
|
||||
(function($){
|
||||
$.fn.jqCron = function(settings) {
|
||||
var saved_settings = settings;
|
||||
return this.each(function() {
|
||||
var cron, saved;
|
||||
var $this = $(this);
|
||||
var settings = jqCronMergeSettings(saved_settings); // clone settings
|
||||
var translations = settings.texts[settings.lang];
|
||||
|
||||
if (typeof(translations) !== 'object' || $.isEmptyObject(translations)) {
|
||||
console && console.error(
|
||||
'Missing translations for language "' + settings.lang + '". ' +
|
||||
'Please include jqCron.' + settings.lang + '.js or manually provide ' +
|
||||
'the necessary translations when calling $.fn.jqCron().'
|
||||
);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!settings.jquery_container) {
|
||||
if($this.is(':container')) {
|
||||
settings.jquery_element = $this.uniqueId('jqCron');
|
||||
}
|
||||
else if($this.is(':autoclose')) {
|
||||
// delete already generated dom if exists
|
||||
if($this.next('.jqCron').length == 1) {
|
||||
$this.next('.jqCron').remove();
|
||||
}
|
||||
// generate new
|
||||
settings.jquery_element = $('<span class="jqCron"></span>').uniqueId('jqCron').insertAfter($this);
|
||||
}
|
||||
else {
|
||||
console && console.error(settings.texts[settings.lang].error1.replace('%s', this.tagName));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// autoset bind_to if it is an input
|
||||
if($this.is(':input')) {
|
||||
settings.bind_to = settings.bind_to || $this;
|
||||
}
|
||||
|
||||
// init cron object
|
||||
if(settings.bind_to){
|
||||
if(settings.bind_to.is(':input')) {
|
||||
// auto bind from input to object if an input, textarea ...
|
||||
settings.bind_to.blur(function(){
|
||||
var value = settings.bind_method.get(settings.bind_to);
|
||||
$this.jqCronGetInstance().setCron(value);
|
||||
});
|
||||
}
|
||||
saved = settings.bind_method.get(settings.bind_to);
|
||||
cron = new jqCron(settings);
|
||||
cron.setCron(saved);
|
||||
}
|
||||
else {
|
||||
cron = new jqCron(settings);
|
||||
}
|
||||
$(this).data('jqCron', cron);
|
||||
});
|
||||
};
|
||||
}).call(window, $);
|
||||
|
||||
/**
|
||||
* jqCron class
|
||||
*/
|
||||
(function($){
|
||||
var jqCronInstances = [];
|
||||
|
||||
function jqCron(settings) {
|
||||
var _initialized = false;
|
||||
var _self = this;
|
||||
var _$elt = this;
|
||||
var _$obj = $('<span class="jqCron-container"></span>');
|
||||
var _$blocks = $('<span class="jqCron-blocks"></span>');
|
||||
var _$blockPERIOD = $('<span class="jqCron-period"></span>');
|
||||
var _$blockDOM = $('<span class="jqCron-dom"></span>');
|
||||
var _$blockMONTH = $('<span class="jqCron-month"></span>');
|
||||
var _$blockMINS = $('<span class="jqCron-mins"></span>');
|
||||
var _$blockDOW = $('<span class="jqCron-dow"></span>');
|
||||
var _$blockTIME = $('<span class="jqCron-time"></span>');
|
||||
var _$cross = $('<span class="jqCron-cross">✘</span>');
|
||||
var _selectors = [];
|
||||
var _selectorPeriod, _selectorMins, _selectorTimeH, _selectorTimeM, _selectorDow, _selectorDom, _selectorMonth;
|
||||
|
||||
// instanciate a new selector
|
||||
function newSelector($block, multiple, type){
|
||||
var selector = new jqCronSelector(_self, $block, multiple, type);
|
||||
selector.$.bind('selector:open', function(){
|
||||
// we close all opened selectors of all other jqCron
|
||||
for(var n = jqCronInstances.length; n--; ){
|
||||
if(jqCronInstances[n] != _self) {
|
||||
jqCronInstances[n].closeSelectors();
|
||||
}
|
||||
else {
|
||||
// we close all other opened selectors of this jqCron
|
||||
for(var o = _selectors.length; o--; ){
|
||||
if(_selectors[o] != selector) {
|
||||
_selectors[o].close();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
selector.$.bind('selector:change', function(){
|
||||
var boundChanged = false;
|
||||
// don't propagate if not initialized
|
||||
if(!_initialized) return;
|
||||
// bind data between two minute selectors (only if they have the same multiple settings)
|
||||
if(settings.multiple_mins == settings.multiple_time_minutes) {
|
||||
if(selector == _selectorMins) {
|
||||
boundChanged = _selectorTimeM.setValue(_selectorMins.getValue());
|
||||
}
|
||||
else if(selector == _selectorTimeM) {
|
||||
boundChanged = _selectorMins.setValue(_selectorTimeM.getValue());
|
||||
}
|
||||
}
|
||||
// we propagate the change event to the main object
|
||||
boundChanged || _$obj.trigger('cron:change', _self.getCron());
|
||||
});
|
||||
_selectors.push(selector);
|
||||
return selector;
|
||||
}
|
||||
|
||||
// disable the selector
|
||||
this.disable = function(){
|
||||
_$obj.addClass('disable');
|
||||
settings.disable = true;
|
||||
_self.closeSelectors();
|
||||
};
|
||||
|
||||
// return if the selector is disabled
|
||||
this.isDisabled = function() {
|
||||
return settings.disable == true;
|
||||
};
|
||||
|
||||
// enable the selector
|
||||
this.enable = function(){
|
||||
_$obj.removeClass('disable');
|
||||
settings.disable = false;
|
||||
};
|
||||
|
||||
// get cron value
|
||||
this.getCron = function(){
|
||||
var period = _selectorPeriod.getValue();
|
||||
var items = ['*', '*', '*', '*', '*'];
|
||||
if(period == 'hour') {
|
||||
items[0] = _selectorMins.getCronValue();
|
||||
}
|
||||
if(period == 'day' || period == 'week' || period == 'month' || period == 'year') {
|
||||
items[0] = _selectorTimeM.getCronValue();
|
||||
items[1] = _selectorTimeH.getCronValue();
|
||||
}
|
||||
if(period == 'month' || period == 'year') {
|
||||
items[2] = _selectorDom.getCronValue();
|
||||
}
|
||||
if(period == 'year') {
|
||||
items[3] = _selectorMonth.getCronValue();
|
||||
}
|
||||
if(period == 'week') {
|
||||
items[4] = _selectorDow.getCronValue();
|
||||
}
|
||||
return items.join(' ');
|
||||
};
|
||||
|
||||
// set cron (string like * * * * *)
|
||||
this.setCron = function(str) {
|
||||
if(!str) return;
|
||||
try {
|
||||
str = str.replace(/\s+/g, ' ').replace(/^ +/, '').replace(/ +$/, ''); // sanitize
|
||||
var mask = str.replace(/[^\* ]/g, '-').replace(/-+/g, '-').replace(/ +/g, '');
|
||||
var items = str.split(' ');
|
||||
if (items.length != 5) _self.error(_self.getText('error2'));
|
||||
if(mask == '*****') { // 1 possibility
|
||||
_selectorPeriod.setValue('minute');
|
||||
}
|
||||
else if(mask == '-****') { // 1 possibility
|
||||
_selectorPeriod.setValue('hour');
|
||||
_selectorMins.setCronValue(items[0]);
|
||||
_selectorTimeM.setCronValue(items[0]);
|
||||
}
|
||||
else if(mask.substring(2, mask.length) == '***') { // 4 possibilities
|
||||
_selectorPeriod.setValue('day');
|
||||
_selectorMins.setCronValue(items[0]);
|
||||
_selectorTimeM.setCronValue(items[0]);
|
||||
_selectorTimeH.setCronValue(items[1]);
|
||||
}
|
||||
else if(mask.substring(2, mask.length) == '-**') { // 4 possibilities
|
||||
_selectorPeriod.setValue('month');
|
||||
_selectorMins.setCronValue(items[0]);
|
||||
_selectorTimeM.setCronValue(items[0]);
|
||||
_selectorTimeH.setCronValue(items[1]);
|
||||
_selectorDom.setCronValue(items[2]);
|
||||
}
|
||||
else if(mask.substring(2, mask.length) == '**-') { // 4 possibilities
|
||||
_selectorPeriod.setValue('week');
|
||||
_selectorMins.setCronValue(items[0]);
|
||||
_selectorTimeM.setCronValue(items[0]);
|
||||
_selectorTimeH.setCronValue(items[1]);
|
||||
_selectorDow.setCronValue(items[4]);
|
||||
}
|
||||
else if (mask.substring(3, mask.length) == '-*') { // 8 possibilities
|
||||
_selectorPeriod.setValue('year');
|
||||
_selectorMins.setCronValue(items[0]);
|
||||
_selectorTimeM.setCronValue(items[0]);
|
||||
_selectorTimeH.setCronValue(items[1]);
|
||||
_selectorDom.setCronValue(items[2]);
|
||||
_selectorMonth.setCronValue(items[3]);
|
||||
}
|
||||
else {
|
||||
_self.error(_self.getText('error4'));
|
||||
}
|
||||
_self.clearError();
|
||||
} catch(e) {}
|
||||
};
|
||||
|
||||
// close all child selectors
|
||||
this.closeSelectors = function(){
|
||||
for(var n = _selectors.length; n--; ){
|
||||
_selectors[n].close();
|
||||
}
|
||||
};
|
||||
|
||||
// get the main element id
|
||||
this.getId = function(){
|
||||
return _$elt.attr('id');
|
||||
}
|
||||
|
||||
// get the translated text
|
||||
this.getText = function(key) {
|
||||
var text = settings.texts[settings.lang][key] || null;
|
||||
if(typeof(text) == "string" && text.match('<b')){
|
||||
text = text.replace(/(<b *\/>)/gi, '</span><b /><span class="jqCron-text">');
|
||||
text = '<span class="jqCron-text">' + text + '</span>';
|
||||
}
|
||||
return text;
|
||||
};
|
||||
|
||||
// get the human readable text
|
||||
this.getHumanText = function() {
|
||||
var texts=[];
|
||||
_$obj
|
||||
.find('> span > span:visible')
|
||||
.find('.jqCron-text, .jqCron-selector > span')
|
||||
.each(function() {
|
||||
var text = $(this).text().replace(/\s+$/g, '').replace(/^\s+/g, '');
|
||||
text && texts.push(text);
|
||||
});
|
||||
return texts.join(' ').replace(/\s:\s/g, ':');
|
||||
}
|
||||
|
||||
// get settings
|
||||
this.getSettings = function(){
|
||||
return settings;
|
||||
};
|
||||
|
||||
// display an error
|
||||
this.error = function(msg) {
|
||||
console && console.error('[jqCron Error] ' + msg);
|
||||
_$obj.addClass('jqCron-error').attr('title', msg);
|
||||
throw msg;
|
||||
};
|
||||
|
||||
// clear error
|
||||
this.clearError = function(){
|
||||
_$obj.attr('title', '').removeClass('jqCron-error');
|
||||
};
|
||||
|
||||
// clear
|
||||
this.clear = function() {
|
||||
_selectorDom.setValue([]);
|
||||
_selectorDow.setValue([]);
|
||||
_selectorMins.setValue([]);
|
||||
_selectorMonth.setValue([]);
|
||||
_selectorTimeH.setValue([]);
|
||||
_selectorTimeM.setValue([]);
|
||||
_self.triggerChange();
|
||||
};
|
||||
|
||||
// init (called in constructor)
|
||||
this.init = function(){
|
||||
var n,i,labelsList,list;
|
||||
if(_initialized) return;
|
||||
|
||||
settings = jqCronMergeSettings(settings);
|
||||
settings.jquery_element || _self.error(_self.getText('error3'));
|
||||
_$elt = settings.jquery_element;
|
||||
_$elt.append(_$obj);
|
||||
_$obj.data('id', settings.id);
|
||||
_$obj.data('jqCron', _self);
|
||||
_$obj.append(_$blocks);
|
||||
settings.no_reset_button || _$obj.append(_$cross);
|
||||
(!settings.disable) || _$obj.addClass('disable');
|
||||
_$blocks.append(_$blockPERIOD);
|
||||
|
||||
if ( /^(ko)$/i.test(settings.lang) )
|
||||
{
|
||||
_$blocks.append(_$blockMONTH, _$blockDOM);
|
||||
}
|
||||
else
|
||||
{
|
||||
_$blocks.append(_$blockDOM, _$blockMONTH);
|
||||
}
|
||||
|
||||
_$blocks.append(_$blockMINS);
|
||||
_$blocks.append(_$blockDOW);
|
||||
_$blocks.append(_$blockTIME);
|
||||
|
||||
// various binding
|
||||
_$cross.click(function(){
|
||||
_self.isDisabled() || _self.clear();
|
||||
});
|
||||
|
||||
// binding from cron to target
|
||||
_$obj.bind('cron:change', function(evt, value){
|
||||
if(!settings.bind_to) return;
|
||||
settings.bind_method.set && settings.bind_method.set(settings.bind_to, value);
|
||||
_self.clearError();
|
||||
});
|
||||
|
||||
// PERIOD
|
||||
_$blockPERIOD.append(_self.getText('text_period'));
|
||||
_selectorPeriod = newSelector(_$blockPERIOD, false, 'period');
|
||||
settings.enabled_minute && _selectorPeriod.add('minute', _self.getText('name_minute'));
|
||||
settings.enabled_hour && _selectorPeriod.add('hour', _self.getText('name_hour'));
|
||||
settings.enabled_day && _selectorPeriod.add('day', _self.getText('name_day'));
|
||||
settings.enabled_week && _selectorPeriod.add('week', _self.getText('name_week'));
|
||||
settings.enabled_month && _selectorPeriod.add('month', _self.getText('name_month'));
|
||||
settings.enabled_year && _selectorPeriod.add('year', _self.getText('name_year'));
|
||||
_selectorPeriod.$.bind('selector:change', function(e, value){
|
||||
_$blockDOM.hide();
|
||||
_$blockMONTH.hide();
|
||||
_$blockMINS.hide();
|
||||
_$blockDOW.hide();
|
||||
_$blockTIME.hide();
|
||||
if(value == 'hour') {
|
||||
_$blockMINS.show();
|
||||
}
|
||||
else if(value == 'day') {
|
||||
_$blockTIME.show();
|
||||
}
|
||||
else if(value == 'week') {
|
||||
_$blockDOW.show();
|
||||
_$blockTIME.show();
|
||||
}
|
||||
else if(value == 'month') {
|
||||
_$blockDOM.show();
|
||||
_$blockTIME.show();
|
||||
}
|
||||
else if(value == 'year') {
|
||||
_$blockDOM.show();
|
||||
_$blockMONTH.show();
|
||||
_$blockTIME.show();
|
||||
}
|
||||
});
|
||||
_selectorPeriod.setValue(settings.default_period);
|
||||
|
||||
// MINS (minutes)
|
||||
_$blockMINS.append(_self.getText('text_mins'));
|
||||
_selectorMins = newSelector(_$blockMINS, settings.multiple_mins, 'minutes');
|
||||
for(i=0, list=settings.minutes; i<list.length; i++){
|
||||
_selectorMins.add(list[i], list[i]);
|
||||
}
|
||||
|
||||
// TIME (hour:min)
|
||||
_$blockTIME.append(_self.getText('text_time'));
|
||||
_selectorTimeH = newSelector(_$blockTIME, settings.multiple_time_hours, 'time_hours');
|
||||
for(i=0, list=settings.hours, labelsList=settings.hour_labels; i<list.length; i++){
|
||||
_selectorTimeH.add(list[i], labelsList[i]);
|
||||
}
|
||||
_selectorTimeM = newSelector(_$blockTIME, settings.multiple_time_minutes, 'time_minutes');
|
||||
for(i=0, list=settings.minutes; i<list.length; i++){
|
||||
_selectorTimeM.add(list[i], list[i]);
|
||||
}
|
||||
|
||||
// DOW (day of week)
|
||||
_$blockDOW.append(_self.getText('text_dow'));
|
||||
_selectorDow = newSelector(_$blockDOW, settings.multiple_dow, 'day_of_week');
|
||||
for(i=0, list=_self.getText('weekdays'); i<list.length; i++){
|
||||
_selectorDow.add(i+1, list[i]);
|
||||
}
|
||||
|
||||
// DOM (day of month)
|
||||
_$blockDOM.append(_self.getText('text_dom'));
|
||||
_selectorDom = newSelector(_$blockDOM, settings.multiple_dom, 'day_of_month');
|
||||
for(i=0, list=settings.monthdays; i<list.length; i++){
|
||||
_selectorDom.add(list[i], list[i]);
|
||||
}
|
||||
|
||||
// MONTH (day of week)
|
||||
_$blockMONTH.append(_self.getText('text_month'));
|
||||
_selectorMonth = newSelector(_$blockMONTH, settings.multiple_month, 'month');
|
||||
for(i=0, list=_self.getText('months'); i<list.length; i++){
|
||||
_selectorMonth.add(i+1, list[i]);
|
||||
}
|
||||
|
||||
// close all selectors when we click in body
|
||||
$('body').click(function(){
|
||||
var i, n = _selectors.length;
|
||||
for(i = 0; i < n; i++){
|
||||
_selectors[i].close();
|
||||
}
|
||||
});
|
||||
_initialized = true;
|
||||
|
||||
// default value
|
||||
if(settings.default_value) {
|
||||
_self.setCron(settings.default_value);
|
||||
}
|
||||
};
|
||||
|
||||
// trigger a change event
|
||||
this.triggerChange = function(){
|
||||
_$obj.trigger('cron:change', _self.getCron());
|
||||
};
|
||||
|
||||
// store instance in array
|
||||
jqCronInstances.push(this);
|
||||
|
||||
// expose main jquery object
|
||||
this.$ = _$obj;
|
||||
|
||||
// init
|
||||
try {
|
||||
this.init();
|
||||
_self.triggerChange();
|
||||
} catch(e){}
|
||||
}
|
||||
this.jqCron = jqCron;
|
||||
}).call(window, $);
|
||||
|
||||
|
||||
/**
|
||||
* jqCronSelector class
|
||||
*/
|
||||
(function($){
|
||||
function jqCronSelector(_cron, _$block, _multiple, _type){
|
||||
var _self = this;
|
||||
var _$list = $('<ul class="jqCron-selector-list"></ul>');
|
||||
var _$title = $('<span class="jqCron-selector-title"></span>');
|
||||
var _$selector = $('<span class="jqCron-selector"></span>');
|
||||
var _values = {};
|
||||
var _value = [];
|
||||
var _hasNumericTexts = true;
|
||||
var _numeric_zero_pad = _cron.getSettings().numeric_zero_pad;
|
||||
|
||||
// return an array without doublon
|
||||
function array_unique(l){
|
||||
var i=0,n=l.length,k={},a=[];
|
||||
while(i<n) {
|
||||
k[l[i]] || (k[l[i]] = 1 && a.push(l[i]));
|
||||
i++;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
|
||||
// get the value (an array if multiple, else a single value)
|
||||
this.getValue = function(){
|
||||
return _multiple ? _value : _value[0];
|
||||
};
|
||||
|
||||
// get a correct string for cron
|
||||
this.getCronValue = function(){
|
||||
if(_value.length == 0) return '*';
|
||||
var cron = [_value[0]], i, s = _value[0], c = _value[0], n = _value.length;
|
||||
for(i=1; i<n; i++) {
|
||||
if(_value[i] == c+1) {
|
||||
c = _value[i];
|
||||
cron[cron.length-1] = s+'-'+c;
|
||||
}
|
||||
else {
|
||||
s = c = _value[i];
|
||||
cron.push(c);
|
||||
}
|
||||
}
|
||||
return cron.join(',');
|
||||
};
|
||||
|
||||
// set the cron value
|
||||
this.setCronValue = function(str) {
|
||||
var values = [], m ,i, n;
|
||||
if(str !== '*') {
|
||||
while(str != '') {
|
||||
// test "*/n" expression
|
||||
m = str.match(/^\*\/([0-9]+),?/);
|
||||
if(m && m.length == 2) {
|
||||
for(i=0; i<=59; i+=(m[1]|0)) {
|
||||
values.push(i);
|
||||
}
|
||||
str = str.replace(m[0], '');
|
||||
continue;
|
||||
}
|
||||
// test "a-b/n" expression
|
||||
m = str.match(/^([0-9]+)-([0-9]+)\/([0-9]+),?/);
|
||||
if(m && m.length == 4) {
|
||||
for(i=(m[1]|0); i<=(m[2]|0); i+=(m[3]|0)) {
|
||||
values.push(i);
|
||||
}
|
||||
str = str.replace(m[0], '');
|
||||
continue;
|
||||
}
|
||||
// test "a-b" expression
|
||||
m = str.match(/^([0-9]+)-([0-9]+),?/);
|
||||
if(m && m.length == 3) {
|
||||
for(i=(m[1]|0); i<=(m[2]|0); i++) {
|
||||
values.push(i);
|
||||
}
|
||||
str = str.replace(m[0], '');
|
||||
continue;
|
||||
}
|
||||
// test "c" expression
|
||||
m = str.match(/^([0-9]+),?/);
|
||||
if(m && m.length == 2) {
|
||||
values.push(m[1]|0);
|
||||
str = str.replace(m[0], '');
|
||||
continue;
|
||||
}
|
||||
// something goes wrong in the expression
|
||||
return ;
|
||||
}
|
||||
}
|
||||
_self.setValue(values);
|
||||
};
|
||||
|
||||
// close the selector
|
||||
this.close = function(){
|
||||
_$selector.trigger('selector:close');
|
||||
};
|
||||
|
||||
// open the selector
|
||||
this.open = function(){
|
||||
_$selector.trigger('selector:open');
|
||||
};
|
||||
|
||||
// whether the selector is open
|
||||
this.isOpened = function() {
|
||||
return _$list.is(':visible');
|
||||
};
|
||||
|
||||
// add a selected value to the list
|
||||
this.addValue = function(key) {
|
||||
var values = _multiple ? _value.slice(0) : []; // clone array
|
||||
values.push(key);
|
||||
_self.setValue(values);
|
||||
};
|
||||
|
||||
// remove a selected value from the list
|
||||
this.removeValue = function(key) {
|
||||
if(_multiple) {
|
||||
var i, newValue = [];
|
||||
for(i=0; i<_value.length; i++){
|
||||
if(key != [_value[i]]) {
|
||||
newValue.push(_value[i]);
|
||||
}
|
||||
}
|
||||
_self.setValue(newValue);
|
||||
}
|
||||
else {
|
||||
_self.clear();
|
||||
}
|
||||
};
|
||||
|
||||
// set the selected value(s) of the list
|
||||
this.setValue = function(keys){
|
||||
var i, newKeys = [], saved = _value.join(' ');
|
||||
if(!$.isArray(keys)) keys = [keys];
|
||||
_$list.find('li').removeClass('selected');
|
||||
keys = array_unique(keys);
|
||||
keys.sort(function(a, b){
|
||||
var ta = typeof(a);
|
||||
var tb = typeof(b);
|
||||
if(ta==tb && ta=="number") return a-b;
|
||||
else return String(a) == String(b) ? 0 : (String(a) < String(b) ? -1 : 1);
|
||||
});
|
||||
if(_multiple) {
|
||||
for(i=0; i<keys.length; i++){
|
||||
if(keys[i] in _values) {
|
||||
_values[keys[i]].addClass('selected');
|
||||
newKeys.push(keys[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
else {
|
||||
if(keys[0] in _values) {
|
||||
_values[keys[0]].addClass('selected');
|
||||
newKeys.push(keys[0]);
|
||||
}
|
||||
}
|
||||
// remove unallowed values
|
||||
_value = newKeys;
|
||||
if(saved != _value.join(' ')) {
|
||||
_$selector.trigger('selector:change', _multiple ? keys : keys[0]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
};
|
||||
|
||||
// get the title text
|
||||
this.getTitleText = function(){
|
||||
var getValueText = function(key) {
|
||||
return (key in _values) ? _values[key].text() : key;
|
||||
};
|
||||
|
||||
if(_value.length == 0) {
|
||||
return _cron.getText('empty_' + _type) || _cron.getText('empty');
|
||||
}
|
||||
var cron = [getValueText(_value[0])], i, s = _value[0], c = _value[0], n = _value.length;
|
||||
for(i=1; i<n; i++) {
|
||||
if(_value[i] == c+1) {
|
||||
c = _value[i];
|
||||
cron[cron.length-1] = getValueText(s)+'-'+getValueText(c);
|
||||
}
|
||||
else {
|
||||
s = c = _value[i];
|
||||
cron.push(getValueText(c));
|
||||
}
|
||||
}
|
||||
return cron.join(',');
|
||||
};
|
||||
|
||||
// clear list
|
||||
this.clear = function() {
|
||||
_values = {};
|
||||
_self.setValue([]);
|
||||
_$list.empty();
|
||||
};
|
||||
|
||||
// add a (key, value) pair
|
||||
this.add = function(key, value) {
|
||||
if(!(value+'').match(/^[0-9]+$/)) _hasNumericTexts = false;
|
||||
if(_numeric_zero_pad && _hasNumericTexts && value < 10) {
|
||||
value = '0'+value;
|
||||
}
|
||||
var $item = $('<li>' + value + '</li>');
|
||||
_$list.append($item);
|
||||
_values[key] = $item;
|
||||
$item.click(function(){
|
||||
if(_multiple && $(this).hasClass('selected')) {
|
||||
_self.removeValue(key);
|
||||
}
|
||||
else {
|
||||
_self.addValue(key);
|
||||
if(!_multiple) _self.close();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
// expose main jquery object
|
||||
this.$ = _$selector;
|
||||
|
||||
// constructor
|
||||
_$block.find('b:eq(0)').after(_$selector).remove();
|
||||
_$selector
|
||||
.addClass('jqCron-selector-' + _$block.find('.jqCron-selector').length)
|
||||
.append(_$title)
|
||||
.append(_$list)
|
||||
.bind('selector:open', function(){
|
||||
if(_hasNumericTexts) {
|
||||
var nbcols = 1, n = _$list.find('li').length;
|
||||
if(n > 5 && n <= 16) nbcols = 2;
|
||||
else if(n > 16 && n <= 23) nbcols = 3;
|
||||
else if(n > 23 && n <= 40) nbcols = 4;
|
||||
else if(n > 40) nbcols = 5;
|
||||
_$list.addClass('cols'+nbcols);
|
||||
}
|
||||
_$list.show();
|
||||
})
|
||||
.bind('selector:close', function(){
|
||||
_$list.hide();
|
||||
})
|
||||
.bind('selector:change', function(){
|
||||
_$title.html(_self.getTitleText());
|
||||
})
|
||||
.click(function(e){
|
||||
e.stopPropagation();
|
||||
})
|
||||
.trigger('selector:change')
|
||||
;
|
||||
$.fn.disableSelection && _$selector.disableSelection(); // only work with jQuery UI
|
||||
_$title.click(function(e){
|
||||
(_self.isOpened() || _cron.isDisabled()) ? _self.close() : _self.open();
|
||||
});
|
||||
_self.close();
|
||||
_self.clear();
|
||||
}
|
||||
this.jqCronSelector = jqCronSelector;
|
||||
}).call(window, $);
|
||||
|
||||
/**
|
||||
* Generate unique id for each element.
|
||||
* Skip elements which have already an id.
|
||||
*/
|
||||
(function($){
|
||||
var jqUID = 0;
|
||||
var jqGetUID = function(prefix){
|
||||
var id;
|
||||
while(1) {
|
||||
jqUID++;
|
||||
id = ((prefix || 'JQUID')+'') + jqUID;
|
||||
if(!document.getElementById(id)) return id;
|
||||
}
|
||||
};
|
||||
$.fn.uniqueId = function(prefix) {
|
||||
return this.each(function(){
|
||||
if($(this).attr('id')) return;
|
||||
var id = jqGetUID(prefix);
|
||||
$(this).attr('id', id);
|
||||
});
|
||||
};
|
||||
}).call(window, $);
|
||||
|
||||
|
||||
/**
|
||||
* Extends jQuery selectors with new block selector
|
||||
*/
|
||||
(function($){
|
||||
$.extend($.expr[':'], {
|
||||
container: function(a) {
|
||||
return (a.tagName+'').toLowerCase() in {
|
||||
a:1,
|
||||
abbr:1,
|
||||
acronym:1,
|
||||
address:1,
|
||||
b:1,
|
||||
big:1,
|
||||
blockquote:1,
|
||||
button:1,
|
||||
cite:1,
|
||||
code:1,
|
||||
dd: 1,
|
||||
del:1,
|
||||
dfn:1,
|
||||
div:1,
|
||||
dt:1,
|
||||
em:1,
|
||||
fieldset:1,
|
||||
form:1,
|
||||
h1:1,
|
||||
h2:1,
|
||||
h3:1,
|
||||
h4:1,
|
||||
h5:1,
|
||||
h6: 1,
|
||||
i:1,
|
||||
ins:1,
|
||||
kbd:1,
|
||||
label:1,
|
||||
li:1,
|
||||
p:1,
|
||||
pre:1,
|
||||
q:1,
|
||||
samp:1,
|
||||
small:1,
|
||||
span:1,
|
||||
strong:1,
|
||||
sub: 1,
|
||||
sup:1,
|
||||
td:1,
|
||||
tt:1
|
||||
};
|
||||
},
|
||||
autoclose: function(a) {
|
||||
return (a.tagName+'').toLowerCase() in {
|
||||
area:1,
|
||||
base:1,
|
||||
basefont:1,
|
||||
br:1,
|
||||
col:1,
|
||||
frame:1,
|
||||
hr:1,
|
||||
img:1,
|
||||
input:1,
|
||||
link:1,
|
||||
meta:1,
|
||||
param:1
|
||||
};
|
||||
}
|
||||
});
|
||||
}).call(window, $);
|
||||
325
user/plugins/admin/themes/grav/app/utils/finderjs.js
Normal file
325
user/plugins/admin/themes/grav/app/utils/finderjs.js
Normal file
@@ -0,0 +1,325 @@
|
||||
/**
|
||||
* (c) Trilby Media, LLC
|
||||
* Author Djamil Legato
|
||||
*
|
||||
* Based on Mark Matyas's Finderjs
|
||||
* MIT License
|
||||
*/
|
||||
|
||||
import $ from 'jquery';
|
||||
import EventEmitter from 'eventemitter3';
|
||||
|
||||
export const DEFAULTS = {
|
||||
labelKey: 'name',
|
||||
valueKey: 'value', // new
|
||||
childKey: 'children',
|
||||
iconKey: 'icon', // new
|
||||
itemKey: 'item-key', // new
|
||||
pathBar: true,
|
||||
className: {
|
||||
container: 'fjs-container',
|
||||
pathBar: 'fjs-path-bar',
|
||||
col: 'fjs-col',
|
||||
list: 'fjs-list',
|
||||
item: 'fjs-item',
|
||||
active: 'fjs-active',
|
||||
children: 'fjs-has-children',
|
||||
url: 'fjs-url',
|
||||
itemPrepend: 'fjs-item-prepend',
|
||||
itemContent: 'fjs-item-content',
|
||||
itemAppend: 'fjs-item-append'
|
||||
}
|
||||
};
|
||||
|
||||
class Finder {
|
||||
constructor(container, data, options) {
|
||||
this.$emitter = new EventEmitter();
|
||||
this.container = $(container);
|
||||
this.data = data;
|
||||
|
||||
this.config = $.extend({}, DEFAULTS, options);
|
||||
|
||||
// dom events
|
||||
this.container.on('click', this.clickEvent.bind(this));
|
||||
this.container.on('keydown', this.keydownEvent.bind(this));
|
||||
|
||||
// internal events
|
||||
this.$emitter.on('item-selected', this.itemSelected.bind(this));
|
||||
this.$emitter.on('create-column', this.addColumn.bind(this));
|
||||
this.$emitter.on('navigate', this.navigate.bind(this));
|
||||
this.$emitter.on('go-to', this.goTo.bind(this, this.data));
|
||||
|
||||
this.container.addClass(this.config.className.container).attr('tabindex', 0);
|
||||
|
||||
this.createColumn(this.data);
|
||||
|
||||
if (this.config.pathBar) {
|
||||
this.pathBar = this.createPathBar();
|
||||
this.pathBar.on('click', '[data-breadcrumb-node]', (event) => {
|
||||
event.preventDefault();
|
||||
const location = $(event.currentTarget).data('breadcrumbNode');
|
||||
this.goTo(this.data, location);
|
||||
});
|
||||
}
|
||||
|
||||
// '' is <Root>
|
||||
if (this.config.defaultPath || this.config.defaultPath === '') {
|
||||
this.goTo(this.data, this.config.defaultPath);
|
||||
}
|
||||
}
|
||||
|
||||
reload(data = this.data) {
|
||||
this.createColumn(data);
|
||||
|
||||
// '' is <Root>
|
||||
if (this.config.defaultPath || this.config.defaultPath === '') {
|
||||
this.goTo(data, this.config.defaultPath);
|
||||
}
|
||||
}
|
||||
|
||||
createColumn(data, parent) {
|
||||
const callback = (data) => this.createColumn(data, parent);
|
||||
|
||||
if (typeof data === 'function') {
|
||||
data.call(this, parent, callback);
|
||||
} else if (Array.isArray(data) || typeof data === 'object') {
|
||||
if (typeof data === 'object') {
|
||||
data = Array.from(data);
|
||||
}
|
||||
const list = this.createList(data);
|
||||
const div = $('<div />');
|
||||
div.append(list).addClass(this.config.className.col);
|
||||
this.$emitter.emit('create-column', div);
|
||||
|
||||
return div;
|
||||
} else {
|
||||
throw new Error('Unknown data type');
|
||||
}
|
||||
}
|
||||
|
||||
createPathBar() {
|
||||
this.container.siblings(`.${this.config.className.pathBar}`).remove();
|
||||
const pathBar = $(`<div class="${this.config.className.pathBar}" />`);
|
||||
pathBar.insertAfter(this.container);
|
||||
|
||||
return pathBar;
|
||||
}
|
||||
|
||||
clickEvent(event) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
const target = $(event.target);
|
||||
const column = target.closest(`.${this.config.className.col}`);
|
||||
const item = target.closest(`.${this.config.className.item}`);
|
||||
|
||||
if (item.length) {
|
||||
this.$emitter.emit('item-selected', { column, item });
|
||||
}
|
||||
}
|
||||
|
||||
keydownEvent(event) {
|
||||
const codes = { 37: 'left', 38: 'up', 39: 'right', 40: 'down' };
|
||||
|
||||
if (event.keyCode in codes) {
|
||||
event.stopPropagation();
|
||||
event.preventDefault();
|
||||
|
||||
this.$emitter.emit('navigate', {
|
||||
direction: codes[event.keyCode]
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
itemSelected(value) {
|
||||
const element = value.item;
|
||||
if (!element.length) { return false; }
|
||||
const item = element[0]._item;
|
||||
const column = value.column;
|
||||
const data = item[this.config.childKey] || this.data;
|
||||
const active = $(column).find(`.${this.config.className.active}`);
|
||||
|
||||
if (active.length) {
|
||||
active.removeClass(this.config.className.active);
|
||||
}
|
||||
|
||||
element.addClass(this.config.className.active);
|
||||
column.nextAll().remove(); // ?!?!?
|
||||
|
||||
this.container[0].focus();
|
||||
window.scrollTo(window.pageXOffset, window.pageYOffset);
|
||||
|
||||
this.updatePathBar();
|
||||
|
||||
let newColumn;
|
||||
if (data) {
|
||||
newColumn = this.createColumn(data, item);
|
||||
this.$emitter.emit('interior-selected', item);
|
||||
} else {
|
||||
this.$emitter.emit('leaf-selected', item);
|
||||
}
|
||||
|
||||
return newColumn;
|
||||
}
|
||||
|
||||
addColumn(column) {
|
||||
this.container.append(column);
|
||||
this.$emitter.emit('column-created', column);
|
||||
}
|
||||
|
||||
navigate(value) {
|
||||
const active = this.findLastActive();
|
||||
const direction = value.direction;
|
||||
let column;
|
||||
let item;
|
||||
let target;
|
||||
|
||||
if (active) {
|
||||
item = active.item;
|
||||
column = active.column;
|
||||
|
||||
if (direction === 'up' && item.prev().length) {
|
||||
target = item.prev();
|
||||
} else if (direction === 'down' && item.next().length) {
|
||||
target = item.next();
|
||||
} else if (direction === 'right' && column.next().length) {
|
||||
column = column.next();
|
||||
target = column.find(`.${this.config.className.item}`).first();
|
||||
} else if (direction === 'left' && column.prev().length) {
|
||||
column = column.prev();
|
||||
target = column.find(`.${this.config.className.active}`).first() || column.find(`.${this.config.className.item}`);
|
||||
}
|
||||
} else {
|
||||
column = this.container.find(`.${this.config.className.col}`).first();
|
||||
target = column.find(`.${this.config.className.item}`).first();
|
||||
}
|
||||
|
||||
if (target) {
|
||||
this.$emitter.emit('item-selected', {
|
||||
column,
|
||||
item: target
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
goTo(data, path) {
|
||||
path = Array.isArray(path) ? path : path.split('/').map(bit => bit.trim()).filter(Boolean);
|
||||
|
||||
if (path.length) {
|
||||
this.container.children().remove();
|
||||
}
|
||||
|
||||
if (typeof data === 'function') {
|
||||
data.call(this, null, (data) => this.selectPath(path, data));
|
||||
} else {
|
||||
this.selectPath(path, data);
|
||||
}
|
||||
}
|
||||
|
||||
selectPath(path, data, column) {
|
||||
column = column || (path.length ? this.createColumn(data) : this.container.find(`> .${this.config.className.col}`));
|
||||
|
||||
const current = path[0] || '';
|
||||
const children = data.find((item) => item[this.config.itemKey] === current);
|
||||
const newColumn = this.itemSelected({
|
||||
column,
|
||||
item: column.find(`[data-fjs-item="${current}"]`).first()
|
||||
});
|
||||
|
||||
path.shift();
|
||||
|
||||
if (path.length && children) {
|
||||
this.selectPath(path, children[this.config.childKey], newColumn);
|
||||
}
|
||||
}
|
||||
|
||||
findLastActive() {
|
||||
const active = this.container.find(`.${this.config.className.active}`);
|
||||
if (!active.length) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const item = active.last();
|
||||
const column = item.closest(`.${this.config.className.col}`);
|
||||
|
||||
return { item, column };
|
||||
}
|
||||
|
||||
createList(data) {
|
||||
const list = $('<ul />');
|
||||
const items = data.map((item) => this.createItem(item));
|
||||
|
||||
const fragments = items.reduce((fragment, current) => {
|
||||
fragment.appendChild(current[0] || current);
|
||||
|
||||
return fragment;
|
||||
}, document.createDocumentFragment());
|
||||
|
||||
list.append(fragments).addClass(this.config.className.list);
|
||||
|
||||
return list;
|
||||
}
|
||||
|
||||
createItem(item) {
|
||||
const listItem = $('<li />');
|
||||
const listItemClasses = [this.config.className.item];
|
||||
const link = $('<a />');
|
||||
const createItemContent = this.config.createItemContent || this.createItemContent;
|
||||
const fragment = createItemContent.call(this, item);
|
||||
link.append(fragment)
|
||||
.attr('href', '')
|
||||
.attr('tabindex', -1);
|
||||
|
||||
if (item.url) {
|
||||
link.attr('href', item.url);
|
||||
listItemClasses.push(item.className);
|
||||
}
|
||||
|
||||
if (item[this.config.childKey]) {
|
||||
listItemClasses.push(this.config.className[this.config.childKey]);
|
||||
}
|
||||
|
||||
listItemClasses.push(`fjs-item-${item.type}`);
|
||||
listItem.addClass(listItemClasses.join(' '));
|
||||
listItem.append(link)
|
||||
.attr('data-fjs-item', item[this.config.itemKey]);
|
||||
|
||||
listItem[0]._item = item;
|
||||
|
||||
return listItem;
|
||||
}
|
||||
|
||||
updatePathBar() {
|
||||
if (!this.config.pathBar) { return false; }
|
||||
|
||||
const activeItems = this.container.find(`.${this.config.className.active}`);
|
||||
let itemKeys = '';
|
||||
this.pathBar.children().empty();
|
||||
activeItems.each((index, activeItem) => {
|
||||
const item = activeItem._item;
|
||||
const isLast = (index + 1) === activeItems.length;
|
||||
itemKeys += `/${item[this.config.itemKey]}`;
|
||||
this.pathBar.append(`
|
||||
<span class="breadcrumb-node breadcrumb-node-${item.type}" ${item.type === 'dir' ? `data-breadcrumb-node="${itemKeys}"` : ''}>
|
||||
<i class="fa fa-fw ${this.getIcon(item.type)}"></i>
|
||||
<span class="breadcrumb-node-name">${$('<div />').html(item[this.config.labelKey]).html()}</span>
|
||||
${!isLast ? '<i class="fa fa-fw fa-chevron-right"></i>' : ''}
|
||||
</span>
|
||||
`);
|
||||
});
|
||||
}
|
||||
|
||||
getIcon(type) {
|
||||
switch (type) {
|
||||
case 'root':
|
||||
return 'fa-sitemap';
|
||||
case 'file':
|
||||
return 'fa-file-o';
|
||||
case 'dir':
|
||||
default:
|
||||
return 'fa-folder';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export default Finder;
|
||||
11
user/plugins/admin/themes/grav/app/utils/formatbytes.js
Normal file
11
user/plugins/admin/themes/grav/app/utils/formatbytes.js
Normal file
@@ -0,0 +1,11 @@
|
||||
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB'];
|
||||
|
||||
export default function formatBytes(bytes, decimals) {
|
||||
if (bytes === 0) return '0 Byte';
|
||||
|
||||
let k = 1000;
|
||||
let value = Math.floor(Math.log(bytes) / Math.log(k));
|
||||
let decimal = decimals + 1 || 3;
|
||||
|
||||
return (bytes / Math.pow(k, value)).toPrecision(decimal) + ' ' + sizes[value];
|
||||
}
|
||||
57
user/plugins/admin/themes/grav/app/utils/gpm.js
Normal file
57
user/plugins/admin/themes/grav/app/utils/gpm.js
Normal file
@@ -0,0 +1,57 @@
|
||||
import { parseJSON, parseStatus, userFeedbackError } from './response';
|
||||
import { config } from 'grav-config';
|
||||
import EventEmitter from 'events';
|
||||
|
||||
export default class GPM extends EventEmitter {
|
||||
constructor(action = 'getUpdates') {
|
||||
super();
|
||||
this.payload = {};
|
||||
this.raw = {};
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
setPayload(payload = {}) {
|
||||
this.payload = payload;
|
||||
this.emit('payload', payload);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
setAction(action = 'getUpdates') {
|
||||
this.action = action;
|
||||
this.emit('action', action);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
fetch(callback = () => true, flush = false) {
|
||||
let data = new FormData();
|
||||
data.append('admin-nonce', config.admin_nonce);
|
||||
|
||||
if (flush) {
|
||||
data.append('flush', true);
|
||||
}
|
||||
|
||||
this.emit('fetching', this);
|
||||
|
||||
fetch(`${config.base_url_relative}/update.json/task${config.param_sep}getUpdates`, {
|
||||
credentials: 'same-origin',
|
||||
method: 'post',
|
||||
body: data
|
||||
}).then((response) => { this.raw = response; return response; })
|
||||
.then(parseStatus)
|
||||
.then(parseJSON)
|
||||
.then((response) => this.response(response))
|
||||
.then((response) => callback(response, this.raw))
|
||||
.then((response) => this.emit('fetched', this.payload, this.raw, this))
|
||||
.catch(userFeedbackError);
|
||||
}
|
||||
|
||||
response(response) {
|
||||
this.payload = response;
|
||||
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
export let Instance = new GPM();
|
||||
50
user/plugins/admin/themes/grav/app/utils/jquery-utils.js
vendored
Normal file
50
user/plugins/admin/themes/grav/app/utils/jquery-utils.js
vendored
Normal file
@@ -0,0 +1,50 @@
|
||||
import $ from 'jquery';
|
||||
import getSlug from 'speakingurl';
|
||||
|
||||
// jQuery no parents filter
|
||||
$.expr[':']['noparents'] = $.expr.createPseudo((text) => (element) => $(element).parents(text).length < 1);
|
||||
|
||||
// Slugify
|
||||
// CommonJS and ES6 version of https://github.com/madflow/jquery-slugify
|
||||
$.fn.slugify = (source, options) => {
|
||||
return this.each((element) => {
|
||||
let target = $(element);
|
||||
let source = $(source);
|
||||
|
||||
target.on('keyup change', () => {
|
||||
target.data('locked', target.val() !== '' && target.val() !== undefined);
|
||||
});
|
||||
|
||||
source.on('keyup change', () => {
|
||||
if (target.data('locked') === true) { return true; }
|
||||
|
||||
let isInput = target.is('input') || target.is('textarea');
|
||||
target[isInput ? 'val' : 'text']($.slugify(source.val(), options));
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// Static method.
|
||||
$.slugify = (sourceString, options) => {
|
||||
options = $.extend({}, $.slugify.options, options);
|
||||
options.lang = options.lang || $('html').prop('lang');
|
||||
|
||||
if (typeof options.preSlug === 'function') {
|
||||
sourceString = options.preSlug(sourceString);
|
||||
}
|
||||
|
||||
sourceString = options.slugFunc(sourceString, options);
|
||||
|
||||
if (typeof options.postSlug === 'function') {
|
||||
sourceString = options.postSlug(sourceString);
|
||||
}
|
||||
|
||||
return sourceString;
|
||||
};
|
||||
|
||||
// Default plugin options
|
||||
$.slugify.options = {
|
||||
preSlug: null,
|
||||
postSlug: null,
|
||||
slugFunc: (input, opts) => getSlug(input, opts)
|
||||
};
|
||||
34
user/plugins/admin/themes/grav/app/utils/keepalive.js
Normal file
34
user/plugins/admin/themes/grav/app/utils/keepalive.js
Normal file
@@ -0,0 +1,34 @@
|
||||
import { config } from 'grav-config';
|
||||
import { userFeedbackError } from './response';
|
||||
|
||||
const MAX_SAFE_DELAY = 2147483647;
|
||||
|
||||
class KeepAlive {
|
||||
constructor() {
|
||||
this.active = false;
|
||||
}
|
||||
|
||||
start() {
|
||||
let timeout = config.admin_timeout / 1.5 * 1000;
|
||||
this.timer = setInterval(() => this.fetch(), Math.min(timeout, MAX_SAFE_DELAY));
|
||||
this.active = true;
|
||||
}
|
||||
|
||||
stop() {
|
||||
clearInterval(this.timer);
|
||||
this.active = false;
|
||||
}
|
||||
|
||||
fetch() {
|
||||
let data = new FormData();
|
||||
data.append('admin-nonce', config.admin_nonce);
|
||||
|
||||
fetch(`${config.base_url_relative}/task${config.param_sep}keepAlive`, {
|
||||
credentials: 'same-origin',
|
||||
method: 'post',
|
||||
body: data
|
||||
}).catch(userFeedbackError);
|
||||
}
|
||||
}
|
||||
|
||||
export default new KeepAlive();
|
||||
21
user/plugins/admin/themes/grav/app/utils/offline.js
Normal file
21
user/plugins/admin/themes/grav/app/utils/offline.js
Normal file
@@ -0,0 +1,21 @@
|
||||
import $ from 'jquery';
|
||||
import isOnline from '../utils/offline';
|
||||
|
||||
const offlineElement = $('#offline-status');
|
||||
|
||||
$(window).on('offline', () => {
|
||||
offlineElement.slideDown();
|
||||
});
|
||||
|
||||
$(window).on('online', () => {
|
||||
offlineElement.slideUp();
|
||||
});
|
||||
|
||||
$(document).ready(() => {
|
||||
if (!isOnline) {
|
||||
offlineElement.slideDown();
|
||||
}
|
||||
});
|
||||
|
||||
// assume online if can't check
|
||||
export default typeof global.navigator.onLine !== 'undefined' ? global.navigator.onLine : true;
|
||||
498
user/plugins/admin/themes/grav/app/utils/packages.js
Normal file
498
user/plugins/admin/themes/grav/app/utils/packages.js
Normal file
@@ -0,0 +1,498 @@
|
||||
import $ from 'jquery';
|
||||
import { config, translations } from 'grav-config';
|
||||
import request from '../utils/request';
|
||||
import { Instance as gpm } from '../utils/gpm';
|
||||
|
||||
class Sorter {
|
||||
getElements(elements, container) {
|
||||
this.elements = elements || document.querySelectorAll('[data-gpm-plugin], [data-gpm-theme]');
|
||||
this.container = container || document.querySelector('.gpm-plugins > table > tbody, .gpm-themes > .themes.card-row');
|
||||
|
||||
return this.elements;
|
||||
}
|
||||
|
||||
static sort(A, B, direction = 'asc') {
|
||||
if (A > B) { return (direction === 'asc') ? 1 : -1; }
|
||||
if (A < B) { return (direction === 'asc') ? -1 : 1; }
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
byCommon(direction = 'asc', data = '') {
|
||||
const elements = this.getElements();
|
||||
this.removeGumroad();
|
||||
|
||||
Array.from(elements).sort((a, b) => {
|
||||
let A = a.dataset[data].toString().toLowerCase();
|
||||
let B = b.dataset[data].toString().toLowerCase();
|
||||
|
||||
return Sorter.sort(A, B, direction);
|
||||
}).forEach((element) => {
|
||||
this.container.appendChild(element);
|
||||
});
|
||||
|
||||
this.addGumroad();
|
||||
return this.container;
|
||||
}
|
||||
|
||||
byName(direction = 'asc', data = 'gpmName') {
|
||||
return this.byCommon(direction, data);
|
||||
}
|
||||
|
||||
byAuthor(direction = 'asc', data = 'gpmAuthor') {
|
||||
return this.byCommon(direction, data);
|
||||
}
|
||||
|
||||
byOfficial(direction = 'asc', data = 'gpmOfficial') {
|
||||
return this.byCommon(direction, data);
|
||||
}
|
||||
|
||||
byPremium(direction = 'asc', data = 'gpmPremium') {
|
||||
return this.byCommon(direction, data);
|
||||
}
|
||||
|
||||
byReleaseDate(direction = 'asc', data = 'gpmReleaseDate') {
|
||||
const elements = this.getElements();
|
||||
|
||||
this.removeGumroad();
|
||||
Array.from(elements).sort((a, b) => {
|
||||
let A = new Date(a.dataset[data]).getTime();
|
||||
let B = new Date(b.dataset[data]).getTime();
|
||||
|
||||
return Sorter.sort(A, B, direction === 'asc' ? 'desc' : 'asc');
|
||||
}).forEach((element) => {
|
||||
this.container.appendChild(element);
|
||||
});
|
||||
|
||||
this.addGumroad();
|
||||
return this.container;
|
||||
}
|
||||
|
||||
byUpdatable(direction = 'asc', data = 'gpmUpdatable') {
|
||||
return this.byCommon(direction, data);
|
||||
}
|
||||
|
||||
byEnabled(direction = 'asc', data = 'gpmEnabled') {
|
||||
return this.byCommon(direction, data);
|
||||
}
|
||||
|
||||
byTesting(direction = 'asc', data = 'gpmTesting') {
|
||||
return this.byCommon(direction, data);
|
||||
}
|
||||
|
||||
addGumroad() {
|
||||
if (window.GumroadOverlay) {
|
||||
window.GumroadOverlay.startNodeAdditionObserver();
|
||||
}
|
||||
}
|
||||
|
||||
removeGumroad() {
|
||||
if (window.GumroadOverlay) {
|
||||
window.GumroadOverlay.nodeAdditionObserver.disconnect();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Packages {
|
||||
constructor() {
|
||||
this.Sort = new Sorter();
|
||||
}
|
||||
|
||||
static getBackToList(type) {
|
||||
global.location.href = `${config.base_url_relative}/${type}s`;
|
||||
}
|
||||
|
||||
static addDependencyToList(type, dependency, slug = '') {
|
||||
if (['admin', 'form', 'login', 'email', 'grav'].indexOf(dependency) !== -1) { return; }
|
||||
let container = $('.package-dependencies-container');
|
||||
let text = `${dependency} <a href="#" class="button" data-dependency-slug="${dependency}" data-${type}-action="remove-dependency-package">Remove</a>`;
|
||||
|
||||
if (slug) {
|
||||
text += ` (was needed by ${slug})`;
|
||||
}
|
||||
|
||||
container.append(`<li>${text}</li>`);
|
||||
}
|
||||
|
||||
addDependenciesToList(dependencies, slug = '') {
|
||||
dependencies.forEach((dependency) => {
|
||||
Packages.addDependencyToList('plugin', dependency.name || dependency, slug);
|
||||
});
|
||||
}
|
||||
|
||||
static getTaskUrl(type, task) {
|
||||
let url = `${config.base_url_relative}`;
|
||||
url += `/${type}s.json`;
|
||||
url += `/task${config.param_sep}${task}`;
|
||||
return url;
|
||||
}
|
||||
|
||||
static getRemovePackageUrl(type) {
|
||||
return `${Packages.getTaskUrl(type, 'removePackage')}`;
|
||||
}
|
||||
|
||||
static getReinstallPackageUrl(type) {
|
||||
return `${Packages.getTaskUrl(type, 'reinstallPackage')}`;
|
||||
}
|
||||
|
||||
static getGetPackagesDependenciesUrl(type) {
|
||||
return `${Packages.getTaskUrl(type, 'getPackagesDependencies')}`;
|
||||
}
|
||||
|
||||
static getInstallDependenciesOfPackagesUrl(type) {
|
||||
return `${Packages.getTaskUrl(type, 'installDependenciesOfPackages')}`;
|
||||
}
|
||||
|
||||
static getInstallPackageUrl(type) {
|
||||
return `${Packages.getTaskUrl(type, 'installPackage')}`;
|
||||
}
|
||||
|
||||
removePackage(type, slug) {
|
||||
let url = Packages.getRemovePackageUrl(type);
|
||||
|
||||
request(url, {
|
||||
method: 'post',
|
||||
body: {
|
||||
package: slug
|
||||
}
|
||||
}, (response) => {
|
||||
if (response.status === 'success') {
|
||||
$('.remove-package-confirm').addClass('hidden');
|
||||
|
||||
if (response.dependencies && response.dependencies.length > 0) {
|
||||
this.addDependenciesToList(response.dependencies);
|
||||
$('.remove-package-dependencies').removeClass('hidden');
|
||||
} else {
|
||||
$('.remove-package-done').removeClass('hidden');
|
||||
}
|
||||
|
||||
// The package was removed. When the modal closes, move to the packages list
|
||||
$(document).on('closing', '[data-remodal-id="remove-package"]', () => {
|
||||
Packages.getBackToList(type);
|
||||
});
|
||||
} else {
|
||||
$('.remove-package-confirm').addClass('hidden');
|
||||
$('.remove-package-error').removeClass('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
reinstallPackage(type, slug, package_name, current_version) {
|
||||
$('.button-bar button').addClass('hidden');
|
||||
$('.button-bar .spinning-wheel').removeClass('hidden');
|
||||
|
||||
let url = Packages.getReinstallPackageUrl(type);
|
||||
|
||||
request(url, {
|
||||
method: 'post',
|
||||
body: {
|
||||
slug: slug,
|
||||
type: type,
|
||||
package_name: package_name,
|
||||
current_version: current_version
|
||||
}
|
||||
}, (response) => {
|
||||
if (response.status === 'success') {
|
||||
$('.reinstall-package-confirm').addClass('hidden');
|
||||
$('.reinstall-package-done').removeClass('hidden');
|
||||
} else {
|
||||
$('.reinstall-package-confirm').addClass('hidden');
|
||||
$('.reinstall-package-error').removeClass('hidden');
|
||||
}
|
||||
|
||||
window.location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
removeDependency(type, slug, button) {
|
||||
let url = Packages.getRemovePackageUrl(type);
|
||||
|
||||
request(url, {
|
||||
method: 'post',
|
||||
body: {
|
||||
package: slug
|
||||
}
|
||||
}, (response) => {
|
||||
if (response.status === 'success') {
|
||||
button.removeClass('button');
|
||||
button.replaceWith($('<span>Removed successfully</span>'));
|
||||
|
||||
if (response.dependencies && response.dependencies.length > 0) {
|
||||
this.addDependenciesToList(response.dependencies, slug);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
static addNeededDependencyToList(action, slug) {
|
||||
$('.install-dependencies-package-container .type-' + action).removeClass('hidden');
|
||||
let list = $('.install-dependencies-package-container .type-' + action + ' ul');
|
||||
|
||||
if (action !== 'install') {
|
||||
let current_version = '';
|
||||
let available_version = '';
|
||||
let name = '';
|
||||
|
||||
let resources = gpm.payload.payload.resources;
|
||||
|
||||
if (resources.plugins[slug]) {
|
||||
available_version = resources.plugins[slug].available;
|
||||
current_version = resources.plugins[slug].version;
|
||||
name = resources.plugins[slug].name;
|
||||
} else if (resources.themes[slug]) {
|
||||
available_version = resources.themes[slug].available;
|
||||
current_version = resources.themes[slug].version;
|
||||
name = resources.themes[slug].name;
|
||||
}
|
||||
|
||||
list.append(`<li>${name ? name : slug}, ${translations.PLUGIN_ADMIN.FROM} v<strong>${current_version}</strong> ${translations.PLUGIN_ADMIN.TO} v<strong>${available_version}</strong></li>`);
|
||||
} else {
|
||||
list.append(`<li>${name ? name : slug}</li>`);
|
||||
}
|
||||
}
|
||||
|
||||
getPackagesDependencies(type, slugs, finishedLoadingCallback) {
|
||||
let url = Packages.getGetPackagesDependenciesUrl(type);
|
||||
|
||||
request(url, {
|
||||
method: 'post',
|
||||
body: {
|
||||
packages: slugs
|
||||
}
|
||||
}, (response) => {
|
||||
|
||||
finishedLoadingCallback();
|
||||
|
||||
if (response.status === 'success') {
|
||||
if (response.dependencies) {
|
||||
let hasDependencies = false;
|
||||
for (var dependency in response.dependencies) {
|
||||
if (response.dependencies.hasOwnProperty(dependency)) {
|
||||
if (dependency === 'grav') {
|
||||
continue;
|
||||
}
|
||||
hasDependencies = true;
|
||||
let dependencyName = dependency;
|
||||
let action = response.dependencies[dependency];
|
||||
|
||||
Packages.addNeededDependencyToList(action, dependencyName);
|
||||
}
|
||||
}
|
||||
|
||||
if (hasDependencies) {
|
||||
$('[data-packages-modal] .install-dependencies-package-container').removeClass('hidden');
|
||||
} else {
|
||||
$('[data-packages-modal] .install-package-container').removeClass('hidden');
|
||||
}
|
||||
} else {
|
||||
$('[data-packages-modal] .install-package-container').removeClass('hidden');
|
||||
}
|
||||
} else {
|
||||
$('[data-packages-modal] .install-package-error').removeClass('hidden');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
installDependenciesOfPackages(type, slugs, callbackSuccess, callbackError) {
|
||||
let url = Packages.getInstallDependenciesOfPackagesUrl(type);
|
||||
|
||||
request(url, {
|
||||
method: 'post',
|
||||
body: {
|
||||
packages: slugs
|
||||
}
|
||||
}, callbackSuccess);
|
||||
}
|
||||
|
||||
installPackages(type, slugs, callbackSuccess) {
|
||||
let url = Packages.getInstallPackageUrl(type);
|
||||
|
||||
global.Promise.all(slugs.map((slug) => {
|
||||
return new global.Promise((resolve, reject) => {
|
||||
request(url, {
|
||||
method: 'post',
|
||||
body: {
|
||||
package: slug,
|
||||
type: type
|
||||
}
|
||||
}, (response) => {
|
||||
resolve(response);
|
||||
});
|
||||
});
|
||||
})).then(callbackSuccess);
|
||||
|
||||
}
|
||||
|
||||
static getSlugsFromEvent(event) {
|
||||
let slugs = '';
|
||||
if ($(event.target).is('[data-packages-slugs]')) {
|
||||
slugs = $(event.target).attr('data-packages-slugs');
|
||||
} else {
|
||||
slugs = $(event.target).parent('[data-packages-slugs]').attr('data-packages-slugs');
|
||||
}
|
||||
|
||||
if (typeof slugs === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
slugs = slugs.split(',');
|
||||
return typeof slugs === 'string' ? [slugs] : slugs;
|
||||
}
|
||||
|
||||
handleGettingPackageDependencies(type, event, action = 'update') {
|
||||
let slugs = Packages.getSlugsFromEvent(event);
|
||||
|
||||
if (!slugs) {
|
||||
alert('No slug set');
|
||||
return;
|
||||
}
|
||||
|
||||
// Cleanup
|
||||
$('.packages-names-list').html('');
|
||||
$('.install-dependencies-package-container li').remove();
|
||||
|
||||
slugs.forEach((slug) => {
|
||||
if (action === 'update') {
|
||||
let current_version = '';
|
||||
let available_version = '';
|
||||
let name = '';
|
||||
|
||||
let resources = gpm.payload.payload.resources;
|
||||
|
||||
if (resources.plugins[slug]) {
|
||||
available_version = resources.plugins[slug].available;
|
||||
current_version = resources.plugins[slug].version;
|
||||
name = resources.plugins[slug].name;
|
||||
} else if (resources.themes[slug]) {
|
||||
available_version = resources.themes[slug].available;
|
||||
current_version = resources.themes[slug].version;
|
||||
name = resources.themes[slug].name;
|
||||
}
|
||||
|
||||
$('.packages-names-list').append(`<li>${name ? name : slug}, ${translations.PLUGIN_ADMIN.FROM} v<strong>${current_version}</strong> ${translations.PLUGIN_ADMIN.TO} v<strong>${available_version}</strong></li>`);
|
||||
} else {
|
||||
$('.packages-names-list').append(`<li>${name ? name : slug}</li>`);
|
||||
}
|
||||
});
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
// fix mismatching types when sharing install modal between plugins/themes
|
||||
const query = '[data-packages-modal] [data-theme-action], [data-packages-modal] [data-plugin-action]';
|
||||
const data = $(query).data('themeAction') || $(query).data('pluginAction');
|
||||
$(query).removeAttr('data-theme-action').removeAttr('data-plugin-action').attr(`data-${type}-action`, data);
|
||||
|
||||
// Restore original state
|
||||
$('[data-packages-modal] .loading').removeClass('hidden');
|
||||
$('[data-packages-modal] .install-dependencies-package-container').addClass('hidden');
|
||||
$('[data-packages-modal] .install-package-container').addClass('hidden');
|
||||
$('[data-packages-modal] .installing-dependencies').addClass('hidden');
|
||||
$('[data-packages-modal] .installing-package').addClass('hidden');
|
||||
$('[data-packages-modal] .installation-complete').addClass('hidden');
|
||||
$('[data-packages-modal] .install-package-error').addClass('hidden');
|
||||
|
||||
this.getPackagesDependencies(type, slugs, () => {
|
||||
let slugs_string = slugs.join();
|
||||
$(`[data-packages-modal] [data-${type}-action="install-dependencies-and-package"]`).attr('data-packages-slugs', slugs_string);
|
||||
$(`[data-packages-modal] [data-${type}-action="install-package"]`).attr('data-packages-slugs', slugs_string);
|
||||
$('[data-packages-modal] .loading').addClass('hidden');
|
||||
});
|
||||
}
|
||||
|
||||
handleInstallingDependenciesAndPackage(type, event) {
|
||||
let slugs = Packages.getSlugsFromEvent(event);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
$('[data-packages-modal] .install-dependencies-package-container').addClass('hidden');
|
||||
$('[data-packages-modal] .install-package-container').addClass('hidden');
|
||||
$('[data-packages-modal] .installing-dependencies').removeClass('hidden');
|
||||
|
||||
this.installDependenciesOfPackages(type, slugs, (response) => {
|
||||
$('[data-packages-modal] .installing-dependencies').addClass('hidden');
|
||||
$('[data-packages-modal] .installing-package').removeClass('hidden');
|
||||
this.installPackages(type, slugs, () => {
|
||||
$('[data-packages-modal] .installing-package').addClass('hidden');
|
||||
$('[data-packages-modal] .installation-complete').removeClass('hidden');
|
||||
|
||||
if (response.status === 'error') {
|
||||
let remodal = $.remodal.lookup[$('[data-packages-modal]').data('remodal')];
|
||||
remodal.close();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
if (slugs.length === 1) {
|
||||
global.location.href = `${config.base_url_relative}/${type}s/${slugs[0]}`;
|
||||
} else {
|
||||
global.location.href = `${config.base_url_relative}/${type}s`;
|
||||
}
|
||||
}, 1000);
|
||||
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
handleInstallingPackage(type, event) {
|
||||
let slugs = Packages.getSlugsFromEvent(event);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
$('[data-packages-modal] .install-package-container').addClass('hidden');
|
||||
$('[data-packages-modal] .installing-package').removeClass('hidden');
|
||||
|
||||
this.installPackages(type, slugs, (response) => {
|
||||
$('[data-packages-modal] .installing-package').addClass('hidden');
|
||||
$('[data-packages-modal] .installation-complete').removeClass('hidden');
|
||||
|
||||
const errors = Array.from(response).filter((r) => r.status === 'error');
|
||||
|
||||
if (errors && errors.length) {
|
||||
let remodal = $.remodal.lookup[$('[data-packages-modal].remodal-is-opened').data('remodal')];
|
||||
remodal.close();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (slugs.length === 1) {
|
||||
global.location.href = `${config.base_url_relative}/${type}s/${slugs[0]}`;
|
||||
} else {
|
||||
global.location.href = `${config.base_url_relative}/${type}s`;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
handleRemovingPackage(type, event) {
|
||||
let slug = $(event.target).attr('data-packages-slugs');
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
this.removePackage(type, slug);
|
||||
}
|
||||
|
||||
handleReinstallPackage(type, event) {
|
||||
let target = $(event.target);
|
||||
let slug = target.attr('data-package-slug');
|
||||
let package_name = target.attr('data-package-name');
|
||||
let current_version = target.attr('data-package-current-version');
|
||||
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
this.reinstallPackage(type, slug, package_name, current_version);
|
||||
}
|
||||
|
||||
handleRemovingDependency(type, event) {
|
||||
let slug = $(event.target).attr('data-dependency-slug');
|
||||
let button = $(event.target);
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
this.removeDependency(type, slug, button);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
export default new Packages();
|
||||
820
user/plugins/admin/themes/grav/app/utils/remodal.js
Normal file
820
user/plugins/admin/themes/grav/app/utils/remodal.js
Normal file
@@ -0,0 +1,820 @@
|
||||
/* Remodal from https://github.com/vodkabears/Remodal
|
||||
* With Stackable option from https://github.com/antstorm/Remodal patch
|
||||
*/
|
||||
|
||||
import $ from 'jquery';
|
||||
|
||||
!(function(root, factory) {
|
||||
return factory(root, $);
|
||||
})(this, function(global, $) {
|
||||
|
||||
'use strict';
|
||||
|
||||
/**
|
||||
* Name of the plugin
|
||||
* @private
|
||||
* @const
|
||||
* @type {String}
|
||||
*/
|
||||
var PLUGIN_NAME = 'remodal';
|
||||
|
||||
/**
|
||||
* Namespace for CSS and events
|
||||
* @private
|
||||
* @const
|
||||
* @type {String}
|
||||
*/
|
||||
var NAMESPACE = window.REMODAL_GLOBALS && window.REMODAL_GLOBALS.NAMESPACE || PLUGIN_NAME;
|
||||
|
||||
/**
|
||||
* Animationstart event with vendor prefixes
|
||||
* @private
|
||||
* @const
|
||||
* @type {String}
|
||||
*/
|
||||
var ANIMATIONSTART_EVENTS = $.map(
|
||||
['animationstart', 'webkitAnimationStart', 'MSAnimationStart', 'oAnimationStart'],
|
||||
|
||||
function(eventName) {
|
||||
return eventName + '.' + NAMESPACE;
|
||||
}
|
||||
|
||||
).join(' ');
|
||||
|
||||
/**
|
||||
* Animationend event with vendor prefixes
|
||||
* @private
|
||||
* @const
|
||||
* @type {String}
|
||||
*/
|
||||
var ANIMATIONEND_EVENTS = $.map(
|
||||
['animationend', 'webkitAnimationEnd', 'MSAnimationEnd', 'oAnimationEnd'],
|
||||
|
||||
function(eventName) {
|
||||
return eventName + '.' + NAMESPACE;
|
||||
}
|
||||
|
||||
).join(' ');
|
||||
|
||||
/**
|
||||
* Default settings
|
||||
* @private
|
||||
* @const
|
||||
* @type {Object}
|
||||
*/
|
||||
var DEFAULTS = $.extend({
|
||||
hashTracking: true,
|
||||
closeOnConfirm: true,
|
||||
closeOnCancel: true,
|
||||
closeOnEscape: true,
|
||||
closeOnOutsideClick: true,
|
||||
modifier: '',
|
||||
stack: false,
|
||||
appendTo: null
|
||||
}, window.REMODAL_GLOBALS && window.REMODAL_GLOBALS.DEFAULTS);
|
||||
|
||||
/**
|
||||
* States of the Remodal
|
||||
* @private
|
||||
* @const
|
||||
* @enum {String}
|
||||
*/
|
||||
var STATES = {
|
||||
CLOSING: 'closing',
|
||||
CLOSED: 'closed',
|
||||
OPENING: 'opening',
|
||||
OPENED: 'opened'
|
||||
};
|
||||
|
||||
/**
|
||||
* Reasons of the state change.
|
||||
* @private
|
||||
* @const
|
||||
* @enum {String}
|
||||
*/
|
||||
var STATE_CHANGE_REASONS = {
|
||||
CONFIRMATION: 'confirmation',
|
||||
CANCELLATION: 'cancellation'
|
||||
};
|
||||
|
||||
/**
|
||||
* Is animation supported?
|
||||
* @private
|
||||
* @const
|
||||
* @type {Boolean}
|
||||
*/
|
||||
var IS_ANIMATION = (function() {
|
||||
var style = document.createElement('div').style;
|
||||
|
||||
return style.animationName !== undefined ||
|
||||
style.WebkitAnimationName !== undefined ||
|
||||
style.MozAnimationName !== undefined ||
|
||||
style.msAnimationName !== undefined ||
|
||||
style.OAnimationName !== undefined;
|
||||
})();
|
||||
|
||||
/**
|
||||
* Is iOS?
|
||||
* @private
|
||||
* @const
|
||||
* @type {Boolean}
|
||||
*/
|
||||
var IS_IOS = /iPad|iPhone|iPod/.test(navigator.platform);
|
||||
|
||||
/**
|
||||
* Current modal
|
||||
* @private
|
||||
* @type {Remodal}
|
||||
*/
|
||||
var openModals = [];
|
||||
|
||||
/**
|
||||
* Scrollbar position
|
||||
* @private
|
||||
* @type {Number}
|
||||
*/
|
||||
var scrollTop;
|
||||
|
||||
/**
|
||||
* Returns an animation duration
|
||||
* @private
|
||||
* @param {jQuery} $elem
|
||||
* @returns {Number}
|
||||
*/
|
||||
function getAnimationDuration($elem) {
|
||||
if (
|
||||
IS_ANIMATION &&
|
||||
$elem.css('animation-name') === 'none' &&
|
||||
$elem.css('-webkit-animation-name') === 'none' &&
|
||||
$elem.css('-moz-animation-name') === 'none' &&
|
||||
$elem.css('-o-animation-name') === 'none' &&
|
||||
$elem.css('-ms-animation-name') === 'none'
|
||||
) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var duration = $elem.css('animation-duration') ||
|
||||
$elem.css('-webkit-animation-duration') ||
|
||||
$elem.css('-moz-animation-duration') ||
|
||||
$elem.css('-o-animation-duration') ||
|
||||
$elem.css('-ms-animation-duration') ||
|
||||
'0s';
|
||||
|
||||
var delay = $elem.css('animation-delay') ||
|
||||
$elem.css('-webkit-animation-delay') ||
|
||||
$elem.css('-moz-animation-delay') ||
|
||||
$elem.css('-o-animation-delay') ||
|
||||
$elem.css('-ms-animation-delay') ||
|
||||
'0s';
|
||||
|
||||
var iterationCount = $elem.css('animation-iteration-count') ||
|
||||
$elem.css('-webkit-animation-iteration-count') ||
|
||||
$elem.css('-moz-animation-iteration-count') ||
|
||||
$elem.css('-o-animation-iteration-count') ||
|
||||
$elem.css('-ms-animation-iteration-count') ||
|
||||
'1';
|
||||
|
||||
var max;
|
||||
var len;
|
||||
var num;
|
||||
var i;
|
||||
|
||||
duration = duration.split(', ');
|
||||
delay = delay.split(', ');
|
||||
iterationCount = iterationCount.split(', ');
|
||||
|
||||
// The 'duration' size is the same as the 'delay' size
|
||||
for (i = 0, len = duration.length, max = Number.NEGATIVE_INFINITY; i < len; i++) {
|
||||
num = parseFloat(duration[i]) * parseInt(iterationCount[i], 10) + parseFloat(delay[i]);
|
||||
|
||||
if (num > max) {
|
||||
max = num;
|
||||
}
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a scrollbar width
|
||||
* @private
|
||||
* @returns {Number}
|
||||
*/
|
||||
function getScrollbarWidth() {
|
||||
if ($(document).height() <= $(window).height()) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
var outer = document.createElement('div');
|
||||
var inner = document.createElement('div');
|
||||
var widthNoScroll;
|
||||
var widthWithScroll;
|
||||
|
||||
outer.style.visibility = 'hidden';
|
||||
outer.style.width = '100px';
|
||||
document.body.appendChild(outer);
|
||||
|
||||
widthNoScroll = outer.offsetWidth;
|
||||
|
||||
// Force scrollbars
|
||||
outer.style.overflow = 'scroll';
|
||||
|
||||
// Add inner div
|
||||
inner.style.width = '100%';
|
||||
outer.appendChild(inner);
|
||||
|
||||
widthWithScroll = inner.offsetWidth;
|
||||
|
||||
// Remove divs
|
||||
outer.parentNode.removeChild(outer);
|
||||
|
||||
return widthNoScroll - widthWithScroll;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locks the screen
|
||||
* @private
|
||||
*/
|
||||
function lockScreen() {
|
||||
if (IS_IOS) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $html = $('html');
|
||||
var lockedClass = namespacify('is-locked');
|
||||
var paddingRight;
|
||||
var $body;
|
||||
|
||||
if (!$html.hasClass(lockedClass)) {
|
||||
$body = $(document.body);
|
||||
|
||||
// Zepto does not support '-=', '+=' in the `css` method
|
||||
paddingRight = parseInt($body.css('padding-right'), 10) + getScrollbarWidth();
|
||||
|
||||
$body.css('padding-right', paddingRight + 'px');
|
||||
$html.addClass(lockedClass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unlocks the screen
|
||||
* @private
|
||||
*/
|
||||
function unlockScreen() {
|
||||
if (IS_IOS) {
|
||||
return;
|
||||
}
|
||||
|
||||
var $html = $('html');
|
||||
var lockedClass = namespacify('is-locked');
|
||||
var paddingRight;
|
||||
var $body;
|
||||
|
||||
if ($html.hasClass(lockedClass)) {
|
||||
$body = $(document.body);
|
||||
|
||||
// Zepto does not support '-=', '+=' in the `css` method
|
||||
paddingRight = parseInt($body.css('padding-right'), 10) - getScrollbarWidth();
|
||||
|
||||
$body.css('padding-right', paddingRight + 'px');
|
||||
$html.removeClass(lockedClass);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a state for an instance
|
||||
* @private
|
||||
* @param {Remodal} instance
|
||||
* @param {STATES} state
|
||||
* @param {Boolean} isSilent If true, Remodal does not trigger events
|
||||
* @param {String} Reason of a state change.
|
||||
*/
|
||||
function setState(instance, state, isSilent, reason) {
|
||||
|
||||
var newState = namespacify('is', state);
|
||||
var allStates = [namespacify('is', STATES.CLOSING),
|
||||
namespacify('is', STATES.OPENING),
|
||||
namespacify('is', STATES.CLOSED),
|
||||
namespacify('is', STATES.OPENED)].join(' ');
|
||||
|
||||
instance.$bg
|
||||
.removeClass(allStates)
|
||||
.addClass(newState);
|
||||
|
||||
instance.$overlay
|
||||
.removeClass(allStates)
|
||||
.addClass(newState);
|
||||
|
||||
instance.$wrapper
|
||||
.removeClass(allStates)
|
||||
.addClass(newState);
|
||||
|
||||
instance.$modal
|
||||
.removeClass(allStates)
|
||||
.addClass(newState);
|
||||
|
||||
instance.state = state;
|
||||
!isSilent && instance.$modal.trigger({
|
||||
type: state,
|
||||
reason: reason
|
||||
}, [{ reason: reason }]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Synchronizes with the animation
|
||||
* @param {Function} doBeforeAnimation
|
||||
* @param {Function} doAfterAnimation
|
||||
* @param {Remodal} instance
|
||||
*/
|
||||
function syncWithAnimation(doBeforeAnimation, doAfterAnimation, instance) {
|
||||
var runningAnimationsCount = 0;
|
||||
|
||||
var handleAnimationStart = function(e) {
|
||||
if (e.target !== this) {
|
||||
return;
|
||||
}
|
||||
|
||||
runningAnimationsCount++;
|
||||
};
|
||||
|
||||
var handleAnimationEnd = function(e) {
|
||||
if (e.target !== this) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (--runningAnimationsCount === 0) {
|
||||
|
||||
// Remove event listeners
|
||||
$.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) {
|
||||
instance[elemName].off(ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS);
|
||||
});
|
||||
|
||||
doAfterAnimation();
|
||||
}
|
||||
};
|
||||
|
||||
$.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) {
|
||||
instance[elemName]
|
||||
.on(ANIMATIONSTART_EVENTS, handleAnimationStart)
|
||||
.on(ANIMATIONEND_EVENTS, handleAnimationEnd);
|
||||
});
|
||||
|
||||
doBeforeAnimation();
|
||||
|
||||
// If the animation is not supported by a browser or its duration is 0
|
||||
if (
|
||||
getAnimationDuration(instance.$bg) === 0 &&
|
||||
getAnimationDuration(instance.$overlay) === 0 &&
|
||||
getAnimationDuration(instance.$wrapper) === 0 &&
|
||||
getAnimationDuration(instance.$modal) === 0
|
||||
) {
|
||||
|
||||
// Remove event listeners
|
||||
$.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) {
|
||||
instance[elemName].off(ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS);
|
||||
});
|
||||
|
||||
doAfterAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes immediately
|
||||
* @private
|
||||
* @param {Remodal} instance
|
||||
*/
|
||||
function halt(instance) {
|
||||
if (instance.state === STATES.CLOSED) {
|
||||
return;
|
||||
}
|
||||
|
||||
$.each(['$bg', '$overlay', '$wrapper', '$modal'], function(index, elemName) {
|
||||
instance[elemName].off(ANIMATIONSTART_EVENTS + ' ' + ANIMATIONEND_EVENTS);
|
||||
});
|
||||
|
||||
removeModal(instance);
|
||||
instance.$bg.removeClass(instance.settings.modifier);
|
||||
instance.$overlay.removeClass(instance.settings.modifier).hide();
|
||||
instance.$wrapper.hide();
|
||||
|
||||
if (openModals.length === 0) {
|
||||
unlockScreen();
|
||||
}
|
||||
|
||||
setState(instance, STATES.CLOSED, true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string with options
|
||||
* @private
|
||||
* @param str
|
||||
* @returns {Object}
|
||||
*/
|
||||
function parseOptions(str) {
|
||||
var obj = {};
|
||||
var arr;
|
||||
var len;
|
||||
var val;
|
||||
var i;
|
||||
|
||||
// Remove spaces before and after delimiters
|
||||
str = str.replace(/\s*:\s*/g, ':').replace(/\s*,\s*/g, ',');
|
||||
|
||||
// Parse a string
|
||||
arr = str.split(',');
|
||||
for (i = 0, len = arr.length; i < len; i++) {
|
||||
arr[i] = arr[i].split(':');
|
||||
val = arr[i][1];
|
||||
|
||||
// Convert a string value if it is like a boolean
|
||||
if (typeof val === 'string' || val instanceof String) {
|
||||
val = val === 'true' || (val === 'false' ? false : val);
|
||||
}
|
||||
|
||||
// Convert a string value if it is like a number
|
||||
if (typeof val === 'string' || val instanceof String) {
|
||||
val = !isNaN(val) ? +val : val;
|
||||
}
|
||||
|
||||
obj[arr[i][0]] = val;
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates a string separated by dashes and prefixed with NAMESPACE
|
||||
* @private
|
||||
* @param {...String}
|
||||
* @returns {String}
|
||||
*/
|
||||
function namespacify() {
|
||||
var result = NAMESPACE;
|
||||
|
||||
for (var i = 0; i < arguments.length; ++i) {
|
||||
result += '-' + arguments[i];
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handles the hashchange event
|
||||
* @private
|
||||
* @listens hashchange
|
||||
*/
|
||||
function handleHashChangeEvent() {
|
||||
var id = location.hash.replace('#', '');
|
||||
var instance;
|
||||
var $elem;
|
||||
|
||||
var current = currentModal();
|
||||
|
||||
if (!id) {
|
||||
|
||||
// Check if we have currently opened modal and animation was completed
|
||||
if (current && current.state === STATES.OPENED && current.settings.hashTracking) {
|
||||
current.close();
|
||||
}
|
||||
} else {
|
||||
|
||||
if (!current || current.id !== id) {
|
||||
// Catch syntax error if your hash is bad
|
||||
try {
|
||||
$elem = $(
|
||||
'[data-' + PLUGIN_NAME + '-id="' + id + '"]'
|
||||
);
|
||||
} catch (err) {
|
||||
}
|
||||
|
||||
if ($elem && $elem.length) {
|
||||
instance = $[PLUGIN_NAME].lookup[$elem.data(PLUGIN_NAME)];
|
||||
|
||||
if (instance && instance.settings.hashTracking) {
|
||||
instance.open();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function currentModal() {
|
||||
return openModals[openModals.length - 1];
|
||||
}
|
||||
|
||||
function removeModal(remodal) {
|
||||
var index = openModals.indexOf(remodal);
|
||||
|
||||
if (index >= 0) {
|
||||
openModals.slice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remodal constructor
|
||||
* @constructor
|
||||
* @param {jQuery} $modal
|
||||
* @param {Object} options
|
||||
*/
|
||||
function Remodal($modal, options) {
|
||||
var $body = $(document.body);
|
||||
var $appendTo = $body;
|
||||
var remodal = this;
|
||||
|
||||
remodal.id = $modal.attr('data-' + PLUGIN_NAME + '-id');
|
||||
remodal.settings = $.extend({}, DEFAULTS, options);
|
||||
remodal.index = $[PLUGIN_NAME].lookup.push(remodal) - 1;
|
||||
remodal.state = STATES.CLOSED;
|
||||
|
||||
// remodal.$overlay = $('.' + namespacify('overlay'));
|
||||
|
||||
if (remodal.settings.appendTo !== null && remodal.settings.appendTo.length) {
|
||||
$appendTo = $(remodal.settings.appendTo);
|
||||
}
|
||||
|
||||
if (!remodal.$overlay) {
|
||||
remodal.$overlay = $('<div>').addClass(namespacify('overlay') + ' ' + namespacify('is', STATES.CLOSED)).hide();
|
||||
$appendTo.append(remodal.$overlay);
|
||||
}
|
||||
|
||||
remodal.$bg = $('.' + namespacify('bg')).addClass(namespacify('is', STATES.CLOSED));
|
||||
|
||||
remodal.$modal = $modal
|
||||
.addClass(
|
||||
NAMESPACE + ' ' +
|
||||
namespacify('is-initialized') + ' ' +
|
||||
remodal.settings.modifier + ' ' +
|
||||
namespacify('is', STATES.CLOSED))
|
||||
.attr('tabindex', '-1');
|
||||
|
||||
remodal.$wrapper = $('<div>')
|
||||
.addClass(
|
||||
namespacify('wrapper') + ' ' +
|
||||
remodal.settings.modifier + ' ' +
|
||||
namespacify('is', STATES.CLOSED))
|
||||
.hide()
|
||||
.append(remodal.$modal);
|
||||
$appendTo.append(remodal.$wrapper);
|
||||
|
||||
// Add the event listener for the close button
|
||||
remodal.$wrapper.on('click.' + NAMESPACE, '[data-' + PLUGIN_NAME + '-action="close"]', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
remodal.close();
|
||||
});
|
||||
|
||||
// Add the event listener for the cancel button
|
||||
remodal.$wrapper.on('click.' + NAMESPACE, '[data-' + PLUGIN_NAME + '-action="cancel"]', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
remodal.$modal.trigger(STATE_CHANGE_REASONS.CANCELLATION);
|
||||
|
||||
if (remodal.settings.closeOnCancel) {
|
||||
remodal.close(STATE_CHANGE_REASONS.CANCELLATION);
|
||||
}
|
||||
});
|
||||
|
||||
// Add the event listener for the confirm button
|
||||
remodal.$wrapper.on('click.' + NAMESPACE, '[data-' + PLUGIN_NAME + '-action="confirm"]', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
remodal.$modal.trigger(STATE_CHANGE_REASONS.CONFIRMATION);
|
||||
|
||||
if (remodal.settings.closeOnConfirm) {
|
||||
remodal.close(STATE_CHANGE_REASONS.CONFIRMATION);
|
||||
}
|
||||
});
|
||||
|
||||
// Add the event listener for the overlay
|
||||
remodal.$wrapper.on('click.' + NAMESPACE, function(e) {
|
||||
var $target = $(e.target);
|
||||
var isWrapper = $target.hasClass(namespacify('wrapper'));
|
||||
var isWithin = $target.closest('.' + namespacify('is', STATES.OPENED)).length;
|
||||
|
||||
if (!isWrapper && isWithin) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (remodal.settings.closeOnOutsideClick) {
|
||||
remodal.close();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens a modal window
|
||||
* @public
|
||||
*/
|
||||
Remodal.prototype.open = function() {
|
||||
var remodal = this;
|
||||
var current;
|
||||
var modalCount;
|
||||
|
||||
// Check if the animation was completed
|
||||
if (remodal.state === STATES.OPENING || remodal.state === STATES.CLOSING) {
|
||||
return;
|
||||
}
|
||||
|
||||
// id = remodal.$modal.attr('data-' + PLUGIN_NAME + '-id');
|
||||
|
||||
if (remodal.id && remodal.settings.hashTracking) {
|
||||
scrollTop = $(window).scrollTop();
|
||||
location.hash = remodal.id;
|
||||
}
|
||||
|
||||
if (!remodal.settings.stack) {
|
||||
current = currentModal();
|
||||
if (current && current !== remodal) {
|
||||
halt(current);
|
||||
}
|
||||
}
|
||||
|
||||
modalCount = openModals.push(remodal);
|
||||
remodal.$overlay.css('z-index', function(_, value) { return parseInt(value, 10) + modalCount; });
|
||||
remodal.$wrapper.css('z-index', function(_, value) { return parseInt(value, 10) + modalCount; });
|
||||
|
||||
lockScreen();
|
||||
remodal.$bg.addClass(remodal.settings.modifier);
|
||||
remodal.$overlay.addClass(remodal.settings.modifier).show();
|
||||
remodal.$wrapper.show().scrollTop(0);
|
||||
remodal.$modal.focus();
|
||||
|
||||
syncWithAnimation(
|
||||
function() {
|
||||
setState(remodal, STATES.OPENING);
|
||||
},
|
||||
|
||||
function() {
|
||||
setState(remodal, STATES.OPENED);
|
||||
},
|
||||
|
||||
remodal);
|
||||
};
|
||||
|
||||
/**
|
||||
* Closes a modal window
|
||||
* @public
|
||||
* @param {String} reason
|
||||
*/
|
||||
Remodal.prototype.close = function(reason) {
|
||||
var remodal = this;
|
||||
var current;
|
||||
|
||||
// Check if the animation was completed
|
||||
if (remodal.state === STATES.OPENING || remodal.state === STATES.CLOSING || remodal.state === STATES.CLOSED) {
|
||||
return;
|
||||
}
|
||||
|
||||
removeModal(remodal);
|
||||
|
||||
if (
|
||||
remodal.settings.hashTracking &&
|
||||
remodal.id === location.hash.substr(1)
|
||||
) {
|
||||
current = currentModal();
|
||||
if (current) {
|
||||
location.hash = current.id;
|
||||
} else {
|
||||
location.hash = '';
|
||||
$(window).scrollTop(scrollTop);
|
||||
}
|
||||
}
|
||||
|
||||
syncWithAnimation(
|
||||
function() {
|
||||
setState(remodal, STATES.CLOSING, false, reason);
|
||||
},
|
||||
|
||||
function() {
|
||||
remodal.$bg.removeClass(remodal.settings.modifier);
|
||||
remodal.$overlay.removeClass(remodal.settings.modifier).hide();
|
||||
remodal.$wrapper.hide();
|
||||
|
||||
if (openModals.length === 0) {
|
||||
unlockScreen();
|
||||
}
|
||||
|
||||
setState(remodal, STATES.CLOSED, false, reason);
|
||||
},
|
||||
|
||||
remodal);
|
||||
};
|
||||
|
||||
/**
|
||||
* Returns a current state of a modal
|
||||
* @public
|
||||
* @returns {STATES}
|
||||
*/
|
||||
Remodal.prototype.getState = function() {
|
||||
return this.state;
|
||||
};
|
||||
|
||||
/**
|
||||
* Destroys a modal
|
||||
* @public
|
||||
*/
|
||||
Remodal.prototype.destroy = function() {
|
||||
var lookup = $[PLUGIN_NAME].lookup;
|
||||
var instanceCount;
|
||||
|
||||
halt(this);
|
||||
this.$wrapper.remove();
|
||||
|
||||
delete lookup[this.index];
|
||||
instanceCount = $.grep(lookup, function(instance) {
|
||||
return !!instance;
|
||||
}).length;
|
||||
|
||||
if (instanceCount === 0) {
|
||||
this.$overlay.remove();
|
||||
this.$bg.removeClass(
|
||||
namespacify('is', STATES.CLOSING) + ' ' +
|
||||
namespacify('is', STATES.OPENING) + ' ' +
|
||||
namespacify('is', STATES.CLOSED) + ' ' +
|
||||
namespacify('is', STATES.OPENED));
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Special plugin object for instances
|
||||
* @public
|
||||
* @type {Object}
|
||||
*/
|
||||
$[PLUGIN_NAME] = {
|
||||
lookup: []
|
||||
};
|
||||
|
||||
/**
|
||||
* Plugin constructor
|
||||
* @constructor
|
||||
* @param {Object} options
|
||||
* @returns {JQuery}
|
||||
*/
|
||||
$.fn[PLUGIN_NAME] = function(opts) {
|
||||
var instance;
|
||||
var $elem;
|
||||
|
||||
this.each(function(index, elem) {
|
||||
$elem = $(elem);
|
||||
|
||||
if ($elem.data(PLUGIN_NAME) == null) {
|
||||
instance = new Remodal($elem, opts);
|
||||
$elem.data(PLUGIN_NAME, instance.index);
|
||||
|
||||
if (
|
||||
instance.settings.hashTracking &&
|
||||
instance.id === location.hash.substr(1)
|
||||
) {
|
||||
instance.open();
|
||||
}
|
||||
} else {
|
||||
instance = $[PLUGIN_NAME].lookup[$elem.data(PLUGIN_NAME)];
|
||||
}
|
||||
});
|
||||
|
||||
return instance;
|
||||
};
|
||||
|
||||
$(document).ready(function() {
|
||||
|
||||
// data-remodal-target opens a modal window with the special Id
|
||||
$(document).on('click', '[data-' + PLUGIN_NAME + '-target]', function(e) {
|
||||
e.preventDefault();
|
||||
|
||||
var elem = e.currentTarget;
|
||||
var id = elem.getAttribute('data-' + PLUGIN_NAME + '-target');
|
||||
var $target = $('[data-' + PLUGIN_NAME + '-id="' + id + '"]');
|
||||
|
||||
$[PLUGIN_NAME].lookup[$target.data(PLUGIN_NAME)].open();
|
||||
});
|
||||
|
||||
// Auto initialization of modal windows
|
||||
// They should have the 'remodal' class attribute
|
||||
// Also you can write the `data-remodal-options` attribute to pass params into the modal
|
||||
$(document).find('.' + NAMESPACE).each(function(i, container) {
|
||||
var $container = $(container);
|
||||
var options = $container.data(PLUGIN_NAME + '-options');
|
||||
|
||||
if (!options) {
|
||||
options = {};
|
||||
} else if (typeof options === 'string' || options instanceof String) {
|
||||
options = parseOptions(options);
|
||||
}
|
||||
|
||||
$container[PLUGIN_NAME](options);
|
||||
});
|
||||
|
||||
// Handles the keydown event
|
||||
$(document).on('keydown.' + NAMESPACE, function(e) {
|
||||
var current = currentModal();
|
||||
|
||||
if (current && current.settings.closeOnEscape && current.state === STATES.OPENED && e.keyCode === 27) {
|
||||
current.close();
|
||||
}
|
||||
});
|
||||
|
||||
// Handles the hashchange event
|
||||
$(window).on('hashchange.' + NAMESPACE, handleHashChangeEvent);
|
||||
});
|
||||
});
|
||||
38
user/plugins/admin/themes/grav/app/utils/request.js
Normal file
38
user/plugins/admin/themes/grav/app/utils/request.js
Normal file
@@ -0,0 +1,38 @@
|
||||
import { parseStatus, parseJSON, userFeedback, userFeedbackError } from './response';
|
||||
import { config } from 'grav-config';
|
||||
|
||||
let raw;
|
||||
let request = function(url, options = {}, callback = () => true) {
|
||||
if (typeof options === 'function') {
|
||||
callback = options;
|
||||
options = {};
|
||||
}
|
||||
|
||||
if (options.method && options.method === 'post') {
|
||||
let data = new FormData();
|
||||
|
||||
options.body = Object.assign({ 'admin-nonce': config.admin_nonce }, options.body || {});
|
||||
Object.keys(options.body).map((key) => data.append(key, options.body[key]));
|
||||
options.body = data;
|
||||
}
|
||||
|
||||
options = Object.assign({
|
||||
credentials: 'same-origin',
|
||||
headers: {
|
||||
'Accept': 'application/json'
|
||||
}
|
||||
}, options);
|
||||
|
||||
return fetch(url, options)
|
||||
.then((response) => {
|
||||
raw = response;
|
||||
return response;
|
||||
})
|
||||
.then(parseStatus)
|
||||
.then(parseJSON)
|
||||
.then(userFeedback)
|
||||
.then((response) => callback(response, raw))
|
||||
.catch(userFeedbackError);
|
||||
};
|
||||
|
||||
export default request;
|
||||
101
user/plugins/admin/themes/grav/app/utils/response.js
Normal file
101
user/plugins/admin/themes/grav/app/utils/response.js
Normal file
@@ -0,0 +1,101 @@
|
||||
import $ from 'jquery';
|
||||
import toastr from './toastr';
|
||||
import isOnline from './offline';
|
||||
import { config } from 'grav-config';
|
||||
import trim from 'mout/string/trim';
|
||||
|
||||
let UNLOADING = false;
|
||||
let error = function(response) {
|
||||
let error = new Error(response.statusText || response || '');
|
||||
error.response = response;
|
||||
|
||||
return error;
|
||||
};
|
||||
|
||||
export function parseStatus(response) {
|
||||
return response;
|
||||
|
||||
/* Whoops can handle JSON responses so we don't need this for now.
|
||||
if (response.status >= 200 && response.status < 300) {
|
||||
return response;
|
||||
} else {
|
||||
throw error(response);
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
export function parseJSON(response) {
|
||||
return response.text().then((text) => {
|
||||
let parsed = text;
|
||||
try {
|
||||
parsed = JSON.parse(text);
|
||||
} catch (error) {
|
||||
let content = document.createElement('div');
|
||||
content.innerHTML = text;
|
||||
|
||||
let the_error = new Error();
|
||||
the_error.stack = trim(content.innerText);
|
||||
|
||||
throw the_error;
|
||||
}
|
||||
|
||||
return parsed;
|
||||
});
|
||||
}
|
||||
|
||||
export function userFeedback(response) {
|
||||
if (UNLOADING) { return true; }
|
||||
|
||||
let status = response.status || (response.error ? 'error' : '');
|
||||
let message = response.message || (response.error ? response.error.message : null);
|
||||
let settings = response.toastr || null;
|
||||
let backup;
|
||||
|
||||
switch (status) {
|
||||
case 'unauthenticated':
|
||||
document.location.href = config.base_url_relative;
|
||||
throw error('Logged out');
|
||||
case 'unauthorized':
|
||||
status = 'error';
|
||||
message = message || 'Unauthorized.';
|
||||
break;
|
||||
case 'error':
|
||||
status = 'error';
|
||||
message = message || 'Unknown error.';
|
||||
break;
|
||||
case 'success':
|
||||
status = 'success';
|
||||
message = message || '';
|
||||
break;
|
||||
default:
|
||||
status = 'error';
|
||||
message = message || 'Invalid AJAX response.';
|
||||
break;
|
||||
}
|
||||
|
||||
if (settings) {
|
||||
backup = Object.assign({}, toastr.options);
|
||||
Object.keys(settings).forEach((key) => { toastr.options[key] = settings[key]; });
|
||||
}
|
||||
|
||||
if (message && (isOnline || (!isOnline && status !== 'error'))) {
|
||||
toastr[status === 'success' ? 'success' : 'error'](message);
|
||||
}
|
||||
|
||||
if (settings) {
|
||||
toastr.options = backup;
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
export function userFeedbackError(error) {
|
||||
if (UNLOADING) { return true; }
|
||||
let stack = error.stack ? `<pre><code>${error.stack}</code></pre>` : '';
|
||||
toastr.error(`Fetch Failed: <br /> ${error.message} ${stack}`);
|
||||
console.error(`${error.message} at ${error.stack}`);
|
||||
}
|
||||
|
||||
$(global).on('beforeunload._ajax', () => {
|
||||
UNLOADING = true;
|
||||
});
|
||||
@@ -0,0 +1,42 @@
|
||||
import $ from 'jquery';
|
||||
import Selectize from 'selectize';
|
||||
|
||||
Selectize.define('option_click', function(options) {
|
||||
const self = this;
|
||||
const setup = self.setup;
|
||||
this.setup = function() {
|
||||
setup.apply(self, arguments);
|
||||
let clicking = false;
|
||||
|
||||
// Detect click on a .clickable
|
||||
self.$dropdown_content.on('mousedown click', function(e) {
|
||||
const target = $(e.target);
|
||||
if (target.hasClass('clickable') || target.closest('.clickable').length) {
|
||||
if (e.type === 'mousedown') {
|
||||
clicking = true;
|
||||
self.isFocused = false; // awful hack to defuse the document mousedown listener
|
||||
} else {
|
||||
self.isFocused = true;
|
||||
setTimeout(function() {
|
||||
clicking = false; // wait until blur has been preempted
|
||||
});
|
||||
}
|
||||
} else { // cleanup in case user right-clicked or dragged off the element
|
||||
clicking = false;
|
||||
self.isFocused = true;
|
||||
}
|
||||
});
|
||||
|
||||
// Intercept default handlers
|
||||
self.$dropdown.off('mousedown click', '[data-selectable]').on('mousedown click', '[data-selectable]', function() {
|
||||
if (!clicking) {
|
||||
return self.onOptionSelect.apply(self, arguments);
|
||||
}
|
||||
});
|
||||
self.$control_input.off('blur').on('blur', function() {
|
||||
if (!clicking) {
|
||||
return self.onBlur.apply(self, arguments);
|
||||
}
|
||||
});
|
||||
};
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* This is a plugin to override the `.refreshValidityState` method of
|
||||
* the Selectize library (https://selectize.github.io/selectize.js/).
|
||||
* The library is not maintained anymore (as of 2017-09-13) and contains
|
||||
* a bug which causes Microsoft Edge to not work with selectized [required]
|
||||
* form fields. This plugin should be removed if
|
||||
* https://github.com/selectize/selectize.js/pull/1320 is ever merged
|
||||
* and a new version of Selectize gets released.
|
||||
*/
|
||||
|
||||
import Selectize from 'selectize';
|
||||
|
||||
Selectize.define('required-fix', function(options) {
|
||||
this.refreshValidityState = () => {
|
||||
if (!this.isRequired) return false;
|
||||
|
||||
let invalid = !this.items.length;
|
||||
this.isInvalid = invalid;
|
||||
|
||||
if (invalid) {
|
||||
this.$control_input.attr('required', '');
|
||||
this.$input.removeAttr('required');
|
||||
} else {
|
||||
this.$control_input.removeAttr('required');
|
||||
this.$input.attr('required');
|
||||
}
|
||||
};
|
||||
});
|
||||
180
user/plugins/admin/themes/grav/app/utils/sidebar.js
Normal file
180
user/plugins/admin/themes/grav/app/utils/sidebar.js
Normal file
@@ -0,0 +1,180 @@
|
||||
import $ from 'jquery';
|
||||
import Cookies from '../utils/cookies';
|
||||
|
||||
const MOBILE_BREAKPOINT = 48 - 0.062;
|
||||
const DESKTOP_BREAKPOINT = 75 + 0.063;
|
||||
const EVENTS = 'touchstart._grav click._grav';
|
||||
const TARGETS = '[data-sidebar-mobile-toggle], #overlay';
|
||||
const MOBILE_QUERY = `(max-width: ${MOBILE_BREAKPOINT}em)`;
|
||||
const DESKTOP_QUERY = `(min-width: ${DESKTOP_BREAKPOINT}em)`;
|
||||
|
||||
let map = new global.Map();
|
||||
|
||||
export default class Sidebar {
|
||||
constructor() {
|
||||
this.timeout = null;
|
||||
this.isOpen = false;
|
||||
this.body = $('body');
|
||||
this.matchMedia = global.matchMedia(MOBILE_QUERY);
|
||||
this.enable();
|
||||
}
|
||||
|
||||
enable() {
|
||||
const sidebar = $('#admin-sidebar');
|
||||
|
||||
this.matchMedia.addListener(this._getBound('checkMatch'));
|
||||
this.checkMatch(this.matchMedia);
|
||||
this.body.on(EVENTS, '[data-sidebar-toggle]', this._getBound('toggleSidebarState'));
|
||||
|
||||
if (sidebar.data('quickopen')) {
|
||||
sidebar.hover(this._getBound('quickOpenIn'), this._getBound('quickOpenOut'));
|
||||
}
|
||||
}
|
||||
|
||||
disable() {
|
||||
const sidebar = $('#admin-sidebar');
|
||||
|
||||
this.close();
|
||||
this.matchMedia.removeListener(this._getBound('checkMatch'));
|
||||
this.body.off(EVENTS, '[data-sidebar-toggle]', this._getBound('toggleSidebarState'));
|
||||
if (sidebar.data('quickopen')) {
|
||||
sidebar.off('mouseenter mouseleave');
|
||||
}
|
||||
}
|
||||
|
||||
attach() {
|
||||
this.body.on(EVENTS, TARGETS, this._getBound('toggle'));
|
||||
}
|
||||
|
||||
detach() {
|
||||
this.body.off(EVENTS, TARGETS, this._getBound('toggle'));
|
||||
}
|
||||
|
||||
quickOpenIn(/* event */) {
|
||||
let isDesktop = global.matchMedia(DESKTOP_QUERY).matches;
|
||||
let delay = $('#admin-sidebar').data('quickopen-delay') || 500;
|
||||
if (this.body.hasClass('sidebar-mobile-open')) { return; }
|
||||
|
||||
let shouldQuickOpen = isDesktop ? this.body.hasClass('sidebar-closed') : !this.body.hasClass('sidebar-open');
|
||||
if (!shouldQuickOpen && !this.body.hasClass('sidebar-quickopen')) { return this.quickOpenOut(); }
|
||||
|
||||
this.timeout = setTimeout(() => {
|
||||
this.body.addClass('sidebar-open sidebar-quickopen');
|
||||
$(global).trigger('sidebar_state._grav', isDesktop);
|
||||
}, delay);
|
||||
}
|
||||
|
||||
quickOpenOut(/* event */) {
|
||||
clearTimeout(this.timeout);
|
||||
if (this.body.hasClass('sidebar-quickopen')) {
|
||||
this.body.removeClass('sidebar-open sidebar-quickopen');
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
open(event, quick = false) {
|
||||
if (event) { event.preventDefault(); }
|
||||
let overlay = $('#overlay');
|
||||
let sidebar = $('#admin-sidebar');
|
||||
|
||||
this.body.addClass('sidebar-mobile-open');
|
||||
overlay.css('display', 'block');
|
||||
|
||||
if (!quick) {
|
||||
sidebar.css('display', 'block').animate({
|
||||
opacity: 1
|
||||
}, 200, () => {
|
||||
this.isOpen = true;
|
||||
});
|
||||
} else {
|
||||
sidebar.css({ display: 'block', opacity: 1 });
|
||||
this.isOpen = true;
|
||||
}
|
||||
}
|
||||
|
||||
close(event, quick = false) {
|
||||
if (event) { event.preventDefault(); }
|
||||
let overlay = $('#overlay');
|
||||
let sidebar = $('#admin-sidebar');
|
||||
|
||||
this.body.removeClass('sidebar-mobile-open');
|
||||
overlay.css('display', 'none');
|
||||
|
||||
if (!quick) {
|
||||
sidebar.animate({
|
||||
opacity: 0
|
||||
}, 200, () => {
|
||||
sidebar.css('display', 'none');
|
||||
this.isOpen = false;
|
||||
});
|
||||
} else {
|
||||
sidebar.css({ opacity: 0, display: 'none' });
|
||||
this.isOpen = false;
|
||||
}
|
||||
}
|
||||
|
||||
toggle(event) {
|
||||
if (event) { event.preventDefault(); }
|
||||
return this[this.isOpen ? 'close' : 'open'](event);
|
||||
}
|
||||
|
||||
toggleSidebarState(event) {
|
||||
if (event) { event.preventDefault(); }
|
||||
clearTimeout(this.timeout);
|
||||
let isDesktop = global.matchMedia(DESKTOP_QUERY).matches;
|
||||
let cookie = null;
|
||||
|
||||
if (isDesktop) {
|
||||
this.body.removeClass('sidebar-open');
|
||||
}
|
||||
|
||||
if (!isDesktop) {
|
||||
this.body.removeClass('sidebar-closed');
|
||||
this.body.removeClass('sidebar-mobile-open');
|
||||
}
|
||||
|
||||
this.body.toggleClass(`sidebar-${isDesktop ? 'closed' : 'open'}`);
|
||||
$(global).trigger('sidebar_state._grav', isDesktop);
|
||||
|
||||
if (isDesktop) {
|
||||
cookie = !this.body.hasClass('sidebar-closed');
|
||||
} else {
|
||||
cookie = this.body.hasClass('sidebar-open');
|
||||
}
|
||||
|
||||
Cookies.set('grav-admin-sidebar', cookie, { expires: Infinity });
|
||||
}
|
||||
|
||||
checkMatch(data) {
|
||||
let sidebar = $('#admin-sidebar');
|
||||
let overlay = $('#overlay');
|
||||
this.isOpen = false;
|
||||
|
||||
overlay.css('display', 'none');
|
||||
sidebar.css({
|
||||
display: data.matches ? 'none' : 'inherit',
|
||||
opacity: data.matches ? 0 : 1
|
||||
});
|
||||
|
||||
if (data.matches) {
|
||||
this.body.removeClass('sidebar-open sidebar-closed');
|
||||
}
|
||||
|
||||
this[data.matches ? 'attach' : 'detach']();
|
||||
}
|
||||
|
||||
_resetMap() {
|
||||
return map.clear();
|
||||
}
|
||||
|
||||
_getBound(fn) {
|
||||
if (map.has(fn)) {
|
||||
return map.get(fn);
|
||||
}
|
||||
|
||||
return map.set(fn, this[fn].bind(this)).get(fn);
|
||||
}
|
||||
}
|
||||
|
||||
export let Instance = new Sidebar();
|
||||
41
user/plugins/admin/themes/grav/app/utils/storage.js
Normal file
41
user/plugins/admin/themes/grav/app/utils/storage.js
Normal file
@@ -0,0 +1,41 @@
|
||||
// localStorage
|
||||
(function() {
|
||||
function isSupported() {
|
||||
var item = 'localStoragePollyfill';
|
||||
try {
|
||||
localStorage.setItem(item, item);
|
||||
localStorage.removeItem(item);
|
||||
sessionStorage.setItem(item, item);
|
||||
sessionStorage.removeItem(item);
|
||||
return true;
|
||||
} catch (e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!isSupported()) {
|
||||
try {
|
||||
Storage.prototype._data = {};
|
||||
|
||||
Storage.prototype.setItem = function(id, val) {
|
||||
this._data[id] = String(val);
|
||||
return this._data[id];
|
||||
};
|
||||
|
||||
Storage.prototype.getItem = function(id) {
|
||||
return this._data.hasOwnProperty(id) ? this._data[id] : undefined;
|
||||
};
|
||||
|
||||
Storage.prototype.removeItem = function(id) {
|
||||
return delete this._data[id];
|
||||
};
|
||||
|
||||
Storage.prototype.clear = function() {
|
||||
this._data = {};
|
||||
return this._data;
|
||||
};
|
||||
} catch (e) {
|
||||
console.error('localStorage pollyfill error: ', e);
|
||||
}
|
||||
}
|
||||
}());
|
||||
29
user/plugins/admin/themes/grav/app/utils/tabs-memory.js
Normal file
29
user/plugins/admin/themes/grav/app/utils/tabs-memory.js
Normal file
@@ -0,0 +1,29 @@
|
||||
import $ from 'jquery';
|
||||
import Cookies from '../utils/cookies';
|
||||
import { Instance as Editors } from '../forms/fields/editor';
|
||||
|
||||
let Data = JSON.parse(Cookies.get('grav-tabs-state') || '{}');
|
||||
|
||||
$('body').on('touchstart click', '[data-tabid]', (event) => {
|
||||
event && event.stopPropagation();
|
||||
let target = $(event.currentTarget);
|
||||
|
||||
Data[target.data('tabkey')] = target.data('scope');
|
||||
Cookies.set('grav-tabs-state', JSON.stringify(Data), { expires: Infinity });
|
||||
|
||||
const panel = $(`[id="${target.data('tabid')}"]`);
|
||||
|
||||
target.siblings('[data-tabid]').removeClass('active');
|
||||
target.addClass('active');
|
||||
|
||||
panel.siblings('[id]').removeClass('active');
|
||||
panel.addClass('active');
|
||||
|
||||
Editors.editors.each((index, editor) => {
|
||||
let codemirror = $(editor).data('codemirror');
|
||||
if (!codemirror) { return; }
|
||||
if (codemirror.display.lastWrapWidth === 0) {
|
||||
codemirror.refresh();
|
||||
}
|
||||
});
|
||||
});
|
||||
6
user/plugins/admin/themes/grav/app/utils/toastr.js
Normal file
6
user/plugins/admin/themes/grav/app/utils/toastr.js
Normal file
@@ -0,0 +1,6 @@
|
||||
import toastr from 'toastr';
|
||||
|
||||
toastr.options.positionClass = 'toast-top-right';
|
||||
toastr.options.preventDuplicates = true;
|
||||
|
||||
export default toastr;
|
||||
Reference in New Issue
Block a user