[luci] [PATCH] [feature add] NCM support for luci

Oskari Rauta oskari.rauta at gmail.com
Thu Nov 28 18:27:26 CET 2013


Hi. I created ncm protocol support for openwrt and LuCI and would like to share my work as patch against openwrt's luci's git tree.

What is NCM? NCM is a kernel module for usb 3g/lte dongles that no longer require ppp, ppp is used internally and when device is "switched" it
reveals a true netifd. Then device is controlled over somehow, over a terminal with AT-commands, or with web browser (HiLink devices). NCM was
supported in openwrt as a kernel module, but there was no support for it's usage, now there is; thanks to this patch: http://patchwork.openwrt.org/patch/4477/

That patch allows NCM to be used directly from /etc/config/network configuration. I also provided driver/at-command set support for Huawei E3267 LTE-dongle, as
I don't have other dongles, this is the first to be supported, but no worries, I made the system modular, so it's pretty easy to add support for other devices as well.
Base package supports: ncm protocol and device identification procedure
This AT command set contains following: device specific scripts for: initialization, carrier information, cell & signal information, connection initiation, mode setting.

So maybe I should stop rambling about patch that exists in a completely different tree and tell what is this patch then about?
This patch adds 2 packages:
1) support for ncm protocol in LuCI. Easy/fast/simple to setup ncm protocol with your web browser.
2) status view for dongle providing information about: hardware, carrier, cell and signal.

So it's a perfect companion for that other package, but belongs to LuCI instead of openwrt's normal package tree. Enjoy.

