├── .idea ├── .name ├── vcs.xml ├── modules.xml ├── serialport.iml ├── misc.xml └── workspace.xml ├── 说明.txt ├── Ensaving.bat ├── css └── serialport.css ├── 2015-10-09_114116.png ├── image ├── serial_run.png ├── serial_stop.png ├── server_stop.png ├── ensaving_logo.png └── bitbug_favicon.ico ├── serialport ├── Parse │ ├── diff_A188.txt │ ├── maituo_A188.json │ ├── huizhong_A188.json │ ├── weian_A188.json │ ├── sanlong_A188.json │ ├── ruina_A188.json │ ├── tiangang_A188.json │ ├── haiweici_A188.json │ ├── haina_A188.json │ └── dataParse.js ├── fail_record_templet.xlsx ├── public │ ├── css │ │ └── serialport.css │ ├── image │ │ ├── serial_run.png │ │ ├── ensaving_logo.png │ │ ├── serial_stop.png │ │ └── bitbug_favicon.ico │ └── js │ │ ├── npm.js │ │ └── bootstrap.min.js ├── excel_test.js ├── node-serialport.js └── command_1.1.html ├── js ├── npm.js └── bootstrap.min.js ├── README.MD └── logs └── accessTransfer.log /.idea/.name: -------------------------------------------------------------------------------- 1 | serialport -------------------------------------------------------------------------------- /说明.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xianyanbu/serialport/HEAD/说明.txt -------------------------------------------------------------------------------- /Ensaving.bat: -------------------------------------------------------------------------------- 1 | @echo off 2 | cd %~dp0\serialport 3 | node node-serialport.js -------------------------------------------------------------------------------- /css/serialport.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xianyanbu/serialport/HEAD/css/serialport.css -------------------------------------------------------------------------------- /2015-10-09_114116.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xianyanbu/serialport/HEAD/2015-10-09_114116.png -------------------------------------------------------------------------------- /image/serial_run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xianyanbu/serialport/HEAD/image/serial_run.png -------------------------------------------------------------------------------- /image/serial_stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xianyanbu/serialport/HEAD/image/serial_stop.png -------------------------------------------------------------------------------- /image/server_stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xianyanbu/serialport/HEAD/image/server_stop.png -------------------------------------------------------------------------------- /image/ensaving_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xianyanbu/serialport/HEAD/image/ensaving_logo.png -------------------------------------------------------------------------------- /image/bitbug_favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xianyanbu/serialport/HEAD/image/bitbug_favicon.ico -------------------------------------------------------------------------------- /serialport/Parse/diff_A188.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xianyanbu/serialport/HEAD/serialport/Parse/diff_A188.txt -------------------------------------------------------------------------------- /serialport/fail_record_templet.xlsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xianyanbu/serialport/HEAD/serialport/fail_record_templet.xlsx -------------------------------------------------------------------------------- /serialport/public/css/serialport.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xianyanbu/serialport/HEAD/serialport/public/css/serialport.css -------------------------------------------------------------------------------- /serialport/public/image/serial_run.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xianyanbu/serialport/HEAD/serialport/public/image/serial_run.png -------------------------------------------------------------------------------- /serialport/public/image/ensaving_logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xianyanbu/serialport/HEAD/serialport/public/image/ensaving_logo.png -------------------------------------------------------------------------------- /serialport/public/image/serial_stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xianyanbu/serialport/HEAD/serialport/public/image/serial_stop.png -------------------------------------------------------------------------------- /serialport/public/image/bitbug_favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xianyanbu/serialport/HEAD/serialport/public/image/bitbug_favicon.ico -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /.idea/serialport.iml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /serialport/excel_test.js: -------------------------------------------------------------------------------- 1 | var ejsExcel = require("ejsexcel"); 2 | var fs = require("fs"); 3 | //获得Excel模板的buffer对象 4 | var exlBuf = fs.readFileSync("./fail_record.xlsx"); 5 | 6 | //数据源 7 | var data = [[],[{"meterid":"pt1","meterpr":"des1","date":new Date().toLocaleString()},{"meterid":"pt1","meterpr":"des1","date":new Date().toLocaleString()}]]; 8 | 9 | //用数据源(对象)data渲染Excel模板 10 | ejsExcel.renderExcelCb(exlBuf, data, function(exlBuf2){ 11 | fs.writeFileSync("./test2.xlsx", exlBuf2); 12 | console.log("生成test2.xlsx"); 13 | }); 14 | 15 | -------------------------------------------------------------------------------- /serialport/public/js/npm.js: -------------------------------------------------------------------------------- 1 | // This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment. 2 | require('../../js/transition.js') 3 | require('../../js/alert.js') 4 | require('../../js/button.js') 5 | require('../../js/carousel.js') 6 | require('../../js/collapse.js') 7 | require('../../js/dropdown.js') 8 | require('../../js/modal.js') 9 | require('../../js/tooltip.js') 10 | require('../../js/popover.js') 11 | require('../../js/scrollspy.js') 12 | require('../../js/tab.js') 13 | require('../../js/affix.js') -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /serialport/Parse/maituo_A188.json: -------------------------------------------------------------------------------- 1 | { 2 | "ctl_info":{ 3 | "meter_general_info": { 4 | "leading_str": "FEFEFEFE68", 5 | "iden_str": "1F90", 6 | "end_bit" : "16", 7 | "brand_str": "maituo", 8 | "meter_code_len":8, 9 | "meter_code_reverse":"false", 10 | "meter_code_order" :"not_normal" 11 | }, 12 | "meter_read_protocol": { 13 | "data_structure": [ 14 | { 15 | "meter_type":1 16 | }, 17 | { 18 | "meter_code":7 19 | }, 20 | { 21 | "ctl_str":1 22 | }, 23 | { 24 | "ctr_data":1 25 | }, 26 | { 27 | "iden_str":3 28 | }, 29 | { 30 | "settlement_heat":4 31 | }, 32 | { 33 | "settlement_heat_unit":1 34 | }, 35 | { 36 | "heat":4 37 | }, 38 | { 39 | "heat_unit":1 40 | }, 41 | { 42 | "heat_power":4 43 | }, 44 | { 45 | "heatpower_unit":1 46 | }, 47 | { 48 | "flow":4 49 | }, 50 | { 51 | "flow_unit":1 52 | }, 53 | { 54 | "accumflow":4 55 | }, 56 | { 57 | "accumflow_unit":1 58 | }, 59 | { 60 | "in_temp":3 61 | }, 62 | { 63 | "out_temp":3 64 | }, 65 | { 66 | "work_time":3 67 | }, 68 | { 69 | "meter_time":7 70 | }, 71 | { 72 | "status":1 73 | }, 74 | { 75 | "parity_bit" : 1 76 | }, 77 | { 78 | "end_bit" :1 79 | } 80 | ], 81 | "data_precision": { 82 | "settlement_heat": 1, 83 | "heat": 1, 84 | "heat_power": 1, 85 | "flow": 1, 86 | "accumflow": 1, 87 | "in_temp": 0.01, 88 | "out_temp": 0.01, 89 | "work_time": 1 90 | }, 91 | "data_status_bits": { 92 | "byte1": { 93 | "1": "短路", 94 | "2": "短路", 95 | "4": "无水", 96 | "8": "设备故障", 97 | "16": "过流", 98 | "32": "低压" 99 | } 100 | }, 101 | "unit_dict": { 102 | "02": "Wh", 103 | "05": "kWh", 104 | "08": "MWh", 105 | "0A": "MWh*100", 106 | "01": "J", 107 | "0B": "KJ", 108 | "0E": "MJ", 109 | "11": "GJ", 110 | "13": "GJ*100", 111 | "15": "10W", 112 | "17": "kW", 113 | "1A": "MW", 114 | "29": "L", 115 | "2A": "10L", 116 | "2C": "m³", 117 | "32": "L/h", 118 | "35": "m³/h" 119 | } 120 | } 121 | } 122 | 123 | } -------------------------------------------------------------------------------- /serialport/Parse/huizhong_A188.json: -------------------------------------------------------------------------------- 1 | { 2 | "ctl_info":{ 3 | "meter_general_info": { 4 | "leading_str": "FEFEFEFE68", 5 | "iden_str": "1F90", 6 | "end_bit" : "16", 7 | "brand_str": "huizhong", 8 | "meter_code_len":9, 9 | "meter_code_reverse":"false", 10 | "meter_code_order" :"normal" 11 | }, 12 | "meter_read_protocol": { 13 | "data_structure": [ 14 | { 15 | "meter_type":1 16 | }, 17 | { 18 | "meter_code":7 19 | }, 20 | { 21 | "ctl_str":1 22 | }, 23 | { 24 | "ctr_data":1 25 | }, 26 | { 27 | "iden_str":3 28 | }, 29 | { 30 | "settlement_heat":4 31 | }, 32 | { 33 | "settlement_heat_unit":1 34 | }, 35 | { 36 | "heat":4 37 | }, 38 | { 39 | "heat_unit":1 40 | }, 41 | { 42 | "heat_power":4 43 | }, 44 | { 45 | "heatpower_unit":1 46 | }, 47 | { 48 | "flow":4 49 | }, 50 | { 51 | "flow_unit":1 52 | }, 53 | { 54 | "accumflow":4 55 | }, 56 | { 57 | "accumflow_unit":1 58 | }, 59 | { 60 | "in_temp":3 61 | }, 62 | { 63 | "out_temp":3 64 | }, 65 | { 66 | "work_time":3 67 | }, 68 | { 69 | "meter_time":7 70 | }, 71 | { 72 | "status":2 73 | }, 74 | { 75 | "parity_bit" : 1 76 | }, 77 | { 78 | "end_bit" :1 79 | } 80 | ], 81 | "data_precision": { 82 | "settlement_heat": 0.01, 83 | "heat": 0.01, 84 | "heat_power": 0.01, 85 | "flow": 0.0001, 86 | "accumflow": 0.01, 87 | "in_temp": 0.01, 88 | "out_temp": 0.01, 89 | "work_time": 1 90 | }, 91 | "data_status_bits": { 92 | "byte1": { 93 | "2": "电池欠压" 94 | }, 95 | "byte2": { 96 | "1": "积分仪故障", 97 | "2": "进水温度传感器故障", 98 | "4": "回水温度传感器故障", 99 | "8": "流量传感器故障" 100 | } 101 | }, 102 | "unit_dict": { 103 | "02": "Wh", 104 | "05": "kWh", 105 | "08": "MWh", 106 | "0A": "MWh*100", 107 | "01": "J", 108 | "0B": "KJ", 109 | "0E": "MJ", 110 | "11": "GJ", 111 | "13": "GJ*100", 112 | "15": "W", 113 | "17": "kW", 114 | "1A": "MW", 115 | "29": "L", 116 | "2A": "10L", 117 | "2C": "m3", 118 | "32": "L/h", 119 | "35": "m3/h" 120 | } 121 | } 122 | } 123 | 124 | } -------------------------------------------------------------------------------- /serialport/Parse/weian_A188.json: -------------------------------------------------------------------------------- 1 | { 2 | "ctl_info":{ 3 | "meter_general_info": { 4 | "leading_str": "FEFEFEFE68", 5 | "iden_str": "1F90", 6 | "end_bit" : "16", 7 | "brand_str": "ruina", 8 | "meter_code_len": 8, 9 | "meter_code_reverse":"true", 10 | "meter_code_order" :"normal" 11 | }, 12 | "meter_read_protocol": { 13 | "data_structure": [ 14 | { 15 | "meter_type":1 16 | }, 17 | { 18 | "meter_code":7 19 | }, 20 | { 21 | "ctl_str":1 22 | }, 23 | { 24 | "ctr_data":1 25 | }, 26 | { 27 | "iden_str":3 28 | }, 29 | { 30 | "settlement_heat":4 31 | }, 32 | { 33 | "settlement_heat_unit":1 34 | }, 35 | { 36 | "heat":4 37 | }, 38 | { 39 | "heat_unit":1 40 | }, 41 | { 42 | "heat_power":4 43 | }, 44 | { 45 | "heatpower_unit":1 46 | }, 47 | { 48 | "flow":4 49 | }, 50 | { 51 | "flow_unit":1 52 | }, 53 | { 54 | "accumflow":4 55 | }, 56 | { 57 | "accumflow_unit":1 58 | }, 59 | { 60 | "in_temp":3 61 | }, 62 | { 63 | "out_temp":3 64 | }, 65 | { 66 | "work_time":3 67 | }, 68 | { 69 | "meter_time":7 70 | }, 71 | { 72 | "status":2 73 | }, 74 | { 75 | "parity_bit" : 1 76 | }, 77 | { 78 | "end_bit" :1 79 | } 80 | ], 81 | "data_precision": { 82 | "settlement_heat": 0.01, 83 | "heat": 0.01, 84 | "heat_power": 0.01, 85 | "flow": 0.0001, 86 | "accumflow": 0.01, 87 | "in_temp": 0.01, 88 | "out_temp": 0.01, 89 | "work_time": 1 90 | }, 91 | "data_status_bits": { 92 | "byte1": { 93 | "4": "电池欠压" 94 | }, 95 | "byte2": { 96 | "2": "进水故障", 97 | "4": "回水故障", 98 | "64": "水倒流", 99 | "128": "无水" 100 | } 101 | }, 102 | "unit_dict": { 103 | "13": "L", 104 | "3B": "L/h", 105 | "2B": "W", 106 | "06": "kWh", 107 | "0D": "m³", 108 | "59": "度", 109 | "5D": "度", 110 | "2D": "kW", 111 | "6D": "时间" 112 | } 113 | } 114 | } 115 | 116 | } -------------------------------------------------------------------------------- /serialport/Parse/sanlong_A188.json: -------------------------------------------------------------------------------- 1 | { 2 | "ctl_info":{ 3 | "meter_general_info": { 4 | "leading_str": "FEFEFEFE68", 5 | "iden_str": "1F90", 6 | "end_bit" : "16", 7 | "brand_str": "sanlong", 8 | "meter_code_len": 8, 9 | "meter_code_reverse":"true", 10 | "meter_code_order" :"normal" 11 | }, 12 | "meter_read_protocol": { 13 | "data_structure": [ 14 | { 15 | "meter_type":1 16 | }, 17 | { 18 | "meter_code":7 19 | }, 20 | { 21 | "ctl_str":1 22 | }, 23 | { 24 | "ctr_data":1 25 | }, 26 | { 27 | "iden_str":3 28 | }, 29 | { 30 | "heat":4 31 | }, 32 | { 33 | "heat_unit":1 34 | }, 35 | { 36 | "settlement_heat":4 37 | }, 38 | { 39 | "settlement_heat_unit":1 40 | }, 41 | { 42 | "heat_power":4 43 | }, 44 | { 45 | "heatpower_unit":1 46 | }, 47 | { 48 | "accumflow":4 49 | }, 50 | { 51 | "accumflow_unit":1 52 | }, 53 | { 54 | "flow":4 55 | }, 56 | { 57 | "flow_unit":1 58 | }, 59 | { 60 | "in_temp":3 61 | }, 62 | { 63 | "out_temp":3 64 | }, 65 | { 66 | "work_time":3 67 | }, 68 | { 69 | "meter_time":7 70 | }, 71 | { 72 | "status":2 73 | }, 74 | { 75 | "parity_bit" : 1 76 | }, 77 | { 78 | "end_bit" :1 79 | } 80 | ], 81 | "data_precision": { 82 | "heat": 0.01, 83 | "settlement_heat": 0.01, 84 | "heat_power": 0.01, 85 | "accumflow": 0.01, 86 | "flow": 0.0001, 87 | "in_temp": 0.01, 88 | "out_temp": 0.01, 89 | "work_time": 1 90 | }, 91 | "data_status_bits": { 92 | "byte1": { 93 | "4": "电池欠压", 94 | "32": "有磁干扰", 95 | "64": "透支状态", 96 | "128": "强制发送" 97 | }, 98 | "byte2": { 99 | 100 | } 101 | }, 102 | "unit_dict": { 103 | "02": "Wh", 104 | "05": "kWh", 105 | "08": "MWh", 106 | "0A": "MWh*100", 107 | "01": "J", 108 | "0B": "KJ", 109 | "0E": "MJ", 110 | "11": "GJ", 111 | "13": "GJ*100", 112 | "14": "W", 113 | "17": "kW", 114 | "1A": "MW", 115 | "29": "L", 116 | "2C": "m3", 117 | "32": "L/h", 118 | "35": "m3/h" 119 | } 120 | } 121 | } 122 | 123 | } -------------------------------------------------------------------------------- /serialport/Parse/ruina_A188.json: -------------------------------------------------------------------------------- 1 | { 2 | "ctl_info":{ 3 | "meter_general_info": { 4 | "leading_str": "FEFEFEFE68", 5 | "iden_str": "1F90", 6 | "end_bit" : "16", 7 | "brand_str": "ruina", 8 | "meter_code_len": 8, 9 | "meter_code_reverse":"true", 10 | "meter_code_order" :"normal" 11 | }, 12 | "meter_read_protocol": { 13 | "data_structure": [ 14 | { 15 | "meter_type":1 16 | }, 17 | { 18 | "meter_code":7 19 | }, 20 | { 21 | "ctl_str":1 22 | }, 23 | { 24 | "ctr_data":1 25 | }, 26 | { 27 | "iden_str":3 28 | }, 29 | { 30 | "settlement_heat":4 31 | }, 32 | { 33 | "settlement_heat_unit":1 34 | }, 35 | { 36 | "heat":4 37 | }, 38 | { 39 | "heat_unit":1 40 | }, 41 | { 42 | "heat_power":4 43 | }, 44 | { 45 | "heatpower_unit":1 46 | }, 47 | { 48 | "flow":4 49 | }, 50 | { 51 | "flow_unit":1 52 | }, 53 | { 54 | "accumflow":4 55 | }, 56 | { 57 | "accumflow_unit":1 58 | }, 59 | { 60 | "in_temp":3 61 | }, 62 | { 63 | "out_temp":3 64 | }, 65 | { 66 | "work_time":3 67 | }, 68 | { 69 | "meter_time":7 70 | }, 71 | { 72 | "status":2 73 | }, 74 | { 75 | "parity_bit" : 1 76 | }, 77 | { 78 | "end_bit" :1 79 | } 80 | ], 81 | "data_precision": { 82 | "settlement_heat": 0.01, 83 | "heat": 0.01, 84 | "heat_power": 0.01, 85 | "flow": 0.0001, 86 | "accumflow": 0.01, 87 | "in_temp": 0.01, 88 | "out_temp": 0.01, 89 | "work_time": 1 90 | }, 91 | "data_status_bits": { 92 | "byte1": { 93 | "4": "电池欠压" 94 | }, 95 | "byte2": { 96 | "2": "进水故障", 97 | "4": "回水故障", 98 | "64": "水倒流", 99 | "128": "无水" 100 | } 101 | }, 102 | "unit_dict": { 103 | "02": "Wh", 104 | "05": "kWh", 105 | "08": "MWh", 106 | "0A": "MWh*100", 107 | "01": "J", 108 | "0B": "KJ", 109 | "0E": "MJ", 110 | "11": "GJ", 111 | "13": "GJ*100", 112 | "14": "W", 113 | "17": "kW", 114 | "1A": "MW", 115 | "29": "L", 116 | "2C": "m³", 117 | "32": "L/h", 118 | "35": "m³/h" 119 | } 120 | } 121 | } 122 | 123 | } -------------------------------------------------------------------------------- /serialport/Parse/tiangang_A188.json: -------------------------------------------------------------------------------- 1 | { 2 | "ctl_info":{ 3 | "meter_general_info": { 4 | "leading_str": "FEFEFEFE68", 5 | "iden_str": "1F90", 6 | "end_bit" : "16", 7 | "brand_str": "tiangang", 8 | "meter_code_len": 8, 9 | "meter_code_reverse":"true", 10 | "meter_code_order" :"normal" 11 | }, 12 | "meter_read_protocol": { 13 | "data_structure": [ 14 | { 15 | "meter_type":1 16 | }, 17 | { 18 | "meter_code":7 19 | }, 20 | { 21 | "ctl_str":1 22 | }, 23 | { 24 | "ctr_data":1 25 | }, 26 | { 27 | "iden_str":3 28 | }, 29 | { 30 | "settlement_heat":4 31 | }, 32 | { 33 | "settlement_heat_unit":1 34 | }, 35 | { 36 | "heat":4 37 | }, 38 | { 39 | "heat_unit":1 40 | }, 41 | { 42 | "heat_power":4 43 | }, 44 | { 45 | "heatpower_unit":1 46 | }, 47 | { 48 | "flow":4 49 | }, 50 | { 51 | "flow_unit":1 52 | }, 53 | { 54 | "accumflow":4 55 | }, 56 | { 57 | "accumflow_unit":1 58 | }, 59 | { 60 | "in_temp":3 61 | }, 62 | { 63 | "out_temp":3 64 | }, 65 | { 66 | "work_time":3 67 | }, 68 | { 69 | "meter_time":7 70 | }, 71 | { 72 | "status":2 73 | }, 74 | { 75 | "parity_bit" : 1 76 | }, 77 | { 78 | "end_bit" :1 79 | } 80 | ], 81 | "data_precision": { 82 | "settlement_heat": 0.01, 83 | "heat": 0.01, 84 | "heat_power": 0.01, 85 | "flow": 0.0001, 86 | "accumflow": 0.01, 87 | "in_temp": 0.01, 88 | "out_temp": 0.01, 89 | "work_time": 1 90 | }, 91 | "data_status_bits": { 92 | "byte1": { 93 | "4": "电池欠压" 94 | }, 95 | "byte2": { 96 | "2": "进水故障", 97 | "4": "回水故障", 98 | "64": "水倒流", 99 | "128": "无水" 100 | } 101 | }, 102 | "unit_dict": { 103 | "02": "Wh", 104 | "05": "kWh", 105 | "08": "MWh", 106 | "0A": "MWh*100", 107 | "01": "J", 108 | "0B": "KJ", 109 | "0E": "MJ", 110 | "11": "GJ", 111 | "13": "GJ*100", 112 | "15": "W", 113 | "17": "kW", 114 | "1A": "MW", 115 | "29": "L", 116 | "2A": "10L", 117 | "2C": "m3", 118 | "32": "L/h", 119 | "35": "m3/h" 120 | } 121 | } 122 | } 123 | 124 | } -------------------------------------------------------------------------------- /serialport/Parse/haiweici_A188.json: -------------------------------------------------------------------------------- 1 | { 2 | "ctl_info":{ 3 | "meter_general_info": { 4 | "leading_str": "FEFEFEFE68", 5 | "iden_str": "1F90", 6 | "end_bit" : "16", 7 | "brand_str": "haiweici", 8 | "meter_code_len": 10, 9 | "meter_code_reverse":"true", 10 | "meter_code_order" :"normal" 11 | }, 12 | "meter_read_protocol": { 13 | "data_structure": [ 14 | { 15 | "meter_type":1 16 | }, 17 | { 18 | "meter_code":7 19 | }, 20 | { 21 | "ctl_str":1 22 | }, 23 | { 24 | "ctr_data":1 25 | }, 26 | { 27 | "iden_str":3 28 | }, 29 | { 30 | "settlement_heat":4 31 | }, 32 | { 33 | "settlement_heat_unit":1 34 | }, 35 | { 36 | "heat":4 37 | }, 38 | { 39 | "heat_unit":1 40 | }, 41 | { 42 | "heat_power":4 43 | }, 44 | { 45 | "heatpower_unit":1 46 | }, 47 | { 48 | "flow":4 49 | }, 50 | { 51 | "flow_unit":1 52 | }, 53 | { 54 | "accumflow":4 55 | }, 56 | { 57 | "accumflow_unit":1 58 | }, 59 | { 60 | "in_temp":3 61 | }, 62 | { 63 | "out_temp":3 64 | }, 65 | { 66 | "work_time":3 67 | }, 68 | { 69 | "meter_time":7 70 | }, 71 | { 72 | "status":2 73 | }, 74 | { 75 | "parity_bit" : 1 76 | }, 77 | { 78 | "end_bit" :1 79 | } 80 | ], 81 | "data_precision": { 82 | "settlement_heat": 0.01, 83 | "heat": 0.01, 84 | "heat_power": 0.01, 85 | "flow": 0.0001, 86 | "accumflow": 0.01, 87 | "in_temp": 0.01, 88 | "out_temp": 0.01, 89 | "work_time": 1 90 | }, 91 | "data_status_bits": { 92 | "byte1": { 93 | "4": "电池欠压" 94 | }, 95 | "byte2": { 96 | "1": "进水口温度低于0ºC", 97 | "2": "进水口温度高于100ºC", 98 | "4": "出水口温度低于ºC", 99 | "8": "出水口温度高于100ºC", 100 | "16": "DAC校准失败", 101 | "32": "流量过大", 102 | "64": "无流量" 103 | } 104 | }, 105 | "unit_dict": { 106 | "02": "Wh", 107 | "05": "kWh", 108 | "08": "MWh", 109 | "0A": "MWh*100", 110 | "01": "J", 111 | "0B": "KJ", 112 | "0E": "MJ", 113 | "11": "GJ", 114 | "13": "GJ*100", 115 | "14": "W", 116 | "17": "kW", 117 | "1A": "MW", 118 | "29": "L", 119 | "2C": "m3", 120 | "32": "L/h", 121 | "35": "m3/h" 122 | } 123 | } 124 | } 125 | 126 | } -------------------------------------------------------------------------------- /serialport/Parse/haina_A188.json: -------------------------------------------------------------------------------- 1 | { 2 | "ctl_info":{ 3 | "meter_general_info": { 4 | "leading_str": "FEFEFEFE68", 5 | "iden_str": "1F90", 6 | "end_bit" : "16", 7 | "brand_str": "haina", 8 | "meter_code_len": 8, 9 | "meter_code_reverse":"true", 10 | "meter_code_order" :"normal" 11 | }, 12 | "meter_read_protocol": { 13 | "data_structure": [ 14 | { 15 | "meter_type":1 16 | }, 17 | { 18 | "meter_code":7 19 | }, 20 | { 21 | "ctl_str":1 22 | }, 23 | { 24 | "ctr_data":1 25 | }, 26 | { 27 | "iden_str":3 28 | }, 29 | { 30 | "settlement_heat":4 31 | }, 32 | { 33 | "settlement_heat_unit":1 34 | }, 35 | { 36 | "heat":4 37 | }, 38 | { 39 | "heat_unit":1 40 | }, 41 | { 42 | "heat_power":4 43 | }, 44 | { 45 | "heatpower_unit":1 46 | }, 47 | { 48 | "flow":4 49 | }, 50 | { 51 | "flow_unit":1 52 | }, 53 | { 54 | "accumflow":4 55 | }, 56 | { 57 | "accumflow_unit":1 58 | }, 59 | { 60 | "in_temp":3 61 | }, 62 | { 63 | "out_temp":3 64 | }, 65 | { 66 | "work_time":3 67 | }, 68 | { 69 | "meter_time":7 70 | }, 71 | { 72 | "status":2 73 | }, 74 | { 75 | "parity_bit" : 1 76 | }, 77 | { 78 | "end_bit" :1 79 | } 80 | ], 81 | "data_precision": { 82 | "settlement_heat": 0.01, 83 | "heat": 0.01, 84 | "heat_power": 0.01, 85 | "flow": 0.0001, 86 | "accumflow": 0.01, 87 | "in_temp": 0.01, 88 | "out_temp": 0.01, 89 | "work_time": 1 90 | }, 91 | "data_status_bits": { 92 | "byte1": { 93 | "1": "通讯失败", 94 | "4": "电池欠压" 95 | }, 96 | "byte2": { 97 | "1": "进水口温度低于0ºC", 98 | "2": "进水口温度高于100ºC", 99 | "4": "出水口温度低于0ºC", 100 | "8": "出水口温度高于100ºC", 101 | "16": "DAC校准失败", 102 | "32": "流量过大", 103 | "64": "无流量" 104 | } 105 | }, 106 | "unit_dict": { 107 | "02": "Wh", 108 | "05": "kWh", 109 | "08": "MWh", 110 | "0A": "MWh*100", 111 | "01": "J", 112 | "0B": "KJ", 113 | "0E": "MJ", 114 | "11": "GJ", 115 | "13": "GJ*100", 116 | "15": "W", 117 | "17": "kW", 118 | "1A": "MW", 119 | "29": "L", 120 | "2C": "m³", 121 | "32": "L/h", 122 | "35": "m³/h" 123 | } 124 | } 125 | } 126 | 127 | } -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # 基于Node.js的串口调试助手 2 | **说明**:该程序通过node.js方法实现对串口进行读写。进行node.js的串口程序开发,必须安装node.js下的串口开发控件‘serialport’。 3 | **安装**: `npm install serialport` 4 | ![串口调试助手界面](/2015-10-09_114116.png) 5 | *** 6 | ## 1.创建串口 7 | ```javascript 8 | function open_serial() { 9 | var serialPort = new SerialPort.SerialPort(com_num, { 10 | baudrate: baudrate, //波特率设置 11 | databits: databits, //数据位 12 | parity: parity, //校验位 13 | stopbits: stopbits //停止位 14 | // parser: SerialPort.parsers.readline("\n") //这句可能调用方法不对,加上这句就会出现接收数据编码不正常 15 | }); 16 | data_to_web('串口已经打开'); 17 | sp = serialPort; 18 | } 19 | ``` 20 | **说明**:通过该方法创建串口。注意创建窗口的时候串口号是必要条件,否则就会报错,无法创建端口。 21 | *** 22 | ## 2.读端口数据 23 | ```javascript 24 | function serial_read() { 25 | sp.on('data', function (info) { 26 | console.log('data_change:' + iconv.decode(info, rec_setting)); 27 | data_to_web(iconv.decode(info, rec_setting)); 28 | data_to_web('接收数据字节长度:' + info.length); 29 | }); 30 | } 31 | ``` 32 | **说明**:通过该方法读取串口数据。 33 | *** 34 | ## 3.写端口数据 35 | ```javascript 36 | function serial_write() { 37 | var buf_once = new Buffer(write_data, send_setting); 38 | sp.write(buf_once, function (err, results) { 39 | if (err) { 40 | console.log('err ' + err); 41 | } 42 | else { 43 | data_to_web('发送数据:' + write_data.toLocaleUpperCase()); 44 | data_to_web('发送数据字节长度: ' + results); 45 | console.log('发送数据:' + write_data.toLocaleUpperCase()); 46 | console.log('发送数据字节长度: ' + results); //发出去的数据字节长度 47 | } 48 | }); 49 | if (auto_send_time != 0) { 50 | var autoSend = setInterval(function () { 51 | if (serial_flag != 'close') { 52 | var buf = new Buffer(write_data, send_setting); 53 | sp.write(buf, function (err, results) { 54 | if (err) { 55 | console.log('err ' + err); 56 | } 57 | else { 58 | data_to_web('发送数据:' + write_data.toLocaleUpperCase()); 59 | data_to_web('发送数据字节长度: ' + results); 60 | console.log('发送数据:' + write_data.toLocaleUpperCase()); 61 | console.log('发送数据字节长度: ' + results); //发出去的数据字节长度 62 | } 63 | }) 64 | } 65 | else { 66 | clearInterval(autoSend); 67 | } 68 | }, auto_send_time); 69 | } 70 | } 71 | ``` 72 | **说明**:通过该方法向串口写入数据。因为在实际串口调试中存在单次发送和定时连续发生,所以这里也对这两种情况做了区分。当然,当我们点下发送按钮的时候,必须立马有一条数据发出,所以如这个方法里面的代码所示,调用该方法的时候我首先会先发送一次数据。这样做的好处是避免延时现象,比如我要间隔2s定时发送数据,那么当发送按钮点击完成,2s后才会开始发送数据。 73 | *** 74 | ## 4.数据发送方式判断 75 | ```javascript 76 | function send_juge() { 77 | 78 | //命令手动发送条件 79 | if (command != "" && auto_send == "auto_send_no") //发送命令手动抄表 80 | { 81 | serial_read_write(command, 0); 82 | } 83 | //命令自动发送 84 | if (command != "" && auto_send == "auto_send_yes") //定时自动抄表 85 | { 86 | serial_read_write(command, auto_time); 87 | } 88 | } 89 | ``` 90 | *** 91 | ## 5.其他 92 | 这里面还有其它的方法和逻辑判断,这里不一一细说,在程序里面都有相应的注释。在web端运用了多个ajax推送页面信息到后台服务的事件,这里只给出一个示例说明: 93 | ```javascript 94 | $(function() 95 | { 96 | $('#set_change').click(function() 97 | { 98 | rec_setting = $("#rec_setting").find('option:selected').val();//获取接收编码 99 | send_setting = $("#send_setting").find('option:selected').val();//获取发送编码 100 | var encode_set = { 101 | rec_setting: rec_setting, 102 | send_setting: send_setting 103 | }; 104 | var JSON_encode = JSON.stringify(encode_set); //将js对象转换成json格式数据 105 | $.ajax({ 106 | type: 'post', 107 | url: "command_1.1.html", 108 | data: JSON_encode, 109 | async: true, 110 | // dataType: 'json', 111 | success: function (data) { 112 | // alert(data); 113 | }, 114 | error: function (XMLHttpRequest, textStatus, errorThrown) { 115 | // alert("Save error!" + XMLHttpRequest.status + " " + XMLHttpRequest.readyState); 116 | alert("指令发送失败,请检查服务器是否运行正常!") 117 | } 118 | }); 119 | connect_toserver.emit('encode_setting', encode_set); 120 | }) 121 | }); 122 | ``` 123 | **说明**:这个方法是推送编码设置到后台程序。 124 | -------------------------------------------------------------------------------- /logs/accessTransfer.log: -------------------------------------------------------------------------------- 1 | ::1 - - [20/Jul/2015:08:11:22 +0000] "GET /favicon.ico HTTP/1.1" 404 24 "-" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 2 | ::1 - - [21/Jul/2015:09:35:00 +0000] "GET /favicon.ico HTTP/1.1" 404 24 "-" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36 SE 2.X MetaSr 1.0" 3 | ::1 - - [21/Jul/2015:09:35:00 +0000] "GET /css/bootstrap.min.css HTTP/1.1" 404 34 "http://localhost:1337/command" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36 SE 2.X MetaSr 1.0" 4 | ::1 - - [21/Jul/2015:09:35:00 +0000] "GET /js/jquery.js HTTP/1.1" 404 25 "http://localhost:1337/command" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36 SE 2.X MetaSr 1.0" 5 | ::1 - - [21/Jul/2015:09:35:00 +0000] "GET /js/bootstrap.min.js HTTP/1.1" 404 32 "http://localhost:1337/command" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36 SE 2.X MetaSr 1.0" 6 | ::1 - - [21/Jul/2015:09:35:31 +0000] "GET /css/bootstrap.min.css HTTP/1.1" 404 34 "http://localhost:1337/command?" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36 SE 2.X MetaSr 1.0" 7 | ::1 - - [21/Jul/2015:09:35:31 +0000] "GET /js/jquery.js HTTP/1.1" 404 25 "http://localhost:1337/command?" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36 SE 2.X MetaSr 1.0" 8 | ::1 - - [21/Jul/2015:09:35:31 +0000] "GET /js/bootstrap.min.js HTTP/1.1" 404 32 "http://localhost:1337/command?" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36 SE 2.X MetaSr 1.0" 9 | ::1 - - [21/Jul/2015:09:38:38 +0000] "GET /favicon.ico HTTP/1.1" 404 24 "-" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 10 | ::1 - - [21/Jul/2015:09:38:55 +0000] "GET /css/bootstrap.min.css HTTP/1.1" 200 122540 "http://localhost:1337/command" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 11 | ::1 - - [21/Jul/2015:09:38:55 +0000] "GET /js/jquery.js HTTP/1.1" 200 247823 "http://localhost:1337/command" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 12 | ::1 - - [21/Jul/2015:09:38:55 +0000] "GET /js/bootstrap.min.js HTTP/1.1" 200 36816 "http://localhost:1337/command" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 13 | ::1 - - [21/Jul/2015:09:39:09 +0000] "GET /css/bootstrap.min.css HTTP/1.1" 304 - "http://localhost:1337/command?" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 14 | ::1 - - [21/Jul/2015:09:39:09 +0000] "GET /js/jquery.js HTTP/1.1" 304 - "http://localhost:1337/command?" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 15 | ::1 - - [21/Jul/2015:09:39:09 +0000] "GET /js/bootstrap.min.js HTTP/1.1" 304 - "http://localhost:1337/command?" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 16 | ::1 - - [21/Jul/2015:09:43:43 +0000] "GET /css/bootstrap.min.css HTTP/1.1" 304 - "http://localhost:1337/command?" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 17 | ::1 - - [21/Jul/2015:09:43:43 +0000] "GET /js/jquery.js HTTP/1.1" 304 - "http://localhost:1337/command?" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 18 | ::1 - - [21/Jul/2015:09:43:43 +0000] "GET /js/bootstrap.min.js HTTP/1.1" 304 - "http://localhost:1337/command?" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 19 | ::1 - - [21/Jul/2015:09:45:21 +0000] "GET /favicon.ico HTTP/1.1" 404 24 "-" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 20 | ::1 - - [21/Jul/2015:09:45:33 +0000] "GET /css/bootstrap.min.css HTTP/1.1" 304 - "http://localhost:1337/command" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 21 | ::1 - - [21/Jul/2015:09:45:33 +0000] "GET /js/jquery.js HTTP/1.1" 304 - "http://localhost:1337/command" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 22 | ::1 - - [21/Jul/2015:09:45:33 +0000] "GET /js/bootstrap.min.js HTTP/1.1" 304 - "http://localhost:1337/command" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 23 | ::1 - - [21/Jul/2015:09:45:38 +0000] "GET /css/bootstrap.min.css HTTP/1.1" 304 - "http://localhost:1337/command?" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 24 | ::1 - - [21/Jul/2015:09:45:38 +0000] "GET /js/jquery.js HTTP/1.1" 304 - "http://localhost:1337/command?" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 25 | ::1 - - [21/Jul/2015:09:45:38 +0000] "GET /js/bootstrap.min.js HTTP/1.1" 304 - "http://localhost:1337/command?" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 26 | ::1 - - [22/Jul/2015:00:10:36 +0000] "GET /favicon.ico HTTP/1.1" 404 24 "-" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 27 | ::1 - - [22/Jul/2015:00:10:58 +0000] "GET /css/bootstrap.min.css HTTP/1.1" 304 - "http://localhost:1337/command" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 28 | ::1 - - [22/Jul/2015:00:10:58 +0000] "GET /js/jquery.js HTTP/1.1" 304 - "http://localhost:1337/command" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 29 | ::1 - - [22/Jul/2015:00:10:58 +0000] "GET /js/bootstrap.min.js HTTP/1.1" 304 - "http://localhost:1337/command" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 30 | ::1 - - [22/Jul/2015:00:11:57 +0000] "GET /css/bootstrap.min.css HTTP/1.1" 304 - "http://localhost:1337/command?" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 31 | ::1 - - [22/Jul/2015:00:11:57 +0000] "GET /js/jquery.js HTTP/1.1" 304 - "http://localhost:1337/command?" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 32 | ::1 - - [22/Jul/2015:00:11:57 +0000] "GET /js/bootstrap.min.js HTTP/1.1" 304 - "http://localhost:1337/command?" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 33 | ::1 - - [22/Jul/2015:00:12:05 +0000] "GET /css/bootstrap.min.css HTTP/1.1" 304 - "http://localhost:1337/command?" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 34 | ::1 - - [22/Jul/2015:00:12:05 +0000] "GET /js/jquery.js HTTP/1.1" 304 - "http://localhost:1337/command?" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 35 | ::1 - - [22/Jul/2015:00:12:05 +0000] "GET /js/bootstrap.min.js HTTP/1.1" 304 - "http://localhost:1337/command?" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 36 | ::1 - - [22/Jul/2015:00:17:19 +0000] "GET / HTTP/1.1" 404 13 "-" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.153 Safari/537.36 SE 2.X MetaSr 1.0" 37 | ::1 - - [22/Jul/2015:03:51:22 +0000] "GET /favicon.ico HTTP/1.1" 404 24 "-" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 38 | ::1 - - [22/Jul/2015:03:51:59 +0000] "GET /favicon.ico HTTP/1.1" 404 24 "-" "Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/35.0.1916.114 Safari/537.36" 39 | -------------------------------------------------------------------------------- /serialport/node-serialport.js: -------------------------------------------------------------------------------- 1 | var SerialPort = require("serialport"); //引入串口模块 2 | var iconv = require('iconv-lite'); //引入数据编码格式转换模块 3 | var http = require('http'); //引入http模块 4 | var express = require('express'); //引入express模块 5 | var sio = require('socket.io'); //引入socket.io模块 6 | var app = express(); //创建express实例 7 | var path = require('path'); //引入path模块 8 | var fs = require('fs'); //引入fs模块 9 | var server = http.createServer(app); 10 | server.listen(2212);//监听2212端口 11 | 12 | if ((typeof sp) != 'undefined') { 13 | sp.setMaxListeners(20); 14 | } 15 | 16 | app.use(express.static(path.join(__dirname, 'public'))); //设置public文件夹为静态资源文件夹,必须建立否则js和css无法加载 17 | app.get('/', function (req, res) { 18 | res.sendFile(__dirname + '/command_1.1.html'); 19 | }); //设置路由,当客户端请求'/'时,发送文件command.html 20 | app.post('/command_1.1.html', function (req, res) { 21 | req.on("data", function (data) { 22 | res.send(data); 23 | }); 24 | }); 25 | 26 | 27 | var socket = sio.listen(server); 28 | var rec_setting = ''; //接收编码方式 29 | var send_setting = ''; //发送编码方式 30 | var send_times = 0; //记录页面点击发送的次数 31 | var sp; //定义一个全局变量,接收创建的端口 32 | //监听connection事件 33 | socket.on('connection', function (socket) { 34 | console.log('与客户端的命令连接通道已经建立'); 35 | 36 | SerialPort.list(function (err, ports) { 37 | ports.forEach(function (port) { 38 | //console.log(port.comName); 39 | serials_to_web(port.comName); 40 | }); 41 | }); 42 | /************************************获取串口配置信息*************************************/ 43 | socket.on('serial_info', function (data) //获取串口信息 44 | { 45 | com_num = data.com_num; //串口号 46 | baudrate = parseInt(data.baudrate); //波特率 47 | databits = parseInt(data.databits); //数据位 48 | stopbits = parseInt(data.stopbits); //停止位 49 | parity = data.parity; //校验 50 | serial_flag = data.serial_flag; //串口状态标志位 51 | 52 | 53 | if (serial_flag == "close") { 54 | if ((typeof sp) != 'undefined') { 55 | if(sp.isOpen()) 56 | { 57 | sp.close(function (err) { 58 | if (err) throw err; 59 | else { 60 | console.log('串口已经关闭'); 61 | data_to_web('串口已经关闭'); 62 | } 63 | }); 64 | } 65 | } 66 | } 67 | } 68 | ); 69 | //说明:串口实现可以重复打开关闭的思路是:如果关闭串口按钮按下,推送串口信息(起始只需要推送串口状态标志位就可以了)到服务器,服务器判断串口信息, 70 | // 如果是关闭串口的操作的话,sp.close()来关闭串口连接,同时更改上一次打开的串口的串口号(这个串口号必须要改,否则没办法继续下一次打开关闭串口的操作), 71 | // 更改了上次打开的串口号之后,下次打开串口直接重新开一个端口就可以了,这样就不会出现'Eroor:SerialPort is not open'的错误了。 72 | 73 | socket.on('meterReading', function (data) { 74 | //监听meterReading事件 获取数据 75 | send_times++; 76 | rec_setting = data.rec_setting; //接收编码 77 | send_setting = data.send_setting; //发送编码 78 | if (data.auto_time != "") //如果发送时间为空,则发送时间为0 79 | { 80 | auto_time = parseInt(data.auto_time); 81 | } 82 | else { 83 | auto_time = 0; 84 | } 85 | auto_send = data.auto_send; //是否自动抄表 86 | command = data.command; //指令 87 | // console.log(rec_setting); 88 | 89 | send_juge(); 90 | 91 | if (send_times == 1) //当页面第一次进行数据发送时,就打开监听,此后监听就一直开启,不应该多次打开,打开一次就够了 92 | { 93 | socket.on('disconnect', function () { 94 | //监听disconnect事件 95 | console.log('已经断开命令通道连接!'); 96 | }); 97 | socket.on('error', function (err) { 98 | if (err) { 99 | console.log(err); 100 | } 101 | }) 102 | } 103 | 104 | }); 105 | 106 | //接收编码的更改 107 | socket.on('encode_setting', function (data) { 108 | rec_setting = data.rec_setting; //接收编码 109 | send_setting = data.send_setting; //发送编码 110 | }); 111 | }); 112 | 113 | 114 | /*************************************推送接收的数据到网页页面*************************************/ 115 | function data_to_web(rec_data) { 116 | socket.emit('data_to_web', rec_data); //‘发送’ data_to_web 事件 117 | } 118 | 119 | /******************************************推送串口信息到网页页面*************************************/ 120 | function serials_to_web(data) { 121 | socket.emit('serials_to_web', data); 122 | } 123 | 124 | 125 | /*********************************串口数据发送和接收***********************************************/ 126 | function serial_read_write(write_data, auto_send_time) //串口操作 127 | { 128 | if (serial_flag != 'close') { 129 | //sp是用来记录创建的serialPort的 130 | if ((typeof sp) != 'undefined') { 131 | if (sp.isOpen()) { 132 | if (sp.path != com_num) //sp有数据,且sp记录打开的串口号和即将创建的串口号不同 133 | { 134 | //sp.close(function (err) { 135 | // if (err) throw err; 136 | //}); 137 | open_serial(); //打开端口 138 | open_wr(); //新创建的端口必须要先打开端口(sp.on('open',callback))才能进行数据读写 139 | } 140 | else { //这种情况就是打开串口之后一直进行发送操作 141 | if (send_times == 1) //第一次进行发送,此时打开读写同时打开监听 142 | { 143 | write_read(); //端口已经创建,直接读写就可以,因为上一次创建端口时已经打开了端口,若此时继续打开端口,sp.on('open',callback)内部的代码不会执行 144 | } 145 | else //已经打开读监听了,如果继续打开,发送多次之后会出现“(node) warning: possible EventEmitter memory leak detected. 11 data listeners added. Use emitter.setMaxListeners() to increase limit.”的警告 146 | //所以此时不需要再次打开监听,只需要进行数据发送就可以了。 147 | { 148 | serial_write(); 149 | } 150 | } 151 | } 152 | else { 153 | open_serial(); //打开端口 154 | open_wr(); 155 | } 156 | } 157 | else //sp没有数据,则直接打开串口 158 | { 159 | open_serial(); 160 | open_wr(); 161 | } 162 | } 163 | /************************************打开串口并读写数据*********************************/ 164 | function open_wr() { 165 | sp.on("open", function (err) { 166 | if (err) { 167 | console.log(err + "打开串口出错,请重试"); 168 | } 169 | else { 170 | console.log('串口已经打开'); 171 | serial_read(); 172 | serial_write(); 173 | } 174 | }); 175 | } 176 | 177 | /*****************************************读写数据***************************************/ 178 | function write_read() { 179 | serial_read(); 180 | serial_write(); 181 | } 182 | 183 | /****************************读串口************************************************/ 184 | function serial_read() { 185 | sp.on('data', function (info) { 186 | console.log('data_change:' + iconv.decode(info, rec_setting)); 187 | data_to_web(iconv.decode(info, rec_setting)); 188 | data_to_web('接收数据字节长度:' + info.length); 189 | }); 190 | } 191 | 192 | /****************************写串口************************************************/ 193 | function serial_write() { 194 | var buf_once = new Buffer(write_data, send_setting); 195 | sp.write(buf_once, function (err, results) { 196 | if (err) { 197 | console.log('err ' + err); 198 | } 199 | else { 200 | data_to_web('发送数据:' + write_data.toLocaleUpperCase()); 201 | data_to_web('发送数据字节长度: ' + results); 202 | console.log('发送数据:' + write_data.toLocaleUpperCase()); 203 | console.log('发送数据字节长度: ' + results); //发出去的数据字节长度 204 | } 205 | }); 206 | if (auto_send_time != 0) { 207 | var autoSend = setInterval(function () { 208 | if (serial_flag != 'close') { 209 | var buf = new Buffer(write_data, send_setting); 210 | sp.write(buf, function (err, results) { 211 | if (err) { 212 | console.log('err ' + err); 213 | } 214 | else { 215 | data_to_web('发送数据:' + write_data.toLocaleUpperCase()); 216 | data_to_web('发送数据字节长度: ' + results); 217 | console.log('发送数据:' + write_data.toLocaleUpperCase()); 218 | console.log('发送数据字节长度: ' + results); //发出去的数据字节长度 219 | } 220 | }) 221 | } 222 | else { 223 | clearInterval(autoSend); 224 | } 225 | }, auto_send_time); 226 | } 227 | } 228 | 229 | /**********************************创建串口***************************************/ 230 | function open_serial() { 231 | var serialPort = new SerialPort.SerialPort(com_num, { 232 | baudrate: baudrate, //波特率设置 233 | databits: databits, //数据位 234 | parity: parity, //校验位 235 | stopbits: stopbits //停止位 236 | // parser: SerialPort.parsers.readline("\n") //这句可能调用方法不对,加上这句就会出现接收数据编码不正常 237 | }); 238 | data_to_web('串口已经打开'); 239 | sp = serialPort; 240 | } 241 | 242 | /**********************************创建串口***************************************/ 243 | 244 | } 245 | 246 | /****************************************发送状态判断*******************************************/ 247 | function send_juge() { 248 | //命令手动发送条件 249 | if (command != "" && auto_send == "auto_send_no") //发送命令手动抄表 250 | { 251 | serial_read_write(command, 0); 252 | } 253 | //命令自动发送 254 | if (command != "" && auto_send == "auto_send_yes") //定时自动抄表 255 | { 256 | serial_read_write(command, auto_time); 257 | } 258 | } 259 | -------------------------------------------------------------------------------- /serialport/Parse/dataParse.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs'); 2 | var settings = require('../settings.js'); 3 | var jsonFile = require(settings.Parse.jsonFile_dataparse_path); //加载瑞纳json文件(数据解析规约) 4 | //var jsonFile = require('./huizhong_A188.json'); //加载汇中json文件(数据解析规约) 5 | //var jsonFile = require('./haiweici_A188.json'); //加载海威茨json文件(数据解析规约) 6 | //var jsonFile = require('./sanlong_A188.json'); //加载三龙json文件(数据解析规约) 7 | meter_type = ["water meter", "heat meter", "gas meter", "other meters"];//定义仪表类型 8 | //10H~19H :水表,20H~29H:热量表,30H~39H:燃气表,40H~49H:其他仪表 9 | //Meter test data buffer 10 | meter_data_str = 'FEFEFEFEFEFEFEFEFEFEFEFEFE682591092059001111812E1F901000000000050003000005' + 11 | '00000000170000000035270000002C6014007414004654005149092204102004006D16'; //瑞纳 12 | //meter_data_str = 'FEFEFEFEFEFEFEFEFEFEFE6820FFFFF130156920812E1F900100' + 13 | // '00000005604005000500000000170000000035831900002C1419001519002800000315170503042000084D16'; //汇中 14 | //meter_data_str = 'FEFEFEFEFE682012345678905555812E1F90120000000005604005000500000000170000000035831900002C1419001519002800000315170503042000088116';//海威茨 15 | var ctl_info = jsonFile.ctl_info; //获取ctl_info下的所有变量 16 | var meter_general_info = ctl_info.meter_general_info; //获取表具信息 17 | var meter_read_protocol = ctl_info.meter_read_protocol; //获取数据解析规则 18 | 19 | var data_structure = meter_read_protocol.data_structure; //获取数据解析规则的各个数据所占字节数 20 | var data_precision = meter_read_protocol.data_precision; //获取数据解析规则的各个数据的处理方式,乘以1,乘以0.1还是乘以0.01 21 | var data_status_bits = meter_read_protocol.data_status_bits; //获取数据解析规则的表具状态标志位 22 | var unit_dict = meter_read_protocol.unit_dict; //获取数据解析规则的单位,mW,kWh等 23 | 24 | var reg; //定义一个正则表达式 25 | var reg_temp = meter_general_info.iden_str; //获取数据标志位,标志位共6位,但前四位固定不变,后两位是会发生变化的,所以必须要根据不变的数据标识位找出全部的数据标志位 26 | reg = new RegExp(reg_temp + "(\\S{2})", "g"); //根据接收到的标志位构造一个正则表达式 27 | 28 | var leading_str = meter_general_info.leading_str; //数据解码起始位,前导符FE的字节数必须大于4 29 | var meter_code_len = meter_general_info.meter_code_len; //获取表号长度 30 | var meter_code_reverse = meter_general_info.meter_code_reverse; //获取表号是否翻转 31 | var meter_code_order = meter_general_info.meter_code_order; //获取表号的顺序是否正常(只对于不需要表号反转的表),不正常的话,表号之外的多余位在后面 32 | //正常的话,表号之外的多余位在前面 33 | 34 | //字符串翻转,两两(1个字节)逆序排列 35 | //说明:这里的字符串翻转并不是逐个的逆序排列,是两两的逆序排列。 36 | // 也就是说一个字节的数据是不允许发生变换的,否则就会改变它的真实数值。 37 | // 所以,这里首先要给一个字节的数据进行翻转,然后逐个地逆序排列整个字符串,这样变换的结果才是我们想要的结果 38 | function reverse(str) { 39 | var res = ""; 40 | var temp_elem = ""; 41 | var temp = ""; 42 | for (var i = 0; i < str.length; i++) { 43 | if (i % 2 == 0) { 44 | temp_elem = str[i + 1] + str[i]; //因为是字符串形式所以直接进行拼接 45 | temp += temp_elem; 46 | } 47 | } 48 | for (var j = 0; j < str.length; j++) { 49 | res += temp[str.length - 1 - j]; //翻转整个字符串 50 | } 51 | return res; 52 | } 53 | //提取json文件中的数据名称信息和,用于后面数据的乘法(数值转换)操作 54 | function get_data_str() { 55 | var res = []; 56 | var result = []; 57 | for (var i = 0; i < data_structure.length; i++) { 58 | for (key in data_structure[i]) //这里的变量key可以任意命名 59 | { 60 | res[i] = key; //获取data_structure的key 61 | result[i] = data_structure[i][key] * 2; //获取对应的数据字节数*2. 62 | // 说明:1个字节是8位,但在取数组长度的时候,一个字节就被看成是两个长度,所以这里获取到的字节数要乘以2 63 | } 64 | } 65 | data_struct = res; //定义两个全局变量,接收data_structure里的key和value 66 | data_len = result; 67 | } 68 | get_data_str(); 69 | 70 | 71 | //给对象赋值操作 72 | //function add_object_info(object,val,unit) 73 | //{ 74 | // if(val == 0) 75 | // { 76 | // object.unit = unit ; 77 | // } 78 | // else if(unit == 0) 79 | // { 80 | // object.val = val; 81 | // } 82 | // else 83 | // { 84 | // object.val = val; 85 | // object.unit = unit ; 86 | // } 87 | //} 88 | //var hello = {val : 8,unit : 9}; 89 | //add_object_info(hello,1,0); 90 | //console.log(hello); 91 | 92 | /**********************************************start--状态位判断子模块--start***********************************************************************************/ 93 | //设计思路:状态位信息由很多种情况,如果人为进行所有可能性的组合,这是一个相当麻烦,而且容易出错的事情。 94 | // 通过我的这种方法可以轻松的由计算机对状态位进行分析而进行有针对性的组合。 95 | 96 | /*******************************************************************/ 97 | // D7 || D6 || D5 || D4 || D3 || D2 || D1 || D0 || 98 | /*******************************************************************/ 99 | //无水 || 逆流 || || || ||回水故障 || 进水故障|| || 100 | /*******************************************************************/ 101 | //这样我们只需要在json文件的状态信息里面写入4种可能性:“128”:“无水”,“64”:“逆流”,“4”:“回水故障”,“2”:“进水故障” 102 | //举个例子:状态信息为C6-->>1100 0110,按位去找对应的10进制数值为:128 64 4 2;然后寻找对应的状态位信息得到结果为:无水,逆流,回水故障,进水故障。 103 | 104 | // 获取每一位的数据信息,返回值为一个数组,记录着那一位的数据为1,比如返回值为[8],则意味着字符串的倒数第四位为1,字符串为1000 105 | function get_bit_value(str) 106 | { 107 | var res = []; 108 | var res_val_count = 0; 109 | for(var i = 0;i 2 | 3 | 4 | 5 | Ensaving-串口调试助手 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 51 | 80 | 108 | 140 | 168 | 195 | 196 | 207 | 215 | 216 | 217 |
218 |
219 |
220 |
221 |
222 | 223 | 224 | 226 | 227 | 228 |

