Compare commits

...

67 Commits

Author SHA1 Message Date
Ro 62e2dea287
Beta 2.6.1 update 1 year ago
Ro 8ce253418d
Adding site creation hot fix
Adding the fixes for site install process errors to the branch.
1 year ago
Ro 8622ba5941
Minor js hotfixes
Updating the script and the html to call the new file name, which is
called 'dash.js'
1 year ago
Ro 302362a478
Merge 'develop' 2.6.0 upadte into beta
Massive update covering issues #71, #80, #81, #83 as well as updated
responsive styles and an overhauled File Manager
1 year ago
Ro c2b3b234fa Merging autoload changes 2 years ago
Ro fce378d437 Merging dependency updates and Moment removal
Bringing over changes from develop into the beta branch, including
updating composer package dependencies and getting rid of Moment in
favor of Carbon for date formatting
2 years ago
Are0h 69fc689d38 Finally merging 2.5.1 updates into beta
2.5.1 is ready to go, so it's time to merge into the beta branch and
  test it to make sure everything is ship shape
2 years ago
Are0h c546aa7b63 hotfix for editor img uploads, formatting tweaks 2 years ago
Are0h 6f2a8cfb4b hot fix for open graph image rendering 2 years ago
are0h 614c9859b1 fix for weird css dahsh deletion bug 2 years ago
are0h 0ee4083949 fix for dash css 2 years ago
are0h d92944b2ec fixes for dash templates default theme 2 years ago
are0h c004452c55 just fixing a script error 2 years ago
are0h 7cefc12692 2.5.0 commit part 1, whew 2 years ago
Ro 2d5de69f1c format bar float fix, added upload progess bar 2 years ago
Ro f6aac33ae4 Had a stray var dump in there. Oops 2 years ago
Ro c98a75f931 updated production assets 2 years ago
Ro 257d2a0623 text editor adjustments, fix for page delete, ignore update 2 years ago
Ro 59e0f37b3e restored dash styles 2 years ago
Ro 32a4f32202 fix for src being removed from img with relative urls, added iframe to
allowed html
2 years ago
Ro 0e52528de0 accidentlly removed feature img from recent and featured lists. oops 2 years ago
Ro 6c053868a2 hot fix for page rendering defaulting to page every time 2 years ago
Ro dc08f60098 hot fix for template and empty layout on new page creation 2 years ago
Ro 8502c4f0e0 fixed start twig template 2 years ago
Ro 2fdd7f40f0 cleared conflict with git ignore 2 years ago
Ro de2aec58c9 Added new dash script, duh
# Conflicts:
#	.gitignore
#	src/com/Base.js
2 years ago
Ro 55b16a0acd update ignore file 2 years ago
Ro 39c6ff7f11 Update for #76 - Beta 2.5.0
# Conflicts:
#	src/com/actions/PageActions.js
#	src/com/controllers/PageEditor.js
#	src/com/controllers/SettingsIndex.js
#	src/com/ui/TextEditor.js
#	src/styles/dash.sass
#	src/styles/main/_colors.sass
#	src/styles/main/_editor-highlight.sass
#	src/styles/main/_normalize.sass
#	src/styles/main/_posts.sass
2 years ago
Ro 1b66f5daf9 Beta 2.4.1 - fixes for #68, #69, #70, #72, #75 2 years ago
Ro e5873b92cf Hot fix to add images to recent and featured links on the index 2 years ago
Ro 4151891129 Release 2.4.0 - #61 #62, fixed #63, #64, #65, #66
# Conflicts:
#	src/com/actions/PageActions.js
#	src/com/controllers/SettingsIndex.js
#	src/styles/main/_posts.sass
3 years ago
Ro 4f4ee5dfc7 updated gitignore 3 years ago
Ro cecead05a1 Fixed #58, #59, #60 3 years ago
Ro 815ebf58f7 Hot fix for page creation (token not being share in create mode) 3 years ago
Ro a475b64aca Fixes for issues #56, #56 and renamed base logo 3 years ago
Ro ccf65e1899 Merge branch 'develop' into beta
Fixed #51, #52, #53, #55
3 years ago
Ro 6e9368c2aa updated composer dependenciesa 3 years ago
Ro 854cf12067 re-activated site backups for now 3 years ago
Ro 85a7eab8be Minor Admin API tweaks, added theme render flag check for one pagers 3 years ago
Ro bf383785a0 changed auth process to just ask server if session is active 3 years ago
Ro b1c884689e Relaxed image upload auth.
# Conflicts:
#	src/com/controllers/MaintenanceManager.js
#	src/com/controllers/SettingsIndex.js
3 years ago
Ro ce30f3efb7 Fixed image upload that needed authorization 3 years ago
Ro fe8a5e4e96 Adds key to original settings file on site restore if not present 3 years ago
Ro da49388caa Added missing key to settings init template 3 years ago
Ro 3c3f2a0881 Quick patch for CORS check while in init state 3 years ago
Ro 3968e32e78 Merge branch 'develop' into beta
added admin method for getting site and member data
3 years ago
Ro 7d80e42642 Edits to API files 3 years ago
Ro 7f3be4c0c5 removed unnecessary script 3 years ago
Ro 8355137952 Merge branch 'develop' into beta
fixes for api token check and empty page feature image
3 years ago
Ro 02563a4c87 First round of changes for 2.2.0 headless update for testing
# Conflicts:
#	src/com/Base.js
#	src/com/controllers/NavIndex.js
#	src/com/controllers/PageEditor.js
#	src/com/controllers/SettingsIndex.js
#	src/libraries/FipamoAPI.js
#	src/libraries/FipamoAdminAPI.js
#	src/package-lock.json
#	src/package.json
3 years ago
Ro 46691b454d added FipamoAPI class 3 years ago
Ro 321903643a add methods to page API, cleaned up api returns 3 years ago
Ro 8f5df10b89 Hot fix for Fipamo admin content API 3 years ago
Ro 3d8c421f76 Merging API clean up from develop 3 years ago
Ro 6b2ee8e401 Version update 3 years ago
Ro 3706a81c5f quick fix for composer settings file 3 years ago
Ro c21704b8bd updated ignore file 3 years ago
Ro 18e67691f5 Merging all UI updates 3 years ago
Ro 005de8027e updated composer json 3 years ago
Ro 913dd3a57f moved password recovery updates to beta 3 years ago
Ro 1c7fcd6664 Merge branch 'develop' into beta
Merging page meta image change
3 years ago
Ro 3b118782ef merge changes from develop 3 years ago
Ro b4ed326387 beta branch should not have source files 3 years ago
Ro a5f2f17670 Merge branch 'develop' into beta 3 years ago
Ro e80408dcb0 Merge branch 'develop' into beta
# Conflicts:
#	.gitignore
#	public/assets/scripts/dash.min.js
#	src/com/controllers/SettingsIndex.js
3 years ago
Ro 573219472a removed theme assets from repo 3 years ago
Ro 00f9229eb2 got rid of src files for styles and front end scripting, added base css and script assets 3 years ago

17
.gitignore vendored

