Convert project to webpack
This commit is contained in:
46
src/scripts/app.js
Normal file
46
src/scripts/app.js
Normal file
@@ -0,0 +1,46 @@
|
||||
//jQuery
|
||||
import './jquery.helpers.js';
|
||||
window.lightbox = require('./lightbox.js');
|
||||
|
||||
//Simplebar
|
||||
import SimpleBar from 'simplebar';
|
||||
window.SimpleBar = SimpleBar;
|
||||
import 'simplebar/dist/simplebar.css';
|
||||
import ResizeObserver from 'resize-observer-polyfill';
|
||||
window.ResizeObserver = ResizeObserver;
|
||||
|
||||
//Common
|
||||
import { copyArray, getElem, setElem, getDragPosition, copyTextToClipboard } from './common.js';
|
||||
window.copyArray = copyArray;
|
||||
window.getElem = getElem;
|
||||
window.setElem = setElem;
|
||||
window.getDragPosition = getDragPosition;
|
||||
window.copyTextToClipboard = copyTextToClipboard;
|
||||
|
||||
//Masks
|
||||
import Spot from './spot.js';
|
||||
import Project from './page.project.js';
|
||||
import Upload from './page.upload.js';
|
||||
import Admin from './page.admin.js';
|
||||
|
||||
require('./../styles/spot.scss');
|
||||
|
||||
import './../../masks/index.html';
|
||||
import LogoText from '../images/logo_black.png';
|
||||
import Logo from '../images/spot-logo-only.svg';
|
||||
console.log(Logo);
|
||||
|
||||
$.get('index.php?a=params').done((e)=>{
|
||||
let oSpot = new Spot(e.data);
|
||||
|
||||
let oProject = new Project(oSpot);
|
||||
oSpot.addPage('project', oProject);
|
||||
|
||||
let oUpload = new Upload(oSpot);
|
||||
oSpot.addPage('upload', oUpload);
|
||||
|
||||
let oAdmin = new Admin(oSpot);
|
||||
oSpot.addPage('admin', oAdmin);
|
||||
|
||||
oSpot.init();
|
||||
});
|
||||
68
src/scripts/common.js
Normal file
68
src/scripts/common.js
Normal file
@@ -0,0 +1,68 @@
|
||||
/* Common Functions */
|
||||
|
||||
export function copyArray(asArray)
|
||||
{
|
||||
return asArray.slice(0); //trick to copy array
|
||||
}
|
||||
|
||||
export function getElem(aoAnchor, asPath)
|
||||
{
|
||||
return (typeof asPath == 'object' && asPath.length > 1)?getElem(aoAnchor[asPath.shift()], asPath):aoAnchor[(typeof asPath == 'object')?asPath.shift():asPath];
|
||||
}
|
||||
|
||||
export function setElem(aoAnchor, asPath, oValue)
|
||||
{
|
||||
var asTypes = {boolean:false, string:'', integer:0, int:0, array:[], object:{}};
|
||||
if(typeof asPath == 'object' && asPath.length > 1)
|
||||
{
|
||||
var nextlevel = asPath.shift();
|
||||
if(!(nextlevel in aoAnchor)) aoAnchor[nextlevel] = {}; //Creating a new level
|
||||
if(typeof aoAnchor[nextlevel] !== 'object') debug('Error - setElem() : Already existing path at level "'+nextlevel+'". Cancelling setElem() action');
|
||||
return setElem(aoAnchor[nextlevel], asPath, oValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
var sKey = (typeof asPath == 'object')?asPath.shift():asPath;
|
||||
return aoAnchor[sKey] = (!(sKey in aoAnchor) && (oValue in asTypes))?asTypes[oValue]:oValue;
|
||||
}
|
||||
}
|
||||
|
||||
export function getDragPosition(oEvent) {
|
||||
let bMouse = oEvent.type.includes('mouse');
|
||||
return {
|
||||
x: bMouse?oEvent.pageX:oEvent.touches[0].clientX,
|
||||
y: bMouse?oEvent.pageY:oEvent.touches[0].clientY
|
||||
};
|
||||
}
|
||||
|
||||
export function copyTextToClipboard(text) {
|
||||
if(!navigator.clipboard) {
|
||||
var textArea = document.createElement('textarea');
|
||||
textArea.value = text;
|
||||
|
||||
// Avoid scrolling to bottom
|
||||
textArea.style.top = '0';
|
||||
textArea.style.left = '0';
|
||||
textArea.style.position = 'fixed';
|
||||
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
|
||||
try {
|
||||
var successful = document.execCommand('copy');
|
||||
if(!successful) console.error('Fallback: Oops, unable to copy', text);
|
||||
} catch (err) {
|
||||
console.error('Fallback: Oops, unable to copy', err);
|
||||
}
|
||||
|
||||
document.body.removeChild(textArea);
|
||||
return;
|
||||
}
|
||||
navigator.clipboard.writeText(text).then(
|
||||
function() {},
|
||||
function(err) {
|
||||
console.error('Async: Could not copy text: ', err);
|
||||
}
|
||||
);
|
||||
}
|
||||
129
src/scripts/jquery.helpers.js
Normal file
129
src/scripts/jquery.helpers.js
Normal file
@@ -0,0 +1,129 @@
|
||||
$.prototype.addInput = function(sType, sName, sValue, aoEvents) {
|
||||
aoEvents = aoEvents || [];
|
||||
let $Input = $('<input>', {type: sType, name: sName, value: sValue}).data('old_value', sValue);
|
||||
$.each(aoEvents, (iIndex, aoEvent) => {
|
||||
$Input.on(aoEvent.on, aoEvent.callback);
|
||||
});
|
||||
return $(this).append($Input);
|
||||
};
|
||||
|
||||
$.prototype.addButton = function(sIcon, sText, sName, fOnClick, sClass)
|
||||
{
|
||||
sText = sText || '';
|
||||
sClass = sClass || '';
|
||||
var $Btn = $('<button>', {name: sName, 'class':sClass})
|
||||
.addIcon('fa-'+sIcon, (sText != ''))
|
||||
.append(sText)
|
||||
.click(fOnClick);
|
||||
|
||||
return $(this).append($Btn);
|
||||
};
|
||||
|
||||
$.prototype.addIcon = function(sIcon, bMargin, sStyle)
|
||||
{
|
||||
bMargin = bMargin || false;
|
||||
sStyle = sStyle || '';
|
||||
return $(this).append($('<i>', {'class':'fa'+sStyle+' '+sIcon+(bMargin?' push':'')}));
|
||||
};
|
||||
|
||||
$.prototype.defaultVal = function(sDefaultValue)
|
||||
{
|
||||
$(this)
|
||||
.data('default_value', sDefaultValue)
|
||||
.val(sDefaultValue)
|
||||
.addClass('defaultText')
|
||||
.focus(function()
|
||||
{
|
||||
var $This = $(this);
|
||||
if($This.val() == $This.data('default_value')) $This.val('').removeClass('defaultText');
|
||||
})
|
||||
.blur(function()
|
||||
{
|
||||
var $This = $(this);
|
||||
if($This.val() == '') $This.val($This.data('default_value')).addClass('defaultText');
|
||||
});
|
||||
};
|
||||
|
||||
$.prototype.checkForm = function(sSelector)
|
||||
{
|
||||
sSelector = sSelector || 'input[type="text"], textarea';
|
||||
var $This = $(this);
|
||||
var bOk = true;
|
||||
$This.find(sSelector).each(function()
|
||||
{
|
||||
$This = $(this);
|
||||
bOk = bOk && $This.val()!='' && $This.val()!=$This.data('default_value');
|
||||
});
|
||||
return bOk;
|
||||
};
|
||||
|
||||
$.prototype.cascadingDown = function(sDuration)
|
||||
{
|
||||
return $(this).slideDown(sDuration, function(){$(this).next().cascadingDown(sDuration);});
|
||||
};
|
||||
|
||||
$.prototype.hoverSwap = function(sDefault, sHover)
|
||||
{
|
||||
return $(this)
|
||||
.data('default', sDefault)
|
||||
.data('hover', sHover)
|
||||
.hover(function(){
|
||||
var $This = $(this),
|
||||
sHover = $This.data('hover');
|
||||
sDefault = $This.data('default');
|
||||
|
||||
if(sDefault!='' && sHover != '') {
|
||||
$This.fadeOut('fast', function() {
|
||||
var $This = $(this);
|
||||
$This.text((sDefault==$This.text())?sHover:sDefault).fadeIn('fast');
|
||||
});
|
||||
}
|
||||
})
|
||||
.text(sDefault);
|
||||
};
|
||||
|
||||
$.prototype.onSwipe = function(fOnStart, fOnMove, fOnEnd){
|
||||
return $(this)
|
||||
.on('dragstart', (e) => {
|
||||
e.preventDefault();
|
||||
})
|
||||
.on('mousedown touchstart', (e) => {
|
||||
var $This = $(this);
|
||||
var oPos = getDragPosition(e);
|
||||
$This.data('x-start', oPos.x);
|
||||
$This.data('y-start', oPos.y);
|
||||
$This.data('x-move', oPos.x);
|
||||
$This.data('y-move', oPos.y);
|
||||
$This.data('moving', true).addClass('moving');
|
||||
fOnStart({
|
||||
xStart: $This.data('x-start'),
|
||||
yStart: $This.data('y-start')
|
||||
});
|
||||
})
|
||||
.on('touchmove mousemove', (e) => {
|
||||
var $This = $(this);
|
||||
if($This.data('moving')) {
|
||||
var oPos = getDragPosition(e);
|
||||
$This.data('x-move', oPos.x);
|
||||
$This.data('y-move', oPos.y);
|
||||
fOnMove({
|
||||
xStart: $This.data('x-start'),
|
||||
yStart: $This.data('y-start'),
|
||||
xMove: $This.data('x-move'),
|
||||
yMove: $This.data('y-move')
|
||||
});
|
||||
}
|
||||
})
|
||||
.on('mouseup mouseleave touchend', (e) => {
|
||||
var $This = $(this);
|
||||
if($This.data('moving')) {
|
||||
$This.data('moving', false).removeClass('moving');
|
||||
fOnEnd({
|
||||
xStart: $This.data('x-start'),
|
||||
yStart: $This.data('y-start'),
|
||||
xEnd: $This.data('x-move'),
|
||||
yEnd: $This.data('y-move')
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
14
src/scripts/leaflet.helpers.js
Normal file
14
src/scripts/leaflet.helpers.js
Normal file
@@ -0,0 +1,14 @@
|
||||
/* Additional Leaflet functions */
|
||||
|
||||
L.Map.include({
|
||||
setOffsetView: function (iOffsetRatioX, oCenter, iZoomLevel) {
|
||||
var oCenter = (typeof oCenter == 'object')?$.extend({}, oCenter):this.getCenter();
|
||||
iZoomLevel = iZoomLevel || this.getZoom();
|
||||
|
||||
var oBounds = this.getBounds();
|
||||
var iOffsetX = (oBounds.getEast() - oBounds.getWest()) * iOffsetRatioX / ( 2 * Math.pow(2, iZoomLevel - this.getZoom()));
|
||||
oCenter.lng = oCenter.lng - iOffsetX;
|
||||
|
||||
this.setView(oCenter, iZoomLevel);
|
||||
}
|
||||
});
|
||||
@@ -62,6 +62,7 @@
|
||||
sanitizeTitle: false
|
||||
, hasVideo: true
|
||||
, onMediaChange: (oMedia) => {}
|
||||
, onClosing: () => {}
|
||||
};
|
||||
|
||||
Lightbox.prototype.option = function(options) {
|
||||
@@ -477,6 +478,7 @@
|
||||
var $hasVideoNav = this.$container.hasClass('lb-video-nav');
|
||||
switch(self.album[imageNumber].type) {
|
||||
case 'video':
|
||||
self.$image.removeAttr('src');
|
||||
this.$video.on('loadedmetadata', function(){
|
||||
self.album[imageNumber].width = this.videoWidth;
|
||||
self.album[imageNumber].height = this.videoHeight;
|
||||
@@ -489,7 +491,7 @@
|
||||
if(!$hasVideoNav) this.$container.addClass('lb-video-nav');
|
||||
break;
|
||||
case 'image':
|
||||
this.$video.attr('src', '');
|
||||
this.$video.trigger('pause').removeAttr('src');
|
||||
if($hasVideoNav) this.$container.removeClass('lb-video-nav');
|
||||
|
||||
// When image to show is preloaded, we send the width and height to sizeContainer()
|
||||
@@ -718,11 +720,10 @@
|
||||
if(this.options.hasVideo) {
|
||||
var $lbContainer = this.$lightbox.find('.lb-container');
|
||||
var $hasVideoNav = $lbContainer.hasClass('lb-video-nav');
|
||||
this.$video.attr('src', '');
|
||||
this.$video.trigger('pause').removeAttr('src');
|
||||
|
||||
if($hasVideoNav) $lbContainer.removeClass('lb-video-nav');
|
||||
}
|
||||
oSpot.flushHash();
|
||||
|
||||
$(window).off('resize', this.sizeOverlay);
|
||||
this.$nav.off('mousewheel');
|
||||
@@ -732,6 +733,8 @@
|
||||
if (this.options.disableScrolling) {
|
||||
$('body').removeClass('lb-disable-scrolling');
|
||||
}
|
||||
|
||||
this.options.onClosing();
|
||||
};
|
||||
|
||||
return new Lightbox();
|
||||
|
||||
165
src/scripts/page.admin.js
Normal file
165
src/scripts/page.admin.js
Normal file
@@ -0,0 +1,165 @@
|
||||
export default class Admin {
|
||||
|
||||
constructor(oSpot) {
|
||||
this.spot = oSpot;
|
||||
}
|
||||
|
||||
pageInit(asHash) {
|
||||
this.spot.get('admin_get', (asElemTypes) => {this.setProjects(asElemTypes);});
|
||||
$('#new').addButton('new', this.spot.lang('new_project'), 'new', () => {this.createProject();});
|
||||
$('#toolbox').addButton('refresh', this.spot.lang('update_project'), 'refresh', () => {this.updateProject();});
|
||||
}
|
||||
|
||||
onFeedback(sType, sMsg, asContext) {
|
||||
delete asContext.a;
|
||||
delete asContext.t;
|
||||
sMsg += ' (';
|
||||
$.each(asContext, function(sKey, sElem) {
|
||||
sMsg += sKey+'='+sElem+' / ' ;
|
||||
});
|
||||
sMsg = sMsg.slice(0, -3)+')';
|
||||
$('#feedback').append($('<p>', {'class': sType}).text(sMsg));
|
||||
}
|
||||
|
||||
setProjects(asElemTypes) {
|
||||
let aoEvents = [
|
||||
{
|
||||
on:'change',
|
||||
callback: (oEvent) => {this.commit(oEvent);}
|
||||
},
|
||||
{
|
||||
on:'keyup',
|
||||
callback: (oEvent) => {this.waitAndCommit(oEvent);}
|
||||
}
|
||||
];
|
||||
let aoChangeEvent = [aoEvents[0]];
|
||||
|
||||
$.each(asElemTypes, (sElemType, aoElems) => {
|
||||
$.each(aoElems, (iKey, oElem) => {
|
||||
var sElemId = sElemType+'_'+oElem.id;
|
||||
var bNew = ($('#'+sElemId).length == 0);
|
||||
|
||||
var $Elem = (bNew?$('<tr>', {'id': sElemId}):$('#'+sElemId))
|
||||
.data('type', sElemType)
|
||||
.data('id', oElem.id);
|
||||
|
||||
if(oElem.del) $Elem.remove();
|
||||
else if(!bNew) {
|
||||
$Elem.find('input').each((iKey, oInput) => {
|
||||
var $Input = $(oInput);
|
||||
if($Input.attr('name') in oElem && $Input.attr('type')!='date') $Input.val(oElem[$Input.attr('name')]);
|
||||
});
|
||||
}
|
||||
else {
|
||||
$Elem.append($('<td>').text(oElem.id || ''));
|
||||
switch(sElemType) {
|
||||
case 'project':
|
||||
$Elem
|
||||
.append($('<td>').addInput('text', 'name', oElem.name, aoEvents))
|
||||
.append($('<td>', {'class': 'mode'}).text(oElem.mode))
|
||||
.append($('<td>').addInput('text', 'codename', oElem.codename, aoEvents))
|
||||
.append($('<td>').addInput('date', 'active_from', oElem.active_from, aoChangeEvent))
|
||||
.append($('<td>').addInput('date', 'active_to', oElem.active_to, aoChangeEvent))
|
||||
.append($('<td>').addButton('close fa-lg', '', 'del_proj', (oEvent)=>{this.del(oEvent);}));
|
||||
break;
|
||||
case 'feed':
|
||||
$Elem
|
||||
.append($('<td>').addInput('text', 'ref_feed_id', oElem.ref_feed_id, aoEvents))
|
||||
.append($('<td>').addInput('number', 'id_spot', oElem.id_spot, aoEvents))
|
||||
.append($('<td>').addInput('number', 'id_project', oElem.id_project, aoEvents))
|
||||
.append($('<td>').text(oElem.name))
|
||||
.append($('<td>').text(oElem.status))
|
||||
.append($('<td>').text(oElem.last_update))
|
||||
.append($('<td>').addButton('close fa-lg', '', 'del_feed', (oEvent)=>{this.del(oEvent);}));
|
||||
break;
|
||||
case 'spot':
|
||||
$Elem
|
||||
.append($('<td>').text(oElem.ref_spot_id))
|
||||
.append($('<td>').text(oElem.name))
|
||||
.append($('<td>').text(oElem.model))
|
||||
break;
|
||||
case 'user':
|
||||
$Elem
|
||||
.append($('<td>').text(oElem.name))
|
||||
.append($('<td>').text(oElem.language))
|
||||
.append($('<td>').text(oElem.timezone))
|
||||
.append($('<td>').addInput('number', 'clearance', oElem.clearance, aoEvents))
|
||||
break;
|
||||
}
|
||||
|
||||
$Elem.appendTo($('#'+sElemType+'_section').find('table tbody'));
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
createProject() {
|
||||
this.spot.get('admin_new', this.setProjects);
|
||||
}
|
||||
|
||||
updateProject() {
|
||||
this.spot.get(
|
||||
'update_project',
|
||||
(asData, sMsg) => {this.spot.onFeedback('success', sMsg, {'update':'project'});},
|
||||
{},
|
||||
(sMsg) => {this.spot.onFeedback('error', sMsg, {'update':'project'});}
|
||||
);
|
||||
}
|
||||
|
||||
commit(oEvent) {
|
||||
let $Elem = $(oEvent.currentTarget);
|
||||
if(typeof this.spot.tmp('wait') != 'undefined') clearTimeout(this.spot.tmp('wait'));
|
||||
|
||||
var sOldVal = $Elem.data('old_value');
|
||||
var sNewVal = $Elem.val();
|
||||
if(sOldVal != sNewVal) {
|
||||
$Elem.data('old_value', sNewVal);
|
||||
|
||||
var $Record = $Elem.closest('tr');
|
||||
var asInputs = {
|
||||
type: $Record.data('type'),
|
||||
id: $Record.data('id'),
|
||||
field: $Elem.attr('name'),
|
||||
value: sNewVal
|
||||
};
|
||||
|
||||
this.spot.get(
|
||||
'admin_set',
|
||||
(asData) => {
|
||||
this.spot.onFeedback('success', this.spot.lang('admin_save_success'), asInputs);
|
||||
this.setProjects(asData);
|
||||
},
|
||||
asInputs,
|
||||
(sError) => {
|
||||
$Elem.data('old_value', sOldVal);
|
||||
this.spot.onFeedback('error', sError, asInputs);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
waitAndCommit(oEvent) {
|
||||
if(typeof this.spot.tmp('wait') != 'undefined') clearTimeout(this.spot.tmp('wait'));
|
||||
this.spot.tmp('wait', setTimeout(() => {this.commit(oEvent);}, 2000));
|
||||
}
|
||||
|
||||
del(oEvent) {
|
||||
var $Record = $(oEvent.currentTarget).closest('tr');
|
||||
var asInputs = {
|
||||
type: $Record.data('type'),
|
||||
id: $Record.data('id')
|
||||
};
|
||||
|
||||
this.spot.get(
|
||||
'admin_del',
|
||||
(asData) => {
|
||||
this.spot.onFeedback('success', this.spot.lang('admin_save_success'), asInputs);
|
||||
this.setProjects(asData);
|
||||
},
|
||||
asInputs,
|
||||
(sError) => {
|
||||
this.spot.onFeedback('error', sError, asInputs);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
1168
src/scripts/page.project.js
Normal file
1168
src/scripts/page.project.js
Normal file
File diff suppressed because it is too large
Load Diff
77
src/scripts/page.upload.js
Normal file
77
src/scripts/page.upload.js
Normal file
@@ -0,0 +1,77 @@
|
||||
import "blueimp-file-upload/js/vendor/jquery.ui.widget.js";
|
||||
import "blueimp-file-upload/js/jquery.iframe-transport.js";
|
||||
import "blueimp-file-upload/js/jquery.fileupload.js";
|
||||
//import "blueimp-file-upload/js/jquery.fileupload-image.js";
|
||||
|
||||
export default class Upload {
|
||||
|
||||
constructor(oSpot) {
|
||||
this.spot = oSpot;
|
||||
}
|
||||
|
||||
pageInit (asHash) {
|
||||
let asProject = this.spot.vars(['projects', this.spot.vars('default_project_codename')]);
|
||||
this.spot.tmp('status-box', $('#status'));
|
||||
if(asProject.editable) {
|
||||
$('#fileupload')
|
||||
.attr('data-url', this.spot.getActionLink('upload'))
|
||||
.fileupload({
|
||||
dataType: 'json',
|
||||
formData: {t: this.spot.consts.timezone},
|
||||
acceptFileTypes: /(\.|\/)(gif|jpe?g|png|mov)$/i,
|
||||
done: (e, asData) => {
|
||||
$.each(asData.result.files, (iKey, oFile) => {
|
||||
let bError = ('error' in oFile);
|
||||
|
||||
//Feedback
|
||||
this.addStatus(bError?oFile.error:(this.spot.lang('upload_success', [oFile.name])));
|
||||
|
||||
//Comments
|
||||
if(!bError) this.addCommentBox(oFile.id, oFile.thumbnail);
|
||||
});
|
||||
},
|
||||
progressall: (e, data) => {
|
||||
let progress = parseInt(data.loaded / data.total * 100, 10);
|
||||
$('#progress .bar').css('width', progress+'%');
|
||||
}
|
||||
});
|
||||
}
|
||||
else this.addStatus(this.spot.lang('upload_mode_archived', [asProject.name]), true);
|
||||
}
|
||||
|
||||
addCommentBox(iMediaId, sThumbnailPath) {
|
||||
$('#comments').append($('<div>', {'class':'comment'})
|
||||
.append($('<img>', {'class':'thumb', 'src':sThumbnailPath}))
|
||||
.append($('<form>')
|
||||
.append($('<input>', {'class':'content', 'name':'content', 'type':'text'}))
|
||||
.append($('<input>', {'class':'id', 'name':'id', 'type':'hidden', 'value':iMediaId}))
|
||||
.append($('<button>', {'class':'save', 'type':'button'})
|
||||
.on('click', (oEvent) => {
|
||||
var $Form = $(oEvent.currentTarget).parent();
|
||||
this.spot.get(
|
||||
'add_comment',
|
||||
(asData) => {
|
||||
this.addStatus(this.spot.lang('media_comment_update', asData.filename));
|
||||
},
|
||||
{
|
||||
id: $Form.find('.id').val(),
|
||||
content: $Form.find('.content').val()
|
||||
},
|
||||
(sMsgId) => {
|
||||
this.addStatus(this.spot.lang(sMsgId));
|
||||
}
|
||||
);
|
||||
})
|
||||
.text(this.spot.lang('save'))
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
addStatus(sMsg, bClear) {
|
||||
bClear = bClear || false;
|
||||
if(bClear) this.spot.tmp('status-box').empty();
|
||||
|
||||
this.spot.tmp('status-box').append($('<p>').text(sMsg));
|
||||
}
|
||||
}
|
||||
@@ -1,109 +1,96 @@
|
||||
function Spot(asGlobals)
|
||||
{
|
||||
self = this;
|
||||
this.consts = asGlobals.consts;
|
||||
this.consts.hash_sep = '-';
|
||||
this.consts.title = 'Spotty';
|
||||
this.consts.default_page = 'project';
|
||||
this.consts.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || this.consts.default_timezone;
|
||||
export default class Spot {
|
||||
|
||||
constructor(asGlobals) {
|
||||
this.consts = asGlobals.consts;
|
||||
this.consts.hash_sep = '-';
|
||||
this.consts.title = 'Spotty';
|
||||
this.consts.default_page = 'project';
|
||||
this.consts.timezone = Intl.DateTimeFormat().resolvedOptions().timeZone || this.consts.default_timezone;
|
||||
|
||||
this.pages = {};
|
||||
|
||||
//Variables & constants from php
|
||||
this.vars('tmp', 'object');
|
||||
this.vars('page', 'string');
|
||||
$.each(asGlobals.vars, (sKey, oValue) => {this.vars(sKey, oValue)});
|
||||
|
||||
//page elem
|
||||
this.elem = {};
|
||||
}
|
||||
|
||||
/* Initialization */
|
||||
|
||||
this.init = function()
|
||||
{
|
||||
//Variables & constants from php
|
||||
self.vars('tmp', 'object');
|
||||
self.vars('page', 'string');
|
||||
self.updateVars(asGlobals.vars);
|
||||
|
||||
//page elem
|
||||
self.elem = {};
|
||||
self.elem.container = $('#container');
|
||||
self.elem.main = $('#main');
|
||||
|
||||
self.resetTmpFunctions();
|
||||
init() {
|
||||
this.elem.container = $('#container');
|
||||
this.elem.main = $('#main');
|
||||
|
||||
//On Key down
|
||||
$('html').on('keydown', function(oEvent){self.onKeydown(oEvent);});
|
||||
$('html').on('keydown', (oEvent) => {this.onKeydown(oEvent);});
|
||||
|
||||
//on window resize
|
||||
$(window).on('resize', function(){self.onResize();});
|
||||
|
||||
//Setup menu
|
||||
//self.initMenu();
|
||||
$(window).on('resize', () => {this.onResize();});
|
||||
|
||||
//Hash management
|
||||
$(window)
|
||||
.bind('hashchange', self.onHashChange)
|
||||
.on('hashchange', () => {this.onHashChange();})
|
||||
.trigger('hashchange');
|
||||
};
|
||||
|
||||
this.updateVars = function(asVars)
|
||||
{
|
||||
$.each(asVars, function(sKey, oValue){self.vars(sKey, oValue)});
|
||||
};
|
||||
}
|
||||
|
||||
/* Variable Management */
|
||||
|
||||
this.vars = function(oVarName, oValue)
|
||||
{
|
||||
vars(oVarName, oValue) {
|
||||
var asVarName = (typeof oVarName == 'object')?oVarName:[oVarName];
|
||||
|
||||
//Set, name & type / default value (init)
|
||||
if(typeof oValue !== 'undefined') setElem(self.vars, copyArray(asVarName), oValue);
|
||||
if(typeof oValue !== 'undefined') setElem(this.vars, copyArray(asVarName), oValue);
|
||||
|
||||
//Get, only name parameter
|
||||
return getElem(self.vars, asVarName);
|
||||
};
|
||||
return getElem(this.vars, asVarName);
|
||||
}
|
||||
|
||||
this.tmp = function(sVarName, oValue)
|
||||
{
|
||||
tmp(sVarName, oValue) {
|
||||
var asVarName = (typeof sVarName == 'object')?sVarName:[sVarName];
|
||||
asVarName.unshift('tmp');
|
||||
return self.vars(asVarName, oValue);
|
||||
};
|
||||
return this.vars(asVarName, oValue);
|
||||
}
|
||||
|
||||
/* Interface with server */
|
||||
|
||||
this.get = function(sAction, fOnSuccess, oVars, fOnError, fonProgress)
|
||||
{
|
||||
if(!oVars) oVars = {};
|
||||
get(sAction, fOnSuccess, oVars, fOnError, fonProgress) {
|
||||
oVars = oVars || {};
|
||||
fOnError = fOnError || function(sError) {console.log(sError);};
|
||||
fonProgress = fonProgress || function(sState){};
|
||||
fonProgress('start');
|
||||
|
||||
oVars['a'] = sAction;
|
||||
oVars['t'] = self.consts.timezone;
|
||||
oVars['t'] = this.consts.timezone;
|
||||
return $.ajax(
|
||||
{
|
||||
url: self.consts.process_page,
|
||||
url: this.consts.process_page,
|
||||
data: oVars,
|
||||
dataType: 'json'
|
||||
})
|
||||
.done(function(oData)
|
||||
{
|
||||
.done((oData) => {
|
||||
fonProgress('done');
|
||||
if(oData.desc.substr(0, self.consts.lang_prefix.length)==self.consts.lang_prefix) oData.desc = self.lang(oData.desc.substr(5));
|
||||
if(oData.desc.substr(0, this.consts.lang_prefix.length)==this.consts.lang_prefix) oData.desc = this.lang(oData.desc.substr(5));
|
||||
|
||||
if(oData.result==self.consts.error) fOnError(oData.desc);
|
||||
if(oData.result==this.consts.error) fOnError(oData.desc);
|
||||
else fOnSuccess(oData.data, oData.desc);
|
||||
})
|
||||
.fail(function(jqXHR, textStatus, errorThrown)
|
||||
{
|
||||
.fail((jqXHR, textStatus, errorThrown) => {
|
||||
fonProgress('fail');
|
||||
fOnError(textStatus+' '+errorThrown);
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
this.lang = function(sKey, asParams) {
|
||||
var sParamType = $.type(asParams);
|
||||
if(sParamType == 'undefined') asParams = [];
|
||||
else if($.type(asParams) != 'array') asParams = [asParams];
|
||||
lang(sKey, asParams) {
|
||||
asParams = asParams || [];
|
||||
if(typeof asParams == 'string') asParams = [asParams];
|
||||
|
||||
var sLang = '';
|
||||
|
||||
if(sKey in self.consts.lang) {
|
||||
sLang = self.consts.lang[sKey];
|
||||
for(i in asParams) sLang = sLang.replace('$'+i, asParams[i]);
|
||||
if(sKey in this.consts.lang) {
|
||||
sLang = this.consts.lang[sKey];
|
||||
for(let i in asParams) sLang = sLang.replace('$'+i, asParams[i]);
|
||||
}
|
||||
else {
|
||||
console.log('missing translation: '+sKey);
|
||||
@@ -111,141 +98,153 @@ function Spot(asGlobals)
|
||||
}
|
||||
|
||||
return sLang;
|
||||
};
|
||||
}
|
||||
|
||||
/* Page Switch - Trigger & Event catching */
|
||||
|
||||
this.onHashChange = function()
|
||||
{
|
||||
var asHash = self.getHash();
|
||||
if(asHash.hash !='' && asHash.page != '') self.switchPage(asHash); //page switching
|
||||
else if(self.vars('page')=='') self.setHash(self.consts.default_page); //first page
|
||||
};
|
||||
onHashChange() {
|
||||
var asHash = this.getHash();
|
||||
if(asHash.hash !='' && asHash.page != '') this.switchPage(asHash); //page switching
|
||||
else if(this.vars('page')=='') this.setHash(this.consts.default_page); //first page
|
||||
}
|
||||
|
||||
this.getHash = function()
|
||||
{
|
||||
var sHash = self.hash();
|
||||
var asHash = sHash.split(self.consts.hash_sep);
|
||||
getHash() {
|
||||
var sHash = this.hash();
|
||||
var asHash = sHash.split(this.consts.hash_sep);
|
||||
var sPage = asHash.shift() || '';
|
||||
return {hash:sHash, page:sPage, items:asHash};
|
||||
};
|
||||
}
|
||||
|
||||
this.setHash = function(sPage, asItems, bReboot)
|
||||
{
|
||||
setHash(sPage, asItems, bReboot) {
|
||||
bReboot = bReboot || false;
|
||||
sPage = sPage || '';
|
||||
asItems = asItems || [];
|
||||
if(typeof asItems == 'string') asItems = [asItems];
|
||||
if(sPage != '')
|
||||
{
|
||||
var sItems = (asItems.length > 0)?self.consts.hash_sep+asItems.join(self.consts.hash_sep):'';
|
||||
self.hash(sPage+sItems, bReboot);
|
||||
}
|
||||
};
|
||||
|
||||
this.hash = function(hash, bReboot)
|
||||
{
|
||||
if(sPage != '') {
|
||||
var sItems = (asItems.length > 0)?this.consts.hash_sep+asItems.join(this.consts.hash_sep):'';
|
||||
this.hash(sPage+sItems, bReboot);
|
||||
}
|
||||
}
|
||||
|
||||
hash(hash, bReboot) {
|
||||
bReboot = bReboot || false;
|
||||
if(!hash) return window.location.hash.slice(1);
|
||||
else window.location.hash = '#'+hash;
|
||||
|
||||
if(bReboot) location.reload();
|
||||
};
|
||||
}
|
||||
|
||||
this.updateHash = function(sType, iId) {
|
||||
updateHash(sType, iId) {
|
||||
sType = sType || '';
|
||||
iId = iId || 0;
|
||||
|
||||
var asHash = self.getHash();
|
||||
if(iId) self.setHash(asHash.page, [asHash.items[0], sType, iId]);
|
||||
};
|
||||
var asHash = this.getHash();
|
||||
if(iId) this.setHash(asHash.page, [asHash.items[0], sType, iId]);
|
||||
}
|
||||
|
||||
this.flushHash = function(asTypes) {
|
||||
flushHash(asTypes) {
|
||||
asTypes = asTypes || [];
|
||||
var asHash = self.getHash();
|
||||
if(asHash.items.length > 1 && (asTypes.length == 0 || asTypes.indexOf(asHash.items[1]) != -1)) self.setHash(asHash.page, [asHash.items[0]]);
|
||||
};
|
||||
var asHash = this.getHash();
|
||||
if(asHash.items.length > 1 && (asTypes.length == 0 || asTypes.indexOf(asHash.items[1]) != -1)) this.setHash(asHash.page, [asHash.items[0]]);
|
||||
}
|
||||
|
||||
/* Page Events */
|
||||
|
||||
pageInit(asHash) {
|
||||
let sPage = this.vars('page');
|
||||
if(this.pages[sPage].pageInit) this.pages[sPage].pageInit(asHash);
|
||||
else console.log('no init for the page: '+asHash.page);
|
||||
}
|
||||
|
||||
onSamePageMove(asHash) {
|
||||
let sPage = this.vars('page');
|
||||
return (this.pages[sPage] && this.pages[sPage].onSamePageMove)?this.pages[sPage].onSamePageMove(asHash):false;
|
||||
}
|
||||
|
||||
onQuitPage() {
|
||||
let sPage = this.vars('page');
|
||||
return (this.pages[sPage] && this.pages[sPage].onQuitPage)?this.pages[sPage].onQuitPage():true;
|
||||
}
|
||||
|
||||
onResize() {
|
||||
let sPage = this.vars('page');
|
||||
if(this.pages[sPage].onResize) this.pages[sPage].onResize();
|
||||
}
|
||||
|
||||
onFeedback(sType, sMsg, asContext) {
|
||||
asContext = asContext || {};
|
||||
let sPage = this.vars('page');
|
||||
if(this.pages[sPage].onFeedback) this.pages[sPage].onFeedback(sType, sMsg, asContext);
|
||||
}
|
||||
|
||||
onKeydown(oEvent) {
|
||||
let sPage = this.vars('page');
|
||||
if(this.pages[sPage].onKeydown) this.pages[sPage].onKeydown(oEvent);
|
||||
}
|
||||
|
||||
/* Page Switch - DOM Replacement */
|
||||
|
||||
this.getActionLink = function(sAction, oVars)
|
||||
{
|
||||
getActionLink(sAction, oVars) {
|
||||
if(!oVars) oVars = {};
|
||||
sVars = '';
|
||||
for(i in oVars)
|
||||
{
|
||||
sVars += '&'+i+'='+oVars[i];
|
||||
}
|
||||
return self.consts.process_page+'?a='+sAction+sVars;
|
||||
};
|
||||
let sVars = '';
|
||||
|
||||
for(i in oVars) sVars += '&'+i+'='+oVars[i];
|
||||
|
||||
this.resetTmpFunctions = function()
|
||||
{
|
||||
self.pageInit = function(asHash){console.log('no init for the page: '+asHash.page)};
|
||||
self.onSamePageMove = function(asHash){return false};
|
||||
self.onQuitPage = function(){return true};
|
||||
self.onResize = function(){};
|
||||
self.onFeedback = function(sType, sMsg){};
|
||||
self.onKeydown = function(oEvent){};
|
||||
};
|
||||
return this.consts.process_page+'?a='+sAction+sVars;
|
||||
}
|
||||
|
||||
this.switchPage = function(asHash)
|
||||
{
|
||||
addPage(sPage, oPage) {
|
||||
this.pages[sPage] = oPage;
|
||||
}
|
||||
|
||||
switchPage(asHash) {
|
||||
var sPageName = asHash.page;
|
||||
var bSamePage = (self.vars('page') == sPageName);
|
||||
var bFirstPage = (self.vars('page') == '');
|
||||
var bSamePage = (this.vars('page') == sPageName);
|
||||
var bFirstPage = (this.vars('page') == '');
|
||||
|
||||
if(!self.consts.pages[sPageName]) { //Page does not exist
|
||||
if(bFirstPage) self.setHash(self.consts.default_page);
|
||||
else self.setHash(self.vars('page'), self.vars(['hash', 'items']));
|
||||
if(!this.consts.pages[sPageName]) { //Page does not exist
|
||||
if(bFirstPage) this.setHash(this.consts.default_page);
|
||||
else this.setHash(this.vars('page'), this.vars(['hash', 'items']));
|
||||
}
|
||||
else if(self.onQuitPage(bSamePage) && !bSamePage || self.onSamePageMove(asHash))
|
||||
else if(this.onQuitPage(bSamePage) && !bSamePage || this.onSamePageMove(asHash))
|
||||
{
|
||||
//Delete tmp variables
|
||||
self.vars('tmp', {});
|
||||
|
||||
//disable tmp functions
|
||||
self.resetTmpFunctions();
|
||||
this.vars('tmp', {});
|
||||
|
||||
//Officially a new page
|
||||
self.vars('page', sPageName);
|
||||
self.vars('hash', asHash);
|
||||
|
||||
this.vars('page', sPageName);
|
||||
this.vars('hash', asHash);
|
||||
|
||||
//Update Page Title
|
||||
this.setPageTitle(sPageName+' '+(asHash.items[0] || ''));
|
||||
|
||||
//Replacing DOM
|
||||
var $Dom = $(self.consts.pages[sPageName]);
|
||||
if(bFirstPage)
|
||||
{
|
||||
self.splash($Dom, asHash, bFirstPage); //first page
|
||||
}
|
||||
else
|
||||
{
|
||||
self.elem.main.stop().fadeTo('fast', 0, function(){self.splash($Dom, asHash, bFirstPage);}); //Switching page
|
||||
}
|
||||
var $Dom = $(this.consts.pages[sPageName]);
|
||||
if(bFirstPage) this.splash($Dom, asHash, bFirstPage); //first page
|
||||
else this.elem.main.stop().fadeTo('fast', 0, () => {this.splash($Dom, asHash, bFirstPage);}); //Switching page
|
||||
}
|
||||
else if(bSamePage) self.vars('hash', asHash);
|
||||
};
|
||||
else if(bSamePage) this.vars('hash', asHash);
|
||||
}
|
||||
|
||||
this.setPageTitle = function(sTitle) {
|
||||
document.title = self.consts.title+' - '+sTitle;
|
||||
};
|
||||
|
||||
this.splash = function($Dom, asHash, bFirstPage)
|
||||
splash($Dom, asHash, bFirstPage)
|
||||
{
|
||||
//Switch main content
|
||||
self.elem.main.empty().html($Dom);
|
||||
this.elem.main.empty().html($Dom);
|
||||
|
||||
//Page Bootstrap
|
||||
self.pageInit(asHash, bFirstPage);
|
||||
this.pageInit(asHash, bFirstPage);
|
||||
|
||||
//Show main
|
||||
var $FadeInElem = bFirstPage?self.elem.container:self.elem.main;
|
||||
var $FadeInElem = bFirstPage?this.elem.container:this.elem.main;
|
||||
$FadeInElem.hide().fadeTo('slow', 1);
|
||||
};
|
||||
}
|
||||
|
||||
this.getNaturalDuration = function(iHours) {
|
||||
setPageTitle(sTitle) {
|
||||
document.title = this.consts.title+' - '+sTitle;
|
||||
}
|
||||
|
||||
getNaturalDuration(iHours) {
|
||||
var iTimeMinutes = 0, iTimeHours = 0, iTimeDays = Math.floor(iHours/8); //8 hours a day
|
||||
if(iTimeDays > 1) iTimeDays = Math.round(iTimeDays * 2) / 2; //Round down to the closest half day
|
||||
else {
|
||||
@@ -256,213 +255,13 @@ function Spot(asGlobals)
|
||||
iTimeMinutes = Math.floor(iHours * 4) * 15; //Round down to the closest 15 minutes
|
||||
}
|
||||
return '~ '
|
||||
+(iTimeDays>0?(iTimeDays+(iTimeDays%2==0?'':'½')+' '+self.lang(iTimeDays>1?'unit_days':'unit_day')):'') //Days
|
||||
+((iTimeHours>0 || iTimeDays==0)?iTimeHours+self.lang('unit_hour'):'') //Hours
|
||||
+(iTimeDays>0?(iTimeDays+(iTimeDays%2==0?'':'½')+' '+this.lang(iTimeDays>1?'unit_days':'unit_day')):'') //Days
|
||||
+((iTimeHours>0 || iTimeDays==0)?iTimeHours+this.lang('unit_hour'):'') //Hours
|
||||
+((iTimeDays>0 || iTimeMinutes==0)?'':iTimeMinutes) //Minutes
|
||||
|
||||
};
|
||||
|
||||
this.checkClearance = function(sClearance) {
|
||||
return (self.vars(['user', 'clearance']) >= sClearance);
|
||||
};
|
||||
}
|
||||
|
||||
/* Common Functions */
|
||||
|
||||
function copyArray(asArray)
|
||||
{
|
||||
return asArray.slice(0); //trick to copy array
|
||||
}
|
||||
|
||||
function getElem(aoAnchor, asPath)
|
||||
{
|
||||
return (typeof asPath == 'object' && asPath.length > 1)?getElem(aoAnchor[asPath.shift()], asPath):aoAnchor[(typeof asPath == 'object')?asPath.shift():asPath];
|
||||
}
|
||||
|
||||
function setElem(aoAnchor, asPath, oValue)
|
||||
{
|
||||
var asTypes = {boolean:false, string:'', integer:0, int:0, array:[], object:{}};
|
||||
if(typeof asPath == 'object' && asPath.length > 1)
|
||||
{
|
||||
var nextlevel = asPath.shift();
|
||||
if(!(nextlevel in aoAnchor)) aoAnchor[nextlevel] = {}; //Creating a new level
|
||||
if(typeof aoAnchor[nextlevel] !== 'object') debug('Error - setElem() : Already existing path at level "'+nextlevel+'". Cancelling setElem() action');
|
||||
return setElem(aoAnchor[nextlevel], asPath, oValue);
|
||||
}
|
||||
else
|
||||
{
|
||||
var sKey = (typeof asPath == 'object')?asPath.shift():asPath;
|
||||
return aoAnchor[sKey] = (!(sKey in aoAnchor) && (oValue in asTypes))?asTypes[oValue]:oValue;
|
||||
|
||||
checkClearance(sClearance) {
|
||||
return (this.vars(['user', 'clearance']) >= sClearance);
|
||||
}
|
||||
}
|
||||
|
||||
$.prototype.addInput = function(sType, sName, sValue, aoEvents)
|
||||
{
|
||||
aoEvents = aoEvents || [];
|
||||
var $Input = $('<input>', {type: sType, name: sName, value: sValue}).data('old_value', sValue);
|
||||
$.each(aoEvents, function(iIndex, aoEvent) {
|
||||
$Input.on(aoEvent.on, aoEvent.callback);
|
||||
});
|
||||
return $(this).append($Input);
|
||||
};
|
||||
|
||||
$.prototype.addButton = function(sIcon, sText, sName, fOnClick, sClass)
|
||||
{
|
||||
sText = sText || '';
|
||||
sClass = sClass || '';
|
||||
var $Btn = $('<button>', {name: sName, 'class':sClass})
|
||||
.addIcon('fa-'+sIcon, (sText != ''))
|
||||
.append(sText)
|
||||
.click(fOnClick);
|
||||
|
||||
return $(this).append($Btn);
|
||||
};
|
||||
|
||||
$.prototype.addIcon = function(sIcon, bMargin, sStyle)
|
||||
{
|
||||
bMargin = bMargin || false;
|
||||
sStyle = sStyle || '';
|
||||
return $(this).append($('<i>', {'class':'fa'+sStyle+' '+sIcon+(bMargin?' push':'')}));
|
||||
};
|
||||
|
||||
$.prototype.defaultVal = function(sDefaultValue)
|
||||
{
|
||||
$(this)
|
||||
.data('default_value', sDefaultValue)
|
||||
.val(sDefaultValue)
|
||||
.addClass('defaultText')
|
||||
.focus(function()
|
||||
{
|
||||
var $This = $(this);
|
||||
if($This.val() == $This.data('default_value')) $This.val('').removeClass('defaultText');
|
||||
})
|
||||
.blur(function()
|
||||
{
|
||||
var $This = $(this);
|
||||
if($This.val() == '') $This.val($This.data('default_value')).addClass('defaultText');
|
||||
});
|
||||
};
|
||||
|
||||
$.prototype.checkForm = function(sSelector)
|
||||
{
|
||||
sSelector = sSelector || 'input[type="text"], textarea';
|
||||
var $This = $(this);
|
||||
var bOk = true;
|
||||
$This.find(sSelector).each(function()
|
||||
{
|
||||
$This = $(this);
|
||||
bOk = bOk && $This.val()!='' && $This.val()!=$This.data('default_value');
|
||||
});
|
||||
return bOk;
|
||||
};
|
||||
|
||||
$.prototype.cascadingDown = function(sDuration)
|
||||
{
|
||||
return $(this).slideDown(sDuration, function(){$(this).next().cascadingDown(sDuration);});
|
||||
};
|
||||
|
||||
$.prototype.hoverSwap = function(sDefault, sHover)
|
||||
{
|
||||
return $(this)
|
||||
.data('default', sDefault)
|
||||
.data('hover', sHover)
|
||||
.hover(function(){
|
||||
var $This = $(this),
|
||||
sHover = $This.data('hover');
|
||||
sDefault = $This.data('default');
|
||||
|
||||
if(sDefault!='' && sHover != '') {
|
||||
$This.fadeOut('fast', function() {
|
||||
var $This = $(this);
|
||||
$This.text((sDefault==$This.text())?sHover:sDefault).fadeIn('fast');
|
||||
});
|
||||
}
|
||||
})
|
||||
.text(sDefault);
|
||||
};
|
||||
|
||||
$.prototype.onSwipe = function(fOnStart, fOnMove, fOnEnd){
|
||||
return $(this)
|
||||
.on('dragstart', (e) => {
|
||||
e.preventDefault();
|
||||
})
|
||||
.on('mousedown touchstart', (e) => {
|
||||
var $This = $(this);
|
||||
var oPos = getDragPosition(e);
|
||||
$This.data('x-start', oPos.x);
|
||||
$This.data('y-start', oPos.y);
|
||||
$This.data('x-move', oPos.x);
|
||||
$This.data('y-move', oPos.y);
|
||||
$This.data('moving', true).addClass('moving');
|
||||
fOnStart({
|
||||
xStart: $This.data('x-start'),
|
||||
yStart: $This.data('y-start')
|
||||
});
|
||||
})
|
||||
.on('touchmove mousemove', (e) => {
|
||||
var $This = $(this);
|
||||
if($This.data('moving')) {
|
||||
var oPos = getDragPosition(e);
|
||||
$This.data('x-move', oPos.x);
|
||||
$This.data('y-move', oPos.y);
|
||||
fOnMove({
|
||||
xStart: $This.data('x-start'),
|
||||
yStart: $This.data('y-start'),
|
||||
xMove: $This.data('x-move'),
|
||||
yMove: $This.data('y-move')
|
||||
});
|
||||
}
|
||||
})
|
||||
.on('mouseup mouseleave touchend', (e) => {
|
||||
var $This = $(this);
|
||||
if($This.data('moving')) {
|
||||
$This.data('moving', false).removeClass('moving');
|
||||
fOnEnd({
|
||||
xStart: $This.data('x-start'),
|
||||
yStart: $This.data('y-start'),
|
||||
xEnd: $This.data('x-move'),
|
||||
yEnd: $This.data('y-move')
|
||||
});
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
function getDragPosition(oEvent) {
|
||||
let bMouse = oEvent.type.includes('mouse');
|
||||
return {
|
||||
x: bMouse?oEvent.pageX:oEvent.touches[0].clientX,
|
||||
y: bMouse?oEvent.pageY:oEvent.touches[0].clientY
|
||||
};
|
||||
}
|
||||
|
||||
function copyTextToClipboard(text) {
|
||||
if(!navigator.clipboard) {
|
||||
var textArea = document.createElement('textarea');
|
||||
textArea.value = text;
|
||||
|
||||
// Avoid scrolling to bottom
|
||||
textArea.style.top = '0';
|
||||
textArea.style.left = '0';
|
||||
textArea.style.position = 'fixed';
|
||||
|
||||
document.body.appendChild(textArea);
|
||||
textArea.focus();
|
||||
textArea.select();
|
||||
|
||||
try {
|
||||
var successful = document.execCommand('copy');
|
||||
if(!successful) console.error('Fallback: Oops, unable to copy', text);
|
||||
} catch (err) {
|
||||
console.error('Fallback: Oops, unable to copy', err);
|
||||
}
|
||||
|
||||
document.body.removeChild(textArea);
|
||||
return;
|
||||
}
|
||||
navigator.clipboard.writeText(text).then(
|
||||
function() {},
|
||||
function(err) {
|
||||
console.error('Async: Could not copy text: ', err);
|
||||
}
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user