This commit is contained in:
2024-05-20 15:37:46 +03:00
commit 00b7dbd0b7
10404 changed files with 3285853 additions and 0 deletions

View File

@ -0,0 +1,243 @@
'use strict';
// This file contains debug screen for edit-media
var debugModal;
jQuery(document).ready(function(jq) {
$ = jq;
debugModal = function() {
}
debugModal.prototype = {
currentModal: null,
modals: [],
parent: '#wpbody', // modal will be written to this element.
multiple: false,
windowHeight: false,
windowWidth: false,
setWidth: false,
setHeight: false,
target: false,
}
debugModal.prototype.init = function()
{
this.windowHeight = $(window).height();
this.windowWidth = $(window).width();
$(document).off('click', '.debugModal');
$(document).on('click', '.debugModal', $.proxy(this.buildModal, this));
$(window).on('resize', $.proxy(this.checkResize, this));
}
debugModal.prototype.get = function()
{
return this.currentModal;
}
debugModal.prototype.show = function()
{
$('.debugModal_overlay').remove();
$('body').removeClass('debug-modal-active');
this.writeOverlay();
this.currentModal.show();
if (this.setWidth)
{
this.currentModal.width(this.setWidth);
}
if (this.setHeight)
{
this.currentModal.height(this.setHeight);
}
var $m = this.currentModal;
var headerHeight = $m.find('.modal_header').outerHeight();
var contentHeight = $m.find('.modal_content').outerHeight();
var contentWidth = $m.find('.modal_content').width();
var modalHeight = headerHeight + contentHeight; //this.currentModal.height();
var modalWidth = contentWidth; //this.currentModal.width();
var top = (this.windowHeight - modalHeight) / 2;
var left = (this.windowWidth - modalWidth) / 2;
if (top < 30)
{
top = 30; // top + admin bar
}
if (left < 0)
{
left: 0;
}
if (modalHeight > this.windowHeight) // if height is higher than screen supports
{
var newHeight = this.windowHeight - top - 5;
this.currentModal.height(newHeight);
var newContentH = newHeight - headerHeight;
$m.find('.modal_content').height(newContentH);
}
this.currentModal.css('left', left + 'px');
this.currentModal.css('top', top + 'px');
this.currentModal.css('height', modalHeight);
$('.debugModal_overlay').show();
$('body').addClass('shortpixel-modal-active');
$(document).off('keydown', $.proxy(this.keyPressHandler, this));
$(document).on('keydown', $.proxy(this.keyPressHandler, this));
this.currentModal.trigger('focus');
}
debugModal.prototype.keyPressHandler = function (e)
{
if (e.keyCode === 27)
this.close();
}
debugModal.prototype.checkResize = function ()
{
this.windowHeight = $(window).height();
this.windowWidth = $(window).width();
if (this.currentModal === null)
return;
this.currentModal.removeAttr('style');
this.currentModal.find('.modal_content').removeAttr('style');
this.currentModal.removeAttr('style');
// redo sizes, repaint.
this.show();
}
debugModal.prototype.close = function()
{
this.currentModal.trigger('modal_close', [this]);
this.currentModal.remove();
this.currentModal = null;
$('.debugModal_overlay').remove();
$('body').removeClass('shortpixel-modal-active');
$(document).off('keydown', $.proxy(this.keyPressHandler, this));
}
debugModal.prototype.fadeOut = function (timeOut)
{
if (typeof timeOut == undefined)
timeOut = 600;
var self = this;
this.currentModal.fadeOut(timeOut, function() { self.close(); } );
}
/* Set the modal content
Sets the content of the modal. Do not run this function after adding controls.
@param string HTML,text content of the modal
*/
debugModal.prototype.setContent = function(content)
{
this.currentModal.find('.modal_content').html(content);
}
/* Builds modal from hidden data
Builds modal from an formatted data object in DOM. Triggered on Click
*/
debugModal.prototype.buildModal = function(e)
{
e.preventDefault();
var target = $(e.target);
if (typeof target.data('modal') == 'undefined')
target = target.parents('.debugModal');
this.target = target;
var id = target.data('modal');
var data = $('#' + id);
// options
if (typeof data.data('width') !== 'undefined')
this.setWidth = data.data('width');
else
this.setWidth = false;
if (typeof data.data('height') !== 'undefined')
this.setHeight = data.data('height');
else
this.setHeight = false;
// var title = $(data).find('.title').text();
// var controls = $(data).find('.controls').html();
var content = $(data).find('.content').html();
this.newModal(id);
this.setContent(content);
// callback on init
if (typeof $(data).data('load') !== 'undefined')
{
// default call
var funcName = data.data('load') + '(modal)';
var callFunc = new Function ('modal', funcName);
}
this.show();
}
debugModal.prototype.newModal = function(id)
{
if (this.currentModal !== null)
this.close();
var modal = $('<div class="debug-modal ' + id + '" > \
<div class="modal_header"> \
<div class="modal_close dashicons dashicons-no"></div><h3 class="modal_title">Debug</h3> \
</div> \
<div class="inner modal_content"></div>\
</div>');
if ($(this.parent).length > 0)
$(this.parent).append(modal);
else
$('body').append(modal); // fallback in case of interrupting page builders
$(modal).draggable({
handle: '.modal_header'
});
this.modals.push(modal);
this.currentModal = modal;
document.querySelector('.debug-modal .modal_close').addEventListener('click', this.close.bind(this), { once: true} );
return this;
}
debugModal.prototype.writeOverlay = function()
{
$(this.parent).append('<div class="debugModal_overlay"></div>');
$('.debugModal_overlay').on('click', $.proxy(this.close, this));
}
var shortpixelDebug = new debugModal();
shortpixelDebug.init();
}); // jquery

View File

@ -0,0 +1,805 @@
/*!jQuery Knob*/
/**
* Downward compatible, touchable dial
*
* Version: 1.2.12
* Requires: jQuery v1.7+
*
* Copyright (c) 2012 Anthony Terrien
* Under MIT License (http://www.opensource.org/licenses/mit-license.php)
*
* Thanks to vor, eskimoblood, spiffistan, FabrizioC
*/
(function (factory) {
if (typeof exports === 'object') {
// CommonJS
module.exports = factory(require('jquery'));
} else if (typeof define === 'function' && define.amd) {
// AMD. Register as an anonymous module.
define(['jquery'], factory);
} else {
// Browser globals
factory(jQuery);
}
}(function ($) {
/**
* Kontrol library
*/
"use strict";
/**
* Definition of globals and core
*/
var k = {}, // kontrol
max = Math.max,
min = Math.min;
k.c = {};
k.c.d = $(document);
k.c.t = function (e) {
return e.originalEvent.touches.length - 1;
};
/**
* Kontrol Object
*
* Definition of an abstract UI control
*
* Each concrete component must call this one.
* <code>
* k.o.call(this);
* </code>
*/
k.o = function () {
var s = this;
this.o = null; // array of options
this.$ = null; // jQuery wrapped element
this.i = null; // mixed HTMLInputElement or array of HTMLInputElement
this.g = null; // deprecated 2D graphics context for 'pre-rendering'
this.v = null; // value ; mixed array or integer
this.cv = null; // change value ; not commited value
this.x = 0; // canvas x position
this.y = 0; // canvas y position
this.w = 0; // canvas width
this.h = 0; // canvas height
this.$c = null; // jQuery canvas element
this.c = null; // rendered canvas context
this.t = 0; // touches index
this.isInit = false;
this.fgColor = null; // main color
this.pColor = null; // previous color
this.dH = null; // draw hook
this.cH = null; // change hook
this.eH = null; // cancel hook
this.rH = null; // release hook
this.scale = 1; // scale factor
this.relative = false;
this.relativeWidth = false;
this.relativeHeight = false;
this.$div = null; // component div
this.run = function () {
var cf = function (e, conf) {
var k;
for (k in conf) {
s.o[k] = conf[k];
}
s._carve().init();
s._configure()
._draw();
};
if (this.$.data('kontroled')) return;
this.$.data('kontroled', true);
this.extend();
this.o = $.extend({
// Config
min: this.$.data('min') !== undefined ? this.$.data('min') : 0,
max: this.$.data('max') !== undefined ? this.$.data('max') : 100,
stopper: true,
readOnly: this.$.data('readonly') || (this.$.attr('readonly') === 'readonly'),
// UI
cursor: this.$.data('cursor') === true && 30
|| this.$.data('cursor') || 0,
thickness: this.$.data('thickness')
&& Math.max(Math.min(this.$.data('thickness'), 1), 0.01)
|| 0.35,
lineCap: this.$.data('linecap') || 'butt',
width: this.$.data('width') || 200,
height: this.$.data('height') || 200,
displayInput: this.$.data('displayinput') == null || this.$.data('displayinput'),
displayPrevious: this.$.data('displayprevious'),
fgColor: this.$.data('fgcolor') || '#87CEEB',
inputColor: this.$.data('inputcolor'),
font: this.$.data('font') || 'Arial',
fontWeight: this.$.data('font-weight') || 'bold',
inline: false,
step: this.$.data('step') || 1,
rotation: this.$.data('rotation'),
// Hooks
draw: null, // function () {}
change: null, // function (value) {}
cancel: null, // function () {}
release: null, // function (value) {}
// Output formatting, allows to add unit: %, ms ...
format: function(v) {
return v;
},
parse: function (v) {
return parseFloat(v);
}
}, this.o
);
// finalize options
this.o.flip = this.o.rotation === 'anticlockwise' || this.o.rotation === 'acw';
if (!this.o.inputColor) {
this.o.inputColor = this.o.fgColor;
}
// routing value
if (this.$.is('fieldset')) {
// fieldset = array of integer
this.v = {};
this.i = this.$.find('input');
this.i.each(function(k) {
var $this = $(this);
s.i[k] = $this;
s.v[k] = s.o.parse($this.val());
$this.bind(
'change blur',
function () {
var val = {};
val[k] = $this.val();
s.val(s._validate(val));
}
);
});
this.$.find('legend').remove();
} else {
// input = integer
this.i = this.$;
this.v = this.o.parse(this.$.val());
this.v === '' && (this.v = this.o.min);
this.$.bind(
'change blur',
function () {
s.val(s._validate(s.o.parse(s.$.val())));
}
);
}
!this.o.displayInput && this.$.hide();
// adds needed DOM elements (canvas, div)
this.$c = $(document.createElement('canvas')).attr({
width: this.o.width,
height: this.o.height
});
// wraps all elements in a div
// add to DOM before Canvas init is triggered
this.$div = $('<div style="'
+ (this.o.inline ? 'display:inline;' : '')
+ 'width:' + this.o.width + 'px;height:' + this.o.height + 'px;'
+ '"></div>');
this.$.wrap(this.$div).before(this.$c);
this.$div = this.$.parent();
if (typeof G_vmlCanvasManager !== 'undefined') {
G_vmlCanvasManager.initElement(this.$c[0]);
}
this.c = this.$c[0].getContext ? this.$c[0].getContext('2d') : null;
if (!this.c) {
throw {
name: "CanvasNotSupportedException",
message: "Canvas not supported. Please use excanvas on IE8.0.",
toString: function(){return this.name + ": " + this.message}
}
}
// hdpi support
this.scale = (window.devicePixelRatio || 1) / (
this.c.webkitBackingStorePixelRatio ||
this.c.mozBackingStorePixelRatio ||
this.c.msBackingStorePixelRatio ||
this.c.oBackingStorePixelRatio ||
this.c.backingStorePixelRatio || 1
);
// detects relative width / height
this.relativeWidth = this.o.width % 1 !== 0
&& this.o.width.indexOf('%');
this.relativeHeight = this.o.height % 1 !== 0
&& this.o.height.indexOf('%');
this.relative = this.relativeWidth || this.relativeHeight;
// computes size and carves the component
this._carve();
// prepares props for transaction
if (this.v instanceof Object) {
this.cv = {};
this.copy(this.v, this.cv);
} else {
this.cv = this.v;
}
// binds configure event
this.$
.bind("configure", cf)
.parent()
.bind("configure", cf);
// finalize init
this._listen()
._configure()
._xy()
.init();
this.isInit = true;
this.$.val(this.o.format(this.v));
this._draw();
return this;
};
this._carve = function() {
if (this.relative) {
var w = this.relativeWidth ?
this.$div.parent().width() *
parseInt(this.o.width) / 100
: this.$div.parent().width(),
h = this.relativeHeight ?
this.$div.parent().height() *
parseInt(this.o.height) / 100
: this.$div.parent().height();
// apply relative
this.w = this.h = Math.min(w, h);
} else {
this.w = this.o.width;
this.h = this.o.height;
}
// finalize div
this.$div.css({
'width': this.w + 'px',
'height': this.h + 'px'
});
// finalize canvas with computed width
this.$c.attr({
width: this.w,
height: this.h
});
// scaling
if (this.scale !== 1) {
this.$c[0].width = this.$c[0].width * this.scale;
this.$c[0].height = this.$c[0].height * this.scale;
this.$c.width(this.w);
this.$c.height(this.h);
}
return this;
};
this._draw = function () {
// canvas pre-rendering
var d = true;
s.g = s.c;
s.clear();
s.dH && (d = s.dH());
d !== false && s.draw();
};
this._touch = function (e) {
var touchMove = function (e) {
var v = s.xy2val(
e.originalEvent.touches[s.t].pageX,
e.originalEvent.touches[s.t].pageY
);
if (v == s.cv) return;
if (s.cH && s.cH(v) === false) return;
s.change(s._validate(v));
s._draw();
};
// get touches index
this.t = k.c.t(e);
// First touch
touchMove(e);
// Touch events listeners
k.c.d
.bind("touchmove.k", touchMove)
.bind(
"touchend.k",
function () {
k.c.d.unbind('touchmove.k touchend.k');
s.val(s.cv);
}
);
return this;
};
this._mouse = function (e) {
var mouseMove = function (e) {
var v = s.xy2val(e.pageX, e.pageY);
if (v == s.cv) return;
if (s.cH && (s.cH(v) === false)) return;
s.change(s._validate(v));
s._draw();
};
// First click
mouseMove(e);
// Mouse events listeners
k.c.d
.bind("mousemove.k", mouseMove)
.bind(
// Escape key cancel current change
"keyup.k",
function (e) {
if (e.keyCode === 27) {
k.c.d.unbind("mouseup.k mousemove.k keyup.k");
if (s.eH && s.eH() === false)
return;
s.cancel();
}
}
)
.bind(
"mouseup.k",
function (e) {
k.c.d.unbind('mousemove.k mouseup.k keyup.k');
s.val(s.cv);
}
);
return this;
};
this._xy = function () {
var o = this.$c.offset();
this.x = o.left;
this.y = o.top;
return this;
};
this._listen = function () {
if (!this.o.readOnly) {
this.$c
.bind(
"mousedown",
function (e) {
e.preventDefault();
s._xy()._mouse(e);
}
)
.bind(
"touchstart",
function (e) {
e.preventDefault();
s._xy()._touch(e);
}
);
this.listen();
} else {
this.$.attr('readonly', 'readonly');
}
if (this.relative) {
$(window).resize(function() {
s._carve().init();
s._draw();
});
}
return this;
};
this._configure = function () {
// Hooks
if (this.o.draw) this.dH = this.o.draw;
if (this.o.change) this.cH = this.o.change;
if (this.o.cancel) this.eH = this.o.cancel;
if (this.o.release) this.rH = this.o.release;
if (this.o.displayPrevious) {
this.pColor = this.h2rgba(this.o.fgColor, "0.4");
this.fgColor = this.h2rgba(this.o.fgColor, "0.6");
} else {
this.fgColor = this.o.fgColor;
}
return this;
};
this._clear = function () {
this.$c[0].width = this.$c[0].width;
};
this._validate = function (v) {
var val = (~~ (((v < 0) ? -0.5 : 0.5) + (v/this.o.step))) * this.o.step;
return Math.round(val * 100) / 100;
};
// Abstract methods
this.listen = function () {}; // on start, one time
this.extend = function () {}; // each time configure triggered
this.init = function () {}; // each time configure triggered
this.change = function (v) {}; // on change
this.val = function (v) {}; // on release
this.xy2val = function (x, y) {}; //
this.draw = function () {}; // on change / on release
this.clear = function () { this._clear(); };
// Utils
this.h2rgba = function (h, a) {
var rgb;
h = h.substring(1,7);
rgb = [
parseInt(h.substring(0,2), 16),
parseInt(h.substring(2,4), 16),
parseInt(h.substring(4,6), 16)
];
return "rgba(" + rgb[0] + "," + rgb[1] + "," + rgb[2] + "," + a + ")";
};
this.copy = function (f, t) {
for (var i in f) {
t[i] = f[i];
}
};
};
/**
* k.Dial
*/
k.Dial = function () {
k.o.call(this);
this.startAngle = null;
this.xy = null;
this.radius = null;
this.lineWidth = null;
this.cursorExt = null;
this.w2 = null;
this.PI2 = 2*Math.PI;
this.extend = function () {
this.o = $.extend({
bgColor: this.$.data('bgcolor') || '#EEEEEE',
angleOffset: this.$.data('angleoffset') || 0,
angleArc: this.$.data('anglearc') || 360,
inline: true
}, this.o);
};
this.val = function (v, triggerRelease) {
if (null != v) {
// reverse format
v = this.o.parse(v);
if (triggerRelease !== false
&& v != this.v
&& this.rH
&& this.rH(v) === false) { return; }
this.cv = this.o.stopper ? max(min(v, this.o.max), this.o.min) : v;
this.v = this.cv;
this.$.val(this.o.format(this.v));
this._draw();
} else {
return this.v;
}
};
this.xy2val = function (x, y) {
var a, ret;
a = Math.atan2(
x - (this.x + this.w2),
- (y - this.y - this.w2)
) - this.angleOffset;
if (this.o.flip) {
a = this.angleArc - a - this.PI2;
}
if (this.angleArc != this.PI2 && (a < 0) && (a > -0.5)) {
// if isset angleArc option, set to min if .5 under min
a = 0;
} else if (a < 0) {
a += this.PI2;
}
ret = (a * (this.o.max - this.o.min) / this.angleArc) + this.o.min;
this.o.stopper && (ret = max(min(ret, this.o.max), this.o.min));
return ret;
};
this.listen = function () {
// bind MouseWheel
var s = this, mwTimerStop,
mwTimerRelease,
mw = function (e) {
e.preventDefault();
var ori = e.originalEvent,
deltaX = ori.detail || ori.wheelDeltaX,
deltaY = ori.detail || ori.wheelDeltaY,
v = s._validate(s.o.parse(s.$.val()))
+ (
deltaX > 0 || deltaY > 0
? s.o.step
: deltaX < 0 || deltaY < 0 ? -s.o.step : 0
);
v = max(min(v, s.o.max), s.o.min);
s.val(v, false);
if (s.rH) {
// Handle mousewheel stop
clearTimeout(mwTimerStop);
mwTimerStop = setTimeout(function () {
s.rH(v);
mwTimerStop = null;
}, 100);
// Handle mousewheel releases
if (!mwTimerRelease) {
mwTimerRelease = setTimeout(function () {
if (mwTimerStop)
s.rH(v);
mwTimerRelease = null;
}, 200);
}
}
},
kval,
to,
m = 1,
kv = {
37: -s.o.step,
38: s.o.step,
39: s.o.step,
40: -s.o.step
};
this.$
.bind(
"keydown",
function (e) {
var kc = e.keyCode;
// numpad support
if (kc >= 96 && kc <= 105) {
kc = e.keyCode = kc - 48;
}
kval = parseInt(String.fromCharCode(kc));
if (isNaN(kval)) {
(kc !== 13) // enter
&& kc !== 8 // bs
&& kc !== 9 // tab
&& kc !== 189 // -
&& (kc !== 190
|| s.$.val().match(/\./)) // . allowed once
&& e.preventDefault();
// arrows
if ($.inArray(kc,[37,38,39,40]) > -1) {
e.preventDefault();
var v = s.o.parse(s.$.val()) + kv[kc] * m;
s.o.stopper && (v = max(min(v, s.o.max), s.o.min));
s.change(s._validate(v));
s._draw();
// long time keydown speed-up
to = window.setTimeout(function () {
m *= 2;
}, 30);
}
}
}
)
.bind(
"keyup",
function (e) {
if (isNaN(kval)) {
if (to) {
window.clearTimeout(to);
to = null;
m = 1;
s.val(s.$.val());
}
} else {
// kval postcond
(s.$.val() > s.o.max && s.$.val(s.o.max))
|| (s.$.val() < s.o.min && s.$.val(s.o.min));
}
}
);
this.$c.bind("mousewheel DOMMouseScroll", mw);
this.$.bind("mousewheel DOMMouseScroll", mw);
};
this.init = function () {
if (this.v < this.o.min
|| this.v > this.o.max) { this.v = this.o.min; }
this.$.val(this.v);
this.w2 = this.w / 2;
this.cursorExt = this.o.cursor / 100;
this.xy = this.w2 * this.scale;
this.lineWidth = this.xy * this.o.thickness;
this.lineCap = this.o.lineCap;
this.radius = this.xy - this.lineWidth / 2;
this.o.angleOffset
&& (this.o.angleOffset = isNaN(this.o.angleOffset) ? 0 : this.o.angleOffset);
this.o.angleArc
&& (this.o.angleArc = isNaN(this.o.angleArc) ? this.PI2 : this.o.angleArc);
// deg to rad
this.angleOffset = this.o.angleOffset * Math.PI / 180;
this.angleArc = this.o.angleArc * Math.PI / 180;
// compute start and end angles
this.startAngle = 1.5 * Math.PI + this.angleOffset;
this.endAngle = 1.5 * Math.PI + this.angleOffset + this.angleArc;
var s = max(
String(Math.abs(this.o.max)).length,
String(Math.abs(this.o.min)).length,
2
) + 2;
this.o.displayInput
&& this.i.css({
'width' : ((this.w / 2 + 4) >> 0) + 'px',
'height' : ((this.w / 2) >> 0) + 'px',
'position' : 'absolute',
'vertical-align' : 'middle',
'margin-top' : ((this.w / 4) >> 0) + 'px',
'margin-left' : '-' + ((this.w * 3 / 4 + 2) >> 0) + 'px',
'border' : 0,
'background' : 'none',
'font' : this.o.fontWeight + ' ' + ((this.w / s) >> 0) + 'px ' + this.o.font,
'text-align' : 'center',
'color' : this.o.inputColor || this.o.fgColor,
'padding' : '0px',
'-webkit-appearance': 'none'
}) || this.i.css({
'width': '0px',
'visibility': 'hidden'
});
};
this.change = function (v) {
this.cv = v;
this.$.val(this.o.format(v));
};
this.angle = function (v) {
return (v - this.o.min) * this.angleArc / (this.o.max - this.o.min);
};
this.arc = function (v) {
var sa, ea;
v = this.angle(v);
if (this.o.flip) {
sa = this.endAngle + 0.00001;
ea = sa - v - 0.00001;
} else {
sa = this.startAngle - 0.00001;
ea = sa + v + 0.00001;
}
this.o.cursor
&& (sa = ea - this.cursorExt)
&& (ea = ea + this.cursorExt);
return {
s: sa,
e: ea,
d: this.o.flip && !this.o.cursor
};
};
this.draw = function () {
var c = this.g, // context
a = this.arc(this.cv), // Arc
pa, // Previous arc
r = 1;
c.lineWidth = this.lineWidth;
c.lineCap = this.lineCap;
if (this.o.bgColor !== "none") {
c.beginPath();
c.strokeStyle = this.o.bgColor;
c.arc(this.xy, this.xy, this.radius, this.endAngle - 0.00001, this.startAngle + 0.00001, true);
c.stroke();
}
if (this.o.displayPrevious) {
pa = this.arc(this.v);
c.beginPath();
c.strokeStyle = this.pColor;
c.arc(this.xy, this.xy, this.radius, pa.s, pa.e, pa.d);
c.stroke();
r = this.cv == this.v;
}
c.beginPath();
c.strokeStyle = r ? this.o.fgColor : this.fgColor ;
c.arc(this.xy, this.xy, this.radius, a.s, a.e, a.d);
c.stroke();
};
this.cancel = function () {
this.val(this.v);
};
};
$.fn.dial = $.fn.knob = function (o) {
return this.each(
function () {
var d = new k.Dial();
d.o = o;
d.$ = $(this);
d.run();
}
).parent();
};
}));