@ -15,8 +15,18 @@ public/assets/css/*
!public/assets/css/dash
!public/assets/scripts
public/assets/scripts/*
<<<<<<< HEAD
!public/assets/scripts/Start.js
/public/assets/images/global/rikc-logo.svg
=======
!public/assets/scripts/dash.js
<<<<<<< HEAD
>>>>>>> develop
=======
!public/assets/scripts/dash.js.map
>>>>>>> develop
!public/assets/images
public/assets/images/*
!public/assets/images/global/
@ -40,4 +50,9 @@ config/pages.json
config/tags.json
*.DS_Store
config.codekit3
/config/backups
/config/backups
src/com/*
src/styles/*
src/com/ui/TextEditor.js

@ -1,13 +0,0 @@
<svg width="305" height="305" viewBox="0 0 305 305" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<g id="Group">
<path id="Path" fill="none" stroke="#894262" stroke-width="30" stroke-linecap="round" stroke-linejoin="round" d="M 289.203003 152.102005 C 289.203003 227.82077 227.820801 289.203003 152.10199 289.203003 C 76.383202 289.203003 15.001002 227.82077 15.001002 152.102005 C 15.001002 76.383209 76.383202 15.001007 152.10199 15.001007 C 227.820801 15.001007 289.203003 76.383209 289.203003 152.102005 Z"/>
</g>
<g id="g1">
<g id="g2">
<path id="path1" fill="#894262" stroke="none" d="M 184.531082 78.841095 C 186.322433 78.841095 188.113785 79.097 189.649246 79.352905 C 190.928772 79.60881 192.208313 80.376541 192.976044 81.656067 C 193.99968 82.935608 194.51149 85.49469 194.51149 88.565598 L 194.51149 97.522369 C 194.51149 99.313721 194.51149 101.616898 194.767395 103.920074 C 194.767395 106.223251 194.51149 108.270508 193.743759 109.550049 C 192.976044 110.82959 192.208313 111.597305 190.928772 111.85321 C 189.649246 112.109131 188.113785 112.365036 186.578339 112.365036 C 184.786987 112.365036 183.251541 112.365036 181.460175 112.109131 C 179.668823 111.85321 177.877472 112.109131 176.342026 112.365036 C 175.062485 112.620941 174.038849 112.876846 172.759308 112.876846 C 171.479767 113.132751 170.456131 113.388672 169.432495 113.644577 C 166.617523 114.412308 164.058426 115.691849 161.243454 117.22728 C 158.428467 119.018646 156.381195 120.809998 154.845749 122.857269 C 154.078018 124.13681 153.054398 125.672256 152.286667 126.951797 C 151.263031 128.487244 150.23941 130.02269 149.47168 131.558136 C 148.959869 132.837677 148.448044 134.373123 148.192139 135.652664 C 147.680328 137.18811 147.168503 138.723557 146.912598 140.003098 C 146.656693 141.282639 146.400787 142.306274 146.400787 143.585815 C 146.400787 144.865356 146.144882 146.144897 145.888962 147.424423 C 145.633057 148.192154 145.633057 148.703964 145.888962 149.21579 C 145.888962 149.983521 145.888962 150.495331 145.633057 151.007141 L 145.633057 155.101669 C 145.377151 156.38121 145.121246 157.660751 145.377151 158.940292 C 145.633057 160.475739 145.633057 162.011185 145.633057 163.290726 L 145.633057 201.421021 C 145.633057 203.724182 145.633057 205.771454 145.377151 207.562805 C 145.121246 209.354156 144.609421 210.889618 143.841705 212.169159 C 143.5858 212.680969 143.073975 212.93689 142.306259 213.19278 C 141.538528 213.4487 140.514893 213.704605 139.747177 213.96051 L 138.467636 213.96051 C 137.699921 214.216415 136.676285 214.216415 135.652649 214.216415 L 121.833618 214.216415 C 120.809982 213.96051 119.786354 213.96051 118.762718 213.96051 L 115.180008 213.96051 C 114.412285 213.704605 113.388649 213.19278 112.620926 212.93689 C 111.853203 212.680969 111.08548 212.169159 110.829575 211.401428 C 110.061852 210.633698 109.805939 209.354156 109.805939 207.562805 C 109.550034 206.027359 109.550034 204.491913 109.550034 202.700562 L 109.550034 89.077408 C 109.550034 87.541962 109.805939 86.262421 110.573662 85.238785 C 111.341385 83.959244 112.620926 82.935608 114.412285 82.679703 C 116.203636 82.423798 118.2509 82.167892 120.554077 82.167892 L 133.605377 82.167892 C 135.652649 82.167892 137.444 82.167892 138.979446 82.423798 C 140.514893 82.679703 141.794449 83.703339 142.562164 84.98288 C 143.073975 85.75061 143.329895 87.030136 143.5858 88.565598 C 143.5858 90.101028 143.5858 91.63649 143.841705 92.916031 C 144.09761 94.451477 144.353516 95.731018 144.865341 97.010559 C 145.121246 98.2901 145.888962 98.80191 147.168503 98.80191 C 147.424423 98.546005 147.936234 98.2901 148.192139 98.2901 C 148.448044 98.2901 148.959869 98.034195 149.215775 97.778275 C 150.23941 97.010559 151.263031 95.986923 152.030762 94.707382 C 152.798492 93.683762 153.566208 92.660126 154.845749 91.892395 C 155.613464 91.380585 156.12529 90.868759 156.6371 90.101028 C 157.148926 89.589233 157.660736 89.077408 158.428467 88.565598 C 159.963898 87.541962 161.499359 86.518326 163.29071 85.49469 C 164.826157 84.471054 166.361603 83.447433 168.152969 82.679703 C 169.944321 81.911972 171.735672 81.400162 173.527023 80.888351 C 175.31839 80.376541 177.109741 79.864731 178.901093 79.352905 C 179.412918 79.097 180.436554 79.097 181.972 79.097 C 183.251541 79.352905 184.275162 79.097 184.531082 78.841095 Z"/>
</g>
<g id="g3">
<path id="path2" fill="#894262" fill-rule="evenodd" stroke="none" d="M 194.653778 204.45752 C 194.653778 209.84726 190.284576 214.216522 184.894806 214.216522 C 179.505035 214.216522 175.135834 209.84726 175.135834 204.45752 C 175.135834 199.06778 179.505035 194.698517 184.894806 194.698517 C 190.284576 194.698517 194.653778 199.06778 194.653778 204.45752 Z"/>
</g>
</g>
</svg>

Before

Width:  |  Height:  |  Size: 4.7 KiB

@ -1,211 +0,0 @@
import FipamoAdminAPI from '../libraries/FipamoAdminAPI';
import Maintenance from './controllers/MaintenanceManager';
import DataUitls from './utils/DataUtils';
import * as DataEvent from './events/DataEvent';
import DashManager from './controllers/DashManager';
import Notfications from './ui/Notifications';
const data = new DataUitls();
const notify = new Notfications();
export default class Base {
//--------------------------
// constructor
//--------------------------
constructor() {
this.processing = false;
this.start();
}
//--------------------------
// methods
//--------------------------
//TODO: Move init functions and set up to their own class
start() {
if (
document.getElementById('login') ||
document.querySelector('[role="site-restore"]')
) {
var options = document.getElementsByClassName('init-option');
for (let index = 0; index < options.length; index++) {
options[index].addEventListener('click', e => this.handleOptions(e));
}
if (document.getElementById('login')) {
document
.getElementById('login-btn')
.addEventListener('click', e => this.handleLogin(e));
} else {
document
.getElementById('init-blog')
.addEventListener('click', e => this.handleSetup(e));
document
.getElementById('blog-restore')
.addEventListener('click', e => this.handleRestore(e));
}
} else if (document.getElementById('dash-reset')) {
document
.getElementById('get-secret-btn')
.addEventListener('click', e => this.handleReset(e));
document
.getElementById('reset-btn')
.addEventListener('click', e => this.handleReset(e));
} else {
new DashManager();
}
}
//--------------------------
// event handlers
//--------------------------
handleLogin(e) {
if (this.processing) return;
let self = this;
e.preventDefault();
let authForm = data.formDataToJSON(document.getElementById('login'));
//notify.alert('Looking, hold up', null);
let api = new FipamoAdminAPI();
this.processing = true;
api.login(authForm)
.then(response => {
self.processing = false;
if (response.type === DataEvent.REQUEST_LAME) {
e.target.innerHTML = response.message;
} else {
e.target.innerHTML = response.message;
setTimeout(() => {
window.location = '/dashboard';
}, 500);
}
})
.catch(err => {
self.processing = false;
});
}
handleSetup(e) {
if (this.processing) return;
let self = this;
e.stopPropagation();
e.preventDefault();
let setUpForm = data.formDataToJSON(document.getElementById('init-form'));
let mm = new Maintenance();
this.processing = true;
mm.create(setUpForm)
.then(response => {
if (response.type === DataEvent.API_INIT_LAME) {
self.processing = false;
e.target.innerHTML = response.message;
} else {
self.processing = false;
e.target.innerHTML = response.message;
setTimeout(() => {
window.location = '/dashboard';
}, 700);
}
})
.catch(err => {
self.processing = false;
//notify.alert(err, false);
});
}
handleRestore(e) {
if (this.processing) return;
let self = this;
e.stopPropagation();
e.preventDefault();
let mm = new Maintenance();
var form = document.getElementById('init-restore');
this.processing = true;
mm.restore(form)
.then(response => {
if (response.type === DataEvent.REQUEST_LAME) {
self.processing = false;
e.target.innerHTML = response.message;
} else {
self.processing = false;
e.target.innerHTML = response.message;
setTimeout(() => {
window.location = '/dashboard';
}, 1500);
}
})
.catch(err => {
self.processing = false;
e.target.innerHTML = err;
});
}
handleReset(e) {
e.stopPropagation();
e.preventDefault();
let self = this;
let mm = new Maintenance();
if (e.target.id == 'get-secret-btn') {
let data = {
email: document.getElementById('email').value,
task: 'retrieveSecret'
};
this.processing = true;
mm.secret(data)
.then(response => {
self.processing = false;
if (response.secret) {
document.getElementById('secret').value = response.secret;
notify.alert(response.message, true);
} else {
if (response.type == 'mailSent') {
notify.alert(response.message, true);
} else {
notify.alert(response.message, false);
}
}
})
.catch(err => {
self.processing = false;
notify.alert(err, false);
});
} else {
let data = {
newPass: document.getElementById('new_password').value,
newPassConfirm: document.getElementById('new_password2').value,
secret: document.getElementById('secret').value
};
mm.newPass(data)
.then(response => {
self.processing = false;
if (response.type == 'passNotCreated') {
notify.alert(response.message, false);
} else {
notify.alert(response.message, true);
setTimeout(() => {
window.location = '/dashboard';
}, 1000);
}
})
.catch(err => {
self.processing = false;
notify.alert(err, false);
});
}
}
handleOptions(e) {
e.stopPropagation();
e.preventDefault();
let init = document.querySelector('[role="restore-fresh"]');
let restore = document.querySelector('[role="restore-backup"]');
if (e.target.id === 'init-switch-restore') {
init.style.display = 'none';
init.style.visibility = 'hidden';
restore.style.display = 'grid';
restore.style.visibility = 'visible';
} else {
init.style.display = 'grid';
init.style.visibility = 'visible';
restore.style.display = 'none';
restore.style.visibility = 'hidden';
}
}
}

@ -1,9 +0,0 @@
import Base from './Base';
document.addEventListener(
'DOMContentLoaded',
function () {
new Base();
},
false
);

@ -1,44 +0,0 @@
import FipamoAdminAPI from "../../libraries/FipamoAdminAPI";
import Notficaton from "../ui/Notifications";
const notify = new Notficaton();
export default class Mailer {
//--------------------------
// constructor
//--------------------------
constructor() {}
//--------------------------
// methods
//--------------------------
sendMail() {
let mailData = {
content: "This is a test email"
};
let admin = new FipamoAdminAPI();
admin
.sendMail(mailData)
.then((result) => {
notify.alert(result.message, true);
})
.catch((err) => {
notify.alert(err.message, false);
});
}
testMail() {
let mailData = {
content: "This is a test email",
mail_task: "TESTING"
};
let admin = new FipamoAdminAPI();
admin
.sendMail(mailData)
.then((result) => {
notify.alert(result.message, true);
})
.catch((err) => {
notify.alert(err.message, false);
});
}
//--------------------------
// event handlers
//--------------------------
}

@ -1,35 +0,0 @@
export default class NavActions {
//--------------------------
// constructor
//--------------------------
constructor() {}
//--------------------------
// methods
//--------------------------
syncMenu() {
let navData = [];
let items = document.getElementById('nav-items').children;
for (let index = 0; index < items.length; index++) {
navData.push({
title: items[index].getElementsByTagName('label')[0].innerHTML,
id: items[index].id,
slug: items[index].getAttribute('data-slug'),
uuid: items[index].getAttribute('data-uuid'),
path: items[index].getAttribute('data-path')
});
}
let data = { menu: navData, remove: null };
return new Promise(function (resolve) {
resolve(data);
});
}
removeItem(id) {
document.getElementById('nav-items').removeChild(document.getElementById(id));
}
//--------------------------
// event handlers
//--------------------------
}

@ -1,53 +0,0 @@
import StringUtils from '../utils/StringUtils';
export default class PostActions {
//--------------------------
// constructor
//--------------------------
constructor() {}
//--------------------------
// methods
//--------------------------
collectInfo(files) {
return new Promise((resolve, reject) => {
let pageInfo = [];
let pageRef = document.querySelector('[role="file-manager"]');
//process html content for storage
let txt = document.createElement('textarea');
txt.innerHTML = document.getElementById('highlight-content').innerHTML;
let html = txt.value;
html = html.replace(/<\/?span[^>]*>/g, ''); //removes prism styling
html = html.replace(/<\/?br[^>]*>/g, '\n'); //convert back to encoded line break for storage
//build data object
pageInfo = {
id: pageRef.getAttribute('data-index'),
uuid: pageRef.getAttribute('data-uuid'),
layout: document.getElementById('page-templates').value,
current_title: pageRef.getAttribute('data-slug'),
content: html,
title: document.getElementById('post-title-text').value,
created: document.getElementById('post-date').getAttribute('data-raw'),
slug: new StringUtils().cleanString(
document.getElementById('post-title-text').value
),
tags: document.getElementById('post-tags').value,
menu: document
.getElementById('option-menu-pin')
.getAttribute('data-active'),
featured: document
.getElementById('option-feature')
.getAttribute('data-active'),
published: document
.getElementById('option-published')
.getAttribute('data-active'),
form_token: document.getElementById('form_token').value,
imageList: files.images,
fileList: files.files
};
resolve(pageInfo);
});
}
//--------------------------
// event handlers
//--------------------------
}

@ -1,79 +0,0 @@
export default class SettingsActions {
//--------------------------
// constructor
//--------------------------
constructor() {}
//--------------------------
// methods
//--------------------------
getInfo() {
let handle = document.getElementById('settings-handle').value;
let email = document.getElementById('settings-email').value;
let url = document.getElementById('settings-url').value;
let title = document.getElementById('settings-title').value;
let desc = document.getElementById('settings-desc').value;
//let privacy = document.getElementById('privacy-toggle').getAttribute('data-private');
let render = document.getElementById('render-toggle').getAttribute('data-render');
let background = document
.querySelector('[role="background"]')
.style.backgroundImage.slice(4, -1)
.replace(/"/g, '');
let selected = '';
let selects = document.querySelectorAll('.theme-select');
let smtpDomain = document.getElementById('smtp-domain').value;
let smtpEmail = document.getElementById('smtp-email').value;
let smtpPass = document.getElementById('smtp-pass').value;
let mgDomain = document.getElementById('mg-domain').value;
let mgKey = document.getElementById('mg-key').value;
let mailActive = '';
let mailOptions = document.querySelectorAll('.mail-option');
let apiStatus = document
.getElementById('api-access-toggle')
.getAttribute('data-enabled');
let dynamicRenderStatus = document
.getElementById('dynamic-render-toggle')
.getAttribute('data-enabled');
var i, count;
for (i = 0, count = selects.length; i < count; i++) {
if (selects[i].getAttribute('data-enabled') == 'true')
selected = selects[i].id;
}
for (i = 0, count = mailOptions.length; i < count; i++) {
if (mailOptions[i].getAttribute('data-enabled') == 'true')
mailActive = mailOptions[i].id;
}
let settingsData = {
global: {
base_url: url,
title: title,
descriptions: desc,
background: background,
private: false,
renderOnSave: render,
theme: selected,
externalAPI: apiStatus,
dynamicRender: dynamicRenderStatus
},
member: { handle: handle, email: email },
email: {
active: mailActive,
smtp: {
domain: smtpDomain,
email: smtpEmail,
password: smtpPass
},
mailgun: {
domain: mgDomain,
key: mgKey
}
}
};
return new Promise(function (resolve) {
resolve(settingsData);
});
}
//--------------------------
// event handlers
//--------------------------
}

@ -1,44 +0,0 @@
import PostIndex from './PostIndex';
import SettingsIndex from './SettingsIndex';
import NaviIndex from './NavIndex';
import Menu from '../ui/Menu';
export default class DashManager {
//--------------------------
// constructor
//--------------------------
constructor() {
this.currentDisplay = '';
this.urlPieces = document.URL.split('/');
this.chooseDisplay(this.urlPieces[4], this.urlPieces[5]);
//start main menu handler
new Menu();
}
//--------------------------
// methods
//--------------------------
start() {}
chooseDisplay(section, page) {
this.currentDisplay = '';
switch (section) {
case 'page':
this.currentDisplay = new PostIndex(page);
break;
case 'settings':
this.currentDisplay = new SettingsIndex();
break;
case 'navigation':
this.currentDisplay = new NaviIndex();
break;
default:
//just chill
break;
}
this.start();
}
//--------------------------
// event handlers
//--------------------------
}

@ -1,292 +0,0 @@
//** REQUEST TYPES **//
export const REQUEST_TYPE_POST = 'POST';
export const REQUEST_TYPE_GET = 'GET';
export const REQUEST_TYPE_PUT = 'PUT';
export const REQUEST_TYPE_DELETE = 'DELETE';
//** POST CONTENT TYPES **//
export const CONTENT_TYPE_JSON = 'json';
export const CONTENT_TYPE_FORM = 'x-www-form-urlencoded';
//** API URLS **//
export const API_STATUS = '/api/v1/status';
export const API_INIT = '/api/v1/init';
export const API_RESTORE = '/api/v1/restore';
export const API_GET_SECRET = '/api/v1/get-secret';
export const API_RESET_PASS = '/api/v1/reset-password';
export const API_CREATE_BACKUP = '/api/v1/backup';
export const API_DOWNLOAD_BACKUP = '/api/v1/backup/download';
export const API_RESTORE_BACKUP = '/api/v1/backup/restore';
export const API_UPLOAD_AVATAR = '/api/v1/settings/add-avatar';
export const API_UPLOAD_BACKGROUND = '/api/v1/settings/add-feature-background';
export const API_IMAGE_UPLOAD = '/api/v1/page/add-entry-image';
export const API_FILES_UPLOAD = '/api/v1/files';
//** API TASKS **//
export const TASK_SITE_INIT = 'blogInit';
export const TASK_BACKUP_RESTORE = 'restoreBackup';
export const TASK_BACKUP_CREATE = 'createBackup';
export const TASK_GET_SECRET = 'retrieveSecret';
export const TASK_RESET_PASS = 'resetPassword';
export const TASK_UPLOAD_FILES = 'uploadFiles';
//** API STATUS **//
export const API_ACCESS_GOOD = 'apiUseAuthorized';
export const API_ACCESS_BAD = 'apiUseNotAuthorized';
/**
* A tub of methods for creating/restoring installs, resetting passwords and uploading images.
*/
class MaintenanceManager {
/**
* @constructor
* @param {string} baseURL - url of site; uses local when empty
* @param {string} key - user api key
*/
constructor(baseURL = null, key = null) {
this.accetableFiles = [
'image/jpeg',
'image/gif',
'image/png',
'image/svg',
'audio/mpeg',
'video/mp4',
'application/pdf',
'text/plain',
'text/rtf'
];
this.percentComplete = 0; //for later
this.token = null;
this.baseURL = null;
this.key = null;
if (key) this.key = key;
if (baseURL) this.baseURL = baseURL;
//if key is valid, checks to see if a session is active and returns
this._request(
this.baseURL
? this.baseURL + API_STATUS + '?key=' + this.key
: API_STATUS + '?key=' + this.key
).then(response => {
if (response.type === API_ACCESS_GOOD) {
this.token = response.token;
} else {
//don't set token
//console.log("NO TOKEN");
}
});
}
/**
* Promise method used create new site from scratch. For local use only.
* @param {object} data - json object that contains data for set up
* @property {string} new_member_handle - handle for new user
* @property {string} new_member_email - email for new user
* @property {string} new_member_pass - password for new user
* @property {string} new_member_title - title for new user
*/
create(data) {
return new Promise((resolve, reject) => {
this._request(
API_INIT,
null,
TASK_SITE_INIT,
REQUEST_TYPE_POST,
CONTENT_TYPE_JSON,
data
)
.then(result => {
resolve(result);
})
.catch(err => {
reject(err);
});
});
}
/**
* Promise method for restoring site from a previous back up. For local use only.
* @param {object} form - form object that contains restore data and files
* @property {string} restore_member_handle - handle for site user
* @property {string} restore_member_pass - password for site user
* @property {file} backup-upload - backup zip file
*/
restore(form) {
return new Promise((resolve, reject) => {
var url, event, method, type, data;
url = API_RESTORE;
event = TASK_BACKUP_RESTORE;
method = REQUEST_TYPE_POST;
type = CONTENT_TYPE_FORM;
data = new FormData(form);
this._request(url, null, event, method, type, data)
.then(result => {
resolve(result);
})
.catch(err => {
reject(err);
});
});
}
/**
* Promise method for creating a zip back up of current site. For local use only.
*/
backup() {
return new Promise((resolve, reject) => {
var url, event, method, type, data;
url = API_CREATE_BACKUP;
event = TASK_BACKUP_CREATE;
method = REQUEST_TYPE_POST;
type = CONTENT_TYPE_JSON;
data = { task: 'create_backup' };
this._request(url, null, event, method, type, data)
.then(result => {
resolve(result);
})
.catch(err => {
reject(err);
});
});
}
/**
* Promise method for retrieving user secret key. For local use only.
* @param {object} data - json object that contains data for set up
* @property {string} email - email for site user
*/
secret(data) {
return new Promise((resolve, reject) => {
this._request(
API_GET_SECRET,
TASK_GET_SECRET,
REQUEST_TYPE_POST,
CONTENT_TYPE_JSON,
data
)
.then(result => {
resolve(result);
})
.catch(err => {
reject(err);
});
});
}
/**
* Promise method for resetting password for user. For local use only.
* @param {object} data - json object that contains data for set up
* @property {string} new_password - password for user
* @property {string} new_password2 - confirm password for user
* @property {string} secret - secret key for user
*/
newPass(data) {
return new Promise((resolve, reject) => {
this._request(
API_RESET_PASS,
TASK_RESET_PASS,
REQUEST_TYPE_POST,
CONTENT_TYPE_JSON,
data
)
.then(result => {
resolve(result);
})
.catch(err => {
reject(err);
});
});
}
/**
* Promise method for uploading files [todo: change to uploading files]
* @param {string} type - type of upload
* @param {input} files - form input containing files
*/
filesUpload(type, files, progress = null) {
return new Promise((resolve, reject) => {
let url = API_FILES_UPLOAD;
if (this.baseURL) {
files.append('key', this.key);
files.append('remote', true);
} else {
files.append('remote', false);
}
this._request(
url,
progress,
TASK_UPLOAD_FILES,
REQUEST_TYPE_POST,
CONTENT_TYPE_FORM,
files
)
.then(r => {
resolve(r);
})
.catch(err => {
reject(err);
});
});
}
//--------------------------
// private
//--------------------------
_request(
requestURL,
progressBar = null,
eventType,
requestType = REQUEST_TYPE_GET,
contentType = CONTENT_TYPE_JSON,
requestData = null
) {
var self = this;
return new Promise(function (resolve, reject) {
var request = new XMLHttpRequest();
request.upload.addEventListener('progress', e =>
self.handleLoadProgress(e, progressBar)
);
request.open(requestType, requestURL, true);
request.onload = () => {
if (request.status == 200) {
let response = JSON.parse(request['response']);
resolve(response);
} else {
let error = JSON.parse(request['response']);
reject(error);
}
};
if (requestType == REQUEST_TYPE_PUT || requestType == REQUEST_TYPE_POST) {
if (eventType === TASK_UPLOAD_FILES)
request.setRequestHeader('fipamo-access-token', self.token);
switch (contentType) {
case CONTENT_TYPE_JSON:
request.setRequestHeader(
'Content-type',
'application/' + contentType
);
request.send(JSON.stringify(requestData));
break;
case CONTENT_TYPE_FORM:
request.send(requestData);
break;
}
} else {
request.send();
}
});
}
//--------------------------
// event handlers
//--------------------------
handleLoadProgress(e, progressBar) {
let percent = Math.ceil((e.loaded / e.total) * 100);
//if a progress bar element is present, talk to it
if (progressBar != null) {
progressBar.style.width = percent + '%';
}
}
}
export { MaintenanceManager as default };

