unclean SPARC
This commit is contained in:
@@ -0,0 +1,135 @@
|
||||
if(!app.helpers) app.helpers = {}
|
||||
/**
|
||||
* Mixing add-in methods to your view instance.
|
||||
* All of this should not be a helper, but inherited this from EICDomContent, but not my framework anymore.
|
||||
* @category MyEic
|
||||
*/
|
||||
app.helpers.activeAttributes = {
|
||||
|
||||
/**
|
||||
* setupTriggers adds all click (data-trigger) and change (data-change) handlers.
|
||||
* handlers should have the signatue : onXyz(component, event), will spit a warning if the handler doesn't exist.
|
||||
* setupTriggers is re-entrant: it can be called again after refreshing part of the view
|
||||
* @param {eicui-components []} components : the view's components (usually result of ui.eicfy(this.el) )
|
||||
*/
|
||||
setupTriggers(components){ // Should inherit this from EICDomContent, but not my framework anymore.
|
||||
for(let component of components.filter(component => component.el.hasAttribute('data-trigger'))) {
|
||||
if(typeof this[component.el.dataset.trigger] !== 'function') {
|
||||
console.warn(`data-trigger without corresponding method : ${component.el.dataset.trigger}`)
|
||||
continue
|
||||
}
|
||||
component.click = this[component.el.dataset.trigger].bind(this, component)
|
||||
}
|
||||
for(let component of components.filter(component => component.el.hasAttribute('data-change'))) {
|
||||
if(typeof this[component.el.dataset.change] !== 'function') {
|
||||
console.warn(`data-change without corresponding method : ${component.el.dataset.trigger}`)
|
||||
continue
|
||||
}
|
||||
component.el.addEventListener("change",this[component.el.dataset.change].bind(this, component))
|
||||
if(component.el.type=='text') component.el.addEventListener("keyup",this[component.el.dataset.change].bind(this, component))
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* setupRefs will populate :
|
||||
* => this.components: an attribute 'data-ref="toto"' will create the reference this.components.toto to the EICUI component
|
||||
* => this.outputs: an attribute 'data-output="titi"' will create the reference this.outputs.toto to the dom node (used by this.output() )
|
||||
* => this.asyncComponents: an attribute 'data-async="tintin"' will create the reference this.asyncComponents.tintin (used by setAsyncLoading() )
|
||||
* => this.asyncElements: an attribute 'data-async="haddock"' will create the reference this.asyncElements.haddock (used by setAsyncLoading() )
|
||||
* setupRefs is re-entrant: it can be called again after refreshing part of the view
|
||||
* @param {eicui-components []} components : the view's components (usually result of ui.eicfy(this.el) )
|
||||
*/
|
||||
setupRefs(components){
|
||||
if(!this.components) this.components = {}
|
||||
for(let component of components.filter(component => component.el.hasAttribute('data-ref'))) {
|
||||
this.components[component.el.dataset.ref] = component
|
||||
}
|
||||
if(!this.asyncComponents) this.asyncComponents = {}
|
||||
for(let component of components.filter(component => component.el.hasAttribute('data-async'))) {
|
||||
this.asyncComponents[component.el.dataset.async] = component
|
||||
}
|
||||
|
||||
if(!this.asyncElements) this.asyncElements = {}
|
||||
for(let el of this.el.querySelectorAll('[data-async]')) {
|
||||
if('eicuiId' in el.dataset) continue
|
||||
this.asyncElements[el.dataset.async] = el
|
||||
}
|
||||
|
||||
if(!this.outputs) this.outputs = {}
|
||||
for(let el of this.el.querySelectorAll('[data-output]')) {
|
||||
this.outputs[el.dataset.output] = el
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* output (singular) : this.output('mydiv', '<p>Some markup</p>') places markup in a data-output node
|
||||
* @param {*} name
|
||||
* @param {*} markup
|
||||
*/
|
||||
output(name, markup){
|
||||
if(name in this.outputs) this.outputs[name].innerHTML = markup
|
||||
else console.warn(`Output ${name} not found !`)
|
||||
},
|
||||
|
||||
/**
|
||||
* setAsyncLoading Turns on all or some of the loaders
|
||||
* @param {boolean} value : true to show loading, false to hide
|
||||
* @param {string[]} names : (optional) array of names to show/hide, (names given via the data-async attributes)
|
||||
* If no names given, will show/hide all asyncs of the view
|
||||
* Small counter so it can handle several async requests which are overlapping and using on the same loader.
|
||||
* Ex1 overlapping completion: loader=true & REQ1-start, loader=true & REQ2-start, REQ1-end & loader=false, REQ2-end & loader=false ]
|
||||
* Ex2 inverted completion: loader=true & REQ1-start, loader=true & REQ2-start, REQ2-end & loader=false, REQ1-end & loader=false ]
|
||||
*/
|
||||
setAsyncLoading(value, names=null){
|
||||
if(!this.asyncComponents) this.asyncComponents = {}
|
||||
if(!this.asyncComponentsCnt) this.asyncComponentsCnt = {}
|
||||
if(!names) names = [ ...Object.keys(this.asyncComponents), ...Object.keys(this.asyncElements)]
|
||||
for(let name of names){
|
||||
if(name in this.asyncComponentsCnt){ this.asyncComponentsCnt[name] = value ? this.asyncComponentsCnt[name]+1 : this.asyncComponentsCnt[name]-1 }
|
||||
else { this.asyncComponentsCnt[name] = value ? 1 : 0 }
|
||||
|
||||
if(name in this.asyncComponents) this.asyncComponents[name].loading = (this.asyncComponentsCnt[name]>0)
|
||||
if(name in this.asyncElements) {
|
||||
const el = this.asyncElements[name].querySelector('.icon-spinner.spin')
|
||||
if(el) el.remove()
|
||||
if((this.asyncComponentsCnt[name]>0)){
|
||||
this.asyncElements[name].prepend(Object.assign(document.createElement('div'),
|
||||
{ className: 'icon-spinner spin', style: 'width:1em;height:1em;margin:1em;position: absolute;z-index: 9999;' }))
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
/**
|
||||
* Manage spin loader
|
||||
* @param {*} element
|
||||
* @param {*} loadingText
|
||||
* @returns
|
||||
*/
|
||||
showLoading(element, loadingText = '<i class="icon-spinner spin"></i>') {
|
||||
if (!element) return
|
||||
element.classList.add('loading')
|
||||
if (element.tagName === 'BUTTON' || element.type === 'button') {
|
||||
element.disabled = true
|
||||
element.setAttribute('data-original-content', element.innerHTML);
|
||||
element.innerHTML = loadingText;
|
||||
} else if (element.hasAttribute('contenteditable')) {
|
||||
element.setAttribute('data-original-content', element.innerHTML);
|
||||
element.innerHTML = `<span class="loader-placeholder">${loadingText}</span>`
|
||||
element.setAttribute('contenteditable', 'false')
|
||||
}
|
||||
},
|
||||
|
||||
hideLoading(element, defaultIcon = null) {
|
||||
if (!element) return;
|
||||
element.classList.remove('loading')
|
||||
if (element.tagName === 'BUTTON' || element.type === 'button') {
|
||||
element.disabled = false;
|
||||
element.innerHTML = element.getAttribute('data-original-content') || defaultIcon || ''
|
||||
} else if (element.hasAttribute('contenteditable')) {
|
||||
element.innerHTML = element.getAttribute('data-original-content') || ''
|
||||
element.setAttribute('contenteditable', 'true')
|
||||
}
|
||||
},
|
||||
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
if(!app.helpers) app.helpers = {}
|
||||
/**
|
||||
* @class mixing addind methods to your view
|
||||
* @category MyEic
|
||||
* @subcategory Helpers
|
||||
* @todo intergate to MeicView ?
|
||||
* @todo extract template path to config ?
|
||||
*/
|
||||
app.helpers.basicDialogs = {
|
||||
/**
|
||||
*
|
||||
* @param {*} options
|
||||
* @param {*} options.message
|
||||
* @param {*} [options.severity]
|
||||
* @param {*} [options.muted]
|
||||
* @param {*} [options.okLabel]
|
||||
* @param {*} [options.cancelLabel]
|
||||
* @returns {*}
|
||||
*/
|
||||
async confirmDialog(options) {
|
||||
options.severity = options.severity ? options.severity : 'danger'
|
||||
options.muted = options.muted ? 'muted' : ''
|
||||
let result = await this.openDialog(await this.loadContent('templates/dialogs/ConfirmDialog', options, options));
|
||||
return(result)
|
||||
},
|
||||
// other basic dialogs here...
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
if(!app.helpers) app.helpers = {}
|
||||
/**
|
||||
* Helper for data transformation.
|
||||
* @class Translator
|
||||
* @category MyEic
|
||||
* @subcategory Helpers
|
||||
*/
|
||||
app.helpers.translator = {
|
||||
toCSV(collection, options /* keys=null, withHeader=true, quoteChar = '"', delimiter = ';', downloadName=null */) {
|
||||
|
||||
let defaultOptions = {
|
||||
headers: [],
|
||||
delimiter: ',',
|
||||
quoteCharacter: '"',
|
||||
filename: 'data'
|
||||
}
|
||||
|
||||
options = {...defaultOptions, ...options}
|
||||
/**/
|
||||
const _download = function(data, filename) {
|
||||
|
||||
const blob = new Blob([new Uint8Array([0xEF, 0xBB, 0xBF]), data], { type: 'text/csv; charset=utf-8' });
|
||||
const url = window.URL.createObjectURL(blob);
|
||||
const a = document.createElement('a');
|
||||
a.setAttribute('href', url);
|
||||
a.setAttribute('download', filename + '.csv');
|
||||
a.click();
|
||||
}
|
||||
|
||||
if(!options.keys) { // take min. common set of keys in collection (take 1st then successively intersect to itself)
|
||||
let ids = Object.keys(collection);
|
||||
options.keys = Object.keys(collection[ids[0]]);
|
||||
for(let id of ids) {
|
||||
options.keys = options.keys.filter(x => (collection[id].hasOwnProperty(x)))
|
||||
}
|
||||
}
|
||||
|
||||
let csv='';
|
||||
let row=[];
|
||||
|
||||
if(options.headers && options.headers.length > 0){
|
||||
for(let key of options.headers) {
|
||||
row.push(options.quoteCharacter + key + options.quoteCharacter);
|
||||
}
|
||||
csv += row.join(options.delimiter)+'\n';
|
||||
} else if(Array.isArray(options.headers)) {
|
||||
for(let title of options.headers){
|
||||
row.push(options.quoteCharacter + title + options.quoteCharacter);
|
||||
}
|
||||
csv += row.join(options.delimiter)+'\n';
|
||||
}
|
||||
for(let id in collection){
|
||||
row = [];
|
||||
for(let key of options.keys){
|
||||
let item = ''
|
||||
if(typeof(key)=='string'){ // normal column
|
||||
item = collection[id]
|
||||
for(let k of key.split('.')) { // Allow for sub-objects with dotted keys notation
|
||||
if(!item) break
|
||||
item = item[k]
|
||||
}
|
||||
} else if(typeof(key)=='function') { // computed column
|
||||
item = key(collection[id])
|
||||
} else console.warn('CSV: Bad column: ',key)
|
||||
row.push(options.quoteCharacter + item + options.quoteCharacter);
|
||||
}
|
||||
csv += row.join(options.delimiter)+'\n';
|
||||
}
|
||||
|
||||
if(options.filename) _download(csv, options.filename);
|
||||
|
||||
return(csv);
|
||||
},
|
||||
fromCSV() {
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
if(!app.helpers) app.helpers = {}
|
||||
/**
|
||||
* Helper to centralize regexps for usual data validations.
|
||||
* @category MyEic
|
||||
* @subcategory Helpers
|
||||
*/
|
||||
app.helpers.validators = {
|
||||
validators : class {
|
||||
/*********************** All Regexp (for eventual direct use **********************/
|
||||
static mailRFC5322Regex = /^([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x22([^\x0d\x22\x5c\x80-\xff]|\x5c[\x00-\x7f])*\x22))*\x40([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d)(\x2e([^\x00-\x20\x22\x28\x29\x2c\x2e\x3a-\x3c\x3e\x40\x5b-\x5d\x7f-\xff]+|\x5b([^\x0d\x5b-\x5d\x80-\xff]|\x5c[\x00-\x7f])*\x5d))*$/
|
||||
static mustacheTokenRegex = new RegExp(/\{\{([\w\-\.\^\#\/]+)\}\}/,'gm')
|
||||
|
||||
|
||||
|
||||
/*********************** All validators **********************/
|
||||
static isValidEmail(text){
|
||||
return(text.match(this.mailRFC5322Regex))
|
||||
}
|
||||
|
||||
static isValidMustacheToken(text){
|
||||
return(text.match(this.mustacheTokenRegex))
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user