File diff suppressed because one or more lines are too long

View File

@ -0,0 +1,128 @@
(function($){
$.fn.twentytwenty = function(options) {
var options = $.extend({
default_offset_pct: 0.5,
orientation: 'horizontal',
slider_move: "drag" //"drag" or "mousemove"
}, options);
return this.each(function() {
var sliderPct = options.default_offset_pct;
var container = $(this);
var sliderOrientation = options.orientation;
var beforeDirection = (sliderOrientation === 'vertical') ? 'down' : 'left';
var afterDirection = (sliderOrientation === 'vertical') ? 'up' : 'right';
container.wrap("<div class='twentytwenty-wrapper twentytwenty-" + sliderOrientation + "'></div>");
container.append("<div class='twentytwenty-overlay'></div>");
var beforeImg = container.find("img:first");
var afterImg = container.find("img:last");
container.append("<div class='twentytwenty-handle'></div>");
var slider = container.find(".twentytwenty-handle");
slider.append("<span class='twentytwenty-" + beforeDirection + "-arrow'></span>");
slider.append("<span class='twentytwenty-" + afterDirection + "-arrow'></span>");
container.addClass("twentytwenty-container");
beforeImg.addClass("twentytwenty-before");
afterImg.addClass("twentytwenty-after");
var overlay = container.find(".twentytwenty-overlay");
overlay.append("<div class='twentytwenty-before-label'></div>");
overlay.append("<div class='twentytwenty-after-label'></div>");
var beforeLabel = container.find("div.twentytwenty-before-label");
var afterLabel = container.find("div.twentytwenty-after-label");
var calcOffset = function(dimensionPct) {
var w = beforeImg.width();
var h = beforeImg.height();
return {
w: w+"px",
h: h+"px",
cw: (dimensionPct*w)+"px",
ch: (dimensionPct*h)+"px",
};
};
var adjustContainer = function(offset) {
if (sliderOrientation === 'vertical') {
beforeImg.css("clip", "rect(0,"+offset.w+","+offset.ch+",0)");
}
else {
beforeImg.css("clip", "rect(0,"+offset.cw+","+offset.h+",0)");
beforeLabel.css("clip", "rect(0,"+offset.cw+","+offset.h+",0)");
afterLabel.css("clip", "rect(0,"+offset.w+","+offset.h+","+offset.cw+")"); //clip: rect(0px, 700px, 1000px, 600px);
}
container.css("height", offset.h);
};
var adjustSlider = function(pct) {
var offset = calcOffset(pct);
slider.css((sliderOrientation==="vertical") ? "top" : "left", (sliderOrientation==="vertical") ? offset.ch : offset.cw);
adjustContainer(offset);
}
$(window).on("resize.twentytwenty", function(e) {
adjustSlider(sliderPct);
});
var offsetX = 0;
var imgWidth = 0;
if(options.slider_move == "drag") {
slider.on("movestart", function(e) {
if (((e.distX > e.distY && e.distX < -e.distY) || (e.distX < e.distY && e.distX > -e.distY)) && sliderOrientation !== 'vertical') {
e.preventDefault();
}
else if (((e.distX < e.distY && e.distX < -e.distY) || (e.distX > e.distY && e.distX > -e.distY)) && sliderOrientation === 'vertical') {
e.preventDefault();
}
container.addClass("active");
offsetX = container.offset().left;
offsetY = container.offset().top;
imgWidth = beforeImg.width();
imgHeight = beforeImg.height();
});
slider.on("moveend", function(e) {
container.removeClass("active");
});
slider.on("move", function(e) {
if (container.hasClass("active")) {
sliderPct = (sliderOrientation === 'vertical') ? (e.pageY-offsetY)/imgHeight : (e.pageX-offsetX)/imgWidth;
if (sliderPct < 0) {
sliderPct = 0;
}
if (sliderPct > 1) {
sliderPct = 1;
}
adjustSlider(sliderPct);
}
});
} else {
container.mousemove(function(e) {
sliderPct = (sliderOrientation === 'vertical')
? (e.pageY-container.offset().top)/beforeImg.height()
: (e.pageX-container.offset().left)/beforeImg.width();
if (sliderPct < 0) {
sliderPct = 0;
}
if (sliderPct > 1) {
sliderPct = 1;
}
adjustSlider(sliderPct);
});
}
container.find("img").on("mousedown", function(event) {
event.preventDefault();
});
$(window).trigger("resize.twentytwenty");
});
};
})(jQuery);

View File

@ -0,0 +1 @@
(function(a){a.fn.twentytwenty=function(b){var b=a.extend({default_offset_pct:0.5,orientation:"horizontal",slider_move:"drag"},b);return this.each(function(){var h=b.default_offset_pct;var e=a(this);var c=b.orientation;var q=(c==="vertical")?"down":"left";var d=(c==="vertical")?"up":"right";e.wrap("<div class='twentytwenty-wrapper twentytwenty-"+c+"'></div>");e.append("<div class='twentytwenty-overlay'></div>");var r=e.find("img:first");var o=e.find("img:last");e.append("<div class='twentytwenty-handle'></div>");var f=e.find(".twentytwenty-handle");f.append("<span class='twentytwenty-"+q+"-arrow'></span>");f.append("<span class='twentytwenty-"+d+"-arrow'></span>");e.addClass("twentytwenty-container");r.addClass("twentytwenty-before");o.addClass("twentytwenty-after");var j=e.find(".twentytwenty-overlay");j.append("<div class='twentytwenty-before-label'></div>");j.append("<div class='twentytwenty-after-label'></div>");var l=e.find("div.twentytwenty-before-label");var k=e.find("div.twentytwenty-after-label");var i=function(t){var s=r.width();var u=r.height();return{w:s+"px",h:u+"px",cw:(t*s)+"px",ch:(t*u)+"px"}};var g=function(s){if(c==="vertical"){r.css("clip","rect(0,"+s.w+","+s.ch+",0)")}else{r.css("clip","rect(0,"+s.cw+","+s.h+",0)");l.css("clip","rect(0,"+s.cw+","+s.h+",0)");k.css("clip","rect(0,"+s.w+","+s.h+","+s.cw+")")}e.css("height",s.h)};var n=function(s){var t=i(s);f.css((c==="vertical")?"top":"left",(c==="vertical")?t.ch:t.cw);g(t)};a(window).on("resize.twentytwenty",function(s){n(h)});var m=0;var p=0;if(b.slider_move=="drag"){f.on("movestart",function(s){if(((s.distX>s.distY&&s.distX<-s.distY)||(s.distX<s.distY&&s.distX>-s.distY))&&c!=="vertical"){s.preventDefault()}else{if(((s.distX<s.distY&&s.distX<-s.distY)||(s.distX>s.distY&&s.distX>-s.distY))&&c==="vertical"){s.preventDefault()}}e.addClass("active");m=e.offset().left;offsetY=e.offset().top;p=r.width();imgHeight=r.height()});f.on("moveend",function(s){e.removeClass("active")});f.on("move",function(s){if(e.hasClass("active")){h=(c==="vertical")?(s.pageY-offsetY)/imgHeight:(s.pageX-m)/p;if(h<0){h=0}if(h>1){h=1}n(h)}})}else{e.mousemove(function(s){h=(c==="vertical")?(s.pageY-e.offset().top)/r.height():(s.pageX-e.offset().left)/r.width();if(h<0){h=0}if(h>1){h=1}n(h)})}e.find("img").on("mousedown",function(s){s.preventDefault()});a(window).trigger("resize.twentytwenty")})}})(jQuery);

View File

@ -0,0 +1,449 @@
'use strict';
/** ShortPixel: Changed punycode to sp_punycode in order to prevent possible conflicts with other WP plugins that use punycode
* Also moved all the functions inside the sp_punycode const for the same reason and changed const's to let's */
// 5.0 Deprecated ** Let's see if the modern world works without this **/
var sp_punycode = function(){
/** Highest positive signed 32-bit float value */
var maxInt = 2147483647; // aka. 0x7FFFFFFF or 2^31-1
/** Bootstring parameters */
var base = 36;
var tMin = 1;
var tMax = 26;
var skew = 38;
var damp = 700;
var initialBias = 72;
var initialN = 128; // 0x80
var delimiter = '-'; // '\x2D'
/** Regular expressions */
var regexPunycode = /^xn--/;
var regexNonASCII = /[^\0-\x7E]/; // non-ASCII chars
var regexSeparators = /[\x2E\u3002\uFF0E\uFF61]/g; // RFC 3490 separators
/** Error messages */
var errors = {
'overflow': 'Overflow: input needs wider integers to process',
'not-basic': 'Illegal input >= 0x80 (not a basic code point)',
'invalid-input': 'Invalid input'
};
/** Convenience shortcuts */
var baseMinusTMin = base - tMin;
var floor = Math.floor;
var stringFromCharCode = String.fromCharCode;
/*--------------------------------------------------------------------------*/
/**
* A generic error utility function.
* @private
* @param {String} type The error type.
* @returns {Error} Throws a `RangeError` with the applicable error message.
*/
function error(type) {
throw new RangeError(errors[type]);
}
/**
* A generic `Array#map` utility function.
* @private
* @param {Array} array The array to iterate over.
* @param {Function} callback The function that gets called for every array
* item.
* @returns {Array} A new array of values returned by the callback function.
*/
function map(array, fn) {
var result = [];
var length = array.length;
while (length--) {
result[length] = fn(array[length]);
}
return result;
}
/**
* A simple `Array#map`-like wrapper to work with domain name strings or email
* addresses.
* @private
* @param {String} domain The domain name or email address.
* @param {Function} callback The function that gets called for every
* character.
* @returns {Array} A new string of characters returned by the callback
* function.
*/
function mapDomain(string, fn) {
var parts = string.split('@');
var result = '';
if (parts.length > 1) {
// In email addresses, only the domain name should be punycoded. Leave
// the local part (i.e. everything up to `@`) intact.
result = parts[0] + '@';
string = parts[1];
}
// Avoid `split(regex)` for IE8 compatibility. See #17.
string = string.replace(regexSeparators, '\x2E');
var labels = string.split('.');
var encoded = map(labels, fn).join('.');
return result + encoded;
}
/**
* Creates an array containing the numeric code points of each Unicode
* character in the string. While JavaScript uses UCS-2 internally,
* this function will convert a pair of surrogate halves (each of which
* UCS-2 exposes as separate characters) into a single code point,
* matching UTF-16.
* @see `punycode.ucs2.encode`
* @see <https://mathiasbynens.be/notes/javascript-encoding>
* @memberOf punycode.ucs2
* @name decode
* @param {String} string The Unicode input string (UCS-2).
* @returns {Array} The new array of code points.
*/
function ucs2decode(string) {
var output = [];
var counter = 0;
var length = string.length;
while (counter < length) {
var value = string.charCodeAt(counter++);
if (value >= 0xD800 && value <= 0xDBFF && counter < length) {
// It's a high surrogate, and there is a next character.
var extra = string.charCodeAt(counter++);
if ((extra & 0xFC00) == 0xDC00) { // Low surrogate.
output.push(((value & 0x3FF) << 10) + (extra & 0x3FF) + 0x10000);
} else {
// It's an unmatched surrogate; only append this code unit, in case the
// next code unit is the high surrogate of a surrogate pair.
output.push(value);
counter--;
}
} else {
output.push(value);
}
}
return output;
}
/**
* Creates a string based on an array of numeric code points.
* @see `punycode.ucs2.decode`
* @memberOf punycode.ucs2
* @name encode
* @param {Array} codePoints The array of numeric code points.
* @returns {String} The new Unicode string (UCS-2).
*/
//const ucs2encode = array => String.fromCodePoint(...array);
var ucs2encode = array => String.fromCodePoint.prototype.apply(null, array);
/**
* Converts a basic code point into a digit/integer.
* @see `digitToBasic()`
* @private
* @param {Number} codePoint The basic numeric code point value.
* @returns {Number} The numeric value of a basic code point (for use in
* representing integers) in the range `0` to `base - 1`, or `base` if
* the code point does not represent a value.
*/
var basicToDigit = function(codePoint) {
if (codePoint - 0x30 < 0x0A) {
return codePoint - 0x16;
}
if (codePoint - 0x41 < 0x1A) {
return codePoint - 0x41;
}
if (codePoint - 0x61 < 0x1A) {
return codePoint - 0x61;
}
return base;
};
/**
* Converts a digit/integer into a basic code point.
* @see `basicToDigit()`
* @private
* @param {Number} digit The numeric value of a basic code point.
* @returns {Number} The basic code point whose value (when used for
* representing integers) is `digit`, which needs to be in the range
* `0` to `base - 1`. If `flag` is non-zero, the uppercase form is
* used; else, the lowercase form is used. The behavior is undefined
* if `flag` is non-zero and `digit` has no uppercase form.
*/
var digitToBasic = function(digit, flag) {
// 0..25 map to ASCII a..z or A..Z
// 26..35 map to ASCII 0..9
return digit + 22 + 75 * (digit < 26) - ((flag != 0) << 5);
};
/**
* Bias adaptation function as per section 3.4 of RFC 3492.
* https://tools.ietf.org/html/rfc3492#section-3.4
* @private
*/
var adapt = function(delta, numPoints, firstTime) {
var k = 0;
delta = firstTime ? floor(delta / damp) : delta >> 1;
delta += floor(delta / numPoints);
for (/* no initialization */; delta > baseMinusTMin * tMax >> 1; k += base) {
delta = floor(delta / baseMinusTMin);
}
return floor(k + (baseMinusTMin + 1) * delta / (delta + skew));
};
/**
* Converts a Punycode string of ASCII-only symbols to a string of Unicode
* symbols.
* @memberOf punycode
* @param {String} input The Punycode string of ASCII-only symbols.
* @returns {String} The resulting string of Unicode symbols.
*/
var decode = function(input) {
// Don't use UCS-2.
var output = [];
var inputLength = input.length;
var i = 0;
var n = initialN;
var bias = initialBias;
// Handle the basic code points: let `basic` be the number of input code
// points before the last delimiter, or `0` if there is none, then copy
// the first basic code points to the output.
var basic = input.lastIndexOf(delimiter);
if (basic < 0) {
basic = 0;
}
for (var j = 0; j < basic; ++j) {
// if it's not a basic code point
if (input.charCodeAt(j) >= 0x80) {
error('not-basic');
}
output.push(input.charCodeAt(j));
}
// Main decoding loop: start just after the last delimiter if any basic code
// points were copied; start at the beginning otherwise.
for (var index = basic > 0 ? basic + 1 : 0; index < inputLength; /* no final expression */) {
// `index` is the index of the next character to be consumed.
// Decode a generalized variable-length integer into `delta`,
// which gets added to `i`. The overflow checking is easier
// if we increase `i` as we go, then subtract off its starting
// value at the end to obtain `delta`.
var oldi = i;
for (var w = 1, k = base; /* no condition */; k += base) {
if (index >= inputLength) {
error('invalid-input');
}
var digit = basicToDigit(input.charCodeAt(index++));
if (digit >= base || digit > floor((maxInt - i) / w)) {
error('overflow');
}
i += digit * w;
var t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
if (digit < t) {
break;
}
var baseMinusT = base - t;
if (w > floor(maxInt / baseMinusT)) {
error('overflow');
}
w *= baseMinusT;
}
var out = output.length + 1;
bias = adapt(i - oldi, out, oldi == 0);
// `i` was supposed to wrap around from `out` to `0`,
// incrementing `n` each time, so we'll fix that now:
if (floor(i / out) > maxInt - n) {
error('overflow');
}
n += floor(i / out);
i %= out;
// Insert `n` at position `i` of the output.
output.splice(i++, 0, n);
}
//return String.fromCodePoint(...output);
return String.fromCodePoint.prototype.apply(null, output);
};
/**
* Converts a string of Unicode symbols (e.g. a domain name label) to a
* Punycode string of ASCII-only symbols.
* @memberOf punycode
* @param {String} input The string of Unicode symbols.
* @returns {String} The resulting Punycode string of ASCII-only symbols.
*/
var encode = function(input) {
var output = [];
// Convert the input in UCS-2 to an array of Unicode code points.
input = ucs2decode(input);
// Cache the length.
var inputLength = input.length;
// Initialize the state.
var n = initialN;
var delta = 0;
var bias = initialBias;
// Handle the basic code points.
for (var currentValue of input) {
if (currentValue < 0x80) {
output.push(stringFromCharCode(currentValue));
}
}
var basicLength = output.length;
var handledCPCount = basicLength;
// `handledCPCount` is the number of code points that have been handled;
// `basicLength` is the number of basic code points.
// Finish the basic string with a delimiter unless it's empty.
if (basicLength) {
output.push(delimiter);
}
// Main encoding loop:
while (handledCPCount < inputLength) {
// All non-basic code points < n have been handled already. Find the next
// larger one:
var m = maxInt;
for (var currentValue of input) {
if (currentValue >= n && currentValue < m) {
m = currentValue;
}
}
// Increase `delta` enough to advance the decoder's <n,i> state to <m,0>,
// but guard against overflow.
var handledCPCountPlusOne = handledCPCount + 1;
if (m - n > floor((maxInt - delta) / handledCPCountPlusOne)) {
error('overflow');
}
delta += (m - n) * handledCPCountPlusOne;
n = m;
for (var currentValue of input) {
if (currentValue < n && ++delta > maxInt) {
error('overflow');
}
if (currentValue == n) {
// Represent delta as a generalized variable-length integer.
var q = delta;
for (var k = base; /* no condition */; k += base) {
var t = k <= bias ? tMin : (k >= bias + tMax ? tMax : k - bias);
if (q < t) {
break;
}
var qMinusT = q - t;
var baseMinusT = base - t;
output.push(
stringFromCharCode(digitToBasic(t + qMinusT % baseMinusT, 0))
);
q = floor(qMinusT / baseMinusT);
}
output.push(stringFromCharCode(digitToBasic(q, 0)));
bias = adapt(delta, handledCPCountPlusOne, handledCPCount == basicLength);
delta = 0;
++handledCPCount;
}
}
++delta;
++n;
}
return output.join('');
};
/**
* Converts a Punycode string representing a domain name or an email address
* to Unicode. Only the Punycoded parts of the input will be converted, i.e.
* it doesn't matter if you call it on a string that has already been
* converted to Unicode.
* @memberOf punycode
* @param {String} input The Punycoded domain name or email address to
* convert to Unicode.
* @returns {String} The Unicode representation of the given Punycode
* string.
*/
var toUnicode = function(input) {
return mapDomain(input, function(string) {
return regexPunycode.test(string)
? decode(string.slice(4).toLowerCase())
: string;
});
};
/**
* Converts a Unicode string representing a domain name or an email address to
* Punycode. Only the non-ASCII parts of the domain name will be converted,
* i.e. it doesn't matter if you call it with a domain that's already in
* ASCII.
* @memberOf punycode
* @param {String} input The domain name or email address to convert, as a
* Unicode string.
* @returns {String} The Punycode representation of the given domain name or
* email address.
*/
var toASCII = function(input) {
return mapDomain(input, function(string) {
return regexNonASCII.test(string)
? 'xn--' + encode(string)
: string;
});
};
/*--------------------------------------------------------------------------*/
/** Define the public API */
return {
/**
* A string representing the current Punycode.js version number.
* @memberOf punycode
* @type String
*/
version: '2.1.0',
/**
* An object of methods to convert from JavaScript's internal character
* representation (UCS-2) to Unicode code points, and back.
* @see <https://mathiasbynens.be/notes/javascript-encoding>
* @memberOf punycode
* @type Object
*/
ucs2: {
'decode': ucs2decode,
'encode': ucs2encode
},
decode: decode,
encode: encode,
toASCII: toASCII,
toUnicode: toUnicode
}
}();
//module.exports = sp_punycode;