@ -1,74 +0,0 @@
import FipamoAdminAPI, { TASK_SYNC_NAV } from '../../libraries/FipamoAdminAPI';
import NavActions from '../actions/NavActions';
import * as DataEvent from '../events/DataEvent';
import Notifications from '../ui/Notifications';
import Sortable from 'sortablejs';
const notify = new Notifications();
export default class NavIndex {
//--------------------------
// constructor
//--------------------------
constructor() {
this.processing = false;
this.admin = new FipamoAdminAPI(null);
this.start();
}
//--------------------------
// methods
//--------------------------
start() {
//grabs elements and makes them sortables
let self = this;
Sortable.create(document.getElementById('nav-items'), {
onUpdate: () => {
new NavActions().syncMenu().then(data => {
notify.alert('Updating Menu', null);
self.admin.sync(TASK_SYNC_NAV, data).then(r => {
if (r.type == DataEvent.MENU_UPDATED) {
notify.alert(r.message, true);
} else {
notify.alert(r.message, true);
}
});
});
}
});
var nav = document.querySelectorAll('.nav-btn');
for (var i = 0, length = nav.length; i < length; i++) {
nav[i].addEventListener('click', e => this.handleNavButton(e), false);
}
}
//--------------------------
// event handlers
//--------------------------
handleNavButton(e) {
if (this.processing) return;
let id = '';
let self = this;
switch (e.target.id) {
case 'remove-item':
id = e.target.getAttribute('data-id');
new NavActions().removeItem(id);
new NavActions().syncMenu().then(data => {
data.remove = e.target.getAttribute('data-uuid');
notify.alert('Editing Menu', null);
self.processing = true;
self.admin.sync(TASK_SYNC_NAV, data).then(r => {
self.processing = false;
if (r.type == DataEvent.MENU_UPDATED) {
notify.alert(r.message, true);
} else {
notify.alert(r.message, true);
}
});
});
break;
case 'edit-item':
self.processing = false;
window.location =
'/dashboard/page/edit/' + e.target.getAttribute('data-id');
break;
}
}
}

