From d9d2f27ba02a15d0d82d5531dcd72d8d57dba8d1 Mon Sep 17 00:00:00 2001 From: dev01 Date: Wed, 16 Nov 2022 17:23:00 +0800 Subject: [PATCH] =?UTF-8?q?[Frontend]=E7=B3=BB=E7=B5=B1=E7=9B=A3=E6=8E=A7?= =?UTF-8?q?=20tooltip=20show=20=E5=8F=96=E5=BE=97=E5=8E=9Felement=20?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E8=A3=9C=E4=B8=8A=20|=20=E9=83=A8=E5=88=86?= =?UTF-8?q?=E5=8A=9F=E8=83=BD=E8=A8=BB=E8=A7=A3=E8=A3=9C=E4=B8=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Frontend/_sysMonAll.html | 5 +- Frontend/_sysMonFloor.html | 394 +-- Frontend/js/site.js | 26 +- Frontend/js/yourteam/plugins/yt-tab/yt-tab.js | 26 +- .../yourteam/plugins/yt-tooltip/yt-tooltip.js | 2 +- .../ApiControllers/DeviceManageController.cs | 2 +- FrontendWebApi/Models/Device.cs | 1 + .../26121072-5f50-4638-bee9-0ff8c9319350.svg | 3016 +++++++++++++++++ 8 files changed, 3255 insertions(+), 217 deletions(-) create mode 100644 FrontendWebApi/wwwroot/upload/26121072-5f50-4638-bee9-0ff8c9319350.svg diff --git a/Frontend/_sysMonAll.html b/Frontend/_sysMonAll.html index 305e282..1f537fb 100644 --- a/Frontend/_sysMonAll.html +++ b/Frontend/_sysMonAll.html @@ -72,7 +72,7 @@ strHtml += `` $.each(floObj.device_list, (index2, devObj) => { - strHtml += `
+ strHtml += `
... ${devObj.full_name} @@ -235,7 +235,8 @@
`, group:"device", - onShow: function () { + onShow: function (tooltipEle, oriEle) { + console.log($(oriEle).data("number")) var tab = new YT.Tab({ tabName: "cardTab" }) loadOpeRecTable(); } diff --git a/Frontend/_sysMonFloor.html b/Frontend/_sysMonFloor.html index a1f9ae3..d88f236 100644 --- a/Frontend/_sysMonFloor.html +++ b/Frontend/_sysMonFloor.html @@ -29,215 +29,223 @@ function chartHandler() { floChart.clear(); - $.get(`${baseImgUrl}/upload/floor_map/c0de2199-e62b-4f82-b7f7-abacd4e1cd17.svg`, function (svg) { + /*let url = `${baseApiUrl}/upload/graph_manage/de08e7ee-9839-4403-90e5-754dec4e389b.jpg`*/ + let url = `${baseImgUrl}/upload/floor_map/2e5be49c-f8e8-4977-b5b6-db4d95ff9691.svg` - if (svg == undefined || svg == null) { - return; - } + download(url) - echarts.registerMap('floor_svg', { svg: svg }); + //ytAjax = new YourTeam.Ajax(url, null, function (res) { + // if (svg == undefined || svg == null) { + // return; + // } - option = { - // animationDurationUpdate: 1500, - tooltip: { - formatter: function (params) { - if (params.data.device_node_guid != undefined && params.data.device_node_guid != null && params.data.device_node_guid != "") { - return `名稱:${params.data.device_node_full_name}
- Guid:${params.data.device_node_guid}` - } - else { - return `名稱:${params.data.device_full_name}
- Guid:${params.data.device_guid}` - } - } - }, - toolbox: { //工具欄 - show: false - }, - geo: { - map: 'floor_svg', - roam: true, - scaleLimit: { //限制放大縮小倍數 - max: 32, - min: 2.5 - }, - layoutSize: '100%', - layoutCenter: ['50%', '50%'], - zoom: 2.5, - silent: true - }, - series: [ - { //不管有無被選擇(圓點) - type: 'scatter', - coordinateSystem: 'geo', - geoIndex: 0, - symbol: 'circle', - symbolSize: 10, - symbolOffset: [10, 10], - label: { - show: false - }, - data: currentData, - z: 2 - }, - { //未選擇的設備(icon) - type: 'scatter', - coordinateSystem: 'geo', - geoIndex: 0, - symbolSize: 30, - label: { - formatter: '{b}', - position: 'bottom', - show: true, - backgroundColor: 'orange' - }, - emphasis: { - label: { - show: true, - fontSize: '20', - fontWeight: 'bold', - color: 'yellow' - } - }, - selectedMode: 'single', - data: null, - z: 1 - }, - { //被選擇的設備(icon) - type: 'effectScatter', - coordinateSystem: 'geo', - geoIndex: 0, - showEffectOn: 'render', - symbolSize: 30, - label: { - formatter: '{b}', - position: 'bottom', - show: true, - backgroundColor: 'orange' - }, - emphasis: { - label: { - show: true, - fontSize: '20', - fontWeight: 'bold', - color: 'yellow' - } - }, - selectedMode: 'single', - data: null, - z: 1 - }, - { //編輯模式底下的設備(圓點,只會有一個) - type: 'scatter', - coordinateSystem: 'geo', - geoIndex: 0, - symbol: 'circle', - symbolSize: 10, - symbolOffset: [10, 10], - label: { - show: false - }, - data: currentData, - z: 2 - }, - { //編輯模式底下的設備(icon,只會有一個) - type: 'scatter', - coordinateSystem: 'geo', - geoIndex: 0, - symbolSize: 30, - label: { - formatter: '{b}', - position: 'bottom', - show: true, - backgroundColor: 'orange' - }, - emphasis: { - label: { - show: true, - fontSize: '20', - fontWeight: 'bold', - color: 'yellow' - } - }, - data: null, - z: 1 - }, - ], - }; + // echarts.registerMap('floor_svg', { svg: svg }); - floChart.setOption(option); + // option = { + // // animationDurationUpdate: 1500, + // tooltip: { + // formatter: function (params) { + // if (params.data.device_node_guid != undefined && params.data.device_node_guid != null && params.data.device_node_guid != "") { + // return `名稱:${params.data.device_node_full_name}
+ // Guid:${params.data.device_node_guid}` + // } + // else { + // return `名稱:${params.data.device_full_name}
+ // Guid:${params.data.device_guid}` + // } + // } + // }, + // toolbox: { //工具欄 + // show: false + // }, + // geo: { + // map: 'floor_svg', + // roam: true, + // scaleLimit: { //限制放大縮小倍數 + // max: 32, + // min: 2.5 + // }, + // layoutSize: '100%', + // layoutCenter: ['50%', '50%'], + // zoom: 2.5, + // silent: true + // }, + // series: [ + // { //不管有無被選擇(圓點) + // type: 'scatter', + // coordinateSystem: 'geo', + // geoIndex: 0, + // symbol: 'circle', + // symbolSize: 10, + // symbolOffset: [10, 10], + // label: { + // show: false + // }, + // data: currentData, + // z: 2 + // }, + // { //未選擇的設備(icon) + // type: 'scatter', + // coordinateSystem: 'geo', + // geoIndex: 0, + // symbolSize: 30, + // label: { + // formatter: '{b}', + // position: 'bottom', + // show: true, + // backgroundColor: 'orange' + // }, + // emphasis: { + // label: { + // show: true, + // fontSize: '20', + // fontWeight: 'bold', + // color: 'yellow' + // } + // }, + // selectedMode: 'single', + // data: null, + // z: 1 + // }, + // { //被選擇的設備(icon) + // type: 'effectScatter', + // coordinateSystem: 'geo', + // geoIndex: 0, + // showEffectOn: 'render', + // symbolSize: 30, + // label: { + // formatter: '{b}', + // position: 'bottom', + // show: true, + // backgroundColor: 'orange' + // }, + // emphasis: { + // label: { + // show: true, + // fontSize: '20', + // fontWeight: 'bold', + // color: 'yellow' + // } + // }, + // selectedMode: 'single', + // data: null, + // z: 1 + // }, + // { //編輯模式底下的設備(圓點,只會有一個) + // type: 'scatter', + // coordinateSystem: 'geo', + // geoIndex: 0, + // symbol: 'circle', + // symbolSize: 10, + // symbolOffset: [10, 10], + // label: { + // show: false + // }, + // data: currentData, + // z: 2 + // }, + // { //編輯模式底下的設備(icon,只會有一個) + // type: 'scatter', + // coordinateSystem: 'geo', + // geoIndex: 0, + // symbolSize: 30, + // label: { + // formatter: '{b}', + // position: 'bottom', + // show: true, + // backgroundColor: 'orange' + // }, + // emphasis: { + // label: { + // show: true, + // fontSize: '20', + // fontWeight: 'bold', + // color: 'yellow' + // } + // }, + // data: null, + // z: 1 + // }, + // ], + // }; - floChart.getZr().on('click', function (params) { - console.log("click", params); - var pixelPoint = [params.offsetX, params.offsetY]; - var dataPoint = floChart.convertFromPixel({ geoIndex: 0 }, pixelPoint); - console.log(dataPoint); + // floChart.setOption(option); - if (floor_map_mode == "edit") { + // floChart.getZr().on('click', function (params) { + // console.log("click", params); + // var pixelPoint = [params.offsetX, params.offsetY]; + // var dataPoint = floChart.convertFromPixel({ geoIndex: 0 }, pixelPoint); + // console.log(dataPoint); - temp_device_on_floor_map = [{ - device_guid: selected_temp_device.device_guid, - device_full_name: selected_temp_device.device_full_name, - device_node_guid: selected_temp_device.device_node_guid ? selected_temp_device.device_node_guid : null, - device_node_full_name: selected_temp_device.device_node_full_name ? selected_temp_device.device_node_full_name : null, - status: selected_temp_device.status, - value: dataPoint - }]; + // if (floor_map_mode == "edit") { - - } + // temp_device_on_floor_map = [{ + // device_guid: selected_temp_device.device_guid, + // device_full_name: selected_temp_device.device_full_name, + // device_node_guid: selected_temp_device.device_node_guid ? selected_temp_device.device_node_guid : null, + // device_node_full_name: selected_temp_device.device_node_full_name ? selected_temp_device.device_node_full_name : null, + // status: selected_temp_device.status, + // value: dataPoint + // }]; - // currentData.push([dataPoint[0], dataPoint[1], 1]); - // floChart.setOption(option); - }); + // } - floChart.on('selectchanged', function (params) { - - console.log("selectchanged", params); - // currentData.push([dataPoint[0], dataPoint[1], 1]); - // floChart.setOption(option); - currentData = $.map(currentData, function (item) { - item.selected = false; - return item; - }); + // // currentData.push([dataPoint[0], dataPoint[1], 1]); + // // floChart.setOption(option); + // }); - if (params.selected.length > 0) { - currentData[params.selected[0].seriesIndex - 1].selected = true; - } + // floChart.on('selectchanged', function (params) { - - }); + // console.log("selectchanged", params); + // // currentData.push([dataPoint[0], dataPoint[1], 1]); + // // floChart.setOption(option); - // floChart.getZr().on('mousewheel', function (params) { - // console.log(params) - // }) - floChart.on('georoam', function (params) { - - var zoom = floChart.getOption().geo[0].zoom; + // currentData = $.map(currentData, function (item) { + // item.selected = false; + // return item; + // }); - if (zoom <= 2.5) { - ResetFloorMap(); - floChart.setOption({ - geo: { - roam: 'scale' - }, - }); - } else { - floChart.setOption({ - geo: { - roam: true - }, - }); - } + // if (params.selected.length > 0) { + // currentData[params.selected[0].seriesIndex - 1].selected = true; + // } - - }); - }) - .fail(function () { - toast_warning("查無該樓層地圖") - floChart.clear(); - }); + + // }); + + // // floChart.getZr().on('mousewheel', function (params) { + // // console.log(params) + // // }) + // floChart.on('georoam', function (params) { + + // var zoom = floChart.getOption().geo[0].zoom; + + // if (zoom <= 2.5) { + // ResetFloorMap(); + // floChart.setOption({ + // geo: { + // roam: 'scale' + // }, + // }); + // } else { + // floChart.setOption({ + // geo: { + // roam: true + // }, + // }); + // } + + + // }); + //}).send(); + //$.get(`${baseImgUrl}/device/GetFloorSvg/c0de2199-e62b-4f82-b7f7-abacd4e1cd17.svg`, function (svg) { + + + //}) + // .fail(function () { + // toast_warning("查無該樓層地圖") + // floChart.clear(); + // }); } \ No newline at end of file diff --git a/Frontend/js/site.js b/Frontend/js/site.js index 1d4e1bf..3aecd08 100644 --- a/Frontend/js/site.js +++ b/Frontend/js/site.js @@ -5,13 +5,18 @@ }) - +/** + * fn 定義 | 手動初始化 Bootstrap dropdown select + */ $.fn.droSetItem = function () { setDropdownItem(this); return this; } - +/** + * 設置 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"); @@ -21,14 +26,19 @@ function setDropdownItem(menuEle) { let actEleId = $(menuEle).prop("id"); $(`.dropdown-toggle[data-target=${actEleId}]`).text(actText); $(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")); + }) } -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; diff --git a/Frontend/js/yourteam/plugins/yt-tab/yt-tab.js b/Frontend/js/yourteam/plugins/yt-tab/yt-tab.js index c8a1982..0e50fa8 100644 --- a/Frontend/js/yourteam/plugins/yt-tab/yt-tab.js +++ b/Frontend/js/yourteam/plugins/yt-tab/yt-tab.js @@ -10,11 +10,14 @@ * */ var YT = YT || {}; -var _ytTabInited = [] -$(function () { +var _ytTabInited = []; +$(function () { }) +/** + * 初始全頁面 yt tab + * */ function initTabsByEle() { $("[data-tabname][data-target]:not([data-tabrole=child])").each(function (index, value) { let tabName = $(value).data("tabname"); @@ -38,25 +41,24 @@ class YourTeamTab { } this.event(); $(`[data-tabname=${this.tabName}][data-tabrole=child]`).css("display", "none"); - $(`[data-tabname=${this.tabName}][data-target]`).first().trigger("click","fromInit"); + $(`[data-tabname=${this.tabName}][data-target]`).first().trigger("click"); _ytTabInited.push(this.tabName); } event = function () { let clsObj = this; - // custom tab - // example : - // - //
- $(`[data-tabname=${this.tabName}][data-target]`).off("click").on("click", function (e, arg) { - console.log(e) + + // Tab Item 按鈕 click + $(`[data-tabname=${this.tabName}][data-target]`).off("click").on("click", function (e) { let target = $(this).data("target"); let obj = this; - $(`[data-tabname=${clsObj.tabName}][data-target]:not([data-tabrole=child])`).removeClass("active") - $(obj).addClass("active") - $(obj).trigger("yt:tab:change") + $(`[data-tabname=${clsObj.tabName}][data-target]:not([data-tabrole=child])`).removeClass("active"); + $(obj).addClass("active"); + // 觸發 yt:tab:change事件 + $(obj).trigger("yt:tab:change"); let tabName = $(target).data("tabname"); if (tabName) { + // 找出該觸發對象 block if ($(target).data("tabrole") == "child") { $(obj).trigger("yt:tab:show"); $(`[data-tabname='${tabName}'][data-tabrole='child']`).css("opacity", 0).hide(); diff --git a/Frontend/js/yourteam/plugins/yt-tooltip/yt-tooltip.js b/Frontend/js/yourteam/plugins/yt-tooltip/yt-tooltip.js index a1071d5..5396020 100644 --- a/Frontend/js/yourteam/plugins/yt-tooltip/yt-tooltip.js +++ b/Frontend/js/yourteam/plugins/yt-tooltip/yt-tooltip.js @@ -71,7 +71,7 @@ $.fn.YTTooltip = function (option) { obj.tooltipDiv = clone; //顯示 tooltip $(clone).css({ "display": display, "position": "absolute" }); - obj.onShow ? obj.onShow(clone,obj) : ""; + obj.onShow ? obj.onShow(clone,obj.ele,obj) : ""; //tooltip 高寬 let toolWidth = $(clone)[0].offsetWidth; let toolHeight = $(clone)[0].offsetHeight; diff --git a/FrontendWebApi/ApiControllers/DeviceManageController.cs b/FrontendWebApi/ApiControllers/DeviceManageController.cs index 210bdee..5a586fd 100644 --- a/FrontendWebApi/ApiControllers/DeviceManageController.cs +++ b/FrontendWebApi/ApiControllers/DeviceManageController.cs @@ -182,7 +182,7 @@ namespace FrontendWebApi.ApiControllers foreach (var f in fl) { List dl = new List(); - sqlString = $@"select d.device_guid, d.full_name, d.status, d.device_coordinate, dm.device_master_icon + sqlString = $@"select d.device_guid, d.full_name, d.status, d.device_coordinate, dm.device_master_icon,d.device_number from device d left join device_master dm on d.device_building_tag = dm.device_building_tag and d.device_name_tag = dm.device_name_tag where d.deleted = 0 and d.device_system_tag = @main_system_tag and d.device_building_tag = @building_tag and d.device_floor_tag = ifnull(@floor_tag, d.device_floor_tag)"; diff --git a/FrontendWebApi/Models/Device.cs b/FrontendWebApi/Models/Device.cs index 17c17f4..71cf0a7 100644 --- a/FrontendWebApi/Models/Device.cs +++ b/FrontendWebApi/Models/Device.cs @@ -30,6 +30,7 @@ namespace FrontendWebApi.Models public class DeviceLists { public string device_guid { get; set; } + public string device_number { get; set; } public string full_name { get; set; } public string device_coordinate { get; set; } public string device_coordinate_3d { get; set; } diff --git a/FrontendWebApi/wwwroot/upload/26121072-5f50-4638-bee9-0ff8c9319350.svg b/FrontendWebApi/wwwroot/upload/26121072-5f50-4638-bee9-0ff8c9319350.svg new file mode 100644 index 0000000..38360db --- /dev/null +++ b/FrontendWebApi/wwwroot/upload/26121072-5f50-4638-bee9-0ff8c9319350.svg @@ -0,0 +1,3016 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +