$(function () { $(".dropdown-menu.dropdown-select-menu").each((index, value) => { setDropdownItem(value) }) }) /** * fn 定義 | 手動初始化 Bootstrap dropdown select */ $.fn.droSetItem = function () { setDropdownItem(this); return this; } /** * fn 定義 | 輸出含原元素 html */ $.fn.outerHtml = function () { return $(this).prop("outerHTML"); } /** * fn 定義 | Loading 操作 * @param {any} type - close / start / exceed , exceed => 繼續執行並可切換文字 * @param {any} text - 右下角 Alert 文字 */ $.fn.Loading = function (type = "close", text) { let ele = this; let aleObj = $(this)[0]._aleObj; let showStatus = $(ele).data("loading-show-status"); function closeLoading() { $(ele).data("loading-show-status", "close"); $("body").css("overflow", "auto"); $(aleObj.ele).YTAlert().hide(); $(ele).animate({ opacity: 0 }, 300, () => { if ($(ele).data("loading-show-status") == "close") { $(ele).hide(); } }) } function showLoading() { $(ele).data("loading-show-status", "show"); $("body").css("overflow", "hidden"); let aleObj = YT.Alert.Tip(text || "讀取中,請稍後", "show"); $(ele)[0]._aleObj = aleObj; $(ele).show(); $(ele).animate({ opacity: 1 }, 300); $(aleObj.ele).YTAlert().text(text); } if (type == "close") { closeLoading(); } else if (type == "exceed") { $(aleObj.ele).YTAlert().text(text); } else if (type == "start") { showLoading(); } return $(this); } $.fn.YTAlert = function () { let th = { element: this }; th.hide = function (delay = 0) { let obj = this; setTimeout(function () { $(obj.element).fadeOut(300); alertIdArray.splice($.inArray($(obj.element).prop("id"), alertIdArray), 1); setTimeout(function () { $(obj.element).remove(); }, 1000); }, delay); } th.text = function (text) { let obj = this; let id = $(obj.element).prop("id").split("tip-alert-")[1]; $(`#alert-text-${id}`).text(text); } return th; } /** * 設置 bootstrap dropdown 為下拉選單 * @param {any} menuEle - .dropdown-menu element */ function setDropdownItem(menuEle) { if ($(menuEle).find(".dropdown-item.active").length == 0) { $(menuEle).find(".dropdown-item").first().addClass("active"); } let actText = $(menuEle).find(".dropdown-item.active").first().text(); let actEleId = $(menuEle).prop("id"); $(`.dropdown-toggle[data-target=${actEleId}]`).text(actText); /*actChaCallback($(menuEle).find(".dropdown-item.active"))*/ $(menuEle).trigger("active:change", $(menuEle).find(".dropdown-item.active")); //點選選項 add active class onEvent("click", ".dropdown-menu.dropdown-select-menu .dropdown-item", function () { $(this).parent(".dropdown-menu.dropdown-select-menu").find(".dropdown-item").removeClass("active"); $(this).addClass("active"); setDropdownItem($(this).parent(".dropdown-menu.dropdown-select-menu")); }) } /** * 預設設備圖像 * @param {any} obj */ function defDev(obj) { let defSrc = 'img/defdev.png'; obj.src = defSrc; } /** * jquery datatable - ajax send data reset * @param {any} table * @param {any} sendData */ function dtAjaxResetSendData(table, sendData) { table.context[0].ajax.data = function (d) { d = sendData; return JSON.stringify(d) } } /** * element 建造 * @param {any} text * @param {any} id * @param {any} name * @param {any} cls * @param {any} data * @param {any} attr */ function eleBuild(text = null, id = null, name = null, cls = [], data = {}, attr = {}) { cls = cls ?? [], data = data ?? {}, attr = attr ?? {}; id = id ? `id="${id}"` : ""; name = name ? `name="${name}"` : ""; cls = cls.length != 0 ? `class="${cls.join(' ')}"` : ""; data = data.length != 0 ? `${Object.keys(data).map(x => `data-${x}="${data[x]}"`).join(" ")}` : ""; attr = attr ? `${Object.keys(attr).map(x => `${x}="${attr[x]}"`).join(" ")}` : ""; let attrArr = [], attrText = ""; attrArr = [id, name, cls, data, attr]; attrText = attrArr.filter(x => x != "").join(" "); text = text === null ? "" : text; return { attrText: attrText, text: text }; } function creEle(ele, text = null, id = null, name = null, cls = [], data = {}, attr = {}) { let comp = eleBuild(text, id, name, cls, data, attr); return $(`<${ele} ${comp.attrText}>${comp.text}${ele}>`); } function creDiv(cls = [], attr = {}, id = null, name = null, data = {}, text = null) { return creEle("div", text, id, name, cls, data, attr); } function creBtn(text = null, id = null, name = null, cls = [], data = {}, attr = {}) { return creEle("button", text, id, name, cls, data, attr); } function creBtnHtml(text = null, id = null, name = null, cls = [], data = {}, attr = {}) { return creEle("button", text, id, name, cls, data, attr).prop("outerHTML"); } function creSelect(id = null, cls = [], name = null, data = {}, attr = {}, text = null) { return creEle("select", text, id, name, cls, data, attr); } function creOption(text = null, value = null, data = {}, attr = {}, cls = [], name = null, id = null) { value != null ? attr.value = value : attr; return creEle("option", text, id, name, cls, data, attr); } function creImg(ele, text = null, id = null, name = null, cls = [], data = {}, attr = {}) { let comp = eleBuild(text, id, name, cls, data, attr); return $(`<${ele} ${comp.attrText}>${comp.text}`); } function creI(cls = [], data = {}, attr = {}, id = null, name = null, text = null) { return creEle("i", text, id, name, cls, data, attr); } function creA(text = null, attr = {}, cls = [], id = null, data = {}, name = null) { return creEle("a", text, id, name, cls, data, attr); } function creSpan(text = null, cls = [], id = null, attr = {}, data = {}, name = null) { return creEle("span", text, id, name, cls, data, attr); } /** * 根據該棟建築底下的'所有'電梯執行緒物件 * */ class ElevatorHandler { constructor(ele, option = {}) { this.ele = ele; this.eleId = ""; this.eleWra = $("
"); this.speed = 0.3; this.movStatus = {}; // {id:elevator01,value:0} 0=no 1=up 2=down this.floorHeight = typeof option.fHeight == "undefined" ? 60 : option.fHeight; this.floorWidth = typeof option.fWidth == "undefined" ? 45 : option.fWidth; this.floors = typeof option.floors == "undefined" ? [{}] : option.floors; this.elevators = typeof option.elevators == "undefined" ? [{}] : option.elevators; // {id:elevator01} this.curElevFloor = typeof option.curElevFloor == "undefined" ? {} : option.curElevFloor; this.setTimeout = null; this.init(); } // 所有電梯初始化 init = function () { this.setTabWra(); this.setTabFloor(); // 若已有每個設備的所在樓層,則預設到該樓層位置 if (Object.keys(this.curElevFloor).length != 0) { $.each(Object.keys(this.curElevFloor), (idx, elevKey) => { this.setElevFloor(elevKey, this.curElevFloor[elevKey]); }) } } // 設置 wrapper setTabWra = function () { let id = $(this.ele).prop("id"); this.eleId = id; if ($(this.ele).parent(".elevator-table-wrapper").length != 0) { $(this.ele).unwrap(".elevator-table-wrapper"); } let wrapper = creDiv(["elevator-table-wrapper"], null, `${id}_wrapper`); $(this.ele).wrap(wrapper); this.eleWra = wrapper; } // 設置 table 樓層 setTabFloor = function () { let _w = this.floorWidth, _h = this.floorHeight; let thead = creEle("thead"), tbody = creEle("tbody"); let _floors = this.floors, _ele = this.ele, _elevators = this.elevators; //樓層從小到大 _floors = _floors.oSort("sort").reverse().map(x => x.name); let theadTr = creEle("tr"); for (let e = 1; e <= _elevators.length + 2; e++) { let th = creEle("th"); th.css({ "width": `${_w}px`, "height": `${_h}px`, "position": "relative" }); if (e != 1 && e != _elevators.length + 2) { let elevId = _elevators[e - 2]?.id; // 電梯方框 let span = creEle("span", null, "elevator-item-" + (elevId), null, ["elevator-item"]); let spanUp = creEle("span", null, null, null, ["elevator-item-toup"]); let spanDown = creEle("span", null, null, null, ["elevator-item-todown"]); span.css({ "width": `${_w - 3}px`, "height": `${_h - 3}px`, "top": `1.5px`, "left": "1.5px", "transition": `transform ${1 / this.speed}s cubic-bezier(0.43, 0.05, 0.62, 1) 0s` }) spanUp.css({ "width": `${_w - 3}px`, "height": `${(_h - 3) / 2}px`, "top": `1.5px`, "left": "1.5px", "transition": `transform ${1 / this.speed}s cubic-bezier(0.43, 0.05, 0.62, 1) 0s` }) spanDown.css({ "width": `${_w - 3}px`, "height": `${(_h - 3) / 2}px`, "top": `1.5px`, "left": "1.5px", "transition": `transform ${1 / this.speed}s cubic-bezier(0.43, 0.05, 0.62, 1) 0s`, "top": `${1.5 + (_h - 3) / 2}px` }) th.append(spanUp); th.append(span); th.append(spanDown); } theadTr.append(th); } thead.css("position", "absolute"); thead.append(theadTr); //樓層表格建置 tbody for (let f = 1; f <= _floors.length; f++) { let tr = creEle("tr"); for (let e = 1; e <= _elevators.length + 2; e++) { let td = creEle("td"); td.css({ "width": `${_w}px`, "height": `${_h}px` }); if (e == 1) { td.addClass("t-black") td.text(_floors[f - 1]); } else if (e == _elevators.length + 2) { } else { let div = creDiv(["d-flex", "justify-content-center", "align-items-end", "h-100"]); div.append(``) td.append(div) } tr.append(td); } tbody.append(tr); } $(_ele).append(thead); $(_ele).append(tbody); //表格外圍無框線 $(_ele).find("tbody tr").each((index, tr) => { $(tr).find("td:eq(0)").css("border-left", "0"); $(tr).find("td:eq(-1)").css("border-right", "0"); if (index == 0) { $(tr).find("td").each((index, td) => { $(td).css("border-top", "0"); }) } else if (index == $("#floorTable tbody tr").length - 1) { $(tr).find("td").each((index, td) => { $(td).css("border-bottom", "0"); }) } }) } // 設置某個電梯到某個樓層 setElevFloor = function (elevId, floId) { let curFloId = this.curElevFloor[elevId]; let curSort = this.floors.filter(x => x.id == curFloId).map(x => x.sort)[0]; let tarSort = this.floors.filter(x => x.id == floId).map(x => x.sort)[0]; let gapFloor = tarSort - curSort; let cssEle = [$(`#elevator-item-${elevId}`)[0], $(`#elevator-item-${elevId}`).prev("span.elevator-item-toup")[0], $(`#elevator-item-${elevId}`).next("span.elevator-item-todown")[0]] if (this.movStatus[elevId] != 0) { $(cssEle).css("transition", `transform ${1 / this.speed * Math.abs(gapFloor)}s cubic-bezier(0, 0, 0.62, 1) 0s`); } clearTimeout(this.setTimeout); this.setTimeout = setTimeout(() => { $(cssEle).css("transition", `transform ${1 / this.speed * Math.abs(gapFloor)}s cubic-bezier(0.43, 0.05, 0.62, 1) 0s`); this.setEleUpDownStyle(elevId); }, (1 / this.speed * Math.abs(gapFloor)) * 1000) this.setEleUpDownStyle(elevId); this.curElevFloor[elevId] = floId; $(cssEle).css("transition", `transform ${1 / this.speed * Math.abs(gapFloor)}s cubic-bezier(0.43, 0.05, 0.62, 1) 0s`) $(cssEle).css("transform", `translateY(${this.floorHeight * (this.floors.length - tarSort)}px)`); } // 設定現在電梯狀態 (往上/往下/停止) setEleMovStatus = function (elevId, status) { this.movStatus[elevId] = status; } // 電梯方框 往上或往下閃爍 setEleUpDownStyle = function (elevId) { if (this.movStatus[elevId] == 1) { $(`#elevator-item-${elevId}`).next("span.elevator-item-todown").removeClass("light-flash-c-bd") $(`#elevator-item-${elevId}`).prev("span.elevator-item-toup").addClass("light-flash-c-bd") .css("--flash-color-1", "#44ea8e").css("--flash-color-2", "rgba(255,255,255,0)"); } else if (this.movStatus[elevId] == 2) { $(`#elevator-item-${elevId}`).prev("span.elevator-item-toup").removeClass("light-flash-c-bd") $(`#elevator-item-${elevId}`).next("span.elevator-item-todown").addClass("light-flash-c-bd") .css("--flash-color-1", "#44ea8e").css("--flash-color-2", "rgba(255,255,255,0)"); } else { $(`#elevator-item-${elevId}`).prev("span.elevator-item-toup").removeClass("light-flash-c-bd"); $(`#elevator-item-${elevId}`).next("span.elevator-item-todown").removeClass("light-flash-c-bd"); } } // 設定現在某個電梯所在樓層 setCurElevFloor = function (elevId, floId) { this.curElevFloor[elevId] = floId; } // 重新繪製 redraw = function () { $(this.ele).empty(); this.setTabFloor(); if (Object.keys(this.curElevFloor).length != 0) { $.each(Object.keys(this.curElevFloor), (idx, elevKey) => { this.setElevFloor(elevKey, this.curElevFloor[elevKey]); }) } } } // 從數字週數轉為中文週數 function dayToChiDay(num) { let chiDay = ["週日", "週一", "週二", "週三", "週四", "週五", "週六"]; return chiDay[num]; } // 取得現在或前後時間,輸出單位依據 type function getTimeByType(type = null, cal = 0, unit = "d") { let now = new Date(); return strToDate(now, type, cal, unit); } // 取得某當下時間或前後時間,輸出單位依據 type function strToDate(text, type = null, cal = 0, unit = "d") { let dec = 24 * 60 * 60 * 1000; if (unit == "d") { dec = 24 * 60 * 60 * 1000; } else if (unit == "h") { dec = 60 * 60 * 1000; } else if (unit == "m") { dec = 60 * 1000; } else if (unit == "s") { dec = 1000; } let calDay = function (_d, _cal) { return new Date(_d.getTime() + (cal * dec)); } let tarDate = new Date(text); let result = 0; if (type == "year") { result = calDay(tarDate, cal).getFullYear(); } else if (type == "month") { result = calDay(tarDate, cal).getMonth(); } else if (type == "date") { result = calDay(tarDate, cal).getDate(); } else if (type == "day") { result = calDay(tarDate, cal).getDay(); } else { result = calDay(tarDate, cal); } return result; } // 將只有時間(Hms) 格式轉換為當日的時間 function fullTime(time) { let nowDate = displayDate(new Date(), "date"); return new Date(nowDate + " " + time); } function dateRanCutPart(start, end, cutNum) { let sTime = (new Date(start)).getTime(); let eTime = (new Date(end)).getTime(); let cutTimeArr = []; if (sTime < eTime) return []; let partNum = (sTime - eTime) / cutNum; for (let i = eTime; i <= sTime; i = i + partNum) { cutTimeArr.push(i) } cutTimeArr.forEach(x => new Date(x)); return cutTimeArr; } function isJSON(str) { try { JSON.parse(str); return true; } catch { return false; } } function addBsToast(container, type = "warning",title="",content ="", id, datas = {}, option = {}) { datas = Object.keys(datas).length != 0 ? `${Object.keys(datas).map(x => `data-${x}="${datas[x]}"`).join(" ")}` : ""; let iconClass = option.iconHtml ?? `fas fa-exclamation-triangle`; let strHtml = `