@ -1,228 +0,0 @@
//TOOLS
import FipamoAdminAPI, {
TASK_PAGE_CREATE,
TASK_PAGE_EDIT,
TASK_PAGE_DELETE
} from '../../libraries/FipamoAdminAPI';
import Maintenance from './MaintenanceManager';
import * as DataEvent from '../events/DataEvent';
import PageActions from '../actions/PageActions';
import * as EditorEvent from '../events/EditorEvent';
//import TinyDatePicker from 'tiny-date-picker'; TODO: Reactivate for scheduled publishing
import TextEditor from '../ui/TextEditor';
import Notfications from '../ui/Notifications';
import FileManager from '../ui/FileManager';
const notify = new Notfications();
export default class PostEditor {
//--------------------------
// constructor
//--------------------------
constructor() {
this.processing = false;
let self = 'this';
this.admin = new FipamoAdminAPI(null, document.getElementById('notify-progress'));
this.mm = new Maintenance(null, null);
this.urlPieces = document.URL.split('/');
this.post = [];
this.postID = null;
this.postUUID = null;
this.postLayout = null;
this.fm = null;
if (document.querySelector('[role="file-manager"]').getAttribute('data-index')) {
this.postID = document
.querySelector('[role="file-manager"]')
.getAttribute('data-index');
this.postUUID = document
.querySelector('[role="file-manager"]')
.getAttribute('data-uuid');
this.postLayout = document
.querySelector('[role="file-manager"]')
.getAttribute('data-layout');
}
if (document.getElementById('edit')) {
this.editor = new TextEditor(
document.getElementById('edit'),
document.querySelector('[role="file-manager"]').offsetHeight +
document.querySelector('[role="page-meta"]').offsetHeight +
document.querySelector('[role="text-editor"]').offsetHeight
);
this.editor.addListener(
EditorEvent.EDITOR_DELETE,
() => this.handleEditorOptions(EditorEvent.EDITOR_DELETE),
false
);
this.editor.addListener(
EditorEvent.EDITOR_UPLOAD_POST_IMAGE,
() => this.handleEditorOptions(EditorEvent.EDITOR_UPLOAD_POST_IMAGE),
false
);
this.editor.addListener(
EditorEvent.EDITOR_UPDATE,
() => this.handleEditorOptions(EditorEvent.EDITOR_UPDATE),
false
);
this.editor.addListener(
EditorEvent.EDITOR_SAVE,
() => this.handleEditorOptions(EditorEvent.EDITOR_SAVE),
false
);
document.getElementById('post-image-upload').addEventListener(
'change',
e => {
this.handleImageUpload(e.target.id, e.target.files);
},
false
);
/*
TinyDatePicker(document.getElementById('post-date'), {
mode: 'dp-below',
format() {
//return self.dateUtils.getDate('origin', date);
}
});
*/
this.start();
}
}
//--------------------------
// methods
//--------------------------
start() {
if (document.querySelector('[role="file-drop"]')) {
//insert fileManager here
this.fm = new FileManager(
document.querySelector('[role="file-drop"]'),
document.getElementById('page-files-upload'),
document.querySelector('[role="page-images-list"]'),
document.querySelector('[role="page-files-list"]')
);
var optionButtons = document.querySelectorAll('.post-option-btn');
for (var i = 0, length = optionButtons.length; i < length; i++) {
optionButtons[i].addEventListener(
'click',
e => this.handlePostOptions(e),
false
);
}
}
}
//--------------------------
// event handlers
//--------------------------
handlePostOptions(e) {
let currentOption = null;
switch (e.target.id) {
case 'option-page-icon':
case 'option-menu-pin':
currentOption = document.getElementById('option-menu-pin');
break;
case 'option-feature-icon':
case 'option-feature':
currentOption = document.getElementById('option-feature');
break;
case 'option-published-icon':
case 'option-published':
currentOption = document.getElementById('option-published');
break;
}
if (currentOption != null) {
let active = currentOption.getAttribute('data-active');
active == 'false'
? currentOption.setAttribute('data-active', 'true')
: currentOption.setAttribute('data-active', 'false');
}
}
handleEditorOptions(e) {
if (this.processing) return;
let self = this;
switch (e) {
case EditorEvent.EDITOR_SAVE:
case EditorEvent.EDITOR_UPDATE:
var task = '';
e === EditorEvent.EDITOR_SAVE
? (task = TASK_PAGE_CREATE)
: (task = TASK_PAGE_EDIT);
new PageActions().collectInfo(this.fm.getFileOrder()).then(page => {
self.processing = true;
notify.alert('Writing down changes', null);
self.admin
.pageActions(task, page)
.then(r => {
self.processing = false;
if (
r.type === DataEvent.PAGE_ERROR ||
r.type === DataEvent.API_REQUEST_LAME
) {
notify.alert(r.message, false);
} else {
if (r.type === DataEvent.PAGE_UPDATED) {
notify.alert(r.message, true);
} else {
notify.alert(r.message, true);
window.location = '/dashboard/page/edit/' + r.id;
}
}
})
.catch(err => {
self.processing = false;
notify.alert(err, false);
});
});
break;
case EditorEvent.EDITOR_DELETE:
if (this.postLayout === 'index') {
notify.alert('Index cannot be deleted', false);
return;
}
if (confirm("AYE! You know you're deleting this post, right?")) {
new PageActions()
.collectInfo(this.fm.getFileOrder())
.then(page => {
self.processing = true;
this.admin
.pageActions(TASK_PAGE_DELETE, page)
.then(() => {
self.processing = false;
window.location = '/dashboard/pages';
})
.catch(err => {
self.processing = false;
notify.alert(err, false);
});
})
.catch(() => {});
} else {
// Do nothing!
}
break;
case EditorEvent.EDITOR_UPLOAD_POST_IMAGE:
document.getElementById('post-image-upload').click();
break;
}
}
handleImageUpload(type, files) {
let self = this;
notify.alert('Uploading Image', null);
let upload = new FormData();
upload.enctype = 'multipart/form-data';
upload.append('upload_files[]', files[0], files[0].name);
this.mm
.filesUpload(files[0].type, upload)
.then(result => {
if (result.message == 'File Uploaded. Great!') {
self.editor.notify(
EditorEvent.EDITOR_UPLOAD_POST_IMAGE,
result.filePath
);
notify.alert('Image Added to Entry', true);
} else {
notify.alert('Uh oh. Image not added', false);
}
})
.catch(() => {
notify.alert('Uh oh. Image not added', false);
});
}
}

