Replaced Moment with Carbon #84
36 changed files with 6997 additions and 7465 deletions
3
.gitignore
vendored
3
.gitignore
vendored
|
@ -39,9 +39,6 @@ public/assets/*
|
|||
public/assets/css/*
|
||||
!public/assets/css/dash
|
||||
!public/assets/scripts
|
||||
public/assets/scripts/*
|
||||
!public/assets/scripts/dash.js
|
||||
!public/assets/scripts/dash.js.map
|
||||
!public/assets/images
|
||||
public/assets/images/*
|
||||
!public/assets/images/global/
|
||||
|
|
File diff suppressed because it is too large
Load diff
File diff suppressed because one or more lines are too long
211
public/assets/scripts/dash/app/Base.js
Normal file
211
public/assets/scripts/dash/app/Base.js
Normal file
|
@ -0,0 +1,211 @@
|
|||
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';
|
||||
}
|
||||
}
|
||||
}
|
9
public/assets/scripts/dash/app/EditPage.js
Normal file
9
public/assets/scripts/dash/app/EditPage.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
import Editor from './controllers/PageEditor.js';
|
||||
|
||||
document.addEventListener(
|
||||
'DOMContentLoaded',
|
||||
function () {
|
||||
new Editor();
|
||||
},
|
||||
false
|
||||
);
|
9
public/assets/scripts/dash/app/Start.js
Normal file
9
public/assets/scripts/dash/app/Start.js
Normal file
|
@ -0,0 +1,9 @@
|
|||
import Base from './Base';
|
||||
|
||||
document.addEventListener(
|
||||
'DOMContentLoaded',
|
||||
function () {
|
||||
new Base();
|
||||
},
|
||||
false
|
||||
);
|
44
public/assets/scripts/dash/app/actions/Mailer.js
Normal file
44
public/assets/scripts/dash/app/actions/Mailer.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
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
|
||||
//--------------------------
|
||||
}
|
35
public/assets/scripts/dash/app/actions/NavActions.js
Normal file
35
public/assets/scripts/dash/app/actions/NavActions.js
Normal file
|
@ -0,0 +1,35 @@
|
|||
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
|
||||
//--------------------------
|
||||
}
|
53
public/assets/scripts/dash/app/actions/PageActions.js
Normal file
53
public/assets/scripts/dash/app/actions/PageActions.js
Normal file
|
@ -0,0 +1,53 @@
|
|||
import StringUtils from '../utils/StringUtils.js';
|
||||
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
|
||||
//--------------------------
|
||||
}
|
79
public/assets/scripts/dash/app/actions/SettingsActions.js
Normal file
79
public/assets/scripts/dash/app/actions/SettingsActions.js
Normal file
|
@ -0,0 +1,79 @@
|
|||
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
|
||||
//--------------------------
|
||||
}
|
44
public/assets/scripts/dash/app/controllers/DashManager.js
Normal file
44
public/assets/scripts/dash/app/controllers/DashManager.js
Normal file
|
@ -0,0 +1,44 @@
|
|||
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
|
||||
//--------------------------
|
||||
}
|
292
public/assets/scripts/dash/app/controllers/MaintenanceManager.js
Normal file
292
public/assets/scripts/dash/app/controllers/MaintenanceManager.js
Normal file
|
@ -0,0 +1,292 @@
|
|||
//** 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 };
|
74
public/assets/scripts/dash/app/controllers/NavIndex.js
Normal file
74
public/assets/scripts/dash/app/controllers/NavIndex.js
Normal file
|
@ -0,0 +1,74 @@
|
|||
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;
|
||||
}
|
||||
}
|
||||
}
|
228
public/assets/scripts/dash/app/controllers/PageEditor.js
Normal file
228
public/assets/scripts/dash/app/controllers/PageEditor.js
Normal file
|
@ -0,0 +1,228 @@
|
|||
//TOOLS
|
||||
import FipamoAdminAPI, {
|
||||
TASK_PAGE_CREATE,
|
||||
TASK_PAGE_EDIT,
|
||||
TASK_PAGE_DELETE
|
||||
} from '../../libraries/FipamoAdminAPI.js';
|
||||
import Maintenance from './MaintenanceManager.js';
|
||||
import * as DataEvent from '../events/DataEvent.js';
|
||||
import PageActions from '../actions/PageActions.js';
|
||||
import * as EditorEvent from '../events/EditorEvent.js';
|
||||
//import TinyDatePicker from 'tiny-date-picker'; TODO: Reactivate for scheduled publishing
|
||||
import TextEditor from '../ui/TextEditor.js';
|
||||
import Notfications from '../ui/Notifications.js';
|
||||
import FileManager from '../ui/FileManager.js';
|
||||
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);
|
||||
});
|
||||
}
|
||||
}
|
30
public/assets/scripts/dash/app/controllers/PostIndex.js
Normal file
30
public/assets/scripts/dash/app/controllers/PostIndex.js
Normal file
|
@ -0,0 +1,30 @@
|
|||
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
|
||||
//--------------------------
|
||||
}
|
268
public/assets/scripts/dash/app/controllers/SettingsIndex.js
Normal file
268
public/assets/scripts/dash/app/controllers/SettingsIndex.js
Normal file
|
@ -0,0 +1,268 @@
|
|||
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('.avatar').addEventListener('click', () => {
|
||||
document.getElementById('avatar-upload').click();
|
||||
});
|
||||
document.querySelector('.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);
|
||||
});
|
||||
}
|
||||
}
|
21
public/assets/scripts/dash/app/events/AuthEvent.js
Normal file
21
public/assets/scripts/dash/app/events/AuthEvent.js
Normal file
|
@ -0,0 +1,21 @@
|
|||
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();
|
51
public/assets/scripts/dash/app/events/DataEvent.js
Normal file
51
public/assets/scripts/dash/app/events/DataEvent.js
Normal file
|
@ -0,0 +1,51 @@
|
|||
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();
|
14
public/assets/scripts/dash/app/events/EditorEvent.js
Normal file
14
public/assets/scripts/dash/app/events/EditorEvent.js
Normal file
|
@ -0,0 +1,14 @@
|
|||
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();
|
52
public/assets/scripts/dash/app/events/EventEmitter.js
Normal file
52
public/assets/scripts/dash/app/events/EventEmitter.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
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;
|
296
public/assets/scripts/dash/app/ui/FileManager.js
Normal file
296
public/assets/scripts/dash/app/ui/FileManager.js
Normal file
|
@ -0,0 +1,296 @@
|
|||
import Sortable from '../vendor/sortable.js';
|
||||
import anime from '../vendor/anime.es.js';
|
||||
import DataUtils from '../utils/DataUtils.js';
|
||||
import Notfications from './Notifications.js';
|
||||
import Maintenance from '../controllers/MaintenanceManager.js';
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
28
public/assets/scripts/dash/app/ui/Menu.js
Normal file
28
public/assets/scripts/dash/app/ui/Menu.js
Normal file
|
@ -0,0 +1,28 @@
|
|||
export default class Menu {
|
||||
//--------------------------
|
||||
// constructor
|
||||
//--------------------------
|
||||
constructor() {
|
||||
this.mobile = false;
|
||||
this.mobileMenu = document.querySelector('.mobile-menu');
|
||||
document
|
||||
.querySelector('.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';
|
||||
}
|
||||
}
|
||||
}
|
109
public/assets/scripts/dash/app/ui/Notifications.js
Normal file
109
public/assets/scripts/dash/app/ui/Notifications.js
Normal file
|
@ -0,0 +1,109 @@
|
|||
import anime from '../vendor/anime.es.js';
|
||||
const notifcation = document.querySelector('.notify-message');
|
||||
const notify = document.getElementById('notify-message');
|
||||
const responseText = document.querySelector('.response-text');
|
||||
const notifyText = document.querySelector('.notify-text');
|
||||
const notifyIcons = document.querySelector('.notify-icons');
|
||||
//const notifyProgress = document.getElementById('notify-progress');
|
||||
const iconGood = document.querySelector('.notify-good');
|
||||
const iconNotGood = document.querySelector('.notify-notgood');
|
||||
const iconWorking = document.querySelector('.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('.top-nav'),
|
||||
rotateX: '180deg',
|
||||
easing: 'easeOutQuint'
|
||||
});
|
||||
|
||||
new anime({
|
||||
targets: document.querySelector('.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('.top-nav'),
|
||||
rotateX: '0deg',
|
||||
easing: 'easeOutQuint'
|
||||
});
|
||||
|
||||
new anime({
|
||||
targets: document.querySelector('.notify'),
|
||||
rotateX: '180deg',
|
||||
easing: 'easeOutQuint'
|
||||
});
|
||||
}
|
||||
}, 2500);
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//--------------------------
|
||||
// event handlers
|
||||
//--------------------------
|
||||
}
|
206
public/assets/scripts/dash/app/ui/TextEditor.js
Normal file
206
public/assets/scripts/dash/app/ui/TextEditor.js
Normal file
|
@ -0,0 +1,206 @@
|
|||
import * as DataEvent from '../events/DataEvent.js';
|
||||
import EventEmitter from '../events/EventEmitter.js';
|
||||
import * as EditorEvent from '../events/EditorEvent.js';
|
||||
import Prism from '../vendor/prism.js';
|
||||
//import MarkdownLang from '../vendor/lang/prism-markdown.js';
|
||||
//Prism.language.md = '../vendor/lang/prism-markdown.js';
|
||||
//Prism.languauges['markdown'];
|
||||
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'), '&')
|
||||
.replace(new RegExp('<', 'g'), '<');
|
||||
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 = '';
|
||||
|
||||
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;
|
95
public/assets/scripts/dash/app/utils/DataUtils.js
Normal file
95
public/assets/scripts/dash/app/utils/DataUtils.js
Normal file
|
@ -0,0 +1,95 @@
|
|||
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
|
||||
//--------------------------
|
||||
}
|
68
public/assets/scripts/dash/app/utils/StringUtils.js
Normal file
68
public/assets/scripts/dash/app/utils/StringUtils.js
Normal file
|
@ -0,0 +1,68 @@
|
|||
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(/</g, '<')
|
||||
.replace(/>/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(/�*39;/g, "'"); // PHP doesn't currently escape if more than one 0, but it should
|
||||
// string = string.replace(/'|�*27;/g, "'"); // This would also be useful here, but not a part of PHP
|
||||
}
|
||||
if (!noquotes) {
|
||||
string = string.replace(/"/g, '"');
|
||||
}
|
||||
// Put this in last place to avoid escape being double-decoded
|
||||
string = string.replace(/&/g, '&');
|
||||
return string;
|
||||
}
|
||||
|
||||
//--------------------------
|
||||
// event handlers
|
||||
//--------------------------
|
||||
}
|
||||
export default StringUtils;
|
1310
public/assets/scripts/dash/app/vendor/anime.es.js
vendored
Normal file
1310
public/assets/scripts/dash/app/vendor/anime.es.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
227
public/assets/scripts/dash/app/vendor/lang/prism-markdown.js
vendored
Normal file
227
public/assets/scripts/dash/app/vendor/lang/prism-markdown.js
vendored
Normal file
|
@ -0,0 +1,227 @@
|
|||
Prism.languages.markdown = Prism.languages.extend('markup', {});
|
||||
Prism.languages.insertBefore('markdown', 'prolog', {
|
||||
'blockquote': {
|
||||
// > ...
|
||||
pattern: /^>(?:[\t ]*>)*/m,
|
||||
alias: 'punctuation'
|
||||
},
|
||||
'code': [
|
||||
{
|
||||
// Prefixed by 4 spaces or 1 tab
|
||||
pattern: /^(?: {4}|\t).+/m,
|
||||
alias: 'keyword'
|
||||
},
|
||||
{
|
||||
// `code`
|
||||
// ``code``
|
||||
pattern: /``.+?``|`[^`\n]+`/,
|
||||
alias: 'keyword'
|
||||
},
|
||||
{
|
||||
// ```optional language
|
||||
// code block
|
||||
// ```
|
||||
pattern: /^```[\s\S]*?^```$/m,
|
||||
greedy: true,
|
||||
inside: {
|
||||
'code-block': {
|
||||
pattern: /^(```.*(?:\r?\n|\r))[\s\S]+?(?=(?:\r?\n|\r)^```$)/m,
|
||||
lookbehind: true
|
||||
},
|
||||
'code-language': {
|
||||
pattern: /^(```).+/,
|
||||
lookbehind: true
|
||||
},
|
||||
'punctuation': /```/
|
||||
}
|
||||
}
|
||||
],
|
||||
'title': [
|
||||
{
|
||||
// title 1
|
||||
// =======
|
||||
|
||||
// title 2
|
||||
// -------
|
||||
pattern: /\S.*(?:\r?\n|\r)(?:==+|--+)/,
|
||||
alias: 'important',
|
||||
inside: {
|
||||
punctuation: /==+$|--+$/
|
||||
}
|
||||
},
|
||||
{
|
||||
// # title 1
|
||||
// ###### title 6
|
||||
pattern: /(^\s*)#+.+/m,
|
||||
lookbehind: true,
|
||||
alias: 'important',
|
||||
inside: {
|
||||
punctuation: /^#+|#+$/
|
||||
}
|
||||
}
|
||||
],
|
||||
'hr': {
|
||||
// ***
|
||||
// ---
|
||||
// * * *
|
||||
// -----------
|
||||
pattern: /(^\s*)([*-])(?:[\t ]*\2){2,}(?=\s*$)/m,
|
||||
lookbehind: true,
|
||||
alias: 'punctuation'
|
||||
},
|
||||
'list': {
|
||||
// * item
|
||||
// + item
|
||||
// - item
|
||||
// 1. item
|
||||
pattern: /(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,
|
||||
lookbehind: true,
|
||||
alias: 'punctuation'
|
||||
},
|
||||
'url-reference': {
|
||||
// [id]: http://example.com "Optional title"
|
||||
// [id]: http://example.com 'Optional title'
|
||||
// [id]: http://example.com (Optional title)
|
||||
// [id]: <http://example.com> "Optional title"
|
||||
pattern: /!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,
|
||||
inside: {
|
||||
'variable': {
|
||||
pattern: /^(!?\[)[^\]]+/,
|
||||
lookbehind: true
|
||||
},
|
||||
'string': /(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,
|
||||
'punctuation': /^[\[\]!:]|[<>]/
|
||||
},
|
||||
alias: 'url'
|
||||
},
|
||||
'bold': {
|
||||
// **strong**
|
||||
// __strong__
|
||||
|
||||
// Allow only one line break
|
||||
pattern: /(^|[^\\])(\*\*|__)(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,
|
||||
lookbehind: true,
|
||||
greedy: true,
|
||||
inside: {
|
||||
'punctuation': /^\*\*|^__|\*\*$|__$/
|
||||
}
|
||||
},
|
||||
'italic': {
|
||||
// *em*
|
||||
// _em_
|
||||
|
||||
// Allow only one line break
|
||||
pattern: /(^|[^\\])([*_])(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,
|
||||
lookbehind: true,
|
||||
greedy: true,
|
||||
inside: {
|
||||
'punctuation': /^[*_]|[*_]$/
|
||||
}
|
||||
},
|
||||
'strike': {
|
||||
// ~~strike through~~
|
||||
// ~strike~
|
||||
|
||||
// Allow only one line break
|
||||
pattern: /(^|[^\\])(~~?)(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,
|
||||
lookbehind: true,
|
||||
greedy: true,
|
||||
inside: {
|
||||
'punctuation': /^~~?|~~?$/
|
||||
}
|
||||
},
|
||||
'url': {
|
||||
// [example](http://example.com "Optional title")
|
||||
// [example] [id]
|
||||
pattern: /!?\[[^\]]+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)| ?\[[^\]\n]*\])/,
|
||||
inside: {
|
||||
'variable': {
|
||||
pattern: /(!?\[)[^\]]+(?=\]$)/,
|
||||
lookbehind: true
|
||||
},
|
||||
'string': {
|
||||
pattern: /"(?:\\.|[^"\\])*"(?=\)$)/
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
['bold', 'italic', 'strike'].forEach(function (token) {
|
||||
['url', 'bold', 'italic', 'strike'].forEach(function (inside) {
|
||||
if (token !== inside) {
|
||||
Prism.languages.markdown[token].inside[inside] = Prism.languages.markdown[inside];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
Prism.hooks.add('after-tokenize', function (env) {
|
||||
if (env.language !== 'markdown' && env.language !== 'md') {
|
||||
return;
|
||||
}
|
||||
|
||||
function walkTokens(tokens) {
|
||||
if (!tokens || typeof tokens === 'string') {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i = 0, l = tokens.length; i < l; i++) {
|
||||
var token = tokens[i];
|
||||
|
||||
if (token.type !== 'code') {
|
||||
walkTokens(token.content);
|
||||
continue;
|
||||
}
|
||||
|
||||
var codeLang = token.content[1];
|
||||
var codeBlock = token.content[3];
|
||||
|
||||
if (codeLang && codeBlock &&
|
||||
codeLang.type === 'code-language' && codeBlock.type === 'code-block' &&
|
||||
typeof codeLang.content === 'string') {
|
||||
|
||||
// this might be a language that Prism does not support
|
||||
var alias = 'language-' + codeLang.content.trim().split(/\s+/)[0].toLowerCase();
|
||||
|
||||
// add alias
|
||||
if (!codeBlock.alias) {
|
||||
codeBlock.alias = [alias];
|
||||
} else if (typeof codeBlock.alias === 'string') {
|
||||
codeBlock.alias = [codeBlock.alias, alias];
|
||||
} else {
|
||||
codeBlock.alias.push(alias);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
walkTokens(env.tokens);
|
||||
});
|
||||
|
||||
Prism.hooks.add('wrap', function (env) {
|
||||
if (env.type !== 'code-block') {
|
||||
return;
|
||||
}
|
||||
|
||||
var codeLang = '';
|
||||
for (var i = 0, l = env.classes.length; i < l; i++) {
|
||||
var cls = env.classes[i];
|
||||
var match = /language-(.+)/.exec(cls);
|
||||
if (match) {
|
||||
codeLang = match[1];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
var grammar = Prism.languages[codeLang];
|
||||
|
||||
if (!grammar) {
|
||||
return;
|
||||
}
|
||||
|
||||
// reverse Prism.util.encode
|
||||
var code = env.content.replace(/</g, '<').replace(/&/g, '&');
|
||||
|
||||
env.content = Prism.highlight(code, grammar, codeLang);
|
||||
});
|
||||
|
||||
Prism.languages.md = Prism.languages.markdown;
|
100
public/assets/scripts/dash/app/vendor/lang/prism-markup.js
vendored
Normal file
100
public/assets/scripts/dash/app/vendor/lang/prism-markup.js
vendored
Normal file
|
@ -0,0 +1,100 @@
|
|||
Prism.languages.markup = {
|
||||
'comment': /<!--[\s\S]*?-->/,
|
||||
'prolog': /<\?[\s\S]+?\?>/,
|
||||
'doctype': /<!DOCTYPE[\s\S]+?>/i,
|
||||
'cdata': /<!\[CDATA\[[\s\S]*?]]>/i,
|
||||
'tag': {
|
||||
pattern: /<\/?(?!\d)[^\s>\/=$<%]+(?:\s(?:\s*[^\s>\/=]+(?:\s*=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+(?=[\s>]))|(?=[\s/>])))+)?\s*\/?>/i,
|
||||
greedy: true,
|
||||
inside: {
|
||||
'tag': {
|
||||
pattern: /^<\/?[^\s>\/]+/i,
|
||||
inside: {
|
||||
'punctuation': /^<\/?/,
|
||||
'namespace': /^[^\s>\/:]+:/
|
||||
}
|
||||
},
|
||||
'attr-value': {
|
||||
pattern: /=\s*(?:"[^"]*"|'[^']*'|[^\s'">=]+)/i,
|
||||
inside: {
|
||||
'punctuation': [
|
||||
/^=/,
|
||||
{
|
||||
pattern: /^(\s*)["']|["']$/,
|
||||
lookbehind: true
|
||||
}
|
||||
]
|
||||
}
|
||||
},
|
||||
'punctuation': /\/?>/,
|
||||
'attr-name': {
|
||||
pattern: /[^\s>\/]+/,
|
||||
inside: {
|
||||
'namespace': /^[^\s>\/:]+:/
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
'entity': /&#?[\da-z]{1,8};/i
|
||||
};
|
||||
|
||||
Prism.languages.markup['tag'].inside['attr-value'].inside['entity'] =
|
||||
Prism.languages.markup['entity'];
|
||||
|
||||
// Plugin to make entity title show the real entity, idea by Roman Komarov
|
||||
Prism.hooks.add('wrap', function(env) {
|
||||
|
||||
if (env.type === 'entity') {
|
||||
env.attributes['title'] = env.content.replace(/&/, '&');
|
||||
}
|
||||
});
|
||||
|
||||
Object.defineProperty(Prism.languages.markup.tag, 'addInlined', {
|
||||
/**
|
||||
* Adds an inlined language to markup.
|
||||
*
|
||||
* An example of an inlined language is CSS with `<style>` tags.
|
||||
*
|
||||
* @param {string} tagName The name of the tag that contains the inlined language. This name will be treated as
|
||||
* case insensitive.
|
||||
* @param {string} lang The language key.
|
||||
* @example
|
||||
* addInlined('style', 'css');
|
||||
*/
|
||||
value: function addInlined(tagName, lang) {
|
||||
var includedCdataInside = {};
|
||||
includedCdataInside['language-' + lang] = {
|
||||
pattern: /(^<!\[CDATA\[)[\s\S]+?(?=\]\]>$)/i,
|
||||
lookbehind: true,
|
||||
inside: Prism.languages[lang]
|
||||
};
|
||||
includedCdataInside['cdata'] = /^<!\[CDATA\[|\]\]>$/i;
|
||||
|
||||
var inside = {
|
||||
'included-cdata': {
|
||||
pattern: /<!\[CDATA\[[\s\S]*?\]\]>/i,
|
||||
inside: includedCdataInside
|
||||
}
|
||||
};
|
||||
inside['language-' + lang] = {
|
||||
pattern: /[\s\S]+/,
|
||||
inside: Prism.languages[lang]
|
||||
};
|
||||
|
||||
var def = {};
|
||||
def[tagName] = {
|
||||
pattern: RegExp(/(<__[\s\S]*?>)(?:<!\[CDATA\[[\s\S]*?\]\]>\s*|[\s\S])*?(?=<\/__>)/.source.replace(/__/g, tagName), 'i'),
|
||||
lookbehind: true,
|
||||
greedy: true,
|
||||
inside: inside
|
||||
};
|
||||
|
||||
Prism.languages.insertBefore('markup', 'cdata', def);
|
||||
}
|
||||
});
|
||||
|
||||
Prism.languages.xml = Prism.languages.extend('markup', {});
|
||||
Prism.languages.html = Prism.languages.markup;
|
||||
Prism.languages.mathml = Prism.languages.markup;
|
||||
Prism.languages.svg = Prism.languages.markup;
|
1
public/assets/scripts/dash/app/vendor/prism-markdown.min.js
vendored
Normal file
1
public/assets/scripts/dash/app/vendor/prism-markdown.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
770
public/assets/scripts/dash/app/vendor/prism.js
vendored
Normal file
770
public/assets/scripts/dash/app/vendor/prism.js
vendored
Normal file
|
@ -0,0 +1,770 @@
|
|||
/* eslint-disable */
|
||||
/* **********************************************
|
||||
Begin prism-core.js
|
||||
********************************************** */
|
||||
|
||||
let _self = {};
|
||||
|
||||
/**
|
||||
* Prism: Lightweight, robust, elegant syntax highlighting
|
||||
* MIT license http://www.opensource.org/licenses/mit-license.php/
|
||||
* @author Lea Verou http://lea.verou.me
|
||||
*/
|
||||
|
||||
let Prism = (function () {
|
||||
// Private helper vars
|
||||
let lang = /\blang(?:uage)?-([\w-]+)\b/i;
|
||||
let uniqueId = 0;
|
||||
|
||||
var _ = _self.Prism = {
|
||||
manual: _self.Prism && _self.Prism.manual,
|
||||
disableWorkerMessageHandler: _self.Prism && _self.Prism.disableWorkerMessageHandler,
|
||||
util: {
|
||||
encode (tokens) {
|
||||
if (tokens instanceof Token) {
|
||||
return new Token(tokens.type, _.util.encode(tokens.content), tokens.alias);
|
||||
} else if (_.util.type(tokens) === 'Array') {
|
||||
return tokens.map(_.util.encode);
|
||||
} else {
|
||||
return tokens.replace(/&/g, '&').replace(/</g, '<').replace(/\u00a0/g, ' ');
|
||||
}
|
||||
},
|
||||
|
||||
type (o) {
|
||||
return Object.prototype.toString.call(o).match(/\[object (\w+)\]/)[1];
|
||||
},
|
||||
|
||||
objId (obj) {
|
||||
if (!obj['__id']) {
|
||||
Object.defineProperty(obj, '__id', { value: ++uniqueId });
|
||||
}
|
||||
return obj['__id'];
|
||||
},
|
||||
|
||||
// Deep clone a language definition (e.g. to extend it)
|
||||
clone (o, visited) {
|
||||
var type = _.util.type(o);
|
||||
visited = visited || {};
|
||||
|
||||
switch (type) {
|
||||
case 'Object':
|
||||
if (visited[_.util.objId(o)]) {
|
||||
return visited[_.util.objId(o)];
|
||||
}
|
||||
var clone = {};
|
||||
visited[_.util.objId(o)] = clone;
|
||||
|
||||
for (var key in o) {
|
||||
if (o.hasOwnProperty(key)) {
|
||||
clone[key] = _.util.clone(o[key], visited);
|
||||
}
|
||||
}
|
||||
|
||||
return clone;
|
||||
|
||||
case 'Array':
|
||||
if (visited[_.util.objId(o)]) {
|
||||
return visited[_.util.objId(o)];
|
||||
}
|
||||
var clone = [];
|
||||
visited[_.util.objId(o)] = clone;
|
||||
|
||||
o.forEach(function (v, i) {
|
||||
clone[i] = _.util.clone(v, visited);
|
||||
});
|
||||
|
||||
return clone;
|
||||
}
|
||||
|
||||
return o;
|
||||
},
|
||||
},
|
||||
|
||||
languages: {
|
||||
extend (id, redef) {
|
||||
var lang = _.util.clone(_.languages[id]);
|
||||
|
||||
for (var key in redef) {
|
||||
lang[key] = redef[key];
|
||||
}
|
||||
|
||||
return lang;
|
||||
},
|
||||
|
||||
/**
|
||||
* Insert a token before another token in a language literal
|
||||
* As this needs to recreate the object (we cannot actually insert before keys in object literals),
|
||||
* we cannot just provide an object, we need anobject and a key.
|
||||
* @param inside The key (or language id) of the parent
|
||||
* @param before The key to insert before. If not provided, the function appends instead.
|
||||
* @param insert Object with the key/value pairs to insert
|
||||
* @param root The object that contains `inside`. If equal to Prism.languages, it can be omitted.
|
||||
*/
|
||||
insertBefore (inside, before, insert, root) {
|
||||
root = root || _.languages;
|
||||
var grammar = root[inside];
|
||||
|
||||
if (arguments.length == 2) {
|
||||
insert = arguments[1];
|
||||
|
||||
for (var newToken in insert) {
|
||||
if (insert.hasOwnProperty(newToken)) {
|
||||
grammar[newToken] = insert[newToken];
|
||||
}
|
||||
}
|
||||
|
||||
return grammar;
|
||||
}
|
||||
|
||||
var ret = {};
|
||||
|
||||
for (var token in grammar) {
|
||||
|
||||
if (grammar.hasOwnProperty(token)) {
|
||||
|
||||
if (token == before) {
|
||||
|
||||
for (var newToken in insert) {
|
||||
|
||||
if (insert.hasOwnProperty(newToken)) {
|
||||
ret[newToken] = insert[newToken];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ret[token] = grammar[token];
|
||||
}
|
||||
}
|
||||
|
||||
// Update references in other language definitions
|
||||
_.languages.DFS(_.languages, function(key, value) {
|
||||
if (value === root[inside] && key != inside) {
|
||||
this[key] = ret;
|
||||
}
|
||||
});
|
||||
|
||||
return root[inside] = ret;
|
||||
},
|
||||
|
||||
// Traverse a language definition with Depth First Search
|
||||
DFS(o, callback, type, visited) {
|
||||
visited = visited || {};
|
||||
for (var i in o) {
|
||||
if (o.hasOwnProperty(i)) {
|
||||
callback.call(o, i, o[i], type || i);
|
||||
|
||||
if (_.util.type(o[i]) === 'Object' && !visited[_.util.objId(o[i])]) {
|
||||
visited[_.util.objId(o[i])] = true;
|
||||
_.languages.DFS(o[i], callback, null, visited);
|
||||
}
|
||||
else if (_.util.type(o[i]) === 'Array' && !visited[_.util.objId(o[i])]) {
|
||||
visited[_.util.objId(o[i])] = true;
|
||||
_.languages.DFS(o[i], callback, i, visited);
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
},
|
||||
plugins: {},
|
||||
|
||||
highlightAll(async, callback) {
|
||||
_.highlightAllUnder(document, async, callback);
|
||||
},
|
||||
|
||||
highlightAllUnder(container, async, callback) {
|
||||
var env = {
|
||||
callback: callback,
|
||||
selector: 'code[class*="language-"], [class*="language-"] code, code[class*="lang-"], [class*="lang-"] code'
|
||||
};
|
||||
|
||||
_.hooks.run("before-highlightall", env);
|
||||
|
||||
var elements = env.elements || container.querySelectorAll(env.selector);
|
||||
|
||||
for (var i=0, element; element = elements[i++];) {
|
||||
_.highlightElement(element, async === true, env.callback);
|
||||
}
|
||||
},
|
||||
|
||||
highlightElement(element, async, callback) {
|
||||
// Find language
|
||||
var language, grammar, parent = element;
|
||||
|
||||
while (parent && !lang.test(parent.className)) {
|
||||
parent = parent.parentNode;
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
language = (parent.className.match(lang) || [,''])[1].toLowerCase();
|
||||
grammar = _.languages[language];
|
||||
}
|
||||
|
||||
// Set language on the element, if not present
|
||||
element.className = element.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;
|
||||
|
||||
if (element.parentNode) {
|
||||
// Set language on the parent, for styling
|
||||
parent = element.parentNode;
|
||||
|
||||
if (/pre/i.test(parent.nodeName)) {
|
||||
parent.className = parent.className.replace(lang, '').replace(/\s+/g, ' ') + ' language-' + language;
|
||||
}
|
||||
}
|
||||
|
||||
var code = element.textContent;
|
||||
|
||||
var env = {
|
||||
element: element,
|
||||
language: language,
|
||||
grammar: grammar,
|
||||
code: code
|
||||
};
|
||||
|
||||
_.hooks.run('before-sanity-check', env);
|
||||
|
||||
if (!env.code || !env.grammar) {
|
||||
if (env.code) {
|
||||
_.hooks.run('before-highlight', env);
|
||||
env.element.textContent = env.code;
|
||||
_.hooks.run('after-highlight', env);
|
||||
}
|
||||
_.hooks.run('complete', env);
|
||||
return;
|
||||
}
|
||||
|
||||
_.hooks.run('before-highlight', env);
|
||||
|
||||
if (async && _self.Worker) {
|
||||
var worker = new Worker(_.filename);
|
||||
|
||||
worker.onmessage = function(evt) {
|
||||
env.highlightedCode = evt.data;
|
||||
|
||||
_.hooks.run('before-insert', env);
|
||||
|
||||
env.element.innerHTML = env.highlightedCode;
|
||||
|
||||
callback && callback.call(env.element);
|
||||
_.hooks.run('after-highlight', env);
|
||||
_.hooks.run('complete', env);
|
||||
};
|
||||
|
||||
worker.postMessage(JSON.stringify({
|
||||
language: env.language,
|
||||
code: env.code,
|
||||
immediateClose: true
|
||||
}));
|
||||
}
|
||||
else {
|
||||
env.highlightedCode = _.highlight(env.code, env.grammar, env.language);
|
||||
|
||||
_.hooks.run('before-insert', env);
|
||||
|
||||
env.element.innerHTML = env.highlightedCode;
|
||||
|
||||
callback && callback.call(element);
|
||||
|
||||
_.hooks.run('after-highlight', env);
|
||||
_.hooks.run('complete', env);
|
||||
}
|
||||
},
|
||||
|
||||
highlight (text, grammar, language) {
|
||||
var env = {
|
||||
code: text,
|
||||
grammar: grammar,
|
||||
language: language
|
||||
};
|
||||
_.hooks.run('before-tokenize', env);
|
||||
env.tokens = _.tokenize(env.code, env.grammar);
|
||||
_.hooks.run('after-tokenize', env);
|
||||
return Token.stringify(_.util.encode(env.tokens), env.language);
|
||||
},
|
||||
|
||||
matchGrammar (text, strarr, grammar, index, startPos, oneshot, target) {
|
||||
var Token = _.Token;
|
||||
|
||||
for (var token in grammar) {
|
||||
if(!grammar.hasOwnProperty(token) || !grammar[token]) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (token == target) {
|
||||
return;
|
||||
}
|
||||
|
||||
var patterns = grammar[token];
|
||||
patterns = (_.util.type(patterns) === "Array") ? patterns : [patterns];
|
||||
|
||||
for (var j = 0; j < patterns.length; ++j) {
|
||||
var pattern = patterns[j],
|
||||
inside = pattern.inside,
|
||||
lookbehind = !!pattern.lookbehind,
|
||||
greedy = !!pattern.greedy,
|
||||
lookbehindLength = 0,
|
||||
alias = pattern.alias;
|
||||
|
||||
if (greedy && !pattern.pattern.global) {
|
||||
// Without the global flag, lastIndex won't work
|
||||
var flags = pattern.pattern.toString().match(/[imuy]*$/)[0];
|
||||
pattern.pattern = RegExp(pattern.pattern.source, flags + "g");
|
||||
}
|
||||
|
||||
pattern = pattern.pattern || pattern;
|
||||
|
||||
// Don’t cache length as it changes during the loop
|
||||
for (var i = index, pos = startPos; i < strarr.length; pos += strarr[i].length, ++i) {
|
||||
|
||||
var str = strarr[i];
|
||||
|
||||
if (strarr.length > text.length) {
|
||||
// Something went terribly wrong, ABORT, ABORT!
|
||||
return;
|
||||
}
|
||||
|
||||
if (str instanceof Token) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (greedy && i != strarr.length - 1) {
|
||||
pattern.lastIndex = pos;
|
||||
var match = pattern.exec(text);
|
||||
if (!match) {
|
||||
break;
|
||||
}
|
||||
|
||||
var from = match.index + (lookbehind ? match[1].length : 0),
|
||||
to = match.index + match[0].length,
|
||||
k = i,
|
||||
p = pos;
|
||||
|
||||
for (var len = strarr.length; k < len && (p < to || (!strarr[k].type && !strarr[k - 1].greedy)); ++k) {
|
||||
p += strarr[k].length;
|
||||
// Move the index i to the element in strarr that is closest to from
|
||||
if (from >= p) {
|
||||
++i;
|
||||
pos = p;
|
||||
}
|
||||
}
|
||||
|
||||
// If strarr[i] is a Token, then the match starts inside another Token, which is invalid
|
||||
if (strarr[i] instanceof Token) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Number of tokens to delete and replace with the new match
|
||||
delNum = k - i;
|
||||
str = text.slice(pos, p);
|
||||
match.index -= pos;
|
||||
} else {
|
||||
pattern.lastIndex = 0;
|
||||
|
||||
var match = pattern.exec(str),
|
||||
delNum = 1;
|
||||
}
|
||||
|
||||
if (!match) {
|
||||
if (oneshot) {
|
||||
break;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if(lookbehind) {
|
||||
lookbehindLength = match[1] ? match[1].length : 0;
|
||||
}
|
||||
|
||||
var from = match.index + lookbehindLength,
|
||||
match = match[0].slice(lookbehindLength),
|
||||
to = from + match.length,
|
||||
before = str.slice(0, from),
|
||||
after = str.slice(to);
|
||||
|
||||
var args = [i, delNum];
|
||||
|
||||
if (before) {
|
||||
++i;
|
||||
pos += before.length;
|
||||
args.push(before);
|
||||
}
|
||||
|
||||
var wrapped = new Token(token, inside? _.tokenize(match, inside) : match, alias, match, greedy);
|
||||
|
||||
args.push(wrapped);
|
||||
|
||||
if (after) {
|
||||
args.push(after);
|
||||
}
|
||||
|
||||
Array.prototype.splice.apply(strarr, args);
|
||||
|
||||
if (delNum != 1)
|
||||
_.matchGrammar(text, strarr, grammar, i, pos, true, token);
|
||||
|
||||
if (oneshot)
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
tokenize(text, grammar, language) {
|
||||
var strarr = [text];
|
||||
|
||||
var rest = grammar.rest;
|
||||
|
||||
if (rest) {
|
||||
for (var token in rest) {
|
||||
grammar[token] = rest[token];
|
||||
}
|
||||
|
||||
delete grammar.rest;
|
||||
}
|
||||
|
||||
_.matchGrammar(text, strarr, grammar, 0, 0, false);
|
||||
|
||||
return strarr;
|
||||
},
|
||||
|
||||
hooks: {
|
||||
all: {},
|
||||
|
||||
add (name, callback) {
|
||||
var hooks = _.hooks.all;
|
||||
|
||||
hooks[name] = hooks[name] || [];
|
||||
|
||||
hooks[name].push(callback);
|
||||
},
|
||||
|
||||
run (name, env) {
|
||||
var callbacks = _.hooks.all[name];
|
||||
|
||||
if (!callbacks || !callbacks.length) {
|
||||
return;
|
||||
}
|
||||
|
||||
for (var i=0, callback; callback = callbacks[i++];) {
|
||||
callback(env);
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
var Token = _.Token = function (type, content, alias, matchedStr, greedy) {
|
||||
this.type = type;
|
||||
this.content = content;
|
||||
this.alias = alias;
|
||||
// Copy of the full string this token was created from
|
||||
this.length = (matchedStr || '').length | 0;
|
||||
this.greedy = !!greedy;
|
||||
};
|
||||
|
||||
Token.stringify = function (o, language, parent) {
|
||||
if (typeof o === 'string') {
|
||||
return o;
|
||||
}
|
||||
|
||||
if (_.util.type(o) === 'Array') {
|
||||
return o.map((element) => {
|
||||
return Token.stringify(element, language, o);
|
||||
}).join('');
|
||||
}
|
||||
|
||||
let env = {
|
||||
type: o.type,
|
||||
content: Token.stringify(o.content, language, parent),
|
||||
tag: 'span',
|
||||
classes: ['token', o.type],
|
||||
attributes: {},
|
||||
language,
|
||||
parent,
|
||||
};
|
||||
|
||||
if (o.alias) {
|
||||
let aliases = _.util.type(o.alias) === 'Array' ? o.alias : [o.alias];
|
||||
Array.prototype.push.apply(env.classes, aliases);
|
||||
}
|
||||
|
||||
_.hooks.run('wrap', env);
|
||||
|
||||
let attributes = Object.keys(env.attributes).map((name) => {
|
||||
return name + '="' + (env.attributes[name] || '').replace(/"/g, '"') + '"';
|
||||
}).join(' ');
|
||||
|
||||
return `<${ env.tag } class="${ env.classes.join(' ') }"${ attributes ? ' ' + attributes : '' }>${ env.content }</${ env.tag }>`;
|
||||
};
|
||||
|
||||
if (!_self.document) {
|
||||
if (!_self.addEventListener) {
|
||||
// in Node.js
|
||||
return _self.Prism;
|
||||
}
|
||||
|
||||
if (!_.disableWorkerMessageHandler) {
|
||||
// In worker
|
||||
_self.addEventListener('message', (evt) => {
|
||||
var message = JSON.parse(evt.data),
|
||||
lang = message.language,
|
||||
code = message.code,
|
||||
immediateClose = message.immediateClose;
|
||||
|
||||
_self.postMessage(_.highlight(code, _.languages[lang], lang));
|
||||
if (immediateClose) {
|
||||
_self.close();
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
return _self.Prism;
|
||||
}
|
||||
|
||||
// Get current script and highlight
|
||||
// let script = document.currentScript || [].slice.call(document.getElementsByTagName('script')).pop();
|
||||
|
||||
// if (script) {
|
||||
// _.filename = script.src;
|
||||
|
||||
// if (!_.manual && !script.hasAttribute('data-manual')) {
|
||||
// if (document.readyState !== 'loading') {
|
||||
// if (window.requestAnimationFrame) {
|
||||
// window.requestAnimationFrame(_.highlightAll);
|
||||
// } else {
|
||||
// window.setTimeout(_.highlightAll, 16);
|
||||
// }
|
||||
// } else {
|
||||
// document.addEventListener('DOMContentLoaded', _.highlightAll);
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
return _self.Prism;
|
||||
}());
|
||||
|
||||
if (typeof module !== 'undefined' && module.exports) {
|
||||
module.exports = Prism;
|
||||
}
|
||||
|
||||
// hack for components to work correctly in node.js
|
||||
if (typeof global !== 'undefined') {
|
||||
global.Prism = Prism;
|
||||
}
|
||||
|
||||
|
||||
/* **********************************************
|
||||
Begin prism-markup.js
|
||||
********************************************** */
|
||||
|
||||
Prism.languages.markup = {
|
||||
comment: /<!--[\s\S]*?-->/,
|
||||
prolog: /<\?[\s\S]+?\?>/,
|
||||
doctype: /<!DOCTYPE[\s\S]+?>/i,
|
||||
cdata: /<!\[CDATA\[[\s\S]*?]]>/i,
|
||||
tag: {
|
||||
pattern: /<\/?(?!\d)[^\s>\/=$<%]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+))?)*\s*\/?>/i,
|
||||
greedy: true,
|
||||
inside: {
|
||||
tag: {
|
||||
pattern: /^<\/?[^\s>\/]+/i,
|
||||
inside: {
|
||||
punctuation: /^<\/?/,
|
||||
namespace: /^[^\s>\/:]+:/,
|
||||
},
|
||||
},
|
||||
'attr-value': {
|
||||
pattern: /=(?:("|')(?:\\[\s\S]|(?!\1)[^\\])*\1|[^\s'">=]+)/i,
|
||||
inside: {
|
||||
punctuation: [
|
||||
/^=/,
|
||||
{
|
||||
pattern: /(^|[^\\])["']/,
|
||||
lookbehind: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
},
|
||||
punctuation: /\/?>/,
|
||||
'attr-name': {
|
||||
pattern: /[^\s>\/]+/,
|
||||
inside: {
|
||||
namespace: /^[^\s>\/:]+:/,
|
||||
},
|
||||
},
|
||||
|
||||
},
|
||||
},
|
||||
entity: /&#?[\da-z]{1,8};/i,
|
||||
};
|
||||
|
||||
Prism.languages.markup.tag.inside['attr-value'].inside.entity = Prism.languages.markup.entity;
|
||||
|
||||
// Plugin to make entity title show the real entity, idea by Roman Komarov
|
||||
Prism.hooks.add('wrap', (env) => {
|
||||
|
||||
if (env.type === 'entity') {
|
||||
env.attributes['title'] = env.content.replace(/&/, '&');
|
||||
}
|
||||
});
|
||||
|
||||
Prism.languages.xml = Prism.languages.markup;
|
||||
Prism.languages.html = Prism.languages.markup;
|
||||
Prism.languages.mathml = Prism.languages.markup;
|
||||
Prism.languages.svg = Prism.languages.markup;
|
||||
|
||||
|
||||
/* **********************************************
|
||||
Begin prism-css.js
|
||||
********************************************** */
|
||||
|
||||
Prism.languages.css = {
|
||||
comment: /\/\*[\s\S]*?\*\//,
|
||||
atrule: {
|
||||
pattern: /@[\w-]+?.*?(?:;|(?=\s*\{))/i,
|
||||
inside: {
|
||||
rule: /@[\w-]+/,
|
||||
// See rest below
|
||||
},
|
||||
},
|
||||
url: /url\((?:(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,
|
||||
selector: /[^{}\s][^{};]*?(?=\s*\{)/,
|
||||
string: {
|
||||
pattern: /("|')(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,
|
||||
greedy: true,
|
||||
},
|
||||
property: /[-_a-z\xA0-\uFFFF][-\w\xA0-\uFFFF]*(?=\s*:)/i,
|
||||
important: /\B!important\b/i,
|
||||
function: /[-a-z0-9]+(?=\()/i,
|
||||
punctuation: /[(){};:]/,
|
||||
};
|
||||
|
||||
Prism.languages.css.atrule.inside.rest = Prism.languages.css;
|
||||
|
||||
if (Prism.languages.markup) {
|
||||
Prism.languages.insertBefore('markup', 'tag', {
|
||||
style: {
|
||||
pattern: /(<style[\s\S]*?>)[\s\S]*?(?=<\/style>)/i,
|
||||
lookbehind: true,
|
||||
inside: Prism.languages.css,
|
||||
alias: 'language-css',
|
||||
greedy: true,
|
||||
},
|
||||
});
|
||||
|
||||
Prism.languages.insertBefore('inside', 'attr-value', {
|
||||
'style-attr': {
|
||||
pattern: /\s*style=("|')(?:\\[\s\S]|(?!\1)[^\\])*\1/i,
|
||||
inside: {
|
||||
'attr-name': {
|
||||
pattern: /^\s*style/i,
|
||||
inside: Prism.languages.markup.tag.inside,
|
||||
},
|
||||
punctuation: /^\s*=\s*['"]|['"]\s*$/,
|
||||
'attr-value': {
|
||||
pattern: /.+/i,
|
||||
inside: Prism.languages.css,
|
||||
},
|
||||
},
|
||||
alias: 'language-css',
|
||||
},
|
||||
}, Prism.languages.markup.tag);
|
||||
}
|
||||
|
||||
/* **********************************************
|
||||
Begin prism-clike.js
|
||||
********************************************** */
|
||||
|
||||
Prism.languages.clike = {
|
||||
comment: [
|
||||
{
|
||||
pattern: /(^|[^\\])\/\*[\s\S]*?(?:\*\/|$)/,
|
||||
lookbehind: true,
|
||||
},
|
||||
{
|
||||
pattern: /(^|[^\\:])\/\/.*/,
|
||||
lookbehind: true,
|
||||
greedy: true,
|
||||
},
|
||||
],
|
||||
string: {
|
||||
pattern: /(["'])(?:\\(?:\r\n|[\s\S])|(?!\1)[^\\\r\n])*\1/,
|
||||
greedy: true,
|
||||
},
|
||||
'class-name': {
|
||||
pattern: /((?:\b(?:class|interface|extends|implements|trait|instanceof|new)\s+)|(?:catch\s+\())[\w.\\]+/i,
|
||||
lookbehind: true,
|
||||
inside: {
|
||||
punctuation: /[.\\]/,
|
||||
},
|
||||
},
|
||||
keyword: /\b(?:if|else|while|do|for|return|in|instanceof|function|new|try|throw|catch|finally|null|break|continue)\b/,
|
||||
boolean: /\b(?:true|false)\b/,
|
||||
function: /[a-z0-9_]+(?=\()/i,
|
||||
number: /\b0x[\da-f]+\b|(?:\b\d+\.?\d*|\B\.\d+)(?:e[+-]?\d+)?/i,
|
||||
operator: /--?|\+\+?|!=?=?|<=?|>=?|==?=?|&&?|\|\|?|\?|\*|\/|~|\^|%/,
|
||||
punctuation: /[{}[\];(),.:]/,
|
||||
};
|
||||
|
||||
|
||||
/* **********************************************
|
||||
Begin prism-javascript.js
|
||||
********************************************** */
|
||||
|
||||
Prism.languages.javascript = Prism.languages.extend('clike', {
|
||||
keyword: /\b(?:as|async|await|break|case|catch|class|const|continue|debugger|default|delete|do|else|enum|export|extends|finally|for|from|function|get|if|implements|import|in|instanceof|interface|let|new|null|of|package|private|protected|public|return|set|static|super|switch|this|throw|try|typeof|var|void|while|with|yield)\b/,
|
||||
number: /\b(?:0[xX][\dA-Fa-f]+|0[bB][01]+|0[oO][0-7]+|NaN|Infinity)\b|(?:\b\d+\.?\d*|\B\.\d+)(?:[Ee][+-]?\d+)?/,
|
||||
// Allow for all non-ASCII characters (See http://stackoverflow.com/a/2008444)
|
||||
function: /[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*\()/i,
|
||||
operator: /-[-=]?|\+[+=]?|!=?=?|<<?=?|>>?>?=?|=(?:==?|>)?|&[&=]?|\|[|=]?|\*\*?=?|\/=?|~|\^=?|%=?|\?|\.{3}/,
|
||||
});
|
||||
|
||||
Prism.languages.insertBefore('javascript', 'keyword', {
|
||||
regex: {
|
||||
pattern: /((?:^|[^$\w\xA0-\uFFFF."'\])\s])\s*)\/(\[[^\]\r\n]+]|\\.|[^/\\\[\r\n])+\/[gimyu]{0,5}(?=\s*($|[\r\n,.;})\]]))/,
|
||||
lookbehind: true,
|
||||
greedy: true,
|
||||
},
|
||||
// This must be declared before keyword because we use "function" inside the look-forward
|
||||
'function-variable': {
|
||||
pattern: /[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*(?=\s*=\s*(?:function\b|(?:\([^()]*\)|[_$a-z\xA0-\uFFFF][$\w\xA0-\uFFFF]*)\s*=>))/i,
|
||||
alias: 'function',
|
||||
},
|
||||
constant: /\b[A-Z][A-Z\d_]*\b/,
|
||||
});
|
||||
|
||||
Prism.languages.insertBefore('javascript', 'string', {
|
||||
'template-string': {
|
||||
pattern: /`(?:\\[\s\S]|\${[^}]+}|[^\\`])*`/,
|
||||
greedy: true,
|
||||
inside: {
|
||||
interpolation: {
|
||||
pattern: /\${[^}]+}/,
|
||||
inside: {
|
||||
'interpolation-punctuation': {
|
||||
pattern: /^\${|}$/,
|
||||
alias: 'punctuation',
|
||||
},
|
||||
rest: null, // See below
|
||||
},
|
||||
},
|
||||
string: /[\s\S]+/,
|
||||
},
|
||||
},
|
||||
});
|
||||
Prism.languages.javascript['template-string'].inside.interpolation.inside.rest = Prism.languages.javascript;
|
||||
|
||||
if (Prism.languages.markup) {
|
||||
Prism.languages.insertBefore('markup', 'tag', {
|
||||
script: {
|
||||
pattern: /(<script[\s\S]*?>)[\s\S]*?(?=<\/script>)/i,
|
||||
lookbehind: true,
|
||||
inside: Prism.languages.javascript,
|
||||
alias: 'language-javascript',
|
||||
greedy: true,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
Prism.languages.js = Prism.languages.javascript;
|
||||
|
||||
export default Prism;
|
1513
public/assets/scripts/dash/app/vendor/sortable.js
vendored
Normal file
1513
public/assets/scripts/dash/app/vendor/sortable.js
vendored
Normal file
File diff suppressed because it is too large
Load diff
432
public/assets/scripts/dash/libraries/FipamoAdminAPI.js
Normal file
432
public/assets/scripts/dash/libraries/FipamoAdminAPI.js
Normal file
|
@ -0,0 +1,432 @@
|
|||
//** 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_GET_SETTINGS = '/api/v1/settings/site';
|
||||
export const API_GET_MEMBER_INFO = '/api/v1/settings/member';
|
||||
export const API_NEW_PAGE = '/api/v1/page/create';
|
||||
export const API_EDIT_PAGE = '/api/v1/page/write';
|
||||
export const API_DELETE_PAGE = '/api/v1/page/delete';
|
||||
export const API_SETTINGS_SYNC = '/api/v1/settings/sync';
|
||||
export const API_PUBLISH_PAGES = '/api/v1/settings/publish';
|
||||
export const API_NAV_SYNC = '/api/v1/settings/nav-sync';
|
||||
export const API_REINDEX_PAGES = '/api/v1/settings/reindex';
|
||||
export const API_SEND_MAIL = '/api/v1/mailer';
|
||||
export const API_LOGIN = '/api/v1/login';
|
||||
//** API TASKS **//
|
||||
export const AUTH_STATUS = 'getAuthStatus';
|
||||
export const TASK_SETTINGS_WRITE = 'writeSettings';
|
||||
export const TASK_PUBLISH_SITE = 'publishSite';
|
||||
export const TASK_PAGE_CREATE = 'createNewPage';
|
||||
export const TASK_PAGE_EDIT = 'editPage';
|
||||
export const TASK_PAGE_DELETE = 'deletePage';
|
||||
export const TASK_SEND_MAIL = 'sendMail';
|
||||
export const TASK_REINDEX_PAGE = 'reIndexPages';
|
||||
export const TASK_SYNC_SETTNIGS = 'syncSite';
|
||||
export const TASK_SYNC_NAV = 'syncNav';
|
||||
export const TASK_GET_SETTINGS = 'getSiteSettings';
|
||||
export const TASK_GET_MEMBER_INFO = 'getMemberInfo';
|
||||
//** API STATUS **//
|
||||
export const API_ACCESS_GOOD = 'apiUseAuthorized';
|
||||
export const API_ACCESS_BAD = 'apiUseNotAuthorized';
|
||||
|
||||
/**
|
||||
* A can of methods used to edit install settings, navigation pages and content pages
|
||||
*/
|
||||
|
||||
class FipamoAdminAPI {
|
||||
/**
|
||||
* @constructor
|
||||
* @param {string} baseURL - url of site; uses local when empty
|
||||
* @param {object} progressBar - element to be used to display upload progress
|
||||
*/
|
||||
constructor(baseURL = null, progressBar = null) {
|
||||
this.percentComplete = 0; //for later
|
||||
this.baseURL = null;
|
||||
this.progressBar = progressBar;
|
||||
this.status = false;
|
||||
if (baseURL) this.baseURL = baseURL;
|
||||
//asks server if a session is active
|
||||
this._request(this.baseURL ? this.baseURL + API_STATUS : API_STATUS).then(
|
||||
response => {
|
||||
if (response.type === API_ACCESS_GOOD) {
|
||||
this.token = response.token;
|
||||
} else {
|
||||
//don't set token
|
||||
//console.log("NO TOKEN");
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
/**
|
||||
* Promise method for authenticating and starting a session\
|
||||
* **POST**`/api/v1/login`
|
||||
* @param {Object[]} data - json object that contains data for set up
|
||||
* @param {string} data[].handle - handle for site user
|
||||
* @param {string} data[].password - password for site user
|
||||
* @example
|
||||
* api.login(data).then(response=>{
|
||||
* console.log("RESPONSE", response);
|
||||
* })
|
||||
* @returns {object} json object that contains type and status of login request
|
||||
```
|
||||
{
|
||||
"message":"Example Message of Affirmation or what you're missing!",
|
||||
"type":"exampleType",
|
||||
}
|
||||
```
|
||||
*/
|
||||
login(data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this.baseURL ? (data.remote = true) : (data.remote = false);
|
||||
this.key ? (data.key = this.key) : (data.key = null);
|
||||
this._request(
|
||||
this.baseURL ? this.baseURL + API_LOGIN : API_LOGIN,
|
||||
AUTH_STATUS,
|
||||
REQUEST_TYPE_POST,
|
||||
CONTENT_TYPE_JSON,
|
||||
data
|
||||
)
|
||||
.then(result => {
|
||||
resolve(result);
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* Method for saving site and navigation settings\
|
||||
* **POST**`/api/v1/settings/:task`
|
||||
* @param {string} task - settings being synced `config | navigation`
|
||||
* @param {object[]} data - json object that contains settings data for [site] or [nav]
|
||||
* @param {string} data[].global.base_url - base url for site [site]
|
||||
* @param {string} data[].global.title - site title [site]
|
||||
* @param {string} data[].global.descriptions - brief site summary [site]
|
||||
* @param {string} data[].global.background - url for site feature image for header [site]
|
||||
* @param {boolean} data[].global.private - privacy state for site [disabled] [site]
|
||||
* @param {boolean} data[].global.renderOnSave - property for publishing site when page saved [disabled] [site]
|
||||
* @param {string} data[].global.theme - current theme for site [site]
|
||||
* @param {boolean} data[].global.externalAPI - toggle for external API access [site]
|
||||
* @param {string} data[].member.handle - current member handle [site]
|
||||
* @param {string} data[].member.email - current member email [site]
|
||||
* @param {string} data[].email.active - current email protocol being used [site]
|
||||
* @param {string} data[].email.smtp.domain - url of smtp service being [site]
|
||||
* @param {string} data[].email.smtp.email - email account of smtp service [site]
|
||||
* @param {string} data[].email.smtp.password - password for email of smtp service [site]
|
||||
* @param {string} data[].email.mailgun.domain - mailgun domain url [site]
|
||||
* @param {string} data[].email.mailgun.key - mailgun key [site]
|
||||
*
|
||||
* @param {string} data[].item.title - page title [nav]
|
||||
* @param {string} data[].item.slug - url safe title [nav]
|
||||
* @param {string} data[].item.uuid - unique identifier [nav]
|
||||
* @param {string} data[].item.path - directory path to associated markdown file [nav]
|
||||
* @example
|
||||
* api.sync(TASK, data).then(response=>{
|
||||
* console.log("RESPONSE", response);
|
||||
* })
|
||||
* @returns {object} json object that contains type and status of sync request
|
||||
```
|
||||
*
|
||||
{
|
||||
"message":"Example Message of Affirmation!",
|
||||
"type":"exampleType",
|
||||
}
|
||||
```
|
||||
*/
|
||||
sync(task, data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
let url = '';
|
||||
switch (task) {
|
||||
case 'syncSite':
|
||||
url = API_SETTINGS_SYNC;
|
||||
break;
|
||||
case 'syncNav':
|
||||
url = API_NAV_SYNC;
|
||||
break;
|
||||
}
|
||||
this._request(
|
||||
this.baseURL ? this.baseURL + url : url,
|
||||
TASK_SETTINGS_WRITE,
|
||||
REQUEST_TYPE_POST,
|
||||
CONTENT_TYPE_JSON,
|
||||
data
|
||||
)
|
||||
.then(result => {
|
||||
resolve(result);
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for retrieving user authorizing user login
|
||||
* @param {object[]} data - json object that contains task
|
||||
* @param {string} data[].task - publishing task
|
||||
* @example
|
||||
* api.publish(TASK).then(response=>{
|
||||
* console.log("RESPONSE", response);
|
||||
* })
|
||||
* @returns {object} json object that contains type and status of publis request
|
||||
* ```
|
||||
{
|
||||
"message":"Example Message of Affirmation!",
|
||||
"type":"exampleType",
|
||||
}
|
||||
* ```
|
||||
*/
|
||||
publish(data) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._request(
|
||||
//API_PUBLISH_PAGES,
|
||||
this.baseURL ? this.baseURL + API_PUBLISH_PAGES : API_PUBLISH_PAGES,
|
||||
TASK_PUBLISH_SITE,
|
||||
REQUEST_TYPE_POST,
|
||||
CONTENT_TYPE_JSON,
|
||||
data
|
||||
)
|
||||
.then(result => {
|
||||
resolve(result);
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for handling page creating and editing\
|
||||
* **POST**`/api/v1/page/:task`
|
||||
* @param {string} task - current page action
|
||||
* @param {object[]} form - form object that contains info for current page being edited/created
|
||||
* @param {string} form[].id - sequence id for page, leave empty for new page
|
||||
* @param {string} form[].uuid - unique identifier for page, leave empty for new page
|
||||
* @param {string} form[].layout - current page layout
|
||||
* @param {string} form[].current_title - saved url save title for persistence when changing title, leave empty for new page
|
||||
* @param {string} form[].content - markdown body of page
|
||||
* @param {string} form[].title - current title of page
|
||||
* @param {string} form[].created - date page was created, leave empty for new page
|
||||
* @param {string} form[].slug - url safe string of page title
|
||||
* @param {string} form[].tags - comma separated list of tags
|
||||
* @param {boolean} form[].menu - property that indicates page is included in site menu
|
||||
* @param {boolean} form[].featured - property that indicates page is featured
|
||||
* @param {boolean} form[].published - property that indicates page is public
|
||||
* @param {string} form[].form_token - hidden property to authenticate form submission
|
||||
* @param {input} form[].feature_image - main image for page
|
||||
* @example
|
||||
* api.pageActions(TASK, data).then(response=>{
|
||||
* console.log("RESPONSE", response);
|
||||
* })
|
||||
* @returns {object} json object that contains type and status of page edit request
|
||||
```
|
||||
{
|
||||
"message":"Example Message of Affirmation!",
|
||||
"type":"exampleType",
|
||||
}
|
||||
```
|
||||
*/
|
||||
pageActions(task, data) {
|
||||
let url, event, content;
|
||||
switch (task) {
|
||||
case TASK_PAGE_CREATE:
|
||||
url = API_NEW_PAGE;
|
||||
event = TASK_PAGE_CREATE;
|
||||
content = CONTENT_TYPE_JSON;
|
||||
break;
|
||||
case TASK_PAGE_EDIT:
|
||||
url = API_EDIT_PAGE;
|
||||
event = TASK_PAGE_EDIT;
|
||||
content = CONTENT_TYPE_JSON;
|
||||
break;
|
||||
|
||||
case TASK_PAGE_DELETE:
|
||||
url = API_DELETE_PAGE;
|
||||
event = TASK_PAGE_DELETE;
|
||||
content = CONTENT_TYPE_JSON;
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (this.baseURL) {
|
||||
//data.key = this.key;
|
||||
data.remote = true;
|
||||
} else {
|
||||
data.remote = false;
|
||||
}
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
this._request(
|
||||
this.baseURL ? this.baseURL + url : url,
|
||||
event,
|
||||
REQUEST_TYPE_POST,
|
||||
content,
|
||||
data
|
||||
)
|
||||
.then(result => {
|
||||
resolve(result);
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Method for sending mail (if completed in settings)\
|
||||
* **POST**`/api/v1/mailer`
|
||||
* @param {object[]} message - json object that contains items to be included in main site navigation
|
||||
* @param {string} message[].content - message to send
|
||||
* @example
|
||||
* api.sendMail(message).then(response=>{
|
||||
* console.log("RESPONSE", response);
|
||||
* })
|
||||
* @returns {object} json object that contains type and status of page edit request
|
||||
```
|
||||
{
|
||||
"message":"Example Message of Affirmation!",
|
||||
"type":"exampleType"
|
||||
}
|
||||
```
|
||||
*/
|
||||
sendMail(message) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._request(
|
||||
this.baseURL ? this.baseURL + API_SEND_MAIL : API_SEND_MAIL,
|
||||
TASK_SEND_MAIL,
|
||||
REQUEST_TYPE_POST,
|
||||
CONTENT_TYPE_JSON,
|
||||
message
|
||||
)
|
||||
.then(result => {
|
||||
resolve(result);
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* *Promise method for retrieving site and member info*\
|
||||
* **GET** `/api/v1/settings/:type`
|
||||
* @param {string} type - type of info requested ['site'|'member'];
|
||||
* @example
|
||||
* api.getInfo("type").then(data=>{
|
||||
console.log("Info Object", data);
|
||||
* })
|
||||
* @returns {object} json object that contains data for requested information
|
||||
*
|
||||
* *info object example*
|
||||
* ```
|
||||
{
|
||||
"message":"message of affirmation!",
|
||||
"task":"type of info request",
|
||||
"data":json-data-object,
|
||||
}
|
||||
* ```
|
||||
*/
|
||||
|
||||
getInfo(type) {
|
||||
let url, task;
|
||||
if (type == 'site') {
|
||||
url = API_GET_SETTINGS;
|
||||
task = TASK_GET_SETTINGS;
|
||||
} else {
|
||||
url = API_GET_MEMBER_INFO;
|
||||
task = TASK_GET_MEMBER_INFO;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._request(this.baseURL ? this.baseURL + url : url, task)
|
||||
.then(result => {
|
||||
resolve(result);
|
||||
})
|
||||
.catch(err => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//--------------------------
|
||||
// private
|
||||
//--------------------------
|
||||
_request(
|
||||
requestURL,
|
||||
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, self.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_SETTINGS_WRITE ||
|
||||
eventType === TASK_PAGE_EDIT ||
|
||||
eventType === TASK_PAGE_CREATE ||
|
||||
eventType === TASK_PAGE_DELETE ||
|
||||
eventType === TASK_PUBLISH_SITE ||
|
||||
eventType === TASK_REINDEX_PAGE
|
||||
)
|
||||
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 {
|
||||
if (
|
||||
eventType === TASK_GET_SETTINGS ||
|
||||
eventType === TASK_GET_MEMBER_INFO
|
||||
) {
|
||||
request.setRequestHeader('fipamo-access-token', self.token);
|
||||
}
|
||||
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 { FipamoAdminAPI as default };
|
294
public/assets/scripts/dash/libraries/FipamoContentAPI.js
Normal file
294
public/assets/scripts/dash/libraries/FipamoContentAPI.js
Normal file
|
@ -0,0 +1,294 @@
|
|||
//** 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_GET_PAGES = "/api/v1/page/published";
|
||||
export const API_GET_FEATURED = "/api/v1/page/featured";
|
||||
export const API_GET_PAGE = "/api/v1/page/single";
|
||||
export const API_GET_MENU = "/api/v1/page/menu";
|
||||
export const API_GET_TAGS = "/api/v1/page/tags";
|
||||
|
||||
//** API TASKS **//
|
||||
export const TASK_GET_CONTENT = "retrieveContent";
|
||||
|
||||
/**
|
||||
* A bag of methods for getting content from an install.
|
||||
*/
|
||||
class FipamoContentAPI {
|
||||
/**
|
||||
* @constructor
|
||||
* @param {string} baseURL - url of install, defaults to local
|
||||
* @param {string} key - user api key found in Settings
|
||||
* @author Ro
|
||||
*/
|
||||
constructor(baseURL = null, key = null) {
|
||||
this.baseURL = null;
|
||||
this.key = null;
|
||||
if (key) this.key = key;
|
||||
if (baseURL) this.baseURL = baseURL;
|
||||
}
|
||||
|
||||
/**
|
||||
* *Promise method for retrieving page data*\
|
||||
* **GET**`/api/v1/page/:type`
|
||||
* @param {string} type - type of pages (`published | menu | featured`) being retrieved; null value defaults to `published`
|
||||
* @example
|
||||
* api.pages('published').then(pages=>{
|
||||
* console.log("Pages Object", pages);
|
||||
* })
|
||||
* @returns {object} json object that contains pages of requested type
|
||||
*
|
||||
* *pages object example*
|
||||
* ```
|
||||
{
|
||||
"pages":
|
||||
[
|
||||
{
|
||||
"id":1,
|
||||
"uuid":"uuid-for-entry",
|
||||
"title":"Entry Title",
|
||||
"feature":"/path/to/image.jpg",
|
||||
"path":"2020/09",
|
||||
"layout":"page",
|
||||
"tags":"these, are, tags",
|
||||
"author":"your-name",
|
||||
"created":"2020 Sep Tue 01",
|
||||
"updated":"2020 Sep Tue 01",
|
||||
"deleted":false,
|
||||
"menu":false,
|
||||
"featured":false,
|
||||
"published":true,
|
||||
"slug":"entry-title",
|
||||
"content":"Premium Content"
|
||||
},
|
||||
{
|
||||
"id":2,
|
||||
"uuid":"uuid-for-entry",
|
||||
"title":"Another Title",
|
||||
"feature":"/path/to/image.jpg",
|
||||
"path":"2020/09",
|
||||
"layout":"page",
|
||||
"tags":"these, are, tags",
|
||||
"author":"your-name",
|
||||
"created":"2020 Sep Tue 01",
|
||||
"updated":"2020 Sep Tue 01",
|
||||
"deleted":false,
|
||||
"menu":false,
|
||||
"featured":false,
|
||||
"published":true,
|
||||
"slug":"another-title",
|
||||
"content":"Premium Content"
|
||||
}
|
||||
],
|
||||
"totalItems":2
|
||||
}
|
||||
* ```
|
||||
*
|
||||
*/
|
||||
pages(type = null) {
|
||||
//set url based on request type
|
||||
let requestURL = "";
|
||||
switch (type) {
|
||||
default:
|
||||
case "published":
|
||||
requestURL = API_GET_PAGES + "?key=" + this.key;
|
||||
break;
|
||||
case "featured":
|
||||
requestURL = API_GET_FEATURED + "?key=" + this.key;
|
||||
break;
|
||||
case "menu":
|
||||
requestURL = API_GET_MENU + "?key=" + this.key;
|
||||
break;
|
||||
}
|
||||
return new Promise((resolve, reject) => {
|
||||
this._request(
|
||||
this.baseURL ? this.baseURL + requestURL : requestURL,
|
||||
TASK_GET_CONTENT
|
||||
)
|
||||
.then((result) => {
|
||||
resolve(result);
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
/**
|
||||
* *Promise method for retrieving single page*\
|
||||
* **GET** `/api/v1/page/single/:id`
|
||||
* @param {string} id - uuid of desired page
|
||||
* @example
|
||||
* api.page("a-uuid-for-a-page").then(page=>{
|
||||
console.log("Page Object", page);
|
||||
* })
|
||||
* @returns {object} json object that contains data for requested page
|
||||
*
|
||||
* *page object example*
|
||||
* ```
|
||||
{
|
||||
"id":1,
|
||||
"uuid":"uuid-for-entry",
|
||||
"title":"Entry Title",
|
||||
"feature":"/path/to/image.jpg",
|
||||
"path":"2020/09",
|
||||
"layout":"page",
|
||||
"tags":"these, are, tags",
|
||||
"author":"your-name",
|
||||
"created":"2020 Sep Tue 01",
|
||||
"updated":"2020 Sep Tue 01",
|
||||
"deleted":false,
|
||||
"menu":false,
|
||||
"featured":false,
|
||||
"published":true,
|
||||
"slug":"entry-title",
|
||||
"content":"Premium Content"
|
||||
}
|
||||
* ```
|
||||
*/
|
||||
page(id) {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._request(
|
||||
this.baseURL
|
||||
? this.baseURL + API_GET_PAGE + "/" + id + "?key=" + this.key
|
||||
: API_GET_PAGE + "/" + id + "?key=" + this.key,
|
||||
TASK_GET_CONTENT,
|
||||
REQUEST_TYPE_GET
|
||||
)
|
||||
.then((result) => {
|
||||
resolve(result);
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* *Promise method for retrieving all tags used by pages*\
|
||||
* **GET** `/api/v1/page/tags`
|
||||
* @example
|
||||
* api.tags().then(tags=>{
|
||||
console.log("Tags Object", tags);
|
||||
* })
|
||||
* @returns {object} json object that contains site tags and page stubs associated with said tag
|
||||
*
|
||||
* *tags object example*
|
||||
* ```
|
||||
[
|
||||
{
|
||||
"tag_name":"this is a tag",
|
||||
"slug":"this-is-a-tag",
|
||||
"pages":
|
||||
[
|
||||
{
|
||||
"title":"This is a title",
|
||||
"slug":"this-is-a-title",
|
||||
"path":"2021/04"
|
||||
},
|
||||
|
||||
{
|
||||
"title":"This is another title",
|
||||
"slug":"this-is-another-title",
|
||||
"path":"2020/10"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"tag_name":"this is another tag",
|
||||
"slug":"this-is-another-tag",
|
||||
"pages":
|
||||
[
|
||||
{
|
||||
"title":"This is a title",
|
||||
"slug":"this-is-a-title",
|
||||
"path":"2021/04"
|
||||
},
|
||||
|
||||
{
|
||||
"title":"This is another title",
|
||||
"slug":"this-is-another-title",
|
||||
"path":"2020/10"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
* ```
|
||||
*/
|
||||
tags() {
|
||||
return new Promise((resolve, reject) => {
|
||||
this._request(
|
||||
this.baseURL
|
||||
? this.baseURL + API_GET_TAGS + "?key=" + this.key
|
||||
: API_GET_TAGS + "?key=" + this.key,
|
||||
TASK_GET_CONTENT,
|
||||
REQUEST_TYPE_GET
|
||||
)
|
||||
.then((result) => {
|
||||
resolve(result);
|
||||
})
|
||||
.catch((err) => {
|
||||
reject(err);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//--------------------------
|
||||
// private
|
||||
//--------------------------
|
||||
_request(
|
||||
requestURL,
|
||||
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.onprogress = self.handleLoadProgress;
|
||||
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) {
|
||||
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) {
|
||||
this.percentComplete = Math.ceil((e.loaded / e.total) * 100);
|
||||
//pass element to display request progress
|
||||
}
|
||||
}
|
||||
|
||||
export { FipamoContentAPI as default };
|
|
@ -194,3 +194,6 @@
|
|||
</div>
|
||||
</section>
|
||||
@endsection
|
||||
@section('scripting')
|
||||
<script type="module" src="/assets/scripts/dash/app/EditPage.js"></script>
|
||||
@endsection
|
||||
|
|
|
@ -11,41 +11,41 @@
|
|||
<link rel="stylesheet" type="text/css" href="/assets/css/dash/start.css">
|
||||
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<body>
|
||||
|
||||
<header>
|
||||
@if($status)
|
||||
<nav class="top-nav">
|
||||
<a href="/dashboard"><img id="the-logo" src="/assets/images/global/fipamo-logo.svg"/></a>
|
||||
<h1>{{ $title }}</h1>
|
||||
<div class="nav-right">
|
||||
@if($status)
|
||||
@include('includes.nav')
|
||||
@endif
|
||||
<header>
|
||||
@if($status)
|
||||
<nav class="top-nav">
|
||||
<a href="/dashboard"><img id="the-logo" src="/assets/images/global/fipamo-logo.svg"/></a>
|
||||
<h1>{{ $title }}</h1>
|
||||
<div class="nav-right">
|
||||
@if($status)
|
||||
@include('includes.nav')
|
||||
@endif
|
||||
</div>
|
||||
</nav>
|
||||
<div class="notify" role="note">
|
||||
@include('includes.notifications')
|
||||
</div>
|
||||
</nav>
|
||||
<div class="notify" role="note">
|
||||
@include('includes.notifications')
|
||||
@endif
|
||||
</header>
|
||||
@if(session('message'))
|
||||
<div class="system-notice-message" role="status">
|
||||
{!! session('message') !!}
|
||||
</div>
|
||||
@endif
|
||||
</header>
|
||||
@if(session('message'))
|
||||
<div class="system-notice-message" role="status">
|
||||
{!! session('message') !!}
|
||||
</div>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
<main>
|
||||
@spaceless
|
||||
@section('main-content')
|
||||
@show
|
||||
@endspaceless
|
||||
</main>
|
||||
<footer>
|
||||
<main>
|
||||
@spaceless
|
||||
@section('main-content')
|
||||
@show
|
||||
@endspaceless
|
||||
</main>
|
||||
<footer>
|
||||
|
||||
</footer>
|
||||
<script type="module" src="/assets/scripts/dash.js"></script>
|
||||
</body>
|
||||
</footer>
|
||||
@section('scripting')
|
||||
@show
|
||||
</body>
|
||||
|
||||
</html>
|
||||
|
|
Loading…
Add table
Reference in a new issue