first commit

This commit is contained in:
2020-06-11 16:42:43 +02:00
commit 449ca1d987
5099 changed files with 622034 additions and 0 deletions
@@ -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();
@@ -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));
@@ -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));
@@ -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 */
};
};
@@ -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">&#10008;</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, $);
@@ -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];
}
@@ -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();
@@ -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)
};
@@ -0,0 +1,32 @@
import { config } from 'grav-config';
import { userFeedbackError } from './response';
class KeepAlive {
constructor() {
this.active = false;
}
start() {
let timeout = config.admin_timeout / 1.5 * 1000;
this.timer = setInterval(() => this.fetch(), timeout);
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();
@@ -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;
@@ -0,0 +1,458 @@
import $ from 'jquery';
import { config, translations } from 'grav-config';
import request from '../utils/request';
import { Instance as gpm } from '../utils/gpm';
import { Promise } from 'es6-promise';
class Sorter {
getElements(elements, container) {
this.elements = elements || $('[data-gpm-plugin], [data-gpm-theme]');
this.container = container || $('.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 = '') {
let elements = this.getElements().sort((a, b) => {
let A = $(a).data(data).toString().toLowerCase();
let B = $(b).data(data).toString().toLowerCase();
return Sorter.sort(A, B, direction);
});
return elements.appendTo(this.container);
}
byName(direction = 'asc', data = 'gpm-name') {
return this.byCommon(direction, data);
}
byAuthor(direction = 'asc', data = 'gpm-author') {
return this.byCommon(direction, data);
}
byOfficial(direction = 'asc', data = 'gpm-official') {
return this.byCommon(direction, data);
}
byReleaseDate(direction = 'asc', data = 'gpm-release-date') {
let elements = this.getElements().sort((a, b) => {
let A = new Date($(a).data(data)).getTime();
let B = new Date($(b).data(data)).getTime();
return Sorter.sort(A, B, direction === 'asc' ? 'desc' : 'asc');
});
elements.appendTo(this.container);
}
byUpdatable(direction = 'asc', data = 'gpm-updatable') {
return this.byCommon(direction, data);
}
byEnabled(direction = 'asc', data = 'gpm-enabled') {
return this.byCommon(direction, data);
}
byTesting(direction = 'asc', data = 'gpm-testing') {
return this.byCommon(direction, data);
}
}
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);
Promise.all(slugs.map((slug) => {
return request(url, {
method: 'post',
body: {
package: slug,
type: type
}
});
})).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();
// 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] .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');
if (response.status === 'error') {
let remodal = $.remodal.lookup[$('[data-packages-modal]').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();
@@ -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;
@@ -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,40 @@
import $ from 'jquery';
import GeminiScrollbar from 'gemini-scrollbar';
const defaults = {
autoshow: false,
createElements: true,
forceGemini: false
};
export default class Scrollbar {
constructor(element, options) {
this.element = $(element);
this.created = false;
if (!this.element.length) { return; }
this.options = Object.assign({}, defaults, options, { element: this.element[0] });
this.element.css('overflow', 'auto');
this.instance = new GeminiScrollbar(this.options);
this.create();
this.element.data('scrollbar', this.instance);
}
create() {
this.instance.create();
this.created = true;
}
update() {
if (!this.created) { return false; }
this.instance.update();
}
destroy() {
if (!this.created) { return false; }
this.instance.destroy();
}
}
export let Instance = new Scrollbar('#admin-main .content-wrapper');
@@ -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');
}
};
});
@@ -0,0 +1,179 @@
import $ from 'jquery';
import Scrollbar from './scrollbar';
import Map from 'es6-map';
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 Map();
export default class Sidebar {
constructor() {
this.timeout = null;
this.isOpen = false;
this.body = $('body');
this.matchMedia = global.matchMedia(MOBILE_QUERY);
this.scroller = new Scrollbar('.admin-menu-wrapper', { autoshow: true });
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');
let scrollbar = $('#admin-menu').data('scrollbar');
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;
}
if (scrollbar) { scrollbar.update(); }
}
close(event, quick = false) {
if (event) { event.preventDefault(); }
let overlay = $('#overlay');
let sidebar = $('#admin-sidebar');
let scrollbar = $('#admin-menu').data('scrollbar');
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;
}
if (scrollbar) { scrollbar.update(); }
}
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;
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);
}
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();
@@ -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);
}
}
}());
@@ -0,0 +1,29 @@
import $ from 'jquery';
import Cookies from 'cookies-js';
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();
}
});
});
@@ -0,0 +1,6 @@
import toastr from 'toastr';
toastr.options.positionClass = 'toast-top-right';
toastr.options.preventDuplicates = true;
export default toastr;