@ -1,30 +0,0 @@
import PageEditor from "./PageEditor";
export default class PostIndex {
//--------------------------
// constructor
//--------------------------
constructor(page) {
this.currentPage = null;
this.choosePage(page);
this.start();
}
//--------------------------
// methods
//--------------------------
start() {}
choosePage(page) {
this.currentPage = "";
switch (page) {
case "edit":
case "add":
this.currentPage = new PageEditor();
break;
default:
//just chill
break;
}
}
//--------------------------
// event handlers
//--------------------------
}

@ -1,268 +0,0 @@
import SettingsActions from '../actions/SettingsActions';
import Maintenance from './MaintenanceManager';
import FipamoAdminAPI, { TASK_SYNC_SETTNIGS } from '../../libraries/FipamoAdminAPI';
import * as DataEvent from '../../../src/com/events/DataEvent';
import Mailer from '../actions/Mailer';
import Notifications from '../ui/Notifications';
const notify = new Notifications();
export default class SettingsIndex {
//--------------------------
// constructor
//--------------------------
constructor() {
this.processing = false;
this.start();
this.admin = new FipamoAdminAPI(null);
this.mm = new Maintenance(null, null);
}
//--------------------------
// methods
//--------------------------
start() {
let self = this;
//handle save button
document.getElementById('save-toggle').addEventListener('click', () =>
new SettingsActions()
.getInfo()
.then(data => {
notify.alert('Saving Settings', null);
self.admin.sync(TASK_SYNC_SETTNIGS, data).then(r => {
if (r.type == DataEvent.SETTINGS_UPDATED) {
notify.alert(r.message, true);
} else {
notify.alert(r.message, true);
}
});
})
.catch(() => {
//console.log(err);
})
);
//handle set up image uploads
document.querySelector('[role="avatar"]').addEventListener('click', () => {
document.getElementById('avatar-upload').click();
});
document.querySelector('[role="background"]').addEventListener('click', () => {
document.getElementById('background-upload').click();
});
document.getElementById('avatar-upload').addEventListener(
'change',
e => {
self.handleImageUpload(e.target.id, e.target.files);
},
false
);
document.getElementById('background-upload').addEventListener(
'change',
e => {
self.handleImageUpload(e.target.id, e.target.files);
},
false
);
//handle api access toggle
var apiButton = document.getElementById('api-access-toggle');
var apiStatus = document.getElementById('api-status');
apiButton.addEventListener('click', e => {
e.stopPropagation();
e.preventDefault();
if (apiButton.getAttribute('data-enabled') == 'false') {
apiButton.setAttribute('data-enabled', 'true');
apiStatus.innerHTML = 'API ACCESS IS ENABLED';
} else {
apiButton.setAttribute('data-enabled', 'false');
apiStatus.innerHTML = 'API ACCESS IS DISABLED';
}
});
//handle dynamic page rendering
var dynamicRenderButton = document.getElementById('dynamic-render-toggle');
var dynamicRenderStatus = document.getElementById('dynamic-render-status');
dynamicRenderButton.addEventListener('click', e => {
e.stopPropagation();
e.preventDefault();
if (dynamicRenderButton.getAttribute('data-enabled') == 'false') {
dynamicRenderButton.setAttribute('data-enabled', 'true');
dynamicRenderStatus.innerHTML = 'DYNAMIC PAGE RENDERING';
} else {
dynamicRenderButton.setAttribute('data-enabled', 'false');
dynamicRenderStatus.innerHTML = 'STATIC PAGE RENDERING';
}
});
document
.getElementById('send-mail')
.addEventListener('click', e => this.handleMailer(e));
document
.getElementById('publish-pages')
.addEventListener('click', e => this.handlePublished(e));
//handle page render on save toggle
document
.getElementById('render-toggle')
.addEventListener('click', e => this.toggleRender(e));
//handle theme toggle
let themeBtns = document.querySelectorAll('.theme-select');
for (var i = 0, length = themeBtns.length; i < length; i++) {
themeBtns[i].addEventListener('click', e => this.handleThemes(e));
}
//handle mail options
let mailBtn = document.querySelectorAll('.mail-option');
for (i = 0, length = mailBtn.length; i < length; i++) {
mailBtn[i].addEventListener('click', e => this.handleMailOptions(e));
}
//handle backup from settings [disabled]
document
.getElementById('create-backup')
.addEventListener('click', e => this.handleBackup(e));
/*
document
.getElementById("reindex-pages")
.addEventListener("click", (e) => this.handleReindex(e));
*/
}
//--------------------------
// event handlers
//--------------------------
togglePrivacy(e) {
e.stopPropagation();
e.preventDefault();
if (e.target.getAttribute('data-private') == 'false') {
e.target.setAttribute('data-private', 'true');
e.target.innerHTML = 'SITE IS PUBLIC';
} else {
e.target.setAttribute('data-private', 'false');
e.target.innerHTML = 'SITE IS PRIVATE';
}
}
toggleRender(e) {
e.stopPropagation();
e.preventDefault();
let button = document.getElementById('render-toggle');
if (button.getAttribute('data-render') == 'false') {
button.setAttribute('data-render', 'true');
//e.target.innerHTML = 'RENDER PAGES ON SAVE';
} else {
button.setAttribute('data-render', 'false');
//e.target.innerHTML = "DON'T RENDER PAGES ON SAVE";
}
}
handleMailer() {
let mailer = new Mailer();
mailer.testMail();
//mailer.sendMail();
}
handleThemes(e) {
e.stopPropagation();
e.preventDefault();
let themes = document.querySelectorAll('.theme-select');
for (var i = 0, length = themes.length; i < length; i++) {
e.target.id == themes[i].id
? themes[i].setAttribute('data-enabled', 'true')
: themes[i].setAttribute('data-enabled', 'false');
}
}
handleMailOptions(e) {
e.preventDefault();
e.stopPropagation();
let smtp = document.getElementById('mail-smtp');
let mailgun = document.getElementById('mail-mg');
let mail = document.querySelectorAll('.mail-option');
for (var i = 0, length = mail.length; i < length; i++) {
if (e.target.id == mail[i].id) {
mail[i].setAttribute('data-enabled', 'true');
if (e.target.id == 'option-smtp') {
smtp.setAttribute('data-enabled', 'true');
mailgun.setAttribute('data-enabled', 'false');
} else if (e.target.id == 'option-none') {
smtp.setAttribute('data-enabled', 'false');
mailgun.setAttribute('data-enabled', 'false');
} else {
smtp.setAttribute('data-enabled', 'false');
mailgun.setAttribute('data-enabled', 'true');
}
} else {
mail[i].setAttribute('data-enabled', 'false');
}
}
}
handleImageUpload(type, files) {
notify.alert('Uploading Image... ', null);
let self = this;
notify.alert('Uploading Image', null);
let upload = new FormData();
upload.enctype = 'multipart/form-data';
upload.append('source', type);
upload.append('upload_files[]', files[0], files[0].name);
this.mm
.filesUpload(files[0].type, upload)
.then(r => {
if (type == 'avatar-upload') {
notify.alert(r.message, true);
document.querySelector('[role="avatar"]').style.background =
'url(' + r.filePath + ') no-repeat center center / cover';
} else {
notify.alert(r.message, true);
document.querySelector('[role="background"]').style.background =
'url(' + r.filePath + ') no-repeat center center / cover';
}
})
.catch(() => {
//console.log(err)
});
}
handlePublished(e) {
if (this.processing) return;
e.preventDefault();
e.stopPropagation();
let self = this;
let task = { task: 'PUBLISH_ALL' };
this.processing = true;
notify.alert('Publishing site...', null);
this.admin
.publish(task)
.then(r => {
self.processing = false;
notify.alert(r.message, true);
})
.catch(err => {
self.processing = false;
notify.alert(err, false);
});
}
handleBackup(e) {
e.preventDefault();
e.stopPropagation();
notify.alert('Creating backup', null);
this.mm
.backup()
.then(r => {
notify.alert(r.message, true);
})
.catch(err => {
notify.alert(err, false);
});
}
handleReindex(e) {
if (this.processing) return;
let self = this;
e.preventDefault();
e.stopPropagation();
let task = { task: 'cleanup pages indexes' };
this.processing = true;
notify.alert('Cleaning up page indexes', null);
this.admin
.handleReindex(task)
.then(r => {
self.processing = false;
notify.alert(r.message, true);
})
.catch(err => {
self.processing = false;
notify.alert(err, false);
});
}
}