View File

@ -0,0 +1 @@
'use strict';var sp_punycode=function(){function a(K){throw new RangeError(y[K])}function b(K,L){for(var M=[],N=K.length;N--;)M[N]=L(K[N]);return M}function c(K,L){var M=K.split('@'),N='';1<M.length&&(N=M[0]+'@',K=M[1]),K=K.replace(x,'.');var O=K.split('.'),P=b(O,L).join('.');return N+P}function d(K){for(var O,L=[],M=0,N=K.length;M<N;)if(O=K.charCodeAt(M++),55296<=O&&56319>=O&&M<N){var P=K.charCodeAt(M++);56320==(64512&P)?L.push(((1023&O)<<10)+(1023&P)+65536):(L.push(O),M--)}else L.push(O);return L}var e=2147483647,f=36,g=1,h=26,p=72,r=128,s='-',u=/^xn--/,v=/[^\0-\x7E]/,x=/[\x2E\u3002\uFF0E\uFF61]/g,y={overflow:'Overflow: input needs wider integers to process','not-basic':'Illegal input >= 0x80 (not a basic code point)','invalid-input':'Invalid input'},z=f-g,A=Math.floor,B=String.fromCharCode,D=function(K){return 10>K-48?K-22:26>K-65?K-65:26>K-97?K-97:f},E=function(K,L){return K+22+75*(26>K)-((0!=L)<<5)},F=function(K,L,M){var N=0;for(K=M?A(K/700):K>>1,K+=A(K/L);K>z*h>>1;N+=f)K=A(K/z);return A(N+(z+1)*K/(K+38))},G=function(K){var L=[],M=K.length,N=0,O=r,P=p,Q=K.lastIndexOf(s);0>Q&&(Q=0);for(var R=0;R<Q;++R)128<=K.charCodeAt(R)&&a('not-basic'),L.push(K.charCodeAt(R));for(var T,S=0<Q?Q+1:0;S<M;){T=N;for(var U=1,V=f;;V+=f){S>=M&&a('invalid-input');var W=D(K.charCodeAt(S++));(W>=f||W>A((e-N)/U))&&a('overflow'),N+=W*U;var X=V<=P?g:V>=P+h?h:V-P;if(W<X)break;var Y=f-X;U>A(e/Y)&&a('overflow'),U*=Y}var Z=L.length+1;P=F(N-T,Z,0==T),A(N/Z)>e-O&&a('overflow'),O+=A(N/Z),N%=Z,L.splice(N++,0,O)}return String.fromCodePoint.prototype.apply(null,L)};var H=function(K){var L=[];K=d(K);var M=K.length,N=r,O=0,P=p;for(var Q of K)128>Q&&L.push(B(Q));var R=L.length,S=R;for(R&&L.push(s);S<M;){var T=e;for(var Q of K)Q>=N&&Q<T&&(T=Q);var U=S+1;T-N>A((e-O)/U)&&a('overflow'),O+=(T-N)*U,N=T;for(var Q of K)if(Q<N&&++O>e&&a('overflow'),Q==N){for(var X,V=O,W=f;;W+=f){if(X=W<=P?g:W>=P+h?h:W-P,V<X)break;var Y=V-X,Z=f-X;L.push(B(E(X+Y%Z,0))),V=A(Y/Z)}L.push(B(E(V,0))),P=F(O,U,S==R),O=0,++S}++O,++N}return L.join('')};return{version:'2.1.0',ucs2:{decode:d,encode:K=>String.fromCodePoint.prototype.apply(null,K)},decode:G,encode:H,toASCII:function(K){return c(K,function(L){return v.test(L)?'xn--'+H(L):L})},toUnicode:function(K){return c(K,function(L){return u.test(L)?G(L.slice(4).toLowerCase()):L})}}}();

View File

@ -0,0 +1,179 @@
'use strict';
class ShortPixelScreenBase
{
isCustom = true;
isMedia = true;
processor;
strings = [];
constructor(MainScreen, processor)
{
this.processor = processor;
this.strings = spio_screenStrings;
}
// Function for subclasses to add more init. Seperated because of screens that need to call Process functions when starting.
Init()
{
}
// var message = {status: false, http_status: response.status, http_text: text, status_text: response.statusText };
HandleError(data)
{
if (this.processor.debugIsActive == 'false')
return; // stay silent when debug is not active.
var text = String(data.http_text);
var title = this.strings.fatalError;
var notice = this.GetErrorNotice(title, text);
var el = this.GetErrorPosition();
if (el === null)
{
console.error('Cannot display error - no position found!');
return;
}
el.prepend(notice);
}
// No actions at the base.
HandleItemError(result)
{
}
HandleErrorStop()
{
if (this.processor.debugIsActive == 'false')
return; // stay silent when debug is not active.
var title = this.strings.fatalErrorStop;
var text = this.strings.fatalErrorStopText;
var notice = this.GetErrorNotice(title, text);
var el = this.GetErrorPosition();
if (el === null)
{
console.error('Cannot display error - no position found!');
return;
}
el.prepend(notice);
}
GetErrorNotice(title, text)
{
var notice = document.createElement('div');
var button = document.createElement('button'); // '<button type="button" class="notice-dismiss"></button>';
button.classList.add('notice-dismiss');
button.type = 'button';
button.addEventListener('click', this.EventCloseErrorNotice);
notice.classList.add('notice', 'notice-error', 'is-dismissible');
notice.innerHTML += '<p><strong>' + title + '</strong></p>';
notice.innerHTML += '<div class="error-details">' + text + '</div>';
notice.append(button);
return notice;
}
EventCloseErrorNotice(event)
{
event.target.parentElement.remove();
}
// Search for where to insert the notice before ( ala WP system )
GetErrorPosition()
{
var el = document.querySelector('.is-shortpixel-settings-page');
if (el !== null) // we are on settings page .
{
return el;
}
var el = document.querySelector('.wrap');
if (el !== null)
return el;
var el = document.querySelector('#wpbody-content');
if (el !== null)
return el;
return null;
}
HandleImage(result, type)
{
return true;
}
UpdateStats()
{
}
RenderItemView(e)
{
}
// @todo Find a better home for this. Global screen class?
ParseNumber(str)
{
str = str.replace(',','', str).replace('.','',str);
return parseInt(str);
}
// ** FADE OUT FUNCTION **
FadeOut(el) {
el.style.opacity = 0;
el.style.display = 'none';
/*
el.style.opacity = 1;
(function fade() {
if ((el.style.opacity -= .1) < 0) {
el.style.display = "none";
el.style.opacity = 0;
} else {
requestAnimationFrame(fade);
}
})(); */
};
// ** FADE IN FUNCTION **
FadeIn(el, display) {
el.style.opacity = 1;
el.style.display = "block";
/*
(function fade() {
var val = parseFloat(el.style.opacity);
if (!((val += .1) > 1)) {
el.style.opacity = val;
requestAnimationFrame(fade);
}
})(); */
};
Show(el)
{
el.style.display = 'block';
}
Hide(el)
{
el.style.display = 'none';
}
} // class

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,544 @@
'use strict';
// MainScreen as an option for delegate functions
class ShortPixelScreen extends ShortPixelScreenItemBase
{
isCustom = true;
isMedia = true;
type = 'custom';
folderTree = null;
currentSelectedPath = null;
stopSignal = false;
Init()
{
super.Init();
this.InitFolderSelector();
this.InitScanButtons();
this.InitFileScreenAction();
}
RenderItemView(e)
{
var data = e.detail;
if (data.custom)
{
var id = data.custom.id;
var element = document.getElementById('sp-msg-' + id);
element.outerHTML = data.custom.itemView;
var isOptimizable = data.custom.is_optimizable;
var isRestorable = data.custom.is_restorable;
var inputSelect = document.querySelector('.item-' + id + ' input[name="select[]"]');
if (null === inputSelect)
{
console.warn('Checkbox not found ' + id);
}
inputSelect.classList.remove('is-optimizable', 'is-restorable');
if (true === isOptimizable)
inputSelect.classList.add('is-optimizable');
if (true === isRestorable)
inputSelect.classList.add('is-restorable');
}
return false;
}
// Check if the processor needs to start when items are being added / folders refreshed
CheckProcessorStart()
{
// If automedia is active, see if something is to process.
if (this.processor.IsAutoMediaActive())
{
this.processor.SetInterval(-1);
this.processor.RunProcess();
}
}
RefreshFolder(id)
{
var data = {};
data.id = id;
data.type = 'folder';
data.screen_action = 'refreshFolder';
data.callback = 'shortpixel.folder.HandleFolderResult';
window.addEventListener('shortpixel.folder.HandleFolderResult', this.HandleFolderResult.bind(this), {'once':true});
// AjaxRequest should return result, which will go through Handleresponse, then LoaditemView.
this.processor.AjaxRequest(data);
}
StopMonitoringFolder(id)
{
if (confirm('Are you sure you want to stop optimizing this folder? '))
{
var data = {};
data.id = id;
data.type = 'folder';
data.screen_action = 'removeCustomFolder';
data.callback = 'shortpixel.folder.HandleFolderResult';
window.addEventListener('shortpixel.folder.HandleFolderResult', this.HandleFolderResult.bind(this), {'once':true});
this.processor.AjaxRequest(data);
}
}
HandleFolderResult(e)
{
var data = e.detail;
if (data.folder)
{
var folder_id = data.folder.id;
if (data.folder.message)
{
var el = document.querySelector('.shortpixel-other-media .item.item-' + folder_id + ' .status');
if (null !== el)
{
el.innerHTML = data.folder.message;
}
else {
console.error('Status Element not found for ' + folder_id);
}
}
if (data.folder.fileCount)
{
var el = document.querySelector('.shortpixel-other-media .item.item-' + folder_id + ' .files-number');
if (null !== el)
{
el.innerText = data.folder.fileCount;
}
else {
console.error('FileCount Element not found for ' + folder_id);
}
}
if (data.folder.action == 'remove')
{
if (true == data.folder.is_done)
{
var el = document.querySelector('.shortpixel-other-media .item.item-' + folder_id);
if ( null !== el)
{
el.remove();
}
else {
console.error('Row Element not found for ' + folder_id);
}
}
}
}
this.CheckProcessorStart();
}
InitScanButtons()
{
var scanButtons = document.querySelectorAll('.scan-button');
var self = this;
if (null !== scanButtons)
{
scanButtons.forEach(function (scanButton) {
scanButton.addEventListener('click', self.StartFolderScanEvent.bind(self));
});
}
var stopButton = document.querySelector('.scan-actions .stop-button');
if (null !== stopButton)
{
stopButton.addEventListener('click', this.StopScanEvent.bind(this));
}
}
InitFolderSelector()
{
var openModalButton = document.querySelector('.open-selectfolder-modal');
if (null !== openModalButton)
{
openModalButton.addEventListener('click', this.OpenFolderModal.bind(this));
}
var closeModalButtons = document.querySelectorAll('.shortpixel-modal input.select-folder-cancel, .sp-folder-picker-shade');
var self = this;
closeModalButtons.forEach(function (button, index)
{
button.addEventListener('click', self.CloseFolderModal.bind(self));
});
var addFolderButton = document.querySelector('.modal-folder-picker .select-folder')
if (null !== addFolderButton)
{
addFolderButton.addEventListener('click', this.AddNewFolderEvent.bind(this));
}
}
InitFileScreenAction()
{
var selectAll = document.querySelector('input[name="select-all"]');
if (null !== selectAll)
selectAll.addEventListener('change', this.SelectAllItemsEvent.bind(this));
var bulkAction = document.querySelector('button[name="doBulkAction"]');
if (null !== bulkAction)
bulkAction.addEventListener('click', this.BulkActionEvent.bind(this));
}
SelectAllItemsEvent(event)
{
var parent = event.target;
var inputs = document.querySelectorAll('input[name="select[]"]');
var toggle = (true === parent.checked) ? true : false;
for(var i = 0; i < inputs.length; i++)
{
inputs[i].checked = toggle;
}
}
BulkActionEvent(event)
{
event.preventDefault();
var target = event.target;
var items = document.querySelectorAll('input[name="select[]"]:checked');
var selectBox = document.querySelector('select[name="bulk-actions"]');
var selectedAction = selectBox.options[selectBox.selectedIndex];
selectBox.selectedIndex = 0; // Return to default
var action = selectedAction.value;
for (var i = 0; i < items.length; i++)
{
var item = items[i];
if (false == item.checked) // failsafe
{
continue;
}
var item_id = item.value;
if ('optimize' === action)
{
if (item.classList.contains('is-optimizable'))
{
this.Optimize(item_id);
}
}
else if ('restore' === action)
{
if (item.classList.contains('is-restorable'))
{
this.RestoreItem(item_id);
}
}
else if ('mark-completed' === action)
{
if (item.classList.contains('is-optimizable'))
{
this.MarkCompleted(item_id);
}
}
else {
}
item.checked = false;
}
var selectAll = document.querySelector('input[name="select-all"]');
selectAll.checked = false;
}
OpenFolderModal()
{
var shade = document.querySelector(".sp-folder-picker-shade");
// this.FadeIn(shade, 500);
this.Show(shade);
var picker = document.querySelector(".shortpixel-modal.modal-folder-picker");
picker.classList.remove('shortpixel-hide');
picker.style.display = 'block';
var picker = document.querySelector(".sp-folder-picker");
if (null === this.folderTree)
{
this.folderTree = new ShortPixelFolderTree(picker, this.processor);
picker.addEventListener('shortpixel-folder.selected', this.HandleFolderSelectedEvent.bind(this));
}
this.Show(picker);
}
HandleFolderSelectedEvent(event)
{
var data = event.detail;
var relpath = data.relpath;
var selectedField = document.querySelector('.sp-folder-picker-selected');
selectedField.textContent = relpath;
this.currentSelectedPath = relpath;
if (null !== this.currentSelectedPath)
{
var addFolderButton = document.querySelector('.modal-folder-picker .select-folder');
if (null !== addFolderButton)
{
addFolderButton.disabled = false;
}
}
}
CloseFolderModal()
{
var shade = document.querySelector(".sp-folder-picker-shade");
this.Hide(shade);
// @todo FadeOut function here
var picker = document.querySelector('.shortpixel-modal.modal-folder-picker');
this.Hide(picker);
}
AddNewFolderEvent(event)
{
var data = {};
data.relpath = this.currentSelectedPath;
data.type = 'folder';
data.screen_action = 'addCustomFolder';
data.callback = 'shortpixel.folder.AddNewDirectory';
// @todo this message logic should prob. become part of the folder selector js proper.
var messageEl = document.querySelector('.modal-folder-picker .folder-message');
if (null !== messageEl)
{
if ( false == messageEl.classList.contains('hidden'))
{
messageEl.classList.add('hidden');
}
}
window.addEventListener('shortpixel.folder.AddNewDirectory', this.UpdateFolderViewEvent.bind(this), {'once':true});
this.processor.AjaxRequest(data);
}
UpdateFolderViewEvent(event)
{
var data = event.detail;
// not for us.
if (false === data.folder)
return false;
if (data.folder.result && data.folder.result.itemView)
{
var element = document.querySelector('.list-overview .item');
var elementHeading = document.querySelector('.list-overview .heading');
if (null !== element)
{
element.insertAdjacentHTML('beforebegin', data.folder.result.itemView);
}
else if (null !== elementHeading) // In case list is empty.
{
elementHeading.insertAdjacentHTML('afterend', data.folder.result.itemView);
var noitems = document.querySelector('.list-overview .no-items');
if (null !== noitems)
noitems.remove();
}
this.CloseFolderModal();
this.CheckProcessorStart();
}
else if (data.folder.is_error == true && data.folder.message)
{
var messageEl = document.querySelector('.modal-folder-picker .folder-message');
if (null !== messageEl)
{
messageEl.textContent = data.folder.message;
messageEl.classList.remove('hidden');
}
}
}
StartFolderScanEvent(event)
{
var element = event.target;
this.stopSignal = false;
this.ToggleScanInterface(true);
var force = false;
if ('mode' in element.dataset)
{
var force = true;
}
var reportElement = document.querySelector('.scan-area .result-table');
while(reportElement.firstChild)
{
reportElement.firstChild.remove();
}
var args = [];
args.force = force;
if (true === force)
{
var data = {};
data.type = 'folder';
data.screen_action = 'resetScanFolderChecked';
data.callback = 'shortpixel.folder.ScanFolder';
window.addEventListener('shortpixel.folder.ScanFolder', this.ScanFolder.bind(this, args), {'once':true});
this.processor.AjaxRequest(data);
}
else {
this.ScanFolder(args);
}
}
ScanFolder(args)
{
if (true === this.stopSignal)
{
return false;
}
var data = {};
data.type = 'folder';
data.screen_action = 'scanNextFolder';
data.callback = 'shortpixel.folder.ScannedDirectoryEvent';
if (true === args.force)
{
data.force = args.force;
}
window.addEventListener('shortpixel.folder.ScannedDirectoryEvent', this.ScannedDirectoryEvent.bind(this, args), {'once':true});
this.processor.AjaxRequest(data);
}
ScannedDirectoryEvent(args, event)
{
var data = event.detail;
data = data.folder;
var reportElement = document.querySelector('.scan-area .result-table');
if ( null === reportElement)
{
console.error('Something wrong with reporting element');
return false;
}
if (data.is_done === true)
{
// @todo Probably emit some done status here
var div = document.createElement('div');
div.classList.add('message');
div.textContent = data.result.message;
this.ToggleScanInterface(false);
reportElement.appendChild(div);
}
else if (data.result)
{
var div = document.createElement('div');
var span_path = document.createElement('span');
var span_filecount = document.createElement('span');
var span_message = document.createElement('span');
span_path.textContent = data.result.path;
span_filecount.textContent = data.result.new_count;
span_message.textContent = data.result.message;
div.appendChild(span_path);
div.appendChild(span_filecount);
div.appendChild(span_message);
reportElement.appendChild(div);
var self = this;
setTimeout( function () { self.ScanFolder(args) }, 200);
}
}
StopScanEvent(event)
{
this.stopSignal = true;
var reportElement = document.querySelector('.scan-area .result-table');
if ( null === reportElement)
{
console.error('Something wrong with reporting element');
return false;
}
var div = document.createElement('div');
div.classList.add('message');
div.textContent = this.strings.stopActionMessage;
reportElement.appendChild(div);
this.ToggleScanInterface(false);
}
ToggleScanInterface(show)
{
if (typeof show === 'undefined')
{
var show = true;
}
var divs = document.querySelectorAll('.scan-actions > div');
divs.forEach(function(div){
if (div.classList.contains('action-scan') && true === show)
{
div.classList.add('not-visible');
}
else if (div.classList.contains('action-scan') && false === show) {
div.classList.remove('not-visible');
}
else if ( div.classList.contains('action-stop') && true === show)
{
div.classList.remove('not-visible');
}
else {
div.classList.add('not-visible');
}
});
var output = document.querySelector('.scan-area .output');
if (null !== output && true === show)
{
output.classList.remove('not-visible');
}
}
} // class

View File

