base.js 경로 수정

main
이범준 5 months ago
parent 22645ee5af
commit c27bd4d7ae

@ -1,6 +1,6 @@
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isELIgnored="false" session="false"%>
<%@ include file="/WEB-INF/jsp/include/taglib.jsp"%>
<div class="spinner-border spinner-border-lg text-primary wait" hidden role="status">
<div class="spinner-border spinner-border-lg text-primary wait" style="display:none;" role="status">
<span class="visually-hidden">Loading...</span>
</div>
@ -20,7 +20,7 @@
<script src="<c:url value='/webjars/3rd-party/sneat/libs/jquery-sticky/jquery-sticky.js' />"></script>
<script src="<c:url value="/webjars/3rd-party/sneat/libs/bootstrap-datepicker/bootstrap-datepicker.js?${ver}"/>"></script>
<!-- base -->
<script src="<c:url value="/resources/js/base/base.js?${ver}"/>"></script>
<script src="<c:url value="/webjars/js/base/base.js?${ver}"/>"></script>
<script src="<c:url value="/webjars/js/base/dataset.js?${ver}"/>"></script>
<script src="<c:url value="/resources/js/base/upload-support.js?${ver}"/>"></script>
<script src="<c:url value="/webjars/js/base/code.js?${ver}"/>"></script>