@ -1,21 +0,0 @@
export const MEMBER_STATUS = 'memberStatus';
export const LOGIN_STATUS = 'loginStatus';
export const SUPPORTER_FOUND = 'SUPPORTER FOUND';
export const SUPPORTER_LISTED = 'SUPPORTER LISTED';
export const SUPPORTER_NOT_FOUND = 'SUPPORTER NOT FOUND';
export const MEMBER_ADDED = 'MEMBER ADDED';
export const MEMBER_NOT_ADDED = 'MEMBER NOT ADDED';
export const MEMBER_LOGIN_GOOD = 'MEMBER LOGIN GOOD';
export const MEMBER_LOGIN_LAME = 'MEMBER LOGIN LAME';
export const MEMBER_EXISTS = 'USER ALREADY EXISTS';
export const MEMBER_LOGIN_MISSING = 'Missing credentials';
class AuthEvent {
//--------------------------
// methods
//--------------------------
//--------------------------
// event handlers
//--------------------------
}
export default new AuthEvent();

@ -1,51 +0,0 @@
export const AUTH_STATUS = "getAuthStatus";
export const REQUEST_GOOD = "requestGood";
export const REQUEST_LAME = "requestLame";
export const API_REQUEST_GOOD = "apiUseAuthorized";
export const API_REQUEST_LAME = "apiUseNotAuthorized";
export const IMG_REQUEST_GOOD = "imgRequestGood";
export const IMG_REQUEST_LAME = "imgRequestLame";
export const SETTINGS_LOADED = "settingsLoaded";
export const POST_IMAGE_ADDED = "postImageAdded";
export const FEATURE_IMAGE_ADDED = "featureImageAdded";
export const PAGE_ERROR = "postError";
export const PAGE_ADDED = "postAdded";
export const PAGE_UPDATED = "postUpdated";
export const PAGE_DELETED = "postImageAdded";
export const PAGES_RENDERED = "pagesRendered";
export const PAGES_NOT_RENDERED = "pagesNotRendered";
export const TAG_PAGES_RENDERED = "tagPagesRendered";
export const TAG_PAGES_NOT_RENDERED = "tagPagesNotRendered";
export const SETTINGS_UPDATED = "settingsUpdated";
export const SETTINGS_NOT_UPDATED = "settingsNotUpdated";
export const MENU_ADD_ITEM = "menuAddItem";
export const MENU_DELETE_ITEM = "menuDeleteItem";
export const MENU_UPDATED = "menuUpdated";
export const AVATAR_UPLOADED = "avatarUploaded";
export const SITE_BACKGROUND_UPLOADED = "siteBackgroundUploaded";
export const UPLOAD_PROGRESS = "uploadProgress";
export const API_PAGE_WRITE = "writingItDown";
export const API_PAGE_CREATE = "writingNewEntry";
export const API_PAGE_DELETE = "erasingPage";
export const API_SETTINGS_WRITE = "savingSettings";
export const API_BACKUP_CREATE = "createBackup";
export const API_BACKUP_DOWNLOAD = "downloadBackup";
export const API_BACKUP_RESTORE = "downloadBackup";
export const API_IMAGES_UPLOAD = "uploadProfileImages";
export const API_RENDER_PAGES = "renderPages";
export const API_REINDEX_PAGES = "reindexPages";
export const API_INIT = "blogInit";
export const API_INIT_GOOD = "blogInitGood";
export const API_INIT_LAME = "blogInitLame";
export const API_GET_SECRET = "retrieveSecret";
export const API_RESET_PASS = "resetPassword";
export const SEND_MAIL = "sendMail";
class DataEvent {
//--------------------------
// methods
//--------------------------
//--------------------------
// event handlers
//--------------------------
}
export default new DataEvent();

@ -1,14 +0,0 @@
export const EDITOR_DELETE = 'editorDelete';
export const EDITOR_UPLOAD_POST_IMAGE = 'editorUploadImage';
export const EDITOR_SAVE = 'editorSave';
export const EDITOR_UPDATE = 'editorUpdate';
class EditorEvent {
//--------------------------
// methods
//--------------------------
//--------------------------
// event handlers
//--------------------------
}
export default new EditorEvent();

@ -1,52 +0,0 @@
class EventEmitter {
//--------------------------
// constructor
//--------------------------
constructor() {
this.listeners = new Map();
}
//--------------------------
// methods
//--------------------------
addListener(label, callback) {
this.listeners.has(label) || this.listeners.set(label, []);
this.listeners.get(label).push(callback);
}
removeListener(label, callback) {
var isFunction = function(obj) {
return typeof obj == 'function' || false;
};
var listeners = this.listeners.get(label),
index;
if (listeners && listeners.length) {
index = listeners.reduce((i, listener, index) => {
return isFunction(listener) && listener === callback ? (i = index) : i;
}, -1);
if (index > -1) {
listeners.splice(index, 1);
this.listeners.set(label, listeners);
return true;
}
}
return false;
}
emitEvent(label, ...args) {
var listeners = this.listeners.get(label);
if (listeners && listeners.length) {
listeners.forEach(listener => {
listener(...args);
});
return true;
}
return false;
}
//--------------------------
// event handlers
//--------------------------
}
export default EventEmitter;