Patch starts:
diff --git a/modules/ncm-luci/Makefile b/modules/ncm-luci/Makefile
new file mode 100644
index 0000000..688a74d
--- /dev/null
+++ b/modules/ncm-luci/Makefile
@@ -0,0 +1,93 @@
+#
+# Copyright (C) 2007-2013 OpenWrt.org
+# Copyright (C) 2010 Vertical Communications
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=luci-proto-ncm
+PKG_VERSION:=1.0
+PKG_RELEASE:=1
+PKG_MAINTAINER:=Oskari Rauta <oskari.rauta at gmail.com>
+PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)-$(PKG_RELEASE)
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/luci-proto-ncm/Default
+  VERSION:=$(PKG_VERSION)-$(PKG_RELEASE)
+  URL:=http://openwrt.org/
+  MAINTAINER:=Oskari Rauta <oskari.rauta at gmail.com>
+endef
+
+define Package/luci-proto-ncm
+$(call Package/luci-proto-ncm/Default)
+  SECTION:=luci
+  CATEGORY:=LuCI
+  SUBMENU:=6. Protocols
+  TITLE:=Support for NCM
+  DEPENDS:=+ncm
+endef
+
+define Package/luci-proto-ncm/description
+ This package contains LuCI support for NCM
+endef
+
+define Package/luci-mod-ncm-status
+$(call Package/luci-proto-ncm/Default)
+  SECTION:=luci
+  CATEGORY:=LuCI
+  SUBMENU:=2. Modules
+  TITLE:=LuCI NCM Status Module
+  DEPENDS:=+luci-mod-admin-core +ncm +comgt +kmod-usb-serial
+endef
+
+define Package/luci-mod-ncm-status/description
+ LuCI NCM Status Module
+endef
+
+define Build/Prepare
+	mkdir -p $(PKG_BUILD_DIR)
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile/Default
+endef
+
+Build/Compile = $(Build/Compile/Default)
+
+define Package/luci-proto-ncm/install
+	$(INSTALL_DIR) $(1)/usr
+	$(INSTALL_DIR) $(1)/usr/lib
+	$(INSTALL_DIR) $(1)/usr/lib/lua
+	$(INSTALL_DIR) $(1)/usr/lib/lua/luci
+	$(INSTALL_DIR) $(1)/usr/lib/lua/luci/model
+	$(INSTALL_DIR) $(1)/usr/lib/lua/luci/model/network
+	$(INSTALL_DIR) $(1)/usr/lib/lua/luci/model/cbi
+	$(INSTALL_DIR) $(1)/usr/lib/lua/luci/model/cbi/admin_network
+	$(INSTALL_DATA) ./files/usr/lib/lua/luci/model/network/proto_ncm.lua $(1)/usr/lib/lua/luci/model/network/
+	$(INSTALL_DATA) ./files/usr/lib/lua/luci/model/cbi/admin_network/proto_ncm.lua $(1)/usr/lib/lua/luci/model/cbi/admin_network/
+endef
+
+define Package/luci-mod-ncm-status/install
+	$(INSTALL_DIR) $(1)/usr
+	$(INSTALL_DIR) $(1)/usr/lib
+	$(INSTALL_DIR) $(1)/usr/lib/lua
+	$(INSTALL_DIR) $(1)/usr/lib/lua/luci
+	$(INSTALL_DIR) $(1)/usr/lib/lua/luci/view
+	$(INSTALL_DIR) $(1)/usr/lib/lua/luci/view/ncm
+	$(INSTALL_DIR) $(1)/usr/lib/lua/luci/controller
+	$(INSTALL_DIR) $(1)/www
+	$(INSTALL_DIR) $(1)/www/luci-static
+	$(INSTALL_DIR) $(1)/www/luci-static/resources
+	$(INSTALL_DATA) ./files/usr/lib/lua/luci/view/ncm/status.htm $(1)/usr/lib/lua/luci/view/ncm/
+	$(INSTALL_DATA) ./files/usr/lib/lua/luci/controller/ncmstatus.lua $(1)/usr/lib/lua/luci/controller/
+	$(INSTALL_DATA) ./files/www/luci-static/resources/ncm_xhr.js $(1)/www/luci-static/resources/
+endef
+
+$(eval $(call BuildPackage,luci-proto-ncm))
+$(eval $(call BuildPackage,luci-mod-ncm-status))
diff --git a/modules/ncm-luci/files/usr/lib/lua/luci/controller/ncmstatus.lua b/modules/ncm-luci/files/usr/lib/lua/luci/controller/ncmstatus.lua
new file mode 100755
index 0000000..cfb6b20
--- /dev/null
+++ b/modules/ncm-luci/files/usr/lib/lua/luci/controller/ncmstatus.lua
@@ -0,0 +1,8 @@
+module("luci.controller.ncmstatus", package.seeall)
+
+function index()
+	local page
+
+	page = entry({"admin", "status", "ncm"}, template("ncm/status"), _("Ncm status"))
+	page.dependent = true
+end
diff --git a/modules/ncm-luci/files/usr/lib/lua/luci/model/cbi/admin_network/proto_ncm.lua b/modules/ncm-luci/files/usr/lib/lua/luci/model/cbi/admin_network/proto_ncm.lua
new file mode 100644
index 0000000..dfaa02e
--- /dev/null
+++ b/modules/ncm-luci/files/usr/lib/lua/luci/model/cbi/admin_network/proto_ncm.lua
@@ -0,0 +1,120 @@
+--[[
+LuCI - Lua Configuration Interface
+
+Copyright 2011 Jo-Philipp Wich <xm at subsignal.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+]]--
+
+local map, section, net = ...
+
+local device, apn, mode, pincode, authtype, username, password
+local delay, bcast, defaultroute, peerdns, dns, metric, clientid, vendorclass
+
+
+device = section:taboption("general", Value, "device", translate("Modem device"))
+device.rmempty = false
+
+local device_suggestions = nixio.fs.glob("/dev/tty[A-Z]*")
+	or nixio.fs.glob("/dev/tts/*")
+
+if device_suggestions then
+	local node
+	for node in device_suggestions do
+		device:value(node)
+	end
+end
+
+mode = section:taboption("general", ListValue, "mode", translate("Service mode"))
+mode:value("2g", translate("2G only"))
+mode:value("3g", translate("3G only"))
+mode:value("lte", translate("LTE only"))
+mode:value("prefer3g", translate("Prefer 3G"))
+mode:value("preferlte", translate("Prefer LTE"))
+mode:value("auto", translate("Automatic / Any"))
+mode.rmempty = false
+mode.default = "auto"
+
+
+apn = section:taboption("general", Value, "apn", translate("APN"))
+
+
+pincode = section:taboption("general", Value, "pincode", translate("PIN"))
+
+
+authtype = section:taboption("general", ListValue, "authtype", translate("Authentication type"))
+authtype:value("none", translate("None"))
+authtype:value("cleartext", translate("Clear text"))
+authtype:value("pap", translate("PAP"))
+authtype:value("chap", translate("CHAP"))
+authtype.rmempty = false
+authtype.default = "none"
+
+
+username = section:taboption("general", Value, "username", translate("PAP/CHAP username"))
+
+
+password = section:taboption("general", Value, "password", translate("PAP/CHAP password"))
+password.password = true
+
+
+bcast = section:taboption("advanced", Flag, "broadcast",
+	translate("Use broadcast flag"),
+	translate("Required for certain ISPs, e.g. Charter with DOCSIS 3"))
+
+bcast.default = bcast.disabled
+
+
+defaultroute = section:taboption("advanced", Flag, "defaultroute",
+	translate("Use default gateway"),
+	translate("If unchecked, no default route is configured"))
+
+defaultroute.default = defaultroute.enabled
+
+
+peerdns = section:taboption("advanced", Flag, "peerdns",
+	translate("Use DNS servers advertised by peer"),
+	translate("If unchecked, the advertised DNS server addresses are ignored"))
+
+peerdns.default = peerdns.enabled
+
+
+dns = section:taboption("advanced", DynamicList, "dns",
+	translate("Use custom DNS servers"))
+
+dns:depends("peerdns", "")
+dns.datatype = "ipaddr"
+dns.cast     = "string"
+
+
+delay = section:taboption("advanced", Value, "delay",
+	translate("Dongle connection delay"))
+delay.default	  = "20"
+delay.placeholder = translate("seconds")
+delay.datatype	  = "uinteger"
+
+
+metric = section:taboption("advanced", Value, "metric",
+	translate("Use gateway metric"))
+
+metric.placeholder = "0"
+metric.datatype	   = "uinteger"
+
+clientid = section:taboption("advanced", Value, "clientid",
+	translate("Client ID to send when requesting DHCP"))
+
+
+vendorclass = section:taboption("advanced", Value, "vendorid",
+	translate("Vendor Class to send when requesting DHCP"))
+
+
+luci.tools.proto.opt_macaddr(section, ifc, translate("Override MAC address"))
+
+
+mtu = section:taboption("advanced", Value, "mtu", translate("Override MTU"))
+mtu.placeholder = "1492"
+mtu.datatype	= "max(9200)"
diff --git a/modules/ncm-luci/files/usr/lib/lua/luci/model/network/proto_ncm.lua b/modules/ncm-luci/files/usr/lib/lua/luci/model/network/proto_ncm.lua
new file mode 100644
index 0000000..dd8dbe4
--- /dev/null
+++ b/modules/ncm-luci/files/usr/lib/lua/luci/model/network/proto_ncm.lua
@@ -0,0 +1,60 @@
+--[[
+LuCI - Network model - 3G, PPP, PPtP, PPPoE and PPPoA protocol extension
+
+Copyright 2011 Jo-Philipp Wich <xm at subsignal.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+]]--
+
+local netmod = luci.model.network
+
+local _, p
+for _, p in ipairs({"ncm"}) do
+
+	local proto = netmod:register_protocol(p)
+
+	function proto.get_i18n(self)
+		return luci.i18n.translate("NCM")
+	end
+
+	function proto.ifname(self)
+		return p .. "-" .. self.sid
+	end
+
+	function proto.opkg_package(self)
+		return "kmod-usb-net-cdc-ncm"
+	end
+
+	function proto.is_installed(self)
+		return nixio.fs.access("/lib/netifd/proto/ncm.sh")
+	end
+
+	function proto.is_floating(self)
+		return false
+	end
+
+	function proto.is_virtual(self)
+		return false
+	end
+
+	function proto.get_interfaces(self)
+		return netmod.protocol.get_interfaces(self)
+	end
+
+	function proto.contains_interface(self, ifc)
+		return netmod.protocol.contains_interface(self, ifc)
+	end
+
+	netmod:register_pattern_virtual("^%s-%%w" % p)
+end
diff --git a/modules/ncm-luci/files/usr/lib/lua/luci/view/ncm/status.htm b/modules/ncm-luci/files/usr/lib/lua/luci/view/ncm/status.htm
new file mode 100644
index 0000000..ac72d11
--- /dev/null
+++ b/modules/ncm-luci/files/usr/lib/lua/luci/view/ncm/status.htm
@@ -0,0 +1,283 @@
+<%#
+LuCI - Lua Configuration Interface
+Copyright 2008 Steven Barth <steven at midlink.org>
+Copyright 2008-2011 Jo-Philipp Wich <xm at subsignal.org>
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+	http://www.apache.org/licenses/LICENSE-2.0
+
+-%>
+
+<%
+	require "luci.sys"
+	require "luci.fs"
+
+	local rv = {
+		gothw		= "0",
+		gotcarrier	= "0",
+		gotsignal	= "0",
+		hwrefresh	= "0",
+		modemdev	= "",
+		driver		= "",
+		vendor		= "",
+		model		= "",
+		notification	= "",
+		firmware	= "",
+		imei		= "",
+		provider	= "",
+		mode		= "",
+		downlink	= "",
+		uplink		= "",
+		cellid		= "",
+		lac		= "",
+		network		= "",
+		signal		= "",
+		rssi		= "",
+		csq		= "",
+		rcsp		= "",
+		ecio		= ""
+	}
+
+	if luci.http.formvalue("gothw") == "1" then
+		rv["gothw"] = luci.http.formvalue("gothw") or "0"
+		rv["modemdev"] = luci.http.formvalue("modemdev")
+		rv["driver"] = luci.http.formvalue("driver")
+		rv["gotcarrier"] = luci.http.formvalue("gotcarrier") or "0"
+		rv["gotsignal"] = luci.http.formvalue("gotsignal") or "0"
+		rv["hwrefresh"] = luci.http.formvalue("hwrefresh") or "0"
+	else
+		local uci = require "luci.model.uci".cursor()
+		if uci:get("network", "wan", "proto") == "ncm" then
+			rv["modemdev"] = uci:get("network", "wan", "device") or "0"
+		else
+			if luci.http.formvalue("status") == "1" then
+
+				rv["notification"] = "[ WAN is not using NCM protocol ]"
+				rv["hwrefresh"] = "0"
+				rv["gothw"] = "0"
+				rv["driver"] = "-"
+
+				luci.http.prepare_content("application/json")
+				luci.http.write_json(rv)
+
+				return
+			end
+
+		end
+
+	end
+
+	if ( luci.http.formvalue("status") == "1" and rv["driver"] ~= "-" ) then
+
+		if luci.fs.access(rv["modemdev"]) then
+
+			if rv["gothw"] == "0" then
+
+				local modemhw = luci.sys.exec(table.concat({"/usr/bin/gcom -d ", rv["modemdev"], " -s /etc/gcom/ncm/getcardinfo.gcom"}, ""))
+
+				for k, v in string.gmatch(modemhw, "(%w+) '(.-)'") do
+					rv[k] = v
+				end
+
+				if rv["driver"] == "" then
+					rv["driver"] = "-"
+				end
+
+				if ( rv["vendor"] == "" or rv["model"] == "" or rv["firmware"] == "" or rv["imei"] == "" or rv["driver"] == "" ) then
+					rv["gothw"] = "0"
+				elseif (rv["driver"] ~= "-" and
+						luci.fs.access(table.concat({"/etc/gcom/ncm/carrier/",rv["driver"], ".gcom"}, "")) and
+						luci.fs.access(table.concat({"/etc/gcom/ncm/signal/", rv["driver"], ".gcom"}, ""))) then
+					rv['gothw'] = "1"
+				else
+					rv["notification"] = " [ Unsupported dongle ]";
+					rv["driver"] = "-"
+					rv["gothw"] = "0"
+					rv["hwrefresh"] = "1"
+				end
+
+			elseif rv["gotcarrier"] == "0" then
+
+				if rv["driver"] ~= "-" then
+
+					local modemcarrier = luci.sys.exec(table.concat({"/usr/bin/gcom -d ", rv["modemdev"], " -s /etc/gcom/ncm/carrier/", rv["driver"], ".gcom"}, ""))
+
+					for k, v in string.gmatch(modemcarrier, "(%w+) '(.-)'") do
+						rv[k] = v
+					end
+
+					if ( rv["provider"] == "" or rv["downlink"] == "" or rv["uplink"] == "" or rv["mode"] == "" ) then
+						rv["gotcarrier"] = "0"
+					else
+						rv["gotcarrier"] = "1"
+					end
+
+				else
+					rv["gotcarrier"] = "1"
+				end
+
+			else
+
+				if rv["driver"] ~= "-" then
+
+					local modemsignal = luci.sys.exec(table.concat({"/usr/bin/gcom -d ", rv["modemdev"], " -s /etc/gcom/ncm/signal/", rv["driver"], ".gcom"}, ""))
+
+					for k, v in string.gmatch(modemsignal, "(%w+) '(.-)'") do
+						rv[k] = v
+					end
+					rv["gotsignal"] = "1"
+				end
+
+			end
+		else
+
+			rv["notification"] = table.concat({"[ ", rv["modemdev"], ": Device not present. ]"}, "")
+
+		end
+
+	end
+
+	if ( luci.http.formvalue("status") == "1" ) then
+
+		luci.http.prepare_content("application/json")
+		luci.http.write_json(rv)
+
+		return
+
+	end
+
+-%>
+
+<%+header%>
+
+<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
+<script type="text/javascript" src="<%=resource%>/ncm_xhr.js"></script>
+<script type="text/javascript">//<![CDATA[
+
+	function capitalize(s)
+	{
+		return s.toLowerCase().replace( /\b./g, function(a){ return a.toUpperCase(); } );
+	};
+
+	XHR2.poll(5, '<%=REQUEST_URI%>', { status: 1 },
+		function(x, info)
+		{
+
+			var e;
+
+			if (e = document.getElementById('notification'))
+				e.innerHTML = info.notification;
+
+			if (( gothwD == "0" && info.gothw == "1" ) || ( info.hwrefresh == "1" )) {
+
+				if (e = document.getElementById('name'))
+					e.innerHTML = capitalize(info.vendor) + " " + capitalize(info.model);
+
+				if (e = document.getElementById('firmware'))
+					e.innerHTML = info.firmware;
+
+				if (e = document.getElementById('imei'))
+					e.innerHTML = info.imei;
+			}
+
+			if ( gotcarrierD == "0" && info.gotcarrier == "1" ) {
+
+				if (e = document.getElementById('provider'))
+					e.innerHTML = info.provider;
+
+				if (e = document.getElementById('linkspeed'))
+					if ( info.downlink != "" && info.uplink != "" )
+						e.innerHTML = info.downlink + " / " + info.uplink ;
+
+				if (e = document.getElementById('mode'))
+					e.innerHTML = info.mode;
+
+			}
+
+			if ( info.gotsignal == "1" ) {
+
+				if (e = document.getElementById('cellid'))
+					e.innerHTML = info.cellid;
+
+				if (e = document.getElementById('lac'))
+					e.innerHTML = info.lac;
+
+				if (e = document.getElementById('network'))
+					e.innerHTML = info.network;
+
+				if (e = document.getElementById('signal'))
+					e.innerHTML = info.signal;
+
+				if (e = document.getElementById('csq'))
+					e.innerHTML = info.csq;
+
+				if (e = document.getElementById('rssi'))
+					e.innerHTML = info.rssi;
+
+				if (e = document.getElementById('rcsp'))
+					e.innerHTML = info.rcsp;
+
+				if (e = document.getElementById('ecio'))
+					e.innerHTML = info.ecio;
+
+			}
+
+			modemdevD = info.modemdev;
+			driverD = info.driver;
+			gothwD = info.gothw;
+			gotcarrierD = info.gotcarrier;
+			hwrefreshD = info.hwrefresh;
+			gotsignalD = info.gotsignal;
+
+		}
+	);
+
+//]]></script>
+
+<h2><a id="content" name="content"><%:NCM Status%></a></h2>
+<small style="color: #777;" id="notification"></small>
+
+<fieldset class="cbi-section">
+	<legend id="name"><%:Detecting dongle%></legend>
+
+	<table width="100%" cellspacing="10">
+		<tr><td width="33%"><%:Firmware version%></td><td id="firmware"></td></tr>
+		<tr><td width="33%"><%:IMEI%></td><td id="imei"></td></tr>
+	</table>
+</fieldset>
+
+<fieldset class="cbi-section">
+	<legend><%:Network%></legend>
+
+	<table width="100%" cellspacing="10">
+		<tr><td width="33%"><%:Provider%></td><td id="provider"></td></tr>
+		<tr><td width="33%"><%:Downlink/Uplink%></td><td id="linkspeed"></td></tr>
+		<tr><td width="33%"><%:Mode%></td><td id="mode"></td></tr>
+	</table>
+</fieldset>
+
+<fieldset class="cbi-section">
+	<legend><%:Cell%></legend>
+
+	<table width="100%" cellspacing="10">
+		<tr><td width="33%"><%:Location Area Code%></td><td id="lac"></td></tr>
+		<tr><td width="33%"><%:Cell ID%></td><td id="cellid"></td></tr>
+	</table>
+</fieldset>
+
+<fieldset class="cbi-section">
+	<legend><%:Signal level%></legend>
+
+	<table width="100%" cellspacing="10">
+		<tr><td width="33%"><%:Network%></td><td id="network"></td></tr>
+		<tr><td width="33%"><%:Signal strength%></td><td id="signal"></td></tr>
+		<tr><td width="33%"><%:CSQ%></td><td id="csq"></td></tr>
+		<tr><td width="33%"><%:RSSI%></td><td id="rssi"></td></tr>
+		<tr><td width="33%"><%:RCSP%></td><td id="rcsp"></td></tr>
+		<tr><td width="33%"><%:ECIO%></td><td id="ecio"></td></tr>
+	</table>
+</fieldset>
+<%+footer%>
diff --git a/modules/ncm-luci/files/www/luci-static/resources/ncm_xhr.js b/modules/ncm-luci/files/www/luci-static/resources/ncm_xhr.js
new file mode 100644
index 0000000..c01e8ac
--- /dev/null
+++ b/modules/ncm-luci/files/www/luci-static/resources/ncm_xhr.js
@@ -0,0 +1,254 @@
+/*
+ * xhr.js - XMLHttpRequest helper class
+ * (c) 2008-2010 Jo-Philipp Wich
+ */
+
+var gothwD = "0";
+var gotcarrierD = "0";
+var gotsignalD = "0";
+var hwrefreshD = "0";
+var modemdevD = ""
+var driverD = ""
+
+XHR2 = function()
+{
+	this.reinit = function()
+	{
+		if (window.XMLHttpRequest) {
+			this._xmlHttp = new XMLHttpRequest();
+		}
+		else if (window.ActiveXObject) {
+			this._xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
+		}
+		else {
+			alert("dongle_xhr.js: XMLHttpRequest is not supported by this browser!");
+		}
+	}
+
+	this.busy = function() {
+		if (!this._xmlHttp)
+			return false;
+
+		switch (this._xmlHttp.readyState)
+		{
+			case 1:
+			case 2:
+			case 3:
+				return true;
+
+			default:
+				return false;
+		}
+	}
+
+	this.abort = function() {
+		if (this.busy())
+			this._xmlHttp.abort();
+	}
+
+	this.get = function(url,data,callback)
+	{
+		this.reinit();
+
+		var xhr2  = this._xmlHttp;
+		var code = this._encode(data);
+
+		url = location.protocol + '//' + location.host + url;
+
+		if (code)
+			if (url.substr(url.length-1,1) == '&')
+				url += code;
+			else
+				url += '?' + code;
+
+		xhr2.open('GET', url, true);
+
+		xhr2.onreadystatechange = function()
+		{
+			if (xhr2.readyState == 4) {
+				var json = null;
+				if (xhr2.getResponseHeader("Content-Type") == "application/json") {
+					try {
+						json = eval('(' + xhr2.responseText + ')');
+					}
+					catch(e) {
+						json = null;
+					}
+				}
+
+				callback(xhr2, json);
+			}
+		}
+
+		xhr2.send(null);
+	}
+
+	this.post = function(url,data,callback)
+	{
+		this.reinit();
+
+		var xhr2  = this._xmlHttp;
+		var code = this._encode(data);
+
+		xhr2.onreadystatechange = function()
+		{
+			if (xhr2.readyState == 4)
+				callback(xhr2);
+		}
+
+		xhr2.open('POST', url, true);
+		xhr2.setRequestHeader('Content-type', 'application/x-www-form-urlencoded');
+		xhr2.setRequestHeader('Content-length', code.length);
+		xhr2.setRequestHeader('Connection', 'close');
+		xhr2.send(code);
+	}
+
+	this.cancel = function()
+	{
+		this._xmlHttp.onreadystatechange = function(){};
+		this._xmlHttp.abort();
+	}
+
+	this.send_form = function(form,callback,extra_values)
+	{
+		var code = '';
+
+		for (var i = 0; i < form.elements.length; i++)
+		{
+			var e = form.elements[i];
+
+			if (e.options)
+			{
+				code += (code ? '&' : '') +
+					form.elements[i].name + '=' + encodeURIComponent(
+						e.options[e.selectedIndex].value
+					);
+			}
+			else if (e.length)
+			{
+				for (var j = 0; j < e.length; j++)
+					if (e[j].name) {
+						code += (code ? '&' : '') +
+							e[j].name + '=' + encodeURIComponent(e[j].value);
+					}
+			}
+			else
+			{
+				code += (code ? '&' : '') +
+					e.name + '=' + encodeURIComponent(e.value);
+			}
+		}
+
+		if (typeof extra_values == 'object')
+			for (var key in extra_values)
+				code += (code ? '&' : '') +
+					key + '=' + encodeURIComponent(extra_values[key]);
+
+		return(
+			(form.method == 'get')
+				? this.get(form.getAttribute('action'), code, callback)
+				: this.post(form.getAttribute('action'), code, callback)
+		);
+	}
+
+	this._encode = function(obj)
+	{
+		obj = obj ? obj : { };
+		obj['gothw'] = gothwD;
+		obj['gotcarrier'] = gotcarrierD;
+		obj['gotsignal'] = gotsignalD;
+		obj['hwrefresh'] = hwrefreshD;
+		obj['modemdev'] = modemdevD;
+		obj['driver'] = driverD;
+		obj['_'] = Math.random();
+
+		if (typeof obj == 'object')
+		{
+			var code = '';
+			var self = this;
+
+			for (var k in obj)
+				code += (code ? '&' : '') +
+					k + '=' + encodeURIComponent(obj[k]);
+
+			return code;
+		}
+
+		return obj;
+	}
+}
+
+XHR2.get = function(url, data, callback)
+{
+	(new XHR2()).get(url, data, callback);
+}
+
+XHR2.poll = function(interval, url, data, callback)
+{
+	if (isNaN(interval) || interval < 1)
+		interval = 5;
+
+	if (!XHR2._q)
+	{
+		XHR2._t = 0;
+		XHR2._q = [ ];
+		XHR2._r = function() {
+			for (var i = 0, e = XHR2._q[0]; i < XHR2._q.length; e = XHR2._q[++i])
+			{
+				if (!(XHR2._t % e.interval) && !e.xhr2.busy())
+					e.xhr2.get(e.url, e.data, e.callback);
+			}
+
+			XHR2._t++;
+		};
+	}
+
+	XHR2._q.push({
+		interval: interval,
+		callback: callback,
+		url:      url,
+		data:     data,
+		xhr2:      new XHR2()
+	});
+
+	XHR2.run();
+}
+
+XHR2.halt = function()
+{
+	if (XHR2._i)
+	{
+		/* show & set poll indicator */
+		try {
+			document.getElementById('xhr_poll_status').style.display = '';
+			document.getElementById('xhr_poll_status_on').style.display = 'none';
+			document.getElementById('xhr_poll_status_off').style.display = '';
+		} catch(e) { }
+
+		window.clearInterval(XHR2._i);
+		XHR2._i = null;
+	}
+}
+
+XHR2.run = function()
+{
+	if (XHR2._r && !XHR2._i)
+	{
+		/* show & set poll indicator */
+		try {
+			document.getElementById('xhr_poll_status').style.display = '';
+			document.getElementById('xhr_poll_status_on').style.display = '';
+			document.getElementById('xhr_poll_status_off').style.display = 'none';
+		} catch(e) { }
+
+		/* kick first round manually to prevent one second lag when setting up
+		 * the poll interval */
+		XHR2._r();
+		XHR2._i = window.setInterval(XHR2._r, 1000);
+	}
+}
+
+XHR2.running = function()
+{
+	return !!(XHR2._r && XHR2._i);
+}



More information about the luci mailing list