fork-lift.js
Version:
JavaScript UI Management System
676 lines (661 loc) • 23.2 kB
JavaScript
/*
______ _ _ _ __ _
| ____| | | | (_)/ _| |
| |__ ___ _ __| | _| |_| |_| |_
| __/ _ \| '__| |/ / | | _| __|
| | | (_) | | | <| | | | | |_
|_| \___/|_| |_|\_\_|_|_| \__|
(c) 2017 Brendan Fuller
*/
/**
* Forklift.js - API System
*/
class ForkliftAPI {
constructor() {
this.cachedURL = {}
}
load(url, callback, cache = true) {
let me = this
if (this.isCached(url) && cache) {
try {
try {
let request = new XMLHttpRequest()
request.onreadystatechange =
function ready() {
if (request.readyState < 4) {
return null
}
if (request.status !== 200) {
return null
}
if (request.readyState === 4) {
me.setCached(url, request)
callback(request)
}
}
request.open('GET', url, true)
request.send('')
} catch (e) {
return false
}
} catch (e) {
return false
}
return true
} else {
callback(this.cachedURL[url])
return true
}
}
isCached(url) {
if (this.cachedURL[url] == undefined) {
return true
}
return false
}
setCached(url, data) {
if (this.cachedURL[url] == undefined) {
this.cachedURL[url] = data
}
}
log(message, css = null) {
setTimeout(console.log.bind(console, message, css), 0);
}
warn(message, css = null) {
setTimeout(console.warn.bind(console, message, css), 0);
}
error(message, css = null) {
setTimeout(console.error.bind(console, message, css), 0);
}
timeEnd(id) {
setTimeout(console.timeEnd(id), 0)
}
}
/**
* PaletteHandler - Handles all created Palettes & Units
*/
class UnitHandler {
constructor(parent, paletteID) {
this.parent = parent
this.paletteID = paletteID
this.units = {}
}
addUnit(unitID, instance) {
if (!this.isUnit(unitID)) {
this.units[unitID] = {}
this.units[unitID].Class = instance
}
}
getUnit(unitID) {
if (this.isUnit()) {
return this.units[unitID]
}
}
getUnits() {
return this.units
}
isUnit(unitID) {
if (this.units[unitID] != undefined) {
return true
}
return false
}
}
/**
* BoxHander - Handles all created BOXES per PALETTE which can be then manipulated by UNITS
*/
class BoxHandler {
/**
* @param {*} parent - The PALETTE instance
* @param {*} pid - The PALETTE id
*/
constructor(parent, pid) {
this.parent = parent
this.pid = pid
this.totalBoxes = 0
this.boxes = {}
}
addBox(object, name) {
this.totalBoxes = this.totalBoxes + 1
this.boxes[this.totalBoxes] = {}
this.boxes[this.totalBoxes].Object = object
this.boxes[this.totalBoxes].Name = name
this.boxes[this.totalBoxes].boxLoaded = false
this.boxes[this.totalBoxes].contentLoaded = false
return this.totalBoxes
}
boxLoaded(id) {
this.boxes[id].boxLoaded = true
this.parent._checkBoxes()
}
contentLoaded(id) {
this.boxes[id].contentLoaded = true
this.parent._checkBoxes()
}
getBoxes() {
return this.boxes
}
_isLoaded() {
for (let box in this.boxes) {
if (!this.boxes[box].contentLoaded) {
return false
}
else if (!this.boxes[box].boxLoaded) {
return false
} else {
//console.log("BOX: " + this.boxes[box].Name + " | PID: " + this.pid)
}
}
if (this.totalBoxes > 0) {
return true
}
return true
}
}
class ForkliftHandler {
constructor() {
this.palettes = {}
this.loaded = false
this.units = {}
}
newPalette(paletteID, file) {
// Check if palette name is already used or not
if (this.palettes[paletteID] == null || this.palettes[paletteID] == undefined) {
// Create a dictonary for the id then
this.palettes[paletteID] = {}
this.palettes[paletteID].BoxHandler = new BoxHandler(this, paletteID)
this.palettes[paletteID].UnitHandler = new UnitHandler(this, paletteID)
this.palettes[paletteID].File = file
} else {
try {
throw new Error("Project name already used!")
} catch (e) {
let file = e.stack.split("\n")[(e.stack.split("\n").length - 1)].split("at ").pop().slice(0, -1)
forklift.API.error(`[forklift.js] %c ${e.message} in '${file}'`)
}
}
}
/**
*
* @param {*} palettes
* @param {*} unitID
* @param {*} file
*/
newUnit(palettes, unitID, file) {
if (this.units[unitID] == null) {
this.units[unitID] = {}
palettes = palettes.split("|")
this.units[unitID].File = file
this.units[unitID].Palettes = palettes
}
}
/**
*
* @param {*} paletteID
*/
getUnitHandler(paletteID) {
if (this.palettes[paletteID] != null) {
return this.palettes[paletteID].UnitHandler
}
}
/**
*
* @param {*} paletteID
*/
getBoxHandler(paletteID) {
if (this.palettes[paletteID] != null) {
return this.palettes[paletteID].BoxHandler
}
}
getPalette(paletteID) {
if (this.palettes[paletteID] != null) {
return this.palettes[paletteID]
}
return null
}
getUnit(unitID) {
if (this.units[unitID] != null) {
return this.units[unitID]
}
return null
}
getFile(paletteID) {
if (this.palettes[paletteID] != null) {
return this.palettes[paletteID].File
}
}
isPalette(paletteID) {
if (this.palettes[paletteID].Palette != undefined) {
return true
}
return false
}
_loadPalettes() {
this.start = performance.now()
for (let palette in this.palettes) {
try {
this.palettes[palette].Palette = require(`./${this.palettes[palette].File}`)
try {
let difference = (performance.now() - this.start).toFixed(2)
forklift.API.log(`[forklift.js] %cLoading Palette: ${palette} | ${difference}ms`, 'color: orange;')
this.palettes[palette].Class = new this.palettes[palette].Palette(palette)
} catch (e) {
forklift.API.warn(`[forklift.js] %cCould not load: ${palette}`, 'color: purple')
let file = e.stack.split("\n")[1].split("(").pop().slice(0, -1)
forklift.API.error(`[forklift.js] %c'${e.message}' in '${file}'`)
if (e) throw e;
}
} catch (e) {
let file = e.stack.split("\n")[0].split("Error: Cannot find module '").pop().slice(0, -1)
forklift.API.error(`[forklift.js] %cCannot find '${file}'`)
if (e) throw e;
}
}
let difference = (performance.now() - this.start).toFixed(2)
forklift.API.log(`[forklift.js] %cPalette(s) Loaded | ${difference}ms`, 'color: orange')
forklift.API.log(`[forklift.js] %cLoading Boxes... | ${difference}ms`, 'color: green')
}
_loadUnits() {
let me = this
for (let unit in this.units) {
try {
this.units[unit].Unit = require(`./${this.units[unit].File}`)
try {
let end = performance.now()
let difference = (end - this.start).toFixed(2)
forklift.API.log(`[forklift.js] %cLoaded Unit: ${unit} | ${difference}ms`, 'color: #ff69b4;')
let data = {}
data.p = this.paletteID
data.u = unit
data.f = this.units[unit].File
this.units[unit].Class = new this.units[unit].Unit(data)
} catch (e) {
forklift.API.warn(`[forklift.js] %cCould not load Unit: ${unit}`, 'color: purple')
let file = e.stack.split("\n")[1].split("(").pop().slice(0, -1)
forklift.API.error(`[forklift.js] %c'${e.message}' in '${file}'`)
if (e) throw e;
}
} catch (e) {
let file = e.stack.split("\n")[0].split("Error: Cannot find module '").pop().slice(0, -1)
forklift.API.error(`[forklift.js] %cCannot find '${file}'`)
if (e) throw e;
}
let allPalette = ""
let loop = 0
for (let palette in this.units[unit].Palettes) {
if (this.palettes[this.units[unit].Palettes[palette]] != null) {
this.getUnitHandler(this.units[unit].Palettes[palette]).addUnit(unit, this.units[unit].Class)
if (loop < 1) {
allPalette = allPalette + this.units[unit].Palettes[palette]
} else {
allPalette = allPalette + ", " + this.units[unit].Palettes[palette]
}
loop = loop + 1
}
}
let difference = (performance.now() - this.start).toFixed(2)
forklift.API.log(`[forklift.js] %c${unit} hooked on palette: [${allPalette}] | ${difference}ms`, 'color: #54b2a9;')
}
if (Object.keys(this.units) == 0) {
let difference = (performance.now() - this.start).toFixed(2)
forklift.API.log(`[forklift.js] %cNo Units found! | ${difference}ms`, 'color: blue')
}
for (let palette in this.palettes) {
let boxHandler = this.getBoxHandler(palette)
let boxes = boxHandler.getBoxes()
for (let box in boxes) {
setTimeout(() => {
boxes[box].Object.onUnitLoad(me.start)
}, 50)
}
this.palettes[palette].Class.onUnitLoad()
}
}
_checkBoxes() {
let me = this
let loaded = true
for (let palette in this.palettes) {
let box = this.getBoxHandler(palette)
if (!box._isLoaded()) {
loaded = false
}
}
if (loaded && this.loaded == false) {
this.loaded = true
let end = performance.now()
let difference = (end - this.start).toFixed(2)
forklift.API.log(`[forklift.js] %cBoxes Loaded | ${difference}ms`, 'color: green')
forklift.API.log(`[forklift.js] %cLoading Units... | ${difference}ms`, 'color: blue')
setTimeout(() => {
me._loadUnits()
}, 0)
}
}
}
/**
* Extends of new Palette Created
*/
class PaletteLoader {
constructor(id) {
this.id = id
this.file = forklift.Handlers.getFile(id)
this.units = forklift.Handlers.getUnitHandler(id).getUnits()
this.elements = {}
}
/**
* Adds a Box (aka Custom HTML Element) that allows for the content to be created
* - Shadow Root
* - Content Slot
* @param {Name of box} name
* @param {Element tag} element -
* @param {Class intance} htmlObject
*/
addBox(name, element, htmlObject) {
if (!this._isBox(name)) {
this.elements[name] = {}
this.elements[name].ElementTag = element
var data = {}
data.file = this.file
data.name = name
data.id = this.id
let proto = Object.create(HTMLElement.prototype, {
createdCallback: {
value: function () {
this.shadow = this.attachShadow({ mode: "closed" });
data.element = this
this.object = new htmlObject(data)
}
},
attributeChangedCallback: {
value: function (name, oldValue, newValue) {
this.object.onAttributeChange(name, oldValue, newValue)
}
}
});
document.registerElement(element, { prototype: proto });
} else {
forklift.API.log(`[forklift.js] %c'${name}' element already used in '${this.file}'`, 'color: red')
}
}
/**
* Gets a ElementTag by its name
* @param {Name of Tag} name
*/
getElementTag(name) {
if (this._isBox(name)) {
return this.elements[name].ElementTag
} else {
return null
}
}
/**
* Query's a Box (custom HTML Element, if only one is specified)
* @param {Name of Box} name
*/
getBox(name) {
//TODO: Add a callback if make sure that if more than one element of the same name is being used, have an error occur.
if (this._isBox(name)) {
return document.querySelector(this.elements[name].ElementTag)
}
}
/**
* Gets Box Instane
* @param {Name of Box} name
*/
getBoxObject(name) {
if (this._isBox(name)) {
return document.querySelector(this.elements[name].ElementTag).object
}
}
_isBox(name) {
if (this.elements[name] != null) return true
return false
}
onUnitLoad() {}
}
/**
* PaletteBox - a BOX extender class for use with a PALETTELOADER
*/
class PaletteBox {
/**
* @param {Palette Data Array} data
*/
constructor(data) {
this.element = data.element
this.file = data.file
this.pid = data.id
this.name = data.name
this.filePath = this.file.split(this.file.substring(this.file.lastIndexOf("/") + 1))[0]
this.shadow = this.element.shadow
this.bid = forklift.Handlers.getBoxHandler(this.pid).addBox(this, this.name)
}
/**
* loadBox - Loads HTML into the BOX shadow document
* @param {HTML File} file
*/
loadBox(file) {
if (file == null) {
forklift.Handlers.getBoxHandler(this.pid).boxLoaded(this.bid)
this.element.shadow.innerHTML = "<slot></slot>"
} else {
let me = this
let html = ""
let feedback = forklift.API.load((me.filePath + file), (data) => {
if (data != null) {
html = data.responseText
html = html.split("<template>")[1].split("</template>")[0]
this.element.shadow.innerHTML = html
} else {
forklift.API.log(`[forklift.js] %cCannot load '${file}'`, 'color: red')
}
this.onBoxLoad()
})
if (!feedback) {
forklift.API.log(`[forklift.js] %cCannot find box '${file}'`, 'color: red')
}
forklift.Handlers.getBoxHandler(this.pid).boxLoaded(this.bid)
}
}
/**
* loadContent - Loads HTML file directly into the TAG
* @param {HTML File} file
*/
loadContent(file) {
if (file == null) {
forklift.Handlers.getBoxHandler(this.pid).contentLoaded(this.bid)
} else {
let me = this
let html = ""
let feedback = forklift.API.load((me.filePath + file), (data) => {
if (data != null) {
html = data.responseText
html = html.split("<template>")[1].split("</template>")[0]
this.element.innerHTML = html
} else {
forklift.API.log(`[forklift.js] %cCannot load '${file}'`, 'color: red')
}
this.onContentLoad()
})
if (!feedback) {
forklift.API.log(`[forklift.js] %cCannot find content '${file}'`, 'color: red')
}
forklift.Handlers.getBoxHandler(this.pid).contentLoaded(this.bid)
}
}
querySelect(object) {
try {
return this.shadow.querySelector(object)
} catch (e) {
let file = e.stack.split("\n")[2].split("(").pop().slice(0, -1)
forklift.API.log(`[forklift.js] %c'${e.message}' \n'${file}'`, 'color: red')
}
}
setAttribute(attr, name) {
try {
return this.element.setAttribute(attr, name)
} catch (e) {
let file = e.stack.split("\n")[2].split("(").pop().slice(0, -1)
forklift.API.log(`[forklift.js] %c'${e.message}' \n'${file}'`, 'color: red')
}
}
getAttribute(attr) {
try {
return this.element.getAttribute(attr)
} catch (e) {
let file = e.stack.split("\n")[2].split("(").pop().slice(0, -1)
forklift.API.log(`[forklift.js] %c'${e.message}' \n'${file}'`, 'color: red')
}
}
callEvent(object, args = null, output = false, unitID = null, type = null) {
let handler = forklift.Handlers.getUnitHandler(this.pid)
let units = handler.getUnits()
let pass = false
if (unitID == null) {
for (let unit in units) {
pass = false
try {
//Check type of UNIT? (if true)
if (type != null) {
//Get type of unit is defined (default to "NONE")
if (units[unit].Class._getType() == type) {
//IF type is same time as given, let it pass and run event
pass = true
} //else, don't do anything cause pass is already set to false
} else {
//If there is not type (which is most likely going to happen), just continue with the if statement
pass = true
}
if (pass) {
console.log(unit)
units[unit].Class[`${object}`].apply(units[unit].Class._getThis(), args)
}
} catch (e) {
//let file = e.stack.split("\n")[(1)].split("at ").pop().slice(0, -1)
//forklift.API.error(`[forklift.js] %c ${e.message} in '${file}'`)
//console.log(e)
}
}
} else {
let check = false
for (let unit in units) {
if (unitID == unit) {
check = true
}
}
if (check) {
let unit = unitID
if (output) {
return units[unit].Class[`${object}`].apply(units[unit].Class._getThis(), args)
} else {
units[unit].Class[`${object}`].apply(units[unit].Class._getThis(), args)
}
}
}
}
onAttributeChange(name, oldValue, newValue) { }
onBoxLoad() { }
onContentLoad() { }
onUnitLoad() { }
}
/**
* UNIT Handler Class
* - When a UNIT is created it will extend this class.
* - This class allows for hooking to other methods
* -
*/
class Unit {
/**
* @param {ID of UNIT} d.u
* @param {File of UNIT} d.f
*/
constructor(d) {
this.id = d.u
this.file = d.f
this.filePath = this.filePath = this.file.split(this.file.substring(this.file.lastIndexOf("/") + 1))[0]
this.type = "NONE"
this.hookedUnits = {}
}
/**
* getDirectory - Gets file path
*/
getDirectory() {
return this.filePath
}
//TODO: Finished UnitHook
unitHook(unitID) {
forklift.Handlers.getUnit(unitID).Class.onUnitBind(this.id)
}
/**
* setType - Sets the type of unit defined by the forklift project
* @param {Unit Type} type
*/
setType(type) {
this.type = type
}
//TODO: Also finish bind event ()
onUnitBind(unitID) {
if (this.hookedUnits[unitID] == null) {
this.hookedUnits[unitID] = unitID
}
}
callEvent(object, args = null, output = false) {
for (let unit in this.hookedUnits) {
try {
forklift.Handlers.getUnit(unit).Class[`${this.id}_${object}`].apply(forklift.Handlers.getUnit(unit).Class._getThis(), args)
} catch (e) {
//error would occur
}
}
}
/**-----------------------------------------------------------------**/
/**
* _getThis - Gets this?
*/
_getThis() {
return this
}
_getType() {
return this.type
}
_addUnit(unitID) {
}
}
class ForkliftApp {
constructor() { }
run() {
forklift.Handlers._loadPalettes()
}
loadPalette(paletteID, file) {
forklift.Handlers.newPalette(paletteID, file)
}
loadUnit(palettes, unitID, file) {
forklift.Handlers.newUnit(palettes, unitID, file)
}
getPalette(paletteID) {
return forklift.Handlers.getPalette(paletteID)
}
getPaletteInstance(paletteID) {
return forklift.Handlers.getPalette(paletteID).Class
}
getUnit(unitID) {
return forklift.Handlers.getUnit(unitID)
}
getUnitInstance(unitID) {
return forklift.Handlers.getUnit(unitID).Class
}
/* -------- Private Functions --------- */
_loadTemplate(location) {
API.load()
}
_statistics() {
}
}
const forklift = {}
forklift.API = new ForkliftAPI()
forklift.Handlers = new ForkliftHandler()
forklift.App = new ForkliftApp()
forklift.Unit = Unit
forklift.PaletteLoader = PaletteLoader
forklift.PaletteBox = PaletteBox
const fl = forklift
/* END OF forklift.js */