@ -1,296 +0,0 @@
import Sortable from 'sortablejs';
import anime from 'animejs/lib/anime.es.js';
import DataUtils from '../utils/DataUtils';
import Notfications from './Notifications.js';
import Maintenance from '../controllers/MaintenanceManager';
const notify = new Notfications();
export default class FileManager {
//--------------------------
// constructor
//--------------------------
constructor(upload, input, imageList, fileList) {
this.mm = new Maintenance(null, null, document.getElementById('notify-progress'));
this.upload = upload;
this.input = input;
this.imageList = imageList;
this.fileList = fileList;
this.accetableFiles = [
'image/jpeg',
'image/gif',
'image/png',
'image/svg',
'audio/mpeg',
'video/mp4',
'application/pdf',
'text/plain',
'text/rtf'
];
this.files = [];
this.sortedFiles = [];
this.storage = [];
this.mediaSort = Sortable.create(this.imageList, {
animation: 150,
onUpdate: () => {
//notify.alert('REINDEXING MEDIA', null);
//this.updateFiles();
}
});
this.fileSort = Sortable.create(this.fileList, {
animation: 150,
onUpdate: () => {
//notify.alert('REINDEXING FILES', null);
//this.updateFiles();
}
});
this.start();
}
//--------------------------
// methods
//--------------------------
start() {
this.upload.addEventListener('dragover', e => this.handleFileActions(e), false);
this.upload.addEventListener('drop', e => this.handleFileActions(e), false);
this.input.addEventListener('change', e => this.handleFileActions(e), false);
var removeMedia = document.querySelectorAll('.media-remove');
for (var i = 0, length = removeMedia.length; i < length; i++) {
removeMedia[i].addEventListener(
'click',
e => this.removeFile(e, 'media'),
false
);
}
}
getFileOrder() {
let imgList = '';
let fileList = '';
for (var i = 0, length = this.imageList.childNodes.length; i < length; i++) {
let div = this.imageList.childNodes[i];
imgList = imgList + div.getAttribute('data-source') + ',';
}
for (var i = 0, length = this.fileList.childNodes.length; i < length; i++) {
let div = this.fileList.childNodes[i];
fileList = fileList + div.getAttribute('data-source') + ',';
}
let media = { images: imgList, files: fileList };
return media;
}
sortFiles(files) {
var self = this;
for (var i = 0, file; (file = files[i]); i++) {
var reader = new FileReader();
// Closure to capture the file information
reader.onload = (theFile => {
return function (f) {
//create remove button object
var remove = document.createElement('button');
var removeIcon = document.createElement('i');
removeIcon.classList.add('ti', 'ti-x');
remove.className = 'media-remove';
remove.appendChild(removeIcon);
//remove.setAttribute('id', mediaCount);
remove.addEventListener(
'click',
e => self.removeFile(e, 'media'),
false
);
//upload the file
let upload = new FormData();
upload.enctype = 'multipart/form-data';
upload.append('upload_files[]', theFile, theFile.name);
let item = null;
let progress = null;
// sort files
switch (theFile.type) {
case 'image/jpg':
case 'image/jpeg':
case 'image/gif':
case 'image/svg':
case 'image/png':
item = self.itemFactory('img-item');
progress = document.getElementById(
'pgs' + item.getAttribute('id')
);
self.mm
.filesUpload(theFile.type, upload, progress)
.then(result => {
item.setAttribute('data-source', result.filePath);
item.style.background =
'url(' +
f.target.result +
') no-repeat center center / cover';
anime({
targets: progress,
width: 0,
easing: 'easeInOutQuint',
duration: 1000,
complete: () => {
item.removeChild(progress);
item.appendChild(remove);
}
});
});
break;
case 'video/mp4':
item = self.itemFactory('video-item');
progress = document.getElementById(
'pgs' + item.getAttribute('id')
);
self.mm
.filesUpload(theFile.type, upload, progress)
.then(result => {
item.setAttribute('data-source', result.filePath);
let video = document.createElement('video');
let source = document.createElement('source');
source.src = f.target.result;
video.appendChild(source);
item.appendChild(video);
anime({
targets: progress,
width: 0,
easing: 'easeInOutQuint',
duration: 1000,
complete: () => {
item.removeChild(progress);
item.appendChild(remove);
}
});
});
break;
case 'audio/mpeg':
item = self.itemFactory('audio-item');
progress = document.getElementById(
'pgs' + item.getAttribute('id')
);
self.mm
.filesUpload(theFile.type, upload, progress)
.then(result => {
item.setAttribute('data-source', result.filePath);
let audio = document.createElement('audio');
audio.setAttribute('controls', true);
let source = document.createElement('source');
source.src = f.target.result;
audio.appendChild(source);
item.appendChild(audio);
anime({
targets: progress,
width: 0,
easing: 'easeInOutQuint',
duration: 1000,
complete: () => {
item.removeChild(progress);
item.appendChild(remove);
}
});
});
break;
case 'application/pdf':
case 'text/plain':
case 'text/rtf':
item = self.itemFactory('file-item');
progress = document.getElementById(
'pgs' + item.getAttribute('id')
);
self.mm
.filesUpload(theFile.type, upload, progress)
.then(result => {
item.setAttribute('data-source', result.filePath);
let link = document.createElement('a');
link.href = result.filePath;
link.innerHTML = result.fileName;
item.appendChild(link);
anime({
targets: progress,
width: 0,
easing: 'easeInOutQuint',
duration: 1000,
complete: () => {
item.removeChild(progress);
item.appendChild(remove);
}
});
});
break;
}
};
})(file);
// Read in the image file as a data URL.
reader.readAsDataURL(file);
}
}
itemFactory(type = null) {
//get counts for lists
var mediaCount = this.imageList.childNodes.length;
var fileCount = this.fileList.childNodes.length;
if (mediaCount < 0) mediaCount = 0;
if (fileCount < 0) fileCount = 0;
var item = document.createElement('div');
item.className = type;
var progress = document.createElement('div');
progress.className = 'item-progress';
item.appendChild(progress);
if (type == 'img-item' || type == 'video-item') {
this.imageList.appendChild(item);
progress.setAttribute('id', 'pgs' + mediaCount);
item.setAttribute('id', mediaCount);
} else {
this.fileList.appendChild(item);
progress.setAttribute('id', 'pgs' + fileCount);
item.setAttribute('id', fileCount);
}
return item;
}
//--------------------------
// event handlers
//--------------------------
removeFile(e) {
var item = e.target.parentNode.parentNode;
switch (item.className) {
case 'img-item':
case 'video-item':
this.imageList.removeChild(item);
break;
case 'audio-item':
case 'file-item':
this.fileList.removeChild(item);
break;
}
notify.alert('File Removed!', true);
}
handleFileActions(e) {
e.stopPropagation();
e.preventDefault();
let self = this;
let rawList = [];
let sortedList = [];
let notOnTheList = [];
switch (e.type) {
case 'dragover':
e.dataTransfer.dropEffect = 'copy'; // Explicitly show this is a copy.
break;
case 'change':
case 'drop':
e.type == 'drop'
? (rawList = e.dataTransfer.files)
: (rawList = e.target.files);
for (var i = 0, f; (f = rawList[i]); i++) {
// check witch files are cool to upload
if (this.accetableFiles.includes(f.type)) {
sortedList.push(f);
} else {
notOnTheList.push(f);
}
}
//send for sorting
self.sortFiles(sortedList);
break;
}
}
}

@ -1,28 +0,0 @@
export default class Menu {
//--------------------------
// constructor
//--------------------------
constructor() {
this.mobile = false;
this.mobileMenu = document.querySelector('[role="mobile-menu"]');
document
.querySelector('[role="menu-toggle"]')
.addEventListener('click', e => this.handleMobile(e));
}
//--------------------------
// methods
//--------------------------
start() {}
//--------------------------
// event handlers
//--------------------------
handleMobile(e) {
if (this.mobile) {
this.mobile = false;
this.mobileMenu.style.display = 'none';
} else {
this.mobile = true;
this.mobileMenu.style.display = 'inline';
}
}
}

@ -1,109 +0,0 @@
import anime from 'animejs/lib/anime.es.js';
const notifcation = document.querySelector('[role="notify-message"]');
const notify = document.getElementById('notify-message');
const responseText = document.querySelector('[role="response-text"]');
const notifyText = document.querySelector('[role="notify-text"]');
const notifyIcons = document.querySelector('[role="notify-icons"]');
//const notifyProgress = document.getElementById('notify-progress');
const iconGood = document.querySelector('[role="notify-good"]');
const iconNotGood = document.querySelector('[role="notify-notgood"]');
const iconWorking = document.querySelector('[role="notify-working"]');
export default class Notfications {
//--------------------------
// constructor
//--------------------------
constructor() {}
//--------------------------
// methods
//--------------------------
alert(text, status) {
iconWorking.style.display = 'none';
iconGood.style.display = 'none';
iconNotGood.style.display = 'none';
var color = '';
responseText.innerHTML = text;
if (status !== null) {
if (status) {
color = '#32cd32';
iconWorking.style.display = 'none';
iconGood.style.display = 'block';
} else {
color = '#F64747';
iconWorking.style.display = 'none';
iconNotGood.style.display = 'block';
}
} else {
color = '#200317';
iconWorking.style.display = 'block';
}
new anime({
targets: document.querySelector('[role="top-nav"]'),
rotateX: '180deg',
easing: 'easeOutQuint'
});
new anime({
targets: document.querySelector('[role="notify"]'),
rotateX: '10deg',
easing: 'easeOutQuint',
complete: () => {
new anime({
targets: notifyIcons,
width: 39,
opacity: 1,
easing: 'easeInQuint',
duration: 300
});
new anime({
targets: notifyText,
backgroundColor: color,
opacity: 1,
easing: 'easeInOutQuad',
duration: 400,
complete: () => {
setTimeout(() => {
if (status !== null) {
anime({
targets: notifyText,
backgroundColor: color,
opacity: 0,
easing: 'easeInOutQuad',
duration: 400
});
anime({
targets: notifyIcons,
width: 0,
opacity: 0,
easing: 'easeOutQuint',
duration: 350
});
new anime({
targets: document.querySelector('[role="top-nav"]'),
rotateX: '0deg',
easing: 'easeOutQuint'
});
new anime({
targets: document.querySelector('[role="notify"]'),
rotateX: '180deg',
easing: 'easeOutQuint'
});
}
}, 2500);
}
});
}
});
}
//--------------------------
// event handlers
//--------------------------
}

