import {
camelCase,
defineProperty,
each,
empty,
hasProp,
isString,
isArrayish,
isObject,
not,
normalizeData,
} from "../Utils/Utils.js"
/**
* Identificador Único para los Atributos
* @memberOf module:Core.Data
* @private
* @type {Number}
* @defaultvalue -1
*/
var DataUI = -1
/**
* Manejador de Eventos Data
* @namespace Data
* @memberOf module:Core
* @requires Utils
* @class
* @tutorial Data
*/
export default class Data {
/**
* @return {module:Core.Data}
*/
constructor() {
this.UID = "FSData"
DataUI++
this.ID = DataUI
}
/**
* Verifica si el objeto dado es un Elemento
* @memberOf module:Core.Data
* @public
* @param {Element} el El elemento
* @return {Boolean}
*/
acceptData(el) {
return el.nodeType === 1 || el.nodeType === 9 || !( +el.nodeType )
}
/**
* Valida si se puede establece o usar el Atributo DataSet del HTMLElement
* @param {Element} el El Elemento
* @memberOf module:Core.Data
* @public
* @return {Boolean}
*/
acceptDataSet(el) {
return this.acceptData(el) && !not(el.dataset);
}
/**
* Obtiene los datos Almacenados en el Elemento
* @memberOf module:Core.Data
* @public
* @param {Element} el El elemento
* @param {Boolean} config Indica si el objeto sera configurable
* @return {Object}
*/
storage(el, config = true) {
let val = el[this.UID]
if (!val) {
val = {}
if (this.acceptData(el)) {
if (el.nodeType) {
el[this.UID] = val
} else {
defineProperty(el, this.UID, {
value: val,
configurable: config
})
}
}
}
return val
}
/**
* Establece el nuevo valor de la propiedad
* @memberOf module:Core.Data
* @public
* @param {Element} el El elemento
* @param {(String|Object)} key La clave
* @param {(Object|String)} data El valor a establecer
* @return {(Object|String)} El nuevo valor establecido
*/
set(el, key, data) {
let store = this.storage(el)
if (isString(key)) {
store[camelCase(key)] = data
} else {
for (let prop in key) {
if (hasProp(key, prop)) {
store[camelCase(prop)] = key[prop]
}
}
}
return store
}
/**
* Obtiene el valor del Atributo o todos
* @memberOf module:Core.Data
* @public
* @param {Element} el El elemento
* @param {(String|Null)} key La clave a buscar si se omite se buscaran todos los atributos
* @return {(Object|String|Boolean)}
*/
get(el, key) {
if (not(key)) {
return this.storage(el)
}
return el[this.UID] && el[this.UID][key] ? el[this.UID][key] : false
}
/**
* Obtiene o Establece el atributo
* @memberOf module:Core.Data
* @public
* @param {Element} el El elemento
* @param {String} key La clave
* @param {(String|Object)} data El valor
* @return {(Object|String)} El valor obtenido o establecido
*/
access(el, key, data) {
if (not(key) || ((key && isString(key)) && not(data)) ) {
return this.get(el, key)
}
this.set(el, key, data)
return not(data) ? key : data
}
/**
* Verifica si el elemento tiene la clave dada
* @public
* @memberOf module:Core.Data
* @param {Element} el El elemento
* @param {String} key La clave
* @return {Boolean}
*/
has(el, key) {
if (not(key)) {
let c = this.storage(el)
return !not(c)
} else {
return this.get(el, key) !== false ? true : ( el.hasAttributes && el.hasAttributes(`data-${key}`) ? el.getAttribute(`data-${key}`) : false)
}
}
/**
* Remueve una clave dada
* @public
* @memberOf module:Core.Data
* @param {Element} el El elemento
* @param {String} key La clave
* @return {(void|Boolean)}
*/
remove(el, key) {
let i, store = el[this.UID]
if (not(store)) {
let ds = this.data(el)
if (not(ds)){
return
}
this.remove(el, key)
if(this.acceptDataSet(el)) {
attrs = el.attributes
i = attrs.length
while (i--) {
if (attrs[i]) {
let name = attrs[i].name
if (name.indexOf("data-") === 0) {
el.removeAttribute(name)
}
}
}
}
return
}
if (!not(key)) {
if (isArrayish(key)) {
key = key.map(camelCase)
} else {
key = camelCase(key)
key = key in store ? [key] : (key.match( /[^\x20\t\r\n\f]+/g ) || [])
}
i = key.length
while (i--) {
delete store[key[i]]
}
}
if (not(key) && not(store)) {
if (el.nodeType) {
el[this.UID] = undefined
} else {
delete el[this.UID]
}
}
return true
}
/**
* Establece los atributos data de un elemento
* @memberOf module:Core.Data
* @public
* @param {Element} elem Elemento a manipular
* @param {String} key La clave del atributo data ejemplo data-valor; key = valor
* @param {(Object|String|Array)} data El resultado del atributo data
* @return {(Object|undefine|Array)} El resultado del atributo data obtenido
*/
attrToStorage(elem, key, data) {
let name
if (empty(data) && elem.nodeType === 1) {
name = "data-" + key.replace(/[A-Z]/g, "-$&").toLowerCase()
data = elem.getAttribute(name)
if (typeof data === "string") {
data = normalizeData(data)
this.set(elem, key, data)
} else {
data = undefined
}
}
return data
}
/**
* Establece u Obtiene los atributos de Data
* @memberOf module:Core.Data
* @public
* @param {(NodeList|Element)} els El o los Elementos
* @param {...(Array|Object|String)} arg Cualqier argumento según su accion hasta 2 maximo
*
* @return {*}
*/
data(els, ...args) {
let ds,
attrs,
i,
el
if(not(els.length)) {
el = els
els = [els]
} else {
el = els[0]
}
if(args.length === 0) {
ds = this.get(el)
if(this.acceptDataSet(el)) {
attrs = el.attributes
i = attrs.length
while (i--) {
if (attrs[i]) {
let name = attrs[i].name
if (name.indexOf("data-") === 0) {
name = camelCase(name.slice( 5 ))
this.attrToStorage(el, name, ds[name])
}
}
}
}
return ds
}
if (args.length === 1) {
if (isArrayish(args[0])) {
let res = {},
i = 0
each(els, (elem) => {
let id = elem.getAttribute("id"),
prefix = !not(id) ? id : el.tagName+i
args[0].forEach( (d) => {
res[prefix] = res[prefix] || {}
let re = this.get(elem, d)
if (not(re)) {
if (elem.nodeType === 1) {
re = elem.hasAttributes(`data-${d}`) ? elem.getAttribute(`data-${d}`) : re
re = normalizeData(re)
}
}
res[prefix][d] = re
})
i++
})
return res
} else if (isObject(args[0])) {
return each(els, (elem) => {
for (let key in args[0]) {
if (hasProp(args[0], key)) {
let value = normalizeData(args[0][key])
this.set(elem, key, value)
}
}
})
} else if (isString(args[0])) {
let res = this.get(el, args[0])
if (!res || not(res)) {
if (el.nodeType === 1) {
res = el.hasAttributes(`data-${args[0]}`) ? el.getAttribute(`data-${args[0]}`) : res
res = normalizeData(res)
}
}
return res
}
}
return each(els, (elem) => {
this.set(elem, args[0], args[1])
})
}
}