@ -0,0 +1,278 @@
'use strict';
class ShortPixelScreenItemBase extends ShortPixelScreenBase
{
type; // media / custom
currentMessage = '';
constructor(MainScreen, processor)
{
super(MainScreen, processor);
}
Init()
{
super.Init();
window.addEventListener('shortpixel.' + this.type + '.resumeprocessing', this.processor.ResumeProcess.bind(this.processor));
window.addEventListener('shortpixel.RenderItemView', this.RenderItemView.bind(this) );
}
HandleImage(resultItem, type)
{
if (type != this.type ) // We don't eat that here.
return false;
if (typeof resultItem.result !== 'undefined')
{
// This is final, not more messing with this. In results (multiple) defined one level higher than result object, if single, it's in result.
var item_id = typeof resultItem.item_id !== 'undefined' ? resultItem.item_id : resultItem.result.item_id;
var message = resultItem.result.message;
var element = document.getElementById('sp-msg-' + item_id); // empty result box while getting
if (typeof message !== 'undefined')
{
var isError = false;
if (resultItem.result.is_error == true)
isError = true;
this.UpdateMessage(item_id, message, isError);
}
if (element !== null)
{
element.innerHTML = '';
// var event = new CustomEvent('shortpixel.loadItemView', {detail: {'type' : type, 'id': result.id }}); // send for new item view.
var fileStatus = this.processor.fStatus[resultItem.fileStatus];
if (fileStatus == 'FILE_SUCCESS' || fileStatus == 'FILE_RESTORED' || resultItem.result.is_done == true)
{
this.processor.LoadItemView({id: item_id, type: type});
}
else if (fileStatus == 'FILE_PENDING')
{
element.style.display = 'none';
}
//window.dispatchEvent(event);
}
}
else
{
console.error('handleImage without Result', resultItem);
}
return false;
}
UpdateMessage(id, message, isError)
{
var element = document.getElementById('sp-message-' + id);
if (typeof isError === 'undefined')
isError = false;
this.currentMessage = message;
if (element == null)
{
var parent = document.getElementById('sp-msg-' + id);
if (parent !== null)
{
var element = document.createElement('div');
element.classList.add('message');
element.setAttribute('id', 'sp-message-' + id);
parent.parentNode.insertBefore(element, parent.nextSibling);
}
}
if (element !== null)
{
if (element.classList.contains('error'))
element.classList.remove('error');
element.innerHTML = message;
if (isError)
element.classList.add('error');
}
else
{
this.processor.Debug('Update Message Column not found - ' + id);
}
}
// Show a message that an action has started.
SetMessageProcessing(id)
{
var message = this.strings.startAction;
var loading = document.createElement('img');
loading.width = 20;
loading.height = 20;
loading.src = this.processor.GetPluginUrl() + '/res/img/bulk/loading-hourglass.svg';
message += loading.outerHTML;
this.UpdateMessage(id, message);
}
UpdateStats(stats, type)
{
// for now, since we process both, only update the totals in tooltip.
if ( type !== 'total')
return;
var waiting = stats.in_queue + stats.in_process;
this.processor.tooltip.RefreshStats(waiting);
}
GeneralResponses(responses)
{
var self = this;
if (responses.length == 0) // no responses.
return;
var shownId = []; // prevent the same ID from creating multiple tooltips. There will be punishment for this.
responses.forEach(function (element, index)
{
if (element.id)
{
if (shownId.indexOf(element.id) > -1)
{
return; // skip
}
else
{
shownId.push(element.id);
}
}
var message = element.message;
if (element.filename)
message += ' - ' + element.filename;
self.processor.tooltip.AddNotice(message);
if (self.processor.rStatus[element.code] == 'RESPONSE_ERROR')
{
if (element.id)
{
var message = self.currentMessage;
self.UpdateMessage(element.id, message + '<br>' + element.message);
self.currentMessage = message; // don't overwrite with this, to prevent echo.
}
else
{
var errorBox = document.getElementById('shortpixel-errorbox');
if (errorBox)
{
var error = document.createElement('div');
error.classList.add('error');
error.innerHTML = element.message;
errorBox.append(error);
}
}
}
});
}
// HandleItemError is handling from results / result, not ResponseController. Check if it has negative effects it's kinda off now.
HandleItemError(result)
{
if (result.message && result.item_id)
{
this.UpdateMessage(result.item_id, result.message, true);
}
if (typeof result.item_id !== 'undefined')
{
this.processor.LoadItemView({id: result.item_id, type: 'media'});
}
}
RestoreItem(id)
{
var data = {};
data.id = id;
data.type = this.type;
data.screen_action = 'restoreItem';
// AjaxRequest should return result, which will go through Handleresponse, then LoaditemView.
this.SetMessageProcessing(id);
this.processor.AjaxRequest(data);
}
CancelOptimizeItem(id)
{
var data = {};
data.id = id;
data.type = this.type;
data.screen_action = 'cancelOptimize';
// AjaxRequest should return result, which will go through Handleresponse, then LoaditemView.
this.processor.AjaxRequest(data);
}
ReOptimize(id, compression, action)
{
var data = {
id : id ,
compressionType: compression,
type: this.type,
screen_action: 'reOptimizeItem'
};
if (typeof action !== 'undefined')
{
data.actionType = action;
}
if (! this.processor.CheckActive())
data.callback = 'shortpixel.' + this.type + '.resumeprocessing';
this.SetMessageProcessing(id);
this.processor.AjaxRequest(data);
}
Optimize(id, force)
{
var data = {
id: id,
type: this.type,
screen_action: 'optimizeItem'
}
if (typeof force !== 'undefined' && true == force)
{
data.flags = 'force';
}
if (! this.processor.CheckActive())
data.callback = 'shortpixel.' + this.type + '.resumeprocessing';
this.SetMessageProcessing(id);
this.processor.AjaxRequest(data);
}
MarkCompleted(id)
{
var data = {};
data.id = id;
data.type = this.type;
data.screen_action = 'markCompleted';
this.processor.AjaxRequest(data);
}
UnMarkCompleted(id)
{
var data = {};
data.id = id;
data.type = this.type;
data.screen_action = 'unMarkCompleted';
this.processor.AjaxRequest(data);
}
} // class

View File

@ -0,0 +1,256 @@
'use strict';
// MainScreen as an option for delegate functions
class ShortPixelScreen extends ShortPixelScreenItemBase //= function (MainScreen, processor)
{
isCustom = true;
isMedia = true;
type = 'media';
Init()
{
super.Init();
this.ListenGallery();
}
RenderItemView(e)
{
e.preventDefault();
var data = e.detail;
if (data.media)
{
var id = data.media.id;
var element = document.getElementById('sp-msg-' + id);
if (element !== null) // Could be other page / not visible / whatever.
element.outerHTML = data.media.itemView;
else {
console.error('Render element not found');
}
}
else {
console.error('Data not found - RenderItemview on media screen');
}
return false; // callback shouldn't do more, see processor.
}
HandleImage(resultItem, type)
{
var res = super.HandleImage(resultItem, type);
var fileStatus = this.processor.fStatus[resultItem.fileStatus];
// If image editor is active and file is being restored because of this reason ( or otherwise ), remove the warning if this one exists.
if (fileStatus == 'FILE_RESTORED')
{
var warning = document.getElementById('shortpixel-edit-image-warning');
if (warning !== null)
{
warning.remove();
}
}
}
RedoLegacy(id)
{
var data = {
id: id,
type: 'media',
screen_action: 'redoLegacy',
}
data.callback = 'shortpixel.LoadItemView';
window.addEventListener('shortpixel.LoadItemView', function (e) {
var itemData = { id: e.detail.media.id, type: 'media' };
this.processor.timesEmpty = 0; // reset the defer on this.
this.processor.LoadItemView(itemData);
this.UpdateMessage(itemData.id, '');
}.bind(this), {'once': true} );
this.SetMessageProcessing(id);
this.processor.AjaxRequest(data);
}
ListenGallery()
{
var self = this;
if (typeof wp.media === 'undefined')
{
this.ListenEditAttachment(); // Edit Media edit attachment screen
return;
}
// This taken from S3-offload / media.js / Grid media gallery
if (typeof wp.media.view.Attachment.Details.TwoColumn !== 'undefined')
{
var detailsColumn = wp.media.view.Attachment.Details.TwoColumn;
var twoCol = true;
}
else {
var detailsColumn = wp.media.view.Attachment.Details;
var twoCol = false;
}
var extended = detailsColumn.extend ({
render: function()
{
detailsColumn.prototype.render.apply( this );
this.fetchSPIOData(this.model.get( 'id' ));
return this;
},
fetchSPIOData : function (id)
{
var data = {};
data.id = id;
data.type = self.type;
data.action = 'getItemView';
data.callback = 'shortpixel.MediaRenderView';
window.addEventListener('shortpixel.MediaRenderView', this.renderSPIOView.bind(this), {'once':true});
self.processor.LoadItemView(data);
},
renderSPIOView: function(e, timed)
{
if (! e.detail || ! e.detail.media || ! e.detail.media.itemView)
{
return;
}
var $spSpace = this.$el.find('.attachment-info .details');
if ($spSpace.length === 0 && (typeof timed === 'undefined' || timed < 5))
{
// It's possible the render is slow or blocked by other plugins. Added a delay and retry bit later to draw.
if (typeof timed === 'undefined')
{
var timed = 0;
}
else {
timed++;
}
setTimeout(function () { this.renderSPIOView(e, true) }.bind(this), 1000);
}
var html = this.doSPIORow(e.detail.media.itemView);
$spSpace.after(html);
},
doSPIORow : function(dataHtml)
{
var html = '';
html += '<div class="shortpixel-popup-info">';
html += '<label class="name">ShortPixel</label>';
html += dataHtml;
html += '</div>';
return html;
},
editAttachment: function(event)
{
event.preventDefault();
self.AjaxOptimizeWarningFromUnderscore(this.model.get( 'id' ));
detailsColumn.prototype.editAttachment.apply( this, [event]);
}
});
if (true === twoCol)
{
wp.media.view.Attachment.Details.TwoColumn = extended; //wpAttachmentDetailsTwoColumn;
}
else {
wp.media.view.Attachment.Details = extended;
}
}
AjaxOptimizeWarningFromUnderscore(id)
{
var data = {
id: id,
type: 'media',
screen_action: 'getItemEditWarning',
callback: 'ShortPixelMedia.getItemEditWarning'
};
window.addEventListener('ShortPixelMedia.getItemEditWarning', this.CheckOptimizeWarning.bind(this), {'once': true} );
this.processor.AjaxRequest(data);
}
CheckOptimizeWarning(event)
{
var data = event.detail;
var image_post_id = data.id;
var is_restorable = data.is_restorable;
var is_optimized = data.is_optimized;
if ('true' === is_restorable || 'true' === is_optimized)
{
this.ShowOptimizeWarning(image_post_id, is_restorable, is_optimized);
}
}
ShowOptimizeWarning(image_post_id, is_restorable, is_optimized)
{
var div = document.createElement('div');
div.id = 'shortpixel-edit-image-warning';
div.classList.add('shortpixel', 'shortpixel-notice', 'notice-warning');
if ('true' == is_restorable)
{
var restore_link = spio_media.restore_link.replace('#post_id#', image_post_id);
div.innerHTML = '<p>' + spio_media.optimized_text + ' <a href="' + restore_link + '">' + spio_media.restore_link_text + '</a></p>' ;
}
else {
div.innerHTML = '<p>' + spio_media.optimized_text + ' ' + spio_media.restore_link_text_unrestorable + '</p>' ;
}
// only if not existing.
if (document.getElementById('shortpixel-edit-image-warning') == null)
{
var $menu = jQuery('.imgedit-menu');
if ($menu.length > 0)
{
$menu.append(div);
}
else {
jQuery(document).one('image-editor-ui-ready', function() // one!
{
jQuery('.imgedit-menu').append(div);
});
}
}
}
// This should be the edit-attachment screen
ListenEditAttachment()
{
var self = this;
var imageEdit = window.imageEdit;
jQuery(document).on('image-editor-ui-ready', function()
{
var element = document.querySelector('input[name="post_ID"]');
if (null === element)
{
console.error('Could not fetch post id on this screen');
return;
}
var post_id = element.value;
var data = {
id: post_id,
type: 'media',
screen_action: 'getItemEditWarning',
callback: 'ShortPixelMedia.getItemEditWarning'
};
window.addEventListener('ShortPixelMedia.getItemEditWarning', self.CheckOptimizeWarning.bind(self), {'once': true} );
window.ShortPixelProcessor.AjaxRequest(data);
});
}
} // class

View File

@ -0,0 +1,31 @@
'use strict';
// MainScreen as an option for delegate functions
class ShortPixelScreen extends ShortPixelScreenBase
{
Init()
{
super.Init();
this.ListenPLUpload();
}
// Only listening on the nolist ( and more specific -> media addnew) , since the post editor classic/ gutenberg and others have this interface otherwise hidden.
ListenPLUpload() {
// Most screen will not have uploader defined or ready.
if (typeof uploader === 'undefined' || uploader === null)
{
return;
}
var self = this;
uploader.bind('UploadComplete', function (up, file, response)
{
// Give processor a swoop when uploading is done, while respecting set boundaries.
self.processor.RunProcess();
});
}
} // class

View File

@ -0,0 +1,219 @@
'use strict';
// Goals : Javascript plain folder/fileBrowser (filetree)
// : Crawler to scan / refresh folders via ajax / JSON
//
class ShortPixelFolderTree
{
strings = [];
icons = [];
didInit = false;
parentElement;
processor = null;
loadedFolders = [];
constructor(element, processor)
{
//this.processor = processor;
this.parentElement = element;
this.processor = processor;
this.Init();
}
Init()
{
if (true === this.didInit)
return;
this.strings = spio_folderbrowser.strings;
this.icons = spio_folderbrowser.icons;
this.ShowLoading();
this.AjaxLoadFolders('');
this.didInit = true;
}
BuildElement()
{
}
ShowLoading()
{
var loading = document.createElement('div');
var h3 = document.createElement('h3');
loading.classList.add('loading');
h3.textContent = this.strings.loading;
loading.appendChild(h3);
this.parentElement.append(loading);
}
RemoveLoading()
{
var elements = document.querySelectorAll('.sp-folder-picker .loading')
elements.forEach(function(element) {
element.remove();
})
}
LoadFolderEvent(e)
{
var data = e.detail;
var folders = data.folder.folders;
this.RemoveLoading();
if (data.folder.is_error == 'true') // error / emtpy result handlers.
{
}
var self = this;
var parent = this.parentElement;
if (data.folder.relpath !== '')
{
var child = document.querySelector('[data-relpath="' + data.folder.relpath + '"]');
if (child !== null)
{
parent = child;
//child.classList.remove('closed');
//child.classList.add('open');
}
}
var ul = document.createElement('ul');
ul.classList.add('loaded', 'expanded');
ul.dataset.loadpath = data.folder.relpath;
folders.forEach(function(element) {
// console.log(element);
var li = document.createElement('li');
li.dataset.relpath = element.relpath;
li.classList.add('folder','closed');
if ( element.is_active && true === element.is_active)
{
li.classList.add('is_active');
}
var link = document.createElement('a');
var icon = document.createElement('i');
icon.classList.add('icon');
link.appendChild(icon);
link.innerHTML += element.name;
link.addEventListener('click', self.ToggleFolderContentsEvent.bind(self));
li.appendChild(link);
ul.appendChild(li);
});
parent.appendChild(ul); // add the tree.
}
// @Todo : find out how to keep track of previously selected items (and switch them out)
// + Emit a singal (trigger event) to external when something is selected, so UX / hidden field can add them in custom.js
ToggleFolderContentsEvent(e)
{
var anchor = e.target;
var li = e.target.parentElement;
if (li.tagName !== 'LI')
{
var li = li.parentElement;
}
// Is classList is_active ( meaning already selected a custom folder ) do nothing with both selection and subfolders
if (true === li.classList.contains('is_active'))
{
return;
}
// remove all previous selected thingies.
var openElements = this.parentElement.querySelectorAll('.folder.selected');
openElements.forEach(function (element)
{
element.classList.remove('selected');
//element.classList.add('closed');
});
var relpath = li.dataset.relpath;
var childTree = document.querySelector('[data-loadpath="' + relpath + '"]');
if (li.classList.contains('closed'))
{
if (null === childTree)
{
this.AjaxLoadFolders(relpath);
}
else {
childTree.classList.add('expanded');
childTree.classList.remove('collapsed');
}
li.classList.remove('closed');
li.classList.add('open');
// var img = li.querySelector('img');
//img.src = this.icons.folder_open;
}
else if (li.classList.contains('open'))
{
if (null !== childTree)
{
childTree.classList.remove('expanded');
childTree.classList.add('collapsed');
}
li.classList.remove('open')
li.classList.add('closed');
// var img = li.querySelector('img');
// img.src = this.icons.folder_closed;
}
li.classList.add('selected');
var selectEvent = new CustomEvent('shortpixel-folder.selected', { 'detail': {'relpath': relpath}});
this.parentElement.dispatchEvent(selectEvent);
}
GetFolderIcon(name)
{
var svg = document.createElement('img');
svg.src = this.icons.folder_closed;
svg.style.width = '20px';
svg.style.height = '20px';
return svg;
}
AjaxLoadFolders(relpath)
{
var folderbase = this.folderbase;
var data = {
type: 'folder',
screen_action: 'browseFolders',
relPath: relpath,
callback: 'shortpixel.folder.LoadFolders',
};
window.addEventListener('shortpixel.folder.LoadFolders', this.LoadFolderEvent.bind(this), {'once':true});
this.processor.AjaxRequest(data);
}
} // class

View File

@ -0,0 +1,53 @@
'use strict';
(function( $) {
if (typeof wp.media === 'undefined' || typeof wp.media.frame === 'undefined')
{
return;
}
var ShortPixelFilter = wp.media.view.AttachmentFilters.extend
({
id: 'shortpixel-media-filter',
createFilters: function() {
var filters = {};
var optimizedfilter = spio_media.mediafilters.optimized;
for (const [key,value] of Object.entries(optimizedfilter))
{
filters[key] = {
text: value,
props: { 'shortpixel_status': key },
priority: 10,
}
};
this.filters = filters;
}
}); // ShortPixelFilter
var AttachmentsBrowser = wp.media.view.AttachmentsBrowser;
wp.media.view.AttachmentsBrowser = wp.media.view.AttachmentsBrowser.extend({
createToolbar: function() {
// Make sure to load the original toolbar
AttachmentsBrowser.prototype.createToolbar.call( this );
this.toolbar.set(
'ShortPixelFilter',
new ShortPixelFilter({
controller: this.controller,
model: this.collection.props,
priority: -80
})
.render()
);
}
});
})( jQuery);
//}); // jquery - Attachmentfilters

View File

