logic-bind-model
Version:
white framework web
627 lines (544 loc) • 27.3 kB
JavaScript
/**** html-column.js | HTMLColumn ****/
//==============================================================
import { ExtendError } from 'logic-entity';
import { Type } from 'logic-entity';
import { MetaColumn } from 'logic-entity';
import { Util } from './util-wrap.js';
// local function
function _isString(obj) { // 공백아닌 문자 여부
if (typeof obj === 'string' && obj.length > 0) return true;
return false;
}
function setDocument(flag, selector, option, value) {
if (typeof document === 'undefined') return;
// 요소 선택: key 셀렉터에 해당하는 첫 번째 요소를 선택합니다.
var elem = document.querySelector(selector);
// flag = flag.toLowerCase();
flag = (flag || '').toLowerCase();
if (elem) {
// 1. jquery(key).val(value);
// → 폼 요소(input, select, textarea 등)의 값을 설정합니다.
if (flag === 'val' || flag === 'value') {
elem.value = value;
return elem;
}
// 2. jquery(key).text(value);
// → 요소의 텍스트 콘텐츠를 설정합니다.
if (flag === 'text') {
elem.textContent = value;
return elem;
}
// 3. jquery(key).html(value);
// → 요소의 내부 HTML(markup)을 설정합니다.
if (flag === 'html') {
elem.innerHTML = value;
return elem;
}
// 4. jquery(key).prop(option, value);
// → DOM 프로퍼티를 설정합니다. 예를 들어, option이 "checked"인 경우 elem.checked = value;
if (flag === 'prop') {
// elem.prop(option, value);
// → 요소의 프로퍼티 값을 설정합니다.
// elem[option] = value;
elem[option] = value;
return elem;
}
// 5. jquery(key).attr(option, value);
// → 요소의 attribute 값을 설정합니다.
if (flag === 'attr') {
elem.setAttribute(option, value);
return elem;
}
// 6. jquery(key).css(option, value);
// → 인라인 스타일을 설정합니다. option이 CSS 프로퍼티 이름(예: "backgroundColor" 또는 "background-color")이어야 합니다.
// 만약 하이픈 표기법("background-color")를 사용하는 경우, 이를 camelCase("backgroundColor")로 변환해주어야 합니다.
if (flag === 'css') {
elem.style[option] = value;
return elem;
}
}
}
function getDocument(flag, selector, option) {
if (typeof document === 'undefined') return '';
// 요소 선택: key 셀렉터에 해당하는 첫 번째 요소를 선택합니다.
var elem = document.querySelector(selector);
flag = flag.toLowerCase();
if (elem) {
// 1. jquery(key).val(__val);
// → 폼 요소(input, select, textarea 등)의 값을 설정합니다.
if (flag === 'val' || flag === 'value') {
return elem.value;
}
// 2. jquery(key).text(__val);
// → 요소의 텍스트 콘텐츠를 설정합니다.
if (flag === 'text') {
return elem.textContent;
}
// 3. jquery(key).html(__val);
// → 요소의 내부 HTML(markup)을 설정합니다.
if (flag === 'html') {
return elem.innerHTML;
}
// 4. jquery(key).prop(option, __val);
if (flag === 'prop') {
// elem.prop(option, __val);
// → 요소의 프로퍼티 값을 설정합니다.
// elem[option] = __val;
return elem[option];
}
// 5. jquery(key).attr(option, __val);
if (flag === 'attr') {
return elem.getAttribute(option);
}
// 6. jquery(key).css(option, __val);
if (flag === 'css') {
return elem.style[option];
// var computedStyle = window.getComputedStyle(elem);
// return computedStyle.getPropertyValue(option);
}
}
return '';
}
function detectElementType(selector) {
if (typeof document === 'undefined') return '';
var elem = document.querySelector(selector);
if (!elem) return '';
var tagName = elem.tagName.toLowerCase();
// 1. value 속성이 있는 form 요소는 무조건 'value'로 판단
var hasValueAttr = 'value' in elem;
var isFormControl = ['input', 'textarea', 'select'].includes(tagName);
if (hasValueAttr && isFormControl) {
return 'value';
}
// 2. HTML 콘텐츠 검사
const html = elem.innerHTML?.trim() || '';
const hasHtmlTag = /<[^>]+>/.test(html);
if (hasHtmlTag) return 'html';
// 3. 텍스트로 판단
return 'text';
}
var HTMLColumn = (function (_super) {
/**
* HTML 컬럼
*
* @constructs HTMLColumn
* @extends MetaColumn
*/
function HTMLColumn(p_name, p_entity, p_option) {
_super.call(this, p_name, p_entity, p_option);
var domType = null;
var isReadOnly = false;
var isHide = false;
var element = null;
var getFilter = null;
var setFilter = null;
// var selector = { key: '', type: 'none' };
var selector = null;
/**
* 아이템 DOM 타입
*
* @member {*} HTMLColumn#domType
*/
Object.defineProperty(this, 'domType', {
get: function() { return domType; },
set: function(nVal) {
// TODO:: 자료종류 {input: {type: 'text'...}} 만들어야함 => 필요성 검토해야함
// TODO: DOM 인스턴스 여부로 검사해야함
if(typeof nVal !== 'object') throw new ExtendError(/EL054601/, null, [this.constructor.name]);
domType = nVal;
},
configurable: true,
enumerable: true
});
/**
* 읽기전용 여부
*
* @member {*} HTMLColumn#isReadOnly
*/
Object.defineProperty(this, 'isReadOnly', {
get: function() { return isReadOnly; },
set: function(nVal) {
if(typeof nVal !== 'boolean') throw new ExtendError(/EL054602/, null, [this.constructor.name]);
isReadOnly = nVal;
},
configurable: true,
enumerable: true
});
/**
* 숨김 여부
*
* @member {*} HTMLColumn#isHide
*/
Object.defineProperty(this, 'isHide', {
get: function() { return isHide; },
set: function(nVal) {
if(typeof nVal !== 'boolean') throw new ExtendError(/EL054603/, null, [this.constructor.name]);
isHide = nVal;
},
configurable: true,
enumerable: true
});
/**
* DOM 요소
*
* @member {*} HTMLColumn#element
*/
Object.defineProperty(this, 'element', {
get: function() { return element; },
set: function(nVal) { // TODO: DOM 인스턴스 여부로 검사해야함
if(typeof nVal !== 'object') throw new ExtendError(/EL054604/, null, [this.constructor.name]);
element = nVal;
},
configurable: true,
enumerable: true
});
/**
* 셀렉터
*
* @member {*} HTMLColumn#selector
* @example
* type
* - val | value : 요소의 value 속성값
* - text : 요소의 텍스트값
* - html : 요소의 html값
* - css.속성명 : css 의 속성값 (객체)
* - prop.속성명 : 요소의 속성명값 (초기상태기준)
* - attr.속성명 : 요소의 속성명값 (현재상태)
* - none : 아무일도 하지 않음, 표현의 목적
*/
Object.defineProperty(this, 'selector', {
get: function() { return selector; },
// set: function(nVal) {
// var newSelector = { key: '', type: 'value' };
// if (typeof nVal === 'string') {
// // selector.key = nVal;
// selector = newSelector;
// selector.key = nVal;
// } else if (typeof nVal === 'object' && typeof nVal.key !== 'undefined') {
// selector = nVal;
// } else {
// throw new Error('Only [selector] type "string | object.key" can be added');
// }
// // selector = selector;
// },
set: function(nVal) {
var newSelector = { key: '' };
var type;
if (typeof nVal === 'string' ) {
type = detectElementType(nVal) || 'none';
newSelector = { key: nVal, type: type };
} else if (typeof nVal === 'object') {
if (typeof nVal['key'] === 'string') newSelector['key'] = nVal['key'];
if (typeof nVal['type'] === 'string') newSelector['type'] = nVal['type'].toLowerCase();
else newSelector['type'] = 'none';
} else throw new ExtendError(/EL054605/, null, [this.constructor.name]);
selector = newSelector;
if (_isString(selector['key']) && typeof document === 'object' && document.querySelector) {
element = document.querySelector(selector['key']);
}
},
configurable: true,
enumerable: true
});
/**
* value 값 필터
*
* @member {Function} HTMLColumn#getFilter
*/
Object.defineProperty(this, 'getFilter', {
get: function() { return getFilter; },
set: function(val) {
if(typeof val !== 'function') throw new ExtendError(/EL054606/, null, [this.constructor.name]);
getFilter = val;
},
configurable: true,
enumerable: true
});
/**
* value 값 필터
*
* @member {Function} HTMLColumn#setFilter
*/
Object.defineProperty(this, 'setFilter', {
get: function() { return setFilter; },
set: function(val) {
if(typeof val !== 'function') throw new ExtendError(/EL054607/, null, [this.constructor.name]);
setFilter = val;
},
configurable: true,
enumerable: true
});
/**
* 아이템 값 (오버라이딩)
*
* @override
* @member {*} HTMLColumn#value
*/
Object.defineProperty(this, 'value', {
get: function() {
var __val;
var key, type, option;
// 우선순위 : 1
if (typeof this.getter === 'function' ) {
__val = this.getter.call(this);
// 검사 및 이벤트 발생
if (this.$value !== null && this.$value !== __val) {
this._onChanged(__val, this.$value);
this.$value = __val; // 내부에 저장
}
// 우선순위 : 2
// } else if (__selector !== null && __filter === null) {
} else if (selector !== null || typeof this.getFilter === 'function') {
const isNode = typeof process !== 'undefined' && process.versions !== null && process.versions.node !== null && globalThis.isDOM !== true;
// node 에서는 강제 종료함
if (!isNode) {
key = this.selector.key;
type = this.selector.type.split('.')[0].toLowerCase();
option = this.selector.type.split('.')[1] || '';
// option = type.indexOf('.') > -1 ? type.substr(type.indexOf('.') + 1) : '';
if (type !== 'none'){
if (type === 'prop' && option === '') throw new ExtendError(/EL054608/, null, [this.constructor.name, key]);
if (type === 'attr' && option === '') throw new ExtendError(/EL054609/, null, [this.constructor.name, key]);
if (type === 'css' && option === '') throw new ExtendError(/EL054610/, null, [this.constructor.name, key]);
if (['val', 'value', 'text', 'html', 'prop', 'attr', 'css'].indexOf(type) < 0) throw new ExtendError(/EL054611/, null, [this.constructor.name, key]);
__val = getDocument(type, key, option);
// if (type === 'value' || type === 'val') {
// __val = jquery(key).val();
// } else if (type === 'text') {
// __val = jquery(key).text();
// } else if (type === 'html') {
// __val = jquery(key).html();
// } else if (type.indexOf('prop') > -1) {
// if (option === '') throw new ExtendError(/EL054608/, null, [this.constructor.name, key]);
// else __val = jquery(key).prop(option);
// } else if (type.indexOf('attr') > -1) {
// if (option === '') throw new ExtendError(/EL054609/, null, [this.constructor.name, key]);
// else __val = jquery(key).attr(option);
// } else if (type.indexOf('css') > -1) {
// if (option === '') throw new ExtendError(/EL054610/, null, [this.constructor.name, key]);
// else __val = jquery(key).css(option);
// } else {
// throw new ExtendError(/EL054611/, null, [this.constructor.name]);
// }
// selector 검사
if (typeof __val === 'undefined' || __val === null) {
console.warn('selector key = '+ key +', type = '+ type +'에 일치하는 값이 없습니다. ');
}
// 검사 및 이벤트 발생
if (this.__sValue !== null && this.__sValue !== __val && __val) {
this._onChanged(__val, this.__sValue);
this.__sValue = String(__val); // sValue 저장
}
}
}
// 필터 적용 : get
if (typeof this.getFilter === 'function') __val = this.getFilter.call(this, __val);
// 우선순위 : 3
} else {
__val = this.$value;
}
/**
* 분기 처리값 '__val' 없는경우 (null, undefined)
* - this.$value 초기화 되지 않은 경우
* - getter 리턴이 없는 경우
* - node selector 를 사용한 경우
* - selector 매칭값이 없는 경우
*/
if (typeof __val === 'undefined' || __val === null) {
// __val = this.$value || this.default; REVIEW: 제거대상
// __val = this.$value;
__val = this.$value === null ? this.default : this.$value;
}
// Get값과 내부값이 다를경우 값 설정 (내부적으로 change 이벤트 발생함)
// if (__val !== this.$value) {
// this.value = __val;
// }
return __val;
},
set: function(val) {
var __val, _fVal;
var key, type, option;
var _oldVal = this.$value;
// var _isSetFilter = true; // selector 설정 여부
// if (typeof this.setter === 'function' ) _val = this.setter.call(this, val);
// // settter 의 리턴이 여부
// if (typeof _val !== 'undefined') __val = _val;
// else __val = val;
if (typeof this.setter === 'function') __val = this.setter.call(this, val) || val;
else __val = val;
// __val = __val === null ? '' : __val; // null 등록 오류 처리
if (this._valueTypes.length > 0) Type.matchType([this._valueTypes], __val);
// if(['number', 'string', 'boolean'].indexOf(typeof __val) < 0) {
// throw new ExtendError(/EL054612/, null, [this.constructor.name]); // TODO: EL054612 에러 코드 제거됨
// }
this.$value = __val; // 내부에 저장
if (selector !== null || typeof this.setFilter === 'function') {
if (typeof this.setFilter === 'function') {
_fVal = this.setFilter.call(this, __val);
}
// 셀렉터 설정 값 1> 필터값, 2> __value
__val = _fVal || __val;
const isNode = typeof process !== 'undefined' && process.versions !== null && process.versions.node !== null && globalThis.isDOM !== true;
// node 에서는 강제 종료함
if (!isNode) {
// 필터 적용 : set
// if (typeof this.setFilter === 'function') {
// __val = this.setFilter.call(this, __val);
// _isSetFilter = __val ? true : false;
// }
// if (typeof this.setFilter === 'function') {
// _fVal = this.setFilter.call(this, __val);
// }
// // 셀렉터 설정 값 1> 필터값, 2> __value
// __val = _fVal || __val;
// 셀렉터 내부값 저장
this.__sValue = String(__val);
// key = this.selector.key;
// type = this.selector.type;
// option = type.indexOf('.') > -1 ? type.substr(type.indexOf('.') + 1) : '';
key = this.selector.key;
type = this.selector.type.split('.')[0].toLowerCase();
option = this.selector.type.split('.')[1] || '';
// 유효한 셀렉터 이면서, 설정할 ....
// if (type !== 'none' && type !== '' && _isSetFilter){
if (type !== 'none'){
if (type === 'prop' && option === '') throw new ExtendError(/EL054613/, null, [this.constructor.name, key]);
if (type === 'attr' && option === '') throw new ExtendError(/EL054614/, null, [this.constructor.name, key]);
if (type === 'css' && option === '') throw new ExtendError(/EL054615/, null, [this.constructor.name, key]);
if (['val', 'value', 'text', 'html', 'prop', 'attr', 'css'].indexOf(type) < 0) throw new ExtendError(/EL054616/, null, [this.constructor.name, key]);
setDocument(type, key, option, __val);
// if (type === 'value' || type === 'val') {
// jquery(key).val(__val);
// } else if (type === 'text') {
// jquery(key).text(__val);
// } else if (type === 'html') {
// jquery(key).html(__val);
// } else if (type.indexOf('prop') > -1) {
// if (option === '') throw new ExtendError(/EL054613/, null, [this.constructor.name, key]);
// else jquery(key).prop(option, __val);
// } else if (type.indexOf('attr') > -1) {
// if (option === '') throw new ExtendError(/EL054614/, null, [this.constructor.name, key]);
// else jquery(key).attr(option, __val);
// } else if (type.indexOf('css') > -1) {
// if (option === '') throw new ExtendError(/EL054615/, null, [this.constructor.name, key]);
// else jquery(key).css(option, __val);
// } else {
// throw new ExtendError(/EL054616/, null, [this.constructor.name]);
// }
}
}
}
// 검사 및 이벤트 발생 : 타입간 호환성
if (_oldVal !== __val && __val) this._onChanged(__val, _oldVal);
// // 이벤트 발생
// this._onChanged();
},
configurable: true,
enumerable: true
});
// 아이템 옵션속성 추가
if (typeof p_option === 'object' ) {
for(var prop in p_option) {
// POINT: get/setFilter 는 후처리해야함
if (p_option.hasOwnProperty(prop) &&
['domType', 'isReadOnly', 'isHide', 'element', 'selector', 'getFilter', 'setFilter'].indexOf(prop) > -1) {
this[prop] = p_option[prop];
}
}
}
// 기본값 설정
// this.default = this.default || '';
}
Util.inherits(HTMLColumn, _super);
HTMLColumn._UNION = [];
HTMLColumn._NS = 'Meta.Entity'; // namespace
HTMLColumn._PARAMS = ['columnName', '_entity']; // creator parameter // REVIEW: 통일 시켜야함
HTMLColumn._VALUE_TYPE = [null, String, Number, Boolean];
/**
* HTMLColumn 을 복제합니다.
* @param
* @returns {HTMLColumn}
*/
HTMLColumn.prototype.clone = function(p_entity) {
// var clone;
// var rObj = this.getObject();
var entity = p_entity ? p_entity : this._entity;
// var top = _super.prototype.clone.call(this);
var clone = new HTMLColumn(this.columnName, entity);
// for(var prop in top) {
// if (top.hasOwnProperty(prop)) {
// if (top[prop]) clone[prop] = top[prop];
// }
// }
if (this['default'] !== '') clone.default = this['default'];
if (this['caption'] !== '') clone.caption = this['caption'];
if (this['required']) clone.required = this['required'];
// if (this['optional']) clone.isNullPass = this['optional'];
if (this['constraints']) clone.constraints = this['constraints'];
if (this['getter']) clone.getter = this['getter'];
if (this['setter']) clone.setter = this['setter'];
if (this['$alias'] !== null) clone.$alias = this['$alias'];
if (this['$value'] !== null) clone.$value = this['$value'];
if (this['domType']) clone.domType = this['domType'];
if (this['isReadOnly']) clone.isReadOnly = this['isReadOnly'];
if (this['isHide']) clone.isHide = this['isHide'];
if (this['element']) clone.element = this['element'];
if (this['selector']) clone.selector = this['selector'];
if (this['getFilter']) clone.getFilter = this['getFilter'];
if (this['setFilter']) clone.setFilter = this['setFilter'];
// if (this.selector) clone.__selector = this.__selector.concat([]); // 배열 + 함수형
return clone;
};
/**
* 현재 객체의 guid 타입의 객체를 가져옵니다.
* - 순환참조는 $ref 값으로 대체된다.
*
* @param {number} p_vOpt 가져오기 옵션
* - opt = 0 : 참조 구조의 객체 (_guid: Yes, $ref: Yes)
* - opt = 1 : 소유 구조의 객체 (_guid: Yes, $ref: Yes)
* - opt = 2 : 소유 구조의 객체 (_guid: No, $ref: No)
* 객체 비교 : equal(a, b)
* a.getObject(2) == b.getObject(2)
* @param {object | array<object>} [p_owned] 현재 객체를 소유하는 상위 객체들
* @returns {object}
*/
HTMLColumn.prototype.getObject = function(p_vOpt, p_owned) {
var obj = _super.prototype.getObject.call(this, p_vOpt, p_owned);
// var vOpt = p_vOpt || 0;
// var owned = p_owned ? [].concat(p_owned, obj) : [].concat(obj);
if (this.domType !== null) obj['domType'] = this.domType;
if (this.isReadOnly !== false) obj['isReadOnly'] = this.isReadOnly;
if (this.isHide !== false) obj['isHide'] = this.isHide;
if (this.element !== null) obj['element'] = this.element;
if (this.selector !== null) obj['selector'] = this.selector;
if (this.getFilter !== null) obj['getFilter'] = this.getFilter;
if (this.setFilter !== null) obj['setFilter'] = this.setFilter;
// if (this.value !== null) obj['value'] = this.value; // 상위에서 설정함
return obj;
};
/**
* 현재 객체를 초기화 후, 지정한 guid 타입의 객체를 사용하여 설정합니다.
*
* @param {object} p_oGuid guid 타입의 객체
* @param {object} [p_origin] 현재 객체를 설정하는 원본 guid 객체
* 기본값은 p_oGuid 객체와 동일
*/
HTMLColumn.prototype.setObject = function(p_oGuid, p_origin) {
_super.prototype.setObject.call(this, p_oGuid, p_origin);
// var origin = p_origin ? p_origin : p_oGuid;
// var entity;
if (p_oGuid['domType']) this.domType = p_oGuid['domType'];
if (typeof p_oGuid['isReadOnly'] !== 'undefined') this.isReadOnly = p_oGuid['isReadOnly'];
if (typeof p_oGuid['isHide'] !== 'undefined') this.isHide = p_oGuid['isHide'];
if (p_oGuid['element']) this.element = p_oGuid['element'];
if (p_oGuid['selector']) this.selector = p_oGuid['selector'];
if (p_oGuid['getFilter']) this.getFilter = p_oGuid['getFilter'];
if (p_oGuid['setFilter']) this.setFilter = p_oGuid['setFilter'];
};
// TODO: 컬럼간 변환 기능
// HTMLColumn.prototype.toEntityColumn = function() {
// };
return HTMLColumn;
}(MetaColumn));
export default HTMLColumn;
export { HTMLColumn, detectElementType };