基于Node.js--串口调试助手

229 | 230 |
231 |
232 |
233 | 234 | 235 | 236 | 244 | 245 | 246 | 265 | 266 | 267 | 278 | 279 | 280 | 289 | 290 | 291 | 303 | 304 |
237 |
238 | 串口号 239 | 242 |
243 |
247 |
248 | 波特率 249 | 263 |
264 |
268 |
269 | 数据位 270 | 276 |
277 |
281 |
282 | 停止位 283 | 287 |
288 |
292 |
293 | 校验位 294 | 301 |
302 |
305 |
306 | 307 | 308 | 313 | 320 | 321 |
309 | 312 | 314 |
315 | 318 |
319 |
322 |
323 |
324 | 325 |
326 |
327 | 329 |
330 |
331 |
332 |
333 |
334 | 335 | 336 | 342 | 343 | 344 | 356 | 357 | 358 | 359 | 366 | 367 |
368 | 369 |
370 |
337 |
338 | 发送指令: 339 | 340 |
341 |
345 |
346 | 347 |
348 | 定时发送: 349 | 353 |
354 |
355 |
360 |
361 | 间隔时间: 362 | 364 |
365 |
371 | 372 |
373 | 374 |
375 | 376 | 377 | 378 | 379 | 389 | 390 | 391 | 401 | 402 | 403 | 404 |
380 |
381 | 接收设置 382 | 387 |
388 |
392 |
393 | 发送设置 394 | 399 |
400 |
405 |
406 |
407 |
408 | 411 |
412 | 413 | -------------------------------------------------------------------------------- /.idea/workspace.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 14 | 15 | 16 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 66 | 67 | 68 | 69 | true 70 | 71 | 72 | 73 | 74 | 75 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 112 | 113 | 114 | 115 | 118 | 119 | 122 | 123 | 124 | 125 | 128 | 129 | 132 | 133 | 136 | 137 | 138 | 139 | 142 | 143 | 146 | 147 | 150 | 151 | 154 | 155 | 156 | 157 | 160 | 161 | 164 | 165 | 168 | 169 | 172 | 173 | 176 | 177 | 178 | 179 | 182 | 183 | 186 | 187 | 190 | 191 | 192 | 193 | 196 | 197 | 200 | 201 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 256 | 257 | 258 | 259 | 260 | 261 | true 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 1444295169214 282 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | 292 | 293 | 294 | 295 | 296 | 297 | 298 | 299 | 300 | 301 | 302 | 303 | 304 | 305 | 306 | 307 | 308 | 309 | 310 | 311 | 314 | 317 | 318 | 319 | 321 | 322 | 323 | 324 | 325 | 326 | 327 | 328 | 329 | 330 | 331 | 332 | 333 | 334 | 335 | 336 | 337 | 338 | 339 | 340 | 341 | 342 | 343 | 344 | 345 | 346 | 347 | 348 | 349 | 350 | 351 | 352 | 353 | 354 | 355 | 356 | 357 | 358 | 359 | 360 | 361 | 362 | 363 | 364 | 365 | 366 | 367 | 368 | 369 | 370 | 371 | 372 | 373 | 374 | 375 | 376 | 377 | 378 | 379 | 380 | 381 | 382 | 383 | 384 | 385 | 386 | 387 | 388 | 389 | 390 | 391 | 392 | 393 | 394 | 395 | 396 | 397 | 398 | 399 | 400 | 401 | 402 | 403 | 404 | 405 | 406 | 407 | 408 | 409 | 410 | 411 | 412 | 413 | 414 | 415 | 416 | 417 | 418 | 419 | 420 | 421 | 422 | 423 | 424 | 425 | 426 | 427 | 428 | 429 | 430 | 431 | -------------------------------------------------------------------------------- /js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.5 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under the MIT license 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.5",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.5",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.5",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.5",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.5",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.5",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.5",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.5",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); -------------------------------------------------------------------------------- /serialport/public/js/bootstrap.min.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.3.5 (http://getbootstrap.com) 3 | * Copyright 2011-2015 Twitter, Inc. 4 | * Licensed under the MIT license 5 | */ 6 | if("undefined"==typeof jQuery)throw new Error("Bootstrap's JavaScript requires jQuery");+function(a){"use strict";var b=a.fn.jquery.split(" ")[0].split(".");if(b[0]<2&&b[1]<9||1==b[0]&&9==b[1]&&b[2]<1)throw new Error("Bootstrap's JavaScript requires jQuery version 1.9.1 or higher")}(jQuery),+function(a){"use strict";function b(){var a=document.createElement("bootstrap"),b={WebkitTransition:"webkitTransitionEnd",MozTransition:"transitionend",OTransition:"oTransitionEnd otransitionend",transition:"transitionend"};for(var c in b)if(void 0!==a.style[c])return{end:b[c]};return!1}a.fn.emulateTransitionEnd=function(b){var c=!1,d=this;a(this).one("bsTransitionEnd",function(){c=!0});var e=function(){c||a(d).trigger(a.support.transition.end)};return setTimeout(e,b),this},a(function(){a.support.transition=b(),a.support.transition&&(a.event.special.bsTransitionEnd={bindType:a.support.transition.end,delegateType:a.support.transition.end,handle:function(b){return a(b.target).is(this)?b.handleObj.handler.apply(this,arguments):void 0}})})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var c=a(this),e=c.data("bs.alert");e||c.data("bs.alert",e=new d(this)),"string"==typeof b&&e[b].call(c)})}var c='[data-dismiss="alert"]',d=function(b){a(b).on("click",c,this.close)};d.VERSION="3.3.5",d.TRANSITION_DURATION=150,d.prototype.close=function(b){function c(){g.detach().trigger("closed.bs.alert").remove()}var e=a(this),f=e.attr("data-target");f||(f=e.attr("href"),f=f&&f.replace(/.*(?=#[^\s]*$)/,""));var g=a(f);b&&b.preventDefault(),g.length||(g=e.closest(".alert")),g.trigger(b=a.Event("close.bs.alert")),b.isDefaultPrevented()||(g.removeClass("in"),a.support.transition&&g.hasClass("fade")?g.one("bsTransitionEnd",c).emulateTransitionEnd(d.TRANSITION_DURATION):c())};var e=a.fn.alert;a.fn.alert=b,a.fn.alert.Constructor=d,a.fn.alert.noConflict=function(){return a.fn.alert=e,this},a(document).on("click.bs.alert.data-api",c,d.prototype.close)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.button"),f="object"==typeof b&&b;e||d.data("bs.button",e=new c(this,f)),"toggle"==b?e.toggle():b&&e.setState(b)})}var c=function(b,d){this.$element=a(b),this.options=a.extend({},c.DEFAULTS,d),this.isLoading=!1};c.VERSION="3.3.5",c.DEFAULTS={loadingText:"loading..."},c.prototype.setState=function(b){var c="disabled",d=this.$element,e=d.is("input")?"val":"html",f=d.data();b+="Text",null==f.resetText&&d.data("resetText",d[e]()),setTimeout(a.proxy(function(){d[e](null==f[b]?this.options[b]:f[b]),"loadingText"==b?(this.isLoading=!0,d.addClass(c).attr(c,c)):this.isLoading&&(this.isLoading=!1,d.removeClass(c).removeAttr(c))},this),0)},c.prototype.toggle=function(){var a=!0,b=this.$element.closest('[data-toggle="buttons"]');if(b.length){var c=this.$element.find("input");"radio"==c.prop("type")?(c.prop("checked")&&(a=!1),b.find(".active").removeClass("active"),this.$element.addClass("active")):"checkbox"==c.prop("type")&&(c.prop("checked")!==this.$element.hasClass("active")&&(a=!1),this.$element.toggleClass("active")),c.prop("checked",this.$element.hasClass("active")),a&&c.trigger("change")}else this.$element.attr("aria-pressed",!this.$element.hasClass("active")),this.$element.toggleClass("active")};var d=a.fn.button;a.fn.button=b,a.fn.button.Constructor=c,a.fn.button.noConflict=function(){return a.fn.button=d,this},a(document).on("click.bs.button.data-api",'[data-toggle^="button"]',function(c){var d=a(c.target);d.hasClass("btn")||(d=d.closest(".btn")),b.call(d,"toggle"),a(c.target).is('input[type="radio"]')||a(c.target).is('input[type="checkbox"]')||c.preventDefault()}).on("focus.bs.button.data-api blur.bs.button.data-api",'[data-toggle^="button"]',function(b){a(b.target).closest(".btn").toggleClass("focus",/^focus(in)?$/.test(b.type))})}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.carousel"),f=a.extend({},c.DEFAULTS,d.data(),"object"==typeof b&&b),g="string"==typeof b?b:f.slide;e||d.data("bs.carousel",e=new c(this,f)),"number"==typeof b?e.to(b):g?e[g]():f.interval&&e.pause().cycle()})}var c=function(b,c){this.$element=a(b),this.$indicators=this.$element.find(".carousel-indicators"),this.options=c,this.paused=null,this.sliding=null,this.interval=null,this.$active=null,this.$items=null,this.options.keyboard&&this.$element.on("keydown.bs.carousel",a.proxy(this.keydown,this)),"hover"==this.options.pause&&!("ontouchstart"in document.documentElement)&&this.$element.on("mouseenter.bs.carousel",a.proxy(this.pause,this)).on("mouseleave.bs.carousel",a.proxy(this.cycle,this))};c.VERSION="3.3.5",c.TRANSITION_DURATION=600,c.DEFAULTS={interval:5e3,pause:"hover",wrap:!0,keyboard:!0},c.prototype.keydown=function(a){if(!/input|textarea/i.test(a.target.tagName)){switch(a.which){case 37:this.prev();break;case 39:this.next();break;default:return}a.preventDefault()}},c.prototype.cycle=function(b){return b||(this.paused=!1),this.interval&&clearInterval(this.interval),this.options.interval&&!this.paused&&(this.interval=setInterval(a.proxy(this.next,this),this.options.interval)),this},c.prototype.getItemIndex=function(a){return this.$items=a.parent().children(".item"),this.$items.index(a||this.$active)},c.prototype.getItemForDirection=function(a,b){var c=this.getItemIndex(b),d="prev"==a&&0===c||"next"==a&&c==this.$items.length-1;if(d&&!this.options.wrap)return b;var e="prev"==a?-1:1,f=(c+e)%this.$items.length;return this.$items.eq(f)},c.prototype.to=function(a){var b=this,c=this.getItemIndex(this.$active=this.$element.find(".item.active"));return a>this.$items.length-1||0>a?void 0:this.sliding?this.$element.one("slid.bs.carousel",function(){b.to(a)}):c==a?this.pause().cycle():this.slide(a>c?"next":"prev",this.$items.eq(a))},c.prototype.pause=function(b){return b||(this.paused=!0),this.$element.find(".next, .prev").length&&a.support.transition&&(this.$element.trigger(a.support.transition.end),this.cycle(!0)),this.interval=clearInterval(this.interval),this},c.prototype.next=function(){return this.sliding?void 0:this.slide("next")},c.prototype.prev=function(){return this.sliding?void 0:this.slide("prev")},c.prototype.slide=function(b,d){var e=this.$element.find(".item.active"),f=d||this.getItemForDirection(b,e),g=this.interval,h="next"==b?"left":"right",i=this;if(f.hasClass("active"))return this.sliding=!1;var j=f[0],k=a.Event("slide.bs.carousel",{relatedTarget:j,direction:h});if(this.$element.trigger(k),!k.isDefaultPrevented()){if(this.sliding=!0,g&&this.pause(),this.$indicators.length){this.$indicators.find(".active").removeClass("active");var l=a(this.$indicators.children()[this.getItemIndex(f)]);l&&l.addClass("active")}var m=a.Event("slid.bs.carousel",{relatedTarget:j,direction:h});return a.support.transition&&this.$element.hasClass("slide")?(f.addClass(b),f[0].offsetWidth,e.addClass(h),f.addClass(h),e.one("bsTransitionEnd",function(){f.removeClass([b,h].join(" ")).addClass("active"),e.removeClass(["active",h].join(" ")),i.sliding=!1,setTimeout(function(){i.$element.trigger(m)},0)}).emulateTransitionEnd(c.TRANSITION_DURATION)):(e.removeClass("active"),f.addClass("active"),this.sliding=!1,this.$element.trigger(m)),g&&this.cycle(),this}};var d=a.fn.carousel;a.fn.carousel=b,a.fn.carousel.Constructor=c,a.fn.carousel.noConflict=function(){return a.fn.carousel=d,this};var e=function(c){var d,e=a(this),f=a(e.attr("data-target")||(d=e.attr("href"))&&d.replace(/.*(?=#[^\s]+$)/,""));if(f.hasClass("carousel")){var g=a.extend({},f.data(),e.data()),h=e.attr("data-slide-to");h&&(g.interval=!1),b.call(f,g),h&&f.data("bs.carousel").to(h),c.preventDefault()}};a(document).on("click.bs.carousel.data-api","[data-slide]",e).on("click.bs.carousel.data-api","[data-slide-to]",e),a(window).on("load",function(){a('[data-ride="carousel"]').each(function(){var c=a(this);b.call(c,c.data())})})}(jQuery),+function(a){"use strict";function b(b){var c,d=b.attr("data-target")||(c=b.attr("href"))&&c.replace(/.*(?=#[^\s]+$)/,"");return a(d)}function c(b){return this.each(function(){var c=a(this),e=c.data("bs.collapse"),f=a.extend({},d.DEFAULTS,c.data(),"object"==typeof b&&b);!e&&f.toggle&&/show|hide/.test(b)&&(f.toggle=!1),e||c.data("bs.collapse",e=new d(this,f)),"string"==typeof b&&e[b]()})}var d=function(b,c){this.$element=a(b),this.options=a.extend({},d.DEFAULTS,c),this.$trigger=a('[data-toggle="collapse"][href="#'+b.id+'"],[data-toggle="collapse"][data-target="#'+b.id+'"]'),this.transitioning=null,this.options.parent?this.$parent=this.getParent():this.addAriaAndCollapsedClass(this.$element,this.$trigger),this.options.toggle&&this.toggle()};d.VERSION="3.3.5",d.TRANSITION_DURATION=350,d.DEFAULTS={toggle:!0},d.prototype.dimension=function(){var a=this.$element.hasClass("width");return a?"width":"height"},d.prototype.show=function(){if(!this.transitioning&&!this.$element.hasClass("in")){var b,e=this.$parent&&this.$parent.children(".panel").children(".in, .collapsing");if(!(e&&e.length&&(b=e.data("bs.collapse"),b&&b.transitioning))){var f=a.Event("show.bs.collapse");if(this.$element.trigger(f),!f.isDefaultPrevented()){e&&e.length&&(c.call(e,"hide"),b||e.data("bs.collapse",null));var g=this.dimension();this.$element.removeClass("collapse").addClass("collapsing")[g](0).attr("aria-expanded",!0),this.$trigger.removeClass("collapsed").attr("aria-expanded",!0),this.transitioning=1;var h=function(){this.$element.removeClass("collapsing").addClass("collapse in")[g](""),this.transitioning=0,this.$element.trigger("shown.bs.collapse")};if(!a.support.transition)return h.call(this);var i=a.camelCase(["scroll",g].join("-"));this.$element.one("bsTransitionEnd",a.proxy(h,this)).emulateTransitionEnd(d.TRANSITION_DURATION)[g](this.$element[0][i])}}}},d.prototype.hide=function(){if(!this.transitioning&&this.$element.hasClass("in")){var b=a.Event("hide.bs.collapse");if(this.$element.trigger(b),!b.isDefaultPrevented()){var c=this.dimension();this.$element[c](this.$element[c]())[0].offsetHeight,this.$element.addClass("collapsing").removeClass("collapse in").attr("aria-expanded",!1),this.$trigger.addClass("collapsed").attr("aria-expanded",!1),this.transitioning=1;var e=function(){this.transitioning=0,this.$element.removeClass("collapsing").addClass("collapse").trigger("hidden.bs.collapse")};return a.support.transition?void this.$element[c](0).one("bsTransitionEnd",a.proxy(e,this)).emulateTransitionEnd(d.TRANSITION_DURATION):e.call(this)}}},d.prototype.toggle=function(){this[this.$element.hasClass("in")?"hide":"show"]()},d.prototype.getParent=function(){return a(this.options.parent).find('[data-toggle="collapse"][data-parent="'+this.options.parent+'"]').each(a.proxy(function(c,d){var e=a(d);this.addAriaAndCollapsedClass(b(e),e)},this)).end()},d.prototype.addAriaAndCollapsedClass=function(a,b){var c=a.hasClass("in");a.attr("aria-expanded",c),b.toggleClass("collapsed",!c).attr("aria-expanded",c)};var e=a.fn.collapse;a.fn.collapse=c,a.fn.collapse.Constructor=d,a.fn.collapse.noConflict=function(){return a.fn.collapse=e,this},a(document).on("click.bs.collapse.data-api",'[data-toggle="collapse"]',function(d){var e=a(this);e.attr("data-target")||d.preventDefault();var f=b(e),g=f.data("bs.collapse"),h=g?"toggle":e.data();c.call(f,h)})}(jQuery),+function(a){"use strict";function b(b){var c=b.attr("data-target");c||(c=b.attr("href"),c=c&&/#[A-Za-z]/.test(c)&&c.replace(/.*(?=#[^\s]*$)/,""));var d=c&&a(c);return d&&d.length?d:b.parent()}function c(c){c&&3===c.which||(a(e).remove(),a(f).each(function(){var d=a(this),e=b(d),f={relatedTarget:this};e.hasClass("open")&&(c&&"click"==c.type&&/input|textarea/i.test(c.target.tagName)&&a.contains(e[0],c.target)||(e.trigger(c=a.Event("hide.bs.dropdown",f)),c.isDefaultPrevented()||(d.attr("aria-expanded","false"),e.removeClass("open").trigger("hidden.bs.dropdown",f))))}))}function d(b){return this.each(function(){var c=a(this),d=c.data("bs.dropdown");d||c.data("bs.dropdown",d=new g(this)),"string"==typeof b&&d[b].call(c)})}var e=".dropdown-backdrop",f='[data-toggle="dropdown"]',g=function(b){a(b).on("click.bs.dropdown",this.toggle)};g.VERSION="3.3.5",g.prototype.toggle=function(d){var e=a(this);if(!e.is(".disabled, :disabled")){var f=b(e),g=f.hasClass("open");if(c(),!g){"ontouchstart"in document.documentElement&&!f.closest(".navbar-nav").length&&a(document.createElement("div")).addClass("dropdown-backdrop").insertAfter(a(this)).on("click",c);var h={relatedTarget:this};if(f.trigger(d=a.Event("show.bs.dropdown",h)),d.isDefaultPrevented())return;e.trigger("focus").attr("aria-expanded","true"),f.toggleClass("open").trigger("shown.bs.dropdown",h)}return!1}},g.prototype.keydown=function(c){if(/(38|40|27|32)/.test(c.which)&&!/input|textarea/i.test(c.target.tagName)){var d=a(this);if(c.preventDefault(),c.stopPropagation(),!d.is(".disabled, :disabled")){var e=b(d),g=e.hasClass("open");if(!g&&27!=c.which||g&&27==c.which)return 27==c.which&&e.find(f).trigger("focus"),d.trigger("click");var h=" li:not(.disabled):visible a",i=e.find(".dropdown-menu"+h);if(i.length){var j=i.index(c.target);38==c.which&&j>0&&j--,40==c.which&&jdocument.documentElement.clientHeight;this.$element.css({paddingLeft:!this.bodyIsOverflowing&&a?this.scrollbarWidth:"",paddingRight:this.bodyIsOverflowing&&!a?this.scrollbarWidth:""})},c.prototype.resetAdjustments=function(){this.$element.css({paddingLeft:"",paddingRight:""})},c.prototype.checkScrollbar=function(){var a=window.innerWidth;if(!a){var b=document.documentElement.getBoundingClientRect();a=b.right-Math.abs(b.left)}this.bodyIsOverflowing=document.body.clientWidth
',trigger:"hover focus",title:"",delay:0,html:!1,container:!1,viewport:{selector:"body",padding:0}},c.prototype.init=function(b,c,d){if(this.enabled=!0,this.type=b,this.$element=a(c),this.options=this.getOptions(d),this.$viewport=this.options.viewport&&a(a.isFunction(this.options.viewport)?this.options.viewport.call(this,this.$element):this.options.viewport.selector||this.options.viewport),this.inState={click:!1,hover:!1,focus:!1},this.$element[0]instanceof document.constructor&&!this.options.selector)throw new Error("`selector` option must be specified when initializing "+this.type+" on the window.document object!");for(var e=this.options.trigger.split(" "),f=e.length;f--;){var g=e[f];if("click"==g)this.$element.on("click."+this.type,this.options.selector,a.proxy(this.toggle,this));else if("manual"!=g){var h="hover"==g?"mouseenter":"focusin",i="hover"==g?"mouseleave":"focusout";this.$element.on(h+"."+this.type,this.options.selector,a.proxy(this.enter,this)),this.$element.on(i+"."+this.type,this.options.selector,a.proxy(this.leave,this))}}this.options.selector?this._options=a.extend({},this.options,{trigger:"manual",selector:""}):this.fixTitle()},c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.getOptions=function(b){return b=a.extend({},this.getDefaults(),this.$element.data(),b),b.delay&&"number"==typeof b.delay&&(b.delay={show:b.delay,hide:b.delay}),b},c.prototype.getDelegateOptions=function(){var b={},c=this.getDefaults();return this._options&&a.each(this._options,function(a,d){c[a]!=d&&(b[a]=d)}),b},c.prototype.enter=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusin"==b.type?"focus":"hover"]=!0),c.tip().hasClass("in")||"in"==c.hoverState?void(c.hoverState="in"):(clearTimeout(c.timeout),c.hoverState="in",c.options.delay&&c.options.delay.show?void(c.timeout=setTimeout(function(){"in"==c.hoverState&&c.show()},c.options.delay.show)):c.show())},c.prototype.isInStateTrue=function(){for(var a in this.inState)if(this.inState[a])return!0;return!1},c.prototype.leave=function(b){var c=b instanceof this.constructor?b:a(b.currentTarget).data("bs."+this.type);return c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c)),b instanceof a.Event&&(c.inState["focusout"==b.type?"focus":"hover"]=!1),c.isInStateTrue()?void 0:(clearTimeout(c.timeout),c.hoverState="out",c.options.delay&&c.options.delay.hide?void(c.timeout=setTimeout(function(){"out"==c.hoverState&&c.hide()},c.options.delay.hide)):c.hide())},c.prototype.show=function(){var b=a.Event("show.bs."+this.type);if(this.hasContent()&&this.enabled){this.$element.trigger(b);var d=a.contains(this.$element[0].ownerDocument.documentElement,this.$element[0]);if(b.isDefaultPrevented()||!d)return;var e=this,f=this.tip(),g=this.getUID(this.type);this.setContent(),f.attr("id",g),this.$element.attr("aria-describedby",g),this.options.animation&&f.addClass("fade");var h="function"==typeof this.options.placement?this.options.placement.call(this,f[0],this.$element[0]):this.options.placement,i=/\s?auto?\s?/i,j=i.test(h);j&&(h=h.replace(i,"")||"top"),f.detach().css({top:0,left:0,display:"block"}).addClass(h).data("bs."+this.type,this),this.options.container?f.appendTo(this.options.container):f.insertAfter(this.$element),this.$element.trigger("inserted.bs."+this.type);var k=this.getPosition(),l=f[0].offsetWidth,m=f[0].offsetHeight;if(j){var n=h,o=this.getPosition(this.$viewport);h="bottom"==h&&k.bottom+m>o.bottom?"top":"top"==h&&k.top-mo.width?"left":"left"==h&&k.left-lg.top+g.height&&(e.top=g.top+g.height-i)}else{var j=b.left-f,k=b.left+f+c;jg.right&&(e.left=g.left+g.width-k)}return e},c.prototype.getTitle=function(){var a,b=this.$element,c=this.options;return a=b.attr("data-original-title")||("function"==typeof c.title?c.title.call(b[0]):c.title)},c.prototype.getUID=function(a){do a+=~~(1e6*Math.random());while(document.getElementById(a));return a},c.prototype.tip=function(){if(!this.$tip&&(this.$tip=a(this.options.template),1!=this.$tip.length))throw new Error(this.type+" `template` option must consist of exactly 1 top-level element!");return this.$tip},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".tooltip-arrow")},c.prototype.enable=function(){this.enabled=!0},c.prototype.disable=function(){this.enabled=!1},c.prototype.toggleEnabled=function(){this.enabled=!this.enabled},c.prototype.toggle=function(b){var c=this;b&&(c=a(b.currentTarget).data("bs."+this.type),c||(c=new this.constructor(b.currentTarget,this.getDelegateOptions()),a(b.currentTarget).data("bs."+this.type,c))),b?(c.inState.click=!c.inState.click,c.isInStateTrue()?c.enter(c):c.leave(c)):c.tip().hasClass("in")?c.leave(c):c.enter(c)},c.prototype.destroy=function(){var a=this;clearTimeout(this.timeout),this.hide(function(){a.$element.off("."+a.type).removeData("bs."+a.type),a.$tip&&a.$tip.detach(),a.$tip=null,a.$arrow=null,a.$viewport=null})};var d=a.fn.tooltip;a.fn.tooltip=b,a.fn.tooltip.Constructor=c,a.fn.tooltip.noConflict=function(){return a.fn.tooltip=d,this}}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.popover"),f="object"==typeof b&&b;(e||!/destroy|hide/.test(b))&&(e||d.data("bs.popover",e=new c(this,f)),"string"==typeof b&&e[b]())})}var c=function(a,b){this.init("popover",a,b)};if(!a.fn.tooltip)throw new Error("Popover requires tooltip.js");c.VERSION="3.3.5",c.DEFAULTS=a.extend({},a.fn.tooltip.Constructor.DEFAULTS,{placement:"right",trigger:"click",content:"",template:''}),c.prototype=a.extend({},a.fn.tooltip.Constructor.prototype),c.prototype.constructor=c,c.prototype.getDefaults=function(){return c.DEFAULTS},c.prototype.setContent=function(){var a=this.tip(),b=this.getTitle(),c=this.getContent();a.find(".popover-title")[this.options.html?"html":"text"](b),a.find(".popover-content").children().detach().end()[this.options.html?"string"==typeof c?"html":"append":"text"](c),a.removeClass("fade top bottom left right in"),a.find(".popover-title").html()||a.find(".popover-title").hide()},c.prototype.hasContent=function(){return this.getTitle()||this.getContent()},c.prototype.getContent=function(){var a=this.$element,b=this.options;return a.attr("data-content")||("function"==typeof b.content?b.content.call(a[0]):b.content)},c.prototype.arrow=function(){return this.$arrow=this.$arrow||this.tip().find(".arrow")};var d=a.fn.popover;a.fn.popover=b,a.fn.popover.Constructor=c,a.fn.popover.noConflict=function(){return a.fn.popover=d,this}}(jQuery),+function(a){"use strict";function b(c,d){this.$body=a(document.body),this.$scrollElement=a(a(c).is(document.body)?window:c),this.options=a.extend({},b.DEFAULTS,d),this.selector=(this.options.target||"")+" .nav li > a",this.offsets=[],this.targets=[],this.activeTarget=null,this.scrollHeight=0,this.$scrollElement.on("scroll.bs.scrollspy",a.proxy(this.process,this)),this.refresh(),this.process()}function c(c){return this.each(function(){var d=a(this),e=d.data("bs.scrollspy"),f="object"==typeof c&&c;e||d.data("bs.scrollspy",e=new b(this,f)),"string"==typeof c&&e[c]()})}b.VERSION="3.3.5",b.DEFAULTS={offset:10},b.prototype.getScrollHeight=function(){return this.$scrollElement[0].scrollHeight||Math.max(this.$body[0].scrollHeight,document.documentElement.scrollHeight)},b.prototype.refresh=function(){var b=this,c="offset",d=0;this.offsets=[],this.targets=[],this.scrollHeight=this.getScrollHeight(),a.isWindow(this.$scrollElement[0])||(c="position",d=this.$scrollElement.scrollTop()),this.$body.find(this.selector).map(function(){var b=a(this),e=b.data("target")||b.attr("href"),f=/^#./.test(e)&&a(e);return f&&f.length&&f.is(":visible")&&[[f[c]().top+d,e]]||null}).sort(function(a,b){return a[0]-b[0]}).each(function(){b.offsets.push(this[0]),b.targets.push(this[1])})},b.prototype.process=function(){var a,b=this.$scrollElement.scrollTop()+this.options.offset,c=this.getScrollHeight(),d=this.options.offset+c-this.$scrollElement.height(),e=this.offsets,f=this.targets,g=this.activeTarget;if(this.scrollHeight!=c&&this.refresh(),b>=d)return g!=(a=f[f.length-1])&&this.activate(a);if(g&&b=e[a]&&(void 0===e[a+1]||b .dropdown-menu > .active").removeClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!1),b.addClass("active").find('[data-toggle="tab"]').attr("aria-expanded",!0),h?(b[0].offsetWidth,b.addClass("in")):b.removeClass("fade"),b.parent(".dropdown-menu").length&&b.closest("li.dropdown").addClass("active").end().find('[data-toggle="tab"]').attr("aria-expanded",!0),e&&e()}var g=d.find("> .active"),h=e&&a.support.transition&&(g.length&&g.hasClass("fade")||!!d.find("> .fade").length);g.length&&h?g.one("bsTransitionEnd",f).emulateTransitionEnd(c.TRANSITION_DURATION):f(),g.removeClass("in")};var d=a.fn.tab;a.fn.tab=b,a.fn.tab.Constructor=c,a.fn.tab.noConflict=function(){return a.fn.tab=d,this};var e=function(c){c.preventDefault(),b.call(a(this),"show")};a(document).on("click.bs.tab.data-api",'[data-toggle="tab"]',e).on("click.bs.tab.data-api",'[data-toggle="pill"]',e)}(jQuery),+function(a){"use strict";function b(b){return this.each(function(){var d=a(this),e=d.data("bs.affix"),f="object"==typeof b&&b;e||d.data("bs.affix",e=new c(this,f)),"string"==typeof b&&e[b]()})}var c=function(b,d){this.options=a.extend({},c.DEFAULTS,d),this.$target=a(this.options.target).on("scroll.bs.affix.data-api",a.proxy(this.checkPosition,this)).on("click.bs.affix.data-api",a.proxy(this.checkPositionWithEventLoop,this)),this.$element=a(b),this.affixed=null,this.unpin=null,this.pinnedOffset=null,this.checkPosition()};c.VERSION="3.3.5",c.RESET="affix affix-top affix-bottom",c.DEFAULTS={offset:0,target:window},c.prototype.getState=function(a,b,c,d){var e=this.$target.scrollTop(),f=this.$element.offset(),g=this.$target.height();if(null!=c&&"top"==this.affixed)return c>e?"top":!1;if("bottom"==this.affixed)return null!=c?e+this.unpin<=f.top?!1:"bottom":a-d>=e+g?!1:"bottom";var h=null==this.affixed,i=h?e:f.top,j=h?g:b;return null!=c&&c>=e?"top":null!=d&&i+j>=a-d?"bottom":!1},c.prototype.getPinnedOffset=function(){if(this.pinnedOffset)return this.pinnedOffset;this.$element.removeClass(c.RESET).addClass("affix");var a=this.$target.scrollTop(),b=this.$element.offset();return this.pinnedOffset=b.top-a},c.prototype.checkPositionWithEventLoop=function(){setTimeout(a.proxy(this.checkPosition,this),1)},c.prototype.checkPosition=function(){if(this.$element.is(":visible")){var b=this.$element.height(),d=this.options.offset,e=d.top,f=d.bottom,g=Math.max(a(document).height(),a(document.body).height());"object"!=typeof d&&(f=e=d),"function"==typeof e&&(e=d.top(this.$element)),"function"==typeof f&&(f=d.bottom(this.$element));var h=this.getState(g,b,e,f);if(this.affixed!=h){null!=this.unpin&&this.$element.css("top","");var i="affix"+(h?"-"+h:""),j=a.Event(i+".bs.affix");if(this.$element.trigger(j),j.isDefaultPrevented())return;this.affixed=h,this.unpin="bottom"==h?this.getPinnedOffset():null,this.$element.removeClass(c.RESET).addClass(i).trigger(i.replace("affix","affixed")+".bs.affix")}"bottom"==h&&this.$element.offset({top:g-b-f})}};var d=a.fn.affix;a.fn.affix=b,a.fn.affix.Constructor=c,a.fn.affix.noConflict=function(){return a.fn.affix=d,this},a(window).on("load",function(){a('[data-spy="affix"]').each(function(){var c=a(this),d=c.data();d.offset=d.offset||{},null!=d.offsetBottom&&(d.offset.bottom=d.offsetBottom),null!=d.offsetTop&&(d.offset.top=d.offsetTop),b.call(c,d)})})}(jQuery); --------------------------------------------------------------------------------