@ -1,203 +0,0 @@
import * as DataEvent from '../events/DataEvent';
import EventEmitter from '../events/EventEmitter';
import * as EditorEvent from '../events/EditorEvent';
import Prism from 'prismjs';
class TextEditor extends EventEmitter {
/**
* Text Editor UI Component
* @constructor
* @param {object} textEditor - Text area that will edit text
* @param {number} scrollLimit - YPos where editor position will become fixed
*/
//--------------------------
// constructor
//--------------------------
constructor(textEditor, scrollLimit) {
super();
document
.querySelector('[role="text-editor-control"]')
.addEventListener('scroll', e => {
console.log('HERE');
});
document.body.addEventListener('scroll', e => {
var fixLimit = scrollLimit;
console.log('POSITION', document.body.scrollTop + ' : ' + fixLimit);
if (document.body.scrollTop + 5 >= fixLimit) {
document
.querySelector('[role="text-editor-control"]')
.classList.add('control-freeze');
} else {
document
.querySelector('[role="text-editor-control"]')
.classList.remove('control-freeze');
}
});
document.getElementById('edit').addEventListener('input', e => {
let result_element = document.querySelector('#highlight-content');
this.textEditor = textEditor;
// Update code
let text = e.target.value;
result_element.innerHTML = text
.replace(new RegExp('&', 'g'), '&amp;')
.replace(new RegExp('<', 'g'), '&lt;');
let editorHeight = document.getElementById('highlight').offsetHeight;
document.querySelector('[role="edit-post-wrapper"]').style.height =
editorHeight + 'px';
e.target.style.height = editorHeight + 30 + 'px'; //TODO: yeah, it's ugly but it works for now, fix soon
// Syntax Highlight
Prism.highlightElement(result_element);
});
document.getElementById('edit').addEventListener('scroll', e => {
/* Scroll result to scroll coords of event - sync with textarea */
let result_element = document.querySelector('#highlight');
// Get and set x and y
result_element.scrollTop = e.scrollTop;
result_element.scrollLeft = e.scrollLeft;
});
document.getElementById('edit').dispatchEvent(new Event('input'));
this.setInputs();
//freeze editor formatting so it doesn't scroll off screen
}
//--------------------------
// methods
//--------------------------
setInputs() {
var editorButtons = document.querySelectorAll('.editor-button');
for (var i = 0, length = editorButtons.length; i < length; i++) {
editorButtons[i].addEventListener(
'click',
e => this.handleEditorOption(e),
false
);
}
}
notify(type, data) {
switch (type) {
case DataEvent.PAGE_UPDATED:
document.getElementById('submit-update').classList.add('icon-hide');
document.getElementById('submit-good').classList.remove('icon-hide');
document.getElementById('edit-update').classList.remove('submit-start');
document.getElementById('edit-update').classList.add('submit-cool');
setTimeout(() => {
document
.getElementById('submit-update')
.classList.remove('icon-hide');
document.getElementById('submit-good').classList.add('icon-hide');
document.getElementById('edit-update').classList.add('submit-start');
document
.getElementById('edit-update')
.classList.remove('submit-cool');
}, 2000);
break;
case DataEvent.PAGE_ADDED:
// do nothing
break;
case EditorEvent.EDITOR_UPLOAD_POST_IMAGE: {
let len = this.textEditor.value.length;
let start = this.textEditor.selectionStart;
let end = this.textEditor.selectionEnd;
let insert = '![image alt text](' + data + ')';
this.textEditor.value =
this.textEditor.value.substring(0, start) +
insert +
this.textEditor.value.substring(end, len);
document.getElementById('edit').dispatchEvent(new Event('input'));
break;
}
}
}
//--------------------------
// event handlers
//--------------------------
handleEditorOption(e) {
e.preventDefault();
let len = this.textEditor.value.length;
let start = this.textEditor.selectionStart;
let end = this.textEditor.selectionEnd;
let selectedText = this.textEditor.value.substring(start, end);
let insert = '';
switch (e.target.id) {
case 'edit-bold':
insert = '**' + selectedText + '**';
this.textEditor.value =
this.textEditor.value.substring(0, start) +
insert +
this.textEditor.value.substring(end, len);
break;
case 'edit-italic':
insert = '*' + selectedText + '*';
//console.log(this.textEditor);
this.textEditor.value =
this.textEditor.value.substring(0, start) +
insert +
this.textEditor.value.substring(end, len);
break;
case 'edit-strikethrough':
insert = '~~' + selectedText + '~~';
this.textEditor.value =
this.textEditor.value.substring(0, start) +
insert +
this.textEditor.value.substring(end, len);
break;
case 'edit-header1':
insert = '# ' + selectedText + '\n';
this.textEditor.value =
this.textEditor.value.substring(0, start) +
insert +
this.textEditor.value.substring(end, len);
break;
case 'edit-header2':
insert = '## ' + selectedText + '\n';
this.textEditor.value =
this.textEditor.value.substring(0, start) +
insert +
this.textEditor.value.substring(end, len);
break;
case 'edit-header3':
insert = '### ' + selectedText + '\n';
this.textEditor.value =
this.textEditor.value.substring(0, start) +
insert +
this.textEditor.value.substring(end, len);
break;
case 'edit-link':
{
let url = prompt("Let's get that url, boss");
let link = url.toLowerCase();
insert = '[' + selectedText + '](' + link + ')';
this.textEditor.value =
this.textEditor.value.substring(0, start) +
insert +
this.textEditor.value.substring(end, len);
}
break;
case 'edit-image':
//this.caretPos = position(this.textEditor).pos;
this.emitEvent(EditorEvent.EDITOR_UPLOAD_POST_IMAGE);
break;
case 'submit-save':
case 'edit-save':
this.emitEvent(EditorEvent.EDITOR_SAVE);
break;
case 'submit-update':
case 'edit-update':
this.emitEvent(EditorEvent.EDITOR_UPDATE);
break;
case 'edit-delete':
this.emitEvent(EditorEvent.EDITOR_DELETE);
break;
default:
//do stuff
break;
}
document.getElementById('edit').dispatchEvent(new Event('input'));
}
}
export default TextEditor;

@ -1,95 +0,0 @@
export default class DataUtils {
//--------------------------
// constructor
//--------------------------
constructor() {}
//--------------------------
// methods
//--------------------------
imgLoad(url) {
'use strict';
// Create new promise with the Promise() constructor;
// This has as its argument a function with two parameters, resolve and reject
return new Promise(function (resolve, reject) {
// Standard XHR to load an image
var request = new XMLHttpRequest();
request.open('GET', url);
request.responseType = 'blob';
// When the request loads, check whether it was successful
request.onload = function () {
if (request.status === 200) {
// If successful, resolve the promise by passing back the request response
resolve(request.response);
} else {
// If it fails, reject the promise with a error message
reject(
new Error(
"Image didn't load successfully; error code: " +
request.status +
' ' +
request.statusText
)
);
}
};
request.onerror = function () {
// Also deal with the case when the entire request fails to begin with
// This is probably a network error, so reject the promise with an appropriate message
reject(new Error('There was a network error.'));
};
// Send the request
request.send();
});
}
loadImage(src) {
'use strict';
let self = this;
return new Promise(function (resolve, reject) {
// Get a reference to the body element, and create a new image object
var myImage = new Image();
myImage.crossOrigin = ''; // or "anonymous"
// Call the function with the URL we want to load, but then chain the
// promise then() method on to the end of it. This contains two callbacks
self.imgLoad(src).then(
function (response) {
// The first runs when the promise resolves, with the request.reponse specified within the resolve() method.
var imageURL = window.URL.createObjectURL(response);
resolve(imageURL);
//$('background-content').setStyle('background-image', 'url('+imageURL+')') //myImage.src = imageURL;
//console.log(imageURL);
//body.appendChild(myImage);
// The second runs when the promise is rejected, and logs the Error specified with the reject() method.
},
function (Error) {
reject(Error);
}
);
});
}
/**
* Create a function to convert the serialize and convert the form data to JSON
* @param : $('#form_example');
* @return a JSON Stringify
*/
formDataToJSON(form) {
let object = {};
let formData = new FormData(form);
formData.forEach((value, key) => {
if (!object.hasOwnProperty(key)) {
object[key] = value;
return;
}
if (!Array.isArray(object[key])) {
object[key] = [object[key]];
}
object[key].push(value);
});
//let json = JSON.stringify(object);
return object;
}
//--------------------------
// event handlers
//--------------------------
}

@ -1,68 +0,0 @@
class StringUtils {
//--------------------------
// constructor
//--------------------------
constructor() {}
//--------------------------
// methods
//--------------------------
cleanString(string) {
var clean = string
.replace(/(^\-+|[^a-zA-Z0-9\/_| -]+|\-+$)/g, '')
.toLowerCase()
.replace(/[\/_| -]+/g, '-');
return clean;
}
decodeHTML(string, quote_style) {
var optTemp = 0,
i = 0,
noquotes = false;
if (typeof quote_style === 'undefined') {
quote_style = 2;
}
string = string
.toString()
.replace(/&lt;/g, '<')
.replace(/&gt;/g, '>');
var OPTS = {
ENT_NOQUOTES: 0,
ENT_HTML_QUOTE_SINGLE: 1,
ENT_HTML_QUOTE_DOUBLE: 2,
ENT_COMPAT: 2,
ENT_QUOTES: 3,
ENT_IGNORE: 4
};
if (quote_style === 0) {
noquotes = true;
}
if (typeof quote_style !== 'number') {
// Allow for a single string or an array of string flags
quote_style = [].concat(quote_style);
for (i = 0; i < quote_style.length; i++) {
// Resolve string input to bitwise e.g. 'PATHINFO_EXTENSION' becomes 4
if (OPTS[quote_style[i]] === 0) {
noquotes = true;
} else if (OPTS[quote_style[i]]) {
optTemp = optTemp | OPTS[quote_style[i]];
}
}
quote_style = optTemp;
}
if (quote_style & OPTS.ENT_HTML_QUOTE_SINGLE) {
string = string.replace(/&#0*39;/g, "'"); // PHP doesn't currently escape if more than one 0, but it should
// string = string.replace(/&apos;|&#x0*27;/g, "'"); // This would also be useful here, but not a part of PHP
}
if (!noquotes) {
string = string.replace(/&quot;/g, '"');
}
// Put this in last place to avoid escape being double-decoded
string = string.replace(/&amp;/g, '&');
return string;
}
//--------------------------
// event handlers
//--------------------------
}
export default StringUtils;
Loading…
Cancel
Save