var NUT = { // UPS table DOM ids listID: "#ups_list", listBodyID: "#ups_list_body", // Field names fields: [ "manufacturer", "device-type", "support-level", "model", "comment", "driver" ], // Actual HTML table columns columns: [ [ "manufacturer" ], [ "model", "comment" ], [ "driver" ], [ "support-level" ], ], // driver => connection type mappings driverMap: function(driver) { // Try not to catch "usb" across a word boundary: if (driver.match(/_usb|usbserial|usbhid-ups/)) return "USB"; // TODO: what about IPMI/powerman-pdu? if (driver.match(/snmp-ups|netxml-ups/)) return "Network"; return "Serial"; }, // Support level => CSS class mappings supportLevelClasses: { 0: "", 1: "red", 2: "orange", 3: "yellow", 4: "blue", 5: "green" }, tableCache: false, // Parse GET parameters from window url and return them as a hash // The call format is: // stable-hcl.html?= // Refer to docs/stable-hcl.txt for examples parseGetParameters: function() { var url = window.location.href; url = url.replace(/#$/, ""); var fieldPos = url.indexOf("?"); var get = {}; if (fieldPos > -1) { var fileName = url.substring(0, fieldPos); var getList = url.substring(fieldPos + 1).split("&"); for (var i = 0; i < getList.length; i++) { var getField = getList[i].split("="); key = unescape(getField[0]).toLowerCase(); value = unescape(getField[1]); // just in case there are any old links out there. if (key === "manufacturer" && value === "Tripp-Lite") { value = "Tripp Lite"; } get[key] = value; } } return get; }, // UPS filter renderers by data column index filterRenderers: { "support-level": function(value) { var result = []; for (var i = 0; i < value; i++) result.push("*"); return result.join(""); }, "driver": function(value) { return this.driverMap(value); }, "device-type": function(value) { var map = { "pdu": "Power Distribution Unit", "ups": "Uninterruptible Power Supply", "scd": "Solar Controller Device" } return map[value]; } }, // Specific filter handlers filterHandlers: { // Filter functions: // @param {string} value value to filter // @param {array} row raw data fields // @return {boolean} true if value passes the filter, false otherwise "driver": function(value, row) { var driver = row[this.fields.indexOf("driver")]; if (this.driverMap(driver) == value) return true; return false; }, "support-level": function(value, row) { var level = row[this.fields.indexOf("support-level")]; if (level >= value) return true; return false; } }, // Returns rendered UPS data according to column index // @param {integer} index // @param {string} value renderFilter: function(index, value) { var renderer = this.filterRenderers[this.fields[index]]; if (typeof renderer == "function") return renderer.call(this, value); return value; }, // Initialization method init: function() { this.initFilters(); this.sortUPSData(UPSData); this.buildUPSList(UPSData); this.buildFilters(UPSData); var get = this.parseGetParameters(); for (var param in get) { var filter = $("#"+param); if (filter) { filter.val(get[param]); this.doFilter(); } } }, // Initialize filter filters references initFilters: function() { // Display filters fieldset hidden by default for user-agents not using javascript $("#filters-set").show(); this.filters = { "support-level": { index: this.fields.indexOf("support-level"), field: $("#support-level") }, "device-type": { index: this.fields.indexOf("device-type"), field: $("#device-type") }, "manufacturer": { index: this.fields.indexOf("manufacturer"), field: $("#manufacturer") }, "model": { index: this.fields.indexOf("model"), field: $("#model") }, "driver": { index: this.fields.indexOf("driver"), field: $("#connection") } } }, // Sorts table data by manufacturer and driver // @param {Object} data sortUPSData: function(data) { var mI = this.fields.indexOf("manufacturer"), dI = this.fields.indexOf("driver"); data.sort(function(a, b) { var toLower = function(ar) { var res = ar.slice(); res.forEach(function(i, index) { if (typeof i == "string") res[index] = i.toLowerCase() }); return res; } var c = toLower(a), d = toLower(b); return c[mI] == d[mI] ? c[dI] > d[dI] : c[mI] > d[mI]; }); }, // Builds UPS list from provided data // @param {array} data buildUPSList: function(data) { var list = $(this.listBodyID); // Initialize table cache if (!this.tableCache) this.tableCache = list.html(); // If we're rebuilding the original table, just use the one in cache if (data == UPSData && this.tableCache) { list.html(this.tableCache); return; } list.empty(); // Bailout if no data if (!data || data.length == 0) return; // Build rows var cellHistory = [], rows = []; var rowHistory = data[0][0]; var classes = [ "even", "odd" ], manufIndex = this.fields.indexOf("manufacturer"), currentClass = 0; data.forEach(function(upsRow, rowIndex) { if (upsRow[manufIndex] != rowHistory) { currentClass = Number(!currentClass); rowHistory = upsRow[manufIndex]; } var cells = []; // Build cells this.columns.forEach(function(column, colIndex) { var cellContent = []; column.forEach(function(field) { cellContent.push(upsRow[this.fields.indexOf(field)]) }, this); cellContent = cellContent.join("
"); // Inspect the last cell on this column and increase row span if the current cell has the same content var cH = cellHistory[colIndex]; if (column.indexOf("driver") == -1 && cH && cH.html == cellContent) cH.rowSpan += 1; else { var cell = ""; if (column.indexOf("driver") != -1) { cell = { html: cellContent, rowSpan: 1, cls: this.supportLevelClasses[upsRow[this.fields.indexOf("support-level") || ""]] }; } else { cell = { html: cellContent, rowSpan: 1, cls: classes[currentClass] } } if (column.indexOf("support-level") != -1) cell.cls += " hidden"; cells.push(cell); cellHistory[colIndex] = cell; } }, this); rows.push(cells); }, this); // Generate actual rows/cells tags rows.forEach(function(r, index) { r.forEach(function(c, index) { r[index] = [ "", c.html, "" ].join(""); }); rows[index] = [ "", r.join(""), "" ].join(""); }); list.html(rows.join("")); }, // Initialize filters event listeners // @param {Object} data buildFilters: function(data) { for (var f in this.filters) { var filter = this.filters[f]; this.populateCombo(data, filter); filter.field.change(this.doFilter); var op = $("#op-" + (filter.index)); if (op) op.change(this.doFilter); } }, // Load data in filter combos // @param {array} data // @param {object} combo // @param {integer} index populateCombo: function(data, filter) { var values = []; var valueDict = {}; var combo = filter.field; var oldValue = combo.val(); combo.html(""); // Special case for connection type if (filter.field.attr("id") == "connection") { [ "Serial", "USB", "Network" ].forEach(function(type) { values.push([ type, type ]); }, this); } else { data.forEach(function(row) { var value = row[filter.index]; if (value != "" && !valueDict[value]) { values.push([ value, this.renderFilter(filter.index, value) ]); valueDict[value] = true; } }, this); values = values.sort(); } values.forEach(function(value) { var option = $(document.createElement("option")); option.val(value[0]); option.text(value[1]); combo.append(option); }, this); combo.val(oldValue); }, // Apply selected filters on UPS list doFilter: function() { var initialRows = UPSData.slice(); var filteredRows = UPSData.slice(); // Applies a single filter on provided UPS data set // @param {string} value // @param {integer} index // @param {array} data // @returns {array} filtered data set var applyFilter = function(value, index, data) { var tmpData = []; tmpData = data.slice(); tmpData.forEach(function(row, rowIndex) { var field = row[index]; var handler = this.filterHandlers[this.fields[index]]; if (handler) { if (!handler.apply(this, [ value, row ])) data.splice(data.indexOf(row), 1); } else if (row[index] != value) data.splice(data.indexOf(row), 1); }, this); return data; } // Sequentially apply filters for (var f in NUT.filters) { var filter = NUT.filters[f]; var value = filter.field.val(); // Is filter active, i.e have the user picked a value in the filter combo if (value != "-1") { var opField = $("#op-" + filter.index); filteredRows = applyFilter.apply(NUT, [ value, filter.index, filteredRows ]); } } // Rebuild UPS list and combos according to filtered data NUT.buildUPSList(filteredRows); [ "manufacturer", "model", "driver" ].forEach(function(id) { if (this.id != id) this.populateCombo(filteredRows, this.filters[id]); }, NUT); }, resetCombos: function() { for (var f in this.filters) { var field = this.filters[f].field; this.populateCombo(UPSData, this.filters[f]); field.val("-1"); } this.buildUPSList(UPSData); } } if (typeof Array.prototype.indexOf != "function") { Array.prototype.indexOf = function(elt) { var i = 0; while (i < this.length) { if (this[i] == elt) return i; i++; } return -1; } } if (typeof Array.prototype.forEach != "function") { Array.prototype.forEach = function(cb, scope) { for (var i = 0, n = this.length; i < n; i++) { if (i in this) cb.call(scope, this[i], i, this); } } } // Global initialization $(function() { NUT.init.call(NUT); });