@ -0,0 +1,678 @@
/*** ShortPixel Image Processor ***
* The processor sends via a browser worker tasks in form of Ajax Request to the browser
* Ajax returns from browser are processed and then delegated to the screens
* Every function starts via capitals and camelcased i.e. LoadWorker
* Normal variables are camel-cased.
*
* The remote secret is to prevent several browser clients on different computers but on same site to start processing.
* This is to limit performance usages on sites with a large number of backend users.
*
* -- Screens --
* A Screen is responsible for putting received values to UI
* Required function of screen are : HandleImage HandleError UpdateStats
* Optional functions : QueueStatus, GeneralResponses
*/
'use strict';
window.ShortPixelProcessor =
{
// spp: {},
isActive: false, // Is the processor active in this window - at all - . Transient
defaultInterval: 3000, // @todo customize this from backend var, hook filter. default is onload interval
interval: 3000, // is current interval. When nothing to be done increases
deferInterval: 15000, // how long to wait between interval to check for new items.
screen: null, // UI Object
tooltip: null, // Tooltip object to show in WP admin bar
isBulkPage: false, // Bypass secret check when bulking, because customer explicitly requests it.
localSecret: null, // Local processorkey stored (or empty)
remoteSecret: null, // Remote key indicating who has process right ( or null )
isManualPaused: false, // tooltip pause :: do not set directly, but only through processor functions!
autoMediaLibrary: false,
worker: null, // HTTP worker to send requests ( worker.js )
timer: null, // Timer to determine waiting time between actions
timer_recheckactive: null,
waitingForAction: false, // used if init yields results that should pause the processor.
timesEmpty: 0, // number of times queue came up empty.
nonce: [],
debugIsActive : false, // indicating is SPIO is in debug mode. Don't report certain things if not.
hasStartQuota: false, // if we start without quota, don't notice too much, don't run.
workerErrors: 0, // times worker encoutered an error.
qStatus: { // The Queue returns
1: 'QUEUE_ITEMS',
4: 'QUEUE_WAITING',
10: 'QUEUE_EMPTY',
2: 'PREPARING',
3: 'PREPARING_DONE',
11: 'PREPARING_RECOUNT',
},
fStatus: { // FileStatus of ImageModel
'-1': 'FILE_ERROR',
1: 'FILE_PENDING',
2: 'FILE_DONE',
3: 'FILE_RESTORED',
},
rStatus: { // ResponseController
1: 'RESPONSE_ACTION', // when an action has been performed *not used*
2: 'RESPONSE_SUCCESS', // not sure this one is needed *not used*
10: 'RESPONSE_ERROR',
11: 'RESPONSE_WARNING', // *not used*
12: 'RESPONSE_ERROR_DELAY', // when an error is serious enough to delay things.*not used*
},
aStatusError: { // AjaxController / optimizeController - when an error occured
'-1': 'PROCESSOR_ACTIVE', // active in another window
'-2': 'NONCE_FAILED',
'-3': 'NO_STATUS',
'-4': 'APIKEY_FAILED',
'-5': 'NOQUOTA',
'-10': 'SERVER FAILURE',
'-903': 'TIMEOUT', // SPIO shortQ retry limit reached.
},
Load: function(hasQuota)
{
window.addEventListener('error', this.ScriptError.bind(this));
this.isBulkPage = ShortPixelProcessorData.isBulkPage;
this.localSecret = localStorage.getItem('bulkSecret');
this.remoteSecret = ShortPixelProcessorData.bulkSecret;
this.debugIsActive = ShortPixelProcessorData.debugIsActive;
this.nonce['process'] = ShortPixelProcessorData.nonce_process;
this.nonce['exit'] = ShortPixelProcessorData.nonce_exit;
this.nonce['itemview'] = ShortPixelProcessorData.nonce_itemview;
this.nonce['ajaxRequest'] = ShortPixelProcessorData.nonce_ajaxrequest;
this.autoMediaLibrary = (ShortPixelProcessorData.autoMediaLibrary == 'true') ? true : false;
if (hasQuota == 1)
this.hasStartQuota = true;
if (ShortPixelProcessorData.interval && ShortPixelProcessorData.interval > 100)
this.interval = ShortPixelProcessorData.interval;
if (ShortPixelProcessorData.interval && ShortPixelProcessorData.interval > 100)
this.deferInterval = ShortPixelProcessorData.deferInterval;
console.log('Start Data from Server', ShortPixelProcessorData.startData, this.interval, this.deferInterval);
console.log('remoteSecret ' + this.remoteSecret + ', localsecret: ' + this.localSecret);
this.tooltip = new ShortPixelToolTip({}, this);
if (typeof ShortPixelScreen == 'undefined')
{
console.error('Missing Screen!');
return;
}
else
{
this.screen = new ShortPixelScreen({}, this);
this.screen.Init();
}
// Load the Startup Data (needs screen)
this.tooltip.InitStats();
// Always load worker, also used for UI actions.
this.LoadWorker();
if (this.CheckActive())
{
if (this.hasStartQuota)
this.RunProcess();
}
},
CheckActive: function()
{
if (this.remoteSecret == false || this.remoteSecret == '' || this.isBulkPage) // if remoteSecret is false, we are the first process. Take it.
{
if (this.localSecret && this.localSecret.length > 0)
{
this.remoteSecret = this.localSecret;
}
else
{
this.localSecret = Math.random().toString(36).substring(7);
localStorage.setItem('bulkSecret',this.localSecret);
// tell worker to use correct key.
this.worker.postMessage({'action' : 'updateLocalSecret',
'key': this.localSecret });
}
this.isActive = true;
}
else if (this.remoteSecret === this.localSecret) // There is a secret, we are the processor.
{
this.isActive = true;
}
else
{
console.log('Check Active: Processor not active - ' + this.remoteSecret + ' - ' + this.localSecret);
this.tooltip.ProcessEnd();
this.StopProcess();
if (null === this.timer_recheckactive)
{
var threemin = 180000; // TTL for a processorkey is 2 minutes now, so wait a broad 3 and check again
this.timer_recheckactive = window.setTimeout(this.RecheckProcessor.bind(this), threemin );
console.log('Waiting for recheckActive ', this.timer_recheckactive);
}
}
if (this.isManualPaused)
{
this.isActive = false;
console.debug('Check Active: Paused');
}
if (this.waitingForAction)
{
this.isActive = false;
this.tooltip.ProcessEnd();
console.debug('Check Active : Waiting for action');
}
return this.isActive;
},
RecheckProcessor: function()
{
var data = {
'screen_action': 'recheckActive',
'callback': 'shortpixel.recheckActive',
};
window.addEventListener('shortpixel.recheckActive', this.RecheckedActiveEvent.bind(this), {'once': true});
this.AjaxRequest(data);
},
RecheckedActiveEvent: function(event)
{
var data = event.detail;
// cleanse the timer;
if (this.timer_recheckactive)
{
window.clearTimeout(this.timer_recheckactive);
this.timer_recheckactive = null;
}
if (true === data.status)
{
if (typeof data.processorKey !== 'undefined')
{
this.remoteSecret = data.processorKey;
}
else { // this happens when it's released, but client doens't have a localsecret, the remotesecret is not returned by request. Set to null and go probably should work in all cases.
this.remoteSecret = null;
}
var bool = this.CheckActive();
if (true === bool)
{
this.timesEmpty = 0; // reset the times empty to start fresh.
this.RunProcess();
}
}
else {
// If key was not given, this should retrigger the next event.
this.CheckActive();
}
},
LoadWorker: function()
{
if (window.Worker)
{
var ajaxURL = ShortPixel.AJAX_URL;
var nonce = '';
console.log('Starting Worker');
this.worker = new Worker(ShortPixelProcessorData.workerURL);
var isBulk = false;
if (this.isBulkPage)
isBulk = true;
this.worker.postMessage({'action' : 'setEnv',
'data': {'isBulk' : isBulk, 'isMedia': this.screen.isMedia, 'isCustom': this.screen.isCustom, 'ajaxUrl' : ajaxURL, 'secret' : this.localSecret}
});
/*this.worker.postMessage({'action': 'init', 'data' : [ajaxURL, this.localSecret], 'isBulk' : isBulk}); */
this.worker.onmessage = this.CheckResponse.bind(this);
window.addEventListener('beforeunload', this.ShutDownWorker.bind(this));
}
},
ShutDownWorker: function()
{
if (this.worker === null) // worker already shut / not loaded
return false;
console.log('Shutting down Worker');
this.worker.postMessage({'action' : 'shutdown', 'nonce': this.nonce['exit'] });
this.worker = null;
window.removeEventListener('beforeunload', this.ShutDownWorker.bind(this));
window.removeEventListener('shortpixel.loadItemView', this.LoadItemView.bind(this));
},
Process: function()
{
if (this.worker === null)
{
this.LoadWorker(); // JIT worker loading
}
this.worker.postMessage({action: 'process', 'nonce' : this.nonce['process']});
},
RunProcess: function()
{
if (this.timer)
{
window.clearTimeout(this.timer);
this.timer = null;
}
if (! this.CheckActive())
{
return;
}
if (this.timer_recheckactive)
{
window.clearTimeout(this.timer_recheckactive);
this.timer_recheckactive = null;
}
console.log('Processor: Run Process in ' + this.interval);
this.timer = window.setTimeout(this.Process.bind(this), this.interval);
},
IsAutoMediaActive: function()
{
return this.autoMediaLibrary;
},
PauseProcess: function() // This is a manual intervention.
{
this.isManualPaused = true;
var event = new CustomEvent('shortpixel.processor.paused', { detail : {paused: this.isManualPaused }});
window.dispatchEvent(event);
console.log('Processor: Process Paused');
window.clearTimeout(this.timer);
this.timer = null;
},
StopProcess: function(args)
{
console.log('Stop Processing Signal #' + this.timer);
// @todo this can probably go? Why would StopProcess cancel Manual pauses?
if (this.isManualPaused == true) /// processor ends on status paused.
{
this.isManualPaused = false;
var event = new CustomEvent('shortpixel.processor.paused', { detail : {paused: this.isManualPaused}});
window.dispatchEvent(event);
}
window.clearTimeout(this.timer);
this.timer = null;
if (typeof args == 'object')
{
if (typeof args.defer !== 'undefined' && args.defer)
{
this.timesEmpty++;
console.log('Stop, defer wait :' + (this.deferInterval * this.timesEmpty), this.timesEmpty);
this.SetInterval( (this.deferInterval * this.timesEmpty) ); //set a long interval
this.RunProcess(); // queue a run once
this.SetInterval(-1); // restore interval
}
else if (typeof args.waiting !== 'undefined')
{
console.log('Stop Process: Waiting for action');
this.waitingForAction = args.waiting;
}
}
this.tooltip.ProcessEnd();
},
ResumeProcess: function()
{
this.isManualPaused = false;
this.waitingForAction = false;
localStorage.setItem('tooltipPause','false'); // also remove the cookie so it doesn't keep hanging on page refresh.
var event = new CustomEvent('shortpixel.processor.paused', { detail : {paused: this.isManualPaused}});
window.dispatchEvent(event);
this.Process(); // don't wait the interval to go on resume.
},
SetInterval: function(interval)
{
if (interval == -1)
this.interval = this.defaultInterval;
else
this.interval = interval;
},
CheckResponse: function(message)
{
var data = message.data;
if (data.status == true && data.response) // data status is from shortpixel worker, not the response object
{
var response = data.response;
var handledError = false; // prevent passing to regular queueHandler is some action is taken.
this.workerErrors = 0;
if ( response.callback)
{
console.log('Running callback : ' + response.callback);
var event = new CustomEvent(response.callback, {detail: response, cancelable: true});
var checkPrevent = window.dispatchEvent(event);
if (! checkPrevent) // if event is preventDefaulted, stop checking response
return;
}
if ( response.status == false)
{
// This is error status, or a usual shutdown, i.e. when process is in another browser.
var error = this.aStatusError[response.error];
if (error == 'PROCESSOR_ACTIVE')
{
this.Debug(response.message);
handledError = true;
this.StopProcess();
}
else if (error == 'NONCE_FAILED')
{
this.Debug('Nonce Failed', 'error');
}
else if (error == 'NOQUOTA')
{
if (this.hasStartQuota)
this.tooltip.AddNotice(response.message);
this.screen.HandleError(response);
this.Debug('No Quota - CheckResponse handler');
this.PauseProcess();
handledError = true;
}
else if (error == 'APIKEY_FAILED')
{
this.StopProcess();
handledError = true;
console.error('No API Key set for this site. See settings');
}
else if (response.error < 0) // something happened.
{
this.StopProcess();
handledError = true;
console.error('Some unknown error occured!', response.error, response);
}
if (handledError == true)
return;
} // status false handler.
// Check the screen if we are custom or media ( or bulk ) . Check the responses for each of those.
if (typeof response.custom == 'object' && response.custom !== null)
{
this.HandleResponse(response.custom, 'custom');
}
if (typeof response.media == 'object' && response.media !== null)
{
this.HandleResponse(response.media, 'media');
}
// Total is a response type for combined stats in the bulk.
if (typeof response.total == 'object' && response.total !== null)
{
this.HandleResponse(response.total, 'total');
}
this.HandleQueueStatus(response);
if (response.processorKey)
{
this.remoteSecret = response.processorKey;
}
if (response.responses)
{
if (typeof this.screen.GeneralResponses == 'function')
{
this.screen.GeneralResponses(response.responses);
}
}
}
else // This is a worker error / http / nonce / generail fail
{
this.workerErrors++;
this.timesEmpty = 0; // don't drag it.
if (data.message)
{
this.screen.HandleError(data.message);
this.Debug(data.message, 'error');
}
if (this.workerErrors >= 3)
{
this.screen.HandleErrorStop();
console.log('Shutting down . Num errors: ' + this.workerErrors);
this.StopProcess();
this.ShutDownWorker();
}
else
{
this.StopProcess({ defer: true });
console.log('Stop / defer');
}
}
// Binded to bulk-screen js for checking data.
var event = new CustomEvent('shortpixel.processor.responseHandled', { detail : {paused: this.isManualPaused}});
window.dispatchEvent(event);
},
HandleResponse: function(response, type)
{
// Issue with the tooltip is when doing usual cycle of emptiness, a running icon is annoying to user. Once queries and yielded results, it might be said that the processor 'is running'
if (response.stats)
this.tooltip.DoingProcess();
if (response.has_error == true)
{
this.tooltip.AddNotice(response.message);
}
if (! this.screen)
{
console.error('Missing screen - can\'t report results');
return false;
}
// Perhaps if optimization, the new stats and actions should be generated server side?
// If there are items, give them to the screen for display of optimization, waiting status etc.
var imageHandled = false; // Only post one image per result-set to the ImageHandler (on bulk), to prevent flooding.
// @todo Make sure that .result and .results can be iterated the same.
if (typeof response.results !== 'undefined' && response.results !== null)
{
for (var i = 0; i < response.results.length; i++)
{
var imageItem = response.results[i];
if (imageItem == null || ! imageItem.result)
{
console.error('Expecting ImageItem Object with result ', imageItem);
continue;
}
if (imageItem.result.is_error)
{
this.HandleItemError(imageItem, type);
}
if (! imageHandled)
{
imageHandled = this.screen.HandleImage(imageItem, type);
}
}
}
if (typeof response.result !== 'undefined' && response.result !== null)
{
if (response.result.is_error)
{
this.HandleItemError(response.result, type);
}
else if (! imageHandled)
{
imageHandled = this.screen.HandleImage(response, type); // whole response here is single item. (final!)
}
}
// Queue status?
if (response.stats)
{
this.tooltip.RefreshStats(response.stats, type);
this.screen.UpdateStats(response.stats, type);
}
},
/// If both are reported back, both did tick, so both must be considered.
HandleQueueStatus: function(data)
{
var mediaStatus = 100;
var customStatus = 100;
// If not statuses were returned, we are probably loading something via ajax, don't increase the defer / checks on the processing
var anyQueueStatus = false;
if (typeof data.media !== 'undefined' && typeof data.media.qstatus !== 'undefined' )
{
anyQueueStatus = true;
mediaStatus = data.media.qstatus;
}
if (typeof data.custom !== 'undefined' && typeof data.custom.qstatus !== 'undefined')
{
anyQueueStatus = true;
customStatus = data.custom.qstatus;
}
if (false === anyQueueStatus)
{
return false; // no further checks.
}
// The lowest queue status (for now) equals earlier in process. Don't halt until both are done.
if (mediaStatus <= customStatus)
var combinedStatus = mediaStatus;
else
var combinedStatus = customStatus;
if (combinedStatus == 100)
{
this.StopProcess({ defer: true });
return false; // no status in this request.
}
if (typeof combinedStatus !== 'undefined')
{
var qstatus = this.qStatus[combinedStatus];
if (qstatus == 'QUEUE_ITEMS' || qstatus == "PREPARING")
{
console.log('Qstatus Preparing or items returns');
this.timesEmpty = 0;
this.RunProcess();
}
if (qstatus == 'QUEUE_WAITING')
{
console.log('Item in Queue, but waiting');
this.timesEmpty++;
this.RunProcess(); // run another queue with timeout
}
else if (qstatus == 'QUEUE_EMPTY')
{
console.debug('Processor: Empty Queue');
//this.tooltip.ProcessEnd();
this.StopProcess({ defer: true });
}
else if (qstatus == "PREPARING_DONE")
{
console.log('Processor: Preparing is done');
this.StopProcess();
}
// React to status of the queue.
if (typeof this.screen.QueueStatus == 'function')
{
this.screen.QueueStatus(qstatus, data);
}
}
},
HandleItemError : function(result, type)
{
console.log('Handle Item Error', result, type);
var error = this.aStatusError[result.error];
if (error == 'NOQUOTA' )
{
this.PauseProcess();
}
this.screen.HandleItemError(result, type);
},
LoadItemView: function(data)
{
// var data = event.detail;
var nonce = this.nonce['itemview'];
if (this.worker !== null)
{
if (typeof data.callback === 'undefined')
{
data.callback = 'shortpixel.RenderItemView';
}
this.worker.postMessage({action: 'getItemView', 'nonce' : this.nonce['itemview'], 'data': { 'id' : data.id, 'type' : data.type, 'callback' : data.callback }});
}
},
AjaxRequest: function(data)
{
if (this.worker === null)
{
this.LoadWorker(); // JIT worker loading
}
var localWorker = false;
this.worker.postMessage({action: 'ajaxRequest', 'nonce' : this.nonce['ajaxRequest'], 'data': data });
},
GetPluginUrl: function()
{
return ShortPixelConstants[0].WP_PLUGIN_URL;
},
Debug: function (message, messageType)
{
if (typeof messageType == 'undefined')
messageType = 'debug';
if (messageType == 'debug')
{
console.debug(message);
}
if (messageType == 'error')
{
console.error('Error: ', message);
}
},
ScriptError: function(error)
{
console.trace('Script Error! ', error);
}
}

View File