@ -107,7 +107,7 @@
}
.hidden {
display:none !important;
display:none;
}
[hidden] {

@ -1,867 +0,0 @@
var wctx = {
path:"",
url:function(path) {
return this.path + path;
},
home:function() {
top.location.href = wctx.url("/");
},
current:function() {
let str = top.location.href.replace(top.location.origin + wctx.path, "");
let pos = str.indexOf("?");
return pos < 0 ? str : str.substr(0, pos);
},
trace:true
};
function log(msg) {
console.log.apply(console, arguments);
}
function debug(msg) {
if (wctx && wctx.trace)
console.log.apply(console, arguments);
}
function trim(s) {
if (null == s || undefined == s) return "";
if ("string" != typeof(s)) return s;
return s.replace(/^\s+/, "").replace(/\s+$/, "");
}
function isEmpty(v) {
if (v == undefined
|| v == "undefined"
|| v == null
|| v == "null"
) return true;
switch (typeof(v)) {
case "string": return "" == trim(v);
case "boolean": if (false == v) return false;
case "number": if (0 == v) return false;
default: return false;
}
}
function ifEmpty(v, nv) {
if (!isEmpty(v)) return v;
if (typeof(nv) == "function")
return nv.apply();
else
return nv;
}
function notEmpty(obj, msg) {
if (isEmpty(obj))
throw msg;
return obj;
}
function toQuery(map, encode) {
if (isEmpty(map)) return "";
var query = [];
for (var key in map) {
var v = map[key];
if (v != null && v == undefined) continue;
if (v != null)
switch (typeof(v)) {
case "object":
case "function": continue;
}
if (isEmpty(v))
query.push(key);
else
query.push(key + "=" + (encode != false ? encodeURIComponent(v) : v));
}
return query.join("&");
}
function uuid() {
var hexDigits = "0123456789abcde",
result = [];
for (var i = 0; i < 36; ++i)
result[i] = hexDigits.substr(Math.floor(Math.random() * 0x10), 1);
result[14] = "4";
result[19] = hexDigits.substr((result[19] & 0x3) | 0x8, 1);
result[8] = result[13] = result[18] = result[23] = "-";
return result.join("");
}
function wait(show) {
if (show == false)
$(".wait").attr("hidden","hidden");
else
$(".wait").removeAttr("hidden");
}
var dialog = {
title:"XIT",
template:null,
open:function(conf) {
if (this.template)
this.create(conf);
else {
var self = this;
ajax.get({
url:wctx.url("/webjars/html/dialog.html"),
success:function(resp) {
self.template = resp;
self.create(conf);
}
});
}
},
close:function(id) {
$("#close" + id).click();
},
create:function(conf) {
let last = {
dlg: $("div.modal.show").last()[0],
setZIndex: (obj, offset) => {
if (!last.dlg) return;
let zIndex = Number($(last.dlg).css("z-index"));
obj.css("z-index", zIndex + offset);
}
};
conf = conf || {};
let id = conf.id || "dlg-" + uuid(),
backdropID = id + "-backdrop",
size = conf.size || "",
tmpl = this.template
.replace(/{id}/g, id)
.replace(/{title}/g, conf.title || dialog.title);
if (size)
size = " modal-" + size;
tmpl = tmpl.replace(/{size}/, size);
dlg = $(tmpl).appendTo("body");
dlg.find(".modal-body").html(conf.content || "").fadeIn();
dlg.on("hidden.bs.modal", function() {// on dialog close
$("#" + id +",#" + backdropID).remove(); // removes the dialog and its backdrop
if (conf.onClose)
conf.onClose();
});
if (conf.onOK) {
let footer = dlg.find(".modal-footer");
footer.removeClass("hidden");
footer.show();
footer.find("button").unbind("click").click(function() {
if (conf.getData) {
var selected = conf.getData.apply();
if (!selected) return;
conf.onOK(selected);
dialog.close(id);
} else {
conf.onOK();
dialog.close(id);
}
});
} else {
if (conf.timeout)
setTimeout(function(){dialog.close(id);}, conf.timeout);
}
last.setZIndex(dlg, 10);
dlg.draggable({handle: ".modal-header"}).modal("show");
$(".modal-backdrop").each(function() { // gives id to its backdrop
let backdrop = $(this);
if (!backdrop.prop("id"))
backdrop.prop("id", backdropID);
});
if (conf.init)
conf.init();
},
alert:function(conf) {
var container = "<div class='container-fluid text-center' style='font-size:1.4em;'>{content}</div>";
if ("string" == typeof(conf)) {
conf = {
content:container.replace(/{content}/g, conf)
};
} else {
conf.content = container.replace(/{content}/g, conf.content);
}
conf.timeout = ifEmpty(conf.timeout, dialog.timeout);
this.open(conf);
}
};
function onError(xhr, options, error) {
if (xhr.readyState == 0)
return dialog.alert("서버에 접근할 수 없습니다.");
var resp = JSON.parse(xhr.responseText);
if (resp.handler)
return eval(resp.handler);
var msgs = [];
for (key in resp)
msgs.push(resp[key])
msgs = msgs.join("<br />");
if (msgs)
dialog.alert(msgs);
}
var ajax = {
request:function(options) {
options.beforeSend = function(xhr) {
if (wctx.csrf)
xhr.setRequestHeader(wctx.csrf.header, wctx.csrf.token);
if (!options.silent)
wait();
}
if (!options.type) {
if (options.data)
options.type = "POST";
}
var success = options.success;
options.success = function(resp) {
if ("string" == typeof resp)
resp = trim(resp);
var stacktrace = resp.stacktrace;
delete resp.stacktrace;
debug("response", resp);
if (!resp.failed)
return success(resp);
dialog.alert({
title:resp.title,
content:[resp.description, resp.message].join("<br />")
});
debug("stacktrace", stacktrace);
};
var handleError = options.error || onError;
options.error = function(xhr, options, error) {
wait(false);
debug("error", xhr, options, error);
handleError(xhr, options, error);
}
var handleComplete = options.complete || function(){};
options.complete = function() {
wait(false);
handleComplete();
};
debug("request", options);
$.ajax(options);
},
get:function(options) {
options.type = "GET";
ajax.request(options);
},
post:function(options) {
options.type = "POST";
ajax.request(options);
}
};
var json = {
with:function(options) {
options.dataType = "json";
if (typeof(options.data) == "string") {
options.contentType = "application/json; charset=UTF-8";
}
return options;
},
get:function(options) {
ajax.get(json.with(options));
},
post:function(options) {
ajax.post(json.with(options));
}
};
function upload(options) {
options.enctype = "multipart/form-data";
options.processData = options.contentType = false;
var data = options.data,
formData = new FormData();
for (var key in data) {
var val = data[key];
if (!Array.isArray(val))
formData.append(key, val);
else {
for (var i = 0; i < val.length; ++i)
formData.append(key, val[i]);
}
}
options.data = formData;
ajax.post(options);
}
$.fn.onEnterPress = function(handler) {
return this.each(function(){
$(this).keypress(function(evt){
if (!handler || evt.which != 13) return;
handler.apply();
});
});
}
function labelFor(input) {
let selector = "label[for='{id}']",
str = "string" == typeof(input),
obj = $(input),
key = str ? input
: obj.attr("id") || obj.attr("name");
if (!key) return "";
selector = selector.replace(/{id}/gi, key);
return $(selector).text()
|| obj.attr("title")
|| obj.attr("placeholder");
}
function validationFailureHandler() {
var handler = {
notice:function(msg, input) {
dialog.alert({
content:msg,
onClose:function(){input.focus();}
});
},
valueMissing: function(input) {
handler.notice(labelFor(input) + "를(을) 입력하십시오.", input);
},
typeMismatch: function(input) {
handler.notice(input.value + "는 " + labelFor(input) + "의 형식에 맞지 않습니다.", input);
},
tooLong: function(input) {
handler.notice(input.value + " 값이 너무 깁니다.", input);
},
patternMismatch: function(input) {
handler.notice(input.value + "는 " + labelFor(input) + "의 형식에 맞지 않습니다.", input);
},
rangeOverflow: function(input) {
handler.notice(labelFor(input) + "의 값은 " + input.max + "보다 작아야 합니다.", input);
},
rangeUnderflow: function(input) {
handler.notice(labelFor(input) + "의 값은 " + input.min + "보다 커야 합니다.", input);
},
stepMismatch: function(input) {
handler.notice(labelFor(input) + "의 값은 " + input.step + "씩 증가 또는 감소해야 합니다.", input);
}
};
return handler;
}
/**Returns the result of validation on the input object.<br />
* The types of validation checks are those defined by input objects' basic validity property, which are
* <ul><li>valueMissing</li>
* <li>typeMismatch</li>
* <li>tooLong</li>
* <li>patternMismatch</li>
* <li>rangeOverflow</li>
* <li>rangeUnderflow</li>
* <li>stepMismatch</li>
* </ul>
* To be effective, the input object must be set with attributes
* <ul><li>required</li>
* <li>type</li>
* <li>maxLength</li>
* <li>pattern</li>
* <li>max</li>
* <li>min</li>
* <li>step</li>
* </ul>
* On failure, failureHandler takes over.<br />
* The default handler from <code>validationFailureHandler()</code> displays a message
* and puts the focus back to the input object.<br />
* For the handler to display a message appropriate for the failure,
* it is recommended to have a label associated with the input object.
* @param input input object
* @param failureHandler object that handles validation failure.<br />
* If not provided, the default handler from <code>validationFailureHandler()</code> is used.
* @returns
* <ul><li>true if the value of the input object is valid</li>
* <li>false otherwise</li>
* </ul>
*/
function validInput(input, failureHandler) {
var validity = input.validity;
if (!validity || validity.valid) return true;
for (var key in validity) {
if (!validity[key]) continue;
if (!failureHandler)
failureHandler = validationFailureHandler();
var handler = failureHandler[key];
if (handler)
handler(input);
else {
log("Handler not found for validation failure of " + key);
}
break;
}
return false;
}
/**Returns the result of input validation on the selected input objects.<br />
* The types of validation checks are those defined by input objects' basic validity property, which are
* <ul><li>valueMissing</li>
* <li>typeMismatch</li>
* <li>tooLong</li>
* <li>patternMismatch</li>
* <li>rangeOverflow</li>
* <li>rangeUnderflow</li>
* <li>stepMismatch</li>
* </ul>
* To be effective, the input objects must be set with attributes
* <ul><li>required</li>
* <li>type</li>
* <li>maxLength</li>
* <li>pattern</li>
* <li>max</li>
* <li>min</li>
* <li>step</li>
* </ul>
* On failure, failureHandler takes over.<br />
* The default handler from <code>validationFailureHandler()</code> displays a message
* and puts the focus back to the offending input object.<br />
* For the handler to display a message appropriate for the failure,
* it is recommended to have labels associated with input objects.
* @param failureHandler object that handles validation failure.<br />
* If not provided, the default handler from <code>validationFailureHandler()</code> is used.
* @returns
* <ul><li>true if the input values of the selected objects are valid</li>
* <li>false otherwise</li>
* </ul>
*/
$.fn.validInputs = function(failureHandler) {
var valid = true;
this.each(function(){
var input = $(this);
if (!(valid = validInput(input[0], failureHandler))) {
return false;
}
});
return valid;
}
$.fn.getValues = function(propertyMapper) {
var inputValues = {};
this.each(function() {
var input = $(this),
key = input.prop("id") || input.prop("name"),
value = input.val();
if (isEmpty(key) || value === undefined) return;
var type = input.prop("type");
if ("file" == type) {
value = input.get(0).files;
} else if (["checkbox", "radio"].indexOf(type) > -1) {
if (!input.is(":checked")) return;
}
var stored = inputValues[key];
if (stored === undefined) {
inputValues[key] = value;
return;
}
if (Array.isArray(stored)) {
if (!Array.isArray(value))
stored.push(value);
else
stored = stored.concat(value);
} else {
var array = [stored];
if (!Array.isArray(value))
array.push(value);
else
array = array.concat(value);
inputValues[key] = array;
}
});
if (propertyMapper) { // propertyMapper = {property:valueProvider, ...}
for (var property in propertyMapper) {
var valueProvider = propertyMapper[property],
value = undefined;
switch (typeof(valueProvider)) {
case "string":
value = inputValues[valueProvider];
break;
case "function":
value = valueProvider(inputValues);
break;
}
if (value === undefined) continue;
delete inputValues[valueProvider];
inputValues[property] = value;
}
}
return inputValues;
}
/**
* @param config
* {start:0,
* fetchSize:10,
* totalSize:135,
* links:3,
* first:function(index, label){return "..."},
* previous:function(index, label){return "..."},
* link:function(index, label){return "..."},
* current:function(index, label){return "..."},
* next:function(index, label){return "..."},
* last:function(index, label){return "..."}
* }
* @returns {String}
*/
function paginate(config) {
var rc = config.totalSize || 0;
// if (!rc) return "";
var fetchCount = config.fetchSize || 0;
// if (!fetchCount) return "";
var fetch = {
all:0,
none:-1,
count:function(elementCount, size) {
if (!elementCount || size == fetch.all) return 1;
return parseInt((elementCount / size) + ((elementCount % size) == 0 ? 0 : 1));
},
end:function(elementCount, size, start) {
if (size < fetch.all) throw "Invalid size: " + size;
if (elementCount < 0) throw "Invalid elementCount: " + elementCount;
var last = elementCount - 1;
if (size == fetch.all) return last;
return Math.min(last, start + size -1);
},
page:function(current, count) {
return parseInt(count < 1 ? 0 : current / count);
},
band:function(page, visibleLinks) {
return parseInt(visibleLinks < 1 ? 0 : page / visibleLinks);
}
};
var lc = fetch.count(rc, fetchCount);
if (lc < 2) return "";
var links = ifEmpty(config.links, fetch.all),
page = fetch.page(ifEmpty(config.start, 0), fetchCount),
band = fetch.band(page, links),
tags = {
link:function(tag, index, label) {
return !tag ? "" : tag(index, label);
},
first:function() {
return band < 2 ? "" : tags.link(config.first, 0, 1);
},
previous:function() {
if (band < 1) return "";
var prevBand = band - 1,
prevPage = (prevBand * links) + (links - 1),
fromRec = prevPage * fetchCount;
return tags.link(config.previous, fromRec, prevPage + 1);
},
visibleLinks:function() {
var s = "",
fromPage = links == fetch.all ? 0 : band * links,
toPage = links == fetch.all ? lc : Math.min(lc, fromPage + links);
for (var i = fromPage; i < toPage; ++i) {
var fromRec = i * fetchCount,
label = i + 1;
s += tags.link(i == page ? config.current : config.link, fromRec, label);
}
return s;
},
next:function(bandCount) {
bandCount = parseInt(bandCount);
if (bandCount - band < 2) return "";
var nextBand = band + 1,
page = nextBand * links,
fromRec = page * fetchCount;
return tags.link(config.next, fromRec, page + 1);
},
last:function(bandCount) {
bandCount = parseInt(bandCount);
var lastBand = bandCount - 1;
if (lastBand - band < 2) return "";
var pages = lastBand * links,
fromRec = pages * fetchCount;
return tags.link(config.last, fromRec, pages + 1);
}
},
tag = "";
if (links != fetch.all) {
tag += tags.first();
tag += tags.previous();
}
tag += tags.visibleLinks();
if (links != fetch.all) {
var bandCount = parseInt(lc / links);
bandCount += lc % links == 0 ? 0 : 1;
tag += tags.next(bandCount);
tag += tags.last(bandCount);
}
return tag;
}
$.fn.paginate = function(config) {
return this.each(function(){
var tag = paginate(config),
container = $(this);
if (tag)
container.html(tag).show();
else {
if (config.hideIfEmpty != false)
container.hide();
}
});
}
$.fn.setPaging = function(config) {
config.links = 5;
config.first = function(index, label) {return '<li onclick="{func};" class="page-item first"><a class="page-link"><i class="tf-icon bx bx-chevrons-left"></i></a></li>'.replace(/{func}/, config.func.replace(/{index}/, label));};
config.previous = function(index, label) {return '<li onclick="{func}" class="page-item prev"><a class="page-link"><i class="tf-icon bx bx-chevron-left"></i></a></li>'.replace(/{func}/, config.func.replace(/{index}/, label));};
config.link = function(index, label) {return '<li onclick="{func}" class="page-item"><a class="page-link">{label}</a></li>'.replace(/{func}/, config.func.replace(/{index}/, label)).replace(/{label}/, label);};
config.current = function(index, label) {return '<li class="page-item active"><a class="page-link">{label}</a></li>'.replace(/{label}/, label);};
config.next = function(index, label) {return '<li onclick="{func}" class="page-item next"><a class="page-link"><i class="tf-icon bx bx-chevron-right"></i></a></li>'.replace(/{func}/, config.func.replace(/{index}/, label));};
config.last = function(index, label) {return '<li onclick="{func}" class="page-item last"><a class="page-link"><i class="tf-icon bx bx-chevrons-right"></i></a></li>'.replace(/{func}/, config.func.replace(/{index}/, label));};
return this.each(function(){
let list = config.list,
start = list.empty ? 0 : config.start + 1,
end = list.empty ? 0 : config.start + list.length,
pagingInfo = list.empty ? "" : start + " ~ " + numberFormat.format(end) + " / " + numberFormat.format(config.totalSize);
$("#"+ config.prefix + "PagingInfo").html(pagingInfo);
let tag = paginate(config),
container = $(this);
if (tag)
container.html(tag.replace(/{func}/g, config.func)).show();
else {
if (config.hideIfEmpty != false)
container.hide();
}
});
}
$.fn.setPagingInfo = function(config) {
return this.each(function(){
let list = config.list;
let pagingInfo = list.empty ? "" : 1
+ " ~ "
+ numberFormat.format(list.length)
+ " / " + numberFormat.format(config.totalSize);
$("#"+ config.prefix + "PagingInfo").html(pagingInfo);
});
}
$.fn.setCurrentRow = function(val) {
if (!val) return;
return this.each(function() {
var e = $(this);
e.find("tr").each(function(){
var tr = $(this),
current = val == tr.attr("data-key");
if (current)
tr.addClass("current-row");
else
tr.removeClass("current-row");
});
});
}
class FormFields {
constructor(selector) {
this.form = document.querySelector(this.selector = selector);
this.children = ["input", "select", "textarea"].map(tag => this.selector + " " + tag).join(",");
this.datamap = [];
}
set(ctrl, obj) {
let setChanged = evt => {
let input = evt.target,
name = input.getAttribute("data-map"),
val = input.value;
ctrl.setValue(name, val);
};
document.querySelectorAll(this.children).forEach(input => {
let prop = input.getAttribute("data-map")
|| input.name
|| input.id;
if (!prop) return;
input.removeEventListener("change", setChanged);
let dataItem = obj instanceof DataItem,
value = dataItem ? obj.getValue(prop) : obj[prop],
inputType = (input.type || input.tagName || "").toLowerCase();
switch (inputType) {
case "radio": input.checked = value && value == input.value; break;
case "checkbox": input.checked = value && value == input.value; break;
case "select":
for(let option of input.options) {
option.selected = option.value == value;
}
break;
default: input.value = ifEmpty(value, ""); break;
}
input.addEventListener("change", setChanged);
});
}
get() {
let obj = {};
document.querySelectorAll(this.children).forEach(input => {
let property = input.name || input.id;
let value = input.value;
if ("radio" == input.type) {
if (input.checked)
obj[property] = value;
} else {
obj[property] = value;
}
// log(property, value, "radio" == input.type ? "radio" : "", input.checked ? "checked" : "");
});
return obj;
//return Object.fromEntries(new FormData(this.form).entries());
}
};
$.fn.datePicker = function(selector) {
return this.each(function() {
$(this)
.datepicker()
.attr("maxlength", "10")
.on("input",function(e) {
if (this.value.length <= 0
|| this.value.length != this.selectionStart
)
return;
var value = this.value.replaceAll("-","");
if (value.length > 7) {
this.value = value.substring(0,4)+"-"+value.substring(4,6)+"-"+value.substring(6);
} else if (value.length > 5) {
this.value = value.substring(0,4)+"-"+value.substring(4);
}
})
.on("paste", function(e) {
var value = e.originalEvent.clipboardData.getData('text');
if (value.length == 8)
this.value = value.substring(0,4)+"-"+value.substring(4,6)+"-"+value.substring(6);
});
var calendarIcon = $(this).next("button.bx-calendar");
if (calendarIcon.length > 0) {
$(calendarIcon).on("click", function() {
$(this).prev().focus();
});
}
});
}
/**
* @param fromSource 시작값을 갖는 input의 selector
* @param toSource 종료값을 갖는 input의 selector
*/
function inputsInRange(fromSource, toSource) {
var from = $(fromSource),
to = $(toSource),
compare = function() {
var fromVal = from.val() || "",
toVal = to.val() || "",
ok = toVal >= fromVal;
if (ok) return;
if ($(this)[0] == from[0])
to.val(from.val())
else
from.val(to.val());
};
from.change(compare);
to.change(compare);
}
function ignore() {
console.log.apply(console, arguments);
}
function fileInput(conf) {
conf = conf || {};
var name = conf.name || "upload",
tag = "<input type='file' name='{name}'{multiple}{accept} style='display:none;'>"
.replace(/{name}/, name)
.replace(/{multiple}/, conf.multiple ? " multiple" : "")
.replace(/{accept}/, conf.accept ? " accept='" + conf.accept + "'" : ""),
obj = {
name:name,
input:null,
files:[],
select:function(onSelect) {
if (!obj.input) {
var input = obj.input = $(tag);
$("body").append(input);
input.change(function() {
var files = $(this).get(0).files,
length = files.length,
selected = [];
for (var i = 0; i < length; ++i) {
var file = files[i];
file.id = name + (obj.files.length);
selected.push(file);
obj.files.push(file);
}
onSelect(selected);
})
}
obj.input.click();
},
getFiles:function() {
return obj.files;
},
remove:function(id) {
if (!id) return;
var files = obj.files;
for (var i = 0; i < files.length; ++i) {
var file = files[i];
if (id == file.id) {
files.splice(i, 1);
break;
}
}
},
clear:function() {
if (obj.input)
$(obj.input).remove();
obj.input = null;
obj.files.forEach(function(file) {delete file;});
obj.files = [];
}
};
return obj;
}
Loading…
Cancel
Save