@ -0,0 +1,728 @@
'use strict'
// New Class for Settings Section.
var ShortPixelSettings = function()
{
this.Init = function()
{
this.InitActions();
this.SaveOnKey();
}
this.InitActions = function()
{
var toggles = document.querySelectorAll('[data-toggle]');
var self = this;
toggles.forEach(function (toggle, index)
{
toggle.addEventListener('change', self.DoToggleAction.bind(self));
var evInit = new CustomEvent('change', {detail : { init: true }} );
toggle.dispatchEvent(evInit);
});
// Modals
var modals = document.querySelectorAll('[data-action="open-modal"]');
modals.forEach(function (modal, index)
{
modal.addEventListener('click', self.OpenModal.bind(self));
});
// Events for the New Exclusion dialog
var newExclusionInputs = document.querySelectorAll('.new-exclusion select, .new-exclusion input, .new-exclusion button, input[name="removeExclusion"], button[name="cancelEditExclusion"]');
newExclusionInputs.forEach(function (input)
{
switch (input.name)
{
case 'addExclusion':
case 'removeExclusion':
case 'cancelEditExclusion':
case 'updateExclusion':
var eventType = 'click';
break;
default:
var eventType = 'change';
break;
}
input.addEventListener(eventType, self.NewExclusionUpdateEvent.bind(self));
});
var exclusionItems = document.querySelectorAll('.exclude-list li');
exclusionItems.forEach(function (input) {
if (false == input.classList.contains('no-exclusion-item'))
{
input.addEventListener('click', self.NewExclusionShowInterfaceEvent.bind(self));
}
});
var addNewExclusionButton = document.querySelector('.new-exclusion-button');
if (addNewExclusionButton !== null)
{
addNewExclusionButton.addEventListener('click', this.NewExclusionShowInterfaceEvent.bind(this));
}
}
this.DoToggleAction = function(event)
{
event.preventDefault();
var checkbox = event.target;
var target = document.getElementById(checkbox.getAttribute('data-toggle'));
if (typeof checkbox.dataset.toggleReverse !== 'undefined')
{
var checked = ! checkbox.checked;
}
else {
var checked = checkbox.checked;
}
if (target === null)
{
console.error('Target element ID not found', checkbox);
return false;
}
if (checked)
{
// target.classList.add('is-visible');
this.ShowElement(target);
}
else
{
this.HideElement(target);
// target.classList.remove('is-visible');
}
}
this.ShowElement = function (elem) {
// Get the natural height of the element
var getHeight = function () {
elem.style.display = 'block'; // Make it visible
var height = elem.scrollHeight + 'px'; // Get it's height
elem.style.display = ''; // Hide it again
return height;
};
var height = getHeight(); // Get the natural height
elem.classList.add('is-visible'); // Make the element visible
elem.style.height = height; // Update the max-height
// Once the transition is complete, remove the inline max-height so the content can scale responsively
window.setTimeout(function () {
elem.style.height = '';
}, 350);
};
// Hide an element
this.HideElement = function (elem) {
// Give the element a height to change from
elem.style.height = elem.scrollHeight + 'px';
// Set the height back to 0
window.setTimeout(function () {
elem.style.height = '0';
}, 1);
// When the transition is complete, hide it
window.setTimeout(function () {
elem.classList.remove('is-visible');
}, 350);
};
this.OpenModal = function(elem)
{
var target = elem.target;
var targetElem = document.getElementById(target.dataset.target);
if (! targetElem)
return;
var shade = document.getElementById('spioSettingsModalShade');
var modal = document.getElementById('spioSettingsModal');
shade.style.display = 'block';
modal.classList.remove('spio-hide');
var body = modal.querySelector('.spio-modal-body');
body.innerHTML = ('afterbegin', targetElem.innerHTML); //.cloneNode()
body.style.background = '#fff';
shade.addEventListener('click', this.CloseModal.bind(this), {'once': true} );
modal.querySelector('.spio-close-help-button').addEventListener('click', this.CloseModal.bind(this), {'once': true});
if (body.querySelector('[data-action="ajaxrequest"]') !== null)
{
body.querySelector('[data-action="ajaxrequest"]').addEventListener('click', this.SendModal.bind(this));
}
}
this.CloseModal = function(elem)
{
var shade = document.getElementById('spioSettingsModalShade');
var modal = document.getElementById('spioSettingsModal');
shade.style.display = 'none';
modal.classList.add('spio-hide');
}
this.SendModal = function(elem)
{
var modal = document.getElementById('spioSettingsModal');
var body = modal.querySelector('.spio-modal-body');
var inputs = body.querySelectorAll('input');
var data = {};
var validated = true;
for (var i = 0; i < inputs.length; i++)
{
data[inputs[i].name] = inputs[i].value;
if (typeof inputs[i].dataset.required !== 'undefined')
{
if (inputs[i].value !== inputs[i].dataset.required)
{
inputs[i].style.border = '1px solid #ff0000';
validated = false;
return false;
}
}
}
if (! validated)
return false;
data.callback = 'shortpixelSettings.receiveModal'
data.type = 'settings';
window.addEventListener('shortpixelSettings.receiveModal', this.ReceiveModal.bind(this), {'once': true} );
window.ShortPixelProcessor.AjaxRequest(data);
}
this.ReceiveModal = function(elem)
{
if (typeof elem.detail.settings.results !== 'undefined')
{
var modal = document.getElementById('spioSettingsModal');
var body = modal.querySelector('.spio-modal-body');
body.innerHTML = elem.detail.settings.results;
}
if (typeof elem.detail.settings.redirect !== 'undefined')
{
window.location.href = elem.detail.settings.redirect;
}
}
this.SaveOnKey = function()
{
var saveForm = document.getElementById('wp_shortpixel_options');
if (saveForm === null)
return false; // no form no save.
window.addEventListener('keydown', function(event) {
if (! (event.key == 's' || event.key == 'S') || ! event.ctrlKey)
{
return true;
}
document.getElementById('wp_shortpixel_options').submit();
event.preventDefault();
return false;
});
}
this.NewExclusionShowInterfaceEvent = function (event)
{
this.ResetExclusionInputs();
event.preventDefault();
var element = document.querySelector('.new-exclusion');
element.classList.remove('not-visible', 'hidden');
var cancelButton = document.querySelector('.new-exclusion .button-actions button[name="cancelEditExclusion"]');
cancelButton.classList.remove('not-visible', 'hidden');
var updateButton = document.querySelector('.new-exclusion .button-actions button[name="updateExclusion"]');
if (event.target.name == 'addNewExclusion')
{
var mode = 'new';
var id = 'new';
var title = document.querySelector('.new-exclusion h3.new-title');
var button = document.querySelector('.new-exclusion .button-actions button[name="addExclusion"]');
}
else {
var mode = 'edit';
if (event.target.id)
{
var id = event.target.id;
var parent = event.target;
}
else {
var id = event.target.parentElement.id;
var parent = event.target.parentElement;
}
var title = document.querySelector('.new-exclusion h3.edit-title');
var button = document.querySelector('.new-exclusion .button-actions button[name="removeExclusion"]');
var input = document.querySelector('.new-exclusion input[name="edit-exclusion"]')
updateButton.classList.remove('not-visible', 'hidden');
input.value = id;
var dataElement = parent.querySelector('input').value;
var data = JSON.parse(dataElement);
this.ReadWriteExclusionForm(data)
}
title.classList.remove('not-visible', 'hidden');
button.classList.remove('not-visible', 'hidden');
}
this.HideExclusionInterface = function()
{
var element = document.querySelector('.new-exclusion');
element.classList.add('not-visible');
}
// EXCLUSIONS
this.NewExclusionUpdateEvent = function(event)
{
var target = event.target;
var inputName = event.target.name;
switch(inputName)
{
case 'exclusion-type':
this.NewExclusionUpdateType(target);
break;
case 'apply-select':
this.NewExclusionUpdateThumbType(target);
break;
case 'addExclusion':
this.NewExclusionButtonAdd(target);
break;
case 'exclusion-exactsize':
this.NewExclusionToggleSizeOption(target);
break;
case 'removeExclusion':
this.RemoveExclusion(target);
break;
case 'cancelEditExclusion':
this.HideExclusionInterface();
break;
case 'updateExclusion':
this.UpdateExclusion();
break;
}
}
this.NewExclusionUpdateType = function(element)
{
var value = element.value;
var selected = element.options[element.selectedIndex];
var example = selected.dataset.example;
if ( typeof example == 'undefined')
{
example = '';
}
var valueOption = document.querySelector('.new-exclusion .value-option');
var sizeOption = document.querySelector('.new-exclusion .size-option');
var regexOption = document.querySelector('.regex-option');
var switchExactOption = document.querySelector('.exact-option');
if (value == 'size')
{
valueOption.classList.add('not-visible');
sizeOption.classList.remove('not-visible');
switchExactOption.classList.remove('not-visible');
regexOption.classList.add('not-visible');
}
else {
valueOption.classList.remove('not-visible');
sizeOption.classList.add('not-visible');
switchExactOption.classList.add('not-visible');
regexOption.classList.remove('not-visible');
}
var valueInput = document.querySelector('input[name="exclusion-value"]');
if (null !== valueInput)
{
valueInput.placeholder = example;
}
}
this.NewExclusionUpdateThumbType = function(element)
{
var value = element.value;
var thumbSelect = document.querySelector('select[name="thumbnail-select"]');
if (value == 'selected-thumbs')
{
thumbSelect.classList.remove('not-visible');
}
else {
thumbSelect.classList.add('not-visible');
}
}
this.ReadWriteExclusionForm = function(setting)
{
// compile all inputs to a json encoded string to add to UX
if (null === setting || typeof setting === 'undefined')
{
var setting = {
'type' : '',
'value' : '',
'apply' : '',
};
var mode = 'read';
}
else {
var mode = 'write';
}
var strings = {};
var typeOption = document.querySelector('.new-exclusion select[name="exclusion-type"]');
var valueOption = document.querySelector('.new-exclusion input[name="exclusion-value"]');
var applyOption = document.querySelector('.new-exclusion select[name="apply-select"]');
var regexOption = document.querySelector('.new-exclusion input[name="exclusion-regex"]');
if ('read' === mode)
{
setting.type = typeOption.value;
setting.value = valueOption.value;
setting.apply = applyOption.value;
strings.type = typeOption.options[typeOption.selectedIndex].innerText;
strings.apply = applyOption.options[applyOption.selectedIndex].innerText;
}
else {
if (setting.type.indexOf('regex') != -1)
{
typeOption.value = setting.type.replace('regex-', '');
}
else {
typeOption.value = setting.type;
}
valueOption.value = setting.value;
applyOption.value = setting.apply;
}
// When selected thumbnails option is selected, add the thumbnails to the list.
if ('selected-thumbs' == applyOption.value)
{
var thumbOption = document.querySelector('.new-exclusion select[name="thumbnail-select"]');
var thumblist = [];
if ('read' === mode)
{
for(var i =0; i < thumbOption.selectedOptions.length; i++)
{
thumblist.push(thumbOption.selectedOptions[i].value);
}
setting.thumblist = thumblist;
}
else if ('write' === mode){
for (var i = 0; i < thumbOption.options.length; i++)
{
if (setting.thumblist.indexOf(thumbOption[i].value) != -1)
{
thumbOption[i].selected = true;
}
}
this.NewExclusionUpdateThumbType(applyOption);
}
}
// Check for regular expression active on certain types
if ('read' === mode && true === regexOption.checked && (typeOption.value == 'name' || typeOption.value == 'path'))
{
setting.type = 'regex-' + setting.type;
}
else if ('write' === mode)
{
if (setting.type.indexOf('regex') != -1)
{
regexOption.checked = true;
}
}
// Options for size setting
if ('size' === setting.type)
{
var exactOption = document.querySelector('.new-exclusion input[name="exclusion-exactsize"]');
var width = document.querySelector('.new-exclusion input[name="exclusion-width"]');
var height = document.querySelector('.new-exclusion input[name="exclusion-height"]');
var minwidth = document.querySelector('.new-exclusion input[name="exclusion-minwidth"]');
var maxwidth = document.querySelector('.new-exclusion input[name="exclusion-maxwidth"]');
var minheight = document.querySelector('.new-exclusion input[name="exclusion-minheight"]');
var maxheight = document.querySelector('.new-exclusion input[name="exclusion-maxheight"]');
if ('read' === mode)
{
if (true === exactOption.checked)
{
setting.value = width.value + 'x' + height.value;
}
else {
setting.value = minwidth.value + '-' + maxwidth.value + 'x' + minheight.value + '-' + maxheight.value;
}
}
else if ('write' === mode)
{
var value = setting.value;
var split = value.split(/(x|×|X)/);
if (value.indexOf('-') === -1)
{
exactOption.checked = true;
width.value = split[0]; // in this split 1 is the X symbol
height.value = split[2];
}
else {
var widths = split[0].split('-'); // split the split for widths
var heights = split[2].split('-');
minwidth.value = widths[0];
maxwidth.value = widths[1];
minheight.value = heights[0];
maxheight.value = heights[1];
}
this.NewExclusionUpdateType(typeOption)
this.NewExclusionToggleSizeOption(exactOption);
}
}
if ('read' === mode)
{
return [setting, strings];
}
}
this.NewExclusionButtonAdd = function(element)
{
var result = this.ReadWriteExclusionForm(null);
var setting = result[0];
var strings = result[1];
var listElement = document.querySelector('.exclude-list');
var newElement = document.createElement('li');
var inputElement = document.createElement('input');
var newIndexInput = document.getElementById('new-exclusion-index');
var newIndex = parseInt(newIndexInput.value) + 1;
newIndexInput.value = newIndex;
newElement.id = 'exclude-' + newIndex;
newElement.addEventListener('click', this.NewExclusionShowInterfaceEvent.bind(this));
inputElement.type = 'hidden';
inputElement.name = 'exclusions[]';
inputElement.value = JSON.stringify(setting);
newElement.appendChild(inputElement);
var spans = [strings.type, setting.value, strings.apply];
for (var i = 0; i < spans.length; i++)
{
var spanElement = document.createElement('span');
spanElement.textContent = spans[i];
newElement.appendChild(spanElement);
}
listElement.appendChild(newElement);
var noItemsItem = document.querySelector('.exclude-list .no-exclusion-item');
if (noItemsItem !== null)
{
noItemsItem.classList.add('not-visible');
}
this.ResetExclusionInputs();
this.HideExclusionInterface();
this.ShowExclusionSaveWarning();
}
this.NewExclusionToggleSizeOption = function(target)
{
var sizeOptionRange = document.querySelector('.new-exclusion .size-option-range');
var sizeOptionExact = document.querySelector('.new-exclusion .size-option-exact');
if (true === target.checked)
{
sizeOptionRange.classList.add('not-visible');
sizeOptionExact.classList.remove('not-visible');
}
else {
sizeOptionRange.classList.remove('not-visible');
sizeOptionExact.classList.add('not-visible');
}
}
this.ResetExclusionInputs = function()
{
var typeOption = document.querySelector('.new-exclusion select[name="exclusion-type"]');
var valueOption = document.querySelector('.new-exclusion input[name="exclusion-value"]');
var applyOption = document.querySelector('.new-exclusion select[name="apply-select"]');
var inputs = document.querySelectorAll('.new-exclusion input, .new-exclusion select');
for (var i = 0; i < inputs.length; i++)
{
var input = inputs[i];
if (input.tagName == 'SELECT')
{
input.selectedIndex = 0;
}
else if (input.type == 'checkbox')
{
input.checked = false;
}
else {
input.value = '';
}
}
var ev = new CustomEvent('change');
typeOption.dispatchEvent(ev);
applyOption.dispatchEvent(ev);
// reset title and buttons.
var titles = document.querySelectorAll('.new-exclusion h3');
var buttons = document.querySelectorAll('.new-exclusion .button-actions button');
for(var i = 0; i < titles.length; i++)
{
titles[i].classList.add('not-visible', 'hidden');
}
for (var i = 0; i < buttons.length; i++)
{
buttons[i].classList.add('not-visible', 'hidden');
}
var exactOption = document.querySelector('.new-exclusion input[name="exclusion-exactsize"]');
exactOption.checked = false
}
this.UpdateExclusion = function()
{
var id = document.querySelector('.new-exclusion input[name="edit-exclusion"]');
var result = this.ReadWriteExclusionForm();
var setting = result[0];
var strings = result[1];
if (id)
{
var element = document.querySelector('.exclude-list #' +id.value + ' input');
var liElement = document.querySelector('.exclude-list #' +id.value);
var removeChildren = [];
if (null !== element)
{
element.value = JSON.stringify(setting);
// Can't directly remove children, because it messes with the collection index.
Array.from(liElement.children).forEach ( function (child, index){
if (child.tagName == 'SPAN')
{
removeChildren.push(child)
}
});
for(var i = 0; i < removeChildren.length; i++ )
{
removeChildren[i].remove();
}
var spans = [strings.type, setting.value, strings.apply];
for (var j = 0; j < spans.length; j++)
{
var spanElement = document.createElement('span');
spanElement.textContent = spans[j];
liElement.appendChild(spanElement);
}
}
}
this.HideExclusionInterface();
this.ShowExclusionSaveWarning();
}
this.ShowExclusionSaveWarning = function()
{
var reminder = document.querySelector('.exclusion-save-reminder');
if (reminder)
{
reminder.classList.remove('hidden');
}
}
this.RemoveExclusion = function()
{
var id = document.querySelector('.new-exclusion input[name="edit-exclusion"]');
if (id)
{
var element = document.querySelector('.exclude-list #' +id.value);
if (null !== element)
{
element.remove();
}
}
this.HideExclusionInterface();
this.ShowExclusionSaveWarning();
}
this.Init();
} // SPSettings
document.addEventListener("DOMContentLoaded", function(){
var s = new ShortPixelSettings();
});

View File

@ -0,0 +1,279 @@
'use strict';
var ShortPixelToolTip = function(reserved, processor)
{
this.strings = '';
this.Init = function()
{
var paused = localStorage.getItem('tooltipPause'); // string returns, not boolean
if (paused == 'true')
{
console.log('manual paused (tooltip)');
processor.PauseProcess();
}
var control = document.querySelector('.ab-item .controls');
if (control)
control.addEventListener('click', this.ToggleProcessing.bind(this));
this.ToggleIcon();
if (processor.isManualPaused == true)
{
this.ProcessPause();
}
this.strings = spio_tooltipStrings;
window.addEventListener('shortpixel.processor.paused', this.ProcessChange.bind(this));
}
this.GetToolTip = function() // internal function please.
{
var element = document.querySelector('li.shortpixel-toolbar-processing');
if (element === null)
{
console.error('Tooltip could not be found!');
}
return element;
}
this.InitStats = function()
{
var processData = ShortPixelProcessorData.startData;
if (typeof processData !== 'object')
{
console.error('Tooltip: No start Data found.');
return false;
}
this.RefreshStats(processData.media.stats, 'media');
this.RefreshStats(processData.custom.stats, 'custom');
this.RefreshStats(processData.total.stats, 'total');
// Hide Tooltip is manual paused is true, but there is also nothing to do.
if (processor.isManualPaused == true && processData.total.stats.total <= 0)
{
this.ProcessEnd();
}
}
// Used to put a 'todo' number in the tooltip when processing
this.RefreshStats = function(stats, type)
{
var neededType;
if (processor.screen.isMedia == true && processor.screen.isCustom == true)
neededType = 'total';
else if (processor.screen.isMedia == true && processor.screen.isCustom == false)
neededType = 'media';
else if (processor.screen.isMedia == false && processor.screen.isCustom == true)
neededType = 'custom';
if (neededType !== type)
return;
var toolTip = this.GetToolTip();
if (toolTip === null)
return false;
var statTip = toolTip.querySelector('.stats');
if (statTip == null)
return;
var inqueue = stats.in_queue;
var inprocess = stats.in_process;
// @todo This needs fixing.
var pattern = new RegExp("\\.|\\,", '');
if (typeof inqueue === 'string' && inqueue)
inqueue = inqueue.replace(pattern,''); // remove number formats
if (typeof inprocess === 'string' && inprocess)
inprocess = inprocess.replace(pattern,''); // remove number formats
var number = parseInt(inqueue) + parseInt(inprocess);
statTip.textContent = this.FormatNumber(number);
// Updating the titles.
var itemTitle = statTip.textContent + ' ';
itemTitle += (number == 1) ? this.strings.item : this.strings.items;
if (toolTip.querySelector('#short-pixel-notice-toolbar') !== null)
{
toolTip.querySelector('#short-pixel-notice-toolbar').title = this.strings.processing + ' ' + itemTitle;
}
if (toolTip.querySelector('.dashicons.pause') !== null)
{
toolTip.querySelector('.dashicons.pause').title = itemTitle + '\n\n' + this.strings.pause;
}
if (toolTip.querySelector('.dashicons.play') !== null)
{
toolTip.querySelector('.dashicons.play').title = itemTitle + '\n\n' + this.strings.resume;
}
if (statTip.classList.contains('hidden') && number > 0)
statTip.classList.remove('hidden');
else if (! statTip.classList.contains('hidden') && number == 0)
statTip.classList.add('hidden');
}
this.FormatNumber = function(num)
{
var digits = 1;
var si = [
{ value: 1E18, symbol: "E" },
{ value: 1E15, symbol: "P" },
{ value: 1E12, symbol: "T" },
{ value: 1E9, symbol: "G" },
{ value: 1E6, symbol: "M" },
{ value: 1E3, symbol: "k" }
], i;
for (i = 0; i < si.length; i++) {
if (num >= si[i].value) {
return (num / si[i].value).toFixed(digits).replace(/\.?0+$/, "") + si[i].symbol;
}
}
return num;
}
this.ToggleProcessing = function(event)
{
event.preventDefault();
//event.stopProp
if (processor.isManualPaused == false)
{
processor.PauseProcess();
localStorage.setItem('tooltipPause','true');
this.ProcessPause();
}
else
{
processor.ResumeProcess();
localStorage.setItem('tooltipPause','false');
this.ProcessResume();
}
processor.CheckActive();
}
this.ToggleIcon = function()
{
var controls = document.querySelectorAll('.ab-item .controls > span');
for(var i = 0; i < controls.length; i++)
{
var control = controls[i];
if (control.classList.contains('pause'))
{
if (processor.isManualPaused == true)
control.classList.add('hidden');
else
control.classList.remove('hidden');
}
else if (control.classList.contains('play'))
{
if (processor.isManualPaused == false)
control.classList.add('hidden');
else
control.classList.remove('hidden');
}
}
}
this.DoingProcess = function()
{
var tooltip = this.GetToolTip();
if (tooltip === null)
return false;
tooltip.classList.remove('shortpixel-hide');
tooltip.classList.add('shortpixel-processing');
}
this.AddNotice = function(message)
{
var tooltip = this.GetToolTip(); // li.shortpixel-toolbar-processing
if (tooltip === null)
return false;
var toolcontent = tooltip.querySelector('.toolbar-notice-wrapper');
if (toolcontent == null)
{
var abItem = tooltip.querySelector('.ab-item');
var wrapper = document.createElement('div');
wrapper.className = 'toolbar-notice-wrapper';
abItem.parentNode.insertBefore(wrapper, abItem.nextSibling);
var toolcontent = tooltip.querySelector('.toolbar-notice-wrapper');
}
var id = message.replace(/[^a-zA-Z ]/g, "").replace(/ /g, "").slice(0,20);
var alert = document.createElement('div');
alert.dataset.msgid = id;
alert.className = 'toolbar-notice toolbar-notice-error';
alert.innerHTML = message;
// Prevent double notices with same message
if (toolcontent.querySelector('[data-msgid="' + id + '"]') == null)
{
var alertChild = toolcontent.appendChild(alert);
window.setTimeout (this.RemoveNotice.bind(this), 5000, alertChild);
}
}
this.RemoveNotice = function(notice)
{
notice.style.opacity = 0;
window.setTimeout(function () { notice.remove() }, 2000);
}
this.ProcessResume = function()
{
var tooltip = this.GetToolTip();
if (tooltip === null)
return false;
tooltip.classList.remove('shortpixel-paused');
tooltip.classList.add('shortpixel-processing');
this.ToggleIcon();
}
this.ProcessEnd = function()
{
var tooltip = this.GetToolTip();
if (tooltip === null)
{
return false;
}
tooltip.classList.add('shortpixel-hide');
tooltip.classList.remove('shortpixel-processing');
}
this.ProcessPause = function()
{
var tooltip = this.GetToolTip();
if (tooltip === null)
return false;
tooltip.classList.add('shortpixel-paused');
tooltip.classList.remove('shortpixel-processing');
tooltip.classList.remove('shortpixel-hide');
this.ToggleIcon();
}
this.ProcessChange = function(e)
{
var detail = e.detail;
if (detail.paused == false)
this.ProcessResume();
else
this.ProcessPause();
}
this.HandleError = function()
{
console.trace('tooltip error');
}
this.Init();
} // tooltip.

View File

@ -0,0 +1,161 @@
'use strict';
onmessage = function(e)
{
var action = e.data.action;
var data = e.data.data;
var nonce = e.data.nonce;
var isBulk = false;
SpWorker.nonce = nonce;
if (typeof e.data.isBulk !== 'undefined')
isBulk = e.data.isBulk;
switch(action)
{
case 'setEnv':
SpWorker.SetEnv(data);
break;
case 'shutdown':
SpWorker.ShutDown();
break;
case 'process':
SpWorker.Process(data);
break;
case 'getItemView':
SpWorker.GetItemView(data);
break;
case 'ajaxRequest':
SpWorker.AjaxRequest(data);
break;
case 'updateLocalSecret':
var key = e.data.key;
SpWorker.UpdateLocalSecret(key);
break;
}
}
var SpWorker = {
ajaxUrl: null,
action: 'shortpixel_image_processing',
secret: null,
nonce: null,
isBulk: false, // If we are on the bulk screen / queue
isCustom: false, // Process this queueType - depends on screen
isMedia: false, // Process this queueType - depends on screen.
stopped: false,
Fetch: async function (data)
{
var params = new URLSearchParams();
params.append('action', this.action);
params.append('bulk-secret', this.secret);
params.append('nonce', this.nonce);
params.append('isBulk', this.isBulk);
var queues = [];
if (this.isMedia == true)
queues.push('media');
if (this.isCustom == true)
queues.push('custom');
params.append('queues', queues);
if (typeof data !== 'undefined' && typeof data == 'object')
{
for(var key in data)
params.append(key, data[key]);
}
var response = await fetch(this.ajaxUrl, {
'method': 'POST',
headers: {
'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'
},
body: params.toString(),
}).catch (function (error){
if (this && ! this.stopped) // if page is switched during init , this might not be set.
{
postMessage({'status' : false, message: error});
console.error('Worker.js reporting issue on catch', error);
}
else {
console.log('stopped but not complaining!');
}
return false;
});
if (response.ok)
{
var json = await response.json();
postMessage({'status' : true, response: json});
}
else if(this && ! this.stopped)
{
var text = await response.text();
if ( typeof text === 'undefined') // if no, try json
{
var text = await response.json();
}
if (typeof text === 'undefined' || text.length === 0) // if still not, then status text perhaps
{
var text = response.status + ' ' + response.statusText;
}
var message = {status: false, http_status: response.status, http_text: text, status_text: response.statusText };
if (response.status == 500) // fatal error
{
console.error('Worker: Fatal error detected');
}
else if (response.status == 502 || response.status == 503) // server gave up
{
console.error('Worker: server unavailable or overloaded');
}
else {
console.error('Worker: Unknown error', response);
}
postMessage({'status' : false, message: message});
}
},
SetEnv: function (data)
{
for (var key in data)
{
this[key] = data[key];
}
},
ShutDown: function()
{
this.stopped = true;
// this.Fetch();
},
UpdateLocalSecret: function(key)
{
this.secret = key;
},
GetItemView: function(data)
{
this.action = 'shortpixel_get_item_view';
this.Fetch(data);
},
AjaxRequest: function(data)
{
this.action = 'shortpixel_ajaxRequest';
this.Fetch(data);
},
Process: function(data)
{
this.action = 'shortpixel_image_processing';
this.Fetch(data);
}
} // worker

View File

@ -0,0 +1,901 @@
/**
* Short Pixel WordPress Plugin javascript
*/
// init checks bulkProcess on each page. initSettings is when the settings View is being loaded.
jQuery(document).ready(function(){ShortPixel.init(); });
function delayedInit() {
if(typeof ShortPixel !== 'undefined' && ShortPixel.didInit == false) {
console.error('ShortPixel: Delayed Init. Check your installation for errors');
ShortPixel.init();
} else {
setTimeout(delayedInit, 10000);
}
}
setTimeout(delayedInit, 10000);
var ShortPixel = function() {
var updateTimer;
// The InitSettings usually runs before these settings, making everything complicated (@todo)
function init() {
if (typeof ShortPixel.API_IS_ACTIVE !== 'undefined') return; //was initialized by the 10 sec. setTimeout, rare but who knows, might happen on very slow connections...
//are we on media list?
if( jQuery('table.wp-list-table.media').length > 0) {
//register a bulk action
jQuery('select[name^="action"] option:last-child').before('<option value="shortpixel-optimize">' + _spTr.optimizeWithSP
+ '</option><option value="shortpixel-lossy"> → ' + _spTr.redoLossy
+ '</option><option value="shortpixel-glossy"> → ' + _spTr.redoGlossy
+ '</option><option value="shortpixel-lossless"> → ' + _spTr.redoLossless
+ '</option><option value="shortpixel-restore"> → ' + _spTr.restoreOriginal
+ '</option><option value="shortpixel-smartcrop"> → ' + _spTr.redoSmartcrop
+ '</option><option value="shortpixel-smartcropless"> → ' + _spTr.redoSmartcropless
+ '</option><option value="shortpixel-mark-completed"> → ' + _spTr.markCompleted
+ '</option>');
}
// Extracting the protected Array from within the 0 element of the parent array
ShortPixel.setOptions(ShortPixelConstants[0]);
if (jQuery('#shortpixel-form-request-key').length > 0)
{
jQuery('#pluginemail').on('change, keyup', jQuery.proxy(this.updateSignupEmail, this));
jQuery('#request_key').on('mouseenter', jQuery.proxy(this.updateSignupEmail, this));
jQuery('#request_key').on('click', jQuery.proxy(this.newApiKey, this));
}
if (window.ShortPixelProcessor)
{
window.ShortPixelProcessor.Load(ShortPixel['HAS_QUOTA']);
}
this.didInit = true;
// Move footer notices to the top, where it should be.
$headerEnd = jQuery( '.wp-header-end' );
jQuery( 'div.shortpixel-notice' ).not( '.inline, .below-h2' ).insertAfter( $headerEnd );
var settingsPage = document.querySelector('.is-shortpixel-settings-page');
if (settingsPage !== null)
{
this.initSettings();
}
}
function setOptions(options) {
for(var opt in options) {
ShortPixel[opt] = options[opt];
}
}
function isEmailValid(email) {
// return /^\w+([\.+-]?\w+)*@\w+([\.-]?\w+)*(\.\w{1,63})+$/.test(email);
var regex = /^\S+@\S+\.\S+$/;
return regex.test(email);
}
function updateSignupEmail() {
clearTimeout( ShortPixel.updateTimer );
ShortPixel.updateTimer = setTimeout( function() {
var email = jQuery('#pluginemail').val().trim();
var $submit = jQuery('#request_key');
var isValid = ShortPixel.isEmailValid(email)
if(isValid) {
jQuery('#request_key').removeClass('disabled');
$submit.removeClass('disabled');
$submit.removeAttr('disabled');
}
else
{
$submit.attr('disabled', true);
$submit.addClass('disabled');
}
jQuery('#request_key').attr('href', jQuery('#request_key').attr('href').split('?')[0] + '?pluginemail=' + email);
}, 1000);
}
function validateKey(button){
jQuery('#valid').val('validate');
jQuery(button).parents('form').submit();
}
jQuery("#key").on('keypress', function(e) {
if(e.which == 13) {
jQuery('#valid').val('validate');
}
});
function enableResize(elm) {
if(jQuery(elm).is(':checked')) {
jQuery("#width,#height").prop("disabled", false);
SpioResize.lastW = false; //to trigger the animation
jQuery(".resize-type-wrap").show(800, window.SpioResize.run);
} else {
jQuery("#width,#height").prop("disabled", true);
window.SpioResize.hide();
jQuery(".resize-type-wrap").hide(800);
}
}
function checkExifWarning()
{
if (! jQuery('input[name="removeExif"]').is(':checked') && jQuery('input[name="png2jpg"]').is(':checked') )
jQuery('.exif_warning').fadeIn();
else
jQuery('.exif_warning').fadeOut();
if (! jQuery('input[name="removeExif"]').is(':checked') && jQuery('.exif_imagick_warning').data('imagick') <= 0)
jQuery('.exif_imagick_warning').fadeIn();
else
jQuery('.exif_imagick_warning').fadeOut();
}
function checkSmartCropWarning()
{
if (jQuery('input[name="useSmartcrop"]').is(':checked') && jQuery('.smartcrop_warning').data('smartcrop') == 1 )
jQuery('.smartcrop_warning').fadeIn();
else
jQuery('.smartcrop_warning').fadeOut();
}
function checkBackUpWarning()
{
if (! jQuery('input[name="backupImages"]').is(':checked') )
{
jQuery('.backup_warning').fadeIn();
}
else {
jQuery('.backup_warning').fadeOut();
}
}
function checkHeavyFeatureWarning(name)
{
var inputName = (name == 'retina') ? 'optimizeRetina' : 'optimizeUnlisted';
var input = jQuery('input[name="' + inputName + '"]');
if (! input)
return;
var warningEl = jQuery('.heavy-feature-virtual.' + name );
if (input.is(':checked'))
{
//input.attr('disabled', true);
warningEl.fadeIn();
}
else {
//input.attr('disabled', false);
warningEl.fadeOut();
}
}
function setupGeneralTab() {
// @todo Make something workable out of this
var rad = 0;
if (typeof document.wp_shortpixel_options !== 'undefined')
rad = document.wp_shortpixel_options.compressionType;
if (document.getElementById('compressionType-database') !== null)
var savedCompression = document.getElementById('compressionType-database').value;
else
var savedCompression = null;
for(var i = 0, prev = null; i < rad.length; i++) {
rad[i].onclick = function() {
if(this !== prev) {
prev = this;
}
if (this.value == savedCompression)
jQuery('.compression-notice-row').addClass('shortpixel-hide');
else
jQuery('.compression-notice-row').removeClass('shortpixel-hide');
};
}
ShortPixel.enableResize("#resize");
jQuery("#resize").on('change', function(){ enableResize(this); });
jQuery(".resize-sizes").on('blur', function(e){
var elm = jQuery(e.target);
if(ShortPixel.resizeSizesAlert == elm.val())
return; // returns if check in progress, presumed.
ShortPixel.resizeSizesAlert = elm.val();
var minSize = jQuery("#min-" + elm.attr('name')).val();
var niceName = jQuery("#min-" + elm.attr('name')).data('nicename');
if(elm.val() < Math.min(minSize, 1024)) { // @todo is this correct? This will always be < 1024, and give first error
if(minSize > 1024) {
alert( SPstringFormat(_spTr.pleaseDoNotSetLesser1024,niceName) );
} else {
alert( SPstringFormat(_spTr.pleaseDoNotSetLesserSize, niceName, niceName, minSize) );
}
e.preventDefault();
//elm.val(this.defaultValue);
elm.focus();
}
else {
this.defaultValue = elm.val();
}
});
jQuery('.shortpixel-confirm').on('click', function(event){
var choice = confirm(event.target.getAttribute('data-confirm'));
if (!choice) {
event.preventDefault();
return false;
}
return true;
});
jQuery('input[name="removeExif"], input[name="png2jpg"]').on('change', function()
{
ShortPixel.checkExifWarning();
});
ShortPixel.checkExifWarning();
jQuery('input[name="backupImages"]').on('change', function()
{
ShortPixel.checkBackUpWarning();
});
ShortPixel.checkBackUpWarning();
jQuery('input[name="useSmartcrop"]').on('change', function()
{
ShortPixel.checkSmartCropWarning();
});
ShortPixel.checkSmartCropWarning();
}
function apiKeyChanged() {
jQuery(".wp-shortpixel-options .shortpixel-key-valid").css("display", "none");
jQuery(".wp-shortpixel-options button#validate").css("display", "inline-block");
}
function setupAdvancedTab() {
/* @TODO - Removed in favor of AjaxController method . Remove in next version
jQuery("input.remove-folder-button").on('click', function(){
var id = jQuery(this).data("value");
var path = jQuery(this).data('name');
var r = confirm( SPstringFormat(_spTr.areYouSureStopOptimizing, path) );
if (r == true) {
jQuery("#removeFolder").val(id);
jQuery('#wp_shortpixel_options').submit();
}
});
jQuery("input.recheck-folder-button").on('click', function(){
var path = jQuery(this).data("value");
var r = confirm( SPstringFormat(_spTr.areYouSureStopOptimizing, path));
if (r == true) {
jQuery("#recheckFolder").val(path);
jQuery('#wp_shortpixel_options').submit();
}
});
*/
if (document.querySelector('.heavy-feature-virtual.retina') !== null)
{
jQuery('input[name="optimizeRetina"]').on('change', function()
{
ShortPixel.checkHeavyFeatureWarning('retina');
});
ShortPixel.checkHeavyFeatureWarning('retina');
}
if (document.querySelector('.heavy-feature-virtual.unlisted') !== null)
{
jQuery('input[name="optimizeUnlisted"]').on('change', function()
{
ShortPixel.checkHeavyFeatureWarning('unlisted');
});
ShortPixel.checkHeavyFeatureWarning('unlisted');
}
}
function checkThumbsUpdTotal(el) {
var total = jQuery("#" +(el.checked ? "total" : "main")+ "ToProcess").val();
jQuery("div.bulk-play span.total").text(total);
jQuery("#displayTotal").text(total);
}
function initSettings() {
ShortPixel.adjustSettingsTabs();
ShortPixel.setupGeneralTab(); // certain alerts.
jQuery( window ).on('resize', function() {
ShortPixel.adjustSettingsTabs();
});
jQuery("article.sp-tabs a.tab-link").on('click', function(e){
var theID = jQuery(e.target).data("id");
ShortPixel.switchSettingsTab( theID );
});
jQuery('input[type=radio][name=deliverWebpType]').on('change', function(e) {
// shortpixel-settings init trigger events for toggles, ignore this when so.
if (e.detail && e.detail.init && e.detail.init === true)
{
return false;
}
if (this.value == 'deliverWebpAltered') {
if(window.confirm(_spTr.alertDeliverWebPAltered)){
var selectedItems = jQuery('input[type=radio][name=deliverWebpAlteringType]:checked').length;
if (selectedItems == 0) {
jQuery('#deliverWebpAlteredWP').prop('checked',true);
}
} else {
jQuery(this).prop('checked', false);
}
} else if(this.value == 'deliverWebpUnaltered') {
window.alert(_spTr.alertDeliverWebPUnaltered);
}
});
// Init active tab
var activeTab = document.querySelector('section.sel-tab');
if (activeTab !== null);
ShortPixel.switchSettingsTab(activeTab.getAttribute('id'));
}
// Switch between settings tabs.
function switchSettingsTab(target){
var tab = target.replace("tab-",""),
beacon = "",
section = jQuery("section#" +target);
// url = location.href.replace(location.hash,"") + '#' + tab;
/*if(history.pushState) {
history.pushState(null, null, url);
}
else {
location.hash = url;
} */
if (section.length == 0)
{
tab = 'settings'; // if tab does not exist.
}
jQuery('input[name="display_part"]').val(tab);
var uri = window.location.href.toString();
if (uri.indexOf("?") > 0) {
var clean_uri = uri.substring(0, uri.indexOf("?"));
clean_uri += '?' + jQuery.param({'page':'wp-shortpixel-settings', 'part': tab});
window.history.replaceState({}, document.title, clean_uri);
}
if(section.length > 0){
jQuery("section").removeClass("sel-tab");
jQuery('section .wp-shortpixel-tab-content').fadeOut(50);
jQuery(section).addClass("sel-tab");
//ShortPixel.adjustSettingsTabs();
//jQuery(section).find('.wp-shortpixel-tab-content').fadeIn(50);
jQuery(section).find('.wp-shortpixel-tab-content').fadeIn(50, ShortPixel.adjustSettingsTabs);
var event = new CustomEvent('shortpixel.ui.settingsTabLoad', { detail : {tabName: tab, section: section }});
window.dispatchEvent(event);
}
}
// Fixes the height of the current active tab.
function adjustSettingsTabsHeight(){
jQuery('.wso.banner').css('opacity', 1);
}
function closeHelpPane() {
jQuery('#shortpixel-hs-button-blind').remove();
jQuery('#shortpixel-hs-tools').remove();
jQuery('#hs-beacon').remove();
jQuery('#botbutton').remove();
jQuery('#shortpixel-hs-blind').remove();
}
function checkQuota() {
var data = {
action:'shortpixel_check_quota',
nonce: ShortPixelConstants[0].nonce_ajaxrequest,
return_json: true
};
jQuery.post(ShortPixel.AJAX_URL, data, function(result) {
console.log("quota refreshed");
console.log(result);
window.location.href = result.redirect;
});
}
function percentDial(query, size) {
jQuery(query).knob({
'readOnly': true,
'width': size,
'height': size,
'fgColor': '#1CAECB',
'format' : function (value) {
return value + '%';
}
});
}
function browseContent(browseData) {
browseData.action = 'shortpixel_browse_content';
var browseResponse = "";
jQuery.ajax({
type: "POST",
url: ShortPixel.AJAX_URL,
data: browseData,
success: function(response) {
browseResponse = response;
},
async: false
});
return browseResponse;
}
function newApiKey(event) {
event.preventDefault();
ShortPixel.updateSignupEmail();
if(!jQuery("#tos").is( ":checked" )) {
event.preventDefault();
jQuery("#tos-robo").fadeIn(400,function(){jQuery("#tos-hand").fadeIn();});
jQuery("#tos").click(function(){
jQuery("#tos-robo").css("display", "none");
jQuery("#tos-hand").css("display", "none");
});
return;
}
if (jQuery('#request_key').is(':disabled'))
{
return false;
}
jQuery('#request_key').addClass('disabled');
jQuery('#pluginemail_spinner').addClass('is-active');
jQuery('#shortpixel-form-request-key').submit();
}
function proposeUpgrade() {
//first open the popup window with the spinner
jQuery("#shortPixelProposeUpgrade .sp-modal-body").addClass('sptw-modal-spinner');
jQuery("#shortPixelProposeUpgrade .sp-modal-body").html("");
jQuery("#shortPixelProposeUpgradeShade").css("display", "block");
jQuery("#shortPixelProposeUpgrade").removeClass('shortpixel-hide');
jQuery("#shortPixelProposeUpgradeShade").on('click', this.closeProposeUpgrade);
//get proposal from server
var browseData = { 'action': 'shortpixel_propose_upgrade', nonce: ShortPixelConstants[0].nonce_ajaxrequest};
jQuery.ajax({
type: "POST",
url: ShortPixel.AJAX_URL,
data: browseData,
success: function(response) {
jQuery("#shortPixelProposeUpgrade .sp-modal-body").removeClass('sptw-modal-spinner');
jQuery("#shortPixelProposeUpgrade .sp-modal-body").html(response);
},
complete: function(response, status)
{
//console.log(response, status);
}
});
}
function closeProposeUpgrade() {
jQuery("#shortPixelProposeUpgradeShade").css("display", "none");
jQuery("#shortPixelProposeUpgrade").addClass('shortpixel-hide');
if(ShortPixel.toRefresh) {
ShortPixel.checkQuota();
}
}
// used in bulk restore all interface
function checkRandomAnswer(e)
{
var value = jQuery(e.target).val();
var answer = jQuery('input[name="random_answer"]').val();
var target = jQuery('input[name="random_answer"]').data('target');
if (value == answer)
{
jQuery(target).removeClass('disabled').prop('disabled', false);
jQuery(target).removeAttr('aria-disabled');
}
else
{
jQuery(target).addClass('disabled').prop('disabled', true);
}
}
function openImageMenu(e) {
e.preventDefault();
//install (lazily) a window click event to close the menus
if(!this.menuCloseEvent) {
jQuery(window).on('click', function(e){
if (!e.target.matches('.sp-dropbtn')) {
jQuery('.sp-dropdown.sp-show').removeClass('sp-show');
}
});
this.menuCloseEvent = true;
}
var shown = e.target.parentElement.classList.contains("sp-show");
jQuery('.sp-dropdown.sp-show').removeClass('sp-show');
if(!shown) e.target.parentElement.classList.add("sp-show");
}
function loadComparer(id, type) {
this.comparerData.origUrl = false;
if(this.comparerData.cssLoaded === false) {
jQuery('<link>')
.appendTo('head')
.attr({
type: 'text/css',
rel: 'stylesheet',
href: this.WP_PLUGIN_URL + '/res/css/twentytwenty.min.css'
});
this.comparerData.cssLoaded = 2;
}
if(this.comparerData.jsLoaded === false) {
jQuery.getScript(this.WP_PLUGIN_URL + '/res/js/jquery.twentytwenty.min.js', function(){
ShortPixel.comparerData.jsLoaded = 2;
/* What should this do?
if(ShortPixel.comparerData.origUrl.length > 0) {
ShortPixel.displayComparerPopup(ShortPixel.comparerData.width, ShortPixel.comparerData.height, ShortPixel.comparerData.origUrl, ShortPixel.comparerData.optUrl);
} */
});
this.comparerData.jsLoaded = 1;
//jQuery(".sp-close-button").click(ShortPixel.closeComparerPopup);
}
if(this.comparerData.origUrl === false) {
if (typeof type == 'undefined')
var type = 'media'; // default.
jQuery.ajax({
type: "POST",
url: ShortPixel.AJAX_URL,
data: { action : 'shortpixel_get_comparer_data', id : id, type: type, nonce: ShortPixelConstants[0].nonce_ajaxrequest },
success: function(response) {
// data = JSON.parse(response);
jQuery.extend(ShortPixel.comparerData, response);
if(ShortPixel.comparerData.jsLoaded == 2) {
ShortPixel.displayComparerPopup(ShortPixel.comparerData.width, ShortPixel.comparerData.height, ShortPixel.comparerData.origUrl, ShortPixel.comparerData.optUrl);
}
}
});
this.comparerData.origUrl = '';
}
}
function displayComparerPopup(width, height, imgOriginal, imgOptimized) {
//image sizes
var origWidth = width;
//depending on the sizes choose the right modal
var sideBySide = (height < 150 || width < 350);
var modal = jQuery(sideBySide ? '#spUploadCompareSideBySide' : '#spUploadCompare');
var modalShade = jQuery('.sp-modal-shade');
if(!sideBySide) {
jQuery("#spCompareSlider").html('<img alt="' + _spTr.originalImage + '" class="spUploadCompareOriginal"/><img alt="' + _spTr.optimizedImage + '" class="spUploadCompareOptimized"/>');
}
//calculate the modal size
width = Math.max(350, Math.min(800, (width < 350 ? (width + 25) * 2 : (height < 150 ? width + 25 : width))));
height = Math.max(150, (sideBySide ? (origWidth > 350 ? 2 * (height + 45) : height + 45) : height * width / origWidth));
var marginLeft = '-' + Math.round(width/2); // center
//set modal sizes and display
jQuery(".sp-modal-body", modal).css("width", width);
jQuery(".shortpixel-slider", modal).css("width", width);
modal.css("width", width);
modal.css('marginLeft', marginLeft + 'px');
modal.removeClass('shortpixel-hide');
jQuery(".sp-modal-body", modal).css("height", height);
modal.show();
//modal.parent().css('display', 'block');
modalShade.show();
if(!sideBySide) {
jQuery("#spCompareSlider").twentytwenty({slider_move: "mousemove"});
}
// Close Options
jQuery(".sp-close-button").on('click', { modal: modal}, ShortPixel.closeComparerPopup);
jQuery(document).on('keyup.sp_modal_active', { modal: modal}, ShortPixel.closeComparerPopup );
jQuery('.sp-modal-shade').on('click', { modal: modal}, ShortPixel.closeComparerPopup, );
//change images srcs
var imgOpt = jQuery(".spUploadCompareOptimized", modal);
jQuery(".spUploadCompareOriginal", modal).attr("src", imgOriginal);
//these timeouts are for the slider - it needs a punch to work :)
setTimeout(function(){
jQuery(window).trigger('resize');
}, 1000);
imgOpt.load(function(){
jQuery(window).trigger('resize');
});
imgOpt.attr("src", imgOptimized);
console.log('Popup Loaded! ', modal);
}
function closeComparerPopup(e) {
e.data.modal.addClass('shortpixel-hide');
jQuery('.sp-modal-shade').hide();
jQuery(document).unbind('keyup.sp_modal_active');
jQuery('.sp-modal-shade').off('click');
jQuery(".sp-close-button").off('click');
}
function convertPunycode(url) {
var parser = document.createElement('a');
parser.href = url;
if(url.indexOf(parser.protocol + '//' + parser.hostname) < 0) {
return parser.href;
}
return url.replace(parser.protocol + '//' + parser.hostname, parser.protocol + '//' + parser.hostname.split('.').map(function(part) {return sp_punycode.toASCII(part)}).join('.'));
}
return {
init : init,
didInit : false,
setOptions : setOptions,
isEmailValid : isEmailValid,
updateSignupEmail : updateSignupEmail,
validateKey : validateKey,
enableResize : enableResize,
setupGeneralTab : setupGeneralTab,
apiKeyChanged : apiKeyChanged,
setupAdvancedTab : setupAdvancedTab,
checkThumbsUpdTotal : checkThumbsUpdTotal,
initSettings : initSettings,
switchSettingsTab : switchSettingsTab,
adjustSettingsTabs : adjustSettingsTabsHeight,
closeHelpPane : closeHelpPane,
checkQuota : checkQuota,
percentDial : percentDial,
// initFolderSelector : initFolderSelector,
browseContent : browseContent,
newApiKey : newApiKey,
proposeUpgrade : proposeUpgrade,
closeProposeUpgrade : closeProposeUpgrade,
// includeUnlisted : includeUnlisted,
checkRandomAnswer : checkRandomAnswer,
// recheckQuota : recheckQuota,
openImageMenu : openImageMenu,
menuCloseEvent : false,
loadComparer : loadComparer,
displayComparerPopup: displayComparerPopup,
closeComparerPopup : closeComparerPopup,
convertPunycode : convertPunycode,
checkExifWarning : checkExifWarning,
checkBackUpWarning : checkBackUpWarning,
checkSmartCropWarning: checkSmartCropWarning,
checkHeavyFeatureWarning : checkHeavyFeatureWarning,
comparerData : {
cssLoaded : false,
jsLoaded : false,
origUrl : false,
optUrl : false,
width : 0,
height : 0
},
toRefresh : false,
resizeSizesAlert: false,
returnedStatusSearching: 0, // How often this status has come back in a row from server.
}
}(); // End of ShortPixel
// first is string to replace, rest are arguments.
function SPstringFormat() {
var params = Array.prototype.slice.call(arguments);
if (params.length === 0)
return;
var s = params.shift();
// skip the first one.
for (i=0; i< params.length; i++) {
s = s.replace(new RegExp('\\{' + i + '\\}', 'gm'), params[i]);
}
return s;
};
/** This doesn't go well with REACT environments */
/*if (!(typeof String.prototype.format == 'function')) {
String.prototype.format = stringFormat;
} */
( function( $, w, d ) {
w.SpioResize = {
image : {
width : 0,
height : 0
},
lag: 2000,
step1: false,
step2: false,
step3: false,
sizeRule: null,
initialized: false,
lastW: false,
lastH: false,
lastType: false,
};
SpioResize.hide = function() {
jQuery('.presentation-wrap').css('opacity', 0);
}
SpioResize.animate = function(img, step1, frame, step2, rule) {
img.animate( step1, 1000, 'swing', function(){
SpioResize.step3 = setTimeout(function(){
document.styleSheets[0].deleteRule(SpioResize.sizeRule);
frame.animate(step2, 1000, 'swing', function() {
SpioResize.sizeRule = document.styleSheets[0].insertRule(rule);
})
}, 600);
});
}
SpioResize.run = function() {
if(!SpioResize.initialized) {
var $document = $( d );
$document.on( 'input change', 'input[name="resizeWidth"], input[name="resizeHeight"]', function(e) {
clearTimeout(SpioResize.change);
SpioResize.changeDone = true;
SpioResize.changeFired = false;
SpioResize.change = setTimeout( function() {
SpioResize.changeFired = true;
SpioResize.run();
}, 1500 );
} );
$document.on( 'blur', 'input[name="resizeWidth"], input[name="resizeHeight"]', function(e) {
if(SpioResize.changeFired) {
return;
}
clearTimeout(SpioResize.change);
SpioResize.change = setTimeout( function() {
SpioResize.run();
}, 1500 );
} );
$document.on( 'change', 'input[name="resizeType"]', function(e) {
SpioResize.run();
});
SpioResize.initialized = true;
}
var w = $('#width').val();
var h = $('#height').val();
if(!w || !h) return;
var type = ($('#resize_type_outer').is(':checked') ? 'outer' : 'inner');
if(w === SpioResize.lastW && h === SpioResize.lastH && type === SpioResize.lastType) {
return;
}
SpioResize.hide();
SpioResize.lastW = w;
SpioResize.lastH = h;
SpioResize.lastType = type;
var frame1W = Math.round(120 * Math.sqrt(w / h));
var frame1H = Math.round(120 * Math.sqrt(h / w));
var frameAR = frame1W / frame1H;
if(frame1W > 280) {
frame1W = 280; frame1H = Math.round(280 / frameAR);
}
if(frame1H > 150) {
frame1H = 150; frame1W = Math.round(150 * frameAR);
}
var imgAR = 15 / 8;
var img = $('img.spai-resize-img');
img.css('width', '');
img.css('height', '');
img.css('margin', '0px');
var frame = $('div.spai-resize-frame');
frame.css('display', 'none');
frame.css('width', frame1W + 'px');
frame.css('height', frame1H + 'px');
frame.css('margin', Math.round((156 - frame1H ) / 2) + 'px auto 0');
clearTimeout(SpioResize.step1); clearTimeout(SpioResize.step2); clearTimeout(SpioResize.step3);
img.stop(); frame.stop();
if(SpioResize.sizeRule !== null) {
document.styleSheets[0].deleteRule(SpioResize.sizeRule);
SpioResize.sizeRule = null;
}
SpioResize.sizeRule = document.styleSheets[0].insertRule('.spai-resize-frame:after { content: "' + w + ' × ' + h + '"; }');
frame.addClass('spai-resize-frame');
$('.presentation-wrap').animate( {opacity: 1}, 500, 'swing', function(){
//because damn chrome is not repainting the frame after we change the sizes otherwise... :(
frame.css('display', 'block');
SpioResize.step2 = setTimeout(function(){
if(type == 'outer') {
if(imgAR > frameAR) {
var step1 = {
height: frame1H + 'px',
margin: Math.round((160 - frame1H) / 2) + 'px 0px'
};
var frameNewW = frame1H * imgAR;
var step2 = { width: Math.round(frameNewW) + 'px' };
var rule = '.spai-resize-frame:after { content: "' + Math.round(frameNewW * w / frame1W) + ' × ' + h + '"; }';
} else {
var step1 = {
width: frame1W + 'px',
margin: Math.round((160 - frame1W / imgAR) / 2) + 'px 0px'
};
var frameNewH = frame1W / imgAR;
var step2 = {
height: Math.round(frameNewH) + 'px',
margin: Math.round((156 - frameNewH) / 2) + 'px auto 0'
};
var rule = '.spai-resize-frame:after { content: "' + w + ' × ' + Math.round(frameNewH * w / frame1W) + '"; }';
}
} else {
if(imgAR > frameAR) {
var step1 = {
width: frame1W,
margin: Math.round((160 - frame1W / imgAR) / 2) + 'px 0px'
};
var frameNewH = frame1W / imgAR;
var step2 = {
height: Math.round(frameNewH) + 'px',
margin: Math.round((156 - frameNewH) / 2) + 'px auto 0'
};
var rule = '.spai-resize-frame:after { content: "' + w + ' × ' + Math.round(frameNewH * w / frame1W) + '"; }';
} else {
var step1 = {
height: frame1H,
margin: Math.round((160 - frame1H) / 2) + 'px 0px'
};
var frameNewW = frame1H * imgAR;
var step2 = {
width: Math.round(frameNewW) + 'px'
};
var rule = '.spai-resize-frame:after { content: "' + Math.round(frameNewW * w / frame1W) + ' × ' + h + '"; }';
}
}
SpioResize.animate(img, step1, frame, step2, rule);
}, 1000);
});
}
$( function() {
if($('#resize').is('checked')) {
SpioResize.run();
}
} );
} )( jQuery, window, document );

View File

@ -0,0 +1,213 @@
/*
* jQueryFileTree Plugin
*
* @author - Cory S.N. LaViska - A Beautiful Site (http://abeautifulsite.net/) - 24 March 2008
* @author - Dave Rogers - (https://github.com/daverogers/)
*
* Usage: $('.fileTreeDemo').fileTree({ options }, callback )
*
* TERMS OF USE
*
* This plugin is dual-licensed under the GNU General Public License and the MIT License and
* is copyright 2008 A Beautiful Site, LLC.
*/
var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; };
(function($, window) {
var FileTree;
FileTree = (function() {
function FileTree(el, args, callback) {
this.onEvent = bind(this.onEvent, this);
var $el, _this, defaults;
$el = $(el);
_this = this;
defaults = {
root: '/',
script: '/files/filetree',
folderEvent: 'click',
expandSpeed: 500,
collapseSpeed: 500,
expandEasing: 'swing',
collapseEasing: 'swing',
multiFolder: true,
loadMessage: 'Loading...',
errorMessage: 'Unable to get file tree information',
multiSelect: false,
onlyFolders: false,
onlyFiles: false,
preventLinkAction: false
};
this.jqft = {
container: $el
};
this.options = $.extend(defaults, args);
this.callback = callback;
this.data = {};
$el.html('<ul class="jqueryFileTree start"><li class="wait">' + this.options.loadMessage + '<li></ul>');
_this.showTree($el, escape(this.options.root), function() {
return _this._trigger('filetreeinitiated', {});
});
$el.delegate("li a", this.options.folderEvent, _this.onEvent);
}
FileTree.prototype.onEvent = function(event) {
var $ev, _this, callback, jqft, options, ref;
$ev = $(event.target);
options = this.options;
jqft = this.jqft;
_this = this;
callback = this.callback;
_this.data = {};
_this.data.li = $ev.closest('li');
_this.data.type = (ref = _this.data.li.hasClass('directory')) != null ? ref : {
'directory': 'file'
};
_this.data.value = $ev.text();
_this.data.rel = $ev.prop('rel');
_this.data.container = jqft.container;
if (options.preventLinkAction) {
event.preventDefault();
}
if ($ev.parent().hasClass('directory')) {
_this.jqft.container.find('LI.directory').removeClass('selected');
$ev.parent().addClass('selected');
if ($ev.parent().hasClass('collapsed')) {
if (!options.multiFolder) {
$ev.parent().parent().find('UL').slideUp({
duration: options.collapseSpeed,
easing: options.collapseEasing
});
$ev.parent().parent().find('LI.directory').removeClass('expanded').addClass('collapsed');
}
$ev.parent().removeClass('collapsed').addClass('expanded');
$ev.parent().find('UL').remove();
return _this.showTree($ev.parent(), $ev.attr('rel'), function() {
_this._trigger('filetreeexpanded', _this.data);
return callback != null;
});
} else {
return $ev.parent().find('UL').slideUp({
duration: options.collapseSpeed,
easing: options.collapseEasing,
start: function() {
return _this._trigger('filetreecollapse', _this.data);
},
complete: function() {
$ev.parent().removeClass('expanded').addClass('collapsed');
_this._trigger('filetreecollapsed', _this.data);
return callback != null;
}
});
}
} else {
if (!options.multiSelect) {
jqft.container.find('li').removeClass('selected');
$ev.parent().addClass('selected');
} else {
if ($ev.parent().find('input').is(':checked')) {
$ev.parent().find('input').prop('checked', false);
$ev.parent().removeClass('selected');
} else {
$ev.parent().find('input').prop('checked', true);
$ev.parent().addClass('selected');
}
}
_this._trigger('filetreeclicked', _this.data);
return typeof callback === "function" ? callback($ev.attr('rel')) : void 0;
}
};
FileTree.prototype.showTree = function(el, dir, finishCallback) {
var $el, _this, data, handleFail, handleResult, options, result;
$el = $(el);
options = this.options;
_this = this;
$el.addClass('wait');
$(".jqueryFileTree.start").remove();
data = {
dir: dir,
onlyFolders: options.onlyFolders,
onlyFiles: options.onlyFiles,
multiSelect: options.multiSelect
};
handleResult = function(result) {
var li;
$el.find('.start').html('');
$el.removeClass('wait').append(result);
if (options.root === dir) {
$el.find('UL:hidden').show(typeof callback !== "undefined" && callback !== null);
} else {
if (jQuery.easing[options.expandEasing] === void 0) {
console.log('Easing library not loaded. Include jQueryUI or 3rd party lib.');
options.expandEasing = 'swing';
}
$el.find('UL:hidden').slideDown({
duration: options.expandSpeed,
easing: options.expandEasing,
start: function() {
return _this._trigger('filetreeexpand', _this.data);
},
complete: finishCallback
});
}
li = $('[rel="' + decodeURIComponent(dir) + '"]').parent();
if (options.multiSelect && li.children('input').is(':checked')) {
li.find('ul li input').each(function() {
$(this).prop('checked', true);
return $(this).parent().addClass('selected');
});
}
return false;
};
handleFail = function() {
$el.find('.start').html('');
$el.removeClass('wait').append("<p>" + options.errorMessage + "</p>");
return false;
};
if (typeof options.script === 'function') {
result = options.script(data);
if (typeof result === 'string' || result instanceof jQuery) {
return handleResult(result);
} else {
return handleFail();
}
} else {
return $.ajax({
url: options.script,
type: 'POST',
dataType: 'HTML',
data: data
}).done(function(result) {
return handleResult(result);
}).fail(function() {
return handleFail();
});
}
};
FileTree.prototype._trigger = function(eventType, data) {
var $el;
$el = this.jqft.container;
return $el.triggerHandler(eventType, data);
};
return FileTree;
})();
return $.fn.extend({
fileTree: function(args, callback) {
return this.each(function() {
var $this, data;
$this = $(this);
data = $this.data('fileTree');
if (!data) {
$this.data('fileTree', (data = new FileTree(this, args, callback)));
}
if (typeof args === 'string') {
return data[option].apply(data);
}
});
}
});
})(window.jQuery, window);

View File

@ -0,0 +1 @@
var bind=function(a,b){return function(){return a.apply(b,arguments)}};(function(b,a){var c;c=(function(){function d(g,e,j){this.onEvent=bind(this.onEvent,this);var f,i,h;f=b(g);i=this;h={root:"/",script:"/files/filetree",folderEvent:"click",expandSpeed:500,collapseSpeed:500,expandEasing:"swing",collapseEasing:"swing",multiFolder:true,loadMessage:"Loading...",errorMessage:"Unable to get file tree information",multiSelect:false,onlyFolders:false,onlyFiles:false,preventLinkAction:false};this.jqft={container:f};this.options=b.extend(h,e);this.callback=j;this.data={};f.html('<ul class="jqueryFileTree start"><li class="wait">'+this.options.loadMessage+"<li></ul>");i.showTree(f,escape(this.options.root),function(){return i._trigger("filetreeinitiated",{})});f.delegate("li a",this.options.folderEvent,i.onEvent)}d.prototype.onEvent=function(h){var f,k,j,i,e,g;f=b(h.target);e=this.options;i=this.jqft;k=this;j=this.callback;k.data={};k.data.li=f.closest("li");k.data.type=(g=k.data.li.hasClass("directory"))!=null?g:{directory:"file"};k.data.value=f.text();k.data.rel=f.prop("rel");k.data.container=i.container;if(e.preventLinkAction){h.preventDefault()}if(f.parent().hasClass("directory")){k.jqft.container.find("LI.directory").removeClass("selected");f.parent().addClass("selected");if(f.parent().hasClass("collapsed")){if(!e.multiFolder){f.parent().parent().find("UL").slideUp({duration:e.collapseSpeed,easing:e.collapseEasing});f.parent().parent().find("LI.directory").removeClass("expanded").addClass("collapsed")}f.parent().removeClass("collapsed").addClass("expanded");f.parent().find("UL").remove();return k.showTree(f.parent(),f.attr("rel"),function(){k._trigger("filetreeexpanded",k.data);return j!=null})}else{return f.parent().find("UL").slideUp({duration:e.collapseSpeed,easing:e.collapseEasing,start:function(){return k._trigger("filetreecollapse",k.data)},complete:function(){f.parent().removeClass("expanded").addClass("collapsed");k._trigger("filetreecollapsed",k.data);return j!=null}})}}else{if(!e.multiSelect){i.container.find("li").removeClass("selected");f.parent().addClass("selected")}else{if(f.parent().find("input").is(":checked")){f.parent().find("input").prop("checked",false);f.parent().removeClass("selected")}else{f.parent().find("input").prop("checked",true);f.parent().addClass("selected")}}k._trigger("filetreeclicked",k.data);return typeof j==="function"?j(f.attr("rel")):void 0}};d.prototype.showTree=function(f,g,n){var m,i,h,e,j,l,k;m=b(f);l=this.options;i=this;m.addClass("wait");b(".jqueryFileTree.start").remove();h={dir:g,onlyFolders:l.onlyFolders,onlyFiles:l.onlyFiles,multiSelect:l.multiSelect};j=function(p){var o;m.find(".start").html("");m.removeClass("wait").append(p);if(l.root===g){m.find("UL:hidden").show(typeof callback!=="undefined"&&callback!==null)}else{if(jQuery.easing[l.expandEasing]===void 0){console.log("Easing library not loaded. Include jQueryUI or 3rd party lib.");l.expandEasing="swing"}m.find("UL:hidden").slideDown({duration:l.expandSpeed,easing:l.expandEasing,start:function(){return i._trigger("filetreeexpand",i.data)},complete:n})}o=b('[rel="'+decodeURIComponent(g)+'"]').parent();if(l.multiSelect&&o.children("input").is(":checked")){o.find("ul li input").each(function(){b(this).prop("checked",true);return b(this).parent().addClass("selected")})}return false};e=function(){m.find(".start").html("");m.removeClass("wait").append("<p>"+l.errorMessage+"</p>");return false};if(typeof l.script==="function"){k=l.script(h);if(typeof k==="string"||k instanceof jQuery){return j(k)}else{return e()}}else{return b.ajax({url:l.script,type:"POST",dataType:"HTML",data:h}).done(function(o){return j(o)}).fail(function(){return e()})}};d.prototype._trigger=function(f,g){var e;e=this.jqft.container;return e.triggerHandler(f,g)};return d})();return b.fn.extend({fileTree:function(d,e){return this.each(function(){var g,f;g=b(this);f=g.data("fileTree");if(!f){g.data("fileTree",(f=new c(this,d,e)))}if(typeof d==="string"){return f[option].apply(f)}})}})})(window.jQuery,window);