ndppd: importing ndppd package in openwrt-routing feed
authorSaverio Proto <zioproto@gmail.com>
Mon, 1 Jul 2013 13:36:26 +0000 (15:36 +0200)
committerSaverio Proto <zioproto@gmail.com>
Mon, 1 Jul 2013 13:36:53 +0000 (15:36 +0200)
86 files changed:
README [new file with mode: 0644]
batman-adv/Config.in [new file with mode: 0644]
batman-adv/Makefile [new file with mode: 0644]
batman-adv/files/etc/config/batman-adv [new file with mode: 0644]
batman-adv/files/etc/hotplug.d/net/99-batman-adv [new file with mode: 0644]
batman-adv/files/lib/batman-adv/config.sh [new file with mode: 0644]
batman-adv/files/lib/netifd/proto/batadv.sh [new file with mode: 0644]
batman-adv/patches/0001-batman-adv-use-the-proper-header-len-when-checking-t.patch [new file with mode: 0644]
batman-adv/patches/0002-batman-adv-vlan-add-protocol-argument-to-packet-tagging-fun.patch [new file with mode: 0644]
batman-adv/patches/0003-batman-adv-check-proto-length-before-accessing-proto.patch [new file with mode: 0644]
batman-adv/patches/0004-batman-adv-check-return-value-of-pskb_trim_rcsum.patch [new file with mode: 0644]
batman-adv/patches/0005-batman-adv-make-DAT-drop-ARP-requests-targeting-loca.patch [new file with mode: 0644]
batman-adv/patches/0006-batman-adv-reorder-clean-up-routine-in-order-to-avoi.patch [new file with mode: 0644]
batman-adv/patches/0007-batman-adv-Avoid-double-freeing-of-bat_counters.patch [new file with mode: 0644]
batman-adv/patches/0008-batman-adv-wait-for-rtnl-in-batadv_store_mesh_iface-.patch [new file with mode: 0644]
batman-adv/patches/0009-batman-adv-Don-t-handle-address-updates-when-bla-is-.patch [new file with mode: 0644]
batman-adv/patches/0010-batman-adv-forward-late-OGMs-from-best-next-hop.patch [new file with mode: 0644]
batmand/Makefile [new file with mode: 0644]
batmand/files/etc/config/batmand [new file with mode: 0644]
batmand/files/etc/config/vis [new file with mode: 0644]
batmand/files/etc/init.d/batmand [new file with mode: 0644]
batmand/files/etc/init.d/vis [new file with mode: 0644]
batmand/patches/100-2.6.36.patch [new file with mode: 0644]
bmx6/Makefile [new file with mode: 0644]
bmx6/files/etc/config/bmx6 [new file with mode: 0644]
bmx6/files/etc/init.d/bmx6 [new file with mode: 0755]
bmxd/Makefile [new file with mode: 0644]
bmxd/files/etc/config/bmxd [new file with mode: 0644]
bmxd/files/etc/init.d/bmxd [new file with mode: 0644]
luci-app-bmx6/COPYING [new file with mode: 0644]
luci-app-bmx6/Makefile [new file with mode: 0644]
luci-app-bmx6/files/etc/config/luci-bmx6 [new file with mode: 0644]
luci-app-bmx6/files/usr/lib/lua/luci/controller/bmx6.lua [new file with mode: 0644]
luci-app-bmx6/files/usr/lib/lua/luci/model/bmx6json.lua [new file with mode: 0644]
luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/advanced.lua [new file with mode: 0644]
luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/hna.lua [new file with mode: 0644]
luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/interfaces.lua [new file with mode: 0644]
luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/main.lua [new file with mode: 0644]
luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/plugins.lua [new file with mode: 0644]
luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/tunnels.lua [new file with mode: 0644]
luci-app-bmx6/files/usr/lib/lua/luci/view/admin_status/index/neighbours_simple.htm [new file with mode: 0644]
luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/chat.htm [new file with mode: 0644]
luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/error.htm [new file with mode: 0644]
luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/gateways_j.htm [new file with mode: 0644]
luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/graph.htm [new file with mode: 0644]
luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/interfaces.htm [new file with mode: 0644]
luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/links.htm [new file with mode: 0644]
luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/neighbours.htm [new file with mode: 0644]
luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/neighbours_j.htm [new file with mode: 0644]
luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/nodes.htm [new file with mode: 0644]
luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/nodes_j.htm [new file with mode: 0644]
luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/status.htm [new file with mode: 0644]
luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/status_j.htm [new file with mode: 0644]
luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/tunnels_j.htm [new file with mode: 0644]
luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/wireless.htm [new file with mode: 0644]
luci-app-bmx6/files/www/cgi-bin/bmx6-info [new file with mode: 0644]
luci-app-bmx6/files/www/luci-static/resources/bmx6/bmx6logo.png [new file with mode: 0644]
luci-app-bmx6/files/www/luci-static/resources/bmx6/js/Curry-1.0.1.js [new file with mode: 0644]
luci-app-bmx6/files/www/luci-static/resources/bmx6/js/dracula_algorithms.js [new file with mode: 0644]
luci-app-bmx6/files/www/luci-static/resources/bmx6/js/dracula_graffle.js [new file with mode: 0644]
luci-app-bmx6/files/www/luci-static/resources/bmx6/js/dracula_graph.js [new file with mode: 0644]
luci-app-bmx6/files/www/luci-static/resources/bmx6/js/jquery-1.4.2.min.js [new file with mode: 0644]
luci-app-bmx6/files/www/luci-static/resources/bmx6/js/polling.js [new file with mode: 0644]
luci-app-bmx6/files/www/luci-static/resources/bmx6/js/raphael-min.js [new file with mode: 0644]
luci-app-bmx6/files/www/luci-static/resources/bmx6/js/seedrandom.js [new file with mode: 0644]
luci-app-bmx6/files/www/luci-static/resources/bmx6/link.png [new file with mode: 0644]
luci-app-bmx6/files/www/luci-static/resources/bmx6/style.css [new file with mode: 0644]
luci-app-bmx6/files/www/luci-static/resources/bmx6/wifi.png [new file with mode: 0644]
luci-app-bmx6/files/www/luci-static/resources/bmx6/world.png [new file with mode: 0644]
luci-app-bmx6/files/www/luci-static/resources/bmx6/world_small.png [new file with mode: 0644]
nodogsplash/Makefile [new file with mode: 0644]
nodogsplash/files/nodogsplash.init [new file with mode: 0644]
olsrd/Makefile [new file with mode: 0644]
olsrd/files/olsrd.config [new file with mode: 0644]
olsrd/files/olsrd.init [new file with mode: 0644]
olsrd/files/olsrd.pud.position.conf [new file with mode: 0644]
olsrd/files/olsrd.sgw.speed.conf [new file with mode: 0644]
olsrd/files/olsrd_secure_key [new file with mode: 0644]
quagga/Makefile [new file with mode: 0644]
quagga/files/quagga [new file with mode: 0644]
quagga/files/quagga.conf [new file with mode: 0644]
quagga/files/quagga.init [new file with mode: 0644]
quagga/patches/120-quagga_manet.patch [new file with mode: 0644]
quagga/patches/140-holdtimer-set.patch [new file with mode: 0644]
quagga/patches/150-no-cross-fs-link.patch [new file with mode: 0644]
quagga/patches/170-use-supported-pagers.patch [new file with mode: 0644]

diff --git a/README b/README
new file mode 100644 (file)
index 0000000..72b7fcc
--- /dev/null
+++ b/README
@@ -0,0 +1,18 @@
+This is an OpenWrt package feed containing community maintained routing packages.
+
+To use these packages, add the following line to the feeds.conf
+in the OpenWrt buildroot:
+
+  src-git routing git://github.com/openwrt-routing/packages.git
+  
+Update the feed:
+
+  ./scripts/feeds update routing
+  
+Activate the package:
+
+  ./scripts/feeds install -a -p routing
+  
+The routing packages should now appear in menuconfig.
+
+
diff --git a/batman-adv/Config.in b/batman-adv/Config.in
new file mode 100644 (file)
index 0000000..1330525
--- /dev/null
@@ -0,0 +1,29 @@
+
+config KMOD_BATMAN_ADV_DEBUG_LOG
+       bool "enable verbose debug logging"
+       depends on PACKAGE_kmod-batman-adv
+       default n
+
+config KMOD_BATMAN_ADV_BLA
+       bool "enable bridge loop avoidance"
+       depends on PACKAGE_kmod-batman-adv
+       default y
+
+config KMOD_BATMAN_ADV_DAT
+       bool "enable distributed arp table"
+       depends on PACKAGE_kmod-batman-adv
+       default y
+
+config KMOD_BATMAN_ADV_NC
+       bool "enable network coding [requires promisc mode support]"
+       depends on PACKAGE_kmod-batman-adv
+       default n
+
+config KMOD_BATMAN_ADV_BATCTL
+       bool "enable batctl"
+       depends on PACKAGE_kmod-batman-adv
+       default y
+       help
+         batctl is a more intuitive managment utility for B.A.T.M.A.N.-Advanced.
+         It is an easier method for configuring batman-adv and
+         provides some additional tools for debugging as well.
diff --git a/batman-adv/Makefile b/batman-adv/Makefile
new file mode 100644 (file)
index 0000000..1e77051
--- /dev/null
@@ -0,0 +1,122 @@
+#
+# Copyright (C) 2010 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+# $Id: Makefile 5624 2006-11-23 00:29:07Z nbd $
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=batman-adv
+
+PKG_VERSION:=2013.2.0
+BATCTL_VERSION:=2013.2.0
+PKG_RELEASE:=3
+PKG_MD5SUM:=9ec18300b96df22f0ed21c9f51e4ccef
+BATCTL_MD5SUM:=712f86cdd0f9076503fc48acf37e109e
+
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
+PKG_SOURCE_URL:=http://downloads.open-mesh.org/batman/releases/batman-adv-$(PKG_VERSION)
+
+PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)/$(PKG_NAME)-$(PKG_VERSION)
+PKG_BATCTL_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)/batctl-$(BATCTL_VERSION)
+
+include $(INCLUDE_DIR)/package.mk
+include $(INCLUDE_DIR)/kernel.mk
+
+define KernelPackage/batman-adv
+  URL:=http://www.open-mesh.org/
+  MAINTAINER:=Marek Lindner <lindner_marek@yahoo.de>
+  SUBMENU:=Network Support
+  DEPENDS:=+kmod-lib-crc16 +kmod-crypto-core +kmod-crypto-crc32c +kmod-lib-crc32c +libc
+  TITLE:=B.A.T.M.A.N. Adv
+  FILES:=$(PKG_BUILD_DIR)/batman-adv.$(LINUX_KMOD_SUFFIX)
+  AUTOLOAD:=$(call AutoLoad,50,batman-adv)
+endef
+
+define KernelPackage/batman-adv/description
+B.A.T.M.A.N. advanced is a kernel module which allows to
+build layer 2 mesh networks. This package contains the
+version $(PKG_VERSION) of the kernel module plus its user space
+configuration & managerment tool batctl.
+endef
+
+define KernelPackage/batman-adv/config
+       source "$(SOURCE)/Config.in"
+endef
+
+MAKE_BATMAN_ADV_ARGS += \
+       CROSS_COMPILE="$(TARGET_CROSS)" \
+       KERNELPATH="$(LINUX_DIR)" \
+       ARCH="$(LINUX_KARCH)" \
+       PATH="$(TARGET_PATH)" \
+       SUBDIRS="$(PKG_BUILD_DIR)" \
+       PWD="$(PKG_BUILD_DIR)" \
+       LINUX_VERSION="$(LINUX_VERSION)" \
+       CONFIG_BATMAN_ADV_DEBUG=$(if $(CONFIG_KMOD_BATMAN_ADV_DEBUG_LOG),y,n) \
+       CONFIG_BATMAN_ADV_BLA=$(if $(CONFIG_KMOD_BATMAN_ADV_BLA),y,n) \
+       CONFIG_BATMAN_ADV_DAT=$(if $(CONFIG_KMOD_BATMAN_ADV_DAT),y,n) \
+       CONFIG_BATMAN_ADV_NC=$(if $(CONFIG_KMOD_BATMAN_ADV_NC),y,n) \
+       REVISION="" all
+
+MAKE_BATCTL_ARGS += \
+       CFLAGS="$(TARGET_CFLAGS)" \
+       CCFLAGS="$(TARGET_CFLAGS)" \
+       OFLAGS="$(TARGET_CFLAGS)" \
+       REVISION="" \
+       CC="$(TARGET_CC)" \
+       NODEBUG=1 \
+       UNAME="Linux" \
+       DESTDIR="$(PKG_INSTALL_DIR)" \
+       STRIP="/bin/true" \
+       batctl install
+
+ifneq ($(DEVELOPER)$(CONFIG_KMOD_BATMAN_ADV_BATCTL),)
+define Download/batctl
+  FILE:=batctl-$(BATCTL_VERSION).tar.gz
+  URL:=$(PKG_SOURCE_URL)
+  MD5SUM:=$(BATCTL_MD5SUM)
+endef
+$(eval $(call Download,batctl))
+
+BATCTL_EXTRACT = tar xzf "$(DL_DIR)/batctl-$(BATCTL_VERSION).tar.gz" -C "$(BUILD_DIR)/$(PKG_NAME)"
+BATCTL_PATCH = $(call Build/DoPatch,"$(PKG_BATCTL_BUILD_DIR)","$(PATCH_DIR)","*batctl*")
+BATCTL_BUILD = $(MAKE) -C $(PKG_BATCTL_BUILD_DIR) $(MAKE_BATCTL_ARGS)
+BATCTL_INSTALL = $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/local/sbin/batctl $(1)/usr/sbin/
+endif
+
+KPATCH ?= $(PATCH)
+define Build/DoPatch
+       @if [ -d "$(2)" ]; then \
+               if [ "$$$$(ls $(2) | grep -Ec $(3))" -gt 0 ]; then \
+                       $(KPATCH) "$(1)" "$(2)" "$(3)"; \
+               fi; \
+       fi
+endef
+
+define Build/Patch
+       $(call Build/DoPatch,"$(PKG_BUILD_DIR)","$(PATCH_DIR)","*batman*")
+       $(BATCTL_EXTRACT)
+       $(BATCTL_PATCH)
+endef
+
+define Build/Compile
+       $(MAKE) -C "$(PKG_BUILD_DIR)" $(MAKE_BATMAN_ADV_ARGS)
+       $(BATCTL_BUILD)
+endef
+
+define Build/Clean
+        rm -rf $(BUILD_DIR)/$(PKG_NAME)/
+endef
+
+define KernelPackage/batman-adv/install
+       $(INSTALL_DIR) $(1)/etc/config $(1)/etc/hotplug.d/net $(1)/etc/hotplug.d/iface $(1)/lib/batman-adv $(1)/usr/sbin $(1)/lib/netifd/proto
+       $(INSTALL_DATA) ./files/etc/config/batman-adv $(1)/etc/config
+       $(INSTALL_DATA) ./files/lib/batman-adv/config.sh $(1)/lib/batman-adv
+       $(INSTALL_BIN) ./files/etc/hotplug.d/net/99-batman-adv $(1)/etc/hotplug.d/net
+       $(INSTALL_BIN) ./files/lib/netifd/proto/batadv.sh $(1)/lib/netifd/proto
+       $(BATCTL_INSTALL)
+endef
+
+$(eval $(call KernelPackage,batman-adv))
diff --git a/batman-adv/files/etc/config/batman-adv b/batman-adv/files/etc/config/batman-adv
new file mode 100644 (file)
index 0000000..79d660d
--- /dev/null
@@ -0,0 +1,20 @@
+
+config 'mesh' 'bat0'
+       option 'aggregated_ogms'
+       option 'ap_isolation'
+       option 'bonding'
+       option 'fragmentation'
+       option 'gw_bandwidth'
+       option 'gw_mode'
+       option 'gw_sel_class'
+       option 'log_level'
+       option 'orig_interval'
+       option 'vis_mode'
+       option 'bridge_loop_avoidance'
+       option 'distributed_arp_table'
+       option 'network_coding'
+       option 'hop_penalty'
+
+# yet another batX instance
+# config 'mesh' 'bat5'
+#      option 'interfaces' 'second_mesh'
diff --git a/batman-adv/files/etc/hotplug.d/net/99-batman-adv b/batman-adv/files/etc/hotplug.d/net/99-batman-adv
new file mode 100644 (file)
index 0000000..f0c391f
--- /dev/null
@@ -0,0 +1,12 @@
+#!/bin/sh
+
+. /lib/batman-adv/config.sh
+
+bat_load_module
+config_load batman-adv
+
+case "$ACTION" in
+       add)
+               [ -d /sys/class/net/$INTERFACE/mesh/ ] && bat_config "$INTERFACE"
+               ;;
+esac
diff --git a/batman-adv/files/lib/batman-adv/config.sh b/batman-adv/files/lib/batman-adv/config.sh
new file mode 100644 (file)
index 0000000..471c1f2
--- /dev/null
@@ -0,0 +1,48 @@
+#!/bin/sh
+
+bat_load_module()
+{
+       [ -d "/sys/module/batman_adv/" ] && return
+
+       . /lib/functions.sh
+       load_modules /etc/modules.d/*-crc16 /etc/modules.d/*-crypto* /etc/modules.d/*-lib-crc* /etc/modules.d/*-batman-adv*
+}
+
+bat_config()
+{
+       local mesh="$1"
+       local aggregated_ogms ap_isolation bonding bridge_loop_avoidance distributed_arp_table fragmentation
+       local gw_bandwidth gw_mode gw_sel_class hop_penalty network_coding log_level orig_interval vis_mode
+
+       config_get aggregated_ogms "$mesh" aggregated_ogms
+       config_get ap_isolation "$mesh" ap_isolation
+       config_get bonding "$mesh" bonding
+       config_get bridge_loop_avoidance "$mesh" bridge_loop_avoidance
+       config_get distributed_arp_table "$mesh" distributed_arp_table
+       config_get fragmentation "$mesh" fragmentation
+       config_get gw_bandwidth "$mesh" gw_bandwidth
+       config_get gw_mode "$mesh" gw_mode
+       config_get gw_sel_class "$mesh" gw_sel_class
+       config_get hop_penalty "$mesh" hop_penalty
+       config_get network_coding "$mesh" network_coding
+       config_get log_level "$mesh" log_level
+       config_get orig_interval "$mesh" orig_interval
+       config_get vis_mode "$mesh" vis_mode
+
+       [ ! -f "/sys/class/net/$mesh/mesh/orig_interval" ] && echo "batman-adv mesh $mesh does not exist - check your interface configuration" && return 1
+
+       [ -n "$aggregate_ogms" ] && echo $aggregate_ogms > /sys/class/net/$mesh/mesh/aggregate_ogms
+       [ -n "$ap_isolation" ] && echo $ap_isolation > /sys/class/net/$mesh/mesh/ap_isolation
+       [ -n "$bonding" ] && echo $bonding > /sys/class/net/$mesh/mesh/bonding
+       [ -n "$bridge_loop_avoidance" ] && echo $bridge_loop_avoidance > /sys/class/net/$mesh/mesh/bridge_loop_avoidance 2>&-
+       [ -n "$distributed_arp_table" ] && echo $distributed_arp_table > /sys/class/net/$mesh/mesh/distributed_arp_table 2>&-
+       [ -n "$fragmentation" ] && echo $fragmentation > /sys/class/net/$mesh/mesh/fragmentation
+       [ -n "$gw_bandwidth" ] && echo $gw_bandwidth > /sys/class/net/$mesh/mesh/gw_bandwidth
+       [ -n "$gw_mode" ] && echo $gw_mode > /sys/class/net/$mesh/mesh/gw_mode
+       [ -n "$gw_sel_class" ] && echo $gw_sel_class > /sys/class/net/$mesh/mesh/gw_sel_class
+       [ -n "$hop_penalty" ] && echo $hop_penalty > /sys/class/net/$mesh/mesh/hop_penalty
+       [ -n "$network_coding" ] && echo $network_coding > /sys/class/net/$mesh/mesh/network_coding 2>&-
+       [ -n "$log_level" ] && echo $log_level > /sys/class/net/$mesh/mesh/log_level 2>&-
+       [ -n "$orig_interval" ] && echo $orig_interval > /sys/class/net/$mesh/mesh/orig_interval
+       [ -n "$vis_mode" ] && echo $vis_mode > /sys/class/net/$mesh/mesh/vis_mode
+}
diff --git a/batman-adv/files/lib/netifd/proto/batadv.sh b/batman-adv/files/lib/netifd/proto/batadv.sh
new file mode 100644 (file)
index 0000000..632a209
--- /dev/null
@@ -0,0 +1,30 @@
+#!/bin/sh
+
+. /lib/functions.sh
+. ../netifd-proto.sh
+init_proto "$@"
+
+proto_batadv_init_config() {
+       proto_config_add_string "mesh"
+}
+
+proto_batadv_setup() {
+       local config="$1"
+       local iface="$2"
+
+       local mesh
+       json_get_vars mesh
+
+       echo "$mesh" > "/sys/class/net/$iface/batman_adv/mesh_iface"
+       proto_init_update "$iface" 1
+       proto_send_update "$config"
+}
+
+proto_batadv_teardown() {
+       local config="$1"
+       local iface="$2"
+
+       echo "none" > "/sys/class/net/$iface/batman_adv/mesh_iface" || true
+}
+
+add_protocol batadv
diff --git a/batman-adv/patches/0001-batman-adv-use-the-proper-header-len-when-checking-t.patch b/batman-adv/patches/0001-batman-adv-use-the-proper-header-len-when-checking-t.patch
new file mode 100644 (file)
index 0000000..525dbac
--- /dev/null
@@ -0,0 +1,74 @@
+From 9b96ecbae7295269aaa0320667f646870de65661 Mon Sep 17 00:00:00 2001
+From: Antonio Quartulli <antonio@open-mesh.com>
+Date: Wed, 3 Apr 2013 10:14:20 +0200
+Subject: [PATCH 01/10] batman-adv: use the proper header len when checking
+ the TTVN
+
+Unicast packet might be of type either UNICAST or
+UNICAST4ADDR.
+In the two cases the header size is different, but the
+mechanism checking the TTVN field was assuming it to be
+always of the same type (UNICAST), so failing to access the
+inner Ethernet header in case of UNICAST4ADDR.
+
+Fix this by passing the real header length as argument.
+
+Signed-off-by: Antonio Quartulli <antonio@open-mesh.com>
+Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
+---
+ routing.c |   11 +++++------
+ 1 file changed, 5 insertions(+), 6 deletions(-)
+
+diff --git a/routing.c b/routing.c
+index 2f1f889..b27a4d7 100644
+--- a/routing.c
++++ b/routing.c
+@@ -939,7 +939,7 @@ out:
+ }
+ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
+-                                   struct sk_buff *skb) {
++                                   struct sk_buff *skb, int hdr_len) {
+       uint8_t curr_ttvn, old_ttvn;
+       struct batadv_orig_node *orig_node;
+       struct ethhdr *ethhdr;
+@@ -948,7 +948,7 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
+       int is_old_ttvn;
+       /* check if there is enough data before accessing it */
+-      if (pskb_may_pull(skb, sizeof(*unicast_packet) + ETH_HLEN) < 0)
++      if (pskb_may_pull(skb, hdr_len + ETH_HLEN) < 0)
+               return 0;
+       /* create a copy of the skb (in case of for re-routing) to modify it. */
+@@ -956,7 +956,7 @@ static int batadv_check_unicast_ttvn(struct batadv_priv *bat_priv,
+               return 0;
+       unicast_packet = (struct batadv_unicast_packet *)skb->data;
+-      ethhdr = (struct ethhdr *)(skb->data + sizeof(*unicast_packet));
++      ethhdr = (struct ethhdr *)(skb->data + hdr_len);
+       /* check if the destination client was served by this node and it is now
+        * roaming. In this case, it means that the node has got a ROAM_ADV
+@@ -1072,8 +1072,7 @@ int batadv_recv_unicast_packet(struct sk_buff *skb,
+       if (check < 0)
+               return NET_RX_DROP;
+-
+-      if (!batadv_check_unicast_ttvn(bat_priv, skb))
++      if (!batadv_check_unicast_ttvn(bat_priv, skb, hdr_size))
+               return NET_RX_DROP;
+       /* packet for me */
+@@ -1117,7 +1116,7 @@ int batadv_recv_ucast_frag_packet(struct sk_buff *skb,
+       if (batadv_check_unicast_packet(bat_priv, skb, hdr_size) < 0)
+               return NET_RX_DROP;
+-      if (!batadv_check_unicast_ttvn(bat_priv, skb))
++      if (!batadv_check_unicast_ttvn(bat_priv, skb, hdr_size))
+               return NET_RX_DROP;
+       unicast_packet = (struct batadv_unicast_frag_packet *)skb->data;
+-- 
+1.7.10.4
+
diff --git a/batman-adv/patches/0002-batman-adv-vlan-add-protocol-argument-to-packet-tagging-fun.patch b/batman-adv/patches/0002-batman-adv-vlan-add-protocol-argument-to-packet-tagging-fun.patch
new file mode 100644 (file)
index 0000000..608bd43
--- /dev/null
@@ -0,0 +1,69 @@
+From 82d1a8ebf19a1b9841ee44ce7b2448114be3e772 Mon Sep 17 00:00:00 2001
+From: Patrick McHardy <kaber@trash.net>
+Date: Wed, 24 Apr 2013 17:42:56 +0200
+Subject: [PATCH 02/10] net: vlan: add protocol argument to packet tagging
+ functions
+
+Add a protocol argument to the VLAN packet tagging functions. In case of HW
+tagging, we need that protocol available in the ndo_start_xmit functions,
+so it is stored in a new field in the skb. The new field fits into a hole
+(on 64 bit) and doesn't increase the sks's size.
+
+Signed-off-by: Patrick McHardy <kaber@trash.net>
+Signed-off-by: David S. Miller <davem@davemloft.net>
+[siwu@hrz.tu-chemnitz.de: added compat code]
+Signed-off-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
+Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
+---
+ bridge_loop_avoidance.c |    2 +-
+ compat.h                |   16 +++++++++++++++-
+ 2 files changed, 16 insertions(+), 2 deletions(-)
+
+diff --git a/bridge_loop_avoidance.c b/bridge_loop_avoidance.c
+index 6a4f728..379061c 100644
+--- a/bridge_loop_avoidance.c
++++ b/bridge_loop_avoidance.c
+@@ -341,7 +341,7 @@ static void batadv_bla_send_claim(struct batadv_priv *bat_priv, uint8_t *mac,
+       }
+       if (vid != -1)
+-              skb = vlan_insert_tag(skb, vid);
++              skb = vlan_insert_tag(skb, htons(ETH_P_8021Q), vid);
+       skb_reset_mac_header(skb);
+       skb->protocol = eth_type_trans(skb, soft_iface);
+diff --git a/compat.h b/compat.h
+index 0663df5..c1dadac 100644
+--- a/compat.h
++++ b/compat.h
+@@ -170,7 +170,7 @@ static const struct { \
+ #if LINUX_VERSION_CODE < KERNEL_VERSION(3, 0, 0)
+ #define kfree_rcu(ptr, rcu_head) call_rcu(&ptr->rcu_head, batadv_free_rcu_##ptr)
+-#define vlan_insert_tag(skb, vid) __vlan_put_tag(skb, vid)
++#define vlan_insert_tag(skb, proto, vid) __vlan_put_tag(skb, vid)
+ void batadv_free_rcu_gw_node(struct rcu_head *rcu);
+ void batadv_free_rcu_neigh_node(struct rcu_head *rcu);
+@@ -278,4 +278,18 @@ static int __batadv_interface_set_mac_addr(x, y)
+ #endif /* < KERNEL_VERSION(3, 9, 0) */
++#if LINUX_VERSION_CODE < KERNEL_VERSION(3, 10, 0)
++
++#ifndef vlan_insert_tag
++
++/* include this header early to let the following define
++ * not mess up the original function prototype.
++ */
++#include <linux/if_vlan.h>
++#define vlan_insert_tag(skb, proto, vid) vlan_insert_tag(skb, vid)
++
++#endif /* vlan_insert_tag */
++
++#endif /* < KERNEL_VERSION(3, 10, 0) */
++
+ #endif /* _NET_BATMAN_ADV_COMPAT_H_ */
+-- 
+1.7.10.4
+
diff --git a/batman-adv/patches/0003-batman-adv-check-proto-length-before-accessing-proto.patch b/batman-adv/patches/0003-batman-adv-check-proto-length-before-accessing-proto.patch
new file mode 100644 (file)
index 0000000..f6270fa
--- /dev/null
@@ -0,0 +1,33 @@
+From aa7d19a5b97fe48657e075e8e4d130bd6916551e Mon Sep 17 00:00:00 2001
+From: Marek Lindner <lindner_marek@yahoo.de>
+Date: Sat, 27 Apr 2013 16:22:28 +0800
+Subject: [PATCH 03/10] batman-adv: check proto length before accessing proto
+ string buffer
+
+batadv_param_set_ra() strips the trailing '\n' from the supplied
+string buffer without checking the length of the buffer first. This
+patches avoids random memory access and associated potential
+crashes.
+
+Reported-by: Sasha Levin <sasha.levin@oracle.com>
+Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
+---
+ main.c |    2 +-
+ 1 file changed, 1 insertion(+), 1 deletion(-)
+
+diff --git a/main.c b/main.c
+index 3e30a0f..9c620cd 100644
+--- a/main.c
++++ b/main.c
+@@ -475,7 +475,7 @@ static int batadv_param_set_ra(const char *val, const struct kernel_param *kp)
+       char *algo_name = (char *)val;
+       size_t name_len = strlen(algo_name);
+-      if (algo_name[name_len - 1] == '\n')
++      if (name_len > 0 && algo_name[name_len - 1] == '\n')
+               algo_name[name_len - 1] = '\0';
+       bat_algo_ops = batadv_algo_get(algo_name);
+-- 
+1.7.10.4
+
diff --git a/batman-adv/patches/0004-batman-adv-check-return-value-of-pskb_trim_rcsum.patch b/batman-adv/patches/0004-batman-adv-check-return-value-of-pskb_trim_rcsum.patch
new file mode 100644 (file)
index 0000000..e38596b
--- /dev/null
@@ -0,0 +1,44 @@
+From d22ebef1431aab13099370b89afa4ba55eb95c35 Mon Sep 17 00:00:00 2001
+From: Marek Lindner <lindner_marek@yahoo.de>
+Date: Tue, 7 May 2013 19:25:02 +0800
+Subject: [PATCH 04/10] batman-adv: check return value of pskb_trim_rcsum()
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+Reported-by: Sven Eckelmann <sven@narfation.org>
+Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
+Acked-by: Martin Hundebøll <martin@hundeboll.net>
+---
+ network-coding.c |    8 ++++++--
+ 1 file changed, 6 insertions(+), 2 deletions(-)
+
+diff --git a/network-coding.c b/network-coding.c
+index f7c5430..e84629e 100644
+--- a/network-coding.c
++++ b/network-coding.c
+@@ -1514,6 +1514,7 @@ batadv_nc_skb_decode_packet(struct batadv_priv *bat_priv, struct sk_buff *skb,
+       struct ethhdr *ethhdr, ethhdr_tmp;
+       uint8_t *orig_dest, ttl, ttvn;
+       unsigned int coding_len;
++      int err;
+       /* Save headers temporarily */
+       memcpy(&coded_packet_tmp, skb->data, sizeof(coded_packet_tmp));
+@@ -1568,8 +1569,11 @@ batadv_nc_skb_decode_packet(struct batadv_priv *bat_priv, struct sk_buff *skb,
+                        coding_len);
+       /* Resize decoded skb if decoded with larger packet */
+-      if (nc_packet->skb->len > coding_len + h_size)
+-              pskb_trim_rcsum(skb, coding_len + h_size);
++      if (nc_packet->skb->len > coding_len + h_size) {
++              err = pskb_trim_rcsum(skb, coding_len + h_size);
++              if (err)
++                      return NULL;
++      }
+       /* Create decoded unicast packet */
+       unicast_packet = (struct batadv_unicast_packet *)skb->data;
+-- 
+1.7.10.4
+
diff --git a/batman-adv/patches/0005-batman-adv-make-DAT-drop-ARP-requests-targeting-loca.patch b/batman-adv/patches/0005-batman-adv-make-DAT-drop-ARP-requests-targeting-loca.patch
new file mode 100644 (file)
index 0000000..e617ad3
--- /dev/null
@@ -0,0 +1,48 @@
+From d6bd8b36fa1f3d72a6fd5942a6e9bde6ddafcd0d Mon Sep 17 00:00:00 2001
+From: Antonio Quartulli <ordex@autistici.org>
+Date: Thu, 9 May 2013 09:35:45 +0200
+Subject: [PATCH 05/10] batman-adv: make DAT drop ARP requests targeting local
+ clients
+
+In the outgoing ARP request snooping routine in DAT, ARP
+Request sent by local clients which are supposed to be
+replied by other local clients can be silently dropped.
+
+The destination host will reply by itself through the LAN
+and therefore there is no need to involve DAT.
+
+Reported-by: Carlos Quijano <carlos@crqgestion.es>
+Signed-off-by: Antonio Quartulli <ordex@autistici.org>
+Tested-by: Carlos Quijano <carlos@crqgestion.es>
+Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
+---
+ distributed-arp-table.c |   13 +++++++++++++
+ 1 file changed, 13 insertions(+)
+
+diff --git a/distributed-arp-table.c b/distributed-arp-table.c
+index 8e15d96..2399920 100644
+--- a/distributed-arp-table.c
++++ b/distributed-arp-table.c
+@@ -837,6 +837,19 @@ bool batadv_dat_snoop_outgoing_arp_request(struct batadv_priv *bat_priv,
+       dat_entry = batadv_dat_entry_hash_find(bat_priv, ip_dst);
+       if (dat_entry) {
++              /* If the ARP request is destined for a local client the local
++               * client will answer itself. DAT would only generate a
++               * duplicate packet.
++               *
++               * Moreover, if the soft-interface is enslaved into a bridge, an
++               * additional DAT answer may trigger kernel warnings about
++               * a packet coming from the wrong port.
++               */
++              if (batadv_is_my_client(bat_priv, dat_entry->mac_addr)) {
++                      ret = true;
++                      goto out;
++              }
++
+               skb_new = arp_create(ARPOP_REPLY, ETH_P_ARP, ip_src,
+                                    bat_priv->soft_iface, ip_dst, hw_src,
+                                    dat_entry->mac_addr, hw_src);
+-- 
+1.7.10.4
+
diff --git a/batman-adv/patches/0006-batman-adv-reorder-clean-up-routine-in-order-to-avoi.patch b/batman-adv/patches/0006-batman-adv-reorder-clean-up-routine-in-order-to-avoi.patch
new file mode 100644 (file)
index 0000000..967fb31
--- /dev/null
@@ -0,0 +1,57 @@
+From 763f413b9c74ccb25cb066408f49f07e5dd78f9b Mon Sep 17 00:00:00 2001
+From: Antonio Quartulli <ordex@autistici.org>
+Date: Tue, 7 May 2013 01:06:18 +0200
+Subject: [PATCH 06/10] batman-adv: reorder clean up routine in order to avoid
+ race conditions
+
+nc_worker accesses the originator table during its periodic
+work, but since the originator table is freed before
+stopping the worker this leads to a global protection fault.
+
+Fix this by killing the worker (in nc_free) before freeing
+the originator table.
+
+Moreover tidy up the entire clean up routine by running all
+the subcomponents freeing procedures first and then killing
+the TT and the originator tables at the end.
+
+Signed-off-by: Antonio Quartulli <ordex@autistici.org>
+Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
+---
+ main.c |   16 ++++++++++++----
+ 1 file changed, 12 insertions(+), 4 deletions(-)
+
+diff --git a/main.c b/main.c
+index 9c620cd..1240f07 100644
+--- a/main.c
++++ b/main.c
+@@ -163,14 +163,22 @@ void batadv_mesh_free(struct net_device *soft_iface)
+       batadv_vis_quit(bat_priv);
+       batadv_gw_node_purge(bat_priv);
+-      batadv_originator_free(bat_priv);
+       batadv_nc_free(bat_priv);
++      batadv_dat_free(bat_priv);
++      batadv_bla_free(bat_priv);
++      /* Free the TT and the originator tables only after having terminated
++       * all the other depending components which may use these structures for
++       * their purposes.
++       */
+       batadv_tt_free(bat_priv);
+-      batadv_bla_free(bat_priv);
+-
+-      batadv_dat_free(bat_priv);
++      /* Since the originator table clean up routine is accessing the TT
++       * tables as well, it has to be invoked after the TT tables have been
++       * freed and marked as empty. This ensures that no cleanup RCU callbacks
++       * accessing the TT data are scheduled for later execution.
++       */
++      batadv_originator_free(bat_priv);
+       free_percpu(bat_priv->bat_counters);
+-- 
+1.7.10.4
+
diff --git a/batman-adv/patches/0007-batman-adv-Avoid-double-freeing-of-bat_counters.patch b/batman-adv/patches/0007-batman-adv-Avoid-double-freeing-of-bat_counters.patch
new file mode 100644 (file)
index 0000000..d4f3806
--- /dev/null
@@ -0,0 +1,47 @@
+From a5d79639a76d7a71116abbc369a246bd1fcbf947 Mon Sep 17 00:00:00 2001
+From: =?UTF-8?q?Martin=20Hundeb=C3=B8ll?= <martin@hundeboll.net>
+Date: Wed, 17 Apr 2013 21:13:16 +0200
+Subject: [PATCH 07/10] batman-adv: Avoid double freeing of bat_counters
+MIME-Version: 1.0
+Content-Type: text/plain; charset=UTF-8
+Content-Transfer-Encoding: 8bit
+
+On errors in batadv_mesh_init(), bat_counters will be freed in both
+batadv_mesh_free() and batadv_softif_init_late(). This patch fixes this
+by returning earlier from batadv_softif_init_late() in case of errors in
+batadv_mesh_init() and by setting bat_counters to NULL after freeing.
+
+Signed-off-by: Martin Hundebøll <martin@hundeboll.net>
+Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
+---
+ main.c           |    1 +
+ soft-interface.c |    1 +
+ 2 files changed, 2 insertions(+)
+
+diff --git a/main.c b/main.c
+index 1240f07..51aafd6 100644
+--- a/main.c
++++ b/main.c
+@@ -181,6 +181,7 @@ void batadv_mesh_free(struct net_device *soft_iface)
+       batadv_originator_free(bat_priv);
+       free_percpu(bat_priv->bat_counters);
++      bat_priv->bat_counters = NULL;
+       atomic_set(&bat_priv->mesh_state, BATADV_MESH_INACTIVE);
+ }
+diff --git a/soft-interface.c b/soft-interface.c
+index 6f20d33..819dfb0 100644
+--- a/soft-interface.c
++++ b/soft-interface.c
+@@ -505,6 +505,7 @@ unreg_debugfs:
+       batadv_debugfs_del_meshif(dev);
+ free_bat_counters:
+       free_percpu(bat_priv->bat_counters);
++      bat_priv->bat_counters = NULL;
+       return ret;
+ }
+-- 
+1.7.10.4
+
diff --git a/batman-adv/patches/0008-batman-adv-wait-for-rtnl-in-batadv_store_mesh_iface-.patch b/batman-adv/patches/0008-batman-adv-wait-for-rtnl-in-batadv_store_mesh_iface-.patch
new file mode 100644 (file)
index 0000000..87a2940
--- /dev/null
@@ -0,0 +1,42 @@
+From 96cd7725540f4dccdd6fbb4fde59243e1cc1ad80 Mon Sep 17 00:00:00 2001
+From: Matthias Schiffer <mschiffer@universe-factory.net>
+Date: Tue, 28 May 2013 17:32:32 +0200
+Subject: [PATCH 08/10] batman-adv: wait for rtnl in batadv_store_mesh_iface
+ instead of failing if it is taken
+
+The rtnl_lock in batadv_store_mesh_iface has been converted to a rtnl_trylock
+some time ago to avoid a possible deadlock between rtnl and s_active on removal
+of the sysfs nodes.
+
+The behaviour introduced by that was quite confusing as it could lead to the
+sysfs store to fail, making batman-adv setup scripts unreliable. As recently the
+sysfs removal was postponed to a worker not running with the rtnl taken, the
+deadlock can't occur any more and it is safe to change the trylock back to a
+lock to make the sysfs store reliable again.
+
+Signed-off-by: Matthias Schiffer <mschiffer@universe-factory.net>
+Reviewed-by: Simon Wunderlich <siwu@hrz.tu-chemnitz.de>
+Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
+---
+ sysfs.c |    5 +----
+ 1 file changed, 1 insertion(+), 4 deletions(-)
+
+diff --git a/sysfs.c b/sysfs.c
+index 15a22ef..929e304 100644
+--- a/sysfs.c
++++ b/sysfs.c
+@@ -582,10 +582,7 @@ static ssize_t batadv_store_mesh_iface(struct kobject *kobj,
+           (strncmp(hard_iface->soft_iface->name, buff, IFNAMSIZ) == 0))
+               goto out;
+-      if (!rtnl_trylock()) {
+-              ret = -ERESTARTSYS;
+-              goto out;
+-      }
++      rtnl_lock();
+       if (status_tmp == BATADV_IF_NOT_IN_USE) {
+               batadv_hardif_disable_interface(hard_iface,
+-- 
+1.7.10.4
+
diff --git a/batman-adv/patches/0009-batman-adv-Don-t-handle-address-updates-when-bla-is-.patch b/batman-adv/patches/0009-batman-adv-Don-t-handle-address-updates-when-bla-is-.patch
new file mode 100644 (file)
index 0000000..52e1acd
--- /dev/null
@@ -0,0 +1,35 @@
+From 518fba156ed911c6183cf5cb34955a6fdf1b4637 Mon Sep 17 00:00:00 2001
+From: Simon Wunderlich <simon@open-mesh.com>
+Date: Fri, 7 Jun 2013 16:52:05 +0200
+Subject: [PATCH 09/10] batman-adv: Don't handle address updates when bla is
+ disabled
+
+The bridge loop avoidance has a hook to handle address updates of the
+originator. These should not be handled when bridge loop avoidance is
+disabled - it might send some bridge loop avoidance packets which should
+not appear if bla is disabled.
+
+Signed-off-by: Simon Wunderlich <simon@open-mesh.com>
+Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
+---
+ bridge_loop_avoidance.c |    4 ++++
+ 1 file changed, 4 insertions(+)
+
+diff --git a/bridge_loop_avoidance.c b/bridge_loop_avoidance.c
+index 379061c..de27b31 100644
+--- a/bridge_loop_avoidance.c
++++ b/bridge_loop_avoidance.c
+@@ -1067,6 +1067,10 @@ void batadv_bla_update_orig_address(struct batadv_priv *bat_priv,
+       group = htons(crc16(0, primary_if->net_dev->dev_addr, ETH_ALEN));
+       bat_priv->bla.claim_dest.group = group;
++      /* purge everything when bridge loop avoidance is turned off */
++      if (!atomic_read(&bat_priv->bridge_loop_avoidance))
++              oldif = NULL;
++
+       if (!oldif) {
+               batadv_bla_purge_claims(bat_priv, NULL, 1);
+               batadv_bla_purge_backbone_gw(bat_priv, 1);
+-- 
+1.7.10.4
+
diff --git a/batman-adv/patches/0010-batman-adv-forward-late-OGMs-from-best-next-hop.patch b/batman-adv/patches/0010-batman-adv-forward-late-OGMs-from-best-next-hop.patch
new file mode 100644 (file)
index 0000000..843ee89
--- /dev/null
@@ -0,0 +1,226 @@
+From 3d999e5116f44b47c742aa16d6382721c360a6d0 Mon Sep 17 00:00:00 2001
+From: Simon Wunderlich <simon@open-mesh.com>
+Date: Thu, 23 May 2013 13:07:42 +0200
+Subject: [PATCH 10/10] batman-adv: forward late OGMs from best next hop
+
+When a packet is received from another node first and later from the
+best next hop, this packet is dropped. However the first OGM was sent
+with the BATADV_NOT_BEST_NEXT_HOP flag and thus dropped by neighbors.
+The late OGM from the best neighbor is then dropped because it is a
+duplicate.
+
+If this situation happens constantly, a node might end up not forwarding
+the "valid" OGMs anymore, and nodes behind will starve from not getting
+valid OGMs.
+
+Fix this by refining the duplicate checking behaviour: The actions
+should depend on whether it was a duplicate for a neighbor only or for
+the originator. OGMs which are not duplicates for a specific neighbor
+will now be considered in batadv_iv_ogm_forward(), but only actually
+forwarded for the best next hop. Therefore, late OGMs from the best
+next hop are forwarded now and not dropped as duplicates anymore.
+
+Signed-off-by: Simon Wunderlich <simon@open-mesh.com>
+Signed-off-by: Marek Lindner <lindner_marek@yahoo.de>
+---
+ bat_iv_ogm.c |   86 +++++++++++++++++++++++++++++++++++++---------------------
+ 1 file changed, 55 insertions(+), 31 deletions(-)
+
+diff --git a/bat_iv_ogm.c b/bat_iv_ogm.c
+index 071f288..f680ee1 100644
+--- a/bat_iv_ogm.c
++++ b/bat_iv_ogm.c
+@@ -29,6 +29,21 @@
+ #include "bat_algo.h"
+ #include "network-coding.h"
++/**
++ * batadv_dup_status - duplicate status
++ * @BATADV_NO_DUP: the packet is a duplicate
++ * @BATADV_ORIG_DUP: OGM is a duplicate in the originator (but not for the
++ *  neighbor)
++ * @BATADV_NEIGH_DUP: OGM is a duplicate for the neighbor
++ * @BATADV_PROTECTED: originator is currently protected (after reboot)
++ */
++enum batadv_dup_status {
++      BATADV_NO_DUP = 0,
++      BATADV_ORIG_DUP,
++      BATADV_NEIGH_DUP,
++      BATADV_PROTECTED,
++};
++
+ static struct batadv_neigh_node *
+ batadv_iv_ogm_neigh_new(struct batadv_hard_iface *hard_iface,
+                       const uint8_t *neigh_addr,
+@@ -650,7 +665,7 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
+                         const struct batadv_ogm_packet *batadv_ogm_packet,
+                         struct batadv_hard_iface *if_incoming,
+                         const unsigned char *tt_buff,
+-                        int is_duplicate)
++                        enum batadv_dup_status dup_status)
+ {
+       struct batadv_neigh_node *neigh_node = NULL, *tmp_neigh_node = NULL;
+       struct batadv_neigh_node *router = NULL;
+@@ -676,7 +691,7 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
+                       continue;
+               }
+-              if (is_duplicate)
++              if (dup_status != BATADV_NO_DUP)
+                       continue;
+               spin_lock_bh(&tmp_neigh_node->lq_update_lock);
+@@ -718,7 +733,7 @@ batadv_iv_ogm_orig_update(struct batadv_priv *bat_priv,
+       neigh_node->tq_avg = batadv_ring_buffer_avg(neigh_node->tq_recv);
+       spin_unlock_bh(&neigh_node->lq_update_lock);
+-      if (!is_duplicate) {
++      if (dup_status == BATADV_NO_DUP) {
+               orig_node->last_ttl = batadv_ogm_packet->header.ttl;
+               neigh_node->last_ttl = batadv_ogm_packet->header.ttl;
+       }
+@@ -902,15 +917,16 @@ out:
+       return ret;
+ }
+-/* processes a batman packet for all interfaces, adjusts the sequence number and
+- * finds out whether it is a duplicate.
+- * returns:
+- *   1 the packet is a duplicate
+- *   0 the packet has not yet been received
+- *  -1 the packet is old and has been received while the seqno window
+- *     was protected. Caller should drop it.
++/**
++ * batadv_iv_ogm_update_seqnos -  process a batman packet for all interfaces,
++ *  adjust the sequence number and find out whether it is a duplicate
++ * @ethhdr: ethernet header of the packet
++ * @batadv_ogm_packet: OGM packet to be considered
++ * @if_incoming: interface on which the OGM packet was received
++ *
++ * Returns duplicate status as enum batadv_dup_status
+  */
+-static int
++static enum batadv_dup_status
+ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
+                           const struct batadv_ogm_packet *batadv_ogm_packet,
+                           const struct batadv_hard_iface *if_incoming)
+@@ -918,17 +934,18 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
+       struct batadv_priv *bat_priv = netdev_priv(if_incoming->soft_iface);
+       struct batadv_orig_node *orig_node;
+       struct batadv_neigh_node *tmp_neigh_node;
+-      int is_duplicate = 0;
++      int is_dup;
+       int32_t seq_diff;
+       int need_update = 0;
+-      int set_mark, ret = -1;
++      int set_mark;
++      enum batadv_dup_status ret = BATADV_NO_DUP;
+       uint32_t seqno = ntohl(batadv_ogm_packet->seqno);
+       uint8_t *neigh_addr;
+       uint8_t packet_count;
+       orig_node = batadv_get_orig_node(bat_priv, batadv_ogm_packet->orig);
+       if (!orig_node)
+-              return 0;
++              return BATADV_NO_DUP;
+       spin_lock_bh(&orig_node->ogm_cnt_lock);
+       seq_diff = seqno - orig_node->last_real_seqno;
+@@ -936,22 +953,29 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
+       /* signalize caller that the packet is to be dropped. */
+       if (!hlist_empty(&orig_node->neigh_list) &&
+           batadv_window_protected(bat_priv, seq_diff,
+-                                  &orig_node->batman_seqno_reset))
++                                  &orig_node->batman_seqno_reset)) {
++              ret = BATADV_PROTECTED;
+               goto out;
++      }
+       rcu_read_lock();
+       hlist_for_each_entry_rcu(tmp_neigh_node,
+                                &orig_node->neigh_list, list) {
+-              is_duplicate |= batadv_test_bit(tmp_neigh_node->real_bits,
+-                                              orig_node->last_real_seqno,
+-                                              seqno);
+-
+               neigh_addr = tmp_neigh_node->addr;
++              is_dup = batadv_test_bit(tmp_neigh_node->real_bits,
++                                       orig_node->last_real_seqno,
++                                       seqno);
++
+               if (batadv_compare_eth(neigh_addr, ethhdr->h_source) &&
+-                  tmp_neigh_node->if_incoming == if_incoming)
++                  tmp_neigh_node->if_incoming == if_incoming) {
+                       set_mark = 1;
+-              else
++                      if (is_dup)
++                              ret = BATADV_NEIGH_DUP;
++              } else {
+                       set_mark = 0;
++                      if (is_dup && (ret != BATADV_NEIGH_DUP))
++                              ret = BATADV_ORIG_DUP;
++              }
+               /* if the window moved, set the update flag. */
+               need_update |= batadv_bit_get_packet(bat_priv,
+@@ -971,8 +995,6 @@ batadv_iv_ogm_update_seqnos(const struct ethhdr *ethhdr,
+               orig_node->last_real_seqno = seqno;
+       }
+-      ret = is_duplicate;
+-
+ out:
+       spin_unlock_bh(&orig_node->ogm_cnt_lock);
+       batadv_orig_node_free_ref(orig_node);
+@@ -994,7 +1016,8 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
+       int is_broadcast = 0, is_bidirect;
+       bool is_single_hop_neigh = false;
+       bool is_from_best_next_hop = false;
+-      int is_duplicate, sameseq, simlar_ttl;
++      int sameseq, similar_ttl;
++      enum batadv_dup_status dup_status;
+       uint32_t if_incoming_seqno;
+       uint8_t *prev_sender;
+@@ -1138,10 +1161,10 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
+       if (!orig_node)
+               return;
+-      is_duplicate = batadv_iv_ogm_update_seqnos(ethhdr, batadv_ogm_packet,
+-                                                 if_incoming);
++      dup_status = batadv_iv_ogm_update_seqnos(ethhdr, batadv_ogm_packet,
++                                               if_incoming);
+-      if (is_duplicate == -1) {
++      if (dup_status == BATADV_PROTECTED) {
+               batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
+                          "Drop packet: packet within seqno protection time (sender: %pM)\n",
+                          ethhdr->h_source);
+@@ -1211,11 +1234,12 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
+        * seqno and similar ttl as the non-duplicate
+        */
+       sameseq = orig_node->last_real_seqno == ntohl(batadv_ogm_packet->seqno);
+-      simlar_ttl = orig_node->last_ttl - 3 <= batadv_ogm_packet->header.ttl;
+-      if (is_bidirect && (!is_duplicate || (sameseq && simlar_ttl)))
++      similar_ttl = orig_node->last_ttl - 3 <= batadv_ogm_packet->header.ttl;
++      if (is_bidirect && ((dup_status == BATADV_NO_DUP) ||
++                          (sameseq && similar_ttl)))
+               batadv_iv_ogm_orig_update(bat_priv, orig_node, ethhdr,
+                                         batadv_ogm_packet, if_incoming,
+-                                        tt_buff, is_duplicate);
++                                        tt_buff, dup_status);
+       /* is single hop (direct) neighbor */
+       if (is_single_hop_neigh) {
+@@ -1236,7 +1260,7 @@ static void batadv_iv_ogm_process(const struct ethhdr *ethhdr,
+               goto out_neigh;
+       }
+-      if (is_duplicate) {
++      if (dup_status == BATADV_NEIGH_DUP) {
+               batadv_dbg(BATADV_DBG_BATMAN, bat_priv,
+                          "Drop packet: duplicate packet received\n");
+               goto out_neigh;
+-- 
+1.7.10.4
+
diff --git a/batmand/Makefile b/batmand/Makefile
new file mode 100644 (file)
index 0000000..4026496
--- /dev/null
@@ -0,0 +1,152 @@
+#
+# Copyright (C) 2008-2011 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+include $(INCLUDE_DIR)/kernel.mk
+
+PKG_NAME:=batmand
+PKG_REV:=1439
+PKG_VERSION:=r$(PKG_REV)
+PKG_RELEASE:=2
+PKG_EXTRA_CFLAGS=-DDEBUG_MALLOC -DMEMORY_USAGE -DPROFILE_DATA -DREVISION_VERSION=\"\ rv$(PKG_REV)\"
+
+PKG_SOURCE_PROTO:=svn
+PKG_SOURCE_VERSION:=$(PKG_REV)
+PKG_SOURCE_SUBDIR:=$(if $(PKG_BRANCH),$(PKG_BRANCH),$(PKG_NAME))-$(PKG_VERSION)
+PKG_SOURCE_URL:=http://downloads.open-mesh.org/svn/batman/trunk/
+PKG_SOURCE:=$(PKG_SOURCE_SUBDIR).tar.gz
+PKG_BUILD_DIR:=$(KERNEL_BUILD_DIR)/$(PKG_SOURCE_SUBDIR)
+
+PKG_KMOD_BUILD_DIR:=$(PKG_BUILD_DIR)/batman/linux/modules
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/batmand/Default
+  URL:=http://www.open-mesh.org/
+  MAINTAINER:=Marek Lindner <lindner_marek@yahoo.de>
+endef
+
+define Package/batmand
+$(call Package/batmand/Default)
+  SECTION:=net
+  CATEGORY:=Network
+  SUBMENU:=Routing and Redirection
+  DEPENDS:=+libpthread +kmod-tun
+  TITLE:=B.A.T.M.A.N. layer 3 routing daemon
+endef
+
+define Package/batmand/description
+B.A.T.M.A.N. layer 3 routing daemon
+endef
+
+define Package/vis
+$(call Package/batmand/Default)
+  SECTION:=net
+  CATEGORY:=Network
+  SUBMENU:=Routing and Redirection
+  DEPENDS:=+libpthread
+  TITLE:=visualization server for B.A.T.M.A.N. layer 3
+endef
+        
+define Package/vis/description
+visualization server for B.A.T.M.A.N. layer 3
+endef
+
+define KernelPackage/batgat
+$(call Package/batmand/Default)
+  SUBMENU:=Network Support
+  DEPENDS:=+batmand @BROKEN
+  TITLE:=B.A.T.M.A.N. gateway module
+  FILES:=$(PKG_KMOD_BUILD_DIR)/batgat.$(LINUX_KMOD_SUFFIX)
+  AUTOLOAD:=$(call AutoLoad,50,batgat)
+endef
+
+
+define KernelPackage/batgat/description
+Kernel gateway module for B.A.T.M.A.N. for better tunnel performance
+endef
+
+MAKE_BATMAND_ARGS += \
+       EXTRA_CFLAGS='$(TARGET_CFLAGS) $(PKG_EXTRA_CFLAGS)' \
+       CCFLAGS="$(TARGET_CFLAGS)" \
+       OFLAGS="$(TARGET_CFLAGS)" \
+       REVISION="$(PKG_REV)" \
+       CC="$(TARGET_CC)" \
+       NODEBUG=1 \
+       UNAME="Linux" \
+       INSTALL_PREFIX="$(PKG_INSTALL_DIR)" \
+       STRIP="/bin/true" \
+       batmand install
+       
+MAKE_VIS_ARGS += \
+       EXTRA_CFLAGS='$(TARGET_CFLAGS) $(PKG_EXTRA_CFLAGS)' \
+       CCFLAGS="$(TARGET_CFLAGS)" \
+       OFLAGS="$(TARGET_CFLAGS)" \
+       REVISION="$(PKG_REV)" \
+       CC="$(TARGET_CC)" \
+       NODEBUG=1 \
+       UNAME="Linux" \
+       INSTALL_PREFIX="$(PKG_INSTALL_DIR)" \
+       STRIP="/bin/true" \
+       vis install
+
+MAKE_BATGAT_ARGS += \
+       CROSS_COMPILE="$(TARGET_CROSS)" \
+       ARCH="$(LINUX_KARCH)" \
+       PATH="$(TARGET_PATH)" \
+       SUBDIRS="$(PKG_KMOD_BUILD_DIR)" \
+       LINUX_VERSION="$(LINUX_VERSION)" \
+       REVISION="$(PKG_REV)" modules   
+
+
+define Build/Configure
+endef
+
+ifneq ($(DEVELOPER)$(CONFIG_PACKAGE_batmand),)
+       BUILD_BATMAND := $(MAKE) -C $(PKG_BUILD_DIR)/batman $(MAKE_BATMAND_ARGS)
+endif
+
+ifneq ($(DEVELOPER)$(CONFIG_PACKAGE_vis),)
+       BUILD_VIS := $(MAKE) -C $(PKG_BUILD_DIR)/vis $(MAKE_VIS_ARGS)
+endif
+       
+ifneq ($(DEVELOPER)$(CONFIG_PACKAGE_kmod-batgat),)
+       BUILD_BATGAT := $(MAKE) -C "$(LINUX_DIR)" $(MAKE_BATGAT_ARGS)
+endif
+               
+define Build/Compile
+       $(BUILD_BATMAND)
+       $(BUILD_VIS)
+       cp $(PKG_KMOD_BUILD_DIR)/Makefile.kbuild $(PKG_KMOD_BUILD_DIR)/Makefile
+       $(BUILD_BATGAT)
+endef
+
+define Package/batmand/install
+       $(INSTALL_DIR) $(1)/usr/sbin $(1)/etc/config $(1)/etc/init.d
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/batmand $(1)/usr/sbin/
+       $(INSTALL_BIN) ./files/etc/init.d/batmand $(1)/etc/init.d
+       $(INSTALL_DATA) ./files/etc/config/batmand $(1)/etc/config
+endef
+
+define Package/batmand/conffiles
+/etc/config/batmand
+endef
+
+define Package/vis/install
+       $(INSTALL_DIR) $(1)/usr/sbin $(1)/etc/config $(1)/etc/init.d
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/vis $(1)/usr/sbin/
+       $(INSTALL_BIN) ./files/etc/init.d/vis $(1)/etc/init.d
+       $(INSTALL_DATA) ./files/etc/config/vis $(1)/etc/config
+endef
+
+define Package/vis/conffiles
+/etc/config/vis
+endef
+
+$(eval $(call BuildPackage,batmand))
+$(eval $(call BuildPackage,vis))
+$(eval $(call KernelPackage,batgat))
diff --git a/batmand/files/etc/config/batmand b/batmand/files/etc/config/batmand
new file mode 100644 (file)
index 0000000..6d1f3be
--- /dev/null
@@ -0,0 +1,12 @@
+config batmand general
+       option interface                ath0
+       option hna                      
+       option gateway_class            
+       option originator_interval
+       option preferred_gateway        
+       option routing_class
+       option visualisation_srv
+       option policy_routing_script
+       option disable_client_nat
+       option disable_aggregation
+       
diff --git a/batmand/files/etc/config/vis b/batmand/files/etc/config/vis
new file mode 100644 (file)
index 0000000..6d30023
--- /dev/null
@@ -0,0 +1,3 @@
+config vis general
+       option interface                ath0
+       
diff --git a/batmand/files/etc/init.d/batmand b/batmand/files/etc/init.d/batmand
new file mode 100644 (file)
index 0000000..403e9f3
--- /dev/null
@@ -0,0 +1,66 @@
+#!/bin/sh /etc/rc.common
+START=90
+
+start () {
+       interface=$(uci get batmand.general.interface)
+       if [ "$interface" = "" ]; then
+               echo $1 Error, you must specify at least a network interface
+               exit
+       fi
+       hnas=$(uci get batmand.general.hna)
+       gateway_class=$(uci get batmand.general.gateway_class)
+       originator_interval=$(uci get batmand.general.originator_interval)
+       preferred_gateway=$(uci get batmand.general.preferred_gateway)
+       routing_class=$(uci get batmand.general.routing_class)
+       visualisation_srv=$(uci get batmand.general.visualisation_srv)
+       policy_routing_script=$(uci get batmand.general.policy_routing_script)
+       disable_client_nat=$(uci get batmand.general.disable_client_nat)
+       disable_aggregation=$(uci get batmand.general.disable_aggregation)
+       batman_args=""
+
+       for hna in $hnas
+          do
+             batman_args=${batman_args}'-a '$hna' '
+          done
+
+       if [ $gateway_class ]; then
+               batman_args=${batman_args}'-g '$gateway_class' '
+       fi
+
+       if [ $originator_interval ]; then
+               batman_args=${batman_args}'-o '$originator_interval' '
+       fi
+
+       if [ $preferred_gateway ]; then
+               batman_args=${batman_args}'-p '$preferred_gateway' '
+       fi
+
+       if [ $routing_class ]; then
+               batman_args=${batman_args}'-r '$routing_class' '
+       fi
+               
+       if [ $visualisation_srv ]; then
+               batman_args=${batman_args}'-s '$visualisation_srv' '
+       fi
+       
+       if [ $policy_routing_script ]; then
+               batman_args=${batman_args}'--policy-routing-script '$policy_routing_script' '
+       fi
+       
+       if [ $disable_client_nat ]; then
+               batman_args=${batman_args}'--disable-client-nat '
+       fi
+       
+       if [ $disable_aggregation ]; then
+               batman_args=${batman_args}'--disable-aggregation '
+       fi
+
+       batman_args=${batman_args}$interface
+       batmand $batman_args >/dev/null 2>&1
+}
+
+stop () {
+        killall batmand
+}
+
+        
diff --git a/batmand/files/etc/init.d/vis b/batmand/files/etc/init.d/vis
new file mode 100644 (file)
index 0000000..ef18eb7
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/sh /etc/rc.common
+START=90
+
+start () {
+       interface=$(uci get vis.general.interface)
+       if [ "$interface" = "" ]; then
+               echo $1 Error, you must specify at least a network interface
+               exit
+       fi
+       vis_args=$interface
+
+       vis $vis_args >/dev/null 2>&1
+}
+
+stop () {
+        killall vis
+}
+
+        
diff --git a/batmand/patches/100-2.6.36.patch b/batmand/patches/100-2.6.36.patch
new file mode 100644 (file)
index 0000000..566c23b
--- /dev/null
@@ -0,0 +1,47 @@
+---
+ batman/linux/modules/gateway.c |   19 +++++++++++++++++++
+ 1 file changed, 19 insertions(+)
+
+--- batmand-r1439.orig/batman/linux/modules/gateway.c
++++ batmand-r1439/batman/linux/modules/gateway.c
+@@ -29,6 +29,7 @@ static struct class *batman_class;
+ static int batgat_open(struct inode *inode, struct file *filp);
+ static int batgat_release(struct inode *inode, struct file *file);
+ static int batgat_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg );
++static long batgat_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg );
+ static void udp_data_ready(struct sock *sk, int len);
+@@ -53,7 +54,11 @@ static int proc_clients_read(char *buf,
+ static struct file_operations fops = {
+       .open = batgat_open,
+       .release = batgat_release,
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
++      .unlocked_ioctl = batgat_ioctl_unlocked,
++#else
+       .ioctl = batgat_ioctl,
++#endif
+ };
+@@ -166,6 +171,20 @@ static int batgat_release(struct inode *
+ }
++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,36)
++#include <linux/smp_lock.h>
++static long batgat_ioctl_unlocked(struct file *file, unsigned int cmd, unsigned long arg )
++{
++      int ret;
++
++      lock_kernel();
++      ret = batgat_ioctl(file->f_path.dentry->d_inode, file, cmd, arg);
++      unlock_kernel();
++
++      return ret;
++}
++#endif
++
+ static int batgat_ioctl( struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg )
+ {
+       uint8_t tmp_ip[4];
diff --git a/bmx6/Makefile b/bmx6/Makefile
new file mode 100644 (file)
index 0000000..8786a3f
--- /dev/null
@@ -0,0 +1,148 @@
+#    Copyright (C) 2011 Fundacio Privada per a la Xarxa Oberta, Lliure i Neutral guifi.net
+#
+#    This program is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License along
+#    with this program; if not, write to the Free Software Foundation, Inc.,
+#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+#    The full GNU General Public License is included in this distribution in
+#    the file called "COPYING".
+#
+# Contibutors:
+#      Axel Neumann, Simó Albert i Beltran, Pau Escrich
+#
+
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=bmx6
+
+PKG_SOURCE_PROTO:=git
+
+PKG_SOURCE_URL:=git://bmx6.net/bmx6.git
+#PKG_SOURCE_URL:=git://github.com/axn/bmx6.git
+
+PKG_REV:=60adcbd4655ccf36a407bae58d66914e33f05a41
+PKG_VERSION:=r2013060803
+PKG_RELEASE:=4
+
+PKG_SOURCE_VERSION:=$(PKG_REV)
+PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
+PKG_SOURCE:=$(PKG_SOURCE_SUBDIR).tar.gz
+PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_SOURCE_SUBDIR)
+
+include $(INCLUDE_DIR)/package.mk
+
+
+TARGET_CFLAGS += $(FPIC)
+
+MAKE_ARGS += \
+       EXTRA_CFLAGS="$(TARGET_CFLAGS) -I. -I$(STAGING_DIR)/usr/include -DNO_DEBUG_ALL -DNO_DEBUG_DUMP" \
+       EXTRA_LDFLAGS="-L$(STAGING_DIR)/usr/lib " \
+       REVISION_VERSION="$(PKG_REV)" \
+       CC="$(TARGET_CC)" \
+       INSTALL_DIR="$(PKG_INSTALL_DIR)" \
+       STRIP="/bin/false" \
+       build_all
+
+define Package/bmx6/Default
+  SECTION:=net
+  CATEGORY:=Network
+  SUBMENU:=Routing and Redirection
+  TITLE:=BMX6 layer 3 routing daemon
+  URL:=http://bmx6.net/
+  MAINTAINER:=Axel Neumann <neumann@cgws.de>
+  DEPENDS:=+kmod-ip6-tunnel +kmod-iptunnel6 +kmod-tun
+endef
+
+define Package/bmx6/description
+BMX6 layer 3 routing daemon supporting IPv4, IPv6, and IPv4 over IPv6
+endef
+
+define Package/bmx6
+  $(call Package/bmx6/Default)
+  MENU:=1
+endef
+
+define Package/bmx6-uci-config
+  $(call Package/bmx6/Default)
+  DEPENDS:=bmx6 +libuci
+  TITLE:=configuration plugin based on uci (recommended!)
+endef
+
+
+define Package/bmx6-json
+  $(call Package/bmx6/Default)
+  DEPENDS:=bmx6 +libjson
+  TITLE:=json plugin based on jsonc
+endef
+
+define Package/bmx6-sms
+  $(call Package/bmx6/Default)
+  DEPENDS:=bmx6
+  TITLE:=sms plugin
+endef
+
+define Package/bmx6-quagga
+  $(call Package/bmx6/Default)
+  DEPENDS:=bmx6 +qmp-quagga @BROKEN
+  TITLE:=bmx6 quagga plugin to redistribute/export routes (needs manet/bmx6 patched quagga 0.99.21)
+endef
+
+define Build/Configure
+       mkdir -p $(PKG_INSTALL_DIR)
+endef
+
+define Build/Compile
+       $(MAKE) -C $(PKG_BUILD_DIR) $(MAKE_ARGS)
+endef
+
+
+define Package/bmx6/install
+       $(INSTALL_DIR) $(1)/usr/sbin $(1)/etc/config $(1)/etc/init.d
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/bmx6 $(1)/usr/sbin/bmx6
+endef
+
+
+define Package/bmx6-uci-config/conffiles
+/etc/config/bmx6
+endef
+
+
+define Package/bmx6-uci-config/install
+       $(INSTALL_DIR) $(1)/usr/lib $(1)/etc/config $(1)/etc/init.d
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/bmx6_uci_config/bmx6_config.so $(1)/usr/lib/bmx6_config.so
+       $(INSTALL_BIN) ./files/etc/init.d/bmx6 $(1)/etc/init.d/bmx6
+       $(INSTALL_DATA) ./files/etc/config/bmx6 $(1)/etc/config/bmx6
+endef
+
+define Package/bmx6-json/install
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/bmx6_json/bmx6_json.so $(1)/usr/lib/bmx6_json.so
+endef
+
+define Package/bmx6-sms/install
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/bmx6_sms/bmx6_sms.so $(1)/usr/lib/bmx6_sms.so
+endef
+
+define Package/bmx6-quagga/install
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/bmx6_quagga/bmx6_quagga.so $(1)/usr/lib/bmx6_quagga.so
+endef
+
+$(eval $(call BuildPackage,bmx6))
+$(eval $(call BuildPackage,bmx6-uci-config))
+$(eval $(call BuildPackage,bmx6-json))
+$(eval $(call BuildPackage,bmx6-sms))
+$(eval $(call BuildPackage,bmx6-quagga))
+
diff --git a/bmx6/files/etc/config/bmx6 b/bmx6/files/etc/config/bmx6
new file mode 100644 (file)
index 0000000..884fd12
--- /dev/null
@@ -0,0 +1,82 @@
+
+# for more information:
+# http://bmx6.net/projects/bmx6/wiki
+# options execute: bmx6 --help
+
+config 'bmx6' 'general'
+#       option 'runtimeDir' '/var/run/bmx6'
+#       option 'tun4Address' '10.202.0.116/32'
+#       option 'tun4Address' '10.254.10.0/32'
+#       option 'tun6Address' '2012:0:0:1000::1/64'
+
+#config 'ipVersion' 'ipVersion'
+#       option 'ipVersion' '6'   # default is 4
+#       option 'throwRules' '0'
+
+
+#config 'plugin'
+#        option 'plugin' 'bmx6_config.so'
+
+
+
+#config 'plugin'
+#        option 'plugin' 'bmx6_json.so'
+
+
+
+#config 'plugin'
+#        option 'plugin' 'bmx6_sms.so'
+
+
+config 'dev' 'mesh_1'
+        option 'dev' 'eth0.12'
+
+config 'dev' 'mesh_2'
+        option 'dev' 'ath0.12'
+
+
+
+#config 'hna' 'my_global_prefix'
+#       option 'hna' '2012:0:0:74:0:0:0:0/64'
+
+
+#config 'tunOut'
+#        option 'tunOut' 'ip6'
+#        option 'network' '2012::/16'
+#        option 'exportDistance' '0'
+
+#config 'tunOut'
+#        option 'tunOut' 'ip4'
+#        option 'network' '10.254.0.0/16'
+#        option 'exportDistance' '0'  # requires quagga plugin !
+#        option 'minPrefixLen' '27'
+
+
+
+#config 'plugin'
+#        option 'plugin' 'bmx6_quagga.so'
+
+
+
+#config 'redistribute'
+#        option 'redistribute' 'ospf6'
+#        option 'network' '10.0.0.0/8'
+#        option 'minPrefixLen' '10'
+#        option 'bandwidth' '10000000'
+#        option 'ospf6' '1'
+#        option 'aggregatePrefixLen' '16'
+
+#config 'redistribute'
+#        option 'redistribute' 'bgp'
+#        option 'network' '0.0.0.0/0'
+#        option 'minPrefixLen' '0'
+#        option 'maxPrefixLen' '24'
+#        option 'bandwidth' '10000000'
+#        option 'bgp' '1'
+#        option 'aggregatePrefixLen' '8'
+
+
+
+
+
+
diff --git a/bmx6/files/etc/init.d/bmx6 b/bmx6/files/etc/init.d/bmx6
new file mode 100755 (executable)
index 0000000..4926e2b
--- /dev/null
@@ -0,0 +1,40 @@
+#!/bin/sh /etc/rc.common
+#    Copyright (C) 2011 Fundacio Privada per a la Xarxa Oberta, Lliure i Neutral guifi.net
+#
+#    This program is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License along
+#    with this program; if not, write to the Free Software Foundation, Inc.,
+#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+#    The full GNU General Public License is included in this distribution in
+#    the file called "COPYING".
+
+START=91
+
+BIN=/usr/sbin/bmx6
+CONF=/etc/config/bmx6
+PID=/var/run/bmx6/pid
+
+
+start() {
+       cd /root/
+       ulimit -c 20000
+       $BIN -f $CONF -d0 > /dev/null &
+}
+
+stop() {
+       start-stop-daemon -p $PID -K
+}
+
+restart() {
+       stop; sleep 3; start
+}
diff --git a/bmxd/Makefile b/bmxd/Makefile
new file mode 100644 (file)
index 0000000..5dfd7f9
--- /dev/null
@@ -0,0 +1,75 @@
+#
+# Copyright (C) 2008 Freifunk Leipzig
+# Copyright (C) 2008-2010 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=bmxd
+
+PKG_SOURCE_PROTO:=git
+PKG_SOURCE_URL:=git://github.com/axn/bmxd.git
+PKG_REV:=9c1d12b554dccd2efde249f5e44a7d4de59ce1a8
+PKG_VERSION:=r2012011001
+#PKG_RELEASE:=1
+PKG_SOURCE_VERSION:=$(PKG_REV)
+PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
+PKG_SOURCE:=$(PKG_SOURCE_SUBDIR).tar.gz
+PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_SOURCE_SUBDIR)
+
+PKG_EXTRA_CFLAGS:=-DNODEBUGALL
+
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/bmxd/Default
+  URL:=http://www.bmx6.net/
+  MAINTAINER:=Axel Neumann <neumann@cgws.de>
+endef
+
+define Package/bmxd
+$(call Package/bmxd/Default)
+  SECTION:=net
+  CATEGORY:=Network
+  SUBMENU:=Routing and Redirection
+  DEPENDS:=+kmod-tun
+  TITLE:=B.a.t.M.a.n. eXperimental (BMX) layer 3 routing daemon
+endef
+
+define Package/bmxd/conffiles
+/etc/config/bmxd
+endef
+
+
+define Package/bmxd/description
+B.a.t.M.a.n. eXperimental (BMX) layer 3 routing daemon
+endef
+
+MAKE_ARGS += \
+       EXTRA_CFLAGS="$(TARGET_CFLAGS) $(PKG_EXTRA_CFLAGS)" \
+       CCFLAGS="$(TARGET_CFLAGS)" \
+       OFLAGS="$(TARGET_CFLAGS)" \
+       REVISION="$(PKG_REV)" \
+       CC="$(TARGET_CC)" \
+       NODEBUG=1 \
+       UNAME="Linux" \
+       INSTALL_PREFIX="$(PKG_INSTALL_DIR)" \
+       STRIP="/bin/true" \
+       bmxd install
+
+define Build/Compile
+       mkdir -p $(PKG_INSTALL_DIR)/usr/sbin
+       $(MAKE) -C $(PKG_BUILD_DIR) $(MAKE_ARGS)
+endef
+
+define Package/bmxd/install
+       $(INSTALL_DIR) $(1)/usr/sbin $(1)/etc/config $(1)/etc/init.d
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/bmxd $(1)/usr/sbin/bmxd
+       $(INSTALL_BIN) ./files/etc/init.d/bmxd $(1)/etc/init.d
+       $(INSTALL_DATA) ./files/etc/config/bmxd $(1)/etc/config
+endef
+
+$(eval $(call BuildPackage,bmxd))
diff --git a/bmxd/files/etc/config/bmxd b/bmxd/files/etc/config/bmxd
new file mode 100644 (file)
index 0000000..c8619e9
--- /dev/null
@@ -0,0 +1,9 @@
+config bmxd general
+       option interface                'ath0'
+#      option announce                 
+#      option gateway_class            
+#      option originator_interval      
+#      option preferred_gateway        
+#      option routing_class            
+#      option visualisation_srv        
+#      option misc                     'base-port=14305'       
diff --git a/bmxd/files/etc/init.d/bmxd b/bmxd/files/etc/init.d/bmxd
new file mode 100644 (file)
index 0000000..d23b4c8
--- /dev/null
@@ -0,0 +1,51 @@
+#!/bin/sh /etc/rc.common
+START=91
+. /lib/config/uci.sh
+uci_load bmxd
+start () {
+       interface="$(uci get bmxd.general.interface)"
+       if [ "$interface" = "" ]; then
+               echo $1 Error, you must specify at least a network interface
+               exit
+       fi
+       announce=$(uci get bmxd.general.announce)
+       gateway_class=$(uci get bmxd.general.gateway_class)
+       originator_interval=$(uci get bmxd.general.originator_interval)
+       preferred_gateway=$(uci get bmxd.general.preferred_gateway)
+       routing_class=$(uci get bmxd.general.routing_class)
+       visualisation_srv=$(uci get bmxd.general.visualisation_srv)
+       misc="$(uci get bmxd.general.misc)"
+       bmx_args=""
+
+       if [ $announce ]; then
+               bmx_args=${bmx_args}'-a '$announce' '
+       fi
+
+       if [ $gateway_class ]; then
+               bmx_args=${bmx_args}'-g '$gateway_class' '
+       fi
+
+       if [ $originator_interval ]; then
+               bmx_args=${bmx_args}'-o '$originator_interval' '
+       fi
+
+       if [ $preferred_gateway ]; then
+               bmx_args=${bmx_args}'-p '$preferred_gateway' '
+       fi
+
+       if [ $routing_class ]; then
+               bmx_args=${bmx_args}'-r '$routing_class' '
+       fi
+               
+       if [ $visualisation_srv ]; then
+               bmx_args=${bmx_args}'-s '$visualisation_srv' '
+       fi
+       
+
+       bmx_args="$misc ${bmx_args} $interface"
+       bmxd $bmx_args >/dev/null 2>&1
+}
+
+stop () {
+       killall bmxd
+}
diff --git a/luci-app-bmx6/COPYING b/luci-app-bmx6/COPYING
new file mode 100644 (file)
index 0000000..d511905
--- /dev/null
@@ -0,0 +1,339 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                           Preamble
+
+  The licenses for most software are designed to take away your
+freedom to share and change it.  By contrast, the GNU General Public
+License is intended to guarantee your freedom to share and change free
+software--to make sure the software is free for all its users.  This
+General Public License applies to most of the Free Software
+Foundation's software and to any other program whose authors commit to
+using it.  (Some other Free Software Foundation software is covered by
+the GNU Lesser General Public License instead.)  You can apply it to
+your programs, too.
+
+  When we speak of free software, we are referring to freedom, not
+price.  Our General Public Licenses are designed to make sure that you
+have the freedom to distribute copies of free software (and charge for
+this service if you wish), that you receive source code or can get it
+if you want it, that you can change the software or use pieces of it
+in new free programs; and that you know you can do these things.
+
+  To protect your rights, we need to make restrictions that forbid
+anyone to deny you these rights or to ask you to surrender the rights.
+These restrictions translate to certain responsibilities for you if you
+distribute copies of the software, or if you modify it.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must give the recipients all the rights that
+you have.  You must make sure that they, too, receive or can get the
+source code.  And you must show them these terms so they know their
+rights.
+
+  We protect your rights with two steps: (1) copyright the software, and
+(2) offer you this license which gives you legal permission to copy,
+distribute and/or modify the software.
+
+  Also, for each author's protection and ours, we want to make certain
+that everyone understands that there is no warranty for this free
+software.  If the software is modified by someone else and passed on, we
+want its recipients to know that what they have is not the original, so
+that any problems introduced by others will not reflect on the original
+authors' reputations.
+
+  Finally, any free program is threatened constantly by software
+patents.  We wish to avoid the danger that redistributors of a free
+program will individually obtain patent licenses, in effect making the
+program proprietary.  To prevent this, we have made it clear that any
+patent must be licensed for everyone's free use or not licensed at all.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                   GNU GENERAL PUBLIC LICENSE
+   TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+  0. This License applies to any program or other work which contains
+a notice placed by the copyright holder saying it may be distributed
+under the terms of this General Public License.  The "Program", below,
+refers to any such program or work, and a "work based on the Program"
+means either the Program or any derivative work under copyright law:
+that is to say, a work containing the Program or a portion of it,
+either verbatim or with modifications and/or translated into another
+language.  (Hereinafter, translation is included without limitation in
+the term "modification".)  Each licensee is addressed as "you".
+
+Activities other than copying, distribution and modification are not
+covered by this License; they are outside its scope.  The act of
+running the Program is not restricted, and the output from the Program
+is covered only if its contents constitute a work based on the
+Program (independent of having been made by running the Program).
+Whether that is true depends on what the Program does.
+
+  1. You may copy and distribute verbatim copies of the Program's
+source code as you receive it, in any medium, provided that you
+conspicuously and appropriately publish on each copy an appropriate
+copyright notice and disclaimer of warranty; keep intact all the
+notices that refer to this License and to the absence of any warranty;
+and give any other recipients of the Program a copy of this License
+along with the Program.
+
+You may charge a fee for the physical act of transferring a copy, and
+you may at your option offer warranty protection in exchange for a fee.
+
+  2. You may modify your copy or copies of the Program or any portion
+of it, thus forming a work based on the Program, and copy and
+distribute such modifications or work under the terms of Section 1
+above, provided that you also meet all of these conditions:
+
+    a) You must cause the modified files to carry prominent notices
+    stating that you changed the files and the date of any change.
+
+    b) You must cause any work that you distribute or publish, that in
+    whole or in part contains or is derived from the Program or any
+    part thereof, to be licensed as a whole at no charge to all third
+    parties under the terms of this License.
+
+    c) If the modified program normally reads commands interactively
+    when run, you must cause it, when started running for such
+    interactive use in the most ordinary way, to print or display an
+    announcement including an appropriate copyright notice and a
+    notice that there is no warranty (or else, saying that you provide
+    a warranty) and that users may redistribute the program under
+    these conditions, and telling the user how to view a copy of this
+    License.  (Exception: if the Program itself is interactive but
+    does not normally print such an announcement, your work based on
+    the Program is not required to print an announcement.)
+
+These requirements apply to the modified work as a whole.  If
+identifiable sections of that work are not derived from the Program,
+and can be reasonably considered independent and separate works in
+themselves, then this License, and its terms, do not apply to those
+sections when you distribute them as separate works.  But when you
+distribute the same sections as part of a whole which is a work based
+on the Program, the distribution of the whole must be on the terms of
+this License, whose permissions for other licensees extend to the
+entire whole, and thus to each and every part regardless of who wrote it.
+
+Thus, it is not the intent of this section to claim rights or contest
+your rights to work written entirely by you; rather, the intent is to
+exercise the right to control the distribution of derivative or
+collective works based on the Program.
+
+In addition, mere aggregation of another work not based on the Program
+with the Program (or with a work based on the Program) on a volume of
+a storage or distribution medium does not bring the other work under
+the scope of this License.
+
+  3. You may copy and distribute the Program (or a work based on it,
+under Section 2) in object code or executable form under the terms of
+Sections 1 and 2 above provided that you also do one of the following:
+
+    a) Accompany it with the complete corresponding machine-readable
+    source code, which must be distributed under the terms of Sections
+    1 and 2 above on a medium customarily used for software interchange; or,
+
+    b) Accompany it with a written offer, valid for at least three
+    years, to give any third party, for a charge no more than your
+    cost of physically performing source distribution, a complete
+    machine-readable copy of the corresponding source code, to be
+    distributed under the terms of Sections 1 and 2 above on a medium
+    customarily used for software interchange; or,
+
+    c) Accompany it with the information you received as to the offer
+    to distribute corresponding source code.  (This alternative is
+    allowed only for noncommercial distribution and only if you
+    received the program in object code or executable form with such
+    an offer, in accord with Subsection b above.)
+
+The source code for a work means the preferred form of the work for
+making modifications to it.  For an executable work, complete source
+code means all the source code for all modules it contains, plus any
+associated interface definition files, plus the scripts used to
+control compilation and installation of the executable.  However, as a
+special exception, the source code distributed need not include
+anything that is normally distributed (in either source or binary
+form) with the major components (compiler, kernel, and so on) of the
+operating system on which the executable runs, unless that component
+itself accompanies the executable.
+
+If distribution of executable or object code is made by offering
+access to copy from a designated place, then offering equivalent
+access to copy the source code from the same place counts as
+distribution of the source code, even though third parties are not
+compelled to copy the source along with the object code.
+
+  4. You may not copy, modify, sublicense, or distribute the Program
+except as expressly provided under this License.  Any attempt
+otherwise to copy, modify, sublicense or distribute the Program is
+void, and will automatically terminate your rights under this License.
+However, parties who have received copies, or rights, from you under
+this License will not have their licenses terminated so long as such
+parties remain in full compliance.
+
+  5. You are not required to accept this License, since you have not
+signed it.  However, nothing else grants you permission to modify or
+distribute the Program or its derivative works.  These actions are
+prohibited by law if you do not accept this License.  Therefore, by
+modifying or distributing the Program (or any work based on the
+Program), you indicate your acceptance of this License to do so, and
+all its terms and conditions for copying, distributing or modifying
+the Program or works based on it.
+
+  6. Each time you redistribute the Program (or any work based on the
+Program), the recipient automatically receives a license from the
+original licensor to copy, distribute or modify the Program subject to
+these terms and conditions.  You may not impose any further
+restrictions on the recipients' exercise of the rights granted herein.
+You are not responsible for enforcing compliance by third parties to
+this License.
+
+  7. If, as a consequence of a court judgment or allegation of patent
+infringement or for any other reason (not limited to patent issues),
+conditions are imposed on you (whether by court order, agreement or
+otherwise) that contradict the conditions of this License, they do not
+excuse you from the conditions of this License.  If you cannot
+distribute so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you
+may not distribute the Program at all.  For example, if a patent
+license would not permit royalty-free redistribution of the Program by
+all those who receive copies directly or indirectly through you, then
+the only way you could satisfy both it and this License would be to
+refrain entirely from distribution of the Program.
+
+If any portion of this section is held invalid or unenforceable under
+any particular circumstance, the balance of the section is intended to
+apply and the section as a whole is intended to apply in other
+circumstances.
+
+It is not the purpose of this section to induce you to infringe any
+patents or other property right claims or to contest validity of any
+such claims; this section has the sole purpose of protecting the
+integrity of the free software distribution system, which is
+implemented by public license practices.  Many people have made
+generous contributions to the wide range of software distributed
+through that system in reliance on consistent application of that
+system; it is up to the author/donor to decide if he or she is willing
+to distribute software through any other system and a licensee cannot
+impose that choice.
+
+This section is intended to make thoroughly clear what is believed to
+be a consequence of the rest of this License.
+
+  8. If the distribution and/or use of the Program is restricted in
+certain countries either by patents or by copyrighted interfaces, the
+original copyright holder who places the Program under this License
+may add an explicit geographical distribution limitation excluding
+those countries, so that distribution is permitted only in or among
+countries not thus excluded.  In such case, this License incorporates
+the limitation as if written in the body of this License.
+
+  9. The Free Software Foundation may publish revised and/or new versions
+of the General Public License from time to time.  Such new versions will
+be similar in spirit to the present version, but may differ in detail to
+address new problems or concerns.
+
+Each version is given a distinguishing version number.  If the Program
+specifies a version number of this License which applies to it and "any
+later version", you have the option of following the terms and conditions
+either of that version or of any later version published by the Free
+Software Foundation.  If the Program does not specify a version number of
+this License, you may choose any version ever published by the Free Software
+Foundation.
+
+  10. If you wish to incorporate parts of the Program into other free
+programs whose distribution conditions are different, write to the author
+to ask for permission.  For software which is copyrighted by the Free
+Software Foundation, write to the Free Software Foundation; we sometimes
+make exceptions for this.  Our decision will be guided by the two goals
+of preserving the free status of all derivatives of our free software and
+of promoting the sharing and reuse of software generally.
+
+                           NO WARRANTY
+
+  11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
+FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW.  EXCEPT WHEN
+OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
+PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
+OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.  THE ENTIRE RISK AS
+TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU.  SHOULD THE
+PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
+REPAIR OR CORRECTION.
+
+  12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
+REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
+INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
+OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
+TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
+YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
+PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
+POSSIBILITY OF SUCH DAMAGES.
+
+                    END OF TERMS AND CONDITIONS
+
+           How to Apply These Terms to Your New Programs
+
+  If you develop a new program, and you want it to be of the greatest
+possible use to the public, the best way to achieve this is to make it
+free software which everyone can redistribute and change under these terms.
+
+  To do so, attach the following notices to the program.  It is safest
+to attach them to the start of each source file to most effectively
+convey the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+Also add information on how to contact you by electronic and paper mail.
+
+If the program is interactive, make it output a short notice like this
+when it starts in an interactive mode:
+
+    Gnomovision version 69, Copyright (C) year name of author
+    Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
+    This is free software, and you are welcome to redistribute it
+    under certain conditions; type `show c' for details.
+
+The hypothetical commands `show w' and `show c' should show the appropriate
+parts of the General Public License.  Of course, the commands you use may
+be called something other than `show w' and `show c'; they could even be
+mouse-clicks or menu items--whatever suits your program.
+
+You should also get your employer (if you work as a programmer) or your
+school, if any, to sign a "copyright disclaimer" for the program, if
+necessary.  Here is a sample; alter the names:
+
+  Yoyodyne, Inc., hereby disclaims all copyright interest in the program
+  `Gnomovision' (which makes passes at compilers) written by James Hacker.
+
+  <signature of Ty Coon>, 1 April 1989
+  Ty Coon, President of Vice
+
+This General Public License does not permit incorporating your program into
+proprietary programs.  If your program is a subroutine library, you may
+consider it more useful to permit linking proprietary applications with the
+library.  If this is what you want to do, use the GNU Lesser General
+Public License instead of this License.
diff --git a/luci-app-bmx6/Makefile b/luci-app-bmx6/Makefile
new file mode 100644 (file)
index 0000000..4e8ce1b
--- /dev/null
@@ -0,0 +1,61 @@
+#    Copyright (C) 2011 Pau Escrich <pau@dabax.net>
+#
+#    This program is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License along
+#    with this program; if not, write to the Free Software Foundation, Inc.,
+#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+#    The full GNU General Public License is included in this distribution in
+#    the file called "COPYING".
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=luci-app-bmx6
+PKG_RELEASE:=2
+
+PKG_BUILD_DIR := $(BUILD_DIR)/$(PKG_NAME)
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/luci-app-bmx6
+  SECTION:=luci
+  CATEGORY:=LuCI
+  SUBMENU:=3. Applications
+  TITLE:= bmx6 configuration, status and visualization module
+  DEPENDS:=+luci-lib-json +luci-mod-admin-core +luci-lib-httpclient +bmx6
+  MAINTAINER:= Pau Escrich <p4u@dabax.net>
+endef
+
+define Package/luci-app-bmx6/description
+       bmx6 web module for LuCi web interface
+endef
+
+define Package/luci-app-bmx6/conffiles
+       /etc/config/luci-bmx6
+endef
+
+define Build/Prepare
+endef
+
+define Build/Configure
+endef
+
+define Build/Compile
+endef
+
+define Package/luci-app-bmx6/install
+       $(CP) ./files/* $(1)/
+       chmod 755 $(1)/www/cgi-bin/bmx6-info
+endef
+
+$(eval $(call BuildPackage,luci-app-bmx6))
+
diff --git a/luci-app-bmx6/files/etc/config/luci-bmx6 b/luci-app-bmx6/files/etc/config/luci-bmx6
new file mode 100644 (file)
index 0000000..df1e715
--- /dev/null
@@ -0,0 +1,7 @@
+config 'bmx6' 'luci'
+       option ignore '0'
+       option place 'admin network BMX6'
+       #option place 'qmp Mesh'
+       option position '3'
+       #option json 'http://127.0.0.1/cgi-bin/bmx6-info?'
+       option json 'exec:/www/cgi-bin/bmx6-info -s'
diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/controller/bmx6.lua b/luci-app-bmx6/files/usr/lib/lua/luci/controller/bmx6.lua
new file mode 100644 (file)
index 0000000..cb5642e
--- /dev/null
@@ -0,0 +1,329 @@
+--[[
+    Copyright (C) 2011 Pau Escrich <pau@dabax.net>
+    Contributors Jo-Philipp Wich <xm@subsignal.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+    The full GNU General Public License is included in this distribution in
+    the file called "COPYING".
+--]]
+
+local bmx6json = require("luci.model.bmx6json")
+
+module("luci.controller.bmx6", package.seeall)
+
+function index()
+       local place = {}
+       local ucim = require "luci.model.uci"
+       local uci = ucim.cursor()
+
+       -- checking if ignore is on
+       if uci:get("luci-bmx6","luci","ignore") == "1" then
+               return nil
+       end
+
+       -- getting value from uci database
+       local uci_place = uci:get("luci-bmx6","luci","place")
+
+       -- default values
+       if uci_place == nil then
+               place = {"bmx6"}
+       else
+               local util = require "luci.util"
+               place = util.split(uci_place," ")
+       end
+
+       -- getting position of menu
+       local uci_position = uci:get("luci-bmx6","luci","position")
+
+       ---------------------------
+       -- Starting with the pages
+       ---------------------------
+
+       --- status (default)
+       entry(place,call("action_nodes_j"),place[#place],tonumber(uci_position))            
+
+       table.insert(place,"Status")
+       entry(place,call("action_status_j"),"Status",0)
+       table.remove(place)
+
+       -- not visible
+       table.insert(place,"nodes_nojs")
+       entry(place, call("action_nodes"), nil)
+       table.remove(place)
+
+       --- nodes
+       table.insert(place,"Nodes")
+       entry(place,call("action_nodes_j"),"Nodes",1)
+       table.remove(place)
+
+               --- links
+       table.insert(place,"Links")
+       entry(place,call("action_links"),"Links",2).leaf = true
+       table.remove(place)
+
+       -- Tunnels
+       table.insert(place,"Tunnels")
+       entry(place,call("action_tunnels_j"), "Tunnels", 3).leaf = true
+       table.remove(place)
+
+       -- Gateways (deprecated)
+       --table.insert(place,"Gateways")
+       --entry(place,call("action_gateways_j"),"Gateways").leaf = true
+       --table.remove(place)
+
+       --- Chat
+       table.insert(place,"Chat")
+       entry(place,call("action_chat"),"Chat",5)
+       table.remove(place)
+
+       --- Graph
+       table.insert(place,"Graph")
+       entry(place, template("bmx6/graph"), "Graph",4)
+       table.remove(place)
+
+       --- Topology (hidden)
+       table.insert(place,"topology")
+       entry(place, call("action_topology"), nil)
+       table.remove(place)
+
+       --- configuration (CBI)
+       table.insert(place,"Configuration")
+       entry(place, cbi("bmx6/main"), "Configuration",6).dependent=false
+
+       table.insert(place,"General")
+       entry(place, cbi("bmx6/main"), "General",1)
+       table.remove(place)
+
+       table.insert(place,"Advanced")
+       entry(place, cbi("bmx6/advanced"), "Advanced",5)
+       table.remove(place)
+
+       table.insert(place,"Interfaces")
+       entry(place, cbi("bmx6/interfaces"), "Interfaces",2)
+       table.remove(place)
+
+       table.insert(place,"Tunnels")
+        entry(place, cbi("bmx6/tunnels"), "Tunnels",3)
+        table.remove(place)
+
+       table.insert(place,"Plugins")
+       entry(place, cbi("bmx6/plugins"), "Plugins",6)
+       table.remove(place)
+
+       table.insert(place,"HNAv6")
+       entry(place, cbi("bmx6/hna"), "HNAv6",4)
+       table.remove(place)
+
+       table.remove(place)
+
+end
+
+function action_status()
+               local status = bmx6json.get("status").status or nil
+               local interfaces = bmx6json.get("interfaces").interfaces or nil
+
+               if status == nil or interfaces == nil then
+                       luci.template.render("bmx6/error", {txt="Cannot fetch data from bmx6 json"})
+               else
+               luci.template.render("bmx6/status", {status=status,interfaces=interfaces})
+               end
+end
+
+function action_status_j()
+       luci.template.render("bmx6/status_j", {})
+end
+
+
+function action_nodes()
+               local orig_list = bmx6json.get("originators").originators or nil
+
+               if orig_list == nil then
+                       luci.template.render("bmx6/error", {txt="Cannot fetch data from bmx6 json"})
+                       return nil
+               end
+
+               local originators = {}
+               local desc = nil
+               local orig = nil
+               local name = ""
+               local ipv4 = ""
+
+               for _,o in ipairs(orig_list) do
+                       orig = bmx6json.get("originators/"..o.name) or {}
+                       desc = bmx6json.get("descriptions/"..o.name) or {}
+
+                       if string.find(o.name,'.') then
+                               name = luci.util.split(o.name,'.')[1]
+                       else
+                               name = o.name
+                       end
+
+                       table.insert(originators,{name=name,orig=orig,desc=desc})
+               end
+
+        luci.template.render("bmx6/nodes", {originators=originators})
+end
+
+function action_nodes_j()
+       local http = require "luci.http"
+       local link_non_js = "/cgi-bin/luci" .. http.getenv("PATH_INFO") .. '/nodes_nojs'
+
+       luci.template.render("bmx6/nodes_j", {link_non_js=link_non_js})
+end
+
+function action_gateways_j()
+       luci.template.render("bmx6/gateways_j", {})
+end
+
+function action_tunnels_j()
+        luci.template.render("bmx6/tunnels_j", {})
+end
+
+
+function action_links(host)
+       local links = bmx6json.get("links", host)
+       local devlinks = {}
+       local _,l
+
+       if links ~= nil then
+               links = links.links
+               for _,l in ipairs(links) do
+                       devlinks[l.viaDev] = {}
+               end
+               for _,l in ipairs(links) do
+                       l.globalId = luci.util.split(l.globalId,'.')[1]
+                       table.insert(devlinks[l.viaDev],l)
+               end
+       end
+
+       luci.template.render("bmx6/links", {links=devlinks})
+end
+
+function action_topology()
+       local originators = bmx6json.get("originators/all")
+       local o,i,l,i2
+       local first = true
+       local topology = '[ '
+       local cache = '/tmp/bmx6-topology.json'
+       local offset = 60
+
+       local cachefd = io.open(cache,r)
+       local update = false
+
+       if cachefd ~= nil then
+               local lastupdate = tonumber(cachefd:read("*line")) or 0
+               if os.time() >= lastupdate + offset then
+                       update = true
+               else
+                       topology = cachefd:read("*all")
+               end
+               cachefd:close()
+       end
+
+       if cachefd == nil or update then
+               for i,o in ipairs(originators) do
+                       local links = bmx6json.get("links",o.primaryIp)
+                       if links then
+                               if first then
+                                       first = false
+                               else
+                                       topology = topology .. ', '
+                               end
+           
+                               topology = topology .. '{ "globalId": "%s", "links": [' %o.globalId:match("^[^%.]+")
+           
+                               local first2 = true
+           
+                               for i2,l in ipairs(links.links) do
+                                       if first2 then
+                                               first2 = false
+                                       else
+                                               topology = topology .. ', '
+                                       end
+           
+                                       topology = topology .. '{ "globalId": "%s", "rxRate": %s, "txRate": %s }'
+                                               %{ l.globalId:match("^[^%.]+"), l.rxRate, l.txRate }
+           
+                               end
+           
+                               topology = topology .. ']}'
+                       end
+           
+               end
+               
+               topology = topology .. ' ]'
+
+               -- Upgrading the content of the cache file
+               cachefd = io.open(cache,'w+')
+               cachefd:write(os.time()..'\n')
+               cachefd:write(topology)
+               cachefd:close()
+       end
+
+       luci.http.prepare_content("application/json")
+       luci.http.write(topology)
+end
+
+
+function action_chat()
+       local sms_dir = "/var/run/bmx6/sms"
+       local rcvd_dir = sms_dir .. "/rcvdSms"
+       local send_file = sms_dir .. "/sendSms/chat"
+       local sms_list = bmx6json.get("rcvdSms")
+       local sender = ""
+       local sms_file = ""
+       local chat = {}
+       local to_send = nil
+       local sent = ""
+       local fd = nil
+
+       if luci.sys.call("test -d " .. sms_dir) ~= 0 then
+               luci.template.render("bmx6/error", {txt="sms plugin disabled or some problem with directory " .. sms_dir})
+               return nil
+       end
+
+       sms_list = luci.util.split(luci.util.exec("ls "..rcvd_dir.."/*:chat"))
+
+       for _,sms_path in ipairs(sms_list) do
+         if #sms_path > #rcvd_dir then
+               sms_file = luci.util.split(sms_path,'/')
+               sms_file = sms_file[#sms_file]
+               sender = luci.util.split(sms_file,':')[1]
+
+               -- Trying to clean the name
+               if string.find(sender,".") ~= nil then
+                       sender = luci.util.split(sender,".")[1]
+               end
+
+               fd = io.open(sms_path,"r")
+               chat[sender] = fd:read()
+               fd:close()
+         end
+       end
+
+       to_send = luci.http.formvalue("toSend")
+       if to_send ~= nil and #to_send > 1  then
+               fd = io.open(send_file,"w")
+               fd:write(to_send)
+               fd:close()
+               sent = to_send
+       else
+               sent = luci.util.exec("cat "..send_file)
+       end
+
+       luci.template.render("bmx6/chat", {chat=chat,sent=sent})
+end
+
diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/model/bmx6json.lua b/luci-app-bmx6/files/usr/lib/lua/luci/model/bmx6json.lua
new file mode 100644 (file)
index 0000000..dfe9ab1
--- /dev/null
@@ -0,0 +1,219 @@
+--[[
+    Copyright (C) 2011 Pau Escrich <pau@dabax.net>
+    Contributors Jo-Philipp Wich <xm@subsignal.org>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+    The full GNU General Public License is included in this distribution in
+    the file called "COPYING".
+--]]
+
+local ltn12 = require("luci.ltn12")
+local json = require("luci.json")
+local util = require("luci.util")
+local uci = require("luci.model.uci")
+local sys = require("luci.sys")
+local template = require("luci.template")
+local http = require("luci.http")
+local string = require("string")
+local table = require("table")
+local nixio = require("nixio")
+local nixiofs = require("nixio.fs")
+local ipairs = ipairs
+
+module "luci.model.bmx6json"
+
+-- Returns a LUA object from bmx6 JSON daemon
+
+function get(field, host)
+       local url
+       if host ~= nil then
+               if host:match(":") then
+                       url = 'http://[%s]/cgi-bin/bmx6-info?' % host
+               else
+                       url = 'http://%s/cgi-bin/bmx6-info?' % host
+               end
+       else
+               url = uci.cursor():get("luci-bmx6","luci","json")
+       end
+
+       if url == nil then
+                print_error("bmx6 json url not configured, cannot fetch bmx6 daemon data",true)
+                return nil
+        end
+
+        local json_url = util.split(url,":")
+        local raw = ""
+
+       if json_url[1] == "http"  then
+               raw,err = wget(url..field,1000)
+       else
+
+               if json_url[1] == "exec" then
+                       raw = sys.exec(json_url[2]..' '..field)
+               else
+                       print_error("bmx6 json url not recognized, cannot fetch bmx6 daemon data. Use http: or exec:",true)
+                       return nil
+               end
+
+       end
+
+       local data = nil
+
+       if raw and raw:len() > 10 then
+               local decoder = json.Decoder()
+               ltn12.pump.all(ltn12.source.string(raw), decoder:sink())
+               data = decoder:get()
+--     else
+--             print_error("Cannot get data from bmx6 daemon",true)
+--             return nil
+       end
+
+       return data
+end
+
+function print_error(txt,popup)
+       util.perror(txt)
+       sys.call("logger -t bmx6json " .. txt)
+
+       if popup then
+               http.write('<script type="text/javascript">alert("Some error detected, please check it: '..txt..'");</script>')
+       else
+               http.write("<h1>Dammit! some error detected</h1>")
+               http.write("bmx6-luci: " .. txt)
+               http.write('<p><FORM><INPUT TYPE="BUTTON" VALUE="Go Back" ONCLICK="history.go(-1)"></FORM></p>')
+       end
+
+end
+
+function text2html(txt)
+       txt = string.gsub(txt,"<","{")
+       txt = string.gsub(txt,">","}")
+       txt = util.striptags(txt)
+       return txt
+end
+
+
+function wget(url, timeout)
+       local rfd, wfd = nixio.pipe()
+       local pid = nixio.fork()
+       if pid == 0 then
+               rfd:close()
+               nixio.dup(wfd, nixio.stdout)
+
+               local candidates = { "/usr/bin/wget", "/bin/wget" }
+               local _, bin
+               for _, bin in ipairs(candidates) do
+                       if nixiofs.access(bin, "x") then
+                               nixio.exec(bin, "-q", "-O", "-", url)
+                       end
+               end
+               return
+       else
+               wfd:close()
+               rfd:setblocking(false)
+
+               local buffer = { }
+               local err1, err2
+
+               while true do
+                       local ready = nixio.poll({{ fd = rfd, events = nixio.poll_flags("in") }}, timeout)
+                       if not ready then
+                               nixio.kill(pid, nixio.const.SIGKILL)
+                               err1 = "timeout"
+                               break
+                       end
+
+                       local rv = rfd:read(4096)
+                       if rv then
+                               -- eof
+                               if #rv == 0 then
+                                       break
+                               end
+
+                               buffer[#buffer+1] = rv
+                       else
+                               -- error
+                               if nixio.errno() ~= nixio.const.EAGAIN and
+                                  nixio.errno() ~= nixio.const.EWOULDBLOCK then
+                                       err1 = "error"
+                                       err2 = nixio.errno()
+                               end
+                       end
+               end
+
+               nixio.waitpid(pid, "nohang")
+               if not err1 then
+                       return table.concat(buffer)
+               else
+                       return nil, err1, err2
+               end
+       end
+end
+
+function getOptions(name)
+       -- Getting json and Checking if bmx6-json is avaiable
+       local options = get("options")
+       if options == nil or options.OPTIONS == nil then
+               m.message = "bmx6-json plugin is not running or some mistake in luci-bmx6 configuration, check /etc/config/luci-bmx6"
+               return nil
+       else
+               options = options.OPTIONS
+       end
+
+       -- Filtering by the option name
+       local i,_
+       local namedopt = nil
+       if name ~= nil then
+               for _,i in ipairs(options) do
+                       if i.name == name and i.CHILD_OPTIONS ~= nil then
+                               namedopt = i.CHILD_OPTIONS
+                               break
+                       end
+               end
+       end
+
+       return namedopt
+end
+
+-- Rturns a help string formated to be used in HTML scope
+function getHtmlHelp(opt)
+       if opt == nil then return nil end
+       
+       local help = ""
+       if opt.help ~= nil then
+               help = text2html(opt.help)
+       end
+       if opt.syntax ~= nil then
+               help = help .. "<br/><b>Syntax: </b>" .. text2html(opt.syntax)
+       end             
+
+       return help
+end
+
+function testandreload()
+       local test = sys.call('bmx6 -c --test > /tmp/bmx6-luci.err.tmp')
+       if test ~= 0 then
+               return sys.exec("cat /tmp/bmx6-luci.err.tmp")
+       end
+
+       local err = sys.call('bmx6 -c --configReload > /tmp/bmx6-luci.err.tmp')
+               if err ~= 0 then
+               return sys.exec("cat /tmp/bmx6-luci.err.tmp")
+       end
+
+       return nil
+end
+
diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/advanced.lua b/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/advanced.lua
new file mode 100644 (file)
index 0000000..9510214
--- /dev/null
@@ -0,0 +1,74 @@
+--[[
+    Copyright (C) 2011 Pau Escrich <pau@dabax.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+    The full GNU General Public License is included in this distribution in
+    the file called "COPYING".
+--]]
+
+m = Map("bmx6", "bmx6")
+
+local bmx6json = require("luci.model.bmx6json")
+local util = require("luci.util")
+local http = require("luci.http")
+local sys = require("luci.sys")
+
+local options = bmx6json.get("options")
+if options == nil or options.OPTIONS == nil then
+       m.message = "bmx6-json plugin is not running or some mistake in luci-bmx6 configuration, check /etc/config/luci-bmx6"
+       options = {}
+else
+       options = options.OPTIONS
+end
+
+local general = m:section(NamedSection,"general","general","General Options")
+
+local name = ""
+local help = ""
+local value = nil
+local _,o
+
+for _,o in ipairs(options) do
+       if o.name ~= nil and o.CHILD_OPTIONS == nil and o.configurable == 1 then
+               help = ""
+               name = o.name
+
+               if o.help ~= nil then
+                       help = bmx6json.text2html(o.help)
+               end
+
+               if o.syntax ~= nil then
+                       help = help .. "<br/><strong>Syntax: </strong>" .. bmx6json.text2html(o.syntax)
+               end
+
+               if o.def ~= nil then
+                       help = help .. "<strong> Default: </strong>" .. o.def
+               end
+
+               value = general:option(Value,name,name,help)
+
+       end
+end
+
+function m.on_commit(self,map)
+       local err = sys.call('bmx6 -c --configReload > /tmp/bmx6-luci.err.tmp')
+       if err ~= 0 then
+               m.message = sys.exec("cat /tmp/bmx6-luci.err.tmp")
+       end
+end
+
+return m
+
diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/hna.lua b/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/hna.lua
new file mode 100644 (file)
index 0000000..db98ae6
--- /dev/null
@@ -0,0 +1,47 @@
+--[[
+    Copyright (C) 2011 Pau Escrich <pau@dabax.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+    The full GNU General Public License is included in this distribution in
+    the file called "COPYING".
+--]]
+
+local sys = require("luci.sys")
+
+m = Map("bmx6", "bmx6")
+
+local hna = m:section(TypedSection,"unicastHna","IPv6 HNA")
+hna.addremove = true
+hna.anonymous = true
+local hna_option = hna:option(Value,"unicastHna", "IPv6 Host Network Announcement. Syntax <NETADDR>/<PREFIX>")
+
+--function hna_option:validate(value)
+--     local err = sys.call('bmx6 -c --test -a ' .. value)
+--     if err ~= 0 then
+--             return nil
+--     end
+--     return value
+--end
+
+function m.on_commit(self,map)
+        local err = sys.call('bmx6 -c --configReload > /tmp/bmx6-luci.err.tmp')
+       if err ~= 0 then
+               m.message = sys.exec("cat /tmp/bmx6-luci.err.tmp")
+        end
+end
+
+return m
+
diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/interfaces.lua b/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/interfaces.lua
new file mode 100644 (file)
index 0000000..fb1261b
--- /dev/null
@@ -0,0 +1,77 @@
+--[[
+    Copyright (C) 2011 Pau Escrich <pau@dabax.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+    The full GNU General Public License is included in this distribution in
+    the file called "COPYING".
+--]]
+
+local sys = require("luci.sys")
+local bmx6json = require("luci.model.bmx6json")
+local m = Map("bmx6", "bmx6")
+
+local eth_int = sys.net.devices()
+local interfaces = m:section(TypedSection,"dev","Devices","")
+interfaces.addremove = true
+interfaces.anonymous = true
+
+local intlv = interfaces:option(ListValue,"dev","Device")
+
+for _,i in ipairs(eth_int) do
+       intlv:value(i,i)
+end
+
+-- Getting json and looking for device section
+local json = bmx6json.get("options")
+
+if json == nil or json.OPTIONS == nil then
+       m.message = "bmx6-json plugin is not running or some mistake in luci-bmx6 configuration, check /etc/config/luci-bmx6"
+       json = {}
+else
+       json = json.OPTIONS
+end
+
+local dev = {}
+for _,j in ipairs(json) do
+       if j.name == "dev" and j.CHILD_OPTIONS ~= nil then
+               dev = j.CHILD_OPTIONS
+               break
+       end
+end
+
+local help = ""
+local name = ""
+
+for _,o in ipairs(dev) do
+       if o.name ~= nil then
+               help = ""
+               name = o.name
+               if o.help ~= nil then
+                       help = bmx6json.text2html(o.help)
+               end
+
+               if o.syntax ~= nil then
+                       help = help .. "<br/><strong>Syntax: </strong>" .. bmx6json.text2html(o.syntax)
+               end
+
+               value = interfaces:option(Value,name,name,help)
+               value.optional = true
+       end
+end
+
+
+return m
+
diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/main.lua b/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/main.lua
new file mode 100644 (file)
index 0000000..8c114bf
--- /dev/null
@@ -0,0 +1,108 @@
+--[[
+    Copyright (C) 2011 Pau Escrich <pau@dabax.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+    The full GNU General Public License is included in this distribution in
+    the file called "COPYING".
+--]]
+
+local sys = require("luci.sys")
+local bmx6json = require("luci.model.bmx6json")
+
+m = Map("bmx6", "bmx6")
+
+-- Getting json and Checking if bmx6-json is avaiable
+local options = bmx6json.get("options")
+if options == nil or options.OPTIONS == nil then
+        m.message = "bmx6-json plugin is not running or some mistake in luci-bmx6 configuration, check /etc/config/luci-bmx6"
+else
+       options = options.OPTIONS
+end
+
+-- Getting a list of interfaces
+local eth_int = luci.sys.net.devices()
+
+-- Getting the most important options from general
+local general = m:section(NamedSection,"general","general","General")
+general.addremove = false
+general:option(Value,"globalPrefix","Global ip prefix","Specify global prefix for interfaces: NETADDR/LENGTH. If you are using IPv6 leave blank to let bmx6 autoassign an ULA IPv6 address.")
+
+if m:get("ipVersion","ipVersion") == "6" then
+       general:option(Value,"tun4Address","IPv4 address or range","specify default IPv4 tunnel address and announced range")
+end
+
+-- IP section
+-- ipVersion section is important, we are allways showing it
+local ipV = m:section(NamedSection,"ipVersion","ipVersion","IP options")
+ipV.addremove = false
+local lipv = ipV:option(ListValue,"ipVersion","IP version")
+lipv:value("4","4")
+lipv:value("6","6")
+lipv.default = "6"
+
+-- rest of ip options are optional, getting them from json
+local ipoptions = {}
+for _,o in ipairs(options) do
+       if o.name == "ipVersion" and o.CHILD_OPTIONS ~= nil then
+               ipoptions = o.CHILD_OPTIONS
+               break
+       end
+end
+
+local help = ""
+local name = ""
+local value = nil
+
+for _,o in ipairs(ipoptions) do
+       if o.name ~= nil then
+               help = ""
+               name = o.name
+               if o.help ~= nil then
+                       help = bmx6json.text2html(o.help)
+               end
+
+               if o.syntax ~= nil then
+                       help = help .. "<br/><strong>Syntax: </strong>" .. bmx6json.text2html(o.syntax)
+               end
+
+               if o.def ~= nil then
+                       help = help .. "<br/><strong> Default: </strong>" .. bmx6json.text2html(o.def)
+               end
+
+               value = ipV:option(Value,name,name,help)
+               value.optional = true
+       end
+end
+
+-- Interfaces section
+local interfaces = m:section(TypedSection,"dev","Devices","")
+interfaces.addremove = true
+interfaces.anonymous = true
+local intlv = interfaces:option(ListValue,"dev","Device")
+
+for _,i in ipairs(eth_int) do
+       intlv:value(i,i)
+end
+
+function m.on_commit(self,map)
+    local err = sys.call('bmx6 -c --configReload > /tmp/bmx6-luci.err.tmp')
+    if err ~= 0 then
+        m.message = sys.exec("cat /tmp/bmx6-luci.err.tmp")
+    end
+end
+
+return m
+
diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/plugins.lua b/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/plugins.lua
new file mode 100644 (file)
index 0000000..518864e
--- /dev/null
@@ -0,0 +1,50 @@
+--[[
+    Copyright (C) 2011 Pau Escrich <pau@dabax.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+    The full GNU General Public License is included in this distribution in
+    the file called "COPYING".
+--]]
+local sys = require("luci.sys")
+
+m = Map("bmx6", "bmx6")
+plugins_dir = {"/usr/lib/","/var/lib","/lib"}
+
+plugin = m:section(TypedSection,"plugin","Plugin")
+plugin.addremove = true
+plugin.anonymous = true
+plv = plugin:option(ListValue,"plugin", "Plugin")
+
+for _,d in ipairs(plugins_dir) do
+       pl = luci.sys.exec("cd "..d..";ls bmx6_*")
+       if #pl > 6 then
+               for _,v in ipairs(luci.util.split(pl,"\n")) do
+                       plv:value(v,v)
+               end
+       end
+end
+
+
+function m.on_commit(self,map)
+       local err = sys.call('/etc/init.d/bmx6 restart')
+       if err ~= 0 then
+               m.message = sys.exec("Cannot restart bmx6")
+       end
+end
+
+
+return m
+
diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/tunnels.lua b/luci-app-bmx6/files/usr/lib/lua/luci/model/cbi/bmx6/tunnels.lua
new file mode 100644 (file)
index 0000000..7a6bfd3
--- /dev/null
@@ -0,0 +1,75 @@
+--[[
+    Copyright (C) 2011 Pau Escrich <pau@dabax.net>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+    The full GNU General Public License is included in this distribution in
+    the file called "COPYING".
+--]]
+
+local sys = require("luci.sys")
+local bmx6json = require("luci.model.bmx6json")
+
+m = Map("bmx6", "bmx6")
+
+-- tunOut
+local tunnelsOut = m:section(TypedSection,"tunOut",translate("Networks to fetch"),translate("Tunnel announcements to fetch if possible"))
+tunnelsOut.addremove = true
+tunnelsOut.anonymous = true
+tunnelsOut:option(Value,"tunOut","Name")
+tunnelsOut:option(Value,"network", translate("Network to fetch"))
+
+local tunoptions = bmx6json.getOptions("tunOut")
+local _,o
+for _,o in ipairs(tunoptions) do
+        if o.name ~= nil  and o.name ~= "network" then
+               help = bmx6json.getHtmlHelp(o)
+               value = tunnelsOut:option(Value,o.name,o.name,help)
+               value.optional = true
+       end
+end
+
+
+--tunIn
+local tunnelsIn = m:section(TypedSection,"tunInNet",translate("Networks to offer"),translate("Tunnels to announce in the network"))
+tunnelsIn.addremove = true
+tunnelsIn.anonymous = true
+
+local net = tunnelsIn:option(Value,"tunInNet", translate("Network to offer"))
+net.default = "10.0.0.0/8"
+
+local bwd = tunnelsIn:option(Value,"bandwidth",translate("Bandwidth (Bytes)"))
+bwd.default = "1000000"
+
+local tuninoptions = bmx6json.getOptions("tunInNet")
+local _,o
+for _,o in ipairs(tuninoptions) do
+        if o.name ~= nil  and o.name ~= "tunInNet" and o.name ~= "bandwidth" then
+               help = bmx6json.getHtmlHelp(o)
+               value = tunnelsIn:option(Value,o.name,o.name,help)
+               value.optional = true
+       end
+end
+
+function m.on_commit(self,map)
+       --Not working. If test returns error the changes are still commited
+       local msg = bmx6json.testandreload()
+       if msg ~= nil then
+               m.message = msg
+       end
+end
+
+return m
+
diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/admin_status/index/neighbours_simple.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/admin_status/index/neighbours_simple.htm
new file mode 100644 (file)
index 0000000..97d6e0e
--- /dev/null
@@ -0,0 +1,108 @@
+<script type="text/javascript">//<![CDATA[
+
+       XHR.poll(5, '/cgi-bin/bmx6-info', { '$neighbours': '' },
+               function(x, st)
+               {
+                       var originators = st.neighbours[0].originators;
+                       var descriptions = st.neighbours[1].descriptions;
+
+                       var tb = document.getElementById('descriptions_table');
+
+                       if ( originators.length != descriptions.length )
+                       {
+                               var tr = tb.insertRow(-1);
+                               tr.className = 'cbi-section-table-row';
+                               var td = tr.insertCell(-1);
+                               td.colSpan = 7;
+                               td.innerHTML = '<em><br /><%:Some problem with JSON: lenght of originators and descriptions different. %></em>';
+                               return 1;
+                       }
+
+                       if ( originators && descriptions && tb)
+                       {
+                               /* clear all rows */
+                               while( tb.rows.length > 1 )
+                                       tb.deleteRow(1);
+
+                               for( var i = 0; i < descriptions.length; i++ )
+                               {
+                                       var tr = tb.insertRow(-1);
+                                       tr.className = 'cbi-section-table-row cbi-rowstyle-' + ((i % 2) + 1);
+                                       tr.insertCell(-1).innerHTML = descriptions[i].DESC_ADV.globalId.replace(/\.[^\.]+$/,"");
+
+                                       var extensions = descriptions[i].DESC_ADV.extensions;
+
+                                       //Looking for the extensions
+                                       var hna6 = [];
+                                       for( var e = 0; e < extensions.length; e++)
+                                       {
+                                               if( extensions[e].HNA6_EXTENSION )
+                                               {
+                                                       hna6 = extensions[e].HNA6_EXTENSION;
+                                                       break;
+                                               }
+                                       }
+
+                                       //Adding first HNA with prefix=128 as main address
+                                       var ipstxt = '';
+                                       var address;
+                                       var prefix;
+
+                                       for( var e = 0; e < hna6.length; e++ )
+                                       {
+                                               address = hna6[e].address;
+                                               prefix = hna6[e].prefixlen;
+                                               if ( prefix == '128' )
+                                                       {
+                                                               ipstxt += address;
+                                                               break;
+                                                       }
+                                       }
+
+                                       tr.insertCell(-1).innerHTML = ipstxt;
+
+                                       tr.insertCell(-1).innerHTML = originators[i].viaDev;
+                                       tr.insertCell(-1).innerHTML = originators[i].metric;
+                                       tr.insertCell(-1).innerHTML = originators[i].lastDesc;
+                                       tr.insertCell(-1).innerHTML = originators[i].lastRef;
+                                       tr.insertCell(-1).innerHTML = originators[i].blocked;
+
+                               }
+
+                               if( tb.rows.length == 1 )
+                               {
+                                       var tr = tb.insertRow(-1);
+                                               tr.className = 'cbi-section-table-row';
+
+                                       var td = tr.insertCell(-1);
+                                               td.colSpan = 7;
+                                               td.innerHTML = '<em><br /><%:There are no nodes available.%></em>';
+                               }
+                       }
+               }
+       );
+//]]></script>
+
+<div class="cbi-map">
+
+<fieldset class="cbi-section">
+       <legend><%:Mesh nodes%></legend>
+       <table class="cbi-section-table" id="descriptions_table">
+               <tr class="cbi-section-table-titles">
+                       <th class="cbi-section-table-cell"><%:Hostname%></th>
+                       <th class="cbi-section-table-cell"><%:Primary IP%></th>
+                       <th class="cbi-section-table-cell"><%:Via Device%></th>
+                       <th class="cbi-section-table-cell"><%:Metric%></th>
+                       <th class="cbi-section-table-cell"><%:Last Desc%></th>
+                       <th class="cbi-section-table-cell"><%:Last Ref%></th>
+                       <th class="cbi-section-table-cell"><%:Blocked%></th>
+               </tr>
+               <tr class="cbi-section-table-row">
+                       <td colspan="7"><em><br /><%:Collecting data...%></em></td>
+               </tr>
+       </table>
+</fieldset>
+
+</div>
+
+
diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/chat.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/chat.htm
new file mode 100644 (file)
index 0000000..8e25b11
--- /dev/null
@@ -0,0 +1,35 @@
+<%+header%>
+<meta http-equiv="refresh" content="60" />
+<h2><a id="content" name="content"><%:Chat%></a></h2>
+<p>This is sms a chat where all bmx6 nodes can participate. The data is replayed using routing packets, so there is a limit of 2040 bytes. Use it only to send short messages.</p>
+<p>Each participant can only send one sms at same time.</p>
+<br />
+
+<strong>Received SMS</strong>
+<br />
+<pre style="background-color:#dadbe6;">
+<% for orig,sms in pairs(chat) do %>
+         <u><%=orig%></u>:<%=sms%>
+<% end %>
+</pre>
+
+<form action=".">
+        <input type="submit" value="refresh" />
+</form>
+
+<br />
+
+<form action="." method="post">
+       <input type="text" name="toSend" />
+       <input type="submit" value="send sms" />
+</form>
+
+<br />
+<table>
+       <tr>
+       <td><strong>Your last sms: </strong></td>
+       <td><pre><%=sent%></pre></td>
+       </tr>
+</table>
+<%+footer%>
+
diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/error.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/error.htm
new file mode 100644 (file)
index 0000000..78de7b9
--- /dev/null
@@ -0,0 +1,10 @@
+<%+header%>
+<h2><a id="content" name="content"><%:ERROR%></a></h2>
+<strong>Some error has occurred</strong>
+<br />
+<pre>
+       <%=txt%>
+</pre>
+<br />
+<%+footer%>
+
diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/gateways_j.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/gateways_j.htm
new file mode 100644 (file)
index 0000000..12bfcd4
--- /dev/null
@@ -0,0 +1,120 @@
+<%+header%>
+<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
+
+<script type="text/javascript">//<![CDATA[
+
+       var displayExtraInfo = function ( id ) {
+
+               document.getElementById('extra-info').innerHTML = document.getElementById(id).innerHTML;
+       }
+       XHR.poll(5, '/cgi-bin/bmx6-info', { 'descriptions/all': '' },
+               function(x, st)
+               {
+                 var tb = document.getElementById('descriptions_table');
+                 var rowcount = 0;
+                 var tunicon = "<%=resource%>/icons/tunnel.png";
+                 /* clear all rows */
+                 while( tb.rows.length > 1 ) tb.deleteRow(1);
+
+                 for ( var k in st  )
+                       {
+                       var description = st[k].DESC_ADV;
+                       var tun4in6;
+
+                       for ( var k in description.extensions )
+                               {
+                               var value = description.extensions[k];
+
+                                       if ( value.TUN4IN6_NET_EXTENSION )
+                                               {
+                                               tun4in6 = value.TUN4IN6_NET_EXTENSION;
+                                               break;
+                                               }
+                               }
+
+                       if ( tun4in6 )
+                       {
+                               var nodename = description.globalId.replace(/\..+$/,'');
+
+                               for( var i = 0; i < tun4in6.length; i++ )
+                               {
+                                       var tr = tb.insertRow(-1);
+                                       var network =  tun4in6[i].network;
+                                       var network_len = tun4in6[i].networklen;
+                                       var network_bw = tun4in6[i].bandwidth;
+
+                                       if ( network_len >= 32 ) continue;
+
+                                       tr.className = 'cbi-section-table-row cbi-rowstyle-' + ((rowcount++ % 2) + 1);
+                                       tr.insertCell(-1).innerHTML = String.format('<a href="/cgi-bin/bmx6control?function=gwselect&amp;node=%s"><img src="%s" /></a>',nodename,tunicon);
+                                       tr.insertCell(-1).innerHTML = nodename;
+                                       tr.insertCell(-1).innerHTML = network + '/' + network_len;
+                                       tr.insertCell(-1).innerHTML = network_bw;
+
+
+                               }
+
+                               if( tb.rows.length == 1 )
+                               {
+                                       var tr = tb.insertRow(-1);
+                                               tr.className = 'cbi-section-table-row';
+
+                                       var td = tr.insertCell(-1);
+                                               td.colSpan = 4;
+                                               td.innerHTML = '<em><br /><%:There are no gateways announced in the network.%></em>';
+                               }
+                               }
+                       }
+               }
+       );
+//]]></script>
+
+<style>
+
+       div.hideme{
+               display: none;
+       }
+
+       div.info{
+               background: #FFF;
+               border: solid 1px;
+               height: 80px;
+               display: block;
+               overflow: auto;
+       }
+
+       div.inforow{
+               text-align:left;
+               display:inline-block;
+               width:20%;
+               margin:5px;
+               vertical-align:top;
+
+       }
+
+#extra-info ul { list-style: none outside none; margin-left: 0em; }
+
+</style>
+<div class="cbi-map">
+
+<h2>Originators</h2>
+<div class="cbi-map-descr"></div>
+<fieldset class="cbi-section">
+       <legend><%:Mesh gateways%></legend>
+       <table class="cbi-section-table" id="descriptions_table">
+               <tr class="cbi-section-table-titles">
+                       <th class="cbi-section-table-cell"></th>
+                       <th class="cbi-section-table-cell"><%:Node%></th>
+                       <th class="cbi-section-table-cell"><%:Network%></th>
+                       <th class="cbi-section-table-cell"><%:Bandwidth%></th>
+               </tr>
+               <tr class="cbi-section-table-row">
+                       <td colspan="4"><em><br /><%:Collecting data...%></em></td>
+               </tr>
+       </table>
+</fieldset>
+
+</div>
+
+<%+footer%>
+
diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/graph.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/graph.htm
new file mode 100644 (file)
index 0000000..a4dabb7
--- /dev/null
@@ -0,0 +1,110 @@
+<%#
+Copyright (C) 2011 Pau Escrich <pau@dabax.net>
+Contributors Jo-Philip
+
+This program is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+This program is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License along
+with this program; if not, write to the Free Software Foundation, Inc.,
+51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+The full GNU General Public License is included in this distribution in
+the file called "COPYING".
+-%>
+
+<%
+       luci.http.prepare_content("text/html")
+
+       local location = { unpack(luci.dispatcher.context.path) }
+       location[#location] = "topology"
+%>
+
+<%+header%>
+
+<script type="text/javascript" src="<%=resource%>/bmx6/js/raphael-min.js"></script>
+<script type="text/javascript" src="<%=resource%>/bmx6/js/dracula_graffle.js"></script>
+<script type="text/javascript" src="<%=resource%>/bmx6/js/jquery-1.4.2.min.js"></script>
+<script type="text/javascript" src="<%=resource%>/bmx6/js/dracula_graph.js"></script>
+
+<button id="redraw" onclick="redraw();">&nbsp redraw &nbsp</button>
+
+<div id="wait" style="text-align: center">
+<br /><br />
+<img src="<%=resource%>/icons/loading.gif" />
+<%:Collecting data...%>
+
+</div>
+
+<div id="canvas" style="min-width:800px; min-height:800px"></div>
+
+<script type="text/javascript">//<![CDATA[
+       var redraw;
+
+       XHR.get('<%=luci.dispatcher.build_url(unpack(location))%>', null,
+               function(x, data)
+               {
+                       var g = new Graph();
+                       var seen = { };
+
+                       for (var i = 0; i < (data.length); i++)
+                       {
+                               // node->node
+                               if (data[i].globalId)
+                                       {
+                                       for (var j = 0; j < (data[i].links.length); j++)
+                                                {
+                                                var key = (data[i].globalId < data[i].links[j].globalId)
+                                                       ? data[i].globalId + '|' + data[i].links[j].globalId
+                                                       : data[i].links[j].globalId + '|' + data[i].globalId;
+
+                                                       var rxRate = data[i].links[j].rxRate;
+                                                       var txRate = data[i].links[j].txRate;
+
+                                                       if (!seen[key] && rxRate>0 && txRate>0)
+                                                       {
+                                                        g.addEdge(data[i].globalId, data[i].links[j].globalId,
+                                                               { label: rxRate + '/' + txRate,
+                                                               directed: false, stroke: '#aaaaaa', fill: '#ffffff',
+                                                               'label-style': { 'font-size': 8 }});
+                                                        seen[key] = true;
+                                                        }
+                                                }
+                                       }
+                                       //g.addEdge(data[i].router, data[i].neighbor,
+                                       //      { label: data[i].label, directed: true, stroke: '#aaaaaa' });
+                               // node->leaf
+                               //else if (data[i].router && data[i].gateway)
+                               //      g.addEdge(data[i].router, data[i].gateway,
+                               //              { label: 'leaf', stroke: '#cccccc' });
+                       }
+
+                       var canvas = document.getElementById('canvas');
+
+                       var layouter = new Graph.Layout.Spring(g);
+                       layouter.layout();
+                       
+                       var divwait = document.getElementById("wait");
+                       divwait.parentNode.removeChild(divwait);
+
+                       var renderer = new Graph.Renderer.Raphael(canvas.id, g, canvas.offsetWidth, canvas.offsetHeight);
+                       renderer.draw();
+
+                       redraw = function() {
+                               layouter.layout();
+                               renderer.draw();
+                       }
+
+               }
+       );
+//]]></script>
+
+
+<%+footer%>
diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/interfaces.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/interfaces.htm
new file mode 100644 (file)
index 0000000..70935ea
--- /dev/null
@@ -0,0 +1,59 @@
+<%+header%>
+
+    <style type="text/css">
+    table.int {
+        border-width: 2px;
+        border-spacing: ;
+        border-style: inset;
+        border-color: white;
+        border-collapse: collapse;
+        background-color: #dadbe6;
+        }
+    table.int tr {
+        border-width: 5px;
+        padding: 4px;
+        border-style: solid;
+        border-color: white;
+        background-color: #dadbe9;
+        }
+    table.int td {
+        border-width: 5px;
+        padding: 4px;
+        border-style: solid;
+        border-color: white;
+        background-color: #dadbe9;
+               text-align: center;
+        }
+    </style>
+
+<h2><a id="content" name="content"><%:Interfaces%></a></h2>
+Interfaces where bmx6 is running
+<br />
+<br />
+<table class="int">
+<tr>
+       <td><strong>Name</strong></td>
+       <td><strong>State</strong></td>
+       <td><strong>Type</strong></td>
+       <td><strong>Rate (Min/Max)</strong></td>
+       <td><strong>Local IP</strong></td>
+       <td><strong>Global IP</strong></td>
+       <td><strong>Multicast IP</strong></td>
+       <td><strong>Primary</strong></td>
+</tr>
+<% for i,v in ipairs(data) do %>
+       <tr>
+               <td><%=v.devName%></td>
+               <td><%=v.state%></td>
+               <td><%=v.type%></td>
+               <td><%=v.rateMin%>/<%=v.rateMax%></td>
+               <td><%=v.llocalIp%></td>
+               <td><%=v.globalIp%></td>
+               <td><%=v.multicastIp%></td>
+               <td><%=v.primary%></td>
+       </tr>
+<%end%>
+</table>
+
+<br />
+<%+footer%>
diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/links.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/links.htm
new file mode 100644 (file)
index 0000000..65e62d7
--- /dev/null
@@ -0,0 +1,55 @@
+<%+header%>
+<meta http-equiv="refresh" content="10" />
+<h2><a id="content" name="content"><%:Links%></a></h2>
+<br />
+<div id="links" style="position:relative;padding-left:2px">
+
+<% for j,d in pairs(links) do %>
+
+       <table>
+       <tr>
+               <td><img src="/luci-static/resources/bmx6/wifi.png"/></td>
+               <td><strong><%=j%></strong></td>
+       </tr>
+       </table>
+
+       <div style="display:table">
+
+<% for i,l in ipairs(d) do  %>
+       <div id="link" style="background-color:#dadbe9;left:50px;width:300px;margin:10px;float:left;position:relative">
+       <table>
+       <tr><th colspan="2">
+               <span style="color:grey;font-weight:700;text-align:left;">
+               <%=l.globalId%>
+               <br />
+               </span>
+       </th></tr>
+       <tr>
+               <td><img src="/luci-static/resources/bmx6/link.png"/></td>
+               <td>
+               <ul>
+                       <li>Local IP: <a href="[<%=l.llocalIp%>]"><%=l.llocalIp%></a></li>
+                       <li>Device: <%=l.viaDev%></li>
+                       <li>Rate (rx/tx):
+                               <% if l.rxRate+l.txRate < 120 then %>
+                                       <span style="color:red;">
+                               <% else %>
+                                       <span style="color:green;">
+                               <% end %>
+                                       <%=l.rxRate%>/<%=l.txRate%>
+                               </span>
+                       </li>
+                       <li>Routes: <%=l.routes%></li>
+               </ul>
+               </td>
+       </tr>
+       </table>
+       </div>
+<% end %>
+</div>
+<% end %>
+</div>
+
+<br />
+<br />
+<%+footer%>
diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/neighbours.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/neighbours.htm
new file mode 100644 (file)
index 0000000..6474116
--- /dev/null
@@ -0,0 +1,89 @@
+<%+header%>
+<style type="text/css">
+
+    table {
+       width:90%;
+       border-top:1px solid #e5eaf8;
+       border-right:1px solid #e5eaf8;
+       margin:1em auto;
+       border-collapse:collapse;
+     }
+
+    td {
+       color:#678197;
+       border-bottom:1px solid #e6eff8;
+       border-left:1px solid #e6eff8;
+       padding:.3em 1em;
+       text-align:center;
+     }
+    th {
+       background:#f4f9fe;
+       text-align:center;
+       font:bold 1.2em/2em "Century Gothic","Trebuchet MS",Arial,Helvetica,sans-serif;
+       color:#66a3d3;
+     }
+
+
+#neighbour {
+       position:relative;
+       margin:5px;
+}
+
+#orig_id {
+       background-color: black;
+       color: white;
+       text-ident:10px;
+}
+</style>
+
+<h2><a id="content" name="content"><%:Neighbours%></a></h2>
+<table>
+ <tr>
+  <th scope="col">Name</th>
+  <th scope="col">IPv4</th>
+  <th scope="col">IPv6</th>
+  <th scope="col">Via Dev</th>
+  <th scope="col">Via IP</th>
+  <th scope="col">Routes</th>
+  <th scope="col">Metric</th>
+  <th scope="col">Last Desc</th>
+  <th scope="col">Last Ref</th>
+ </tr>
+
+<% for i,o in ipairs(originators) do %>
+               <tr>
+               <td><%=o.name%></td>
+               <td><a href="http://<%=o.ipv4%>"><%=o.ipv4%></a></td>
+               <td><a href="http://[<%=o.orig.primaryIp%>]"><%=o.orig.primaryIp%></a></td>
+               <td><%=o.orig.viaDev%></td>
+               <td><%=o.orig.viaIp%></td>
+               <td><%=o.orig.routes%></td>
+               <td><%=o.orig.metric%></td>
+               <td><%=o.orig.lastDesc%></td>
+               <td><%=o.orig.lastRef%></td>
+               </tr>
+<%end%>
+</table>
+
+<table>
+ <tr>
+ <th scope="col">Node</th>
+ <th scope="col">Announced networks</th>
+ </tr>
+
+<% for i,o in ipairs(originators) do %>
+       <tr>
+        <td><%=o.name%></td>
+        <td style="text-align:left;">
+       <% if o.desc.DESC_ADV ~= nil then %>
+       <% for j,h in ipairs(o.desc.DESC_ADV.extensions[2].HNA6_EXTENSION) do %>
+         <%=h.address%>&nbsp;&nbsp;&nbsp;&nbsp;
+       <% end %>
+       <% end %>
+        </td>
+       </tr>
+<% end %>
+</table>
+
+<br />
+<%+footer%>
diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/neighbours_j.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/neighbours_j.htm
new file mode 100644 (file)
index 0000000..14f5597
--- /dev/null
@@ -0,0 +1,188 @@
+<%+header%>
+<script type="text/javascript">//<![CDATA[
+
+       var displayExtraInfo = function ( id ) {
+
+               document.getElementById('extra-info').innerHTML = document.getElementById(id).innerHTML;
+       }
+       XHR.poll(5, '/cgi-bin/bmx6-info', { '$neighbours': '' },
+               function(x, st)
+               {
+                       var originators = st.neighbours[0].originators;
+                       var descriptions = st.neighbours[1].descriptions;
+
+                       var tb = document.getElementById('descriptions_table');
+
+                       if ( originators.length != descriptions.length )
+                       {
+                               var tr = tb.insertRow(-1);
+                               tr.className = 'cbi-section-table-row';
+                               var td = tr.insertCell(-1);
+                               td.colSpan = 7;
+                               td.innerHTML = '<em><br /><%:Some problem with JSON: lenght of originators and descriptions different. %></em>';
+                               td.innerHTML += '<em><%: Please perform a manually cache flush from a terminal: bmx6 -c --flushAll %></em>'
+                               return 1;
+                       }
+
+                       if ( originators && descriptions && tb)
+                       {
+                               /* clear all rows */
+                               while( tb.rows.length > 1 )
+                                       tb.deleteRow(1);
+
+                               for( var i = 0; i < descriptions.length; i++ )
+                               {
+                                       var tr = tb.insertRow(-1);
+                                       var nodename =  descriptions[i].DESC_ADV.globalId.replace(/\.[^\.]+$/,"");
+                                       tr.className = 'cbi-section-table-row cbi-rowstyle-' + ((i % 2) + 1);
+                                       tr.insertCell(-1).innerHTML = '<a onclick="displayExtraInfo(\'ip-'+i+'\')"><img src=\"<%=resource%>/cbi/help.gif\" /></a>';
+                                       tr.insertCell(-1).innerHTML = nodename;
+
+                                       var extensions = descriptions[i].DESC_ADV.extensions;
+
+                                       //Looking for the extensions
+                                       var hna6 = [];
+                                       var tun4in6 = [];
+                                       var tun6 = [];
+                                       for( var e = 0; e < extensions.length; e++)
+                                       {
+                                               if( extensions[e].HNA6_EXTENSION )
+                                               {
+                                                       hna6 = extensions[e].HNA6_EXTENSION;
+                                               }
+                                               if ( extensions[e].TUN4IN6_NET_EXTENSION )
+                                               {
+                                                       tun4in6 = extensions[e].TUN4IN6_NET_EXTENSION;
+                                               }
+                                       }
+
+                                       var gateways = '<ul>';
+                                       for ( var t = 0; t < tun4in6.length; t++)
+                                       {
+                                               if ( tun4in6[t].networklen == "32" )
+                                                       gateways += '<li><a href="http://' + tun4in6[t].network + '">' + tun4in6[t].network + '</a></li>';
+                                               else
+                                                       gateways += "<li>"+tun4in6[t].network+'/'+tun4in6[t].networklen + ' | ' + tun4in6[t].bandwidth+'</li>';
+                                       }
+                                       gateways += '</ul>';
+
+                                       //Adding HNAs with prefix=128 as main address
+                                       var ipstxt = '';
+                                       var address;
+                                       var first = 1;
+                                       var ipstxt_hidden = '<ul>';
+                                       var hna6list = '<ul>';
+
+                                       for( var e = 0; e < hna6.length; e++ )
+                                       {
+                                               address = hna6[e].address;
+                                               prefix = hna6[e].prefixlen;
+                                               if ( prefix == '128' )
+                                                       {
+                                                       if (first)
+                                                               {
+                                                               ipstxt += address;
+                                                               ipstxt_hidden += '<li><a href="http://['+address+']" >'+address+"</a></li>";
+                                                               first = 0;
+                                                               }
+                                                       else    {
+                                                               ipstxt_hidden += '<li><a href="http://['+address+']" >'+address+"</a></li>";
+                                                               }
+                                                       }
+                                               else    {
+                                                       hna6list += '<li>'+address+'/'+prefix+'</li>';
+                                                       }
+                                       }
+                                       hna6list += '</ul>';
+                                       ipstxt_hidden += '</ul>';
+
+                                       tr.insertCell(-1).innerHTML = ipstxt;
+
+                                       tr.insertCell(-1).innerHTML = originators[i].viaDev;
+                                       tr.insertCell(-1).innerHTML = originators[i].metric;
+                                       tr.insertCell(-1).innerHTML = originators[i].lastDesc;
+                                       tr.insertCell(-1).innerHTML = originators[i].lastRef;
+                                       tr.insertCell(-1).innerHTML = originators[i].blocked;
+                                       //tr.onclick = displayExtraInfo("ip-"+i);
+
+                                       extrainfo = '<div id="ip-'+ i +'" class="hideme">' + "<div class='inforow'><br /><br /><h4>" + nodename + '</h4></div>\n' + "<div class='inforow'><h5>Available IPs</h5>\n<p>" + ipstxt_hidden + "</p></div>\n" + "<div class='inforow'><h5>Gateways announced</h5>\n<p>" + gateways + "</p></div>\n" + "<div class='inforow'><h5>Networks announced</h5>\n<p>" + hna6list + "</p></div>\n";
+
+                                       extrainfo += "\n</div>";
+
+                                       tr.insertCell(-1).innerHTML = extrainfo;
+
+                               }
+
+                               if( tb.rows.length == 1 )
+                               {
+                                       var tr = tb.insertRow(-1);
+                                               tr.className = 'cbi-section-table-row';
+
+                                       var td = tr.insertCell(-1);
+                                               td.colSpan = 7;
+                                               td.innerHTML = '<em><br /><%:There are no nodes available.%></em>';
+                               }
+                       }
+               }
+       );
+//]]></script>
+
+<style>
+
+       div.hideme{
+               display: none;
+       }
+
+       div.info{
+               background: #FFF;
+               border: solid 1px;
+               height: 80px;
+               display: block;
+               overflow: auto;
+               text-align: center;
+       }
+
+       div.inforow{
+               text-align:center;
+               display:inline-block;
+               width:20%;
+               margin:5px;
+               vertical-align:top;
+       }
+
+#extra-info ul { list-style: none outside none; margin-left: 0em; }
+
+</style>
+<div class="cbi-map">
+
+<h2>Originators</h2>
+<div class="cbi-map-descr"></div>
+<div id="extra-info" class="info">
+       <br />
+       Click to the icon <img src="<%=resource%>/cbi/help.gif" /> to see individual node information
+</div>
+<fieldset class="cbi-section">
+       <legend><%:Mesh nodes%></legend>
+       <table class="cbi-section-table" id="descriptions_table">
+               <tr class="cbi-section-table-titles">
+                       <th class="cbi-section-table-cell"></th>
+                       <th class="cbi-section-table-cell"><%:Hostname%></th>
+                       <th class="cbi-section-table-cell"><%:Primary IP%></th>
+                       <th class="cbi-section-table-cell"><%:Via Device%></th>
+                       <th class="cbi-section-table-cell"><%:Metric%></th>
+                       <th class="cbi-section-table-cell"><%:Last Desc%></th>
+                       <th class="cbi-section-table-cell"><%:Last Ref%></th>
+                       <th class="cbi-section-table-cell"><%:Blocked%></th>
+               </tr>
+               <tr class="cbi-section-table-row">
+                       <td colspan="8"><em><br /><%:Collecting data...%></em></td>
+               </tr>
+       </table>
+</fieldset>
+
+</div>
+<a href="<%=link_non_js%>">Go to non JavaScript view</a>
+
+
+<%+footer%>
+
diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/nodes.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/nodes.htm
new file mode 100644 (file)
index 0000000..18e5cc9
--- /dev/null
@@ -0,0 +1,87 @@
+<%+header%>
+<style type="text/css">
+
+    table {
+       width:90%;
+       border-top:1px solid #e5eaf8;
+       border-right:1px solid #e5eaf8;
+       margin:1em auto;
+       border-collapse:collapse;
+     }
+
+    td {
+       color:#678197;
+       border-bottom:1px solid #e6eff8;
+       border-left:1px solid #e6eff8;
+       padding:.3em 1em;
+       text-align:center;
+     }
+    th {
+       background:#f4f9fe;
+       text-align:center;
+       font:bold 1.2em/2em "Century Gothic","Trebuchet MS",Arial,Helvetica,sans-serif;
+       color:#66a3d3;
+     }
+
+
+#neighbour {
+       position:relative;
+       margin:5px;
+}
+
+#orig_id {
+       background-color: black;
+       color: white;
+       text-ident:10px;
+}
+</style>
+
+<h2><a id="content" name="content"><%:Nodes%></a></h2>
+<table>
+ <tr>
+  <th scope="col">Name</th>
+  <th scope="col">IPv6</th>
+  <th scope="col">Via Dev</th>
+  <th scope="col">Via IP</th>
+  <th scope="col">Routes</th>
+  <th scope="col">Metric</th>
+  <th scope="col">Last Desc</th>
+  <th scope="col">Last Ref</th>
+ </tr>
+
+<% for i,o in ipairs(originators) do %>
+               <tr>
+               <td><%=o.name%></td>
+               <td><a href="http://[<%=o.orig.primaryIp%>]"><%=o.orig.primaryIp%></a></td>
+               <td><%=o.orig.viaDev%></td>
+               <td><%=o.orig.viaIp%></td>
+               <td><%=o.orig.routes%></td>
+               <td><%=o.orig.metric%></td>
+               <td><%=o.orig.lastDesc%></td>
+               <td><%=o.orig.lastRef%></td>
+               </tr>
+<%end%>
+</table>
+
+<table>
+ <tr>
+ <th scope="col">Node</th>
+ <th scope="col">Announced networks</th>
+ </tr>
+
+<% for i,o in ipairs(originators) do %>
+       <tr>
+        <td><%=o.name%></td>
+        <td style="text-align:left;">
+       <% if o.desc.DESC_ADV ~= nil then %>
+       <% for j,h in ipairs(o.desc.DESC_ADV.extensions[2].HNA6_EXTENSION) do %>
+         <%=h.address%>&nbsp;&nbsp;&nbsp;&nbsp;
+       <% end %>
+       <% end %>
+        </td>
+       </tr>
+<% end %>
+</table>
+
+<br />
+<%+footer%>
diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/nodes_j.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/nodes_j.htm
new file mode 100644 (file)
index 0000000..0435655
--- /dev/null
@@ -0,0 +1,193 @@
+<%#
+   Copyright (C) 2011 Pau Escrich <pau@dabax.net>
+   Contributors Lluis Esquerda <eskerda@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along
+   with this program; if not, write to the Free Software Foundation, Inc.,
+   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+   The full GNU General Public License is included in this distribution in
+   the file called "COPYING".
+-%>
+
+<%+header%>
+<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
+<script type="text/javascript" src="<%=resource%>/bmx6/js/polling.js"></script>
+
+
+<style>
+
+       div.hideme{
+               display: none;
+       }
+
+       div.info{
+               background: #FFF;
+               border: solid 0px;
+               height: 90px;
+               display: block;
+               overflow: auto;
+       }
+
+       div.inforow{
+               text-align:left;
+               display:inline-block;
+               width:20%;
+               margin:5px;
+               vertical-align:top;
+
+       }
+
+#extra-info ul { list-style: none outside none; margin-left: 0em; }
+
+</style>
+<div class="cbi-map">
+
+<h2>Node originators</h2>
+<div class="cbi-map-descr"></div>
+<div id="extra-info" class="info">
+       <br />
+       <center>
+       Click icon <img src="<%=resource%>/bmx6/world.png" /> to see individual node information
+       </center>
+</div>
+<fieldset class="cbi-section">
+       <legend><%:Mesh nodes%></legend>
+       <table class="cbi-section-table" id="descriptions_table">
+               <tr class="cbi-section-table-titles">
+                       <th class="cbi-section-table-cell"></th>
+                       <th class="cbi-section-table-cell"><%:Hostname%></th>
+                       <th class="cbi-section-table-cell"><%:Primary IP%></th>
+                       <th class="cbi-section-table-cell"><%:Via Device%></th>
+                       <th class="cbi-section-table-cell"><%:Metric%></th>
+                       <th class="cbi-section-table-cell"><%:Last Desc%></th>
+                       <th class="cbi-section-table-cell"><%:Last Ref%></th>
+                       <th class="cbi-section-table-cell"><%:Blocked%></th>
+               </tr>
+               <tr class="cbi-section-table-row">
+                       <td colspan="8"><br /><center><em><%:Collecting data...%></em></center></td>
+               </tr>
+       </table>
+</fieldset>
+
+</div>
+
+<a href="<%=link_non_js%>">Go to non JavaScript view</a>
+
+<script type="text/javascript">//<![CDATA[
+               var displayExtraInfo = function ( id ) {
+                 document.getElementById('extra-info').innerHTML = document.getElementById(id).innerHTML;
+               }
+       
+               new TablePooler(5,"/cgi-bin/bmx6-info", {'$neighbours':''}, "descriptions_table", function(st){
+                 var infoicon = "<%=resource%>/bmx6/world_small.png";
+                 var nodeicon = "<%=resource%>/bmx6/world.png";
+                 var originators = st.neighbours[0].originators;
+                 var descriptions = st.neighbours[1].descriptions;
+                 var res = Array();
+                 var error = "";
+
+                 if ( originators.length != descriptions.length )
+                 {
+                       error = '<em><br /><%:Some problem with JSON: lenght of originators and descriptions differs. %> \
+                       <%: Please perform a cache flush from a console it this persists: bmx6 -c --flushAll %></em>';
+                       res.push([[error,7]]);
+                       return res;
+                 }  
+
+                 for ( var i = 0; i < descriptions.length; i++ ){
+                       var nodename =  descriptions[i].DESC_ADV.globalId.replace(/\.[^\.]+$/,"");              
+                       var extensions = descriptions[i].DESC_ADV.extensions;
+                       //var extrainfo = '<a onclick="displayExtraInfo(\'ip-' + i + '\')"><img src="' + infoicon + '" / ></a>';
+                       var extrainfo_link = '<a onclick="displayExtraInfo(\'ip-' + i + '\')">' +  '<img src="' + infoicon + '" />' + '</a>';
+                       // Looking for the extensions
+                       var hna6 = [];
+                       var tun4in6 = [];
+                       var tun6 = [];
+                       for( var e = 0; e < extensions.length; e++)
+                               {
+                               if( extensions[e].HNA6_EXTENSION )
+                               hna6 = extensions[e].HNA6_EXTENSION;
+                               if ( extensions[e].TUN4IN6_NET_EXTENSION )
+                               tun4in6 = extensions[e].TUN4IN6_NET_EXTENSION;
+                               }
+
+                       // Gateways
+                       var gateways = '<ul>';
+                       for ( var t = 0; t < tun4in6.length; t++)
+                               {
+                               if ( tun4in6[t].networklen == "32" )
+                                       gateways += '<li><a href="http://' + tun4in6[t].network + '">' + tun4in6[t].network + '</a></li>';
+                               else
+                                       gateways += "<li>"+tun4in6[t].network+'/'+tun4in6[t].networklen + ' | ' + tun4in6[t].bandwidth+'</li>';
+                               }
+                       gateways += '</ul>';
+
+                       //Adding HNAs with prefix=128 as main address
+                       var ipstxt = '';
+                       var address;
+                       var first = 1;
+                       var ipstxt_hidden = '<ul>';
+                       var hna6list = '<ul>';
+                       var extrainfo = "";
+
+                       for( var e = 0; e < hna6.length; e++ )
+                       {
+                               address = hna6[e].address;
+                               prefix = hna6[e].prefixlen;
+                               if ( prefix == '128' )
+                                       {
+                                       if (first)
+                                               {
+                                               ipstxt += address;
+                                               ipstxt_hidden += '<li><a href="http://['+address+']" >'+address+"</a></li>";
+                                               first = 0;
+                                               }
+                                       else    {
+                                               ipstxt_hidden += '<li><a href="http://['+address+']" >'+address+"</a></li>";
+                                               }
+                                       }
+                               else    {
+                                       hna6list += '<li>'+address+'/'+prefix+'</li>';
+                                       }
+                       }
+                       hna6list += '</ul>';
+                       ipstxt_hidden += '</ul>';
+
+                       extrainfo = '<div id="ip-'+ i +'" class="hideme">'
+                       + "<div class='inforow'>" 
+                       + "<h4>" + nodename  + '</h4>\n' + '<img src="' + nodeicon + '" />'+ "</div>"
+                       
+                       + "<div class='inforow'>"
+                       + "<h5>Available IPs</h5>\n"
+                       + ipstxt_hidden + "</div>\n"
+                       
+                       + "<div class='inforow'>"
+                       + "<h5>Gateways announced</h5>\n"
+                       + gateways + "</div>\n"
+                       
+                       + "<div class='inforow'>"
+                       + "<h5>Networks announced</h5>\n"
+                       + hna6list +  "</div>\n"
+                       + "\n</div>";
+                       
+                       res.push([extrainfo_link,nodename, ipstxt, originators[i].viaDev, originators[i].metric,
+                       originators[i].lastDesc, originators[i].lastRef, originators[i].blocked, extrainfo]);
+
+                 }
+                 return res;
+               });
+//]]></script>
+
+<%+footer%>
+
diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/status.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/status.htm
new file mode 100644 (file)
index 0000000..11e9682
--- /dev/null
@@ -0,0 +1,69 @@
+<%+header%>
+<link rel="stylesheet" type="text/css" href="/luci-static/resources/bmx6/style.css" />
+
+<img src="<%=resource%>/bmx6/bmx6logo.png" />
+
+Bmx6 is a routing protocol for Linux based operating systems. Visit <a href="http://bmx6.net">bmx6.net</a> for more info.
+
+<br />
+<br />
+
+<h2>Status of bmx6</h2>
+
+<table>
+<tr>
+       <th>Version</th>
+       <th>Compatibility</th>
+       <th>CodeVersion</th>
+       <th>Global Id</th>
+       <th>Primary Ip</th>
+       <th>Local Id</th>
+       <th>Uptime</th>
+       <th>CPU</th>
+       <th>Nodes</th>
+</tr>
+       <tr>
+               <td><%=status.version%></td>
+               <td><%=status.compatibility%></td>
+               <td><%=status.codeVersion%></td>
+               <td><%=status.globalId%>/<%=status.rateMax%></td>
+               <td><%=status.primaryIp%></td>
+               <td><%=status.myLocalId%></td>
+               <td><%=status.uptime%></td>
+               <td><%=status.cpu%></td>
+               <td><%=status.nodes%></td>
+       </tr>
+</table>
+
+<br />
+<br />
+
+<h2>Status of interfaces</h2>
+<table>
+<tr>
+       <th>Name</th>
+       <th>State</th>
+       <th>Type</th>
+       <th>Rate (Min/Max)</th>
+       <th>Local IP</th>
+       <th>Global IP</th>
+       <th>Multicast IP</th>
+       <th>Primary</th>
+</tr>
+<% for i,v in ipairs(interfaces) do %>
+       <tr>
+               <td><%=v.devName%></td>
+               <td><%=v.state%></td>
+               <td><%=v.type%></td>
+               <td><%=v.rateMin%>/<%=v.rateMax%></td>
+               <td><%=v.llocalIp%></td>
+               <td><%=v.globalIp%></td>
+               <td><%=v.multicastIp%></td>
+               <td><%=v.primary%></td>
+       </tr>
+<%end%>
+</table>
+
+<br />
+
+<%+footer%>
diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/status_j.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/status_j.htm
new file mode 100644 (file)
index 0000000..5e51f09
--- /dev/null
@@ -0,0 +1,114 @@
+<%+header%>
+<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
+<script type="text/javascript" src="<%=resource%>/bmx6/js/polling.js"></script>
+
+
+<style>
+
+       div.hideme{
+               display: none;
+       }
+
+       div.info{
+               background: #FFF;
+               border: solid 1px;
+               height: 80px;
+               display: block;
+               overflow: auto;
+       }
+
+       div.inforow{
+               text-align:left;
+               display:inline-block;
+               width:20%;
+               margin:5px;
+               vertical-align:top;
+
+       }
+
+#extra-info ul { list-style: none outside none; margin-left: 0em; }
+
+</style>
+<div class="cbi-map">
+<center>
+<img src="<%=resource%>/bmx6/bmx6logo.png" />
+<br />
+<br />
+a mesh routing protocol for Linux devices.<br />
+Visit <a href="http://bmx6.net">bmx6.net</a> for more info.
+<br />
+<br />
+</center>
+
+<h2>status</h2>
+<div class="cbi-map-descr"></div>
+<fieldset class="cbi-section">
+       <legend><%:status%></legend>
+       <table class="cbi-section-table" id="status_table">
+               <tr class="cbi-section-table-titles">
+                       <th class="cbi-section-table-cell"><%:Version%></th>
+                       <th class="cbi-section-table-cell"><%:Compat%></th>
+                       <th class="cbi-section-table-cell"><%:Code Version%></th>
+                       <th class="cbi-section-table-cell"><%:Global ID%></th>
+                       <th class="cbi-section-table-cell"><%:Primary IP%></th>
+                       <th class="cbi-section-table-cell"><%:Tun6Address%></th>
+                       <th class="cbi-section-table-cell"><%:Tun4Address%></th>
+                       <th class="cbi-section-table-cell"><%:Local ID%></th>
+                       <th class="cbi-section-table-cell"><%:Uptime%></th>
+                       <th class="cbi-section-table-cell"><%:Cpu load%></th>
+                       <th class="cbi-section-table-cell"><%:Nodes seen%></th>
+               </tr>
+               <tr class="cbi-section-table-row">
+                       <td colspan="11"><em><br /><%:Collecting data...%></em></td>
+               </tr>
+       </table>
+</fieldset>
+
+<fieldset class="cbi-section">
+       
+       <legend><%:Network devices%></legend>
+       <table class="cbi-section-table" id="ifaces_table">
+               <tr class="cbi-section-table-titles">
+                       <th class="cbi-section-table-cell"><%:Name%></th>
+                       <th class="cbi-section-table-cell"><%:State%></th>
+                       <th class="cbi-section-table-cell"><%:Type%></th>
+                       <th class="cbi-section-table-cell"><%:Rate%></th>
+                       <th class="cbi-section-table-cell"><%:Local IP%></th>
+                       <th class="cbi-section-table-cell"><%:Global IP%></th>
+                       <th class="cbi-section-table-cell"><%:Multicast IP%></th>
+                       <th class="cbi-section-table-cell"><%:is Primary%></th>
+               </tr>
+               <tr class="cbi-section-table-row">
+                       <td colspan="8"><em><br /><%:Collecting data...%></em></td>
+               </tr>
+       </table>
+</fieldset>
+
+</div>
+
+<script type="text/javascript">//<![CDATA[
+               new TablePooler(5,"/cgi-bin/bmx6-info", {'status':''}, "status_table", function(st){
+               var res = Array();
+                       var sta = st.status;
+
+               res.push([sta.version,sta.compat,sta.revision,sta.globalId,sta.primaryIp,sta.tun6Address,
+               sta.tun4Address,sta.myLocalId,sta.uptime,sta.cpu,sta.nodes]);
+               return res;
+               });
+
+               new TablePooler(5,"/cgi-bin/bmx6-info", {'interfaces':''}, "ifaces_table", function(st){
+               var res = Array();
+               var ifaces = st.interfaces;
+
+               for ( var i = 0; i < ifaces.length; i++)
+                       {
+                       res.push([ifaces[i].devName,ifaces[i].state,ifaces[i].type,ifaces[i].rateMin+"/"+ifaces[i].rateMax,
+                               ifaces[i].llocalIp,ifaces[i].globalIp,ifaces[i].multicastIp,ifaces[i].primary]);
+                       }
+               return res;
+               });
+//]]></script>
+
+
+<%+footer%>
+
diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/tunnels_j.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/tunnels_j.htm
new file mode 100644 (file)
index 0000000..1b7ce42
--- /dev/null
@@ -0,0 +1,107 @@
+<%#
+   Copyright (C) 2011 Pau Escrich <pau@dabax.net>
+   Contributors Lluis Esquerda <eskerda@gmail.com>
+
+   This program is free software; you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation; either version 2 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License along
+   with this program; if not, write to the Free Software Foundation, Inc.,
+   51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+   The full GNU General Public License is included in this distribution in
+   the file called "COPYING".
+-%>
+
+
+<%+header%>
+<script type="text/javascript" src="<%=resource%>/cbi.js"></script>
+<script type="text/javascript" src="<%=resource%>/bmx6/js/polling.js"></script>
+
+
+<style>
+
+       div.hideme{
+               display: none;
+       }
+
+       div.info{
+               background: #FFF;
+               border: solid 1px;
+               height: 80px;
+               display: block;
+               overflow: auto;
+       }
+
+       div.inforow{
+               text-align:left;
+               display:inline-block;
+               width:20%;
+               margin:5px;
+               vertical-align:top;
+
+       }
+
+#extra-info ul { list-style: none outside none; margin-left: 0em; }
+
+</style>
+<div class="cbi-map">
+
+<h2>Gateways tunnel announcements</h2>
+<div class="cbi-map-descr"></div>
+<fieldset class="cbi-section">
+       <legend><%:Mesh gateways%></legend>
+       <table class="cbi-section-table" id="descriptions_table">
+               <tr class="cbi-section-table-titles">
+                       <th class="cbi-section-table-cell"></th>
+                       <th class="cbi-section-table-cell"><%:Tunnel%></th>
+                       <th class="cbi-section-table-cell"><%:Node%></th>
+                       <th class="cbi-section-table-cell"><%:Network%></th>
+                       <th class="cbi-section-table-cell"><%:Bandwidth%></th>
+                       <th class="cbi-section-table-cell"><%:SearchNet%></th>
+                       <th class="cbi-section-table-cell"><%:Type%></th>
+                       <th class="cbi-section-table-cell"><%:Path Metric%></th>
+                       <th class="cbi-section-table-cell"><%:IP metric%></th>
+                       <th class="cbi-section-table-cell"><%:Tun metric%></th>
+                       <th class="cbi-section-table-cell"><%:Bonus%></th>
+                       <th class="cbi-section-table-cell"><%:search id%></th>
+               </tr>
+               <tr class="cbi-section-table-row">
+                       <td colspan="10"><em><br /><%:Collecting data...%></em></td>
+               </tr>
+       </table>
+</fieldset>
+
+</div>
+
+<script type="text/javascript">//<![CDATA[
+               new TablePooler(5,"/cgi-bin/bmx6-info", {'$tunnels':''}, "descriptions_table", function(st){
+                  var tunicon = "<%=resource%>/icons/tunnel.png";
+                  var tunicon_dis = "<%=resource%>/icons/tunnel_disabled.png";
+                  var applyicon = "<%=resource%>/cbi/apply.gif";
+                 var res = Array();
+                  for ( var k in st.tunnels ){
+                        var tunnel = st.tunnels[k];
+                        var nodename = tunnel.remoteId.replace(/\..+$/,'');
+                       var advnet = tunnel.advNet;
+                       var status = '<img src="'+tunicon_dis+'"/>';
+                       
+                       if ( tunnel.tunName != "---" ) status = '<img src="'+tunicon+'"/>';
+                       if ( advnet == "0.0.0.0/0" ) advnet = "<b>Internet</b>";
+
+                       res.push([status, tunnel.name, nodename, advnet, tunnel.advBw, tunnel.searchNet, tunnel.advType,
+                               tunnel.pathMtc, tunnel.ipMtc, tunnel.tunMtc, tunnel.bonus, tunnel.searchId]);
+                 }
+                 return res;
+               });
+//]]></script>
+
+<%+footer%>
+
diff --git a/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/wireless.htm b/luci-app-bmx6/files/usr/lib/lua/luci/view/bmx6/wireless.htm
new file mode 100644 (file)
index 0000000..aa7e19d
--- /dev/null
@@ -0,0 +1,7 @@
+<%+header%>
+<h2><a id="content" name="content"><%:Wireless%></a></h2>
+<br />
+<h3>Wireless</h3>
+<%=data%>
+<br />
+<%+footer%>
diff --git a/luci-app-bmx6/files/www/cgi-bin/bmx6-info b/luci-app-bmx6/files/www/cgi-bin/bmx6-info
new file mode 100644 (file)
index 0000000..931cbf6
--- /dev/null
@@ -0,0 +1,112 @@
+#!/bin/sh
+#    Copyright (C) 2011 Pau Escrich
+#    Contributors Jo-Philipp Wich <xm@subsignal.org>
+#
+#    This program is free software; you can redistribute it and/or modify
+#    it under the terms of the GNU General Public License as published by
+#    the Free Software Foundation; either version 2 of the License, or
+#    (at your option) any later version.
+#
+#    This program is distributed in the hope that it will be useful,
+#    but WITHOUT ANY WARRANTY; without even the implied warranty of
+#    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+#    GNU General Public License for more details.
+#
+#    You should have received a copy of the GNU General Public License along
+#    with this program; if not, write to the Free Software Foundation, Inc.,
+#    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+#
+#    The full GNU General Public License is included in this distribution in
+#    the file called "COPYING".
+#
+#    This script gives information about bmx6
+#    Can be executed from a linux shell: ./bmx6-info -s links
+#    Or from web interfae (with cgi enabled): http://host/cgi-bin/bmx6-info?links
+#    If you ask for a directory you wil get the directory contents in JSON forman
+
+BMX6_DIR="$(uci get bmx6.general.runtimeDir 2>/dev/null)" || BMX6_DIR="/var/run/bmx6/json"
+
+#Checking if shell mode or cgi-bin mode
+if [ "$1" == "-s" ]; then
+       QUERY="$2"
+else
+       QUERY="${QUERY_STRING%%=*}"
+       echo "Content-type: application/json"
+       echo ""
+
+fi
+
+check_path() {
+        [ -d "$1" ] && path=$(cd $1; pwd)
+        [ -f "$1" ] && path=$(cd $1/..; pwd)
+        [ $(echo "$path" | grep -c "^$BMX6_DIR") -ne 1 ] && exit 1
+}
+
+print_query() {
+       # If the query is a directory
+       [ -d "$BMX6_DIR/$1" ] &&
+        {
+       # If /all has not been specified
+               [ -z "$QALL" ] &&
+               {
+               total=$(ls $BMX6_DIR/$1 | wc -w)
+               i=1
+               echo -n "{ \"$1\": [ "
+               for f in $(ls $BMX6_DIR/$1); do
+                       echo -n "{ \"name\": \"$f\" }"
+                       [ $i -lt $total ]  && echo -n ','
+                       i=$(( $i + 1 ))
+               done
+               echo -n " ] }"
+
+       # If /all has been specified, printing all the files together
+               } || {
+               comma=""
+               echo -n "[ "
+               for entry in "$BMX6_DIR/$1/"*; do
+                       [ -f "$entry" ] &&
+                       {
+                               ${comma:+echo "$comma"}
+                               tr -d '\n' < "$entry"
+                               comma=","
+                       }
+               done
+               echo -n " ]"
+               }
+        }
+
+       # If the query is a file, just printing the file
+       [ -f "$BMX6_DIR/$QUERY" ] && cat "$BMX6_DIR/$QUERY";
+}
+
+if [ "${QUERY##*/}" == "all" ]; then
+       QUERY="${QUERY%/all}"
+       QALL=1
+fi
+
+
+if [ "$QUERY" == '$neighbours' ]; then
+       QALL=1
+       echo '{ "neighbours": [ '
+       echo '{ "originators": '
+       print_query originators
+       echo '}, '
+       echo '{ "descriptions": '
+       print_query descriptions
+       echo "} ] }"
+       exit 0
+
+else if [ "$QUERY" == '$tunnels' ]; then
+       bmx6 -c --jshow tunnels
+       exit 0
+
+else
+       check_path "$BMX6_DIR/$QUERY"
+       print_query $QUERY
+       exit 0
+fi
+fi
+
+ls -1F "$BMX6_DIR"
+exit 0
+
diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/bmx6logo.png b/luci-app-bmx6/files/www/luci-static/resources/bmx6/bmx6logo.png
new file mode 100644 (file)
index 0000000..12f7526
Binary files /dev/null and b/luci-app-bmx6/files/www/luci-static/resources/bmx6/bmx6logo.png differ
diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/Curry-1.0.1.js b/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/Curry-1.0.1.js
new file mode 100644 (file)
index 0000000..f5aa0f9
--- /dev/null
@@ -0,0 +1,29 @@
+/**\r
+ * Curry - Function currying\r
+ * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com\r
+ * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)\r
+ * Date: 10/4/2008\r
+ *\r
+ * @author Ariel Flesler\r
+ * @version 1.0.1\r
+ */\r
+\r
+function curry( fn ){\r
+       return function(){\r
+               var args = curry.args(arguments),\r
+                       master = arguments.callee,\r
+                       self = this;\r
+\r
+               return args.length >= fn.length ? fn.apply(self,args) : function(){\r
+                       return master.apply( self, args.concat(curry.args(arguments)) );\r
+               };\r
+       };\r
+};\r
+\r
+curry.args = function( args ){\r
+       return Array.prototype.slice.call(args);\r
+};\r
+\r
+Function.prototype.curry = function(){\r
+       return curry(this);\r
+};
\ No newline at end of file
diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/dracula_algorithms.js b/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/dracula_algorithms.js
new file mode 100644 (file)
index 0000000..0fbb085
--- /dev/null
@@ -0,0 +1,599 @@
+/*
+ * Various algorithms and data structures, licensed under the MIT-license.
+ * (c) 2010 by Johann Philipp Strathausen <strathausen@gmail.com>
+ * http://strathausen.eu
+ *
+ */
+
+
+
+/*
+        Bellman-Ford
+    
+    Path-finding algorithm, finds the shortest paths from one node to all nodes.
+    
+    
+        Complexity
+        
+    O( |E| Â· |V| ), where E = edges and V = vertices (nodes)
+    
+    
+        Constraints
+    
+    Can run on graphs with negative edge weights as long as they do not have
+    any negative weight cycles.
+    
+ */
+function bellman_ford(g, source) {
+
+    /* STEP 1: initialisation */
+    for(var n in g.nodes)
+        g.nodes[n].distance = Infinity;
+        /* predecessors are implicitly null */
+    source.distance = 0;
+    
+    step("Initially, all distances are infinite and all predecessors are null.");
+    
+    /* STEP 2: relax each edge (this is at the heart of Bellman-Ford) */
+    /* repeat this for the number of nodes minus one */
+    for(var i = 1; i < g.nodes.length; i++)
+        /* for each edge */
+        for(var e in g.edges) {
+            var edge = g.edges[e];
+            if(edge.source.distance + edge.weight < edge.target.distance) {
+                step("Relax edge between " + edge.source.id + " and " + edge.target.id + ".");
+                edge.target.distance = edge.source.distance + edge.weight;
+                edge.target.predecessor = edge.source;
+            }
+           //Added by Jake Stothard (Needs to be tested)
+           if(!edge.style.directed) {
+               if(edge.target.distance + edge.weight < edge.source.distance) {
+                    g.snapShot("Relax edge between "+edge.target.id+" and "+edge.source.id+".");
+                    edge.source.distance = edge.target.distance + edge.weight;
+                    edge.source.predecessor = edge.target;
+               }
+           }
+        }
+    step("Ready.");
+    
+    /* STEP 3: TODO Check for negative cycles */
+    /* For now we assume here that the graph does not contain any negative
+       weights cycles. (this is left as an excercise to the reader[tm]) */
+}
+
+
+
+/*
+   Path-finding algorithm Dijkstra
+   
+   - worst-case running time is O((|E| + |V|) Â· log |V| ) thus better than
+     Bellman-Ford for sparse graphs (with less edges), but cannot handle
+     negative edge weights
+ */
+function dijkstra(g, source) {
+
+    /* initially, all distances are infinite and all predecessors are null */
+    for(var n in g.nodes)
+        g.nodes[n].distance = Infinity;
+        /* predecessors are implicitly null */
+
+    g.snapShot("Initially, all distances are infinite and all predecessors are null.");
+
+    source.distance = 0;
+    /* set of unoptimized nodes, sorted by their distance (but a Fibonacci heap
+       would be better) */
+    var q = new BinaryMinHeap(g.nodes, "distance");
+
+    /* pointer to the node in focus */
+    var node;
+
+    /* get the node with the smallest distance
+       as long as we have unoptimized nodes. q.min() can have O(log n). */
+    while(q.min() != undefined) {
+        /* remove the latest */
+        node = q.extractMin();
+        node.optimized = true;
+
+        /* no nodes accessible from this one, should not happen */
+        if(node.distance == Infinity)
+            throw "Orphaned node!";
+
+        /* for each neighbour of node */
+        for(e in node.edges) {
+           var other = (node == node.edges[e].target) ? node.edges[e].source : node.edges[e].target;
+               
+            if(other.optimized)
+                continue;
+
+            /* look for an alternative route */
+            var alt = node.distance + node.edges[e].weight;
+            
+            /* update distance and route if a better one has been found */
+            if (alt < other.distance) {
+            
+                /* update distance of neighbour */
+                other.distance = alt;
+
+                /* update priority queue */
+                q.heapify();
+
+                /* update path */
+                other.predecessor = node;
+                g.snapShot("Enhancing node.")
+            }
+        }
+    }
+}
+
+
+/* All-Pairs-Shortest-Paths */
+/* Runs at worst in O(|V|³) and at best in Omega(|V|³) :-)
+   complexity Sigma(|V|²) */
+/* This implementation is not yet ready for general use, but works with the
+   Dracula graph library. */
+function floyd_warshall(g, source) {
+
+    /* Step 1: initialising empty path matrix (second dimension is implicit) */
+    var path = [];
+    var next = [];
+    var n = g.nodes.length;
+
+    /* construct path matrix, initialize with Infinity */
+    for(j in g.nodes) {
+        path[j] = [];
+        next[j] = [];
+        for(i in g.nodes)
+            path[j][i] = j == i ? 0 : Infinity;
+    }   
+    
+    /* initialize path with edge weights */
+    for(e in g.edges)
+        path[g.edges[e].source.id][g.edges[e].target.id] = g.edges[e].weight;
+    
+    /* Note: Usually, the initialisation is done by getting the edge weights
+       from a node matrix representation of the graph, not by iterating through
+       a list of edges as done here. */
+    
+    /* Step 2: find best distances (the heart of Floyd-Warshall) */
+    for(k in g.nodes){
+        for(i in g.nodes) {
+            for(j in g.nodes)
+                if(path[i][j] > path[i][k] + path[k][j]) {
+                    path[i][j] = path[i][k] + path[k][j];
+                    /* Step 2.b: remember the path */
+                    next[i][j] = k;
+                }
+        }
+    }
+
+    /* Step 3: Path reconstruction, get shortest path */
+    function getPath(i, j) {
+        if(path[i][j] == Infinity)
+            throw "There is no path.";
+        var intermediate = next[i][j];
+        if(intermediate == undefined)
+            return null;
+        else
+            return getPath(i, intermediate)
+                .concat([intermediate])
+                .concat(getPath(intermediate, j));
+    }
+
+    /* TODO use the knowledge, e.g. mark path in graph */
+}
+
+/*
+        Ford-Fulkerson
+    
+    Max-Flow-Min-Cut Algorithm finding the maximum flow through a directed
+    graph from source to sink.
+    
+    
+        Complexity
+
+    O(E * max(f)), max(f) being the maximum flow
+    
+    
+        Description
+
+    As long as there is an open path through the residual graph, send the
+    minimum of the residual capacities on the path.
+    
+    
+        Constraints
+    
+    The algorithm works only if all weights are integers. Otherwise it is
+    possible that the Ford–Fulkerson algorithm will not converge to the maximum
+    value.
+    
+    
+        Input
+    
+    g - Graph object
+    s - Source ID
+    t - Target (sink) ID
+    
+    
+        Output
+    
+    Maximum flow from Source s to Target t
+
+ */
+/*
+        Edmonds-Karp
+    
+    Max-Flow-Min-Cut Algorithm finding the maximum flow through a directed
+    graph from source to sink. An implementation of the Ford-Fulkerson
+    algorithm.
+    
+    
+        Complexity
+    
+    O(|V|*|E|²)
+    
+    
+        Input
+        
+    g - Graph object (with node and edge lists, capacity is a property of edge)
+    s - source ID
+    t - sink ID
+    
+ */
+function edmonds_karp(g, s, t) {
+
+}
+
+/*
+   A simple binary min-heap serving as a priority queue
+   - takes an array as the input, with elements having a key property
+   - elements will look like this:
+        {
+            key: "... key property ...", 
+            value: "... element content ..."
+        }
+    - provides insert(), min(), extractMin() and heapify()
+    - example usage (e.g. via the Firebug or Chromium console):
+        var x = {foo: 20, hui: "bla"};
+        var a = new BinaryMinHeap([x,{foo:3},{foo:10},{foo:20},{foo:30},{foo:6},{foo:1},{foo:3}],"foo");
+        console.log(a.extractMin());
+        console.log(a.extractMin());
+        x.foo = 0; // update key
+        a.heapify(); // call this always after having a key updated
+        console.log(a.extractMin());
+        console.log(a.extractMin());
+    - can also be used on a simple array, like [9,7,8,5]
+ */
+function BinaryMinHeap(array, key) {
+    
+    /* Binary tree stored in an array, no need for a complicated data structure */
+    var tree = [];
+    
+    var key = key || 'key';
+    
+    /* Calculate the index of the parent or a child */
+    var parent = function(index) { return Math.floor((index - 1)/2); };
+    var right = function(index) { return 2 * index + 2; };
+    var left = function(index) { return 2 * index + 1; };
+
+    /* Helper function to swap elements with their parent 
+       as long as the parent is bigger */
+    function bubble_up(i) {
+        var p = parent(i);
+        while((p >= 0) && (tree[i][key] < tree[p][key])) {
+            /* swap with parent */
+            tree[i] = tree.splice(p, 1, tree[i])[0];
+            /* go up one level */
+            i = p;
+            p = parent(i);
+        }
+    }
+
+    /* Helper function to swap elements with the smaller of their children
+       as long as there is one */
+    function bubble_down(i) {
+        var l = left(i);
+        var r = right(i);
+        
+        /* as long as there are smaller children */
+        while(tree[l] && (tree[i][key] > tree[l][key]) || tree[r] && (tree[i][key] > tree[r][key])) {
+            
+            /* find smaller child */
+            var child = tree[l] ? tree[r] ? tree[l][key] > tree[r][key] ? r : l : l : l;
+            
+            /* swap with smaller child with current element */
+            tree[i] = tree.splice(child, 1, tree[i])[0];
+            
+            /* go up one level */
+            i = child;
+            l = left(i);
+            r = right(i);
+        }
+    }
+    
+    /* Insert a new element with respect to the heap property
+       1. Insert the element at the end
+       2. Bubble it up until it is smaller than its parent */
+    this.insert = function(element) {
+    
+        /* make sure there's a key property */
+        (element[key] == undefined) && (element = {key:element});
+        
+        /* insert element at the end */
+        tree.push(element);
+
+        /* bubble up the element */
+        bubble_up(tree.length - 1);
+    }
+    
+    /* Only show us the minimum */
+    this.min = function() {
+        return tree.length == 1 ? undefined : tree[0];
+    }
+    
+    /* Return and remove the minimum
+       1. Take the root as the minimum that we are looking for
+       2. Move the last element to the root (thereby deleting the root) 
+       3. Compare the new root with both of its children, swap it with the
+          smaller child and then check again from there (bubble down)
+    */
+    this.extractMin = function() {
+        var result = this.min();
+        
+        /* move the last element to the root or empty the tree completely */
+        /* bubble down the new root if necessary */
+        (tree.length == 1) && (tree = []) || (tree[0] = tree.pop()) && bubble_down(0);
+        
+        return result;        
+    }
+    
+    /* currently unused, TODO implement */
+    this.changeKey = function(index, key) {
+        throw "function not implemented";
+    }
+
+    this.heapify = function() {
+        for(var start = Math.floor((tree.length - 2) / 2); start >= 0; start--) {
+            bubble_down(start);
+        }
+    }
+    
+    /* insert the input elements one by one only when we don't have a key property (TODO can be done more elegant) */
+    for(i in (array || []))
+        this.insert(array[i]);
+}
+
+
+
+/*
+    Quick Sort:
+        1. Select some random value from the array, the median.
+        2. Divide the array in three smaller arrays according to the elements
+           being less, equal or greater than the median.
+        3. Recursively sort the array containg the elements less than the
+           median and the one containing elements greater than the median.
+        4. Concatenate the three arrays (less, equal and greater).
+        5. One or no element is always sorted.
+    TODO: This could be implemented more efficiently by using only one array object and several pointers.
+*/
+function quickSort(arr) {
+    /* recursion anchor: one element is always sorted */
+    if(arr.length <= 1) return arr;
+    /* randomly selecting some value */
+    var median = arr[Math.floor(Math.random() * arr.length)];
+    var arr1 = [], arr2 = [], arr3 = [];
+    for(var i in arr) {
+        arr[i] < median && arr1.push(arr[i]);
+        arr[i] == median && arr2.push(arr[i]);
+        arr[i] > median && arr3.push(arr[i]);
+    }
+    /* recursive sorting and assembling final result */
+    return quickSort(arr1).concat(arr2).concat(quickSort(arr3));
+}
+
+/*
+    Selection Sort:
+        1. Select the minimum and remove it from the array
+        2. Sort the rest recursively
+        3. Return the minimum plus the sorted rest
+        4. An array with only one element is already sorted
+*/
+function selectionSort(arr) {
+    /* recursion anchor: one element is always sorted */
+    if(arr.length == 1) return arr;
+    var minimum = Infinity;
+    var index;
+    for(var i in arr) {
+        if(arr[i] < minimum) {
+            minimum = arr[i];
+            index = i; /* remember the minimum index for later removal */
+        }
+    }
+    /* remove the minimum */
+    arr.splice(index, 1);
+    /* assemble result and sort recursively (could be easily done iteratively as well)*/
+    return [minimum].concat(selectionSort(arr));
+}
+
+/*
+    Merge Sort:
+        1. Cut the array in half
+        2. Sort each of them recursively
+        3. Merge the two sorted arrays
+        4. An array with only one element is considered sorted
+
+*/
+function mergeSort(arr) {
+    /* merges two sorted arrays into one sorted array */
+    function merge(a, b) {
+        /* result set */
+        var c = [];
+        /* as long as there are elements in the arrays to be merged */
+        while(a.length > 0 || b.length > 0){
+            /* are there elements to be merged, if yes, compare them and merge */
+            var n = a.length > 0 && b.length > 0 ? a[0] < b[0] ? a.shift() : b.shift() : b.length > 0 ? b.shift() : a.length > 0 ? a.shift() : null;
+            /* always push the smaller one onto the result set */
+            n != null && c.push(n);
+        }
+        return c;
+    }
+    /* this mergeSort implementation cuts the array in half, wich should be fine with randomized arrays, but introduces the risk of a worst-case scenario */
+    median = Math.floor(arr.length / 2);
+    var part1 = arr.slice(0, median); /* for some reason it doesn't work if inserted directly in the return statement (tried so with firefox) */
+    var part2 = arr.slice(median - arr.length);
+    return arr.length <= 1 ? arr : merge(
+        mergeSort(part1), /* first half */
+        mergeSort(part2) /* second half */
+    );
+}
+
+/* Balanced Red-Black-Tree */
+function RedBlackTree(arr) {
+    
+}
+
+function BTree(arr) {
+    
+}
+
+function NaryTree(n, arr) {
+    
+}
+
+/**
+ * Knuth-Morris-Pratt string matching algorithm - finds a pattern in a text.
+ * FIXME: Doesn't work correctly yet.
+ */
+function kmp(p, t) {
+
+    /**
+     * PREFIX, OVERLAP or FALIURE function for KMP. Computes how many iterations
+     * the algorithm can skip after a mismatch.
+     *
+     * @input p - pattern (string)
+     * @result array of skippable iterations
+     */
+    function prefix(p) {
+        /* pi contains the computed skip marks */
+        var pi = [0], k = 0;
+        for(q = 1; q < p.length; q++) {
+            while(k > 0 && (p.charAt(k) != p.charAt(q)))
+                k = pi[k-1];
+            
+            (p.charAt(k) == p.charAt(q)) && k++;
+            
+            pi[q] = k;
+        }
+        return pi;
+    }
+    
+    /* The actual KMP algorithm starts here. */
+    
+    var pi = prefix(p), q = 0, result = [];
+    
+    for(var i = 0; i < t.length; i++) {
+        /* jump forward as long as the character doesn't match */
+        while((q > 0) && (p.charAt(q) != t.charAt(i)))
+            q = pi[q];
+        
+        (p.charAt(q) == t.charAt(i)) && q++;
+        
+        (q == p.length) && result.push(i - p.length) && (q = pi[q]);
+    }
+    
+    return result;
+}
+
+/* step for algorithm visualisation */
+function step(comment, funct) {
+    //wait for input
+    //display comment (before or after waiting)
+//    next.wait();
+    /* execute callback function */
+    funct();
+}
+
+/**
+ * Curry - Function currying
+ * Copyright (c) 2008 Ariel Flesler - aflesler(at)gmail(dot)com | http://flesler.blogspot.com
+ * Licensed under BSD (http://www.opensource.org/licenses/bsd-license.php)
+ * Date: 10/4/2008
+ *
+ * @author Ariel Flesler
+ * @version 1.0.1
+ */
+function curry( fn ){
+       return function(){
+               var args = curry.args(arguments),
+                       master = arguments.callee,
+                       self = this;
+
+               return args.length >= fn.length ? fn.apply(self,args) : function(){
+                       return master.apply( self, args.concat(curry.args(arguments)) );
+               };
+       };
+};
+
+curry.args = function( args ){
+       return Array.prototype.slice.call(args);
+};
+
+Function.prototype.curry = function(){
+       return curry(this);
+};
+
+/**
+ * Topological Sort
+ *
+ * Sort a directed graph based on incoming edges
+ *
+ * Coded by Jake Stothard
+ */
+function topological_sort(g) {
+    //Mark nodes as "deleted" instead of actually deleting them
+    //That way we don't have to copy g
+
+    for(i in g.nodes)
+       g.nodes[i].deleted = false;
+    
+    var ret = topological_sort_helper(g);
+
+    //Cleanup: Remove the deleted property
+    for(i in g.nodes)
+       delete g.nodes[i].deleted
+
+    return ret;
+}
+function topological_sort_helper(g) {
+    //Find node with no incoming edges
+    var node;
+    for(i in g.nodes) {
+       if(g.nodes[i].deleted)
+           continue; //Bad style, meh
+       
+       var incoming = false;
+       for(j in g.nodes[i].edges) {
+           if(g.nodes[i].edges[j].target == g.nodes[i]
+             && g.nodes[i].edges[j].source.deleted == false) {
+               incoming = true;
+               break;
+           }
+       }
+       if(!incoming) {
+           node = g.nodes[i];
+           break;
+       }
+    }
+
+    // Either unsortable or done. Either way, GTFO
+    if(node == undefined)
+       return [];
+
+    //"Delete" node from g
+    node.deleted = true;
+    
+    var tail = topological_sort_helper(g);
+
+    tail.unshift(node);
+
+    return tail;
+}
diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/dracula_graffle.js b/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/dracula_graffle.js
new file mode 100644 (file)
index 0000000..ddf171d
--- /dev/null
@@ -0,0 +1,106 @@
+/**
+ * Originally grabbed from the official RaphaelJS Documentation
+ * http://raphaeljs.com/graffle.html
+ * Adopted (arrows) and commented by Philipp Strathausen http://blog.ameisenbar.de
+ * Licenced under the MIT licence.
+ */
+
+/**
+ * Usage:
+ * connect two shapes
+ * parameters: 
+ *      source shape [or connection for redrawing], 
+ *      target shape,
+ *      style with { fg : linecolor, bg : background color, directed: boolean }
+ * returns:
+ *      connection { draw = function() }
+ */
+Raphael.fn.connection = function (obj1, obj2, style) {
+    var selfRef = this;
+    /* create and return new connection */
+    var edge = {/*
+        from : obj1,
+        to : obj2,
+        style : style,*/
+        draw : function() {
+            /* get bounding boxes of target and source */
+            var bb1 = obj1.getBBox();
+            var bb2 = obj2.getBBox();
+            var off1 = 0;
+            var off2 = 0;
+            /* coordinates for potential connection coordinates from/to the objects */
+            var p = [
+                {x: bb1.x + bb1.width / 2, y: bb1.y - off1},              /* NORTH 1 */
+                {x: bb1.x + bb1.width / 2, y: bb1.y + bb1.height + off1}, /* SOUTH 1 */
+                {x: bb1.x - off1, y: bb1.y + bb1.height / 2},             /* WEST  1 */
+                {x: bb1.x + bb1.width + off1, y: bb1.y + bb1.height / 2}, /* EAST  1 */
+                {x: bb2.x + bb2.width / 2, y: bb2.y - off2},              /* NORTH 2 */
+                {x: bb2.x + bb2.width / 2, y: bb2.y + bb2.height + off2}, /* SOUTH 2 */
+                {x: bb2.x - off2, y: bb2.y + bb2.height / 2},             /* WEST  2 */
+                {x: bb2.x + bb2.width + off2, y: bb2.y + bb2.height / 2}  /* EAST  2 */
+            ];
+            
+            /* distances between objects and according coordinates connection */
+            var d = {}, dis = [];
+
+            /*
+             * find out the best connection coordinates by trying all possible ways
+             */
+            /* loop the first object's connection coordinates */
+            for (var i = 0; i < 4; i++) {
+                /* loop the seond object's connection coordinates */
+                for (var j = 4; j < 8; j++) {
+                    var dx = Math.abs(p[i].x - p[j].x),
+                        dy = Math.abs(p[i].y - p[j].y);
+                    if ((i == j - 4) || (((i != 3 && j != 6) || p[i].x < p[j].x) && ((i != 2 && j != 7) || p[i].x > p[j].x) && ((i != 0 && j != 5) || p[i].y > p[j].y) && ((i != 1 && j != 4) || p[i].y < p[j].y))) {
+                        dis.push(dx + dy);
+                        d[dis[dis.length - 1].toFixed(3)] = [i, j];
+                    }
+                }
+            }
+            var res = dis.length == 0 ? [0, 4] : d[Math.min.apply(Math, dis).toFixed(3)];
+            /* bezier path */
+            var x1 = p[res[0]].x,
+                y1 = p[res[0]].y,
+                x4 = p[res[1]].x,
+                y4 = p[res[1]].y,
+                dx = Math.max(Math.abs(x1 - x4) / 2, 10),
+                dy = Math.max(Math.abs(y1 - y4) / 2, 10),
+                x2 = [x1, x1, x1 - dx, x1 + dx][res[0]].toFixed(3),
+                y2 = [y1 - dy, y1 + dy, y1, y1][res[0]].toFixed(3),
+                x3 = [0, 0, 0, 0, x4, x4, x4 - dx, x4 + dx][res[1]].toFixed(3),
+                y3 = [0, 0, 0, 0, y1 + dy, y1 - dy, y4, y4][res[1]].toFixed(3);
+            /* assemble path and arrow */
+            var path = ["M", x1.toFixed(3), y1.toFixed(3), "C", x2, y2, x3, y3, x4.toFixed(3), y4.toFixed(3)].join(",");
+            /* arrow */
+            if(style && style.directed) {
+                /* magnitude, length of the last path vector */
+                var mag = Math.sqrt((y4 - y3) * (y4 - y3) + (x4 - x3) * (x4 - x3));
+                /* vector normalisation to specified length  */
+                var norm = function(x,l){return (-x*(l||5)/mag);};
+                /* calculate array coordinates (two lines orthogonal to the path vector) */
+                var arr = [
+                    {x:(norm(x4-x3)+norm(y4-y3)+x4).toFixed(3), y:(norm(y4-y3)+norm(x4-x3)+y4).toFixed(3)},
+                    {x:(norm(x4-x3)-norm(y4-y3)+x4).toFixed(3), y:(norm(y4-y3)-norm(x4-x3)+y4).toFixed(3)}
+                ];
+                path = path + ",M"+arr[0].x+","+arr[0].y+",L"+x4+","+y4+",L"+arr[1].x+","+arr[1].y; 
+            }
+            /* function to be used for moving existent path(s), e.g. animate() or attr() */
+            var move = "attr";
+            /* applying path(s) */
+            edge.fg && edge.fg[move]({path:path}) 
+                || (edge.fg = selfRef.path(path).attr({stroke: style && style.stroke || "#000", fill: "none"}).toBack());
+            edge.bg && edge.bg[move]({path:path})
+                || style && style.fill && (edge.bg = style.fill.split && selfRef.path(path).attr({stroke: style.fill.split("|")[0], fill: "none", "stroke-width": style.fill.split("|")[1] || 3}).toBack());
+            /* setting label */
+            style && style.label 
+                && (edge.label && edge.label.attr({x:(x1+x4)/2, y:(y1+y4)/2}) 
+                    || (edge.label = selfRef.text((x1+x4)/2, (y1+y4)/2, style.label).attr({fill: "#000", "font-size": style["font-size"] || "12px"})));
+            style && style.label && style["label-style"] && edge.label && edge.label.attr(style["label-style"]);
+            style && style.callback && style.callback(edge);
+        }
+    }
+    edge.draw();
+    return edge;
+};
+//Raphael.prototype.set.prototype.dodo=function(){console.log("works");};
diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/dracula_graph.js b/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/dracula_graph.js
new file mode 100644 (file)
index 0000000..f3e43e1
--- /dev/null
@@ -0,0 +1,527 @@
+/*
+ *  Dracula Graph Layout and Drawing Framework 0.0.3alpha
+ *  (c) 2010 Philipp Strathausen <strathausen@gmail.com>, http://strathausen.eu
+ *  Contributions by Jake Stothard <stothardj@gmail.com>.
+ *
+ *  based on the Graph JavaScript framework, version 0.0.1
+ *  (c) 2006 Aslak Hellesoy <aslak.hellesoy@gmail.com>
+ *  (c) 2006 Dave Hoover <dave.hoover@gmail.com>
+ *
+ *  Ported from Graph::Layouter::Spring in
+ *    http://search.cpan.org/~pasky/Graph-Layderer-0.02/
+ *  The algorithm is based on a spring-style layouter of a Java-based social
+ *  network tracker PieSpy written by Paul Mutton <paul@jibble.org>.
+ *
+ *  This code is freely distributable under the MIT license. Commercial use is
+ *  hereby granted without any cost or restriction.
+ *
+ *  Links:
+ *
+ *  Graph Dracula JavaScript Framework:
+ *      http://graphdracula.net
+ *
+ /*--------------------------------------------------------------------------*/
+
+/*
+ * Edge Factory
+ */
+var AbstractEdge = function() {
+}
+AbstractEdge.prototype = {
+    hide: function() {
+        this.connection.fg.hide();
+        this.connection.bg && this.bg.connection.hide();
+    }
+};
+var EdgeFactory = function() {
+    this.template = new AbstractEdge();
+    this.template.style = new Object();
+    this.template.style.directed = false;
+    this.template.weight = 1;
+};
+EdgeFactory.prototype = {
+    build: function(source, target) {
+        var e = jQuery.extend(true, {}, this.template);
+        e.source = source;
+        e.target = target;
+        return e;
+    }
+};
+
+/*
+ * Graph
+ */
+var Graph = function() {
+    this.nodes = {};
+    this.edges = [];
+    this.snapshots = []; // previous graph states TODO to be implemented
+    this.edgeFactory = new EdgeFactory();
+};
+Graph.prototype = {
+    /* 
+     * add a node
+     * @id          the node's ID (string or number)
+     * @content     (optional, dictionary) can contain any information that is
+     *              being interpreted by the layout algorithm or the graph
+     *              representation
+     */
+    addNode: function(id, content) {
+        /* testing if node is already existing in the graph */
+        if(this.nodes[id] == undefined) {
+            this.nodes[id] = new Graph.Node(id, content);
+        }
+        return this.nodes[id];
+    },
+
+    addEdge: function(source, target, style) {
+        var s = this.addNode(source);
+        var t = this.addNode(target);
+        var edge = this.edgeFactory.build(s, t);
+        jQuery.extend(edge.style,style);
+        s.edges.push(edge);
+        this.edges.push(edge);
+        // NOTE: Even directed edges are added to both nodes.
+        t.edges.push(edge);
+    },
+    
+    /* TODO to be implemented
+     * Preserve a copy of the graph state (nodes, positions, ...)
+     * @comment     a comment describing the state
+     */
+    snapShot: function(comment) {
+        /* FIXME
+        var graph = new Graph();
+        graph.nodes = jQuery.extend(true, {}, this.nodes);
+        graph.edges = jQuery.extend(true, {}, this.edges);
+        this.snapshots.push({comment: comment, graph: graph});
+        */
+    },
+    removeNode: function(id) {
+        delete this.nodes[id];
+        for(var i = 0; i < this.edges.length; i++) {
+            if (this.edges[i].source.id == id || this.edges[i].target.id == id) {
+                this.edges.splice(i, 1);
+                i--;
+            }
+        }
+    }
+};
+
+/*
+ * Node
+ */
+Graph.Node = function(id, node){
+    node = node || {};
+    node.id = id;
+    node.edges = [];
+    node.hide = function() {
+        this.hidden = true;
+        this.shape && this.shape.hide(); /* FIXME this is representation specific code and should be elsewhere */
+        for(i in this.edges)
+            (this.edges[i].source.id == id || this.edges[i].target == id) && this.edges[i].hide && this.edges[i].hide();
+    };
+    node.show = function() {
+        this.hidden = false;
+        this.shape && this.shape.show();
+        for(i in this.edges)
+            (this.edges[i].source.id == id || this.edges[i].target == id) && this.edges[i].show && this.edges[i].show();
+    };
+    return node;
+};
+Graph.Node.prototype = {
+};
+
+/*
+ * Renderer base class
+ */
+Graph.Renderer = {};
+
+/*
+ * Renderer implementation using RaphaelJS
+ */
+Graph.Renderer.Raphael = function(element, graph, width, height) {
+    this.width = width || 400;
+    this.height = height || 400;
+    var selfRef = this;
+    this.r = Raphael(element, this.width, this.height);
+    this.radius = 40; /* max dimension of a node */
+    this.graph = graph;
+    this.mouse_in = false;
+
+    /* TODO default node rendering function */
+    if(!this.graph.render) {
+        this.graph.render = function() {
+            return;
+        }
+    }
+    
+    /*
+     * Dragging
+     */
+    this.isDrag = false;
+    this.dragger = function (e) {
+        this.dx = e.clientX;
+        this.dy = e.clientY;
+        selfRef.isDrag = this;
+        this.set && this.set.animate({"fill-opacity": .1}, 200) && this.set.toFront();
+        e.preventDefault && e.preventDefault();
+    };
+    
+    var d = document.getElementById(element);
+    d.onmousemove = function (e) {
+        e = e || window.event;
+        if (selfRef.isDrag) {
+            var bBox = selfRef.isDrag.set.getBBox();
+            // TODO round the coordinates here (eg. for proper image representation)
+            var newX = e.clientX - selfRef.isDrag.dx + (bBox.x + bBox.width / 2);
+            var newY = e.clientY - selfRef.isDrag.dy + (bBox.y + bBox.height / 2);
+            /* prevent shapes from being dragged out of the canvas */
+            var clientX = e.clientX - (newX < 20 ? newX - 20 : newX > selfRef.width - 20 ? newX - selfRef.width + 20 : 0);
+            var clientY = e.clientY - (newY < 20 ? newY - 20 : newY > selfRef.height - 20 ? newY - selfRef.height + 20 : 0);
+            selfRef.isDrag.set.translate(clientX - Math.round(selfRef.isDrag.dx), clientY - Math.round(selfRef.isDrag.dy));
+            //            console.log(clientX - Math.round(selfRef.isDrag.dx), clientY - Math.round(selfRef.isDrag.dy));
+            for (var i in selfRef.graph.edges) {
+                selfRef.graph.edges[i].connection && selfRef.graph.edges[i].connection.draw();
+            }
+            //selfRef.r.safari();
+            selfRef.isDrag.dx = clientX;
+            selfRef.isDrag.dy = clientY;
+        }
+    };
+    d.onmouseup = function () {
+        selfRef.isDrag && selfRef.isDrag.set.animate({"fill-opacity": .6}, 500);
+        selfRef.isDrag = false;
+    };
+    this.draw();
+};
+Graph.Renderer.Raphael.prototype = {
+    translate: function(point) {
+        return [
+            (point[0] - this.graph.layoutMinX) * this.factorX + this.radius,
+            (point[1] - this.graph.layoutMinY) * this.factorY + this.radius
+        ];
+    },
+
+    rotate: function(point, length, angle) {
+        var dx = length * Math.cos(angle);
+        var dy = length * Math.sin(angle);
+        return [point[0]+dx, point[1]+dy];
+    },
+
+    draw: function() {
+        this.factorX = (this.width - 2 * this.radius) / (this.graph.layoutMaxX - this.graph.layoutMinX);
+        this.factorY = (this.height - 2 * this.radius) / (this.graph.layoutMaxY - this.graph.layoutMinY);
+        for (i in this.graph.nodes) {
+            this.drawNode(this.graph.nodes[i]);
+        }
+        for (var i = 0; i < this.graph.edges.length; i++) {
+            this.drawEdge(this.graph.edges[i]);
+        }
+    },
+
+    drawNode: function(node) {
+        var point = this.translate([node.layoutPosX, node.layoutPosY]);
+        node.point = point;
+
+        /* if node has already been drawn, move the nodes */
+        if(node.shape) {
+            var oBBox = node.shape.getBBox();
+            var opoint = { x: oBBox.x + oBBox.width / 2, y: oBBox.y + oBBox.height / 2};
+            node.shape.translate(Math.round(point[0] - opoint.x), Math.round(point[1] - opoint.y));
+            this.r.safari();
+            return node;
+        }/* else, draw new nodes */
+
+        var shape;
+
+        /* if a node renderer function is provided by the user, then use it 
+           or the default render function instead */
+        if(!node.render) {
+            node.render = function(r, node) {
+                /* the default node drawing */
+                var color = Raphael.getColor();
+                var ellipse = r.ellipse(0, 0, 30, 20).attr({fill: color, stroke: color, "stroke-width": 2});
+                /* set DOM node ID */
+                ellipse.node.id = node.label || node.id;
+                shape = r.set().
+                    push(ellipse).
+                    push(r.text(0, 30, node.label || node.id));
+                return shape;
+            }
+        }
+        /* or check for an ajax representation of the nodes */
+        if(node.shapes) {
+            // TODO ajax representation evaluation
+        }
+
+        shape = node.render(this.r, node).hide();
+
+        shape.attr({"fill-opacity": .6});
+        /* re-reference to the node an element belongs to, needed for dragging all elements of a node */
+        shape.items.forEach(function(item){ item.set = shape; item.node.style.cursor = "move"; });
+        shape.mousedown(this.dragger);
+
+        var box = shape.getBBox();
+        shape.translate(Math.round(point[0]-(box.x+box.width/2)),Math.round(point[1]-(box.y+box.height/2)))
+        //console.log(box,point);
+        node.hidden || shape.show();
+        node.shape = shape;
+    },
+    drawEdge: function(edge) {
+        /* if this edge already exists the other way around and is undirected */
+        if(edge.backedge)
+            return;
+        if(edge.source.hidden || edge.target.hidden) {
+            edge.connection && edge.connection.fg.hide() | edge.connection.bg && edge.connection.bg.hide();
+            return;
+        }
+        /* if edge already has been drawn, only refresh the edge */
+        if(!edge.connection) {
+            edge.style && edge.style.callback && edge.style.callback(edge); // TODO move this somewhere else
+            edge.connection = this.r.connection(edge.source.shape, edge.target.shape, edge.style);
+            return;
+        }
+        //FIXME showing doesn't work well
+        edge.connection.fg.show();
+        edge.connection.bg && edge.connection.bg.show();
+        edge.connection.draw();
+    }
+};
+Graph.Layout = {};
+Graph.Layout.Spring = function(graph) {
+    this.graph = graph;
+    this.iterations = 500;
+    this.maxRepulsiveForceDistance = 6;
+    this.k = 2;
+    this.c = 0.01;
+    this.maxVertexMovement = 0.5;
+    this.layout();
+};
+Graph.Layout.Spring.prototype = {
+    layout: function() {
+        this.layoutPrepare();
+        for (var i = 0; i < this.iterations; i++) {
+            this.layoutIteration();
+        }
+        this.layoutCalcBounds();
+    },
+    
+    layoutPrepare: function() {
+        for (i in this.graph.nodes) {
+            var node = this.graph.nodes[i];
+            node.layoutPosX = 0;
+            node.layoutPosY = 0;
+            node.layoutForceX = 0;
+            node.layoutForceY = 0;
+        }
+        
+    },
+    
+    layoutCalcBounds: function() {
+        var minx = Infinity, maxx = -Infinity, miny = Infinity, maxy = -Infinity;
+
+        for (i in this.graph.nodes) {
+            var x = this.graph.nodes[i].layoutPosX;
+            var y = this.graph.nodes[i].layoutPosY;
+            
+            if(x > maxx) maxx = x;
+            if(x < minx) minx = x;
+            if(y > maxy) maxy = y;
+            if(y < miny) miny = y;
+        }
+
+        this.graph.layoutMinX = minx;
+        this.graph.layoutMaxX = maxx;
+        this.graph.layoutMinY = miny;
+        this.graph.layoutMaxY = maxy;
+    },
+    
+    layoutIteration: function() {
+        // Forces on nodes due to node-node repulsions
+
+        var prev = new Array();
+        for(var c in this.graph.nodes) {
+            var node1 = this.graph.nodes[c];
+            for (var d in prev) {
+                var node2 = this.graph.nodes[prev[d]];
+                this.layoutRepulsive(node1, node2);
+                
+            }
+            prev.push(c);
+        }
+        
+        // Forces on nodes due to edge attractions
+        for (var i = 0; i < this.graph.edges.length; i++) {
+            var edge = this.graph.edges[i];
+            this.layoutAttractive(edge);             
+        }
+        
+        // Move by the given force
+        for (i in this.graph.nodes) {
+            var node = this.graph.nodes[i];
+            var xmove = this.c * node.layoutForceX;
+            var ymove = this.c * node.layoutForceY;
+
+            var max = this.maxVertexMovement;
+            if(xmove > max) xmove = max;
+            if(xmove < -max) xmove = -max;
+            if(ymove > max) ymove = max;
+            if(ymove < -max) ymove = -max;
+            
+            node.layoutPosX += xmove;
+            node.layoutPosY += ymove;
+            node.layoutForceX = 0;
+            node.layoutForceY = 0;
+        }
+    },
+
+    layoutRepulsive: function(node1, node2) {
+        if (typeof node1 == 'undefined' || typeof node2 == 'undefined')
+            return;
+        var dx = node2.layoutPosX - node1.layoutPosX;
+        var dy = node2.layoutPosY - node1.layoutPosY;
+        var d2 = dx * dx + dy * dy;
+        if(d2 < 0.01) {
+            dx = 0.1 * Math.random() + 0.1;
+            dy = 0.1 * Math.random() + 0.1;
+            var d2 = dx * dx + dy * dy;
+        }
+        var d = Math.sqrt(d2);
+        if(d < this.maxRepulsiveForceDistance) {
+            var repulsiveForce = this.k * this.k / d;
+            node2.layoutForceX += repulsiveForce * dx / d;
+            node2.layoutForceY += repulsiveForce * dy / d;
+            node1.layoutForceX -= repulsiveForce * dx / d;
+            node1.layoutForceY -= repulsiveForce * dy / d;
+        }
+    },
+
+    layoutAttractive: function(edge) {
+        var node1 = edge.source;
+        var node2 = edge.target;
+        
+        var dx = node2.layoutPosX - node1.layoutPosX;
+        var dy = node2.layoutPosY - node1.layoutPosY;
+        var d2 = dx * dx + dy * dy;
+        if(d2 < 0.01) {
+            dx = 0.1 * Math.random() + 0.1;
+            dy = 0.1 * Math.random() + 0.1;
+            var d2 = dx * dx + dy * dy;
+        }
+        var d = Math.sqrt(d2);
+        if(d > this.maxRepulsiveForceDistance) {
+            d = this.maxRepulsiveForceDistance;
+            d2 = d * d;
+        }
+        var attractiveForce = (d2 - this.k * this.k) / this.k;
+        if(edge.attraction == undefined) edge.attraction = 1;
+        attractiveForce *= Math.log(edge.attraction) * 0.5 + 1;
+        
+        node2.layoutForceX -= attractiveForce * dx / d;
+        node2.layoutForceY -= attractiveForce * dy / d;
+        node1.layoutForceX += attractiveForce * dx / d;
+        node1.layoutForceY += attractiveForce * dy / d;
+    }
+};
+
+Graph.Layout.Ordered = function(graph, order) {
+    this.graph = graph;
+    this.order = order;
+    this.layout();
+};
+Graph.Layout.Ordered.prototype = {
+    layout: function() {
+        this.layoutPrepare();
+        this.layoutCalcBounds();
+    },
+    
+    layoutPrepare: function(order) {
+        for (i in this.graph.nodes) {
+            var node = this.graph.nodes[i];
+            node.layoutPosX = 0;
+            node.layoutPosY = 0;
+        }
+            var counter = 0;
+            for (i in this.order) {
+                var node = this.order[i];
+                node.layoutPosX = counter;
+                node.layoutPosY = Math.random();
+                counter++;
+            }
+    },
+    
+    layoutCalcBounds: function() {
+        var minx = Infinity, maxx = -Infinity, miny = Infinity, maxy = -Infinity;
+
+        for (i in this.graph.nodes) {
+            var x = this.graph.nodes[i].layoutPosX;
+            var y = this.graph.nodes[i].layoutPosY;
+            
+            if(x > maxx) maxx = x;
+            if(x < minx) minx = x;
+            if(y > maxy) maxy = y;
+            if(y < miny) miny = y;
+        }
+
+        this.graph.layoutMinX = minx;
+        this.graph.layoutMaxX = maxx;
+
+        this.graph.layoutMinY = miny;
+        this.graph.layoutMaxY = maxy;
+    }
+};
+
+/*
+ * usefull JavaScript extensions, 
+ */
+
+function log(a) {console.log&&console.log(a);}
+
+/*
+ * Raphael Tooltip Plugin
+ * - attaches an element as a tooltip to another element
+ *
+ * Usage example, adding a rectangle as a tooltip to a circle:
+ *
+ *      paper.circle(100,100,10).tooltip(paper.rect(0,0,20,30));
+ *
+ * If you want to use more shapes, you'll have to put them into a set.
+ *
+ */
+Raphael.el.tooltip = function (tp) {
+    this.tp = tp;
+    this.tp.o = {x: 0, y: 0};
+    this.tp.hide();
+    this.hover(
+        function(event){ 
+            this.mousemove(function(event){ 
+                this.tp.translate(event.clientX - 
+                                  this.tp.o.x,event.clientY - this.tp.o.y);
+                this.tp.o = {x: event.clientX, y: event.clientY};
+            });
+            this.tp.show().toFront();
+        }, 
+        function(event){
+            this.tp.hide();
+            this.unmousemove();
+        });
+    return this;
+};
+
+/* For IE */
+if (!Array.prototype.forEach)
+{
+  Array.prototype.forEach = function(fun /*, thisp*/)
+  {
+    var len = this.length;
+    if (typeof fun != "function")
+      throw new TypeError();
+
+    var thisp = arguments[1];
+    for (var i = 0; i < len; i++)
+    {
+      if (i in this)
+        fun.call(thisp, this[i], i, this);
+    }
+  };
+}
diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/jquery-1.4.2.min.js b/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/jquery-1.4.2.min.js
new file mode 100644 (file)
index 0000000..7c24308
--- /dev/null
@@ -0,0 +1,154 @@
+/*!
+ * jQuery JavaScript Library v1.4.2
+ * http://jquery.com/
+ *
+ * Copyright 2010, John Resig
+ * Dual licensed under the MIT or GPL Version 2 licenses.
+ * http://jquery.org/license
+ *
+ * Includes Sizzle.js
+ * http://sizzlejs.com/
+ * Copyright 2010, The Dojo Foundation
+ * Released under the MIT, BSD, and GPL Licenses.
+ *
+ * Date: Sat Feb 13 22:33:48 2010 -0500
+ */
+(function(A,w){function ma(){if(!c.isReady){try{s.documentElement.doScroll("left")}catch(a){setTimeout(ma,1);return}c.ready()}}function Qa(a,b){b.src?c.ajax({url:b.src,async:false,dataType:"script"}):c.globalEval(b.text||b.textContent||b.innerHTML||"");b.parentNode&&b.parentNode.removeChild(b)}function X(a,b,d,f,e,j){var i=a.length;if(typeof b==="object"){for(var o in b)X(a,o,b[o],f,e,d);return a}if(d!==w){f=!j&&f&&c.isFunction(d);for(o=0;o<i;o++)e(a[o],b,f?d.call(a[o],o,e(a[o],b)):d,j);return a}return i?
+e(a[0],b):w}function J(){return(new Date).getTime()}function Y(){return false}function Z(){return true}function na(a,b,d){d[0].type=a;return c.event.handle.apply(b,d)}function oa(a){var b,d=[],f=[],e=arguments,j,i,o,k,n,r;i=c.data(this,"events");if(!(a.liveFired===this||!i||!i.live||a.button&&a.type==="click")){a.liveFired=this;var u=i.live.slice(0);for(k=0;k<u.length;k++){i=u[k];i.origType.replace(O,"")===a.type?f.push(i.selector):u.splice(k--,1)}j=c(a.target).closest(f,a.currentTarget);n=0;for(r=
+j.length;n<r;n++)for(k=0;k<u.length;k++){i=u[k];if(j[n].selector===i.selector){o=j[n].elem;f=null;if(i.preType==="mouseenter"||i.preType==="mouseleave")f=c(a.relatedTarget).closest(i.selector)[0];if(!f||f!==o)d.push({elem:o,handleObj:i})}}n=0;for(r=d.length;n<r;n++){j=d[n];a.currentTarget=j.elem;a.data=j.handleObj.data;a.handleObj=j.handleObj;if(j.handleObj.origHandler.apply(j.elem,e)===false){b=false;break}}return b}}function pa(a,b){return"live."+(a&&a!=="*"?a+".":"")+b.replace(/\./g,"`").replace(/ /g,
+"&")}function qa(a){return!a||!a.parentNode||a.parentNode.nodeType===11}function ra(a,b){var d=0;b.each(function(){if(this.nodeName===(a[d]&&a[d].nodeName)){var f=c.data(a[d++]),e=c.data(this,f);if(f=f&&f.events){delete e.handle;e.events={};for(var j in f)for(var i in f[j])c.event.add(this,j,f[j][i],f[j][i].data)}}})}function sa(a,b,d){var f,e,j;b=b&&b[0]?b[0].ownerDocument||b[0]:s;if(a.length===1&&typeof a[0]==="string"&&a[0].length<512&&b===s&&!ta.test(a[0])&&(c.support.checkClone||!ua.test(a[0]))){e=
+true;if(j=c.fragments[a[0]])if(j!==1)f=j}if(!f){f=b.createDocumentFragment();c.clean(a,b,f,d)}if(e)c.fragments[a[0]]=j?f:1;return{fragment:f,cacheable:e}}function K(a,b){var d={};c.each(va.concat.apply([],va.slice(0,b)),function(){d[this]=a});return d}function wa(a){return"scrollTo"in a&&a.document?a:a.nodeType===9?a.defaultView||a.parentWindow:false}var c=function(a,b){return new c.fn.init(a,b)},Ra=A.jQuery,Sa=A.$,s=A.document,T,Ta=/^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,Ua=/^.[^:#\[\.,]*$/,Va=/\S/,
+Wa=/^(\s|\u00A0)+|(\s|\u00A0)+$/g,Xa=/^<(\w+)\s*\/?>(?:<\/\1>)?$/,P=navigator.userAgent,xa=false,Q=[],L,$=Object.prototype.toString,aa=Object.prototype.hasOwnProperty,ba=Array.prototype.push,R=Array.prototype.slice,ya=Array.prototype.indexOf;c.fn=c.prototype={init:function(a,b){var d,f;if(!a)return this;if(a.nodeType){this.context=this[0]=a;this.length=1;return this}if(a==="body"&&!b){this.context=s;this[0]=s.body;this.selector="body";this.length=1;return this}if(typeof a==="string")if((d=Ta.exec(a))&&
+(d[1]||!b))if(d[1]){f=b?b.ownerDocument||b:s;if(a=Xa.exec(a))if(c.isPlainObject(b)){a=[s.createElement(a[1])];c.fn.attr.call(a,b,true)}else a=[f.createElement(a[1])];else{a=sa([d[1]],[f]);a=(a.cacheable?a.fragment.cloneNode(true):a.fragment).childNodes}return c.merge(this,a)}else{if(b=s.getElementById(d[2])){if(b.id!==d[2])return T.find(a);this.length=1;this[0]=b}this.context=s;this.selector=a;return this}else if(!b&&/^\w+$/.test(a)){this.selector=a;this.context=s;a=s.getElementsByTagName(a);return c.merge(this,
+a)}else return!b||b.jquery?(b||T).find(a):c(b).find(a);else if(c.isFunction(a))return T.ready(a);if(a.selector!==w){this.selector=a.selector;this.context=a.context}return c.makeArray(a,this)},selector:"",jquery:"1.4.2",length:0,size:function(){return this.length},toArray:function(){return R.call(this,0)},get:function(a){return a==null?this.toArray():a<0?this.slice(a)[0]:this[a]},pushStack:function(a,b,d){var f=c();c.isArray(a)?ba.apply(f,a):c.merge(f,a);f.prevObject=this;f.context=this.context;if(b===
+"find")f.selector=this.selector+(this.selector?" ":"")+d;else if(b)f.selector=this.selector+"."+b+"("+d+")";return f},each:function(a,b){return c.each(this,a,b)},ready:function(a){c.bindReady();if(c.isReady)a.call(s,c);else Q&&Q.push(a);return this},eq:function(a){return a===-1?this.slice(a):this.slice(a,+a+1)},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},slice:function(){return this.pushStack(R.apply(this,arguments),"slice",R.call(arguments).join(","))},map:function(a){return this.pushStack(c.map(this,
+function(b,d){return a.call(b,d,b)}))},end:function(){return this.prevObject||c(null)},push:ba,sort:[].sort,splice:[].splice};c.fn.init.prototype=c.fn;c.extend=c.fn.extend=function(){var a=arguments[0]||{},b=1,d=arguments.length,f=false,e,j,i,o;if(typeof a==="boolean"){f=a;a=arguments[1]||{};b=2}if(typeof a!=="object"&&!c.isFunction(a))a={};if(d===b){a=this;--b}for(;b<d;b++)if((e=arguments[b])!=null)for(j in e){i=a[j];o=e[j];if(a!==o)if(f&&o&&(c.isPlainObject(o)||c.isArray(o))){i=i&&(c.isPlainObject(i)||
+c.isArray(i))?i:c.isArray(o)?[]:{};a[j]=c.extend(f,i,o)}else if(o!==w)a[j]=o}return a};c.extend({noConflict:function(a){A.$=Sa;if(a)A.jQuery=Ra;return c},isReady:false,ready:function(){if(!c.isReady){if(!s.body)return setTimeout(c.ready,13);c.isReady=true;if(Q){for(var a,b=0;a=Q[b++];)a.call(s,c);Q=null}c.fn.triggerHandler&&c(s).triggerHandler("ready")}},bindReady:function(){if(!xa){xa=true;if(s.readyState==="complete")return c.ready();if(s.addEventListener){s.addEventListener("DOMContentLoaded",
+L,false);A.addEventListener("load",c.ready,false)}else if(s.attachEvent){s.attachEvent("onreadystatechange",L);A.attachEvent("onload",c.ready);var a=false;try{a=A.frameElement==null}catch(b){}s.documentElement.doScroll&&a&&ma()}}},isFunction:function(a){return $.call(a)==="[object Function]"},isArray:function(a){return $.call(a)==="[object Array]"},isPlainObject:function(a){if(!a||$.call(a)!=="[object Object]"||a.nodeType||a.setInterval)return false;if(a.constructor&&!aa.call(a,"constructor")&&!aa.call(a.constructor.prototype,
+"isPrototypeOf"))return false;var b;for(b in a);return b===w||aa.call(a,b)},isEmptyObject:function(a){for(var b in a)return false;return true},error:function(a){throw a;},parseJSON:function(a){if(typeof a!=="string"||!a)return null;a=c.trim(a);if(/^[\],:{}\s]*$/.test(a.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g,"@").replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g,"]").replace(/(?:^|:|,)(?:\s*\[)+/g,"")))return A.JSON&&A.JSON.parse?A.JSON.parse(a):(new Function("return "+
+a))();else c.error("Invalid JSON: "+a)},noop:function(){},globalEval:function(a){if(a&&Va.test(a)){var b=s.getElementsByTagName("head")[0]||s.documentElement,d=s.createElement("script");d.type="text/javascript";if(c.support.scriptEval)d.appendChild(s.createTextNode(a));else d.text=a;b.insertBefore(d,b.firstChild);b.removeChild(d)}},nodeName:function(a,b){return a.nodeName&&a.nodeName.toUpperCase()===b.toUpperCase()},each:function(a,b,d){var f,e=0,j=a.length,i=j===w||c.isFunction(a);if(d)if(i)for(f in a){if(b.apply(a[f],
+d)===false)break}else for(;e<j;){if(b.apply(a[e++],d)===false)break}else if(i)for(f in a){if(b.call(a[f],f,a[f])===false)break}else for(d=a[0];e<j&&b.call(d,e,d)!==false;d=a[++e]);return a},trim:function(a){return(a||"").replace(Wa,"")},makeArray:function(a,b){b=b||[];if(a!=null)a.length==null||typeof a==="string"||c.isFunction(a)||typeof a!=="function"&&a.setInterval?ba.call(b,a):c.merge(b,a);return b},inArray:function(a,b){if(b.indexOf)return b.indexOf(a);for(var d=0,f=b.length;d<f;d++)if(b[d]===
+a)return d;return-1},merge:function(a,b){var d=a.length,f=0;if(typeof b.length==="number")for(var e=b.length;f<e;f++)a[d++]=b[f];else for(;b[f]!==w;)a[d++]=b[f++];a.length=d;return a},grep:function(a,b,d){for(var f=[],e=0,j=a.length;e<j;e++)!d!==!b(a[e],e)&&f.push(a[e]);return f},map:function(a,b,d){for(var f=[],e,j=0,i=a.length;j<i;j++){e=b(a[j],j,d);if(e!=null)f[f.length]=e}return f.concat.apply([],f)},guid:1,proxy:function(a,b,d){if(arguments.length===2)if(typeof b==="string"){d=a;a=d[b];b=w}else if(b&&
+!c.isFunction(b)){d=b;b=w}if(!b&&a)b=function(){return a.apply(d||this,arguments)};if(a)b.guid=a.guid=a.guid||b.guid||c.guid++;return b},uaMatch:function(a){a=a.toLowerCase();a=/(webkit)[ \/]([\w.]+)/.exec(a)||/(opera)(?:.*version)?[ \/]([\w.]+)/.exec(a)||/(msie) ([\w.]+)/.exec(a)||!/compatible/.test(a)&&/(mozilla)(?:.*? rv:([\w.]+))?/.exec(a)||[];return{browser:a[1]||"",version:a[2]||"0"}},browser:{}});P=c.uaMatch(P);if(P.browser){c.browser[P.browser]=true;c.browser.version=P.version}if(c.browser.webkit)c.browser.safari=
+true;if(ya)c.inArray=function(a,b){return ya.call(b,a)};T=c(s);if(s.addEventListener)L=function(){s.removeEventListener("DOMContentLoaded",L,false);c.ready()};else if(s.attachEvent)L=function(){if(s.readyState==="complete"){s.detachEvent("onreadystatechange",L);c.ready()}};(function(){c.support={};var a=s.documentElement,b=s.createElement("script"),d=s.createElement("div"),f="script"+J();d.style.display="none";d.innerHTML="   <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
+var e=d.getElementsByTagName("*"),j=d.getElementsByTagName("a")[0];if(!(!e||!e.length||!j)){c.support={leadingWhitespace:d.firstChild.nodeType===3,tbody:!d.getElementsByTagName("tbody").length,htmlSerialize:!!d.getElementsByTagName("link").length,style:/red/.test(j.getAttribute("style")),hrefNormalized:j.getAttribute("href")==="/a",opacity:/^0.55$/.test(j.style.opacity),cssFloat:!!j.style.cssFloat,checkOn:d.getElementsByTagName("input")[0].value==="on",optSelected:s.createElement("select").appendChild(s.createElement("option")).selected,
+parentNode:d.removeChild(d.appendChild(s.createElement("div"))).parentNode===null,deleteExpando:true,checkClone:false,scriptEval:false,noCloneEvent:true,boxModel:null};b.type="text/javascript";try{b.appendChild(s.createTextNode("window."+f+"=1;"))}catch(i){}a.insertBefore(b,a.firstChild);if(A[f]){c.support.scriptEval=true;delete A[f]}try{delete b.test}catch(o){c.support.deleteExpando=false}a.removeChild(b);if(d.attachEvent&&d.fireEvent){d.attachEvent("onclick",function k(){c.support.noCloneEvent=
+false;d.detachEvent("onclick",k)});d.cloneNode(true).fireEvent("onclick")}d=s.createElement("div");d.innerHTML="<input type='radio' name='radiotest' checked='checked'/>";a=s.createDocumentFragment();a.appendChild(d.firstChild);c.support.checkClone=a.cloneNode(true).cloneNode(true).lastChild.checked;c(function(){var k=s.createElement("div");k.style.width=k.style.paddingLeft="1px";s.body.appendChild(k);c.boxModel=c.support.boxModel=k.offsetWidth===2;s.body.removeChild(k).style.display="none"});a=function(k){var n=
+s.createElement("div");k="on"+k;var r=k in n;if(!r){n.setAttribute(k,"return;");r=typeof n[k]==="function"}return r};c.support.submitBubbles=a("submit");c.support.changeBubbles=a("change");a=b=d=e=j=null}})();c.props={"for":"htmlFor","class":"className",readonly:"readOnly",maxlength:"maxLength",cellspacing:"cellSpacing",rowspan:"rowSpan",colspan:"colSpan",tabindex:"tabIndex",usemap:"useMap",frameborder:"frameBorder"};var G="jQuery"+J(),Ya=0,za={};c.extend({cache:{},expando:G,noData:{embed:true,object:true,
+applet:true},data:function(a,b,d){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var f=a[G],e=c.cache;if(!f&&typeof b==="string"&&d===w)return null;f||(f=++Ya);if(typeof b==="object"){a[G]=f;e[f]=c.extend(true,{},b)}else if(!e[f]){a[G]=f;e[f]={}}a=e[f];if(d!==w)a[b]=d;return typeof b==="string"?a[b]:a}},removeData:function(a,b){if(!(a.nodeName&&c.noData[a.nodeName.toLowerCase()])){a=a==A?za:a;var d=a[G],f=c.cache,e=f[d];if(b){if(e){delete e[b];c.isEmptyObject(e)&&c.removeData(a)}}else{if(c.support.deleteExpando)delete a[c.expando];
+else a.removeAttribute&&a.removeAttribute(c.expando);delete f[d]}}}});c.fn.extend({data:function(a,b){if(typeof a==="undefined"&&this.length)return c.data(this[0]);else if(typeof a==="object")return this.each(function(){c.data(this,a)});var d=a.split(".");d[1]=d[1]?"."+d[1]:"";if(b===w){var f=this.triggerHandler("getData"+d[1]+"!",[d[0]]);if(f===w&&this.length)f=c.data(this[0],a);return f===w&&d[1]?this.data(d[0]):f}else return this.trigger("setData"+d[1]+"!",[d[0],b]).each(function(){c.data(this,
+a,b)})},removeData:function(a){return this.each(function(){c.removeData(this,a)})}});c.extend({queue:function(a,b,d){if(a){b=(b||"fx")+"queue";var f=c.data(a,b);if(!d)return f||[];if(!f||c.isArray(d))f=c.data(a,b,c.makeArray(d));else f.push(d);return f}},dequeue:function(a,b){b=b||"fx";var d=c.queue(a,b),f=d.shift();if(f==="inprogress")f=d.shift();if(f){b==="fx"&&d.unshift("inprogress");f.call(a,function(){c.dequeue(a,b)})}}});c.fn.extend({queue:function(a,b){if(typeof a!=="string"){b=a;a="fx"}if(b===
+w)return c.queue(this[0],a);return this.each(function(){var d=c.queue(this,a,b);a==="fx"&&d[0]!=="inprogress"&&c.dequeue(this,a)})},dequeue:function(a){return this.each(function(){c.dequeue(this,a)})},delay:function(a,b){a=c.fx?c.fx.speeds[a]||a:a;b=b||"fx";return this.queue(b,function(){var d=this;setTimeout(function(){c.dequeue(d,b)},a)})},clearQueue:function(a){return this.queue(a||"fx",[])}});var Aa=/[\n\t]/g,ca=/\s+/,Za=/\r/g,$a=/href|src|style/,ab=/(button|input)/i,bb=/(button|input|object|select|textarea)/i,
+cb=/^(a|area)$/i,Ba=/radio|checkbox/;c.fn.extend({attr:function(a,b){return X(this,a,b,true,c.attr)},removeAttr:function(a){return this.each(function(){c.attr(this,a,"");this.nodeType===1&&this.removeAttribute(a)})},addClass:function(a){if(c.isFunction(a))return this.each(function(n){var r=c(this);r.addClass(a.call(this,n,r.attr("class")))});if(a&&typeof a==="string")for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1)if(e.className){for(var j=" "+e.className+" ",
+i=e.className,o=0,k=b.length;o<k;o++)if(j.indexOf(" "+b[o]+" ")<0)i+=" "+b[o];e.className=c.trim(i)}else e.className=a}return this},removeClass:function(a){if(c.isFunction(a))return this.each(function(k){var n=c(this);n.removeClass(a.call(this,k,n.attr("class")))});if(a&&typeof a==="string"||a===w)for(var b=(a||"").split(ca),d=0,f=this.length;d<f;d++){var e=this[d];if(e.nodeType===1&&e.className)if(a){for(var j=(" "+e.className+" ").replace(Aa," "),i=0,o=b.length;i<o;i++)j=j.replace(" "+b[i]+" ",
+" ");e.className=c.trim(j)}else e.className=""}return this},toggleClass:function(a,b){var d=typeof a,f=typeof b==="boolean";if(c.isFunction(a))return this.each(function(e){var j=c(this);j.toggleClass(a.call(this,e,j.attr("class"),b),b)});return this.each(function(){if(d==="string")for(var e,j=0,i=c(this),o=b,k=a.split(ca);e=k[j++];){o=f?o:!i.hasClass(e);i[o?"addClass":"removeClass"](e)}else if(d==="undefined"||d==="boolean"){this.className&&c.data(this,"__className__",this.className);this.className=
+this.className||a===false?"":c.data(this,"__className__")||""}})},hasClass:function(a){a=" "+a+" ";for(var b=0,d=this.length;b<d;b++)if((" "+this[b].className+" ").replace(Aa," ").indexOf(a)>-1)return true;return false},val:function(a){if(a===w){var b=this[0];if(b){if(c.nodeName(b,"option"))return(b.attributes.value||{}).specified?b.value:b.text;if(c.nodeName(b,"select")){var d=b.selectedIndex,f=[],e=b.options;b=b.type==="select-one";if(d<0)return null;var j=b?d:0;for(d=b?d+1:e.length;j<d;j++){var i=
+e[j];if(i.selected){a=c(i).val();if(b)return a;f.push(a)}}return f}if(Ba.test(b.type)&&!c.support.checkOn)return b.getAttribute("value")===null?"on":b.value;return(b.value||"").replace(Za,"")}return w}var o=c.isFunction(a);return this.each(function(k){var n=c(this),r=a;if(this.nodeType===1){if(o)r=a.call(this,k,n.val());if(typeof r==="number")r+="";if(c.isArray(r)&&Ba.test(this.type))this.checked=c.inArray(n.val(),r)>=0;else if(c.nodeName(this,"select")){var u=c.makeArray(r);c("option",this).each(function(){this.selected=
+c.inArray(c(this).val(),u)>=0});if(!u.length)this.selectedIndex=-1}else this.value=r}})}});c.extend({attrFn:{val:true,css:true,html:true,text:true,data:true,width:true,height:true,offset:true},attr:function(a,b,d,f){if(!a||a.nodeType===3||a.nodeType===8)return w;if(f&&b in c.attrFn)return c(a)[b](d);f=a.nodeType!==1||!c.isXMLDoc(a);var e=d!==w;b=f&&c.props[b]||b;if(a.nodeType===1){var j=$a.test(b);if(b in a&&f&&!j){if(e){b==="type"&&ab.test(a.nodeName)&&a.parentNode&&c.error("type property can't be changed");
+a[b]=d}if(c.nodeName(a,"form")&&a.getAttributeNode(b))return a.getAttributeNode(b).nodeValue;if(b==="tabIndex")return(b=a.getAttributeNode("tabIndex"))&&b.specified?b.value:bb.test(a.nodeName)||cb.test(a.nodeName)&&a.href?0:w;return a[b]}if(!c.support.style&&f&&b==="style"){if(e)a.style.cssText=""+d;return a.style.cssText}e&&a.setAttribute(b,""+d);a=!c.support.hrefNormalized&&f&&j?a.getAttribute(b,2):a.getAttribute(b);return a===null?w:a}return c.style(a,b,d)}});var O=/\.(.*)$/,db=function(a){return a.replace(/[^\w\s\.\|`]/g,
+function(b){return"\\"+b})};c.event={add:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){if(a.setInterval&&a!==A&&!a.frameElement)a=A;var e,j;if(d.handler){e=d;d=e.handler}if(!d.guid)d.guid=c.guid++;if(j=c.data(a)){var i=j.events=j.events||{},o=j.handle;if(!o)j.handle=o=function(){return typeof c!=="undefined"&&!c.event.triggered?c.event.handle.apply(o.elem,arguments):w};o.elem=a;b=b.split(" ");for(var k,n=0,r;k=b[n++];){j=e?c.extend({},e):{handler:d,data:f};if(k.indexOf(".")>-1){r=k.split(".");
+k=r.shift();j.namespace=r.slice(0).sort().join(".")}else{r=[];j.namespace=""}j.type=k;j.guid=d.guid;var u=i[k],z=c.event.special[k]||{};if(!u){u=i[k]=[];if(!z.setup||z.setup.call(a,f,r,o)===false)if(a.addEventListener)a.addEventListener(k,o,false);else a.attachEvent&&a.attachEvent("on"+k,o)}if(z.add){z.add.call(a,j);if(!j.handler.guid)j.handler.guid=d.guid}u.push(j);c.event.global[k]=true}a=null}}},global:{},remove:function(a,b,d,f){if(!(a.nodeType===3||a.nodeType===8)){var e,j=0,i,o,k,n,r,u,z=c.data(a),
+C=z&&z.events;if(z&&C){if(b&&b.type){d=b.handler;b=b.type}if(!b||typeof b==="string"&&b.charAt(0)==="."){b=b||"";for(e in C)c.event.remove(a,e+b)}else{for(b=b.split(" ");e=b[j++];){n=e;i=e.indexOf(".")<0;o=[];if(!i){o=e.split(".");e=o.shift();k=new RegExp("(^|\\.)"+c.map(o.slice(0).sort(),db).join("\\.(?:.*\\.)?")+"(\\.|$)")}if(r=C[e])if(d){n=c.event.special[e]||{};for(B=f||0;B<r.length;B++){u=r[B];if(d.guid===u.guid){if(i||k.test(u.namespace)){f==null&&r.splice(B--,1);n.remove&&n.remove.call(a,u)}if(f!=
+null)break}}if(r.length===0||f!=null&&r.length===1){if(!n.teardown||n.teardown.call(a,o)===false)Ca(a,e,z.handle);delete C[e]}}else for(var B=0;B<r.length;B++){u=r[B];if(i||k.test(u.namespace)){c.event.remove(a,n,u.handler,B);r.splice(B--,1)}}}if(c.isEmptyObject(C)){if(b=z.handle)b.elem=null;delete z.events;delete z.handle;c.isEmptyObject(z)&&c.removeData(a)}}}}},trigger:function(a,b,d,f){var e=a.type||a;if(!f){a=typeof a==="object"?a[G]?a:c.extend(c.Event(e),a):c.Event(e);if(e.indexOf("!")>=0){a.type=
+e=e.slice(0,-1);a.exclusive=true}if(!d){a.stopPropagation();c.event.global[e]&&c.each(c.cache,function(){this.events&&this.events[e]&&c.event.trigger(a,b,this.handle.elem)})}if(!d||d.nodeType===3||d.nodeType===8)return w;a.result=w;a.target=d;b=c.makeArray(b);b.unshift(a)}a.currentTarget=d;(f=c.data(d,"handle"))&&f.apply(d,b);f=d.parentNode||d.ownerDocument;try{if(!(d&&d.nodeName&&c.noData[d.nodeName.toLowerCase()]))if(d["on"+e]&&d["on"+e].apply(d,b)===false)a.result=false}catch(j){}if(!a.isPropagationStopped()&&
+f)c.event.trigger(a,b,f,true);else if(!a.isDefaultPrevented()){f=a.target;var i,o=c.nodeName(f,"a")&&e==="click",k=c.event.special[e]||{};if((!k._default||k._default.call(d,a)===false)&&!o&&!(f&&f.nodeName&&c.noData[f.nodeName.toLowerCase()])){try{if(f[e]){if(i=f["on"+e])f["on"+e]=null;c.event.triggered=true;f[e]()}}catch(n){}if(i)f["on"+e]=i;c.event.triggered=false}}},handle:function(a){var b,d,f,e;a=arguments[0]=c.event.fix(a||A.event);a.currentTarget=this;b=a.type.indexOf(".")<0&&!a.exclusive;
+if(!b){d=a.type.split(".");a.type=d.shift();f=new RegExp("(^|\\.)"+d.slice(0).sort().join("\\.(?:.*\\.)?")+"(\\.|$)")}e=c.data(this,"events");d=e[a.type];if(e&&d){d=d.slice(0);e=0;for(var j=d.length;e<j;e++){var i=d[e];if(b||f.test(i.namespace)){a.handler=i.handler;a.data=i.data;a.handleObj=i;i=i.handler.apply(this,arguments);if(i!==w){a.result=i;if(i===false){a.preventDefault();a.stopPropagation()}}if(a.isImmediatePropagationStopped())break}}}return a.result},props:"altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
+fix:function(a){if(a[G])return a;var b=a;a=c.Event(b);for(var d=this.props.length,f;d;){f=this.props[--d];a[f]=b[f]}if(!a.target)a.target=a.srcElement||s;if(a.target.nodeType===3)a.target=a.target.parentNode;if(!a.relatedTarget&&a.fromElement)a.relatedTarget=a.fromElement===a.target?a.toElement:a.fromElement;if(a.pageX==null&&a.clientX!=null){b=s.documentElement;d=s.body;a.pageX=a.clientX+(b&&b.scrollLeft||d&&d.scrollLeft||0)-(b&&b.clientLeft||d&&d.clientLeft||0);a.pageY=a.clientY+(b&&b.scrollTop||
+d&&d.scrollTop||0)-(b&&b.clientTop||d&&d.clientTop||0)}if(!a.which&&(a.charCode||a.charCode===0?a.charCode:a.keyCode))a.which=a.charCode||a.keyCode;if(!a.metaKey&&a.ctrlKey)a.metaKey=a.ctrlKey;if(!a.which&&a.button!==w)a.which=a.button&1?1:a.button&2?3:a.button&4?2:0;return a},guid:1E8,proxy:c.proxy,special:{ready:{setup:c.bindReady,teardown:c.noop},live:{add:function(a){c.event.add(this,a.origType,c.extend({},a,{handler:oa}))},remove:function(a){var b=true,d=a.origType.replace(O,"");c.each(c.data(this,
+"events").live||[],function(){if(d===this.origType.replace(O,""))return b=false});b&&c.event.remove(this,a.origType,oa)}},beforeunload:{setup:function(a,b,d){if(this.setInterval)this.onbeforeunload=d;return false},teardown:function(a,b){if(this.onbeforeunload===b)this.onbeforeunload=null}}}};var Ca=s.removeEventListener?function(a,b,d){a.removeEventListener(b,d,false)}:function(a,b,d){a.detachEvent("on"+b,d)};c.Event=function(a){if(!this.preventDefault)return new c.Event(a);if(a&&a.type){this.originalEvent=
+a;this.type=a.type}else this.type=a;this.timeStamp=J();this[G]=true};c.Event.prototype={preventDefault:function(){this.isDefaultPrevented=Z;var a=this.originalEvent;if(a){a.preventDefault&&a.preventDefault();a.returnValue=false}},stopPropagation:function(){this.isPropagationStopped=Z;var a=this.originalEvent;if(a){a.stopPropagation&&a.stopPropagation();a.cancelBubble=true}},stopImmediatePropagation:function(){this.isImmediatePropagationStopped=Z;this.stopPropagation()},isDefaultPrevented:Y,isPropagationStopped:Y,
+isImmediatePropagationStopped:Y};var Da=function(a){var b=a.relatedTarget;try{for(;b&&b!==this;)b=b.parentNode;if(b!==this){a.type=a.data;c.event.handle.apply(this,arguments)}}catch(d){}},Ea=function(a){a.type=a.data;c.event.handle.apply(this,arguments)};c.each({mouseenter:"mouseover",mouseleave:"mouseout"},function(a,b){c.event.special[a]={setup:function(d){c.event.add(this,b,d&&d.selector?Ea:Da,a)},teardown:function(d){c.event.remove(this,b,d&&d.selector?Ea:Da)}}});if(!c.support.submitBubbles)c.event.special.submit=
+{setup:function(){if(this.nodeName.toLowerCase()!=="form"){c.event.add(this,"click.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="submit"||d==="image")&&c(b).closest("form").length)return na("submit",this,arguments)});c.event.add(this,"keypress.specialSubmit",function(a){var b=a.target,d=b.type;if((d==="text"||d==="password")&&c(b).closest("form").length&&a.keyCode===13)return na("submit",this,arguments)})}else return false},teardown:function(){c.event.remove(this,".specialSubmit")}};
+if(!c.support.changeBubbles){var da=/textarea|input|select/i,ea,Fa=function(a){var b=a.type,d=a.value;if(b==="radio"||b==="checkbox")d=a.checked;else if(b==="select-multiple")d=a.selectedIndex>-1?c.map(a.options,function(f){return f.selected}).join("-"):"";else if(a.nodeName.toLowerCase()==="select")d=a.selectedIndex;return d},fa=function(a,b){var d=a.target,f,e;if(!(!da.test(d.nodeName)||d.readOnly)){f=c.data(d,"_change_data");e=Fa(d);if(a.type!=="focusout"||d.type!=="radio")c.data(d,"_change_data",
+e);if(!(f===w||e===f))if(f!=null||e){a.type="change";return c.event.trigger(a,b,d)}}};c.event.special.change={filters:{focusout:fa,click:function(a){var b=a.target,d=b.type;if(d==="radio"||d==="checkbox"||b.nodeName.toLowerCase()==="select")return fa.call(this,a)},keydown:function(a){var b=a.target,d=b.type;if(a.keyCode===13&&b.nodeName.toLowerCase()!=="textarea"||a.keyCode===32&&(d==="checkbox"||d==="radio")||d==="select-multiple")return fa.call(this,a)},beforeactivate:function(a){a=a.target;c.data(a,
+"_change_data",Fa(a))}},setup:function(){if(this.type==="file")return false;for(var a in ea)c.event.add(this,a+".specialChange",ea[a]);return da.test(this.nodeName)},teardown:function(){c.event.remove(this,".specialChange");return da.test(this.nodeName)}};ea=c.event.special.change.filters}s.addEventListener&&c.each({focus:"focusin",blur:"focusout"},function(a,b){function d(f){f=c.event.fix(f);f.type=b;return c.event.handle.call(this,f)}c.event.special[b]={setup:function(){this.addEventListener(a,
+d,true)},teardown:function(){this.removeEventListener(a,d,true)}}});c.each(["bind","one"],function(a,b){c.fn[b]=function(d,f,e){if(typeof d==="object"){for(var j in d)this[b](j,f,d[j],e);return this}if(c.isFunction(f)){e=f;f=w}var i=b==="one"?c.proxy(e,function(k){c(this).unbind(k,i);return e.apply(this,arguments)}):e;if(d==="unload"&&b!=="one")this.one(d,f,e);else{j=0;for(var o=this.length;j<o;j++)c.event.add(this[j],d,i,f)}return this}});c.fn.extend({unbind:function(a,b){if(typeof a==="object"&&
+!a.preventDefault)for(var d in a)this.unbind(d,a[d]);else{d=0;for(var f=this.length;d<f;d++)c.event.remove(this[d],a,b)}return this},delegate:function(a,b,d,f){return this.live(b,d,f,a)},undelegate:function(a,b,d){return arguments.length===0?this.unbind("live"):this.die(b,null,d,a)},trigger:function(a,b){return this.each(function(){c.event.trigger(a,b,this)})},triggerHandler:function(a,b){if(this[0]){a=c.Event(a);a.preventDefault();a.stopPropagation();c.event.trigger(a,b,this[0]);return a.result}},
+toggle:function(a){for(var b=arguments,d=1;d<b.length;)c.proxy(a,b[d++]);return this.click(c.proxy(a,function(f){var e=(c.data(this,"lastToggle"+a.guid)||0)%d;c.data(this,"lastToggle"+a.guid,e+1);f.preventDefault();return b[e].apply(this,arguments)||false}))},hover:function(a,b){return this.mouseenter(a).mouseleave(b||a)}});var Ga={focus:"focusin",blur:"focusout",mouseenter:"mouseover",mouseleave:"mouseout"};c.each(["live","die"],function(a,b){c.fn[b]=function(d,f,e,j){var i,o=0,k,n,r=j||this.selector,
+u=j?this:c(this.context);if(c.isFunction(f)){e=f;f=w}for(d=(d||"").split(" ");(i=d[o++])!=null;){j=O.exec(i);k="";if(j){k=j[0];i=i.replace(O,"")}if(i==="hover")d.push("mouseenter"+k,"mouseleave"+k);else{n=i;if(i==="focus"||i==="blur"){d.push(Ga[i]+k);i+=k}else i=(Ga[i]||i)+k;b==="live"?u.each(function(){c.event.add(this,pa(i,r),{data:f,selector:r,handler:e,origType:i,origHandler:e,preType:n})}):u.unbind(pa(i,r),e)}}return this}});c.each("blur focus focusin focusout load resize scroll unload click dblclick mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave change select submit keydown keypress keyup error".split(" "),
+function(a,b){c.fn[b]=function(d){return d?this.bind(b,d):this.trigger(b)};if(c.attrFn)c.attrFn[b]=true});A.attachEvent&&!A.addEventListener&&A.attachEvent("onunload",function(){for(var a in c.cache)if(c.cache[a].handle)try{c.event.remove(c.cache[a].handle.elem)}catch(b){}});(function(){function a(g){for(var h="",l,m=0;g[m];m++){l=g[m];if(l.nodeType===3||l.nodeType===4)h+=l.nodeValue;else if(l.nodeType!==8)h+=a(l.childNodes)}return h}function b(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];
+if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1&&!p){t.sizcache=l;t.sizset=q}if(t.nodeName.toLowerCase()===h){y=t;break}t=t[g]}m[q]=y}}}function d(g,h,l,m,q,p){q=0;for(var v=m.length;q<v;q++){var t=m[q];if(t){t=t[g];for(var y=false;t;){if(t.sizcache===l){y=m[t.sizset];break}if(t.nodeType===1){if(!p){t.sizcache=l;t.sizset=q}if(typeof h!=="string"){if(t===h){y=true;break}}else if(k.filter(h,[t]).length>0){y=t;break}}t=t[g]}m[q]=y}}}var f=/((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
+e=0,j=Object.prototype.toString,i=false,o=true;[0,0].sort(function(){o=false;return 0});var k=function(g,h,l,m){l=l||[];var q=h=h||s;if(h.nodeType!==1&&h.nodeType!==9)return[];if(!g||typeof g!=="string")return l;for(var p=[],v,t,y,S,H=true,M=x(h),I=g;(f.exec(""),v=f.exec(I))!==null;){I=v[3];p.push(v[1]);if(v[2]){S=v[3];break}}if(p.length>1&&r.exec(g))if(p.length===2&&n.relative[p[0]])t=ga(p[0]+p[1],h);else for(t=n.relative[p[0]]?[h]:k(p.shift(),h);p.length;){g=p.shift();if(n.relative[g])g+=p.shift();
+t=ga(g,t)}else{if(!m&&p.length>1&&h.nodeType===9&&!M&&n.match.ID.test(p[0])&&!n.match.ID.test(p[p.length-1])){v=k.find(p.shift(),h,M);h=v.expr?k.filter(v.expr,v.set)[0]:v.set[0]}if(h){v=m?{expr:p.pop(),set:z(m)}:k.find(p.pop(),p.length===1&&(p[0]==="~"||p[0]==="+")&&h.parentNode?h.parentNode:h,M);t=v.expr?k.filter(v.expr,v.set):v.set;if(p.length>0)y=z(t);else H=false;for(;p.length;){var D=p.pop();v=D;if(n.relative[D])v=p.pop();else D="";if(v==null)v=h;n.relative[D](y,v,M)}}else y=[]}y||(y=t);y||k.error(D||
+g);if(j.call(y)==="[object Array]")if(H)if(h&&h.nodeType===1)for(g=0;y[g]!=null;g++){if(y[g]&&(y[g]===true||y[g].nodeType===1&&E(h,y[g])))l.push(t[g])}else for(g=0;y[g]!=null;g++)y[g]&&y[g].nodeType===1&&l.push(t[g]);else l.push.apply(l,y);else z(y,l);if(S){k(S,q,l,m);k.uniqueSort(l)}return l};k.uniqueSort=function(g){if(B){i=o;g.sort(B);if(i)for(var h=1;h<g.length;h++)g[h]===g[h-1]&&g.splice(h--,1)}return g};k.matches=function(g,h){return k(g,null,null,h)};k.find=function(g,h,l){var m,q;if(!g)return[];
+for(var p=0,v=n.order.length;p<v;p++){var t=n.order[p];if(q=n.leftMatch[t].exec(g)){var y=q[1];q.splice(1,1);if(y.substr(y.length-1)!=="\\"){q[1]=(q[1]||"").replace(/\\/g,"");m=n.find[t](q,h,l);if(m!=null){g=g.replace(n.match[t],"");break}}}}m||(m=h.getElementsByTagName("*"));return{set:m,expr:g}};k.filter=function(g,h,l,m){for(var q=g,p=[],v=h,t,y,S=h&&h[0]&&x(h[0]);g&&h.length;){for(var H in n.filter)if((t=n.leftMatch[H].exec(g))!=null&&t[2]){var M=n.filter[H],I,D;D=t[1];y=false;t.splice(1,1);if(D.substr(D.length-
+1)!=="\\"){if(v===p)p=[];if(n.preFilter[H])if(t=n.preFilter[H](t,v,l,p,m,S)){if(t===true)continue}else y=I=true;if(t)for(var U=0;(D=v[U])!=null;U++)if(D){I=M(D,t,U,v);var Ha=m^!!I;if(l&&I!=null)if(Ha)y=true;else v[U]=false;else if(Ha){p.push(D);y=true}}if(I!==w){l||(v=p);g=g.replace(n.match[H],"");if(!y)return[];break}}}if(g===q)if(y==null)k.error(g);else break;q=g}return v};k.error=function(g){throw"Syntax error, unrecognized expression: "+g;};var n=k.selectors={order:["ID","NAME","TAG"],match:{ID:/#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
+CLASS:/\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,NAME:/\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,ATTR:/\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,TAG:/^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,CHILD:/:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,POS:/:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,PSEUDO:/:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/},leftMatch:{},attrMap:{"class":"className","for":"htmlFor"},attrHandle:{href:function(g){return g.getAttribute("href")}},
+relative:{"+":function(g,h){var l=typeof h==="string",m=l&&!/\W/.test(h);l=l&&!m;if(m)h=h.toLowerCase();m=0;for(var q=g.length,p;m<q;m++)if(p=g[m]){for(;(p=p.previousSibling)&&p.nodeType!==1;);g[m]=l||p&&p.nodeName.toLowerCase()===h?p||false:p===h}l&&k.filter(h,g,true)},">":function(g,h){var l=typeof h==="string";if(l&&!/\W/.test(h)){h=h.toLowerCase();for(var m=0,q=g.length;m<q;m++){var p=g[m];if(p){l=p.parentNode;g[m]=l.nodeName.toLowerCase()===h?l:false}}}else{m=0;for(q=g.length;m<q;m++)if(p=g[m])g[m]=
+l?p.parentNode:p.parentNode===h;l&&k.filter(h,g,true)}},"":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("parentNode",h,m,g,p,l)},"~":function(g,h,l){var m=e++,q=d;if(typeof h==="string"&&!/\W/.test(h)){var p=h=h.toLowerCase();q=b}q("previousSibling",h,m,g,p,l)}},find:{ID:function(g,h,l){if(typeof h.getElementById!=="undefined"&&!l)return(g=h.getElementById(g[1]))?[g]:[]},NAME:function(g,h){if(typeof h.getElementsByName!=="undefined"){var l=[];
+h=h.getElementsByName(g[1]);for(var m=0,q=h.length;m<q;m++)h[m].getAttribute("name")===g[1]&&l.push(h[m]);return l.length===0?null:l}},TAG:function(g,h){return h.getElementsByTagName(g[1])}},preFilter:{CLASS:function(g,h,l,m,q,p){g=" "+g[1].replace(/\\/g,"")+" ";if(p)return g;p=0;for(var v;(v=h[p])!=null;p++)if(v)if(q^(v.className&&(" "+v.className+" ").replace(/[\t\n]/g," ").indexOf(g)>=0))l||m.push(v);else if(l)h[p]=false;return false},ID:function(g){return g[1].replace(/\\/g,"")},TAG:function(g){return g[1].toLowerCase()},
+CHILD:function(g){if(g[1]==="nth"){var h=/(-?)(\d*)n((?:\+|-)?\d*)/.exec(g[2]==="even"&&"2n"||g[2]==="odd"&&"2n+1"||!/\D/.test(g[2])&&"0n+"+g[2]||g[2]);g[2]=h[1]+(h[2]||1)-0;g[3]=h[3]-0}g[0]=e++;return g},ATTR:function(g,h,l,m,q,p){h=g[1].replace(/\\/g,"");if(!p&&n.attrMap[h])g[1]=n.attrMap[h];if(g[2]==="~=")g[4]=" "+g[4]+" ";return g},PSEUDO:function(g,h,l,m,q){if(g[1]==="not")if((f.exec(g[3])||"").length>1||/^\w/.test(g[3]))g[3]=k(g[3],null,null,h);else{g=k.filter(g[3],h,l,true^q);l||m.push.apply(m,
+g);return false}else if(n.match.POS.test(g[0])||n.match.CHILD.test(g[0]))return true;return g},POS:function(g){g.unshift(true);return g}},filters:{enabled:function(g){return g.disabled===false&&g.type!=="hidden"},disabled:function(g){return g.disabled===true},checked:function(g){return g.checked===true},selected:function(g){return g.selected===true},parent:function(g){return!!g.firstChild},empty:function(g){return!g.firstChild},has:function(g,h,l){return!!k(l[3],g).length},header:function(g){return/h\d/i.test(g.nodeName)},
+text:function(g){return"text"===g.type},radio:function(g){return"radio"===g.type},checkbox:function(g){return"checkbox"===g.type},file:function(g){return"file"===g.type},password:function(g){return"password"===g.type},submit:function(g){return"submit"===g.type},image:function(g){return"image"===g.type},reset:function(g){return"reset"===g.type},button:function(g){return"button"===g.type||g.nodeName.toLowerCase()==="button"},input:function(g){return/input|select|textarea|button/i.test(g.nodeName)}},
+setFilters:{first:function(g,h){return h===0},last:function(g,h,l,m){return h===m.length-1},even:function(g,h){return h%2===0},odd:function(g,h){return h%2===1},lt:function(g,h,l){return h<l[3]-0},gt:function(g,h,l){return h>l[3]-0},nth:function(g,h,l){return l[3]-0===h},eq:function(g,h,l){return l[3]-0===h}},filter:{PSEUDO:function(g,h,l,m){var q=h[1],p=n.filters[q];if(p)return p(g,l,h,m);else if(q==="contains")return(g.textContent||g.innerText||a([g])||"").indexOf(h[3])>=0;else if(q==="not"){h=
+h[3];l=0;for(m=h.length;l<m;l++)if(h[l]===g)return false;return true}else k.error("Syntax error, unrecognized expression: "+q)},CHILD:function(g,h){var l=h[1],m=g;switch(l){case "only":case "first":for(;m=m.previousSibling;)if(m.nodeType===1)return false;if(l==="first")return true;m=g;case "last":for(;m=m.nextSibling;)if(m.nodeType===1)return false;return true;case "nth":l=h[2];var q=h[3];if(l===1&&q===0)return true;h=h[0];var p=g.parentNode;if(p&&(p.sizcache!==h||!g.nodeIndex)){var v=0;for(m=p.firstChild;m;m=
+m.nextSibling)if(m.nodeType===1)m.nodeIndex=++v;p.sizcache=h}g=g.nodeIndex-q;return l===0?g===0:g%l===0&&g/l>=0}},ID:function(g,h){return g.nodeType===1&&g.getAttribute("id")===h},TAG:function(g,h){return h==="*"&&g.nodeType===1||g.nodeName.toLowerCase()===h},CLASS:function(g,h){return(" "+(g.className||g.getAttribute("class"))+" ").indexOf(h)>-1},ATTR:function(g,h){var l=h[1];g=n.attrHandle[l]?n.attrHandle[l](g):g[l]!=null?g[l]:g.getAttribute(l);l=g+"";var m=h[2];h=h[4];return g==null?m==="!=":m===
+"="?l===h:m==="*="?l.indexOf(h)>=0:m==="~="?(" "+l+" ").indexOf(h)>=0:!h?l&&g!==false:m==="!="?l!==h:m==="^="?l.indexOf(h)===0:m==="$="?l.substr(l.length-h.length)===h:m==="|="?l===h||l.substr(0,h.length+1)===h+"-":false},POS:function(g,h,l,m){var q=n.setFilters[h[2]];if(q)return q(g,l,h,m)}}},r=n.match.POS;for(var u in n.match){n.match[u]=new RegExp(n.match[u].source+/(?![^\[]*\])(?![^\(]*\))/.source);n.leftMatch[u]=new RegExp(/(^(?:.|\r|\n)*?)/.source+n.match[u].source.replace(/\\(\d+)/g,function(g,
+h){return"\\"+(h-0+1)}))}var z=function(g,h){g=Array.prototype.slice.call(g,0);if(h){h.push.apply(h,g);return h}return g};try{Array.prototype.slice.call(s.documentElement.childNodes,0)}catch(C){z=function(g,h){h=h||[];if(j.call(g)==="[object Array]")Array.prototype.push.apply(h,g);else if(typeof g.length==="number")for(var l=0,m=g.length;l<m;l++)h.push(g[l]);else for(l=0;g[l];l++)h.push(g[l]);return h}}var B;if(s.documentElement.compareDocumentPosition)B=function(g,h){if(!g.compareDocumentPosition||
+!h.compareDocumentPosition){if(g==h)i=true;return g.compareDocumentPosition?-1:1}g=g.compareDocumentPosition(h)&4?-1:g===h?0:1;if(g===0)i=true;return g};else if("sourceIndex"in s.documentElement)B=function(g,h){if(!g.sourceIndex||!h.sourceIndex){if(g==h)i=true;return g.sourceIndex?-1:1}g=g.sourceIndex-h.sourceIndex;if(g===0)i=true;return g};else if(s.createRange)B=function(g,h){if(!g.ownerDocument||!h.ownerDocument){if(g==h)i=true;return g.ownerDocument?-1:1}var l=g.ownerDocument.createRange(),m=
+h.ownerDocument.createRange();l.setStart(g,0);l.setEnd(g,0);m.setStart(h,0);m.setEnd(h,0);g=l.compareBoundaryPoints(Range.START_TO_END,m);if(g===0)i=true;return g};(function(){var g=s.createElement("div"),h="script"+(new Date).getTime();g.innerHTML="<a name='"+h+"'/>";var l=s.documentElement;l.insertBefore(g,l.firstChild);if(s.getElementById(h)){n.find.ID=function(m,q,p){if(typeof q.getElementById!=="undefined"&&!p)return(q=q.getElementById(m[1]))?q.id===m[1]||typeof q.getAttributeNode!=="undefined"&&
+q.getAttributeNode("id").nodeValue===m[1]?[q]:w:[]};n.filter.ID=function(m,q){var p=typeof m.getAttributeNode!=="undefined"&&m.getAttributeNode("id");return m.nodeType===1&&p&&p.nodeValue===q}}l.removeChild(g);l=g=null})();(function(){var g=s.createElement("div");g.appendChild(s.createComment(""));if(g.getElementsByTagName("*").length>0)n.find.TAG=function(h,l){l=l.getElementsByTagName(h[1]);if(h[1]==="*"){h=[];for(var m=0;l[m];m++)l[m].nodeType===1&&h.push(l[m]);l=h}return l};g.innerHTML="<a href='#'></a>";
+if(g.firstChild&&typeof g.firstChild.getAttribute!=="undefined"&&g.firstChild.getAttribute("href")!=="#")n.attrHandle.href=function(h){return h.getAttribute("href",2)};g=null})();s.querySelectorAll&&function(){var g=k,h=s.createElement("div");h.innerHTML="<p class='TEST'></p>";if(!(h.querySelectorAll&&h.querySelectorAll(".TEST").length===0)){k=function(m,q,p,v){q=q||s;if(!v&&q.nodeType===9&&!x(q))try{return z(q.querySelectorAll(m),p)}catch(t){}return g(m,q,p,v)};for(var l in g)k[l]=g[l];h=null}}();
+(function(){var g=s.createElement("div");g.innerHTML="<div class='test e'></div><div class='test'></div>";if(!(!g.getElementsByClassName||g.getElementsByClassName("e").length===0)){g.lastChild.className="e";if(g.getElementsByClassName("e").length!==1){n.order.splice(1,0,"CLASS");n.find.CLASS=function(h,l,m){if(typeof l.getElementsByClassName!=="undefined"&&!m)return l.getElementsByClassName(h[1])};g=null}}})();var E=s.compareDocumentPosition?function(g,h){return!!(g.compareDocumentPosition(h)&16)}:
+function(g,h){return g!==h&&(g.contains?g.contains(h):true)},x=function(g){return(g=(g?g.ownerDocument||g:0).documentElement)?g.nodeName!=="HTML":false},ga=function(g,h){var l=[],m="",q;for(h=h.nodeType?[h]:h;q=n.match.PSEUDO.exec(g);){m+=q[0];g=g.replace(n.match.PSEUDO,"")}g=n.relative[g]?g+"*":g;q=0;for(var p=h.length;q<p;q++)k(g,h[q],l);return k.filter(m,l)};c.find=k;c.expr=k.selectors;c.expr[":"]=c.expr.filters;c.unique=k.uniqueSort;c.text=a;c.isXMLDoc=x;c.contains=E})();var eb=/Until$/,fb=/^(?:parents|prevUntil|prevAll)/,
+gb=/,/;R=Array.prototype.slice;var Ia=function(a,b,d){if(c.isFunction(b))return c.grep(a,function(e,j){return!!b.call(e,j,e)===d});else if(b.nodeType)return c.grep(a,function(e){return e===b===d});else if(typeof b==="string"){var f=c.grep(a,function(e){return e.nodeType===1});if(Ua.test(b))return c.filter(b,f,!d);else b=c.filter(b,f)}return c.grep(a,function(e){return c.inArray(e,b)>=0===d})};c.fn.extend({find:function(a){for(var b=this.pushStack("","find",a),d=0,f=0,e=this.length;f<e;f++){d=b.length;
+c.find(a,this[f],b);if(f>0)for(var j=d;j<b.length;j++)for(var i=0;i<d;i++)if(b[i]===b[j]){b.splice(j--,1);break}}return b},has:function(a){var b=c(a);return this.filter(function(){for(var d=0,f=b.length;d<f;d++)if(c.contains(this,b[d]))return true})},not:function(a){return this.pushStack(Ia(this,a,false),"not",a)},filter:function(a){return this.pushStack(Ia(this,a,true),"filter",a)},is:function(a){return!!a&&c.filter(a,this).length>0},closest:function(a,b){if(c.isArray(a)){var d=[],f=this[0],e,j=
+{},i;if(f&&a.length){e=0;for(var o=a.length;e<o;e++){i=a[e];j[i]||(j[i]=c.expr.match.POS.test(i)?c(i,b||this.context):i)}for(;f&&f.ownerDocument&&f!==b;){for(i in j){e=j[i];if(e.jquery?e.index(f)>-1:c(f).is(e)){d.push({selector:i,elem:f});delete j[i]}}f=f.parentNode}}return d}var k=c.expr.match.POS.test(a)?c(a,b||this.context):null;return this.map(function(n,r){for(;r&&r.ownerDocument&&r!==b;){if(k?k.index(r)>-1:c(r).is(a))return r;r=r.parentNode}return null})},index:function(a){if(!a||typeof a===
+"string")return c.inArray(this[0],a?c(a):this.parent().children());return c.inArray(a.jquery?a[0]:a,this)},add:function(a,b){a=typeof a==="string"?c(a,b||this.context):c.makeArray(a);b=c.merge(this.get(),a);return this.pushStack(qa(a[0])||qa(b[0])?b:c.unique(b))},andSelf:function(){return this.add(this.prevObject)}});c.each({parent:function(a){return(a=a.parentNode)&&a.nodeType!==11?a:null},parents:function(a){return c.dir(a,"parentNode")},parentsUntil:function(a,b,d){return c.dir(a,"parentNode",
+d)},next:function(a){return c.nth(a,2,"nextSibling")},prev:function(a){return c.nth(a,2,"previousSibling")},nextAll:function(a){return c.dir(a,"nextSibling")},prevAll:function(a){return c.dir(a,"previousSibling")},nextUntil:function(a,b,d){return c.dir(a,"nextSibling",d)},prevUntil:function(a,b,d){return c.dir(a,"previousSibling",d)},siblings:function(a){return c.sibling(a.parentNode.firstChild,a)},children:function(a){return c.sibling(a.firstChild)},contents:function(a){return c.nodeName(a,"iframe")?
+a.contentDocument||a.contentWindow.document:c.makeArray(a.childNodes)}},function(a,b){c.fn[a]=function(d,f){var e=c.map(this,b,d);eb.test(a)||(f=d);if(f&&typeof f==="string")e=c.filter(f,e);e=this.length>1?c.unique(e):e;if((this.length>1||gb.test(f))&&fb.test(a))e=e.reverse();return this.pushStack(e,a,R.call(arguments).join(","))}});c.extend({filter:function(a,b,d){if(d)a=":not("+a+")";return c.find.matches(a,b)},dir:function(a,b,d){var f=[];for(a=a[b];a&&a.nodeType!==9&&(d===w||a.nodeType!==1||!c(a).is(d));){a.nodeType===
+1&&f.push(a);a=a[b]}return f},nth:function(a,b,d){b=b||1;for(var f=0;a;a=a[d])if(a.nodeType===1&&++f===b)break;return a},sibling:function(a,b){for(var d=[];a;a=a.nextSibling)a.nodeType===1&&a!==b&&d.push(a);return d}});var Ja=/ jQuery\d+="(?:\d+|null)"/g,V=/^\s+/,Ka=/(<([\w:]+)[^>]*?)\/>/g,hb=/^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,La=/<([\w:]+)/,ib=/<tbody/i,jb=/<|&#?\w+;/,ta=/<script|<object|<embed|<option|<style/i,ua=/checked\s*(?:[^=]|=\s*.checked.)/i,Ma=function(a,b,d){return hb.test(d)?
+a:b+"></"+d+">"},F={option:[1,"<select multiple='multiple'>","</select>"],legend:[1,"<fieldset>","</fieldset>"],thead:[1,"<table>","</table>"],tr:[2,"<table><tbody>","</tbody></table>"],td:[3,"<table><tbody><tr>","</tr></tbody></table>"],col:[2,"<table><tbody></tbody><colgroup>","</colgroup></table>"],area:[1,"<map>","</map>"],_default:[0,"",""]};F.optgroup=F.option;F.tbody=F.tfoot=F.colgroup=F.caption=F.thead;F.th=F.td;if(!c.support.htmlSerialize)F._default=[1,"div<div>","</div>"];c.fn.extend({text:function(a){if(c.isFunction(a))return this.each(function(b){var d=
+c(this);d.text(a.call(this,b,d.text()))});if(typeof a!=="object"&&a!==w)return this.empty().append((this[0]&&this[0].ownerDocument||s).createTextNode(a));return c.text(this)},wrapAll:function(a){if(c.isFunction(a))return this.each(function(d){c(this).wrapAll(a.call(this,d))});if(this[0]){var b=c(a,this[0].ownerDocument).eq(0).clone(true);this[0].parentNode&&b.insertBefore(this[0]);b.map(function(){for(var d=this;d.firstChild&&d.firstChild.nodeType===1;)d=d.firstChild;return d}).append(this)}return this},
+wrapInner:function(a){if(c.isFunction(a))return this.each(function(b){c(this).wrapInner(a.call(this,b))});return this.each(function(){var b=c(this),d=b.contents();d.length?d.wrapAll(a):b.append(a)})},wrap:function(a){return this.each(function(){c(this).wrapAll(a)})},unwrap:function(){return this.parent().each(function(){c.nodeName(this,"body")||c(this).replaceWith(this.childNodes)}).end()},append:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.appendChild(a)})},
+prepend:function(){return this.domManip(arguments,true,function(a){this.nodeType===1&&this.insertBefore(a,this.firstChild)})},before:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,this)});else if(arguments.length){var a=c(arguments[0]);a.push.apply(a,this.toArray());return this.pushStack(a,"before",arguments)}},after:function(){if(this[0]&&this[0].parentNode)return this.domManip(arguments,false,function(b){this.parentNode.insertBefore(b,
+this.nextSibling)});else if(arguments.length){var a=this.pushStack(this,"after",arguments);a.push.apply(a,c(arguments[0]).toArray());return a}},remove:function(a,b){for(var d=0,f;(f=this[d])!=null;d++)if(!a||c.filter(a,[f]).length){if(!b&&f.nodeType===1){c.cleanData(f.getElementsByTagName("*"));c.cleanData([f])}f.parentNode&&f.parentNode.removeChild(f)}return this},empty:function(){for(var a=0,b;(b=this[a])!=null;a++)for(b.nodeType===1&&c.cleanData(b.getElementsByTagName("*"));b.firstChild;)b.removeChild(b.firstChild);
+return this},clone:function(a){var b=this.map(function(){if(!c.support.noCloneEvent&&!c.isXMLDoc(this)){var d=this.outerHTML,f=this.ownerDocument;if(!d){d=f.createElement("div");d.appendChild(this.cloneNode(true));d=d.innerHTML}return c.clean([d.replace(Ja,"").replace(/=([^="'>\s]+\/)>/g,'="$1">').replace(V,"")],f)[0]}else return this.cloneNode(true)});if(a===true){ra(this,b);ra(this.find("*"),b.find("*"))}return b},html:function(a){if(a===w)return this[0]&&this[0].nodeType===1?this[0].innerHTML.replace(Ja,
+""):null;else if(typeof a==="string"&&!ta.test(a)&&(c.support.leadingWhitespace||!V.test(a))&&!F[(La.exec(a)||["",""])[1].toLowerCase()]){a=a.replace(Ka,Ma);try{for(var b=0,d=this.length;b<d;b++)if(this[b].nodeType===1){c.cleanData(this[b].getElementsByTagName("*"));this[b].innerHTML=a}}catch(f){this.empty().append(a)}}else c.isFunction(a)?this.each(function(e){var j=c(this),i=j.html();j.empty().append(function(){return a.call(this,e,i)})}):this.empty().append(a);return this},replaceWith:function(a){if(this[0]&&
+this[0].parentNode){if(c.isFunction(a))return this.each(function(b){var d=c(this),f=d.html();d.replaceWith(a.call(this,b,f))});if(typeof a!=="string")a=c(a).detach();return this.each(function(){var b=this.nextSibling,d=this.parentNode;c(this).remove();b?c(b).before(a):c(d).append(a)})}else return this.pushStack(c(c.isFunction(a)?a():a),"replaceWith",a)},detach:function(a){return this.remove(a,true)},domManip:function(a,b,d){function f(u){return c.nodeName(u,"table")?u.getElementsByTagName("tbody")[0]||
+u.appendChild(u.ownerDocument.createElement("tbody")):u}var e,j,i=a[0],o=[],k;if(!c.support.checkClone&&arguments.length===3&&typeof i==="string"&&ua.test(i))return this.each(function(){c(this).domManip(a,b,d,true)});if(c.isFunction(i))return this.each(function(u){var z=c(this);a[0]=i.call(this,u,b?z.html():w);z.domManip(a,b,d)});if(this[0]){e=i&&i.parentNode;e=c.support.parentNode&&e&&e.nodeType===11&&e.childNodes.length===this.length?{fragment:e}:sa(a,this,o);k=e.fragment;if(j=k.childNodes.length===
+1?(k=k.firstChild):k.firstChild){b=b&&c.nodeName(j,"tr");for(var n=0,r=this.length;n<r;n++)d.call(b?f(this[n],j):this[n],n>0||e.cacheable||this.length>1?k.cloneNode(true):k)}o.length&&c.each(o,Qa)}return this}});c.fragments={};c.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){c.fn[a]=function(d){var f=[];d=c(d);var e=this.length===1&&this[0].parentNode;if(e&&e.nodeType===11&&e.childNodes.length===1&&d.length===1){d[b](this[0]);
+return this}else{e=0;for(var j=d.length;e<j;e++){var i=(e>0?this.clone(true):this).get();c.fn[b].apply(c(d[e]),i);f=f.concat(i)}return this.pushStack(f,a,d.selector)}}});c.extend({clean:function(a,b,d,f){b=b||s;if(typeof b.createElement==="undefined")b=b.ownerDocument||b[0]&&b[0].ownerDocument||s;for(var e=[],j=0,i;(i=a[j])!=null;j++){if(typeof i==="number")i+="";if(i){if(typeof i==="string"&&!jb.test(i))i=b.createTextNode(i);else if(typeof i==="string"){i=i.replace(Ka,Ma);var o=(La.exec(i)||["",
+""])[1].toLowerCase(),k=F[o]||F._default,n=k[0],r=b.createElement("div");for(r.innerHTML=k[1]+i+k[2];n--;)r=r.lastChild;if(!c.support.tbody){n=ib.test(i);o=o==="table"&&!n?r.firstChild&&r.firstChild.childNodes:k[1]==="<table>"&&!n?r.childNodes:[];for(k=o.length-1;k>=0;--k)c.nodeName(o[k],"tbody")&&!o[k].childNodes.length&&o[k].parentNode.removeChild(o[k])}!c.support.leadingWhitespace&&V.test(i)&&r.insertBefore(b.createTextNode(V.exec(i)[0]),r.firstChild);i=r.childNodes}if(i.nodeType)e.push(i);else e=
+c.merge(e,i)}}if(d)for(j=0;e[j];j++)if(f&&c.nodeName(e[j],"script")&&(!e[j].type||e[j].type.toLowerCase()==="text/javascript"))f.push(e[j].parentNode?e[j].parentNode.removeChild(e[j]):e[j]);else{e[j].nodeType===1&&e.splice.apply(e,[j+1,0].concat(c.makeArray(e[j].getElementsByTagName("script"))));d.appendChild(e[j])}return e},cleanData:function(a){for(var b,d,f=c.cache,e=c.event.special,j=c.support.deleteExpando,i=0,o;(o=a[i])!=null;i++)if(d=o[c.expando]){b=f[d];if(b.events)for(var k in b.events)e[k]?
+c.event.remove(o,k):Ca(o,k,b.handle);if(j)delete o[c.expando];else o.removeAttribute&&o.removeAttribute(c.expando);delete f[d]}}});var kb=/z-?index|font-?weight|opacity|zoom|line-?height/i,Na=/alpha\([^)]*\)/,Oa=/opacity=([^)]*)/,ha=/float/i,ia=/-([a-z])/ig,lb=/([A-Z])/g,mb=/^-?\d+(?:px)?$/i,nb=/^-?\d/,ob={position:"absolute",visibility:"hidden",display:"block"},pb=["Left","Right"],qb=["Top","Bottom"],rb=s.defaultView&&s.defaultView.getComputedStyle,Pa=c.support.cssFloat?"cssFloat":"styleFloat",ja=
+function(a,b){return b.toUpperCase()};c.fn.css=function(a,b){return X(this,a,b,true,function(d,f,e){if(e===w)return c.curCSS(d,f);if(typeof e==="number"&&!kb.test(f))e+="px";c.style(d,f,e)})};c.extend({style:function(a,b,d){if(!a||a.nodeType===3||a.nodeType===8)return w;if((b==="width"||b==="height")&&parseFloat(d)<0)d=w;var f=a.style||a,e=d!==w;if(!c.support.opacity&&b==="opacity"){if(e){f.zoom=1;b=parseInt(d,10)+""==="NaN"?"":"alpha(opacity="+d*100+")";a=f.filter||c.curCSS(a,"filter")||"";f.filter=
+Na.test(a)?a.replace(Na,b):b}return f.filter&&f.filter.indexOf("opacity=")>=0?parseFloat(Oa.exec(f.filter)[1])/100+"":""}if(ha.test(b))b=Pa;b=b.replace(ia,ja);if(e)f[b]=d;return f[b]},css:function(a,b,d,f){if(b==="width"||b==="height"){var e,j=b==="width"?pb:qb;function i(){e=b==="width"?a.offsetWidth:a.offsetHeight;f!=="border"&&c.each(j,function(){f||(e-=parseFloat(c.curCSS(a,"padding"+this,true))||0);if(f==="margin")e+=parseFloat(c.curCSS(a,"margin"+this,true))||0;else e-=parseFloat(c.curCSS(a,
+"border"+this+"Width",true))||0})}a.offsetWidth!==0?i():c.swap(a,ob,i);return Math.max(0,Math.round(e))}return c.curCSS(a,b,d)},curCSS:function(a,b,d){var f,e=a.style;if(!c.support.opacity&&b==="opacity"&&a.currentStyle){f=Oa.test(a.currentStyle.filter||"")?parseFloat(RegExp.$1)/100+"":"";return f===""?"1":f}if(ha.test(b))b=Pa;if(!d&&e&&e[b])f=e[b];else if(rb){if(ha.test(b))b="float";b=b.replace(lb,"-$1").toLowerCase();e=a.ownerDocument.defaultView;if(!e)return null;if(a=e.getComputedStyle(a,null))f=
+a.getPropertyValue(b);if(b==="opacity"&&f==="")f="1"}else if(a.currentStyle){d=b.replace(ia,ja);f=a.currentStyle[b]||a.currentStyle[d];if(!mb.test(f)&&nb.test(f)){b=e.left;var j=a.runtimeStyle.left;a.runtimeStyle.left=a.currentStyle.left;e.left=d==="fontSize"?"1em":f||0;f=e.pixelLeft+"px";e.left=b;a.runtimeStyle.left=j}}return f},swap:function(a,b,d){var f={};for(var e in b){f[e]=a.style[e];a.style[e]=b[e]}d.call(a);for(e in b)a.style[e]=f[e]}});if(c.expr&&c.expr.filters){c.expr.filters.hidden=function(a){var b=
+a.offsetWidth,d=a.offsetHeight,f=a.nodeName.toLowerCase()==="tr";return b===0&&d===0&&!f?true:b>0&&d>0&&!f?false:c.curCSS(a,"display")==="none"};c.expr.filters.visible=function(a){return!c.expr.filters.hidden(a)}}var sb=J(),tb=/<script(.|\s)*?\/script>/gi,ub=/select|textarea/i,vb=/color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,N=/=\?(&|$)/,ka=/\?/,wb=/(\?|&)_=.*?(&|$)/,xb=/^(\w+:)?\/\/([^\/?#]+)/,yb=/%20/g,zb=c.fn.load;c.fn.extend({load:function(a,b,d){if(typeof a!==
+"string")return zb.call(this,a);else if(!this.length)return this;var f=a.indexOf(" ");if(f>=0){var e=a.slice(f,a.length);a=a.slice(0,f)}f="GET";if(b)if(c.isFunction(b)){d=b;b=null}else if(typeof b==="object"){b=c.param(b,c.ajaxSettings.traditional);f="POST"}var j=this;c.ajax({url:a,type:f,dataType:"html",data:b,complete:function(i,o){if(o==="success"||o==="notmodified")j.html(e?c("<div />").append(i.responseText.replace(tb,"")).find(e):i.responseText);d&&j.each(d,[i.responseText,o,i])}});return this},
+serialize:function(){return c.param(this.serializeArray())},serializeArray:function(){return this.map(function(){return this.elements?c.makeArray(this.elements):this}).filter(function(){return this.name&&!this.disabled&&(this.checked||ub.test(this.nodeName)||vb.test(this.type))}).map(function(a,b){a=c(this).val();return a==null?null:c.isArray(a)?c.map(a,function(d){return{name:b.name,value:d}}):{name:b.name,value:a}}).get()}});c.each("ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "),
+function(a,b){c.fn[b]=function(d){return this.bind(b,d)}});c.extend({get:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b=null}return c.ajax({type:"GET",url:a,data:b,success:d,dataType:f})},getScript:function(a,b){return c.get(a,null,b,"script")},getJSON:function(a,b,d){return c.get(a,b,d,"json")},post:function(a,b,d,f){if(c.isFunction(b)){f=f||d;d=b;b={}}return c.ajax({type:"POST",url:a,data:b,success:d,dataType:f})},ajaxSetup:function(a){c.extend(c.ajaxSettings,a)},ajaxSettings:{url:location.href,
+global:true,type:"GET",contentType:"application/x-www-form-urlencoded",processData:true,async:true,xhr:A.XMLHttpRequest&&(A.location.protocol!=="file:"||!A.ActiveXObject)?function(){return new A.XMLHttpRequest}:function(){try{return new A.ActiveXObject("Microsoft.XMLHTTP")}catch(a){}},accepts:{xml:"application/xml, text/xml",html:"text/html",script:"text/javascript, application/javascript",json:"application/json, text/javascript",text:"text/plain",_default:"*/*"}},lastModified:{},etag:{},ajax:function(a){function b(){e.success&&
+e.success.call(k,o,i,x);e.global&&f("ajaxSuccess",[x,e])}function d(){e.complete&&e.complete.call(k,x,i);e.global&&f("ajaxComplete",[x,e]);e.global&&!--c.active&&c.event.trigger("ajaxStop")}function f(q,p){(e.context?c(e.context):c.event).trigger(q,p)}var e=c.extend(true,{},c.ajaxSettings,a),j,i,o,k=a&&a.context||e,n=e.type.toUpperCase();if(e.data&&e.processData&&typeof e.data!=="string")e.data=c.param(e.data,e.traditional);if(e.dataType==="jsonp"){if(n==="GET")N.test(e.url)||(e.url+=(ka.test(e.url)?
+"&":"?")+(e.jsonp||"callback")+"=?");else if(!e.data||!N.test(e.data))e.data=(e.data?e.data+"&":"")+(e.jsonp||"callback")+"=?";e.dataType="json"}if(e.dataType==="json"&&(e.data&&N.test(e.data)||N.test(e.url))){j=e.jsonpCallback||"jsonp"+sb++;if(e.data)e.data=(e.data+"").replace(N,"="+j+"$1");e.url=e.url.replace(N,"="+j+"$1");e.dataType="script";A[j]=A[j]||function(q){o=q;b();d();A[j]=w;try{delete A[j]}catch(p){}z&&z.removeChild(C)}}if(e.dataType==="script"&&e.cache===null)e.cache=false;if(e.cache===
+false&&n==="GET"){var r=J(),u=e.url.replace(wb,"$1_="+r+"$2");e.url=u+(u===e.url?(ka.test(e.url)?"&":"?")+"_="+r:"")}if(e.data&&n==="GET")e.url+=(ka.test(e.url)?"&":"?")+e.data;e.global&&!c.active++&&c.event.trigger("ajaxStart");r=(r=xb.exec(e.url))&&(r[1]&&r[1]!==location.protocol||r[2]!==location.host);if(e.dataType==="script"&&n==="GET"&&r){var z=s.getElementsByTagName("head")[0]||s.documentElement,C=s.createElement("script");C.src=e.url;if(e.scriptCharset)C.charset=e.scriptCharset;if(!j){var B=
+false;C.onload=C.onreadystatechange=function(){if(!B&&(!this.readyState||this.readyState==="loaded"||this.readyState==="complete")){B=true;b();d();C.onload=C.onreadystatechange=null;z&&C.parentNode&&z.removeChild(C)}}}z.insertBefore(C,z.firstChild);return w}var E=false,x=e.xhr();if(x){e.username?x.open(n,e.url,e.async,e.username,e.password):x.open(n,e.url,e.async);try{if(e.data||a&&a.contentType)x.setRequestHeader("Content-Type",e.contentType);if(e.ifModified){c.lastModified[e.url]&&x.setRequestHeader("If-Modified-Since",
+c.lastModified[e.url]);c.etag[e.url]&&x.setRequestHeader("If-None-Match",c.etag[e.url])}r||x.setRequestHeader("X-Requested-With","XMLHttpRequest");x.setRequestHeader("Accept",e.dataType&&e.accepts[e.dataType]?e.accepts[e.dataType]+", */*":e.accepts._default)}catch(ga){}if(e.beforeSend&&e.beforeSend.call(k,x,e)===false){e.global&&!--c.active&&c.event.trigger("ajaxStop");x.abort();return false}e.global&&f("ajaxSend",[x,e]);var g=x.onreadystatechange=function(q){if(!x||x.readyState===0||q==="abort"){E||
+d();E=true;if(x)x.onreadystatechange=c.noop}else if(!E&&x&&(x.readyState===4||q==="timeout")){E=true;x.onreadystatechange=c.noop;i=q==="timeout"?"timeout":!c.httpSuccess(x)?"error":e.ifModified&&c.httpNotModified(x,e.url)?"notmodified":"success";var p;if(i==="success")try{o=c.httpData(x,e.dataType,e)}catch(v){i="parsererror";p=v}if(i==="success"||i==="notmodified")j||b();else c.handleError(e,x,i,p);d();q==="timeout"&&x.abort();if(e.async)x=null}};try{var h=x.abort;x.abort=function(){x&&h.call(x);
+g("abort")}}catch(l){}e.async&&e.timeout>0&&setTimeout(function(){x&&!E&&g("timeout")},e.timeout);try{x.send(n==="POST"||n==="PUT"||n==="DELETE"?e.data:null)}catch(m){c.handleError(e,x,null,m);d()}e.async||g();return x}},handleError:function(a,b,d,f){if(a.error)a.error.call(a.context||a,b,d,f);if(a.global)(a.context?c(a.context):c.event).trigger("ajaxError",[b,a,f])},active:0,httpSuccess:function(a){try{return!a.status&&location.protocol==="file:"||a.status>=200&&a.status<300||a.status===304||a.status===
+1223||a.status===0}catch(b){}return false},httpNotModified:function(a,b){var d=a.getResponseHeader("Last-Modified"),f=a.getResponseHeader("Etag");if(d)c.lastModified[b]=d;if(f)c.etag[b]=f;return a.status===304||a.status===0},httpData:function(a,b,d){var f=a.getResponseHeader("content-type")||"",e=b==="xml"||!b&&f.indexOf("xml")>=0;a=e?a.responseXML:a.responseText;e&&a.documentElement.nodeName==="parsererror"&&c.error("parsererror");if(d&&d.dataFilter)a=d.dataFilter(a,b);if(typeof a==="string")if(b===
+"json"||!b&&f.indexOf("json")>=0)a=c.parseJSON(a);else if(b==="script"||!b&&f.indexOf("javascript")>=0)c.globalEval(a);return a},param:function(a,b){function d(i,o){if(c.isArray(o))c.each(o,function(k,n){b||/\[\]$/.test(i)?f(i,n):d(i+"["+(typeof n==="object"||c.isArray(n)?k:"")+"]",n)});else!b&&o!=null&&typeof o==="object"?c.each(o,function(k,n){d(i+"["+k+"]",n)}):f(i,o)}function f(i,o){o=c.isFunction(o)?o():o;e[e.length]=encodeURIComponent(i)+"="+encodeURIComponent(o)}var e=[];if(b===w)b=c.ajaxSettings.traditional;
+if(c.isArray(a)||a.jquery)c.each(a,function(){f(this.name,this.value)});else for(var j in a)d(j,a[j]);return e.join("&").replace(yb,"+")}});var la={},Ab=/toggle|show|hide/,Bb=/^([+-]=)?([\d+-.]+)(.*)$/,W,va=[["height","marginTop","marginBottom","paddingTop","paddingBottom"],["width","marginLeft","marginRight","paddingLeft","paddingRight"],["opacity"]];c.fn.extend({show:function(a,b){if(a||a===0)return this.animate(K("show",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");
+this[a].style.display=d||"";if(c.css(this[a],"display")==="none"){d=this[a].nodeName;var f;if(la[d])f=la[d];else{var e=c("<"+d+" />").appendTo("body");f=e.css("display");if(f==="none")f="block";e.remove();la[d]=f}c.data(this[a],"olddisplay",f)}}a=0;for(b=this.length;a<b;a++)this[a].style.display=c.data(this[a],"olddisplay")||"";return this}},hide:function(a,b){if(a||a===0)return this.animate(K("hide",3),a,b);else{a=0;for(b=this.length;a<b;a++){var d=c.data(this[a],"olddisplay");!d&&d!=="none"&&c.data(this[a],
+"olddisplay",c.css(this[a],"display"))}a=0;for(b=this.length;a<b;a++)this[a].style.display="none";return this}},_toggle:c.fn.toggle,toggle:function(a,b){var d=typeof a==="boolean";if(c.isFunction(a)&&c.isFunction(b))this._toggle.apply(this,arguments);else a==null||d?this.each(function(){var f=d?a:c(this).is(":hidden");c(this)[f?"show":"hide"]()}):this.animate(K("toggle",3),a,b);return this},fadeTo:function(a,b,d){return this.filter(":hidden").css("opacity",0).show().end().animate({opacity:b},a,d)},
+animate:function(a,b,d,f){var e=c.speed(b,d,f);if(c.isEmptyObject(a))return this.each(e.complete);return this[e.queue===false?"each":"queue"](function(){var j=c.extend({},e),i,o=this.nodeType===1&&c(this).is(":hidden"),k=this;for(i in a){var n=i.replace(ia,ja);if(i!==n){a[n]=a[i];delete a[i];i=n}if(a[i]==="hide"&&o||a[i]==="show"&&!o)return j.complete.call(this);if((i==="height"||i==="width")&&this.style){j.display=c.css(this,"display");j.overflow=this.style.overflow}if(c.isArray(a[i])){(j.specialEasing=
+j.specialEasing||{})[i]=a[i][1];a[i]=a[i][0]}}if(j.overflow!=null)this.style.overflow="hidden";j.curAnim=c.extend({},a);c.each(a,function(r,u){var z=new c.fx(k,j,r);if(Ab.test(u))z[u==="toggle"?o?"show":"hide":u](a);else{var C=Bb.exec(u),B=z.cur(true)||0;if(C){u=parseFloat(C[2]);var E=C[3]||"px";if(E!=="px"){k.style[r]=(u||1)+E;B=(u||1)/z.cur(true)*B;k.style[r]=B+E}if(C[1])u=(C[1]==="-="?-1:1)*u+B;z.custom(B,u,E)}else z.custom(B,u,"")}});return true})},stop:function(a,b){var d=c.timers;a&&this.queue([]);
+this.each(function(){for(var f=d.length-1;f>=0;f--)if(d[f].elem===this){b&&d[f](true);d.splice(f,1)}});b||this.dequeue();return this}});c.each({slideDown:K("show",1),slideUp:K("hide",1),slideToggle:K("toggle",1),fadeIn:{opacity:"show"},fadeOut:{opacity:"hide"}},function(a,b){c.fn[a]=function(d,f){return this.animate(b,d,f)}});c.extend({speed:function(a,b,d){var f=a&&typeof a==="object"?a:{complete:d||!d&&b||c.isFunction(a)&&a,duration:a,easing:d&&b||b&&!c.isFunction(b)&&b};f.duration=c.fx.off?0:typeof f.duration===
+"number"?f.duration:c.fx.speeds[f.duration]||c.fx.speeds._default;f.old=f.complete;f.complete=function(){f.queue!==false&&c(this).dequeue();c.isFunction(f.old)&&f.old.call(this)};return f},easing:{linear:function(a,b,d,f){return d+f*a},swing:function(a,b,d,f){return(-Math.cos(a*Math.PI)/2+0.5)*f+d}},timers:[],fx:function(a,b,d){this.options=b;this.elem=a;this.prop=d;if(!b.orig)b.orig={}}});c.fx.prototype={update:function(){this.options.step&&this.options.step.call(this.elem,this.now,this);(c.fx.step[this.prop]||
+c.fx.step._default)(this);if((this.prop==="height"||this.prop==="width")&&this.elem.style)this.elem.style.display="block"},cur:function(a){if(this.elem[this.prop]!=null&&(!this.elem.style||this.elem.style[this.prop]==null))return this.elem[this.prop];return(a=parseFloat(c.css(this.elem,this.prop,a)))&&a>-10000?a:parseFloat(c.curCSS(this.elem,this.prop))||0},custom:function(a,b,d){function f(j){return e.step(j)}this.startTime=J();this.start=a;this.end=b;this.unit=d||this.unit||"px";this.now=this.start;
+this.pos=this.state=0;var e=this;f.elem=this.elem;if(f()&&c.timers.push(f)&&!W)W=setInterval(c.fx.tick,13)},show:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.show=true;this.custom(this.prop==="width"||this.prop==="height"?1:0,this.cur());c(this.elem).show()},hide:function(){this.options.orig[this.prop]=c.style(this.elem,this.prop);this.options.hide=true;this.custom(this.cur(),0)},step:function(a){var b=J(),d=true;if(a||b>=this.options.duration+this.startTime){this.now=
+this.end;this.pos=this.state=1;this.update();this.options.curAnim[this.prop]=true;for(var f in this.options.curAnim)if(this.options.curAnim[f]!==true)d=false;if(d){if(this.options.display!=null){this.elem.style.overflow=this.options.overflow;a=c.data(this.elem,"olddisplay");this.elem.style.display=a?a:this.options.display;if(c.css(this.elem,"display")==="none")this.elem.style.display="block"}this.options.hide&&c(this.elem).hide();if(this.options.hide||this.options.show)for(var e in this.options.curAnim)c.style(this.elem,
+e,this.options.orig[e]);this.options.complete.call(this.elem)}return false}else{e=b-this.startTime;this.state=e/this.options.duration;a=this.options.easing||(c.easing.swing?"swing":"linear");this.pos=c.easing[this.options.specialEasing&&this.options.specialEasing[this.prop]||a](this.state,e,0,1,this.options.duration);this.now=this.start+(this.end-this.start)*this.pos;this.update()}return true}};c.extend(c.fx,{tick:function(){for(var a=c.timers,b=0;b<a.length;b++)a[b]()||a.splice(b--,1);a.length||
+c.fx.stop()},stop:function(){clearInterval(W);W=null},speeds:{slow:600,fast:200,_default:400},step:{opacity:function(a){c.style(a.elem,"opacity",a.now)},_default:function(a){if(a.elem.style&&a.elem.style[a.prop]!=null)a.elem.style[a.prop]=(a.prop==="width"||a.prop==="height"?Math.max(0,a.now):a.now)+a.unit;else a.elem[a.prop]=a.now}}});if(c.expr&&c.expr.filters)c.expr.filters.animated=function(a){return c.grep(c.timers,function(b){return a===b.elem}).length};c.fn.offset="getBoundingClientRect"in s.documentElement?
+function(a){var b=this[0];if(a)return this.each(function(e){c.offset.setOffset(this,a,e)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);var d=b.getBoundingClientRect(),f=b.ownerDocument;b=f.body;f=f.documentElement;return{top:d.top+(self.pageYOffset||c.support.boxModel&&f.scrollTop||b.scrollTop)-(f.clientTop||b.clientTop||0),left:d.left+(self.pageXOffset||c.support.boxModel&&f.scrollLeft||b.scrollLeft)-(f.clientLeft||b.clientLeft||0)}}:function(a){var b=
+this[0];if(a)return this.each(function(r){c.offset.setOffset(this,a,r)});if(!b||!b.ownerDocument)return null;if(b===b.ownerDocument.body)return c.offset.bodyOffset(b);c.offset.initialize();var d=b.offsetParent,f=b,e=b.ownerDocument,j,i=e.documentElement,o=e.body;f=(e=e.defaultView)?e.getComputedStyle(b,null):b.currentStyle;for(var k=b.offsetTop,n=b.offsetLeft;(b=b.parentNode)&&b!==o&&b!==i;){if(c.offset.supportsFixedPosition&&f.position==="fixed")break;j=e?e.getComputedStyle(b,null):b.currentStyle;
+k-=b.scrollTop;n-=b.scrollLeft;if(b===d){k+=b.offsetTop;n+=b.offsetLeft;if(c.offset.doesNotAddBorder&&!(c.offset.doesAddBorderForTableAndCells&&/^t(able|d|h)$/i.test(b.nodeName))){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=d;d=b.offsetParent}if(c.offset.subtractsBorderForOverflowNotVisible&&j.overflow!=="visible"){k+=parseFloat(j.borderTopWidth)||0;n+=parseFloat(j.borderLeftWidth)||0}f=j}if(f.position==="relative"||f.position==="static"){k+=o.offsetTop;n+=o.offsetLeft}if(c.offset.supportsFixedPosition&&
+f.position==="fixed"){k+=Math.max(i.scrollTop,o.scrollTop);n+=Math.max(i.scrollLeft,o.scrollLeft)}return{top:k,left:n}};c.offset={initialize:function(){var a=s.body,b=s.createElement("div"),d,f,e,j=parseFloat(c.curCSS(a,"marginTop",true))||0;c.extend(b.style,{position:"absolute",top:0,left:0,margin:0,border:0,width:"1px",height:"1px",visibility:"hidden"});b.innerHTML="<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
+a.insertBefore(b,a.firstChild);d=b.firstChild;f=d.firstChild;e=d.nextSibling.firstChild.firstChild;this.doesNotAddBorder=f.offsetTop!==5;this.doesAddBorderForTableAndCells=e.offsetTop===5;f.style.position="fixed";f.style.top="20px";this.supportsFixedPosition=f.offsetTop===20||f.offsetTop===15;f.style.position=f.style.top="";d.style.overflow="hidden";d.style.position="relative";this.subtractsBorderForOverflowNotVisible=f.offsetTop===-5;this.doesNotIncludeMarginInBodyOffset=a.offsetTop!==j;a.removeChild(b);
+c.offset.initialize=c.noop},bodyOffset:function(a){var b=a.offsetTop,d=a.offsetLeft;c.offset.initialize();if(c.offset.doesNotIncludeMarginInBodyOffset){b+=parseFloat(c.curCSS(a,"marginTop",true))||0;d+=parseFloat(c.curCSS(a,"marginLeft",true))||0}return{top:b,left:d}},setOffset:function(a,b,d){if(/static/.test(c.curCSS(a,"position")))a.style.position="relative";var f=c(a),e=f.offset(),j=parseInt(c.curCSS(a,"top",true),10)||0,i=parseInt(c.curCSS(a,"left",true),10)||0;if(c.isFunction(b))b=b.call(a,
+d,e);d={top:b.top-e.top+j,left:b.left-e.left+i};"using"in b?b.using.call(a,d):f.css(d)}};c.fn.extend({position:function(){if(!this[0])return null;var a=this[0],b=this.offsetParent(),d=this.offset(),f=/^body|html$/i.test(b[0].nodeName)?{top:0,left:0}:b.offset();d.top-=parseFloat(c.curCSS(a,"marginTop",true))||0;d.left-=parseFloat(c.curCSS(a,"marginLeft",true))||0;f.top+=parseFloat(c.curCSS(b[0],"borderTopWidth",true))||0;f.left+=parseFloat(c.curCSS(b[0],"borderLeftWidth",true))||0;return{top:d.top-
+f.top,left:d.left-f.left}},offsetParent:function(){return this.map(function(){for(var a=this.offsetParent||s.body;a&&!/^body|html$/i.test(a.nodeName)&&c.css(a,"position")==="static";)a=a.offsetParent;return a})}});c.each(["Left","Top"],function(a,b){var d="scroll"+b;c.fn[d]=function(f){var e=this[0],j;if(!e)return null;if(f!==w)return this.each(function(){if(j=wa(this))j.scrollTo(!a?f:c(j).scrollLeft(),a?f:c(j).scrollTop());else this[d]=f});else return(j=wa(e))?"pageXOffset"in j?j[a?"pageYOffset":
+"pageXOffset"]:c.support.boxModel&&j.document.documentElement[d]||j.document.body[d]:e[d]}});c.each(["Height","Width"],function(a,b){var d=b.toLowerCase();c.fn["inner"+b]=function(){return this[0]?c.css(this[0],d,false,"padding"):null};c.fn["outer"+b]=function(f){return this[0]?c.css(this[0],d,false,f?"margin":"border"):null};c.fn[d]=function(f){var e=this[0];if(!e)return f==null?null:this;if(c.isFunction(f))return this.each(function(j){var i=c(this);i[d](f.call(this,j,i[d]()))});return"scrollTo"in
+e&&e.document?e.document.compatMode==="CSS1Compat"&&e.document.documentElement["client"+b]||e.document.body["client"+b]:e.nodeType===9?Math.max(e.documentElement["client"+b],e.body["scroll"+b],e.documentElement["scroll"+b],e.body["offset"+b],e.documentElement["offset"+b]):f===w?c.css(e,d):this.css(d,typeof f==="string"?f:f+"px")}});A.jQuery=A.$=c})(window);
diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/polling.js b/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/polling.js
new file mode 100644 (file)
index 0000000..8f81443
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+    Copyright (C) 2011 Pau Escrich <pau@dabax.net>
+    Contributors Lluis Esquerda <eskerda@gmail.com>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License along
+    with this program; if not, write to the Free Software Foundation, Inc.,
+    51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+
+    The full GNU General Public License is included in this distribution in
+    the file called "COPYING".
+*/
+
+               
+/*
+       Table pooler is a function to easy call XHR poller. 
+
+               new TablePooler(5,"/cgi-bin/bmx6-info", {'status':''}, "status_table", function(st){
+                       var table = Array()
+                       table.push(st.first,st.second)
+                       return table
+               }
+       Parameters are: 
+               polling_time: time between pollings
+               json_url: the json url to fetch the data
+               json_call: the json call
+               output_table_id: the table where javascript will put the data
+               callback_function: the function that will be executed each polling_time
+       
+       The callback_function must return an array of arrays (matrix).
+       In the code st is the data obtained from the json call
+*/
+
+       function TablePooler (time, jsonurl, getparams, table_id, callback) {
+               this.table = document.getElementById(table_id);
+               this.callback = callback;
+               this.jsonurl = jsonurl;
+               this.getparams = getparams;
+               this.time = time;
+
+               this.clear = function(){
+                       /* clear all rows */
+                       while( this.table.rows.length > 1 ) this.table.deleteRow(1);
+               }
+               this.start = function(){
+                       XHR.poll(this.time, this.jsonurl, this.getparams, function(x, st){
+                               var data = this.callback(st);
+                               var content, tr, td;
+                               this.clear();
+                               for (var i = 0; i < data.length; i++){
+                                       tr = this.table.insertRow(-1);
+                                       tr.className = 'cbi-section-table-row cbi-rowstyle-' + ((i % 2) + 1);
+                                               
+                                       for (var j = 0; j < data[i].length; j++){
+                                               td = tr.insertCell(-1);
+                                               if (data[i][j].length == 2) {
+                                                       td.colSpan = data[i][j][1];
+                                                       content = data[i][j][0];
+                                               }
+                                               else content = data[i][j];
+                                               td.innerHTML = content;
+                                       }
+                               }
+                       }.bind(this));
+               }
+
+
+               this.start();
+       }
+
+
+
diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/raphael-min.js b/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/raphael-min.js
new file mode 100644 (file)
index 0000000..8718b5b
--- /dev/null
@@ -0,0 +1,7 @@
+/*
+ * Raphael 1.3.1 - JavaScript Vector Library
+ *
+ * Copyright (c) 2008 - 2009 Dmitry Baranovskiy (http://raphaeljs.com)
+ * Licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) license.
+ */
+Raphael=(function(){var a=/[, ]+/,aO=/^(circle|rect|path|ellipse|text|image)$/,L=document,au=window,l={was:"Raphael" in au,is:au.Raphael},an=function(){if(an.is(arguments[0],"array")){var d=arguments[0],e=w[aW](an,d.splice(0,3+an.is(d[0],al))),S=e.set();for(var R=0,a0=d[m];R<a0;R++){var E=d[R]||{};aO.test(E.type)&&S[f](e[E.type]().attr(E));}return S;}return w[aW](an,arguments);},aT=function(){},aL="appendChild",aW="apply",aS="concat",at="",am=" ",z="split",F="click dblclick mousedown mousemove mouseout mouseover mouseup"[z](am),Q="hasOwnProperty",az="join",m="length",aY="prototype",aZ=String[aY].toLowerCase,ab=Math,g=ab.max,aI=ab.min,al="number",aA="toString",aw=Object[aY][aA],aQ={},aM=ab.pow,f="push",aU=/^(?=[\da-f]$)/,c=/^url\(['"]?([^\)]+)['"]?\)$/i,x=/^\s*((#[a-f\d]{6})|(#[a-f\d]{3})|rgb\(\s*([\d\.]+\s*,\s*[\d\.]+\s*,\s*[\d\.]+)\s*\)|rgb\(\s*([\d\.]+%\s*,\s*[\d\.]+%\s*,\s*[\d\.]+%)\s*\)|hs[bl]\(\s*([\d\.]+\s*,\s*[\d\.]+\s*,\s*[\d\.]+)\s*\)|hs[bl]\(\s*([\d\.]+%\s*,\s*[\d\.]+%\s*,\s*[\d\.]+%)\s*\))\s*$/i,O=ab.round,v="setAttribute",W=parseFloat,G=parseInt,aN=String[aY].toUpperCase,j={"clip-rect":"0 0 1e9 1e9",cursor:"default",cx:0,cy:0,fill:"#fff","fill-opacity":1,font:'10px "Arial"',"font-family":'"Arial"',"font-size":"10","font-style":"normal","font-weight":400,gradient:0,height:0,href:"http://raphaeljs.com/",opacity:1,path:"M0,0",r:0,rotation:0,rx:0,ry:0,scale:"1 1",src:"",stroke:"#000","stroke-dasharray":"","stroke-linecap":"butt","stroke-linejoin":"butt","stroke-miterlimit":0,"stroke-opacity":1,"stroke-width":1,target:"_blank","text-anchor":"middle",title:"Raphael",translation:"0 0",width:0,x:0,y:0},Z={along:"along","clip-rect":"csv",cx:al,cy:al,fill:"colour","fill-opacity":al,"font-size":al,height:al,opacity:al,path:"path",r:al,rotation:"csv",rx:al,ry:al,scale:"csv",stroke:"colour","stroke-opacity":al,"stroke-width":al,translation:"csv",width:al,x:al,y:al},aP="replace";an.version="1.3.1";an.type=(au.SVGAngle||L.implementation.hasFeature("http://www.w3.org/TR/SVG11/feature#BasicStructure","1.1")?"SVG":"VML");if(an.type=="VML"){var ag=document.createElement("div");ag.innerHTML="<!--[if vml]><br><br><![endif]-->";if(ag.childNodes[m]!=2){return null;}}an.svg=!(an.vml=an.type=="VML");aT[aY]=an[aY];an._id=0;an._oid=0;an.fn={};an.is=function(e,d){d=aZ.call(d);return((d=="object"||d=="undefined")&&typeof e==d)||(e==null&&d=="null")||aZ.call(aw.call(e).slice(8,-1))==d;};an.setWindow=function(d){au=d;L=au.document;};var aD=function(e){if(an.vml){var d=/^\s+|\s+$/g;aD=aj(function(R){var S;R=(R+at)[aP](d,at);try{var a0=new ActiveXObject("htmlfile");a0.write("<body>");a0.close();S=a0.body;}catch(a2){S=createPopup().document.body;}var i=S.createTextRange();try{S.style.color=R;var a1=i.queryCommandValue("ForeColor");a1=((a1&255)<<16)|(a1&65280)|((a1&16711680)>>>16);return"#"+("000000"+a1[aA](16)).slice(-6);}catch(a2){return"none";}});}else{var E=L.createElement("i");E.title="Rapha\xebl Colour Picker";E.style.display="none";L.body[aL](E);aD=aj(function(i){E.style.color=i;return L.defaultView.getComputedStyle(E,at).getPropertyValue("color");});}return aD(e);};an.hsb2rgb=aj(function(a3,a1,a7){if(an.is(a3,"object")&&"h" in a3&&"s" in a3&&"b" in a3){a7=a3.b;a1=a3.s;a3=a3.h;}var R,S,a8;if(a7==0){return{r:0,g:0,b:0,hex:"#000"};}if(a3>1||a1>1||a7>1){a3/=255;a1/=255;a7/=255;}var a0=~~(a3*6),a4=(a3*6)-a0,E=a7*(1-a1),e=a7*(1-(a1*a4)),a9=a7*(1-(a1*(1-a4)));R=[a7,e,E,E,a9,a7,a7][a0];S=[a9,a7,a7,e,E,E,a9][a0];a8=[E,E,a9,a7,a7,e,E][a0];R*=255;S*=255;a8*=255;var a5={r:R,g:S,b:a8},d=(~~R)[aA](16),a2=(~~S)[aA](16),a6=(~~a8)[aA](16);d=d[aP](aU,"0");a2=a2[aP](aU,"0");a6=a6[aP](aU,"0");a5.hex="#"+d+a2+a6;return a5;},an);an.rgb2hsb=aj(function(d,e,a1){if(an.is(d,"object")&&"r" in d&&"g" in d&&"b" in d){a1=d.b;e=d.g;d=d.r;}if(an.is(d,"string")){var a3=an.getRGB(d);d=a3.r;e=a3.g;a1=a3.b;}if(d>1||e>1||a1>1){d/=255;e/=255;a1/=255;}var a0=g(d,e,a1),i=aI(d,e,a1),R,E,S=a0;if(i==a0){return{h:0,s:0,b:a0};}else{var a2=(a0-i);E=a2/a0;if(d==a0){R=(e-a1)/a2;}else{if(e==a0){R=2+((a1-d)/a2);}else{R=4+((d-e)/a2);}}R/=6;R<0&&R++;R>1&&R--;}return{h:R,s:E,b:S};},an);var aE=/,?([achlmqrstvxz]),?/gi;an._path2string=function(){return this.join(",")[aP](aE,"$1");};function aj(E,e,d){function i(){var R=Array[aY].slice.call(arguments,0),a0=R[az]("\u25ba"),S=i.cache=i.cache||{},a1=i.count=i.count||[];if(S[Q](a0)){return d?d(S[a0]):S[a0];}a1[m]>=1000&&delete S[a1.shift()];a1[f](a0);S[a0]=E[aW](e,R);return d?d(S[a0]):S[a0];}return i;}an.getRGB=aj(function(d){if(!d||!!((d=d+at).indexOf("-")+1)){return{r:-1,g:-1,b:-1,hex:"none",error:1};}if(d=="none"){return{r:-1,g:-1,b:-1,hex:"none"};}!(({hs:1,rg:1})[Q](d.substring(0,2))||d.charAt()=="#")&&(d=aD(d));var S,i,E,a2,a3,a0=d.match(x);if(a0){if(a0[2]){a2=G(a0[2].substring(5),16);E=G(a0[2].substring(3,5),16);i=G(a0[2].substring(1,3),16);}if(a0[3]){a2=G((a3=a0[3].charAt(3))+a3,16);E=G((a3=a0[3].charAt(2))+a3,16);i=G((a3=a0[3].charAt(1))+a3,16);}if(a0[4]){a0=a0[4][z](/\s*,\s*/);i=W(a0[0]);E=W(a0[1]);a2=W(a0[2]);}if(a0[5]){a0=a0[5][z](/\s*,\s*/);i=W(a0[0])*2.55;E=W(a0[1])*2.55;a2=W(a0[2])*2.55;}if(a0[6]){a0=a0[6][z](/\s*,\s*/);i=W(a0[0]);E=W(a0[1]);a2=W(a0[2]);return an.hsb2rgb(i,E,a2);}if(a0[7]){a0=a0[7][z](/\s*,\s*/);i=W(a0[0])*2.55;E=W(a0[1])*2.55;a2=W(a0[2])*2.55;return an.hsb2rgb(i,E,a2);}a0={r:i,g:E,b:a2};var e=(~~i)[aA](16),R=(~~E)[aA](16),a1=(~~a2)[aA](16);e=e[aP](aU,"0");R=R[aP](aU,"0");a1=a1[aP](aU,"0");a0.hex="#"+e+R+a1;return a0;}return{r:-1,g:-1,b:-1,hex:"none",error:1};},an);an.getColor=function(e){var i=this.getColor.start=this.getColor.start||{h:0,s:1,b:e||0.75},d=this.hsb2rgb(i.h,i.s,i.b);i.h+=0.075;if(i.h>1){i.h=0;i.s-=0.2;i.s<=0&&(this.getColor.start={h:0,s:1,b:i.b});}return d.hex;};an.getColor.reset=function(){delete this.start;};an.parsePathString=aj(function(d){if(!d){return null;}var i={a:7,c:6,h:1,l:2,m:2,q:4,s:4,t:2,v:1,z:0},e=[];if(an.is(d,"array")&&an.is(d[0],"array")){e=av(d);}if(!e[m]){(d+at)[aP](/([achlmqstvz])[\s,]*((-?\d*\.?\d*(?:e[-+]?\d+)?\s*,?\s*)+)/ig,function(R,E,a1){var a0=[],S=aZ.call(E);a1[aP](/(-?\d*\.?\d*(?:e[-+]?\d+)?)\s*,?\s*/ig,function(a3,a2){a2&&a0[f](+a2);});while(a0[m]>=i[S]){e[f]([E][aS](a0.splice(0,i[S])));if(!i[S]){break;}}});}e[aA]=an._path2string;return e;});an.findDotsAtSegment=function(e,d,be,bc,a0,R,a2,a1,a8){var a6=1-a8,a5=aM(a6,3)*e+aM(a6,2)*3*a8*be+a6*3*a8*a8*a0+aM(a8,3)*a2,a3=aM(a6,3)*d+aM(a6,2)*3*a8*bc+a6*3*a8*a8*R+aM(a8,3)*a1,ba=e+2*a8*(be-e)+a8*a8*(a0-2*be+e),a9=d+2*a8*(bc-d)+a8*a8*(R-2*bc+d),bd=be+2*a8*(a0-be)+a8*a8*(a2-2*a0+be),bb=bc+2*a8*(R-bc)+a8*a8*(a1-2*R+bc),a7=(1-a8)*e+a8*be,a4=(1-a8)*d+a8*bc,E=(1-a8)*a0+a8*a2,i=(1-a8)*R+a8*a1,S=(90-ab.atan((ba-bd)/(a9-bb))*180/ab.PI);(ba>bd||a9<bb)&&(S+=180);return{x:a5,y:a3,m:{x:ba,y:a9},n:{x:bd,y:bb},start:{x:a7,y:a4},end:{x:E,y:i},alpha:S};};var U=aj(function(a5){if(!a5){return{x:0,y:0,width:0,height:0};}a5=H(a5);var a2=0,a1=0,R=[],e=[],E;for(var S=0,a4=a5[m];S<a4;S++){E=a5[S];if(E[0]=="M"){a2=E[1];a1=E[2];R[f](a2);e[f](a1);}else{var a0=aC(a2,a1,E[1],E[2],E[3],E[4],E[5],E[6]);R=R[aS](a0.min.x,a0.max.x);e=e[aS](a0.min.y,a0.max.y);a2=E[5];a1=E[6];}}var d=aI[aW](0,R),a3=aI[aW](0,e);return{x:d,y:a3,width:g[aW](0,R)-d,height:g[aW](0,e)-a3};}),av=function(a0){var E=[];if(!an.is(a0,"array")||!an.is(a0&&a0[0],"array")){a0=an.parsePathString(a0);}for(var e=0,R=a0[m];e<R;e++){E[e]=[];for(var d=0,S=a0[e][m];d<S;d++){E[e][d]=a0[e][d];}}E[aA]=an._path2string;return E;},ad=aj(function(R){if(!an.is(R,"array")||!an.is(R&&R[0],"array")){R=an.parsePathString(R);}var a4=[],a6=0,a5=0,a9=0,a8=0,E=0;if(R[0][0]=="M"){a6=R[0][1];a5=R[0][2];a9=a6;a8=a5;E++;a4[f](["M",a6,a5]);}for(var a1=E,ba=R[m];a1<ba;a1++){var d=a4[a1]=[],a7=R[a1];if(a7[0]!=aZ.call(a7[0])){d[0]=aZ.call(a7[0]);switch(d[0]){case"a":d[1]=a7[1];d[2]=a7[2];d[3]=a7[3];d[4]=a7[4];d[5]=a7[5];d[6]=+(a7[6]-a6).toFixed(3);d[7]=+(a7[7]-a5).toFixed(3);break;case"v":d[1]=+(a7[1]-a5).toFixed(3);break;case"m":a9=a7[1];a8=a7[2];default:for(var a0=1,a2=a7[m];a0<a2;a0++){d[a0]=+(a7[a0]-((a0%2)?a6:a5)).toFixed(3);}}}else{d=a4[a1]=[];if(a7[0]=="m"){a9=a7[1]+a6;a8=a7[2]+a5;}for(var S=0,e=a7[m];S<e;S++){a4[a1][S]=a7[S];}}var a3=a4[a1][m];switch(a4[a1][0]){case"z":a6=a9;a5=a8;break;case"h":a6+=+a4[a1][a3-1];break;case"v":a5+=+a4[a1][a3-1];break;default:a6+=+a4[a1][a3-2];a5+=+a4[a1][a3-1];}}a4[aA]=an._path2string;return a4;},0,av),r=aj(function(R){if(!an.is(R,"array")||!an.is(R&&R[0],"array")){R=an.parsePathString(R);}var a3=[],a5=0,a4=0,a8=0,a7=0,E=0;if(R[0][0]=="M"){a5=+R[0][1];a4=+R[0][2];a8=a5;a7=a4;E++;a3[0]=["M",a5,a4];}for(var a1=E,a9=R[m];a1<a9;a1++){var d=a3[a1]=[],a6=R[a1];if(a6[0]!=aN.call(a6[0])){d[0]=aN.call(a6[0]);switch(d[0]){case"A":d[1]=a6[1];d[2]=a6[2];d[3]=a6[3];d[4]=a6[4];d[5]=a6[5];d[6]=+(a6[6]+a5);d[7]=+(a6[7]+a4);break;case"V":d[1]=+a6[1]+a4;break;case"H":d[1]=+a6[1]+a5;break;case"M":a8=+a6[1]+a5;a7=+a6[2]+a4;default:for(var a0=1,a2=a6[m];a0<a2;a0++){d[a0]=+a6[a0]+((a0%2)?a5:a4);}}}else{for(var S=0,e=a6[m];S<e;S++){a3[a1][S]=a6[S];}}switch(d[0]){case"Z":a5=a8;a4=a7;break;case"H":a5=d[1];break;case"V":a4=d[1];break;default:a5=a3[a1][a3[a1][m]-2];a4=a3[a1][a3[a1][m]-1];}}a3[aA]=an._path2string;return a3;},null,av),aX=function(e,E,d,i){return[e,E,d,i,d,i];},aK=function(e,E,a0,R,d,i){var S=1/3,a1=2/3;return[S*e+a1*a0,S*E+a1*R,S*d+a1*a0,S*i+a1*R,d,i];},K=function(a9,bE,bi,bg,ba,a4,S,a8,bD,bb){var R=ab.PI,bf=R*120/180,d=R/180*(+ba||0),bm=[],bj,bA=aj(function(bF,bI,i){var bH=bF*ab.cos(i)-bI*ab.sin(i),bG=bF*ab.sin(i)+bI*ab.cos(i);return{x:bH,y:bG};});if(!bb){bj=bA(a9,bE,-d);a9=bj.x;bE=bj.y;bj=bA(a8,bD,-d);a8=bj.x;bD=bj.y;var e=ab.cos(R/180*ba),a6=ab.sin(R/180*ba),bo=(a9-a8)/2,bn=(bE-bD)/2;bi=g(bi,ab.abs(bo));bg=g(bg,ab.abs(bn));var by=(bo*bo)/(bi*bi)+(bn*bn)/(bg*bg);if(by>1){bi=ab.sqrt(by)*bi;bg=ab.sqrt(by)*bg;}var E=bi*bi,br=bg*bg,bt=(a4==S?-1:1)*ab.sqrt(ab.abs((E*br-E*bn*bn-br*bo*bo)/(E*bn*bn+br*bo*bo))),bd=bt*bi*bn/bg+(a9+a8)/2,bc=bt*-bg*bo/bi+(bE+bD)/2,a3=ab.asin(((bE-bc)/bg).toFixed(7)),a2=ab.asin(((bD-bc)/bg).toFixed(7));a3=a9<bd?R-a3:a3;a2=a8<bd?R-a2:a2;a3<0&&(a3=R*2+a3);a2<0&&(a2=R*2+a2);if(S&&a3>a2){a3=a3-R*2;}if(!S&&a2>a3){a2=a2-R*2;}}else{a3=bb[0];a2=bb[1];bd=bb[2];bc=bb[3];}var a7=a2-a3;if(ab.abs(a7)>bf){var be=a2,bh=a8,a5=bD;a2=a3+bf*(S&&a2>a3?1:-1);a8=bd+bi*ab.cos(a2);bD=bc+bg*ab.sin(a2);bm=K(a8,bD,bi,bg,ba,0,S,bh,a5,[a2,be,bd,bc]);}a7=a2-a3;var a1=ab.cos(a3),bC=ab.sin(a3),a0=ab.cos(a2),bB=ab.sin(a2),bp=ab.tan(a7/4),bs=4/3*bi*bp,bq=4/3*bg*bp,bz=[a9,bE],bx=[a9+bs*bC,bE-bq*a1],bw=[a8+bs*bB,bD-bq*a0],bu=[a8,bD];bx[0]=2*bz[0]-bx[0];bx[1]=2*bz[1]-bx[1];if(bb){return[bx,bw,bu][aS](bm);}else{bm=[bx,bw,bu][aS](bm)[az]()[z](",");var bk=[];for(var bv=0,bl=bm[m];bv<bl;bv++){bk[bv]=bv%2?bA(bm[bv-1],bm[bv],d).y:bA(bm[bv],bm[bv+1],d).x;}return bk;}},M=function(e,d,E,i,a2,a1,a0,S,a3){var R=1-a3;return{x:aM(R,3)*e+aM(R,2)*3*a3*E+R*3*a3*a3*a2+aM(a3,3)*a0,y:aM(R,3)*d+aM(R,2)*3*a3*i+R*3*a3*a3*a1+aM(a3,3)*S};},aC=aj(function(i,d,R,E,a9,a8,a5,a2){var a7=(a9-2*R+i)-(a5-2*a9+R),a4=2*(R-i)-2*(a9-R),a1=i-R,a0=(-a4+ab.sqrt(a4*a4-4*a7*a1))/2/a7,S=(-a4-ab.sqrt(a4*a4-4*a7*a1))/2/a7,a3=[d,a2],a6=[i,a5],e;ab.abs(a0)>1000000000000&&(a0=0.5);ab.abs(S)>1000000000000&&(S=0.5);if(a0>0&&a0<1){e=M(i,d,R,E,a9,a8,a5,a2,a0);a6[f](e.x);a3[f](e.y);}if(S>0&&S<1){e=M(i,d,R,E,a9,a8,a5,a2,S);a6[f](e.x);a3[f](e.y);}a7=(a8-2*E+d)-(a2-2*a8+E);a4=2*(E-d)-2*(a8-E);a1=d-E;a0=(-a4+ab.sqrt(a4*a4-4*a7*a1))/2/a7;S=(-a4-ab.sqrt(a4*a4-4*a7*a1))/2/a7;ab.abs(a0)>1000000000000&&(a0=0.5);ab.abs(S)>1000000000000&&(S=0.5);if(a0>0&&a0<1){e=M(i,d,R,E,a9,a8,a5,a2,a0);a6[f](e.x);a3[f](e.y);}if(S>0&&S<1){e=M(i,d,R,E,a9,a8,a5,a2,S);a6[f](e.x);a3[f](e.y);}return{min:{x:aI[aW](0,a6),y:aI[aW](0,a3)},max:{x:g[aW](0,a6),y:g[aW](0,a3)}};}),H=aj(function(a9,a4){var R=r(a9),a5=a4&&r(a4),a6={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},d={x:0,y:0,bx:0,by:0,X:0,Y:0,qx:null,qy:null},a0=function(ba,bb){var i,bc;if(!ba){return["C",bb.x,bb.y,bb.x,bb.y,bb.x,bb.y];}!(ba[0] in {T:1,Q:1})&&(bb.qx=bb.qy=null);switch(ba[0]){case"M":bb.X=ba[1];bb.Y=ba[2];break;case"A":ba=["C"][aS](K[aW](0,[bb.x,bb.y][aS](ba.slice(1))));break;case"S":i=bb.x+(bb.x-(bb.bx||bb.x));bc=bb.y+(bb.y-(bb.by||bb.y));ba=["C",i,bc][aS](ba.slice(1));break;case"T":bb.qx=bb.x+(bb.x-(bb.qx||bb.x));bb.qy=bb.y+(bb.y-(bb.qy||bb.y));ba=["C"][aS](aK(bb.x,bb.y,bb.qx,bb.qy,ba[1],ba[2]));break;case"Q":bb.qx=ba[1];bb.qy=ba[2];ba=["C"][aS](aK(bb.x,bb.y,ba[1],ba[2],ba[3],ba[4]));break;case"L":ba=["C"][aS](aX(bb.x,bb.y,ba[1],ba[2]));break;case"H":ba=["C"][aS](aX(bb.x,bb.y,ba[1],bb.y));break;case"V":ba=["C"][aS](aX(bb.x,bb.y,bb.x,ba[1]));break;case"Z":ba=["C"][aS](aX(bb.x,bb.y,bb.X,bb.Y));break;}return ba;},e=function(ba,bb){if(ba[bb][m]>7){ba[bb].shift();var bc=ba[bb];while(bc[m]){ba.splice(bb++,0,["C"][aS](bc.splice(0,6)));}ba.splice(bb,1);a7=g(R[m],a5&&a5[m]||0);}},E=function(be,bd,bb,ba,bc){if(be&&bd&&be[bc][0]=="M"&&bd[bc][0]!="M"){bd.splice(bc,0,["M",ba.x,ba.y]);bb.bx=0;bb.by=0;bb.x=be[bc][1];bb.y=be[bc][2];a7=g(R[m],a5&&a5[m]||0);}};for(var a2=0,a7=g(R[m],a5&&a5[m]||0);a2<a7;a2++){R[a2]=a0(R[a2],a6);e(R,a2);a5&&(a5[a2]=a0(a5[a2],d));a5&&e(a5,a2);E(R,a5,a6,d,a2);E(a5,R,d,a6,a2);var a1=R[a2],a8=a5&&a5[a2],S=a1[m],a3=a5&&a8[m];a6.x=a1[S-2];a6.y=a1[S-1];a6.bx=W(a1[S-4])||a6.x;a6.by=W(a1[S-3])||a6.y;d.bx=a5&&(W(a8[a3-4])||d.x);d.by=a5&&(W(a8[a3-3])||d.y);d.x=a5&&a8[a3-2];d.y=a5&&a8[a3-1];}return a5?[R,a5]:R;},null,av),p=aj(function(a4){var a3=[];for(var a0=0,a5=a4[m];a0<a5;a0++){var e={},a2=a4[a0].match(/^([^:]*):?([\d\.]*)/);e.color=an.getRGB(a2[1]);if(e.color.error){return null;}e.color=e.color.hex;a2[2]&&(e.offset=a2[2]+"%");a3[f](e);}for(var a0=1,a5=a3[m]-1;a0<a5;a0++){if(!a3[a0].offset){var E=W(a3[a0-1].offset||0),R=0;for(var S=a0+1;S<a5;S++){if(a3[S].offset){R=a3[S].offset;break;}}if(!R){R=100;S=a5;}R=W(R);var a1=(R-E)/(S-a0+1);for(;a0<S;a0++){E+=a1;a3[a0].offset=E+"%";}}}return a3;}),ao=function(){var i,e,R,E,d;if(an.is(arguments[0],"string")||an.is(arguments[0],"object")){if(an.is(arguments[0],"string")){i=L.getElementById(arguments[0]);}else{i=arguments[0];}if(i.tagName){if(arguments[1]==null){return{container:i,width:i.style.pixelWidth||i.offsetWidth,height:i.style.pixelHeight||i.offsetHeight};}else{return{container:i,width:arguments[1],height:arguments[2]};}}}else{if(an.is(arguments[0],al)&&arguments[m]>3){return{container:1,x:arguments[0],y:arguments[1],width:arguments[2],height:arguments[3]};}}},aG=function(d,i){var e=this;for(var E in i){if(i[Q](E)&&!(E in d)){switch(typeof i[E]){case"function":(function(R){d[E]=d===e?R:function(){return R[aW](e,arguments);};})(i[E]);break;case"object":d[E]=d[E]||{};aG.call(this,d[E],i[E]);break;default:d[E]=i[E];break;}}}},ak=function(d,e){d==e.top&&(e.top=d.prev);d==e.bottom&&(e.bottom=d.next);d.next&&(d.next.prev=d.prev);d.prev&&(d.prev.next=d.next);},Y=function(d,e){if(e.top===d){return;}ak(d,e);d.next=null;d.prev=e.top;e.top.next=d;e.top=d;},k=function(d,e){if(e.bottom===d){return;}ak(d,e);d.next=e.bottom;d.prev=null;e.bottom.prev=d;e.bottom=d;},A=function(e,d,i){ak(e,i);d==i.top&&(i.top=e);d.next&&(d.next.prev=e);e.next=d.next;e.prev=d;d.next=e;},aq=function(e,d,i){ak(e,i);d==i.bottom&&(i.bottom=e);d.prev&&(d.prev.next=e);e.prev=d.prev;d.prev=e;e.next=d;},s=function(d){return function(){throw new Error("Rapha\xebl: you are calling to method \u201c"+d+"\u201d of removed object");};},ar=/^r(?:\(([^,]+?)\s*,\s*([^\)]+?)\))?/;if(an.svg){aT[aY].svgns="http://www.w3.org/2000/svg";aT[aY].xlink="http://www.w3.org/1999/xlink";var O=function(d){return +d+(~~d===d)*0.5;},V=function(S){for(var e=0,E=S[m];e<E;e++){if(aZ.call(S[e][0])!="a"){for(var d=1,R=S[e][m];d<R;d++){S[e][d]=O(S[e][d]);}}else{S[e][6]=O(S[e][6]);S[e][7]=O(S[e][7]);}}return S;},aJ=function(i,d){if(d){for(var e in d){if(d[Q](e)){i[v](e,d[e]);}}}else{return L.createElementNS(aT[aY].svgns,i);}};an[aA]=function(){return"Your browser supports SVG.\nYou are running Rapha\xebl "+this.version;};var q=function(d,E){var e=aJ("path");E.canvas&&E.canvas[aL](e);var i=new ax(e,E);i.type="path";aa(i,{fill:"none",stroke:"#000",path:d});return i;};var b=function(E,a7,d){var a4="linear",a1=0.5,S=0.5,a9=E.style;a7=(a7+at)[aP](ar,function(bb,i,bc){a4="radial";if(i&&bc){a1=W(i);S=W(bc);var ba=((S>0.5)*2-1);aM(a1-0.5,2)+aM(S-0.5,2)>0.25&&(S=ab.sqrt(0.25-aM(a1-0.5,2))*ba+0.5)&&S!=0.5&&(S=S.toFixed(5)-0.00001*ba);}return at;});a7=a7[z](/\s*\-\s*/);if(a4=="linear"){var a0=a7.shift();a0=-W(a0);if(isNaN(a0)){return null;}var R=[0,0,ab.cos(a0*ab.PI/180),ab.sin(a0*ab.PI/180)],a6=1/(g(ab.abs(R[2]),ab.abs(R[3]))||1);R[2]*=a6;R[3]*=a6;if(R[2]<0){R[0]=-R[2];R[2]=0;}if(R[3]<0){R[1]=-R[3];R[3]=0;}}var a3=p(a7);if(!a3){return null;}var e=aJ(a4+"Gradient");e.id="r"+(an._id++)[aA](36);aJ(e,a4=="radial"?{fx:a1,fy:S}:{x1:R[0],y1:R[1],x2:R[2],y2:R[3]});d.defs[aL](e);for(var a2=0,a8=a3[m];a2<a8;a2++){var a5=aJ("stop");aJ(a5,{offset:a3[a2].offset?a3[a2].offset:!a2?"0%":"100%","stop-color":a3[a2].color||"#fff"});e[aL](a5);}aJ(E,{fill:"url(#"+e.id+")",opacity:1,"fill-opacity":1});a9.fill=at;a9.opacity=1;a9.fillOpacity=1;return 1;};var N=function(e){var d=e.getBBox();aJ(e.pattern,{patternTransform:an.format("translate({0},{1})",d.x,d.y)});};var aa=function(a6,bf){var a9={"":[0],none:[0],"-":[3,1],".":[1,1],"-.":[3,1,1,1],"-..":[3,1,1,1,1,1],". ":[1,3],"- ":[4,3],"--":[8,3],"- .":[4,3,1,3],"--.":[8,3,1,3],"--..":[8,3,1,3,1,3]},bb=a6.node,a7=a6.attrs,a3=a6.rotate(),S=function(bm,bl){bl=a9[aZ.call(bl)];if(bl){var bj=bm.attrs["stroke-width"]||"1",bh={round:bj,square:bj,butt:0}[bm.attrs["stroke-linecap"]||bf["stroke-linecap"]]||0,bk=[];var bi=bl[m];while(bi--){bk[bi]=bl[bi]*bj+((bi%2)?1:-1)*bh;}aJ(bb,{"stroke-dasharray":bk[az](",")});}};bf[Q]("rotation")&&(a3=bf.rotation);var a2=(a3+at)[z](a);if(!(a2.length-1)){a2=null;}else{a2[1]=+a2[1];a2[2]=+a2[2];}W(a3)&&a6.rotate(0,true);for(var ba in bf){if(bf[Q](ba)){if(!j[Q](ba)){continue;}var a8=bf[ba];a7[ba]=a8;switch(ba){case"rotation":a6.rotate(a8,true);break;case"href":case"title":case"target":var bd=bb.parentNode;if(aZ.call(bd.tagName)!="a"){var E=aJ("a");bd.insertBefore(E,bb);E[aL](bb);bd=E;}bd.setAttributeNS(a6.paper.xlink,ba,a8);break;case"cursor":bb.style.cursor=a8;break;case"clip-rect":var e=(a8+at)[z](a);if(e[m]==4){a6.clip&&a6.clip.parentNode.parentNode.removeChild(a6.clip.parentNode);var i=aJ("clipPath"),bc=aJ("rect");i.id="r"+(an._id++)[aA](36);aJ(bc,{x:e[0],y:e[1],width:e[2],height:e[3]});i[aL](bc);a6.paper.defs[aL](i);aJ(bb,{"clip-path":"url(#"+i.id+")"});a6.clip=bc;}if(!a8){var be=L.getElementById(bb.getAttribute("clip-path")[aP](/(^url\(#|\)$)/g,at));be&&be.parentNode.removeChild(be);aJ(bb,{"clip-path":at});delete a6.clip;}break;case"path":if(a8&&a6.type=="path"){a7.path=V(r(a8));aJ(bb,{d:a7.path});}break;case"width":bb[v](ba,a8);if(a7.fx){ba="x";a8=a7.x;}else{break;}case"x":if(a7.fx){a8=-a7.x-(a7.width||0);}case"rx":if(ba=="rx"&&a6.type=="rect"){break;}case"cx":a2&&(ba=="x"||ba=="cx")&&(a2[1]+=a8-a7[ba]);bb[v](ba,O(a8));a6.pattern&&N(a6);break;case"height":bb[v](ba,a8);if(a7.fy){ba="y";a8=a7.y;}else{break;}case"y":if(a7.fy){a8=-a7.y-(a7.height||0);}case"ry":if(ba=="ry"&&a6.type=="rect"){break;}case"cy":a2&&(ba=="y"||ba=="cy")&&(a2[2]+=a8-a7[ba]);bb[v](ba,O(a8));a6.pattern&&N(a6);break;case"r":if(a6.type=="rect"){aJ(bb,{rx:a8,ry:a8});}else{bb[v](ba,a8);}break;case"src":if(a6.type=="image"){bb.setAttributeNS(a6.paper.xlink,"href",a8);}break;case"stroke-width":bb.style.strokeWidth=a8;bb[v](ba,a8);if(a7["stroke-dasharray"]){S(a6,a7["stroke-dasharray"]);}break;case"stroke-dasharray":S(a6,a8);break;case"translation":var a0=(a8+at)[z](a);a0[0]=+a0[0]||0;a0[1]=+a0[1]||0;if(a2){a2[1]+=a0[0];a2[2]+=a0[1];}t.call(a6,a0[0],a0[1]);break;case"scale":var a0=(a8+at)[z](a);a6.scale(+a0[0]||1,+a0[1]||+a0[0]||1,+a0[2]||null,+a0[3]||null);break;case"fill":var R=(a8+at).match(c);if(R){var i=aJ("pattern"),a5=aJ("image");i.id="r"+(an._id++)[aA](36);aJ(i,{x:0,y:0,patternUnits:"userSpaceOnUse",height:1,width:1});aJ(a5,{x:0,y:0});a5.setAttributeNS(a6.paper.xlink,"href",R[1]);i[aL](a5);var bg=L.createElement("img");bg.style.cssText="position:absolute;left:-9999em;top-9999em";bg.onload=function(){aJ(i,{width:this.offsetWidth,height:this.offsetHeight});aJ(a5,{width:this.offsetWidth,height:this.offsetHeight});L.body.removeChild(this);a6.paper.safari();};L.body[aL](bg);bg.src=R[1];a6.paper.defs[aL](i);bb.style.fill="url(#"+i.id+")";aJ(bb,{fill:"url(#"+i.id+")"});a6.pattern=i;a6.pattern&&N(a6);break;}if(!an.getRGB(a8).error){delete bf.gradient;delete a7.gradient;!an.is(a7.opacity,"undefined")&&an.is(bf.opacity,"undefined")&&aJ(bb,{opacity:a7.opacity});!an.is(a7["fill-opacity"],"undefined")&&an.is(bf["fill-opacity"],"undefined")&&aJ(bb,{"fill-opacity":a7["fill-opacity"]});}else{if((({circle:1,ellipse:1})[Q](a6.type)||(a8+at).charAt()!="r")&&b(bb,a8,a6.paper)){a7.gradient=a8;a7.fill="none";break;}}case"stroke":bb[v](ba,an.getRGB(a8).hex);break;case"gradient":(({circle:1,ellipse:1})[Q](a6.type)||(a8+at).charAt()!="r")&&b(bb,a8,a6.paper);break;case"opacity":case"fill-opacity":if(a7.gradient){var d=L.getElementById(bb.getAttribute("fill")[aP](/^url\(#|\)$/g,at));if(d){var a1=d.getElementsByTagName("stop");a1[a1[m]-1][v]("stop-opacity",a8);}break;}default:ba=="font-size"&&(a8=G(a8,10)+"px");var a4=ba[aP](/(\-.)/g,function(bh){return aN.call(bh.substring(1));});bb.style[a4]=a8;bb[v](ba,a8);break;}}}D(a6,bf);if(a2){a6.rotate(a2.join(am));}else{W(a3)&&a6.rotate(a3,true);}};var h=1.2;var D=function(d,R){if(d.type!="text"||!(R[Q]("text")||R[Q]("font")||R[Q]("font-size")||R[Q]("x")||R[Q]("y"))){return;}var a3=d.attrs,e=d.node,a5=e.firstChild?G(L.defaultView.getComputedStyle(e.firstChild,at).getPropertyValue("font-size"),10):10;if(R[Q]("text")){a3.text=R.text;while(e.firstChild){e.removeChild(e.firstChild);}var E=(R.text+at)[z]("\n");for(var S=0,a4=E[m];S<a4;S++){if(E[S]){var a1=aJ("tspan");S&&aJ(a1,{dy:a5*h,x:a3.x});a1[aL](L.createTextNode(E[S]));e[aL](a1);}}}else{var E=e.getElementsByTagName("tspan");for(var S=0,a4=E[m];S<a4;S++){S&&aJ(E[S],{dy:a5*h,x:a3.x});}}aJ(e,{y:a3.y});var a0=d.getBBox(),a2=a3.y-(a0.y+a0.height/2);a2&&isFinite(a2)&&aJ(e,{y:a3.y+a2});};var ax=function(e,d){var E=0,i=0;this[0]=e;this.id=an._oid++;this.node=e;e.raphael=this;this.paper=d;this.attrs=this.attrs||{};this.transformations=[];this._={tx:0,ty:0,rt:{deg:0,cx:0,cy:0},sx:1,sy:1};!d.bottom&&(d.bottom=this);this.prev=d.top;d.top&&(d.top.next=this);d.top=this;this.next=null;};ax[aY].rotate=function(e,d,E){if(this.removed){return this;}if(e==null){if(this._.rt.cx){return[this._.rt.deg,this._.rt.cx,this._.rt.cy][az](am);}return this._.rt.deg;}var i=this.getBBox();e=(e+at)[z](a);if(e[m]-1){d=W(e[1]);E=W(e[2]);}e=W(e[0]);if(d!=null){this._.rt.deg=e;}else{this._.rt.deg+=e;}(E==null)&&(d=null);this._.rt.cx=d;this._.rt.cy=E;d=d==null?i.x+i.width/2:d;E=E==null?i.y+i.height/2:E;if(this._.rt.deg){this.transformations[0]=an.format("rotate({0} {1} {2})",this._.rt.deg,d,E);this.clip&&aJ(this.clip,{transform:an.format("rotate({0} {1} {2})",-this._.rt.deg,d,E)});}else{this.transformations[0]=at;this.clip&&aJ(this.clip,{transform:at});}aJ(this.node,{transform:this.transformations[az](am)});return this;};ax[aY].hide=function(){!this.removed&&(this.node.style.display="none");return this;};ax[aY].show=function(){!this.removed&&(this.node.style.display="");return this;};ax[aY].remove=function(){if(this.removed){return;}ak(this,this.paper);this.node.parentNode.removeChild(this.node);for(var d in this){delete this[d];}this.removed=true;};ax[aY].getBBox=function(){if(this.removed){return this;}if(this.type=="path"){return U(this.attrs.path);}if(this.node.style.display=="none"){this.show();var E=true;}var a1={};try{a1=this.node.getBBox();}catch(S){}finally{a1=a1||{};}if(this.type=="text"){a1={x:a1.x,y:Infinity,width:0,height:0};for(var d=0,R=this.node.getNumberOfChars();d<R;d++){var a0=this.node.getExtentOfChar(d);(a0.y<a1.y)&&(a1.y=a0.y);(a0.y+a0.height-a1.y>a1.height)&&(a1.height=a0.y+a0.height-a1.y);(a0.x+a0.width-a1.x>a1.width)&&(a1.width=a0.x+a0.width-a1.x);}}E&&this.hide();return a1;};ax[aY].attr=function(){if(this.removed){return this;}if(arguments[m]==0){var R={};for(var E in this.attrs){if(this.attrs[Q](E)){R[E]=this.attrs[E];}}this._.rt.deg&&(R.rotation=this.rotate());(this._.sx!=1||this._.sy!=1)&&(R.scale=this.scale());R.gradient&&R.fill=="none"&&(R.fill=R.gradient)&&delete R.gradient;return R;}if(arguments[m]==1&&an.is(arguments[0],"string")){if(arguments[0]=="translation"){return t.call(this);}if(arguments[0]=="rotation"){return this.rotate();}if(arguments[0]=="scale"){return this.scale();}if(arguments[0]=="fill"&&this.attrs.fill=="none"&&this.attrs.gradient){return this.attrs.gradient;}return this.attrs[arguments[0]];}if(arguments[m]==1&&an.is(arguments[0],"array")){var d={};for(var e in arguments[0]){if(arguments[0][Q](e)){d[arguments[0][e]]=this.attrs[arguments[0][e]];}}return d;}if(arguments[m]==2){var S={};S[arguments[0]]=arguments[1];aa(this,S);}else{if(arguments[m]==1&&an.is(arguments[0],"object")){aa(this,arguments[0]);}}return this;};ax[aY].toFront=function(){if(this.removed){return this;}this.node.parentNode[aL](this.node);var d=this.paper;d.top!=this&&Y(this,d);return this;};ax[aY].toBack=function(){if(this.removed){return this;}if(this.node.parentNode.firstChild!=this.node){this.node.parentNode.insertBefore(this.node,this.node.parentNode.firstChild);k(this,this.paper);var d=this.paper;}return this;};ax[aY].insertAfter=function(d){if(this.removed){return this;}var e=d.node;if(e.nextSibling){e.parentNode.insertBefore(this.node,e.nextSibling);}else{e.parentNode[aL](this.node);}A(this,d,this.paper);return this;};ax[aY].insertBefore=function(d){if(this.removed){return this;}var e=d.node;e.parentNode.insertBefore(this.node,e);aq(this,d,this.paper);return this;};var P=function(e,d,S,R){d=O(d);S=O(S);var E=aJ("circle");e.canvas&&e.canvas[aL](E);var i=new ax(E,e);i.attrs={cx:d,cy:S,r:R,fill:"none",stroke:"#000"};i.type="circle";aJ(E,i.attrs);return i;};var aF=function(i,d,a1,e,S,a0){d=O(d);a1=O(a1);var R=aJ("rect");i.canvas&&i.canvas[aL](R);var E=new ax(R,i);E.attrs={x:d,y:a1,width:e,height:S,r:a0||0,rx:a0||0,ry:a0||0,fill:"none",stroke:"#000"};E.type="rect";aJ(R,E.attrs);return E;};var ai=function(e,d,a0,S,R){d=O(d);a0=O(a0);var E=aJ("ellipse");e.canvas&&e.canvas[aL](E);var i=new ax(E,e);i.attrs={cx:d,cy:a0,rx:S,ry:R,fill:"none",stroke:"#000"};i.type="ellipse";aJ(E,i.attrs);return i;};var o=function(i,a0,d,a1,e,S){var R=aJ("image");aJ(R,{x:d,y:a1,width:e,height:S,preserveAspectRatio:"none"});R.setAttributeNS(i.xlink,"href",a0);i.canvas&&i.canvas[aL](R);var E=new ax(R,i);E.attrs={x:d,y:a1,width:e,height:S,src:a0};E.type="image";return E;};var X=function(e,d,S,R){var E=aJ("text");aJ(E,{x:d,y:S,"text-anchor":"middle"});e.canvas&&e.canvas[aL](E);var i=new ax(E,e);i.attrs={x:d,y:S,"text-anchor":"middle",text:R,font:j.font,stroke:"none",fill:"#000"};i.type="text";aa(i,i.attrs);return i;};var aV=function(e,d){this.width=e||this.width;this.height=d||this.height;this.canvas[v]("width",this.width);this.canvas[v]("height",this.height);return this;};var w=function(){var E=ao[aW](null,arguments),i=E&&E.container,e=E.x,a0=E.y,R=E.width,d=E.height;if(!i){throw new Error("SVG container not found.");}var S=aJ("svg");R=R||512;d=d||342;aJ(S,{xmlns:"http://www.w3.org/2000/svg",version:1.1,width:R,height:d});if(i==1){S.style.cssText="position:absolute;left:"+e+"px;top:"+a0+"px";L.body[aL](S);}else{if(i.firstChild){i.insertBefore(S,i.firstChild);}else{i[aL](S);}}i=new aT;i.width=R;i.height=d;i.canvas=S;aG.call(i,i,an.fn);i.clear();return i;};aT[aY].clear=function(){var d=this.canvas;while(d.firstChild){d.removeChild(d.firstChild);}this.bottom=this.top=null;(this.desc=aJ("desc"))[aL](L.createTextNode("Created with Rapha\xebl"));d[aL](this.desc);d[aL](this.defs=aJ("defs"));};aT[aY].remove=function(){this.canvas.parentNode&&this.canvas.parentNode.removeChild(this.canvas);for(var d in this){this[d]=s(d);}};}if(an.vml){var aH=function(a8){var a5=/[ahqstv]/ig,a0=r;(a8+at).match(a5)&&(a0=H);a5=/[clmz]/g;if(a0==r&&!(a8+at).match(a5)){var e={M:"m",L:"l",C:"c",Z:"x",m:"t",l:"r",c:"v",z:"x"},R=/([clmz]),?([^clmz]*)/gi,S=/-?[^,\s-]+/g;var a4=(a8+at)[aP](R,function(a9,bb,i){var ba=[];i[aP](S,function(bc){ba[f](O(bc));});return e[bb]+ba;});return a4;}var a6=a0(a8),E,a4=[],d;for(var a2=0,a7=a6[m];a2<a7;a2++){E=a6[a2];d=aZ.call(a6[a2][0]);d=="z"&&(d="x");for(var a1=1,a3=E[m];a1<a3;a1++){d+=O(E[a1])+(a1!=a3-1?",":at);}a4[f](d);}return a4[az](am);};an[aA]=function(){return"Your browser doesn\u2019t support SVG. Falling down to VML.\nYou are running Rapha\xebl "+this.version;};var q=function(d,S){var E=ah("group");E.style.cssText="position:absolute;left:0;top:0;width:"+S.width+"px;height:"+S.height+"px";E.coordsize=S.coordsize;E.coordorigin=S.coordorigin;var i=ah("shape"),e=i.style;e.width=S.width+"px";e.height=S.height+"px";i.coordsize=this.coordsize;i.coordorigin=this.coordorigin;E[aL](i);var R=new ax(i,E,S);R.isAbsolute=true;R.type="path";R.path=[];R.Path=at;d&&aa(R,{fill:"none",stroke:"#000",path:d});S.canvas[aL](E);return R;};var aa=function(a3,a8){a3.attrs=a3.attrs||{};var a6=a3.node,a9=a3.attrs,a0=a6.style,E,bd=a3;for(var a1 in a8){if(a8[Q](a1)){a9[a1]=a8[a1];}}a8.href&&(a6.href=a8.href);a8.title&&(a6.title=a8.title);a8.target&&(a6.target=a8.target);a8.cursor&&(a0.cursor=a8.cursor);if(a8.path&&a3.type=="path"){a9.path=a8.path;a6.path=aH(a9.path);}if(a8.rotation!=null){a3.rotate(a8.rotation,true);}if(a8.translation){E=(a8.translation+at)[z](a);t.call(a3,E[0],E[1]);if(a3._.rt.cx!=null){a3._.rt.cx+=+E[0];a3._.rt.cy+=+E[1];a3.setBox(a3.attrs,E[0],E[1]);}}if(a8.scale){E=(a8.scale+at)[z](a);a3.scale(+E[0]||1,+E[1]||+E[0]||1,+E[2]||null,+E[3]||null);}if("clip-rect" in a8){var d=(a8["clip-rect"]+at)[z](a);if(d[m]==4){d[2]=+d[2]+(+d[0]);d[3]=+d[3]+(+d[1]);var a2=a6.clipRect||L.createElement("div"),bc=a2.style,S=a6.parentNode;bc.clip=an.format("rect({1}px {2}px {3}px {0}px)",d);if(!a6.clipRect){bc.position="absolute";bc.top=0;bc.left=0;bc.width=a3.paper.width+"px";bc.height=a3.paper.height+"px";S.parentNode.insertBefore(a2,S);a2[aL](S);a6.clipRect=a2;}}if(!a8["clip-rect"]){a6.clipRect&&(a6.clipRect.style.clip=at);}}if(a3.type=="image"&&a8.src){a6.src=a8.src;}if(a3.type=="image"&&a8.opacity){a6.filterOpacity=" progid:DXImageTransform.Microsoft.Alpha(opacity="+(a8.opacity*100)+")";a0.filter=(a6.filterMatrix||at)+(a6.filterOpacity||at);}a8.font&&(a0.font=a8.font);a8["font-family"]&&(a0.fontFamily='"'+a8["font-family"][z](",")[0][aP](/^['"]+|['"]+$/g,at)+'"');a8["font-size"]&&(a0.fontSize=a8["font-size"]);a8["font-weight"]&&(a0.fontWeight=a8["font-weight"]);a8["font-style"]&&(a0.fontStyle=a8["font-style"]);if(a8.opacity!=null||a8["stroke-width"]!=null||a8.fill!=null||a8.stroke!=null||a8["stroke-width"]!=null||a8["stroke-opacity"]!=null||a8["fill-opacity"]!=null||a8["stroke-dasharray"]!=null||a8["stroke-miterlimit"]!=null||a8["stroke-linejoin"]!=null||a8["stroke-linecap"]!=null){a6=a3.shape||a6;var a7=(a6.getElementsByTagName("fill")&&a6.getElementsByTagName("fill")[0]),ba=false;!a7&&(ba=a7=ah("fill"));if("fill-opacity" in a8||"opacity" in a8){var e=((+a9["fill-opacity"]+1||2)-1)*((+a9.opacity+1||2)-1);e<0&&(e=0);e>1&&(e=1);a7.opacity=e;}a8.fill&&(a7.on=true);if(a7.on==null||a8.fill=="none"){a7.on=false;}if(a7.on&&a8.fill){var i=a8.fill.match(c);if(i){a7.src=i[1];a7.type="tile";}else{a7.color=an.getRGB(a8.fill).hex;a7.src=at;a7.type="solid";if(an.getRGB(a8.fill).error&&(bd.type in {circle:1,ellipse:1}||(a8.fill+at).charAt()!="r")&&b(bd,a8.fill)){a9.fill="none";a9.gradient=a8.fill;}}}ba&&a6[aL](a7);var R=(a6.getElementsByTagName("stroke")&&a6.getElementsByTagName("stroke")[0]),bb=false;!R&&(bb=R=ah("stroke"));if((a8.stroke&&a8.stroke!="none")||a8["stroke-width"]||a8["stroke-opacity"]!=null||a8["stroke-dasharray"]||a8["stroke-miterlimit"]||a8["stroke-linejoin"]||a8["stroke-linecap"]){R.on=true;}(a8.stroke=="none"||R.on==null||a8.stroke==0||a8["stroke-width"]==0)&&(R.on=false);R.on&&a8.stroke&&(R.color=an.getRGB(a8.stroke).hex);var e=((+a9["stroke-opacity"]+1||2)-1)*((+a9.opacity+1||2)-1),a4=(W(a8["stroke-width"])||1)*0.75;e<0&&(e=0);e>1&&(e=1);a8["stroke-width"]==null&&(a4=a9["stroke-width"]);a8["stroke-width"]&&(R.weight=a4);a4&&a4<1&&(e*=a4)&&(R.weight=1);R.opacity=e;a8["stroke-linejoin"]&&(R.joinstyle=a8["stroke-linejoin"]||"miter");R.miterlimit=a8["stroke-miterlimit"]||8;a8["stroke-linecap"]&&(R.endcap=a8["stroke-linecap"]=="butt"?"flat":a8["stroke-linecap"]=="square"?"square":"round");if(a8["stroke-dasharray"]){var a5={"-":"shortdash",".":"shortdot","-.":"shortdashdot","-..":"shortdashdotdot",". ":"dot","- ":"dash","--":"longdash","- .":"dashdot","--.":"longdashdot","--..":"longdashdotdot"};R.dashstyle=a5[Q](a8["stroke-dasharray"])?a5[a8["stroke-dasharray"]]:at;}bb&&a6[aL](R);}if(bd.type=="text"){var a0=bd.paper.span.style;a9.font&&(a0.font=a9.font);a9["font-family"]&&(a0.fontFamily=a9["font-family"]);a9["font-size"]&&(a0.fontSize=a9["font-size"]);a9["font-weight"]&&(a0.fontWeight=a9["font-weight"]);a9["font-style"]&&(a0.fontStyle=a9["font-style"]);bd.node.string&&(bd.paper.span.innerHTML=(bd.node.string+at)[aP](/</g,"&#60;")[aP](/&/g,"&#38;")[aP](/\n/g,"<br>"));bd.W=a9.w=bd.paper.span.offsetWidth;bd.H=a9.h=bd.paper.span.offsetHeight;bd.X=a9.x;bd.Y=a9.y+O(bd.H/2);switch(a9["text-anchor"]){case"start":bd.node.style["v-text-align"]="left";bd.bbx=O(bd.W/2);break;case"end":bd.node.style["v-text-align"]="right";bd.bbx=-O(bd.W/2);break;default:bd.node.style["v-text-align"]="center";break;}}};var b=function(d,a1){d.attrs=d.attrs||{};var a2=d.attrs,a4=d.node.getElementsByTagName("fill"),S="linear",a0=".5 .5";d.attrs.gradient=a1;a1=(a1+at)[aP](ar,function(a6,a7,i){S="radial";if(a7&&i){a7=W(a7);i=W(i);aM(a7-0.5,2)+aM(i-0.5,2)>0.25&&(i=ab.sqrt(0.25-aM(a7-0.5,2))*((i>0.5)*2-1)+0.5);a0=a7+am+i;}return at;});a1=a1[z](/\s*\-\s*/);if(S=="linear"){var e=a1.shift();e=-W(e);if(isNaN(e)){return null;}}var R=p(a1);if(!R){return null;}d=d.shape||d.node;a4=a4[0]||ah("fill");if(R[m]){a4.on=true;a4.method="none";a4.type=(S=="radial")?"gradientradial":"gradient";a4.color=R[0].color;a4.color2=R[R[m]-1].color;var a5=[];for(var E=0,a3=R[m];E<a3;E++){R[E].offset&&a5[f](R[E].offset+am+R[E].color);}a4.colors&&(a4.colors.value=a5[m]?a5[az](","):"0% "+a4.color);if(S=="radial"){a4.focus="100%";a4.focussize=a0;a4.focusposition=a0;}else{a4.angle=(270-e)%360;}}return 1;};var ax=function(R,a0,d){var S=0,i=0,e=0,E=1;this[0]=R;this.id=an._oid++;this.node=R;R.raphael=this;this.X=0;this.Y=0;this.attrs={};this.Group=a0;this.paper=d;this._={tx:0,ty:0,rt:{deg:0},sx:1,sy:1};!d.bottom&&(d.bottom=this);this.prev=d.top;d.top&&(d.top.next=this);d.top=this;this.next=null;};ax[aY].rotate=function(e,d,i){if(this.removed){return this;}if(e==null){if(this._.rt.cx){return[this._.rt.deg,this._.rt.cx,this._.rt.cy][az](am);}return this._.rt.deg;}e=(e+at)[z](a);if(e[m]-1){d=W(e[1]);i=W(e[2]);}e=W(e[0]);if(d!=null){this._.rt.deg=e;}else{this._.rt.deg+=e;}i==null&&(d=null);this._.rt.cx=d;this._.rt.cy=i;this.setBox(this.attrs,d,i);this.Group.style.rotation=this._.rt.deg;return this;};ax[aY].setBox=function(bb,e,d){if(this.removed){return this;}var a5=this.Group.style,R=(this.shape&&this.shape.style)||this.node.style;bb=bb||{};for(var a9 in bb){if(bb[Q](a9)){this.attrs[a9]=bb[a9];}}e=e||this._.rt.cx;d=d||this._.rt.cy;var a7=this.attrs,a1,a0,a2,ba;switch(this.type){case"circle":a1=a7.cx-a7.r;a0=a7.cy-a7.r;a2=ba=a7.r*2;break;case"ellipse":a1=a7.cx-a7.rx;a0=a7.cy-a7.ry;a2=a7.rx*2;ba=a7.ry*2;break;case"rect":case"image":a1=+a7.x;a0=+a7.y;a2=a7.width||0;ba=a7.height||0;break;case"text":this.textpath.v=["m",O(a7.x),", ",O(a7.y-2),"l",O(a7.x)+1,", ",O(a7.y-2)][az](at);a1=a7.x-O(this.W/2);a0=a7.y-this.H/2;a2=this.W;ba=this.H;break;case"path":if(!this.attrs.path){a1=0;a0=0;a2=this.paper.width;ba=this.paper.height;}else{var a8=U(this.attrs.path);a1=a8.x;a0=a8.y;a2=a8.width;ba=a8.height;}break;default:a1=0;a0=0;a2=this.paper.width;ba=this.paper.height;break;}e=(e==null)?a1+a2/2:e;d=(d==null)?a0+ba/2:d;var E=e-this.paper.width/2,a4=d-this.paper.height/2;if(this.type=="path"||this.type=="text"){(a5.left!=E+"px")&&(a5.left=E+"px");(a5.top!=a4+"px")&&(a5.top=a4+"px");this.X=this.type=="text"?a1:-E;this.Y=this.type=="text"?a0:-a4;this.W=a2;this.H=ba;(R.left!=-E+"px")&&(R.left=-E+"px");(R.top!=-a4+"px")&&(R.top=-a4+"px");}else{(a5.left!=E+"px")&&(a5.left=E+"px");(a5.top!=a4+"px")&&(a5.top=a4+"px");this.X=a1;this.Y=a0;this.W=a2;this.H=ba;(a5.width!=this.paper.width+"px")&&(a5.width=this.paper.width+"px");(a5.height!=this.paper.height+"px")&&(a5.height=this.paper.height+"px");(R.left!=a1-E+"px")&&(R.left=a1-E+"px");(R.top!=a0-a4+"px")&&(R.top=a0-a4+"px");(R.width!=a2+"px")&&(R.width=a2+"px");(R.height!=ba+"px")&&(R.height=ba+"px");var S=(+bb.r||0)/aI(a2,ba);if(this.type=="rect"&&this.arcsize.toFixed(4)!=S.toFixed(4)&&(S||this.arcsize)){var a6=ah("roundrect"),bc={},a9=0,a3=this.events&&this.events[m];a6.arcsize=S;a6.raphael=this;this.Group[aL](a6);this.Group.removeChild(this.node);this[0]=this.node=a6;this.arcsize=S;for(var a9 in a7){bc[a9]=a7[a9];}delete bc.scale;this.attr(bc);if(this.events){for(;a9<a3;a9++){this.events[a9].unbind=ae(this.node,this.events[a9].name,this.events[a9].f,this);}}}}};ax[aY].hide=function(){!this.removed&&(this.Group.style.display="none");return this;};ax[aY].show=function(){!this.removed&&(this.Group.style.display="block");return this;};ax[aY].getBBox=function(){if(this.removed){return this;}if(this.type=="path"){return U(this.attrs.path);}return{x:this.X+(this.bbx||0),y:this.Y,width:this.W,height:this.H};};ax[aY].remove=function(){if(this.removed){return;}ak(this,this.paper);this.node.parentNode.removeChild(this.node);this.Group.parentNode.removeChild(this.Group);this.shape&&this.shape.parentNode.removeChild(this.shape);for(var d in this){delete this[d];}this.removed=true;};ax[aY].attr=function(){if(this.removed){return this;}if(arguments[m]==0){var E={};for(var e in this.attrs){if(this.attrs[Q](e)){E[e]=this.attrs[e];}}this._.rt.deg&&(E.rotation=this.rotate());(this._.sx!=1||this._.sy!=1)&&(E.scale=this.scale());E.gradient&&E.fill=="none"&&(E.fill=E.gradient)&&delete E.gradient;return E;}if(arguments[m]==1&&an.is(arguments[0],"string")){if(arguments[0]=="translation"){return t.call(this);}if(arguments[0]=="rotation"){return this.rotate();}if(arguments[0]=="scale"){return this.scale();}if(arguments[0]=="fill"&&this.attrs.fill=="none"&&this.attrs.gradient){return this.attrs.gradient;}return this.attrs[arguments[0]];}if(this.attrs&&arguments[m]==1&&an.is(arguments[0],"array")){var d={};for(var e=0,R=arguments[0][m];e<R;e++){d[arguments[0][e]]=this.attrs[arguments[0][e]];}return d;}var S;if(arguments[m]==2){S={};S[arguments[0]]=arguments[1];}arguments[m]==1&&an.is(arguments[0],"object")&&(S=arguments[0]);if(S){if(S.text&&this.type=="text"){this.node.string=S.text;}aa(this,S);if(S.gradient&&(({circle:1,ellipse:1})[Q](this.type)||(S.gradient+at).charAt()!="r")){b(this,S.gradient);}(this.type!="path"||this._.rt.deg)&&this.setBox(this.attrs);}return this;};ax[aY].toFront=function(){!this.removed&&this.Group.parentNode[aL](this.Group);this.paper.top!=this&&Y(this,this.paper);return this;};ax[aY].toBack=function(){if(this.removed){return this;}if(this.Group.parentNode.firstChild!=this.Group){this.Group.parentNode.insertBefore(this.Group,this.Group.parentNode.firstChild);k(this,this.paper);}return this;};ax[aY].insertAfter=function(d){if(this.removed){return this;}if(d.Group.nextSibling){d.Group.parentNode.insertBefore(this.Group,d.Group.nextSibling);}else{d.Group.parentNode[aL](this.Group);}A(this,d,this.paper);return this;};ax[aY].insertBefore=function(d){if(this.removed){return this;}d.Group.parentNode.insertBefore(this.Group,d.Group);aq(this,d,this.paper);return this;};var P=function(e,d,a1,S){var R=ah("group"),a0=ah("oval"),i=a0.style;R.style.cssText="position:absolute;left:0;top:0;width:"+e.width+"px;height:"+e.height+"px";R.coordsize=e.coordsize;R.coordorigin=e.coordorigin;R[aL](a0);var E=new ax(a0,R,e);E.type="circle";aa(E,{stroke:"#000",fill:"none"});E.attrs.cx=d;E.attrs.cy=a1;E.attrs.r=S;E.setBox({x:d-S,y:a1-S,width:S*2,height:S*2});e.canvas[aL](R);return E;},aF=function(e,a1,a0,a2,E,d){var R=ah("group"),i=ah("roundrect"),a3=(+d||0)/(aI(a2,E));R.style.cssText="position:absolute;left:0;top:0;width:"+e.width+"px;height:"+e.height+"px";R.coordsize=e.coordsize;R.coordorigin=e.coordorigin;R[aL](i);i.arcsize=a3;var S=new ax(i,R,e);S.type="rect";aa(S,{stroke:"#000"});S.arcsize=a3;S.setBox({x:a1,y:a0,width:a2,height:E,r:d});e.canvas[aL](R);return S;},ai=function(d,a2,a1,i,e){var R=ah("group"),E=ah("oval"),a0=E.style;R.style.cssText="position:absolute;left:0;top:0;width:"+d.width+"px;height:"+d.height+"px";R.coordsize=d.coordsize;R.coordorigin=d.coordorigin;R[aL](E);var S=new ax(E,R,d);S.type="ellipse";aa(S,{stroke:"#000"});S.attrs.cx=a2;S.attrs.cy=a1;S.attrs.rx=i;S.attrs.ry=e;S.setBox({x:a2-i,y:a1-e,width:i*2,height:e*2});d.canvas[aL](R);return S;},o=function(e,d,a2,a1,a3,E){var R=ah("group"),i=ah("image"),a0=i.style;R.style.cssText="position:absolute;left:0;top:0;width:"+e.width+"px;height:"+e.height+"px";R.coordsize=e.coordsize;R.coordorigin=e.coordorigin;i.src=d;R[aL](i);var S=new ax(i,R,e);S.type="image";S.attrs.src=d;S.attrs.x=a2;S.attrs.y=a1;S.attrs.w=a3;S.attrs.h=E;S.setBox({x:a2,y:a1,width:a3,height:E});e.canvas[aL](R);return S;},X=function(e,a2,a1,a3){var R=ah("group"),E=ah("shape"),a0=E.style,a4=ah("path"),d=a4.style,i=ah("textpath");R.style.cssText="position:absolute;left:0;top:0;width:"+e.width+"px;height:"+e.height+"px";R.coordsize=e.coordsize;R.coordorigin=e.coordorigin;a4.v=an.format("m{0},{1}l{2},{1}",O(a2),O(a1),O(a2)+1);a4.textpathok=true;a0.width=e.width;a0.height=e.height;i.string=a3+at;i.on=true;E[aL](i);E[aL](a4);R[aL](E);var S=new ax(i,R,e);S.shape=E;S.textpath=a4;S.type="text";S.attrs.text=a3;S.attrs.x=a2;S.attrs.y=a1;S.attrs.w=1;S.attrs.h=1;aa(S,{font:j.font,stroke:"none",fill:"#000"});S.setBox();e.canvas[aL](R);return S;},aV=function(i,d){var e=this.canvas.style;i==+i&&(i+="px");d==+d&&(d+="px");e.width=i;e.height=d;e.clip="rect(0 "+i+" "+d+" 0)";return this;},ah;L.createStyleSheet().addRule(".rvml","behavior:url(#default#VML)");try{!L.namespaces.rvml&&L.namespaces.add("rvml","urn:schemas-microsoft-com:vml");ah=function(d){return L.createElement("<rvml:"+d+' class="rvml">');};}catch(af){ah=function(d){return L.createElement("<"+d+' xmlns="urn:schemas-microsoft.com:vml" class="rvml">');};}var w=function(){var i=ao[aW](null,arguments),d=i.container,a2=i.height,a3,e=i.width,a1=i.x,a0=i.y;if(!d){throw new Error("VML container not found.");}var R=new aT,S=R.canvas=L.createElement("div"),E=S.style;e=e||512;a2=a2||342;e==+e&&(e+="px");a2==+a2&&(a2+="px");R.width=1000;R.height=1000;R.coordsize="1000 1000";R.coordorigin="0 0";R.span=L.createElement("span");R.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";S[aL](R.span);E.cssText=an.format("width:{0};height:{1};position:absolute;clip:rect(0 {0} {1} 0);overflow:hidden",e,a2);if(d==1){L.body[aL](S);E.left=a1+"px";E.top=a0+"px";}else{d.style.width=e;d.style.height=a2;if(d.firstChild){d.insertBefore(S,d.firstChild);}else{d[aL](S);}}aG.call(R,R,an.fn);return R;};aT[aY].clear=function(){this.canvas.innerHTML=at;this.span=L.createElement("span");this.span.style.cssText="position:absolute;left:-9999em;top:-9999em;padding:0;margin:0;line-height:1;display:inline;";this.canvas[aL](this.span);this.bottom=this.top=null;};aT[aY].remove=function(){this.canvas.parentNode.removeChild(this.canvas);for(var d in this){this[d]=s(d);}};}if((/^Apple|^Google/).test(navigator.vendor)&&!(navigator.userAgent.indexOf("Version/4.0")+1)){aT[aY].safari=function(){var d=this.rect(-99,-99,this.width+99,this.height+99);setTimeout(function(){d.remove();});};}else{aT[aY].safari=function(){};}var ae=(function(){if(L.addEventListener){return function(R,i,e,d){var E=function(S){return e.call(d,S);};R.addEventListener(i,E,false);return function(){R.removeEventListener(i,E,false);return true;};};}else{if(L.attachEvent){return function(S,E,i,e){var R=function(a0){return i.call(e,a0||au.event);};S.attachEvent("on"+E,R);var d=function(){S.detachEvent("on"+E,R);return true;};return d;};}}})();for(var ac=F[m];ac--;){(function(d){ax[aY][d]=function(e){if(an.is(e,"function")){this.events=this.events||[];this.events.push({name:d,f:e,unbind:ae(this.shape||this.node,d,e,this)});}return this;};ax[aY]["un"+d]=function(E){var i=this.events,e=i[m];while(e--){if(i[e].name==d&&i[e].f==E){i[e].unbind();i.splice(e,1);!i.length&&delete this.events;return this;}}return this;};})(F[ac]);}ax[aY].hover=function(e,d){return this.mouseover(e).mouseout(d);};ax[aY].unhover=function(e,d){return this.unmouseover(e).unmouseout(d);};aT[aY].circle=function(d,i,e){return P(this,d||0,i||0,e||0);};aT[aY].rect=function(d,R,e,i,E){return aF(this,d||0,R||0,e||0,i||0,E||0);};aT[aY].ellipse=function(d,E,i,e){return ai(this,d||0,E||0,i||0,e||0);};aT[aY].path=function(d){d&&!an.is(d,"string")&&!an.is(d[0],"array")&&(d+=at);return q(an.format[aW](an,arguments),this);};aT[aY].image=function(E,d,R,e,i){return o(this,E||"about:blank",d||0,R||0,e||0,i||0);};aT[aY].text=function(d,i,e){return X(this,d||0,i||0,e||at);};aT[aY].set=function(d){arguments[m]>1&&(d=Array[aY].splice.call(arguments,0,arguments[m]));return new T(d);};aT[aY].setSize=aV;aT[aY].top=aT[aY].bottom=null;aT[aY].raphael=an;function u(){return this.x+am+this.y;}ax[aY].scale=function(a6,a5,E,e){if(a6==null&&a5==null){return{x:this._.sx,y:this._.sy,toString:u};}a5=a5||a6;!+a5&&(a5=a6);var ba,a8,a9,a7,bm=this.attrs;if(a6!=0){var a4=this.getBBox(),a1=a4.x+a4.width/2,R=a4.y+a4.height/2,bl=a6/this._.sx,bk=a5/this._.sy;E=(+E||E==0)?E:a1;e=(+e||e==0)?e:R;var a3=~~(a6/ab.abs(a6)),a0=~~(a5/ab.abs(a5)),be=this.node.style,bo=E+(a1-E)*bl,bn=e+(R-e)*bk;switch(this.type){case"rect":case"image":var a2=bm.width*a3*bl,bd=bm.height*a0*bk;this.attr({height:bd,r:bm.r*aI(a3*bl,a0*bk),width:a2,x:bo-a2/2,y:bn-bd/2});break;case"circle":case"ellipse":this.attr({rx:bm.rx*a3*bl,ry:bm.ry*a0*bk,r:bm.r*aI(a3*bl,a0*bk),cx:bo,cy:bn});break;case"path":var bg=ad(bm.path),bh=true;for(var bj=0,bc=bg[m];bj<bc;bj++){var bf=bg[bj],bi,S=aN.call(bf[0]);if(S=="M"&&bh){continue;}else{bh=false;}if(S=="A"){bf[bg[bj][m]-2]*=bl;bf[bg[bj][m]-1]*=bk;bf[1]*=a3*bl;bf[2]*=a0*bk;bf[5]=+(a3+a0?!!+bf[5]:!+bf[5]);}else{if(S=="H"){for(bi=1,jj=bf[m];bi<jj;bi++){bf[bi]*=bl;}}else{if(S=="V"){for(bi=1,jj=bf[m];bi<jj;bi++){bf[bi]*=bk;}}else{for(bi=1,jj=bf[m];bi<jj;bi++){bf[bi]*=(bi%2)?bl:bk;}}}}}var d=U(bg),ba=bo-d.x-d.width/2,a8=bn-d.y-d.height/2;bg[0][1]+=ba;bg[0][2]+=a8;this.attr({path:bg});break;}if(this.type in {text:1,image:1}&&(a3!=1||a0!=1)){if(this.transformations){this.transformations[2]="scale("[aS](a3,",",a0,")");this.node[v]("transform",this.transformations[az](am));ba=(a3==-1)?-bm.x-(a2||0):bm.x;a8=(a0==-1)?-bm.y-(bd||0):bm.y;this.attr({x:ba,y:a8});bm.fx=a3-1;bm.fy=a0-1;}else{this.node.filterMatrix=" progid:DXImageTransform.Microsoft.Matrix(M11="[aS](a3,", M12=0, M21=0, M22=",a0,", Dx=0, Dy=0, sizingmethod='auto expand', filtertype='bilinear')");be.filter=(this.node.filterMatrix||at)+(this.node.filterOpacity||at);}}else{if(this.transformations){this.transformations[2]=at;this.node[v]("transform",this.transformations[az](am));bm.fx=0;bm.fy=0;}else{this.node.filterMatrix=at;be.filter=(this.node.filterMatrix||at)+(this.node.filterOpacity||at);}}bm.scale=[a6,a5,E,e][az](am);this._.sx=a6;this._.sy=a5;}return this;};ax[aY].clone=function(){var d=this.attr();delete d.scale;delete d.translation;return this.paper[this.type]().attr(d);};var aB=function(d,e){return function(a9,S,a0){a9=H(a9);var a5,a4,E,a1,R="",a8={},a6,a3=0;for(var a2=0,a7=a9.length;a2<a7;a2++){E=a9[a2];if(E[0]=="M"){a5=+E[1];a4=+E[2];}else{a1=n(a5,a4,E[1],E[2],E[3],E[4],E[5],E[6]);if(a3+a1>S){if(e&&!a8.start){a6=an.findDotsAtSegment(a5,a4,E[1],E[2],E[3],E[4],E[5],E[6],(S-a3)/a1);R+=["C",a6.start.x,a6.start.y,a6.m.x,a6.m.y,a6.x,a6.y];if(a0){return R;}a8.start=R;R=["M",a6.x,a6.y+"C",a6.n.x,a6.n.y,a6.end.x,a6.end.y,E[5],E[6]][az]();a3+=a1;a5=+E[5];a4=+E[6];continue;}if(!d&&!e){a6=an.findDotsAtSegment(a5,a4,E[1],E[2],E[3],E[4],E[5],E[6],(S-a3)/a1);return{x:a6.x,y:a6.y,alpha:a6.alpha};}}a3+=a1;a5=+E[5];a4=+E[6];}R+=E;}a8.end=R;a6=d?a3:e?a8:an.findDotsAtSegment(a5,a4,E[1],E[2],E[3],E[4],E[5],E[6],1);a6.alpha&&(a6={x:a6.x,y:a6.y,alpha:a6.alpha});return a6;};},n=aj(function(E,d,a0,S,a6,a5,a4,a3){var R={x:0,y:0},a2=0;for(var a1=0;a1<1.01;a1+=0.01){var e=M(E,d,a0,S,a6,a5,a4,a3,a1);a1&&(a2+=ab.sqrt(aM(R.x-e.x,2)+aM(R.y-e.y,2)));R=e;}return a2;});var ap=aB(1),C=aB(),J=aB(0,1);ax[aY].getTotalLength=function(){if(this.type!="path"){return;}return ap(this.attrs.path);};ax[aY].getPointAtLength=function(d){if(this.type!="path"){return;}return C(this.attrs.path,d);};ax[aY].getSubpath=function(i,e){if(this.type!="path"){return;}if(ab.abs(this.getTotalLength()-e)<0.000001){return J(this.attrs.path,i).end;}var d=J(this.attrs.path,e,1);return i?J(d,i).end:d;};an.easing_formulas={linear:function(d){return d;},"<":function(d){return aM(d,3);},">":function(d){return aM(d-1,3)+1;},"<>":function(d){d=d*2;if(d<1){return aM(d,3)/2;}d-=2;return(aM(d,3)+2)/2;},backIn:function(e){var d=1.70158;return e*e*((d+1)*e-d);},backOut:function(e){e=e-1;var d=1.70158;return e*e*((d+1)*e+d)+1;},elastic:function(i){if(i==0||i==1){return i;}var e=0.3,d=e/4;return aM(2,-10*i)*ab.sin((i-d)*(2*ab.PI)/e)+1;},bounce:function(E){var e=7.5625,i=2.75,d;if(E<(1/i)){d=e*E*E;}else{if(E<(2/i)){E-=(1.5/i);d=e*E*E+0.75;}else{if(E<(2.5/i)){E-=(2.25/i);d=e*E*E+0.9375;}else{E-=(2.625/i);d=e*E*E+0.984375;}}}return d;}};var I={length:0},aR=function(){var a2=+new Date;for(var be in I){if(be!="length"&&I[Q](be)){var bj=I[be];if(bj.stop){delete I[be];I[m]--;continue;}var a0=a2-bj.start,bb=bj.ms,ba=bj.easing,bf=bj.from,a7=bj.diff,E=bj.to,a6=bj.t,a9=bj.prev||0,a1=bj.el,R=bj.callback,a8={},d;if(a0<bb){var S=an.easing_formulas[ba]?an.easing_formulas[ba](a0/bb):a0/bb;for(var bc in bf){if(bf[Q](bc)){switch(Z[bc]){case"along":d=S*bb*a7[bc];E.back&&(d=E.len-d);var bd=C(E[bc],d);a1.translate(a7.sx-a7.x||0,a7.sy-a7.y||0);a7.x=bd.x;a7.y=bd.y;a1.translate(bd.x-a7.sx,bd.y-a7.sy);E.rot&&a1.rotate(a7.r+bd.alpha,bd.x,bd.y);break;case"number":d=+bf[bc]+S*bb*a7[bc];break;case"colour":d="rgb("+[B(O(bf[bc].r+S*bb*a7[bc].r)),B(O(bf[bc].g+S*bb*a7[bc].g)),B(O(bf[bc].b+S*bb*a7[bc].b))][az](",")+")";break;case"path":d=[];for(var bh=0,a5=bf[bc][m];bh<a5;bh++){d[bh]=[bf[bc][bh][0]];for(var bg=1,bi=bf[bc][bh][m];bg<bi;bg++){d[bh][bg]=+bf[bc][bh][bg]+S*bb*a7[bc][bh][bg];}d[bh]=d[bh][az](am);}d=d[az](am);break;case"csv":switch(bc){case"translation":var a4=a7[bc][0]*(a0-a9),a3=a7[bc][1]*(a0-a9);a6.x+=a4;a6.y+=a3;d=a4+am+a3;break;case"rotation":d=+bf[bc][0]+S*bb*a7[bc][0];bf[bc][1]&&(d+=","+bf[bc][1]+","+bf[bc][2]);break;case"scale":d=[+bf[bc][0]+S*bb*a7[bc][0],+bf[bc][1]+S*bb*a7[bc][1],(2 in E[bc]?E[bc][2]:at),(3 in E[bc]?E[bc][3]:at)][az](am);break;case"clip-rect":d=[];var bh=4;while(bh--){d[bh]=+bf[bc][bh]+S*bb*a7[bc][bh];}break;}break;}a8[bc]=d;}}a1.attr(a8);a1._run&&a1._run.call(a1);}else{if(E.along){var bd=C(E.along,E.len*!E.back);a1.translate(a7.sx-(a7.x||0)+bd.x-a7.sx,a7.sy-(a7.y||0)+bd.y-a7.sy);E.rot&&a1.rotate(a7.r+bd.alpha,bd.x,bd.y);}(a6.x||a6.y)&&a1.translate(-a6.x,-a6.y);E.scale&&(E.scale=E.scale+at);a1.attr(E);delete I[be];I[m]--;a1.in_animation=null;an.is(R,"function")&&R.call(a1);}bj.prev=a0;}}an.svg&&a1&&a1.paper.safari();I[m]&&setTimeout(aR);},B=function(d){return d>255?255:(d<0?0:d);},t=function(d,i){if(d==null){return{x:this._.tx,y:this._.ty,toString:u};}this._.tx+=+d;this._.ty+=+i;switch(this.type){case"circle":case"ellipse":this.attr({cx:+d+this.attrs.cx,cy:+i+this.attrs.cy});break;case"rect":case"image":case"text":this.attr({x:+d+this.attrs.x,y:+i+this.attrs.y});break;case"path":var e=ad(this.attrs.path);e[0][1]+=+d;e[0][2]+=+i;this.attr({path:e});break;}return this;};ax[aY].animateWith=function(e,i,d,R,E){I[e.id]&&(i.start=I[e.id].start);return this.animate(i,d,R,E);};ax[aY].animateAlong=ay();ax[aY].animateAlongBack=ay(1);function ay(d){return function(E,i,e,S){var R={back:d};an.is(e,"function")?(S=e):(R.rot=e);E&&E.constructor==ax&&(E=E.attrs.path);E&&(R.along=E);return this.animate(R,i,S);};}ax[aY].onAnimation=function(d){this._run=d||0;return this;};ax[aY].animate=function(be,a5,a4,E){if(an.is(a4,"function")||!a4){E=a4||null;}var a9={},e={},a2={};for(var a6 in be){if(be[Q](a6)){if(Z[Q](a6)){a9[a6]=this.attr(a6);(a9[a6]==null)&&(a9[a6]=j[a6]);e[a6]=be[a6];switch(Z[a6]){case"along":var bc=ap(be[a6]),a7=C(be[a6],bc*!!be.back),R=this.getBBox();a2[a6]=bc/a5;a2.tx=R.x;a2.ty=R.y;a2.sx=a7.x;a2.sy=a7.y;e.rot=be.rot;e.back=be.back;e.len=bc;be.rot&&(a2.r=W(this.rotate())||0);break;case"number":a2[a6]=(e[a6]-a9[a6])/a5;break;case"colour":a9[a6]=an.getRGB(a9[a6]);var a8=an.getRGB(e[a6]);a2[a6]={r:(a8.r-a9[a6].r)/a5,g:(a8.g-a9[a6].g)/a5,b:(a8.b-a9[a6].b)/a5};break;case"path":var S=H(a9[a6],e[a6]);a9[a6]=S[0];var a3=S[1];a2[a6]=[];for(var bb=0,a1=a9[a6][m];bb<a1;bb++){a2[a6][bb]=[0];for(var ba=1,bd=a9[a6][bb][m];ba<bd;ba++){a2[a6][bb][ba]=(a3[bb][ba]-a9[a6][bb][ba])/a5;}}break;case"csv":var d=(be[a6]+at)[z](a),a0=(a9[a6]+at)[z](a);switch(a6){case"translation":a9[a6]=[0,0];a2[a6]=[d[0]/a5,d[1]/a5];break;case"rotation":a9[a6]=(a0[1]==d[1]&&a0[2]==d[2])?a0:[0,d[1],d[2]];a2[a6]=[(d[0]-a9[a6][0])/a5,0,0];break;case"scale":be[a6]=d;a9[a6]=(a9[a6]+at)[z](a);a2[a6]=[(d[0]-a9[a6][0])/a5,(d[1]-a9[a6][1])/a5,0,0];break;case"clip-rect":a9[a6]=(a9[a6]+at)[z](a);a2[a6]=[];var bb=4;while(bb--){a2[a6][bb]=(d[bb]-a9[a6][bb])/a5;}break;}e[a6]=d;}}}}this.stop();this.in_animation=1;I[this.id]={start:be.start||+new Date,ms:a5,easing:a4,from:a9,diff:a2,to:e,el:this,callback:E,t:{x:0,y:0}};++I[m]==1&&aR();return this;};ax[aY].stop=function(){I[this.id]&&I[m]--;delete I[this.id];return this;};ax[aY].translate=function(d,e){return this.attr({translation:d+" "+e});};ax[aY][aA]=function(){return"Rapha\xebl\u2019s object";};an.ae=I;var T=function(d){this.items=[];this[m]=0;if(d){for(var e=0,E=d[m];e<E;e++){if(d[e]&&(d[e].constructor==ax||d[e].constructor==T)){this[this.items[m]]=this.items[this.items[m]]=d[e];this[m]++;}}}};T[aY][f]=function(){var R,d;for(var e=0,E=arguments[m];e<E;e++){R=arguments[e];if(R&&(R.constructor==ax||R.constructor==T)){d=this.items[m];this[d]=this.items[d]=R;this[m]++;}}return this;};T[aY].pop=function(){delete this[this[m]--];return this.items.pop();};for(var y in ax[aY]){if(ax[aY][Q](y)){T[aY][y]=(function(d){return function(){for(var e=0,E=this.items[m];e<E;e++){this.items[e][d][aW](this.items[e],arguments);}return this;};})(y);}}T[aY].attr=function(e,a0){if(e&&an.is(e,"array")&&an.is(e[0],"object")){for(var d=0,S=e[m];d<S;d++){this.items[d].attr(e[d]);}}else{for(var E=0,R=this.items[m];E<R;E++){this.items[E].attr[aW](this.items[E],arguments);}}return this;};T[aY].animate=function(S,e,a2,a1){(an.is(a2,"function")||!a2)&&(a1=a2||null);var d=this.items[m],E=d,a0=this,R;a1&&(R=function(){!--d&&a1.call(a0);});this.items[--E].animate(S,e,a2||R,R);while(E--){this.items[E].animateWith(this.items[d-1],S,e,a2||R,R);}return this;};T[aY].insertAfter=function(e){var d=this.items[m];while(d--){this.items[d].insertAfter(e);}return this;};T[aY].getBBox=function(){var d=[],a0=[],e=[],R=[];for(var E=this.items[m];E--;){var S=this.items[E].getBBox();d[f](S.x);a0[f](S.y);e[f](S.x+S.width);R[f](S.y+S.height);}d=aI[aW](0,d);a0=aI[aW](0,a0);return{x:d,y:a0,width:g[aW](0,e)-d,height:g[aW](0,R)-a0};};an.registerFont=function(e){if(!e.face){return e;}this.fonts=this.fonts||{};var E={w:e.w,face:{},glyphs:{}},i=e.face["font-family"];for(var a0 in e.face){if(e.face[Q](a0)){E.face[a0]=e.face[a0];}}if(this.fonts[i]){this.fonts[i][f](E);}else{this.fonts[i]=[E];}if(!e.svg){E.face["units-per-em"]=G(e.face["units-per-em"],10);for(var R in e.glyphs){if(e.glyphs[Q](R)){var S=e.glyphs[R];E.glyphs[R]={w:S.w,k:{},d:S.d&&"M"+S.d[aP](/[mlcxtrv]/g,function(a1){return{l:"L",c:"C",x:"z",t:"m",r:"l",v:"c"}[a1]||"M";})+"z"};if(S.k){for(var d in S.k){if(S[Q](d)){E.glyphs[R].k[d]=S.k[d];}}}}}}return e;};aT[aY].getFont=function(a2,a3,e,R){R=R||"normal";e=e||"normal";a3=+a3||{normal:400,bold:700,lighter:300,bolder:800}[a3]||400;var S=an.fonts[a2];if(!S){var E=new RegExp("(^|\\s)"+a2[aP](/[^\w\d\s+!~.:_-]/g,at)+"(\\s|$)","i");for(var d in an.fonts){if(an.fonts[Q](d)){if(E.test(d)){S=an.fonts[d];break;}}}}var a0;if(S){for(var a1=0,a4=S[m];a1<a4;a1++){a0=S[a1];if(a0.face["font-weight"]==a3&&(a0.face["font-style"]==e||!a0.face["font-style"])&&a0.face["font-stretch"]==R){break;}}}return a0;};aT[aY].print=function(R,E,d,a1,a2,bb){bb=bb||"middle";var a7=this.set(),ba=(d+at)[z](at),a8=0,a4=at,bc;an.is(a1,"string")&&(a1=this.getFont(a1));if(a1){bc=(a2||16)/a1.face["units-per-em"];var e=a1.face.bbox.split(a),a0=+e[0],a3=+e[1]+(bb=="baseline"?e[3]-e[1]+(+a1.face.descent):(e[3]-e[1])/2);for(var a6=0,S=ba[m];a6<S;a6++){var a5=a6&&a1.glyphs[ba[a6-1]]||{},a9=a1.glyphs[ba[a6]];a8+=a6?(a5.w||a1.w)+(a5.k&&a5.k[ba[a6]]||0):0;a9&&a9.d&&a7[f](this.path(a9.d).attr({fill:"#000",stroke:"none",translation:[a8,0]}));}a7.scale(bc,bc,a0,a3).translate(R-a0,E-a3);}return a7;};an.format=function(i){var e=an.is(arguments[1],"array")?[0][aS](arguments[1]):arguments,d=/\{(\d+)\}/g;i&&an.is(i,"string")&&e[m]-1&&(i=i[aP](d,function(R,E){return e[++E]==null?at:e[E];}));return i||at;};an.ninja=function(){var d=Raphael;if(l.was){Raphael=l.is;}else{delete Raphael;}return d;};an.el=ax[aY];return an;})();
\ No newline at end of file
diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/seedrandom.js b/luci-app-bmx6/files/www/luci-static/resources/bmx6/js/seedrandom.js
new file mode 100644 (file)
index 0000000..ce8ce8b
--- /dev/null
@@ -0,0 +1,266 @@
+// seedrandom.js
+// Author: David Bau 3/11/2010
+//
+// Defines a method Math.seedrandom() that, when called, substitutes
+// an explicitly seeded RC4-based algorithm for Math.random().  Also
+// supports automatic seeding from local or network sources of entropy.
+//
+// Usage:
+//
+//   <script src=http://davidbau.com/encode/seedrandom-min.js></script>
+//
+//   Math.seedrandom('yipee'); Sets Math.random to a function that is
+//                             initialized using the given explicit seed.
+//
+//   Math.seedrandom();        Sets Math.random to a function that is
+//                             seeded using the current time, dom state,
+//                             and other accumulated local entropy.
+//                             The generated seed string is returned.
+//
+//   Math.seedrandom('yowza', true);
+//                             Seeds using the given explicit seed mixed
+//                             together with accumulated entropy.
+//
+//   <script src="http://bit.ly/srandom-512"></script>
+//                             Seeds using physical random bits downloaded
+//                             from random.org.
+//
+// Examples:
+//
+//   Math.seedrandom("hello");            // Use "hello" as the seed.
+//   document.write(Math.random());       // Always 0.5463663768140734
+//   document.write(Math.random());       // Always 0.43973793770592234
+//   var rng1 = Math.random;              // Remember the current prng.
+//
+//   var autoseed = Math.seedrandom();    // New prng with an automatic seed.
+//   document.write(Math.random());       // Pretty much unpredictable.
+//
+//   Math.random = rng1;                  // Continue "hello" prng sequence.
+//   document.write(Math.random());       // Always 0.554769432473455
+//
+//   Math.seedrandom(autoseed);           // Restart at the previous seed.
+//   document.write(Math.random());       // Repeat the 'unpredictable' value.
+//
+// Notes:
+//
+// Each time seedrandom('arg') is called, entropy from the passed seed
+// is accumulated in a pool to help generate future seeds for the
+// zero-argument form of Math.seedrandom, so entropy can be injected over
+// time by calling seedrandom with explicit data repeatedly.
+//
+// On speed - This javascript implementation of Math.random() is about
+// 3-10x slower than the built-in Math.random() because it is not native
+// code, but this is typically fast enough anyway.  Seeding is more expensive,
+// especially if you use auto-seeding.  Some details (timings on Chrome 4):
+//
+// Our Math.random()            - avg less than 0.002 milliseconds per call
+// seedrandom('explicit')       - avg less than 0.5 milliseconds per call
+// seedrandom('explicit', true) - avg less than 2 milliseconds per call
+// seedrandom()                 - avg about 38 milliseconds per call
+//
+// LICENSE (BSD):
+//
+// Copyright 2010 David Bau, all rights reserved.
+//
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions are met:
+// 
+//   1. Redistributions of source code must retain the above copyright
+//      notice, this list of conditions and the following disclaimer.
+//
+//   2. Redistributions in binary form must reproduce the above copyright
+//      notice, this list of conditions and the following disclaimer in the
+//      documentation and/or other materials provided with the distribution.
+// 
+//   3. Neither the name of this module nor the names of its contributors may
+//      be used to endorse or promote products derived from this software
+//      without specific prior written permission.
+// 
+// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+//
+/**
+ * All code is in an anonymous closure to keep the global namespace clean.
+ *
+ * @param {number=} overflow 
+ * @param {number=} startdenom
+ */
+(function (pool, math, width, chunks, significance, overflow, startdenom) {
+
+
+//
+// seedrandom()
+// This is the seedrandom function described above.
+//
+math['seedrandom'] = function seedrandom(seed, use_entropy) {
+  var key = [];
+  var arc4;
+
+  // Flatten the seed string or build one from local entropy if needed.
+  seed = mixkey(flatten(
+    use_entropy ? [seed, pool] :
+    arguments.length ? seed :
+    [new Date().getTime(), pool, window], 3), key);
+
+  // Use the seed to initialize an ARC4 generator.
+  arc4 = new ARC4(key);
+
+  // Mix the randomness into accumulated entropy.
+  mixkey(arc4.S, pool);
+
+  // Override Math.random
+
+  // This function returns a random double in [0, 1) that contains
+  // randomness in every bit of the mantissa of the IEEE 754 value.
+
+  math['random'] = function random() {  // Closure to return a random double:
+    var n = arc4.g(chunks);             // Start with a numerator n < 2 ^ 48
+    var d = startdenom;                 //   and denominator d = 2 ^ 48.
+    var x = 0;                          //   and no 'extra last byte'.
+    while (n < significance) {          // Fill up all significant digits by
+      n = (n + x) * width;              //   shifting numerator and
+      d *= width;                       //   denominator and generating a
+      x = arc4.g(1);                    //   new least-significant-byte.
+    }
+    while (n >= overflow) {             // To avoid rounding up, before adding
+      n /= 2;                           //   last byte, shift everything
+      d /= 2;                           //   right using integer math until
+      x >>>= 1;                         //   we have exactly the desired bits.
+    }
+    return (n + x) / d;                 // Form the number within [0, 1).
+  };
+
+  // Return the seed that was used
+  return seed;
+};
+
+//
+// ARC4
+//
+// An ARC4 implementation.  The constructor takes a key in the form of
+// an array of at most (width) integers that should be 0 <= x < (width).
+//
+// The g(count) method returns a pseudorandom integer that concatenates
+// the next (count) outputs from ARC4.  Its return value is a number x
+// that is in the range 0 <= x < (width ^ count).
+//
+/** @constructor */
+function ARC4(key) {
+  var t, u, me = this, keylen = key.length;
+  var i = 0, j = me.i = me.j = me.m = 0;
+  me.S = [];
+  me.c = [];
+
+  // The empty key [] is treated as [0].
+  if (!keylen) { key = [keylen++]; }
+
+  // Set up S using the standard key scheduling algorithm.
+  while (i < width) { me.S[i] = i++; }
+  for (i = 0; i < width; i++) {
+    t = me.S[i];
+    j = lowbits(j + t + key[i % keylen]);
+    u = me.S[j];
+    me.S[i] = u;
+    me.S[j] = t;
+  }
+
+  // The "g" method returns the next (count) outputs as one number.
+  me.g = function getnext(count) {
+    var s = me.S;
+    var i = lowbits(me.i + 1); var t = s[i];
+    var j = lowbits(me.j + t); var u = s[j];
+    s[i] = u;
+    s[j] = t;
+    var r = s[lowbits(t + u)];
+    while (--count) {
+      i = lowbits(i + 1); t = s[i];
+      j = lowbits(j + t); u = s[j];
+      s[i] = u;
+      s[j] = t;
+      r = r * width + s[lowbits(t + u)];
+    }
+    me.i = i;
+    me.j = j;
+    return r;
+  };
+  // For robust unpredictability discard an initial batch of values.
+  // See http://www.rsa.com/rsalabs/node.asp?id=2009
+  me.g(width);
+}
+
+//
+// flatten()
+// Converts an object tree to nested arrays of strings.
+//
+/** @param {Object=} result 
+  * @param {string=} prop */
+function flatten(obj, depth, result, prop) {
+  result = [];
+  if (depth && typeof(obj) == 'object') {
+    for (prop in obj) {
+      if (prop.indexOf('S') < 5) {    // Avoid FF3 bug (local/sessionStorage)
+        try { result.push(flatten(obj[prop], depth - 1)); } catch (e) {}
+      }
+    }
+  }
+  return result.length ? result : '' + obj;
+}
+
+//
+// mixkey()
+// Mixes a string seed into a key that is an array of integers, and
+// returns a shortened string seed that is equivalent to the result key.
+//
+/** @param {number=} smear 
+  * @param {number=} j */
+function mixkey(seed, key, smear, j) {
+  seed += '';                         // Ensure the seed is a string
+  smear = 0;
+  for (j = 0; j < seed.length; j++) {
+    key[lowbits(j)] =
+      lowbits((smear ^= key[lowbits(j)] * 19) + seed.charCodeAt(j));
+  }
+  seed = '';
+  for (j in key) { seed += String.fromCharCode(key[j]); }
+  return seed;
+}
+
+//
+// lowbits()
+// A quick "n mod width" for width a power of 2.
+//
+function lowbits(n) { return n & (width - 1); }
+
+//
+// The following constants are related to IEEE 754 limits.
+//
+startdenom = math.pow(width, chunks);
+significance = math.pow(2, significance);
+overflow = significance * 2;
+
+//
+// When seedrandom.js is loaded, we immediately mix a few bits
+// from the built-in RNG into the entropy pool.  Because we do
+// not want to intefere with determinstic PRNG state later,
+// seedrandom will not call math.random on its own again after
+// initialization.
+//
+mixkey(math.random(), pool);
+
+// End anonymous scope, and pass initial values.
+})(
+  [],   // pool: entropy pool starts empty
+  Math, // math: package containing random, pow, and seedrandom
+  256,  // width: each RC4 output is 0 <= x < 256
+  6,    // chunks: at least six RC4 outputs for each double
+  52    // significance: there are 52 significant digits in a double
+);
diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/link.png b/luci-app-bmx6/files/www/luci-static/resources/bmx6/link.png
new file mode 100644 (file)
index 0000000..58977ff
Binary files /dev/null and b/luci-app-bmx6/files/www/luci-static/resources/bmx6/link.png differ
diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/style.css b/luci-app-bmx6/files/www/luci-static/resources/bmx6/style.css
new file mode 100644 (file)
index 0000000..d8191a7
--- /dev/null
@@ -0,0 +1,22 @@
+    table {
+       width:90%;
+       border-top:1px solid #e5eaf8;
+       border-right:1px solid #e5eaf8;
+       margin:1em auto;
+       border-collapse:collapse;
+     }
+
+    td {
+       color:#678197;
+       border-bottom:1px solid #e6eff8;
+       border-left:1px solid #e6eff8;
+       padding:.3em 1em;
+       text-align:center;
+     }
+    th {
+       background:#f4f9fe;
+       text-align:center;
+       font:bold 1.2em/2em Century Gothic,Trebuchet MS,Arial,Helvetica,sans-serif;
+       color:#66a3d3;
+     }
+
diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/wifi.png b/luci-app-bmx6/files/www/luci-static/resources/bmx6/wifi.png
new file mode 100644 (file)
index 0000000..4195b08
Binary files /dev/null and b/luci-app-bmx6/files/www/luci-static/resources/bmx6/wifi.png differ
diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/world.png b/luci-app-bmx6/files/www/luci-static/resources/bmx6/world.png
new file mode 100644 (file)
index 0000000..29b53c9
Binary files /dev/null and b/luci-app-bmx6/files/www/luci-static/resources/bmx6/world.png differ
diff --git a/luci-app-bmx6/files/www/luci-static/resources/bmx6/world_small.png b/luci-app-bmx6/files/www/luci-static/resources/bmx6/world_small.png
new file mode 100644 (file)
index 0000000..f5f3105
Binary files /dev/null and b/luci-app-bmx6/files/www/luci-static/resources/bmx6/world_small.png differ
diff --git a/nodogsplash/Makefile b/nodogsplash/Makefile
new file mode 100644 (file)
index 0000000..d1d9212
--- /dev/null
@@ -0,0 +1,56 @@
+#
+# Copyright (C) 2013 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=nodogsplash
+PKG_FIXUP:=autoreconf
+PKG_VERSION:=0.9_beta9.9.9-pre
+PKG_RELEASE:=1
+
+PKG_BUILD_DIR:=$(BUILD_DIR)/$(PKG_NAME)-$(PKG_VERSION)/
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
+PKG_SOURCE_URL:=git://github.com/nodogsplash/nodogsplash.git
+PKG_SOURCE_VERSION:=master
+PKG_SOURCE_SUBDIR:=$(PKG_NAME)-$(PKG_VERSION)
+PKG_BUILD_PARALLEL:=1
+
+include $(INCLUDE_DIR)/package.mk
+
+
+define Package/nodogsplash
+       SUBMENU:=Captive Portals
+       SECTION:=net
+       CATEGORY:=Network
+       DEPENDS:=+libpthread +iptables-mod-ipopt
+       TITLE:=Open public network gateway daemon
+       URL:=https://github.com/nodogsplash/nodogsplash
+endef
+
+define Package/nodogsplash/description
+       Nodogsplash offers a simple way to open a free hotspot providing
+       restricted access to an internet connection.
+endef
+
+define Package/nodogsplash/install
+       $(INSTALL_DIR) $(1)/usr/bin
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/nodogsplash $(1)/usr/bin/
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/ndsctl $(1)/usr/bin/
+
+       $(INSTALL_DIR) $(1)/etc/init.d
+       $(INSTALL_BIN) files/nodogsplash.init $(1)/etc/init.d/$(PKG_NAME)
+
+       $(INSTALL_DIR) $(1)/etc/$(PKG_NAME)/htdocs/images
+       $(CP) $(PKG_BUILD_DIR)/resources/nodogsplash.conf $(1)/etc/$(PKG_NAME)/
+       $(CP) $(PKG_BUILD_DIR)/resources/splash.html $(1)/etc/$(PKG_NAME)/htdocs/
+       $(CP) $(PKG_BUILD_DIR)/resources/infoskel.html $(1)/etc/$(PKG_NAME)/htdocs/
+       $(CP) $(PKG_BUILD_DIR)/resources/splash.jpg $(1)/etc/$(PKG_NAME)/htdocs/images/
+endef
+
+
+$(eval $(call BuildPackage,nodogsplash))
+
diff --git a/nodogsplash/files/nodogsplash.init b/nodogsplash/files/nodogsplash.init
new file mode 100644 (file)
index 0000000..7eba60b
--- /dev/null
@@ -0,0 +1,176 @@
+#!/bin/sh /etc/rc.common
+#
+# description: Startup/shutdown script for nodogsplash captive portal
+#
+# P. Kube 2007
+#
+# (Based on wifidog startup script
+# Date    : 2004-08-25
+# Version : 1.0
+# Comment by that author: Could be better, but it's working as expected)
+#
+
+
+IPT=/usr/sbin/iptables
+WD_DIR=/usr/bin
+OPTIONS=""
+START=65
+STOP=65
+# -s -d 5 runs in background, with level 5 (not so verbose) messages to syslog
+# -f -d 7 runs in foreground, with level 7 (verbose) debug messages to terminal
+# N.B.: -f will fail if starting at boot from rcS
+#OPTIONS="-s -d 5"
+
+start() {
+       echo "Starting nodogsplash ... "
+       if $WD_DIR/ndsctl status 2> /dev/null; then
+               echo "FAILED:  nodogsplash already running"
+       else
+               if test_module && $WD_DIR/nodogsplash $OPTIONS; then
+                       echo "OK: nodogsplash started"
+               else
+                       echo "FAILED:  nodogsplash exited with non 0 status"
+               fi
+       fi
+}
+
+stop() {
+       echo "Stopping nodogsplash ... "
+       if $WD_DIR/ndsctl status 2> /dev/null; then
+               if $WD_DIR/ndsctl stop; then
+                       echo "OK: nodogsplash stopped"
+               else
+                       echo "FAILED:  ndsctl stop exited with non 0 status"
+               fi
+       else
+               echo "FAILED:  nodogsplash was not running"
+       fi
+}
+
+status() {
+       $WD_DIR/ndsctl status
+}
+
+test_module() {
+
+    ### Test ipt_mark with iptables
+    test_ipt_mark () {
+       ($IPT -A FORWARD -m mark --mark 2 -j ACCEPT 2>&1) > /dev/null
+       IPTABLES_OK=$?
+       if [ "$IPTABLES_OK" -eq 0 ]; then
+           ($IPT -D FORWARD -m mark --mark 2 -j ACCEPT 2>&1) > /dev/null
+           return 0
+       else
+           return 1
+       fi
+    }
+    ### Test ipt_mac with iptables
+    test_ipt_mac () {
+       ($IPT -A INPUT -m mac --mac-source 00:00:00:00:00:00 -j ACCEPT 2>&1) > /dev/null
+       IPTABLES_OK=$?
+       if [ "$IPTABLES_OK" -eq 0 ]; then
+           ($IPT -D INPUT -m mac --mac-source 00:00:00:00:00:00 -j ACCEPT 2>&1) > /dev/null
+           return 0
+       else
+           return 1
+       fi
+    }
+
+    ### Test ipt_IMQ with iptables
+    test_ipt_IMQ () {
+       ($IPT -t mangle -A PREROUTING -j IMQ --todev 0 2>&1) > /dev/null
+       IPTABLES_OK=$?
+       if [ "$IPTABLES_OK" -eq 0 ]; then
+           ($IPT -t mangle -D PREROUTING -j IMQ --todev 0 2>&1) > /dev/null
+           return 0
+       else
+           return 1
+       fi
+    }
+
+    ### Test imq with ip
+    test_imq () {
+       (ip link set imq0 up 2>&1) > /dev/null
+       IMQ0_OK=$?
+       (ip link set imq1 up 2>&1) > /dev/null
+       IMQ1_OK=$?
+       if [ "$IMQ0_OK" -eq 0 -a "$IMQ1_OK" -eq 0 ]; then
+           (ip link set imq0 down 2>&1) > /dev/null
+           (ip link set imq1 down 2>&1) > /dev/null
+           return 0
+       else
+           return 1
+       fi
+    }
+
+    ### Test sch_htb with tc; requires imq0
+    test_sch_htb () {
+       (tc qdisc del dev imq0 root 2>&1) > /dev/null
+       (tc qdisc add dev imq0 root htb 2>&1) > /dev/null
+       TC_OK=$?
+       if [ "$TC_OK" -eq 0 ]; then
+           (tc qdisc del dev imq0 root 2>&1) > /dev/null
+           return 0
+       else
+           return 1
+       fi
+    }
+
+    
+    ### Find a module on disk
+    module_exists () {
+      EXIST=$(find /lib/modules/`uname -r` -name $1.*o 2> /dev/null)
+      if [ -n "$EXIST" ]; then
+        return 0
+      else
+        return 1
+      fi
+    }
+
+    ### Test if a module is in memory
+    module_in_memory () {
+      MODULE=$(lsmod | grep $1 | awk '{print $1}')
+      if [ "$MODULE" = "$1" ]; then
+        return 0
+      else
+        return 1
+      fi
+    }
+
+    ### Test functionality of a module; load if necessary
+    do_module_tests () {
+       echo "  Testing module $1 $2"
+       "test_$1"
+       if [ $? -ne 0 ]; then
+           echo "   Module $1 $2 needed"
+           echo "   Scanning disk for $1 module"
+           module_exists $1
+           if [ $? -ne 0 ]; then 
+               echo "   $1 module missing: please install it"
+               exit 1
+           else
+               echo "   $1 exists, trying to load"
+               insmod $1 $2 > /dev/null
+               if [ $? -ne 0 ]; then
+                   echo "   Error: insmod $1 $2 failed"
+                   exit 1
+               else
+                   echo "   $1 $2 loaded successfully"
+               fi
+           fi
+       else
+           echo "   $1 is working"
+       fi 
+               
+    }
+
+    echo " Testing required modules"
+
+    do_module_tests "ipt_mac"
+    do_module_tests "ipt_mark"
+    # if not using traffic control,
+    # you can comment out the following 3 lines:
+    do_module_tests "imq" "numdevs=2"
+    do_module_tests "ipt_IMQ"
+    do_module_tests "sch_htb"
+}
diff --git a/olsrd/Makefile b/olsrd/Makefile
new file mode 100644 (file)
index 0000000..c1ca224
--- /dev/null
@@ -0,0 +1,290 @@
+# 
+# Copyright (C) 2009-2012 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=olsrd
+PKG_VERSION:=0.6.5.4
+PKG_RELEASE:=1
+
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.bz2
+PKG_SOURCE_URL:=http://www.olsr.org/releases/0.6
+
+PKG_MD5SUM:=44689e790359f9363e5ebb924c548730
+PKG_BUILD_PARALLEL:=1
+
+include $(INCLUDE_DIR)/package.mk
+
+TARGET_CFLAGS += $(FPIC)
+
+define Package/olsrd/template
+  SECTION:=net
+  CATEGORY:=Network
+  SUBMENU:=Routing and Redirection
+  MAINTAINER:=Saverio Proto <zioproto@gmail.com>
+  TITLE:=OLSR (Optimized Link State Routing) daemon
+  URL:=http://www.olsr.org/
+endef
+
+define Package/olsrd
+  $(call Package/olsrd/template)
+  MENU:=1
+  DEPENDS:=+libpthread
+endef
+
+define Package/olsrd/conffiles
+/etc/config/olsrd
+endef
+
+define Package/olsrd-mod-arprefresh
+  $(call Package/olsrd/template)
+  DEPENDS:=olsrd
+  TITLE:=Kernel ARP cache refresh plugin
+endef
+
+define Package/olsrd-mod-dot-draw
+  $(call Package/olsrd/template)
+  DEPENDS:=olsrd
+  TITLE:=Dot topology information plugin
+endef
+
+define Package/olsrd-mod-bmf
+  $(call Package/olsrd/template)
+  DEPENDS:=olsrd +kmod-tun
+  TITLE:=Basic multicast forwarding plugin
+endef
+
+define Package/olsrd-mod-dyn-gw
+  $(call Package/olsrd/template)
+  DEPENDS:=olsrd
+  TITLE:=Dynamic internet gateway plugin
+endef
+
+define Package/olsrd-mod-dyn-gw-plain
+  $(call Package/olsrd/template)
+  DEPENDS:=olsrd
+  TITLE:=Dynamic internet gateway plain plugin
+endef
+
+define Package/olsrd-mod-httpinfo
+  $(call Package/olsrd/template)
+  DEPENDS:=olsrd
+  TITLE:=Small informative web server plugin
+endef
+
+define Package/olsrd-mod-jsoninfo
+  $(call Package/olsrd/template)
+  DEPENDS:=olsrd
+  TITLE:=Small informative plugin with JSON output
+endef
+
+define Package/olsrd-mod-mdns
+  $(call Package/olsrd/template)
+  DEPENDS:=olsrd
+  TITLE:=MDNS/Zeroconf/Bonjour packet distribution 
+endef
+
+define Package/olsrd-mod-nameservice
+  $(call Package/olsrd/template)
+  DEPENDS:=olsrd
+  TITLE:=Lightweight hostname resolver plugin
+endef
+
+define Package/olsrd-mod-p2pd
+  $(call Package/olsrd/template)
+  DEPENDS:=olsrd
+  TITLE:=Peer to Peer Discovery plugin
+endef
+
+
+define Package/olsrd-mod-pgraph
+  $(call Package/olsrd/template)
+  DEPENDS:=olsrd
+  TITLE:=output network topology for pgraph
+endef
+
+define Package/olsrd-mod-pud
+  $(call Package/olsrd/template)
+  DEPENDS:=olsrd
+  TITLE:=Position Update Distribution plugin
+endef
+
+define Package/olsrd-mod-quagga
+  $(call Package/olsrd/template)
+  DEPENDS:=olsrd
+  TITLE:=Quagga plugin
+endef
+
+define Package/olsrd-mod-secure
+  $(call Package/olsrd/template)
+  DEPENDS:=olsrd
+  TITLE:=Message signing plugin to secure routing domain
+endef
+
+define Package/olsrd-mod-sgwdynspeed
+  $(call Package/olsrd/template)
+  DEPENDS:=olsrd
+  TITLE:=Smart Gateway dynamic speed plugin
+endef
+
+define Package/olsrd-mod-txtinfo
+  $(call Package/olsrd/template)
+  DEPENDS:=olsrd
+  TITLE:=Small informative web server plugin
+endef
+
+define Package/olsrd-mod-watchdog
+  $(call Package/olsrd/template)
+  DEPENDS:=olsrd
+  TITLE:=Watchdog plugin
+endef
+
+define Package/olsrd-mod-pud/conffiles
+/etc/olsrd.d/olsrd.pud.position.conf
+endef
+
+define Package/olsrd-mod-secure/conffiles
+/etc/olsrd.d/olsrd_secure_key
+endef
+
+define Package/olsrd-mod-sgwdynspeed/conffiles
+/etc/olsrd.d/olsrd.sgw.speed.conf
+endef
+
+MAKE_FLAGS+= \
+       NO_DEBUG_MESSAGES=1 \
+       OS="linux" \
+       DESTDIR="$(PKG_INSTALL_DIR)" \
+       STRIP="true" \
+       INSTALL_LIB="true" \
+       SUBDIRS="arprefresh bmf dot_draw dyn_gw dyn_gw_plain httpinfo jsoninfo mdns nameservice p2pd pgraph pud quagga secure sgwdynspeed txtinfo watchdog"
+
+define Build/Compile
+       $(call Build/Compile/Default,all)
+       $(call Build/Compile/Default,libs)
+endef
+
+define Package/olsrd/install
+       $(INSTALL_DIR) $(1)/etc/config
+       $(INSTALL_DATA) ./files/olsrd.config $(1)/etc/config/olsrd
+       $(INSTALL_DIR) $(1)/usr/sbin
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/olsrd $(1)/usr/sbin/
+       $(INSTALL_DIR) $(1)/etc/init.d
+       $(INSTALL_BIN) ./files/olsrd.init $(1)/etc/init.d/olsrd
+endef
+
+define Package/olsrd-mod-arprefresh/install
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/arprefresh/olsrd_arprefresh.so.* $(1)/usr/lib/
+endef
+
+define Package/olsrd-mod-dot-draw/install
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/dot_draw/olsrd_dot_draw.so.* $(1)/usr/lib/
+endef
+
+define Package/olsrd-mod-bmf/install
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/bmf/olsrd_bmf.so.* $(1)/usr/lib/
+endef
+
+define Package/olsrd-mod-dyn-gw/install
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/dyn_gw/olsrd_dyn_gw.so.* $(1)/usr/lib/
+endef
+
+define Package/olsrd-mod-dyn-gw-plain/install
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/dyn_gw_plain/olsrd_dyn_gw_plain.so.* $(1)/usr/lib/
+endef
+
+define Package/olsrd-mod-httpinfo/install
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/httpinfo/olsrd_httpinfo.so.* $(1)/usr/lib/
+endef
+
+define Package/olsrd-mod-jsoninfo/install
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/jsoninfo/olsrd_jsoninfo.so.* $(1)/usr/lib/
+endef
+
+define Package/olsrd-mod-mdns/install
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/mdns/olsrd_mdns.so.* $(1)/usr/lib/
+endef
+
+define Package/olsrd-mod-nameservice/install
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/nameservice/olsrd_nameservice.so.* $(1)/usr/lib/
+endef
+
+define Package/olsrd-mod-p2pd/install
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/p2pd/olsrd_p2pd.so.* $(1)/usr/lib/
+endef
+
+define Package/olsrd-mod-pgraph/install
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/pgraph/olsrd_pgraph.so.* $(1)/usr/lib/
+endef
+
+define Package/olsrd-mod-pud/install
+       $(INSTALL_DIR) $(1)/etc/olsrd.d
+       $(CP) ./files/olsrd.pud.position.conf $(1)/etc/olsrd.d/
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/pud/nmealib/lib/libnmea.so $(1)/usr/lib/
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/pud/wireformat/lib/libOlsrdPudWireFormat.so $(1)/usr/lib/
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/pud/olsrd_pud.so.* $(1)/usr/lib/
+endef
+
+define Package/olsrd-mod-quagga/install
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/quagga/olsrd_quagga.so.* $(1)/usr/lib/
+endef
+
+define Package/olsrd-mod-secure/install
+       $(INSTALL_DIR) $(1)/etc/olsrd.d
+       $(CP) ./files/olsrd_secure_key $(1)/etc/olsrd.d/
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/secure/olsrd_secure.so.* $(1)/usr/lib/
+endef
+
+define Package/olsrd-mod-sgwdynspeed/install
+       $(INSTALL_DIR) $(1)/etc/olsrd.d
+       $(CP) ./files/olsrd.sgw.speed.conf $(1)/etc/olsrd.d/
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/sgwdynspeed/olsrd_sgwdynspeed.so.* $(1)/usr/lib/
+endef
+
+define Package/olsrd-mod-txtinfo/install
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/txtinfo/olsrd_txtinfo.so.* $(1)/usr/lib/
+endef
+
+define Package/olsrd-mod-watchdog/install
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(INSTALL_BIN) $(PKG_BUILD_DIR)/lib/watchdog/olsrd_watchdog.so.* $(1)/usr/lib/
+endef
+
+$(eval $(call BuildPackage,olsrd))
+$(eval $(call BuildPackage,olsrd-mod-arprefresh))
+$(eval $(call BuildPackage,olsrd-mod-dot-draw))
+$(eval $(call BuildPackage,olsrd-mod-bmf))
+$(eval $(call BuildPackage,olsrd-mod-dyn-gw))
+$(eval $(call BuildPackage,olsrd-mod-dyn-gw-plain))
+$(eval $(call BuildPackage,olsrd-mod-httpinfo))
+$(eval $(call BuildPackage,olsrd-mod-jsoninfo))
+$(eval $(call BuildPackage,olsrd-mod-mdns))
+$(eval $(call BuildPackage,olsrd-mod-nameservice))
+$(eval $(call BuildPackage,olsrd-mod-p2pd))
+$(eval $(call BuildPackage,olsrd-mod-pgraph))
+$(eval $(call BuildPackage,olsrd-mod-pud))
+$(eval $(call BuildPackage,olsrd-mod-quagga))
+$(eval $(call BuildPackage,olsrd-mod-secure))
+$(eval $(call BuildPackage,olsrd-mod-sgwdynspeed))
+$(eval $(call BuildPackage,olsrd-mod-txtinfo))
+$(eval $(call BuildPackage,olsrd-mod-watchdog))
diff --git a/olsrd/files/olsrd.config b/olsrd/files/olsrd.config
new file mode 100644 (file)
index 0000000..9a19eb9
--- /dev/null
@@ -0,0 +1,26 @@
+config olsrd
+       # uncomment the following line to use a custom config file instead:
+       #option config_file '/etc/olsrd.conf'
+
+       option IpVersion '4'
+
+config LoadPlugin
+       option library 'olsrd_arprefresh.so.0.1'
+
+config LoadPlugin
+       option library 'olsrd_dyn_gw.so.0.5'
+
+config LoadPlugin
+       option library 'olsrd_httpinfo.so.0.1'
+       option port '1978'
+       list Net '0.0.0.0 0.0.0.0'
+
+config LoadPlugin
+       option library 'olsrd_nameservice.so.0.3'
+
+config LoadPlugin
+       option library 'olsrd_txtinfo.so.0.1'
+       option accept '0.0.0.0'
+
+config Interface
+       list interface 'wlan'
diff --git a/olsrd/files/olsrd.init b/olsrd/files/olsrd.init
new file mode 100644 (file)
index 0000000..f4641aa
--- /dev/null
@@ -0,0 +1,837 @@
+#!/bin/sh /etc/rc.common
+# Copyright (C) 2008-2013 OpenWrt.org
+
+START=65
+
+SERVICE_DAEMONIZE=1
+SERVICE_WRITE_PID=1
+
+CONF=/var/etc/olsrd.conf
+PID=/var/run/olsrd.pid
+PID6=/var/run/olsrd.ipv6.pid
+
+OLSRD_OLSRD_SCHEMA='ignore:internal config_file:internal DebugLevel=0 AllowNoInt=yes'
+OLSRD_IPCCONNECT_SCHEMA='ignore:internal Host:list Net:list2'
+OLSRD_LOADPLUGIN_SCHEMA='ignore:internal library:internal Host4:list Net4:list2 Host:list Net:list2 Host6:list Net6:list2 Ping:list redistribute:list NonOlsrIf:list name:list lat lon latlon_infile HNA:list2 hosts:list2'
+OLSRD_INTERFACE_SCHEMA='ignore:internal interface:internal AutoDetectChanges:bool'
+OLSRD_INTERFACE_DEFAULTS_SCHEMA='AutoDetectChanges:bool'
+
+T='    '
+N='
+'
+
+log() {
+       logger -t olsrd -p daemon.info -s "${initscript}: $@"
+}
+
+error() {
+       log "ERROR: $@"
+}
+
+validate_varname() {
+       local varname="$1"
+       [ -z "$varname" -o "$varname" != "${varname%%[!A-Za-z0-9_]*}" ] && return 1
+       return 0
+}
+
+validate_olsrd_option() {
+       local str="$1"
+       [ -z "$str" -o "$str" != "${str%%[!     0-9A-Za-z./|:_-]*}" ] && return 1
+       return 0
+}
+
+system_config() {
+       local cfg="$1"
+       local cfgt hostname latlon oldIFS
+
+       config_get cfgt "$cfg" TYPE
+
+       if [ "$cfgt" = "system" ]; then
+               config_get hostname "$cfg" hostname
+               hostname="${hostname:-OpenWrt}"
+               SYSTEM_HOSTNAME="$hostname"
+       fi
+
+       if [ -z "$SYSTEM_LAT" -o -z "$SYSTEM_LON" ]; then
+               config_get latlon "$cfg" latlon
+               oldIFS="$IFS"; IFS=" ${T}${N},"; set -- $latlon; IFS="$oldIFS"
+               SYSTEM_LAT="$1"
+               SYSTEM_LON="$2"
+       fi
+
+       if [ -z "$SYSTEM_LAT" -o -z "$SYSTEM_LON" ]; then
+               config_get latlon "$cfg" latitude
+               SYSTEM_LAT="$latlon"
+               config_get latlon "$cfg" longitude
+               SYSTEM_LON="$latlon"
+       fi
+}
+
+olsrd_find_config_file() {
+       local cfg="$1"
+       validate_varname "$cfg" || return 0
+
+       config_get_bool ignore "$cfg" ignore 0
+       [ "$ignore" -ne 0 ] && return 0
+       config_get OLSRD_CONFIG_FILE "$cfg" config_file
+
+       return 0
+}
+
+warning_invalid_value() {
+       local funcname="warning_invalid_value"
+       local package="$1"
+       validate_varname "$package" || package=
+       local config="$2"
+       validate_varname "$config" || config=
+       local option="$3"
+       validate_varname "$option" || option=
+
+       if [ -n "$package" -a -n "$config" ]; then
+               log "$funcname() in option '$package.$config${option:+.}$option', skipped"
+       else
+               log "$funcname() skipped"
+       fi
+
+       return 0
+}
+
+olsrd_write_option() {
+       local param="$1"
+       local cfg="$2"
+       validate_varname "$cfg" || return 1
+       local option="$3"
+       validate_varname "$option" || return 1
+       local value="$4"
+       local option_type="$5"
+
+       if [ "$option_type" = bool ]; then
+               case "$value" in
+                       1|on|true|enabled|yes) value=yes;;
+                       0|off|false|disabled|no) value=no;;
+                       *) warning_invalid_value olsrd "$cfg" "$option"; return 1;;
+               esac
+       fi
+
+       if ! validate_olsrd_option "$value"; then
+               warning_invalid_value olsrd "$cfg" "$option"
+               return 1
+       fi
+
+       if [ "$value" != "${value%%[G-Zg-z_-]*}" ]; then
+               if [ "$option" != "Ip6AddrType" -a "$option" != "LinkQualityMult" -a "$value" != "yes" -a "$value" != "no" ]; then
+                       value="\"$value\""
+               fi
+       fi
+
+       echo -n "${N}$param$option $value"
+
+       return 0
+}
+
+olsrd_write_plparam() {
+       local funcname="olsrd_write_plparam"
+       local param="$1"
+       local cfg="$2"
+       validate_varname "$cfg" || return 1
+       local option="$3"
+       validate_varname "$option" || return 1
+       local value="$4"
+       local option_type="$5"
+       local _option oldIFS
+
+       if [ "$option_type" = bool ]; then
+               case "$value" in
+                       1|on|true|enabled|yes) value=yes;;
+                       0|off|false|disabled|no) value=no;;
+                       *) warning_invalid_value olsrd "$cfg" "$option"; return 1;;
+               esac
+       fi
+
+       if ! validate_olsrd_option "$value"; then
+               warning_invalid_value olsrd "$cfg" "$option"
+               return 1
+       fi
+
+       oldIFS="$IFS"
+       IFS='-_'
+       set -- $option
+       option="$*"
+       IFS="$oldIFS"
+       _option="$option"
+
+       if [ "$option" = 'hosts' ]; then
+               set -- $value
+               option="$1"
+               shift
+               value="$*"
+       fi
+
+       if [ "$option" = 'NonOlsrIf' ]; then
+               if validate_varname "$value"; then
+                       if network_get_device ifname "$value"; then
+                               log "$funcname() Info: mdns Interface '$value' ifname '$ifname' found"
+                       else
+                               log "$funcname() Warning: mdns Interface '$value' not found, skipped"
+                       fi
+               else
+                       warning_invalid_value olsrd "$cfg" "NonOlsrIf"
+               fi
+               [ -z "$ifname" ] || value=$ifname
+       fi
+
+       echo -n "${N}${param}PlParam \"$option\" \"$value\""
+
+       return 0
+}
+
+config_update_schema() {
+       local schema_varname="$1"
+       validate_varname "$schema_varname" || return 1
+       local command="$2"
+       validate_varname "$command" || return 1
+       local option="$3"
+       validate_varname "$option" || return 1
+       local value="$4"
+       local schema
+       local cur_option
+
+       case "$varname" in
+               *_LENGTH) return 0;;
+               *_ITEM*) return 0;;
+       esac
+
+       eval "export -n -- \"schema=\${$schema_varname}\""
+
+       for cur_option in $schema; do
+               [ "${cur_option%%[:=]*}" = "$option" ] && return 0
+       done
+
+       if [ "$command" = list ]; then
+               set -- $value
+               if [ "$#" -ge "3" ]; then
+                       schema_entry="$option:list3"
+               elif [ "$#" -ge "2" ]; then
+                       schema_entry="$option:list2"
+               else
+                       schema_entry="$option:list"
+               fi
+       else
+               schema_entry="$option"
+       fi
+
+       append "$schema_varname" "$schema_entry"
+
+       return 0
+}
+
+config_write_options() {
+       local funcname="config_write_options"
+       local schema="$1"
+       local cfg="$2"
+       validate_varname "$cfg" || return 1
+       local write_func="$3"
+       [ -z "$write_func" ] && output_func=echo
+       local write_param="$4"
+
+       local schema_entry option option_length option_type default value list_size list_item list_value i position speed oldIFS
+       local list_speed_vars="HelloInterval HelloValidityTime TcInterval TcValidityTime MidInterval MidValidityTime HnaInterval HnaValidityTime"
+
+       get_value_for_entry()
+       {
+               local schema_entry="$1"
+
+               default="${schema_entry#*[=]}"
+               [ "$default" = "$schema_entry" ] && default=
+               option="${schema_entry%%[=]*}"
+
+               oldIFS="$IFS"; IFS=':'; set -- $option; IFS="$oldIFS"
+               option="$1"
+               option_type="$2"
+
+               validate_varname "$option" || return 1
+               [ -z "$option_type" ] || validate_varname "$option_type" || return 1
+               [ "$option_type" = internal ] && return 1
+
+               config_get value "$cfg" "$option"
+               [ "$option" = "speed" ] && return 1
+
+               return 0
+       }
+
+       already_in_schema()
+       {
+               case " $schema " in
+                       *" $1 "*)
+                               return 0
+                       ;;
+                       *)
+                               return 1
+                       ;;
+               esac
+       }
+
+       already_in_schema "speed" && {
+               get_value_for_entry "speed"
+
+               if [ 2>/dev/null $value -gt 0 -a $value -le 20 ]; then
+                       speed="$value"
+               else
+                       log "$funcname() Warning: invalid speed-value: '$value' - allowed integers: 1...20, fallback to 6"
+                       speed=6
+               fi
+
+               for schema_entry in $list_speed_vars; do {
+                       already_in_schema "$schema_entry" || schema="$schema $schema_entry"
+               } done
+       }
+
+       for schema_entry in $schema; do
+               if [ -n "$speed" ]; then                # like sven-ola freifunk firmware fff-1.7.4
+                       case "$schema_entry" in
+                               HelloInterval)
+                                       value="$(( $speed / 2 + 1 )).0"
+                               ;;
+                               HelloValidityTime)
+                                       value="$(( $speed * 25 )).0"
+                               ;;
+                               TcInterval)     # todo: not fisheye? -> $(( $speed * 2 ))
+                                       value=$(( $speed / 2 ))
+                                       [ $value -eq 0 ] && value=1
+                                       value="$value.0"
+                               ;;
+                               TcValidityTime)
+                                       value="$(( $speed * 100 )).0"
+                               ;;
+                               MidInterval)
+                                       value="$(( $speed * 5 )).0"
+                               ;;
+                               MidValidityTime)
+                                       value="$(( $speed * 100 )).0"
+                               ;;
+                               HnaInterval)
+                                       value="$(( $speed * 2 )).0"
+                               ;;
+                               HnaValidityTime)
+                                       value="$(( $speed * 25 )).0"
+                               ;;
+                               *)
+                                       get_value_for_entry "$schema_entry" || continue
+                               ;;
+                       esac
+
+                       is_speed_var()
+                       {
+                               case " $list_speed_vars " in
+                                       *" $1 "*)
+                                               return 0
+                                       ;;
+                                       *)
+                                               return 1
+                                       ;;
+                               esac
+                       }
+
+                       is_speed_var "$schema_entry" && option="$schema_entry"
+               else
+                       get_value_for_entry "$schema_entry" || continue
+               fi
+
+               if [ -z "$value" ]; then
+                       oldIFS="$IFS"; IFS='+'; set -- $default; IFS="$oldIFS"
+                       value=$*
+               elif [ "$value" = '-' -a -n "$default" ]; then
+                       continue
+               fi
+
+               [ -z "$value" ] && continue
+
+               case "$option_type" in
+                       list) list_size=1;;
+                       list2) list_size=2;;
+                       list3) list_size=3;;
+                       *) list_size=0;;
+               esac
+
+               if [ "$list_size" -gt 0 ]; then
+                       config_get option_length "$cfg" "${option}_LENGTH"
+                       if [ -n "$option_length" ]; then
+                               i=1
+                               while [ "$i" -le "$option_length" ]; do
+                                       config_get list_value "$cfg" "${option}_ITEM$i"
+                                       "$write_func" "$write_param" "$cfg" "$option" "$list_value" "$option_type" || break
+                                       i=$((i + 1))
+                               done
+                       else
+                               list_value=
+                               i=0
+                               for list_item in $value; do
+                                       append "list_value" "$list_item"
+                                       i=$((i + 1))
+                                       position=$((i % list_size))
+                                       if [ "$position" -eq 0 ]; then
+                                               "$write_func" "$write_param" "$cfg" "$option" "$list_value" "$option_type" || break
+                                               list_value=
+                                       fi
+                               done
+                               [ "$position" -ne 0 ] && "$write_func" "$write_param" "$cfg" "$option" "$list_value" "$option_type"
+                       fi
+               else
+                       "$write_func" "$write_param" "$cfg" "$option" "$value" "$option_type"
+               fi
+       done
+
+       return 0
+}
+
+olsrd_write_olsrd() {
+       local cfg="$1"
+       validate_varname "$cfg" || return 0
+       local ignore
+
+       config_get_bool ignore "$cfg" ignore 0
+       [ "$ignore" -ne 0 ] && return 0
+
+       [ "$OLSRD_COUNT" -gt 0 ] && return 0
+
+       config_get ipversion "$cfg" IpVersion
+       if [ "$ipversion" = "6and4" ]; then
+               OLSRD_IPVERSION_6AND4=1
+               config_set "$cfg" IpVersion '6'
+       fi
+       config_get smartgateway "$cfg" SmartGateway
+       config_get smartgatewayuplink "$cfg" SmartGatewayUplink
+
+       config_write_options "$OLSRD_OLSRD_SCHEMA" "$cfg" olsrd_write_option
+       echo
+       OLSRD_COUNT=$((OLSRD_COUNT + 1))
+       return 0
+}
+
+olsrd_write_ipcconnect() {
+       local cfg="$1"
+       validate_varname "$cfg" || return 0
+       local ignore
+
+       config_get_bool ignore "$cfg" ignore 0
+       [ "$ignore" -ne 0 ] && return 0
+
+       [ "$IPCCONNECT_COUNT" -gt 0 ] && return 0
+
+       echo -n "${N}IpcConnect${N}{"
+       config_write_options "$OLSRD_IPCCONNECT_SCHEMA" "$cfg" olsrd_write_option "${T}"
+       echo "${N}}"
+       IPCCONNECT_COUNT=$((IPCCONNECT_COUNT + 1))
+
+       return 0
+}
+
+olsrd_write_hna4() {
+       local cfg="$1"
+       validate_varname "$cfg" || return 0
+       local ignore
+
+       config_get_bool ignore "$cfg" ignore 0
+       [ "$ignore" -ne 0 ] && return 0
+
+       config_get netaddr "$cfg" netaddr
+       if ! validate_olsrd_option "$netaddr"; then
+               warning_invalid_value olsrd "$cfg" "netaddr"
+               return 0
+       fi
+
+       config_get netmask "$cfg" netmask
+       if ! validate_olsrd_option "$netmask"; then
+               warning_invalid_value olsrd "$cfg" "netmask"
+               return 0
+       fi
+
+       [ "$HNA4_COUNT" -le 0 ] && echo -n "${N}Hna4${N}{"
+       echo -n "${N}${T}${T}$netaddr $netmask"
+       HNA4_COUNT=$((HNA4_COUNT + 1))
+
+       return 0
+}
+
+olsrd_write_hna6() {
+       local cfg="$1"
+       validate_varname "$cfg" || return 0
+       local ignore
+
+       config_get_bool ignore "$cfg" ignore 0
+       [ "$ignore" -ne 0 ] && return 0
+
+       config_get netaddr "$cfg" netaddr
+       if ! validate_olsrd_option "$netaddr"; then
+               warning_invalid_value olsrd "$cfg" "netaddr"
+               return 0
+       fi
+
+       config_get prefix "$cfg" prefix
+       if ! validate_olsrd_option "$prefix"; then
+               warning_invalid_value olsrd "$cfg" "prefix"
+               return 0
+       fi
+
+       [ "$HNA6_COUNT" -le 0 ] && echo -n "${N}Hna6${N}{"
+       echo -n "${N}${T}${T}$netaddr $prefix"
+       HNA6_COUNT=$((HNA6_COUNT + 1))
+
+       return 0
+}
+
+olsrd_write_loadplugin() {
+       local funcname="olsrd_write_loadplugin"
+       local cfg="$1"
+       validate_varname "$cfg" || return 0
+       local ignore
+       local name
+       local suffix
+       local lat
+       local lon
+       local latlon_infile
+
+       config_get_bool ignore "$cfg" ignore 0
+       [ "$ignore" -ne 0 ] && return 0
+
+       config_get library "$cfg" library
+       if ! validate_olsrd_option "$library"; then
+               warning_invalid_value olsrd "$cfg" "library"
+               return 0
+       fi
+       if ! [ -x "/lib/$library" -o -x "/usr/lib/$library" -o -x "/usr/local/lib/$library" ]; then
+               log "$funcname() Warning: Plugin library '$library' not found, skipped"
+               return 0
+       fi
+
+       case "$library" in
+               olsrd_nameservice.*)
+                       config_get name "$cfg" name
+                       [ -z "$name" ] && config_set "$cfg" name $SYSTEM_HOSTNAME
+
+                       config_get suffix "$cfg" suffix
+                       [ -z "$suffix" ] && config_set "$cfg" suffix '.olsr'
+
+                       config_get lat "$cfg" lat
+                       config_get lon "$cfg" lon
+                       config_get latlon_infile "$cfg" latlon_infile
+                       if [ \( -z "$lat" -o -z "$lat" \) -a -z "$latlon_infile" ]; then
+                               if [ -f '/var/run/latlon.txt' ]; then
+                                       config_set "$cfg" lat ''
+                                       config_set "$cfg" lon ''
+                                       config_set "$cfg" latlon_infile '/var/run/latlon.txt'
+                               else
+                                       config_set "$cfg" lat "$SYSTEM_LAT"
+                                       config_set "$cfg" lon "$SYSTEM_LON"
+                               fi
+                       fi
+
+                       for f in latlon_file hosts_file services_file resolv_file macs_file; do
+                               config_get $f "$cfg" $f
+                       done
+
+                       [ -z "$latlon_file" ] && config_set "$cfg" latlon_file '/var/run/latlon.js'
+               ;;
+               olsrd_watchdog.*)
+                       config_get wd_file "$cfg" file
+               ;;
+       esac
+
+       echo -n "${N}LoadPlugin \"$library\"${N}{"
+       config_write_options "$OLSRD_LOADPLUGIN_SCHEMA" "$cfg" olsrd_write_plparam "${T}"
+       echo "${N}}"
+
+       return 0
+}
+
+olsrd_write_interface() {
+       local funcname="olsrd_write_interface"
+       local cfg="$1"
+       validate_varname "$cfg" || return 0
+       local ignore
+       local interfaces
+       local interface
+       local ifnames
+
+       config_get_bool ignore "$cfg" ignore 0
+       [ "$ignore" -ne 0 ] && return 0
+
+       ifnames=
+       config_get interfaces "$cfg" interface
+
+       for interface in $interfaces; do
+               if validate_varname "$interface"; then
+                       if network_get_device IFNAME "$interface"; then
+                               ifnames="$ifnames \"$IFNAME\""
+                               ifsglobal="$ifsglobal $IFNAME"
+                       else
+                               log "$funcname() Warning: Interface '$interface' not found, skipped"
+                       fi
+               else
+                       warning_invalid_value olsrd "$cfg" "interface"
+               fi
+       done
+
+       [ -z "$ifnames" ] && return 0
+
+       echo -n "${N}Interface$ifnames${N}{"
+       config_write_options "$OLSRD_INTERFACE_SCHEMA" "$cfg" olsrd_write_option "${T}"
+       echo "${N}}"
+       INTERFACES_COUNT=$((INTERFACES_COUNT + 1))
+
+       return 0
+}
+
+olsrd_write_interface_defaults() {
+       local cfg="$1"
+       validate_varname "$cfg" || return 0
+
+       echo -n "${N}InterfaceDefaults$ifnames${N}{"
+       config_write_options "$OLSRD_INTERFACE_DEFAULTS_SCHEMA" "$cfg" olsrd_write_option "${T}"
+       echo "${N}}"
+
+       return 1
+}
+
+olsrd_update_schema() {
+       local command="$1"
+       validate_varname "$command" || return 0
+       local varname="$2"
+       validate_varname "$varname" || return 0
+       local value="$3"
+       local cfg="$CONFIG_SECTION"
+       local cfgt
+       local cur_varname
+
+       config_get cfgt "$cfg" TYPE
+       case "$cfgt" in
+               olsrd) config_update_schema OLSRD_OLSRD_SCHEMA "$command" "$varname" "$value";;
+               IpcConnect) config_update_schema OLSRD_IPCCONNECT_SCHEMA "$command" "$varname" "$value";;
+               LoadPlugin) config_update_schema OLSRD_LOADPLUGIN_SCHEMA "$command" "$varname" "$value";;
+               Interface) config_update_schema OLSRD_INTERFACE_SCHEMA "$command" "$varname" "$value";;
+               InterfaceDefaults) config_update_schema OLSRD_INTERFACE_DEFAULTS_SCHEMA "$command" "$varname" "$value";;
+       esac
+
+       return 0
+}
+
+olsrd_write_config() {
+       OLSRD_IPVERSION_6AND4=0
+       OLSRD_COUNT=0
+       config_foreach olsrd_write_olsrd olsrd
+       IPCCONNECT_COUNT=0
+       config_foreach olsrd_write_ipcconnect IpcConnect
+       HNA4_COUNT=0
+       config_foreach olsrd_write_hna4 Hna4
+       [ "$HNA4_COUNT" -gt 0 ] && echo "${N}}"
+       HNA6_COUNT=0
+       config_foreach olsrd_write_hna6 Hna6
+       [ "$HNA6_COUNT" -gt 0 ] && echo "${N}}"
+       config_foreach olsrd_write_loadplugin LoadPlugin
+       INTERFACES_COUNT=0
+       config_foreach olsrd_write_interface_defaults InterfaceDefaults
+       config_foreach olsrd_write_interface Interface
+       echo
+
+       return 0
+}
+
+get_wan_ifnames()
+{
+       local wanifnames word catch_next
+
+       which ip >/dev/null || return 1
+
+       set -- $( ip route list exact 0.0.0.0/0 table all )
+       for word in $*; do
+               case "$word" in
+                       dev)
+                               catch_next="true"
+                       ;;
+                       *)
+                               [ -n "$catch_next" ] && {
+                                       case "$wanifnames" in
+                                               *" $word "*)
+                                               ;;
+                                               *)
+                                                       wanifnames="$wanifnames $word "
+                                               ;;
+                                       esac
+
+                                       catch_next=
+                               }
+                       ;;
+               esac
+       done
+
+       echo "$wanifnames"
+}
+
+olsrd_setup_smartgw_rules() {
+       local funcname="olsrd_setup_smartgw_rules"
+       # Check if ipip is installed
+       [ -e /etc/modules.d/[0-9]*-ipip ] || {
+               log "$funcname() Warning: kmod-ipip is missing. SmartGateway will not work until you install it."
+               return 1
+       }
+
+       local wanifnames="$( get_wan_ifnames )"
+
+       if [ -z "$wanifnames" ]; then
+               nowan=1
+       else
+               nowan=0
+       fi
+
+       IP4T=$(which iptables)
+       IP6T=$(which ip6tables)
+
+       # Delete smartgw firewall rules first
+       for IPT in $IP4T $IP6T; do
+               while $IPT -D forwarding_rule -o tnl_+ -j ACCEPT 2> /dev/null; do :;done
+               for IFACE in $wanifnames; do
+                       while $IPT -D forwarding_rule -i tunl0 -o $IFACE -j ACCEPT 2> /dev/null; do :; done
+               done
+               for IFACE in $ifsglobal; do
+                       while $IPT -D input_rule -i $IFACE -p 4 -j ACCEPT 2> /dev/null; do :; done
+               done
+       done
+       while $IP4T -t nat -D postrouting_rule -o tnl_+ -j MASQUERADE 2> /dev/null; do :;done
+
+       if [ "$smartgateway" == "yes" ]; then
+               log "$funcname() Notice: Inserting firewall rules for SmartGateway"
+               if [ ! "$smartgatewayuplink" == "none" ]; then
+                       if [ "$smartgatewayuplink" == "ipv4" ]; then
+                               # Allow everything to be forwarded to tnl_+ and use NAT for it
+                               $IP4T -I forwarding_rule -o tnl_+ -j ACCEPT
+                               $IP4T -t nat -I postrouting_rule -o tnl_+ -j MASQUERADE
+                               # Allow forwarding from tunl0 to (all) wan-interfaces
+                               if [ "$nowan"="0" ]; then
+                                       for IFACE in $wanifnames; do
+                                               $IP4T -A forwarding_rule -i tunl0 -o $IFACE -j ACCEPT
+                                       done
+                               fi
+                               # Allow incoming ipip on all olsr-interfaces
+                               for IFACE in $ifsglobal; do
+                                       $IP4T -I input_rule -i $IFACE -p 4 -j ACCEPT
+                               done
+                       elif [ "$smartgatewayuplink" == "ipv6" ]; then
+                               $IP6T -I forwarding_rule -o tnl_+ -j ACCEPT
+                               if [ "$nowan"="0" ]; then
+                                       for IFACE in $wanifnames; do
+                                               $IP6T -A forwarding_rule -i tunl0 -o $IFACE -j ACCEPT
+                                       done
+                               fi
+                               for IFACE in $ifsglobal; do
+                                       $IP6T -I input_rule -i $IFACE -p 4 -j ACCEPT
+                               done
+                       else
+                               for IPT in $IP4T $IP6T; do
+                                       $IPT -I forwarding_rule -o tnl_+ -j ACCEPT
+                                       $IPT -t nat -I postrouting_rule -o tnl_+ -j MASQUERADE
+                                       if [ "$nowan"="0" ]; then
+                                               for IFACE in $wanifnames; do
+                                                       $IPT -A forwarding_rule -i tunl0 -o $IFACE -j ACCEPT
+                                               done
+                                       fi
+                                       for IFACE in $ifsglobal; do
+                                               $IPT -I input_rule -i $IFACE -p 4 -j ACCEPT
+                                       done
+                               done
+                       fi
+               fi
+       fi
+}
+
+start() {
+       SYSTEM_HOSTNAME=
+       SYSTEM_LAT=
+       SYSTEM_LON=
+       config_load system
+       config_foreach system_config system
+
+       option_cb() {
+               olsrd_update_schema "option" "$@"
+       }
+
+       list_cb() {
+               olsrd_update_schema "list" "$@"
+       }
+
+       . /lib/functions/network.sh
+
+       config_load olsrd
+       reset_cb
+
+       OLSRD_CONFIG_FILE=
+       config_foreach olsrd_find_config_file olsrd
+
+       if [ -z "$OLSRD_CONFIG_FILE" ]; then
+               mkdir -p -- /var/etc/
+               olsrd_write_config > /var/etc/olsrd.conf
+               if [ "$INTERFACES_COUNT" -gt 0 -a "$OLSRD_COUNT" -gt 0 ]; then
+                       OLSRD_CONFIG_FILE=/var/etc/olsrd.conf
+               fi
+       fi
+
+       [ -z "$OLSRD_CONFIG_FILE" ] && return 1
+
+       local bindv6only='0'
+       if [ "$OLSRD_IPVERSION_6AND4" -ne 0 ]; then
+               bindv6only="$(sysctl -n net.ipv6.bindv6only)"
+               sysctl -w net.ipv6.bindv6only=1 > /dev/null
+               sed -e '/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/d' < "$OLSRD_CONFIG_FILE" > /var/etc/olsrd.conf.ipv6
+               sed -e 's/^IpVersion[ ][ ]*6$/IpVersion 4/' -e 's/^\t\t[A-Fa-f0-9.:]*[:][A-Fa-f0-9.:]*[ ][0-9]*$//' < "$OLSRD_CONFIG_FILE" > /var/etc/olsrd.conf.ipv4
+               rm $OLSRD_CONFIG_FILE
+
+               # some filenames should get the suffix .ipv6
+               for file in $latlon_file $hosts_file $services_file $resolv_file $macs_file $wd_file;do
+                       f=$(echo $file|sed 's/\//\\\//g')
+                       sed -i "s/$f/$f.ipv6/g" /var/etc/olsrd.conf.ipv6
+               done
+
+               SERVICE_PID_FILE="$PID6"
+               if service_check /usr/sbin/olsrd; then
+                       error "there is already an IPv6 instance of olsrd running (pid: '$(cat $PID6)'), not starting."
+               else
+                       service_start /usr/sbin/olsrd -f /var/etc/olsrd.conf.ipv6 -nofork
+               fi
+
+               SERVICE_PID_FILE="$PID"
+               if service_check /usr/sbin/olsrd; then
+                       error "there is already an IPv4 instance of olsrd running (pid: '$(cat $PID)'), not starting."
+               else
+                       service_start /usr/sbin/olsrd -f /var/etc/olsrd.conf.ipv4 -nofork
+               fi
+
+               sleep 3
+               sysctl -w net.ipv6.bindv6only="$bindv6only" > /dev/null
+
+       else
+
+               if [ "$ipversion" = "6" ]; then
+                       sed -i '/[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}/d' "$OLSRD_CONFIG_FILE"
+               fi
+
+               SERVICE_PID_FILE="$PID"
+               if service_check /usr/sbin/olsrd; then
+                       error "there is already an IPv4 instance of olsrd running (pid: '$(cat $PID)'), not starting."
+                       return 1
+               else
+                       service_start /usr/sbin/olsrd -f "$OLSRD_CONFIG_FILE" -nofork
+                       service_check /usr/sbin/olsrd || {
+                               log "startup-error: check via: '/usr/sbin/olsrd -f \"$OLSRD_CONFIG_FILE\" -nofork'"
+                       }
+               fi
+       fi
+
+       olsrd_setup_smartgw_rules
+}
+
+stop() {
+       SERVICE_PID_FILE="$PID"
+       service_stop /usr/sbin/olsrd
+
+       SERVICE_PID_FILE="$PID6"
+       service_stop /usr/sbin/olsrd
+}
diff --git a/olsrd/files/olsrd.pud.position.conf b/olsrd/files/olsrd.pud.position.conf
new file mode 100644 (file)
index 0000000..0ab0fb7
--- /dev/null
@@ -0,0 +1,51 @@
+#
+# OLSrd PUD plugin position configuration file
+#
+
+# GPS operating mode.
+# Values : bad, 2d, 3d
+# Default: bad
+# Note   : a value of 'bad' will make the resulting position information invalid
+#          (the mask will indicate that all other information is not present)
+#fix = bad
+
+# GPS quality indicator.
+# Values : bad, low, mid, high
+# Default: high
+#sig = high
+
+# HDOP (Horizontal Dilution Of Precision)
+# Values : floating point value
+# Default: 0.0
+# Note   : From HDOP, VDOP and PDOP are determined: VDOP = HDOP, and PDOP = sqrt(2) * HDOP.
+#hdop = 0.0
+
+# Latitude
+# Values : floating point value
+#          Unit is NMEA like latitude: ddmm.sssss (d=degrees, m=minutes, s=seconds)
+# Default: 0000.00000
+#lat = 0000.00000
+
+# Longitude
+# Values: floating point value
+#          Unit is NMEA like longitude: dddmm.sssss (d=degrees, m=minutes, s=seconds)
+# Default: 00000.00000
+#lon = 00000.00000
+
+# Elevation
+# Values: floating point value
+#         Unit is meters above the mean sea level (geoid)
+# Default: 0.0
+#elv = 0.0
+
+# Speed
+# Values: floating point value
+#         Unit is over the ground in kph
+# Default: 0.0
+#speed = 0.0
+
+# Direction/Track
+# Values: floating point value
+#         Unit is (compass) degrees
+# Default: 0.0
+#direction = 0.0
diff --git a/olsrd/files/olsrd.sgw.speed.conf b/olsrd/files/olsrd.sgw.speed.conf
new file mode 100644 (file)
index 0000000..ae795d8
--- /dev/null
@@ -0,0 +1,14 @@
+#
+# OLSrd Smart Gateway uplink speed configuration file
+#
+
+
+#
+# Upstream speed in kilobit/s
+# Default: 128
+#upstream=128
+
+#
+# Downstream speed in kilobit/s
+# Default: 1024
+#downstream=1024
diff --git a/olsrd/files/olsrd_secure_key b/olsrd/files/olsrd_secure_key
new file mode 100644 (file)
index 0000000..4a7d725
--- /dev/null
@@ -0,0 +1 @@
+1234567890123456
\ No newline at end of file
diff --git a/quagga/Makefile b/quagga/Makefile
new file mode 100644 (file)
index 0000000..fac45c2
--- /dev/null
@@ -0,0 +1,304 @@
+#
+# Copyright (C) 2006-2013 OpenWrt.org
+#
+# This is free software, licensed under the GNU General Public License v2.
+# See /LICENSE for more information.
+#
+
+include $(TOPDIR)/rules.mk
+
+PKG_NAME:=quagga
+PKG_VERSION:=0.99.22
+PKG_RELEASE:=6
+PKG_MD5SUM:=3057bf3a91116a1017dd0df7e5e8ef93
+
+PKG_SOURCE:=$(PKG_NAME)-$(PKG_VERSION).tar.gz
+PKG_SOURCE_URL:=http://download.savannah.gnu.org/releases/quagga/
+PKG_CONFIG_DEPENDS:= \
+       CONFIG_IPV6 \
+       CONFIG_PACKAGE_quagga-watchquagga \
+       CONFIG_PACKAGE_quagga-zebra \
+       CONFIG_PACKAGE_quagga-libzebra \
+       CONFIG_PACKAGE_quagga-libospf \
+       CONFIG_PACKAGE_quagga-bgpd \
+       CONFIG_PACKAGE_quagga-isisd \
+       CONFIG_PACKAGE_quagga-ospf6d \
+       CONFIG_PACKAGE_quagga-ripd \
+       CONFIG_PACKAGE_quagga-ripngd \
+       CONFIG_PACKAGE_quagga-babeld \
+       CONFIG_PACKAGE_quagga-vtysh
+PKG_BUILD_PARALLEL:=1
+PKG_FIXUP:=autoreconf
+PKG_INSTALL:=1
+
+include $(INCLUDE_DIR)/package.mk
+
+define Package/quagga/Default
+  SECTION:=net
+  CATEGORY:=Network
+  SUBMENU:=Routing and Redirection
+  DEPENDS:=quagga
+  TITLE:=The Quagga Software Routing Suite
+  URL:=http://www.quagga.net
+  MAINTAINER:=Vasilis Tsiligiannis <b_tsiligiannis@silverton.gr>
+endef
+
+define Package/quagga
+  $(call Package/quagga/Default)
+  DEPENDS:=+librt
+  MENU:=1
+endef
+
+define Package/quagga/description
+  A routing software package that provides TCP/IP based routing services
+  with routing protocols support such as RIPv1, RIPv2, RIPng, OSPFv2,
+  OSPFv3, BGP-4, and BGP-4+
+endef
+
+define Package/quagga-watchquagga
+  $(call Package/quagga/Default)
+  TITLE:=Quagga watchdog
+  DEPENDS+=+quagga-libzebra
+  DEFAULT:=y if PACKAGE_quagga
+endef
+
+define Package/quagga-zebra
+  $(call Package/quagga/Default)
+  TITLE:=Zebra daemon
+  DEPENDS+=+quagga-libzebra
+  DEFAULT:=y if PACKAGE_quagga
+endef
+
+define Package/quagga-libzebra
+  $(call Package/quagga/Default)
+  TITLE:=zebra library
+endef
+
+define Package/quagga-libospf
+  $(call Package/quagga/Default)
+  TITLE:=OSPF library
+endef
+
+define Package/quagga-bgpd
+  $(call Package/quagga/Default)
+  DEPENDS+=+quagga-libzebra
+  TITLE:=BGPv4, BGPv4+, BGPv4- routing engine
+endef
+
+define Package/quagga-isisd
+  $(call Package/quagga/Default)
+  DEPENDS+=+quagga-libzebra
+  TITLE:=IS-IS routing engine
+endef
+
+define Package/quagga-ospfd
+  $(call Package/quagga/Default)
+  DEPENDS+=+quagga-libospf +quagga-libzebra
+  TITLE:=OSPFv2 routing engine
+endef
+
+define Package/quagga-ospf6d
+  $(call Package/quagga/Default)
+  DEPENDS+=+quagga-libospf +quagga-libzebra @IPV6
+  TITLE:=OSPFv3 routing engine
+endef
+
+define Package/quagga-ripd
+  $(call Package/quagga/Default)
+  DEPENDS+=+quagga-libzebra
+  TITLE:=RIP routing engine
+endef
+
+define Package/quagga-ripngd
+  $(call Package/quagga/Default)
+  DEPENDS+=+quagga-libzebra @IPV6
+  TITLE:=RIPNG routing engine
+endef
+
+define Package/quagga-babeld
+  $(call Package/quagga/Default)
+  DEPENDS+=+quagga-libzebra @IPV6
+  TITLE:=Babel routing engine
+endef
+
+define Package/quagga-vtysh
+  $(call Package/quagga/Default)
+  DEPENDS+=+quagga-libzebra +libreadline +libncurses
+  TITLE:=integrated shell for Quagga routing software
+endef
+
+define Package/quagga-zebra/conffiles
+/etc/quagga/zebra.conf
+endef
+
+define Package/quagga-bgpd/conffiles
+/etc/quagga/bgpd.conf
+endef
+
+define Package/quagga-isisd/conffiles
+/etc/quagga/isisd.conf
+endef
+
+define Package/quagga-ospfd/conffiles
+/etc/quagga/ospfd.conf
+endef
+
+define Package/quagga-ospf6d/conffiles
+/etc/quagga/ospf6d.conf
+endef
+
+define Package/quagga-ripd/conffiles
+/etc/quagga/ripd.conf
+endef
+
+define Package/quagga-ripngd/conffiles
+/etc/quagga/ripngd.conf
+endef
+
+define Package/quagga-babeld/conffiles
+/etc/quagga/babeld.conf
+endef
+
+ifneq ($(SDK),)
+CONFIG_PACKAGE_quagga-libzebra:=m
+CONFIG_PACKAGE_quagga-libospf:=m
+CONFIG_PACKAGE_quagga-watchquagga:=m
+CONFIG_PACKAGE_quagga-zebra:=m
+CONFIG_PACKAGE_quagga-bgpd:=m
+CONFIG_PACKAGE_quagga-isisd:=m
+CONFIG_PACKAGE_quagga-ospf6d:=m
+CONFIG_PACKAGE_quagga-ripd:=m
+CONFIG_PACKAGE_quagga-ripngd:=m
+CONFIG_PACKAGE_quagga-babeld:=m
+CONFIG_PACKAGE_quagga-vtysh:=m
+endif
+
+CONFIGURE_ARGS+= \
+       --localstatedir=/var/run/quagga \
+       --sysconfdir=/etc/quagga/ \
+       --enable-shared \
+       --disable-static \
+       --enable-user=network \
+       --enable-group=network \
+       --enable-pie=no \
+       --enable-multipath=8 \
+       --disable-ospfclient \
+       --disable-capabilities \
+       --disable-doc \
+       $(call autoconf_bool,CONFIG_PACKAGE_quagga-libzebra,zebra) \
+       $(call autoconf_bool,CONFIG_PACKAGE_quagga-libospf,ospfd) \
+       $(call autoconf_bool,CONFIG_PACKAGE_quagga-bgpd,bgpd) \
+       $(call autoconf_bool,CONFIG_PACKAGE_quagga-isisd,isisd) \
+       $(call autoconf_bool,CONFIG_PACKAGE_quagga-ospf6d,ospf6d) \
+       $(call autoconf_bool,CONFIG_PACKAGE_quagga-ripd,ripd) \
+       $(call autoconf_bool,CONFIG_PACKAGE_quagga-ripngd,ripngd) \
+       $(call autoconf_bool,CONFIG_PACKAGE_quagga-babeld,babeld) \
+       $(call autoconf_bool,CONFIG_PACKAGE_quagga-vtysh,vtysh) \
+
+MAKE_FLAGS += \
+       CFLAGS="$(TARGET_CFLAGS) -std=gnu99"
+
+define Package/quagga/install
+       $(INSTALL_DIR) $(1)/usr/sbin
+       $(INSTALL_BIN) ./files/quagga $(1)/usr/sbin/quagga.init
+       $(INSTALL_DIR) $(1)/etc/init.d
+       $(INSTALL_BIN) ./files/quagga.init $(1)/etc/init.d/quagga
+endef
+
+define Package/quagga-watchquagga/install
+       $(INSTALL_DIR) $(1)/usr/sbin
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/watchquagga $(1)/usr/sbin/
+endef
+
+define Package/quagga-zebra/install
+       $(INSTALL_DIR) $(1)/usr/sbin
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/zebra $(1)/usr/sbin/
+       $(INSTALL_DIR) $(1)/etc/quagga
+       chmod 0750 $(1)/etc/quagga
+       $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/zebra.conf
+endef
+
+define Package/quagga-bgpd/install
+       $(INSTALL_DIR) $(1)/usr/sbin
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/bgpd $(1)/usr/sbin/
+       $(INSTALL_DIR) $(1)/etc/quagga
+       chmod 0750 $(1)/etc/quagga
+       $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/bgpd.conf
+endef
+
+define Package/quagga-isisd/install
+       $(INSTALL_DIR) $(1)/usr/sbin
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/isisd $(1)/usr/sbin/
+       $(INSTALL_DIR) $(1)/etc/quagga
+       chmod 0750 $(1)/etc/quagga
+       $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/isisd.conf
+endef
+
+define Package/quagga-ospfd/install
+       $(INSTALL_DIR) $(1)/usr/sbin
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/ospfd $(1)/usr/sbin/
+       $(INSTALL_DIR) $(1)/etc/quagga
+       chmod 0750 $(1)/etc/quagga
+       $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/ospfd.conf
+endef
+
+define Package/quagga-ospf6d/install
+       $(INSTALL_DIR) $(1)/usr/sbin
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/ospf6d $(1)/usr/sbin/
+       $(INSTALL_DIR) $(1)/etc/quagga
+       chmod 0750 $(1)/etc/quagga
+       $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/ospf6d.conf
+endef
+
+define Package/quagga-ripd/install
+       $(INSTALL_DIR) $(1)/usr/sbin
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/ripd $(1)/usr/sbin/
+       $(INSTALL_DIR) $(1)/etc/quagga
+       chmod 0750 $(1)/etc/quagga
+       $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/ripd.conf
+endef
+
+define Package/quagga-ripngd/install
+       $(INSTALL_DIR) $(1)/usr/sbin
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/ripngd $(1)/usr/sbin/
+       $(INSTALL_DIR) $(1)/etc/quagga
+       chmod 0750 $(1)/etc/quagga
+       $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/ripngd.conf
+endef
+
+define Package/quagga-babeld/install
+       $(INSTALL_DIR) $(1)/usr/sbin
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/sbin/babeld $(1)/usr/sbin/
+       $(INSTALL_DIR) $(1)/etc/quagga
+       chmod 0750 $(1)/etc/quagga
+       $(INSTALL_CONF) ./files/quagga.conf $(1)/etc/quagga/babeld.conf
+endef
+
+define Package/quagga-vtysh/install
+       $(INSTALL_DIR) $(1)/usr/bin
+       $(INSTALL_BIN) $(PKG_INSTALL_DIR)/usr/bin/vtysh $(1)/usr/bin/
+endef
+
+define Package/quagga-libospf/install
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(CP) $(PKG_INSTALL_DIR)/usr/lib/libospf.so.* $(1)/usr/lib/
+endef
+
+define Package/quagga-libzebra/install
+       $(INSTALL_DIR) $(1)/usr/lib
+       $(CP) $(PKG_INSTALL_DIR)/usr/lib/libzebra.so.* $(1)/usr/lib/
+endef
+
+$(eval $(call BuildPackage,quagga))
+$(eval $(call BuildPackage,quagga-libzebra))
+$(eval $(call BuildPackage,quagga-libospf))
+$(eval $(call BuildPackage,quagga-watchquagga))
+$(eval $(call BuildPackage,quagga-zebra))
+$(eval $(call BuildPackage,quagga-bgpd))
+$(eval $(call BuildPackage,quagga-isisd))
+$(eval $(call BuildPackage,quagga-ospfd))
+$(eval $(call BuildPackage,quagga-ospf6d))
+$(eval $(call BuildPackage,quagga-ripd))
+$(eval $(call BuildPackage,quagga-ripngd))
+$(eval $(call BuildPackage,quagga-babeld))
+$(eval $(call BuildPackage,quagga-vtysh))
diff --git a/quagga/files/quagga b/quagga/files/quagga
new file mode 100644 (file)
index 0000000..b172090
--- /dev/null
@@ -0,0 +1,335 @@
+#!/bin/sh
+#
+# quagga       Starts/stops quagga daemons and watchquagga.
+#              Create a daemon.conf file to have that routing daemon
+#              started/stopped automagically when using this script
+#              without any daemon names as args.
+#              If watchquagga is available, it will also be
+#              started/stopped if the script is called without
+#              any daemon names.
+#
+
+ME=$(basename $0)
+
+usage() {
+       echo "Usage: ${ME} {start|stop|restart} [daemon ...]"
+       exit 2
+}
+
+if [ -z "$1" ]
+then
+       usage
+else
+       COMMAND=$1
+fi
+shift
+ARG_DAEMONS=$*
+BINDIR=/usr/sbin
+CONFDIR=/etc/quagga
+STATEDIR=/var/run/quagga
+RUNUSER=network
+RUNGROUP=$RUNUSER
+DAEMONS="zebra ripd ripngd ospfd ospf6d bgpd"
+DAEMON_FLAGS=-d
+WATCHQUAGGA_FLAGS="-d -z -T 60 -R"
+WATCHQUAGGA_CMD="$0 watchrestart"
+if [ ${COMMAND} != "watchrestart" -a -x "${BINDIR}/watchquagga" ]
+then
+       DAEMONS="${DAEMONS} watchquagga"
+fi
+DAEMONS_STARTSEQ=${DAEMONS}
+
+reverse()
+{
+       local revlist r
+        revlist=
+        for r
+       do
+                revlist="$r $revlist"
+        done
+        echo $revlist
+}
+
+DAEMONS_STOPSEQ=$(reverse ${DAEMONS_STARTSEQ})
+
+#pidof() {
+#      ps ax | awk 'match($5, "(^|/)'"$1"'$") > 0 { printf " %s", $1 }'
+#}
+
+quit() {
+       echo "${ME}: $1"
+       exit 0
+}
+
+die() {
+       echo "${ME}: $1"
+       exit 1
+}
+
+is_in() {
+       local i
+       for i in $2
+       do
+               [ "$1" = "$i" ] && return 0
+       done
+       return 1
+}
+
+select_subset() {
+       local unknown i j
+       unknown=
+       RESULT=
+       for i in $1
+       do
+               is_in $i "$2" || unknown="$unknown $i"
+       done
+       if [ -n "$unknown" ]
+       then
+               RESULT=$unknown
+               return 1
+       else
+               for j in $2
+               do
+                       is_in $j "$1" && RESULT="$RESULT $j"
+               done
+               return 0
+       fi
+}
+
+# check command
+
+case ${COMMAND}
+in
+       start|stop|restart)
+               ;;
+       watchrestart)
+               if [ -n "$ARG_DAEMONS" ]
+               then
+                       echo "${ME}: watchrestart mode is only for use by watchquagga"
+                       exit 2
+               fi
+               ;;
+       *)
+               usage
+               ;;
+esac
+
+# select daemons to start
+
+case ${COMMAND}
+in
+       start|restart|watchrestart)
+               START_DAEMONS=
+               for d in ${DAEMONS_STARTSEQ}
+               do
+                       [ -x "${BINDIR}/${d}" -a -f "${CONFDIR}/${d}.conf" ] \
+                       && START_DAEMONS="${START_DAEMONS}${d} "
+               done
+               WATCHQUAGGA_DAEMONS=${START_DAEMONS}
+               if is_in watchquagga "${DAEMONS_STARTSEQ}"
+               then
+                       START_DAEMONS="${START_DAEMONS} watchquagga"
+               fi
+               if [ -n "${ARG_DAEMONS}" ]
+               then
+                       if select_subset "${ARG_DAEMONS}" "${DAEMONS}"
+                       then
+                               if select_subset "${ARG_DAEMONS}" "${START_DAEMONS}"
+                               then
+                                       START_DAEMONS=${RESULT}
+                               else
+                                       die "these daemons are not startable:${RESULT}."
+                               fi
+                       else
+                               die "unknown daemons:${RESULT}; choose from: ${DAEMONS}."
+                       fi
+               fi
+               ;;
+esac
+
+# select daemons to stop
+
+case ${COMMAND}
+in
+       stop|restart|watchrestart)
+               STOP_DAEMONS=${DAEMONS_STOPSEQ}
+               if [ -n "${ARG_DAEMONS}" ]
+               then
+                       if select_subset "${ARG_DAEMONS}" "${STOP_DAEMONS}"
+                       then
+                               STOP_DAEMONS=${RESULT}
+                       else
+                               die "unknown daemons:${RESULT}; choose from: ${DAEMONS}."
+                       fi
+               fi
+               stop_daemons=
+               for d in ${STOP_DAEMONS}
+               do
+                       pidfile=${STATEDIR}/${d}.pid
+                       if [ -f "${pidfile}" -o -n "$(pidof ${d})" ]
+                       then
+                               stop_daemons="${stop_daemons}${d} "
+                       elif [ -n "${ARG_DAEMONS}" ]
+                       then
+                               echo "${ME}: found no ${d} process running."
+                       fi
+               done
+               STOP_DAEMONS=${stop_daemons}
+               ;;
+esac
+
+# stop daemons
+
+for d in $STOP_DAEMONS
+do
+       echo -n "${ME}: Stopping ${d} ... "
+       pidfile=${STATEDIR}/${d}.pid
+       if [ -f "${pidfile}" ]
+       then
+               file_pid=$(cat ${pidfile})
+               if [ -z "${file_pid}" ]
+               then
+                       echo -n "no pid file entry found ... "
+               fi
+       else
+               file_pid=
+               echo -n "no pid file found ... "
+       fi
+       proc_pid=$(pidof ${d})
+       if [ -z "${proc_pid}" ]
+       then
+               echo -n "found no ${d} process running ... "
+       else
+               count=0
+               notinpidfile=
+               for p in ${proc_pid}
+               do
+                       count=$((${count}+1))
+                       if kill ${p}
+                       then
+                               echo -n "killed ${p} ... "
+                       else
+                               echo -n "failed to kill ${p} ... "
+                       fi
+                       [ "${p}" = "${file_pid}" ] \
+                       || notinpidfile="${notinpidfile} ${p}"
+               done
+               [ ${count} -le 1 ] \
+               || echo -n "WARNING: ${count} ${d} processes were found running ... "
+               for n in ${notinpidfile}
+               do
+                       echo -n "WARNING: process ${n} was not in pid file ... "
+               done
+       fi
+       count=0
+       survivors=$(pidof ${d})
+       while [ -n "${survivors}" ]
+       do
+               sleep 1
+               count=$((${count}+1))
+               survivors=$(pidof ${d})
+               [ -z "${survivors}" -o ${count} -gt 5 ] && break
+               for p in ${survivors}
+               do
+                       sleep 1
+                       echo -n "${p} "
+                       kill ${p}
+               done
+       done
+       survivors=$(pidof ${d})
+       [ -n "${survivors}" ] && \
+       if kill -KILL ${survivors}
+       then
+               echo -n "KILLed ${survivors} ... "
+       else
+               echo -n "failed to KILL ${survivors} ... "
+       fi
+       sleep 1
+       survivors=$(pidof ${d})
+       if [ -z "${survivors}" ]
+       then
+               echo -n "done."
+               if [ -f "${pidfile}" ]
+               then
+                       rm -f ${pidfile} \
+                       || echo -n " Failed to remove pidfile."
+               fi
+       else
+               echo -n "failed to stop ${survivors} - giving up."
+               if [ "${survivors}" != "${file_pid}" ]
+               then
+                       if echo "${survivors}" > ${pidfile}
+                       then
+                               chown ${RUNUSER}:${RUNGROUP} ${pidfile}
+                               echo -n " Wrote ${survivors} to pidfile."
+                       else
+                               echo -n " Failed to write ${survivors} to pidfile."
+                       fi
+               fi
+       fi
+       echo
+done
+
+# start daemons
+
+if [ -n "$START_DAEMONS" ]
+then
+       [ -d ${CONFDIR} ] \
+       || quit "${ME}: no config directory ${CONFDIR} - exiting."
+       chown -R ${RUNUSER}:${RUNGROUP} ${CONFDIR}
+       [ -d ${STATEDIR} ] || mkdir -p ${STATEDIR} \
+       || die "${ME}: could not create state directory ${STATEDIR} - exiting."
+       chown -R ${RUNUSER}:${RUNGROUP} ${STATEDIR}
+
+       for d in $START_DAEMONS
+       do
+               echo -n "${ME}: Starting ${d} ... "
+               proc_pid=$(pidof ${d})
+               pidfile=${STATEDIR}/${d}.pid
+               file_pid=
+               if [ -f "${pidfile}" ]
+               then
+                       file_pid=$(cat ${pidfile})
+                       if [ -n "${file_pid}" ]
+                       then
+                               echo -n "found old pid file entry ${file_pid} ... "
+                       fi
+               fi
+               if [ -n "${proc_pid}" ]
+               then
+                       echo -n "found ${d} running (${proc_pid}) - skipping ${d}."
+                       if [ "${proc_pid}" != "${file_pid}" ]
+                       then
+                               if echo "${proc_pid}" > ${pidfile}
+                               then
+                                       chown ${RUNUSER}:${RUNGROUP} ${pidfile}
+                                       echo -n " Wrote ${proc_pid} to pidfile."
+                               else
+                                       echo -n " Failed to write ${proc_pid} to pidfile."
+                               fi
+                       fi
+               elif rm -f "${pidfile}"
+               then
+                       if [ "${d}" = "watchquagga" ]
+                       then
+                               "${BINDIR}/${d}" \
+                                       ${WATCHQUAGGA_FLAGS} \
+                                       "${WATCHQUAGGA_CMD}" \
+                                       ${WATCHQUAGGA_DAEMONS}
+                               status=$?
+                       else
+                               "${BINDIR}/${d}" ${DAEMON_FLAGS}
+                               status=$?
+                       fi
+                       if [ $status -eq 0 ]
+                       then
+                               echo -n "done."
+                       else
+                               echo -n "failed."
+                       fi
+               else
+                       echo -n " failed to remove pidfile."
+               fi
+               echo
+       done
+fi
diff --git a/quagga/files/quagga.conf b/quagga/files/quagga.conf
new file mode 100644 (file)
index 0000000..fb7a54e
--- /dev/null
@@ -0,0 +1,7 @@
+password zebra
+!
+access-list vty permit 127.0.0.0/8
+access-list vty deny any
+!
+line vty
+ access-class vty
diff --git a/quagga/files/quagga.init b/quagga/files/quagga.init
new file mode 100644 (file)
index 0000000..21fbf2c
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/sh /etc/rc.common
+# Copyright (C) 2006 OpenWrt.org
+
+START=60
+start() {
+       /usr/sbin/quagga.init start
+}
+
+stop() {
+       /usr/sbin/quagga.init stop
+}
diff --git a/quagga/patches/120-quagga_manet.patch b/quagga/patches/120-quagga_manet.patch
new file mode 100644 (file)
index 0000000..684a27d
--- /dev/null
@@ -0,0 +1,243 @@
+--- a/lib/log.c
++++ b/lib/log.c
+@@ -925,13 +925,19 @@ proto_redistnum(int afi, const char *s)
+       return ZEBRA_ROUTE_STATIC;
+       else if (strncmp (s, "r", 1) == 0)
+       return ZEBRA_ROUTE_RIP;
+-      else if (strncmp (s, "o", 1) == 0)
++      else if (strncmp (s, "os", 2) == 0)
+       return ZEBRA_ROUTE_OSPF;
+       else if (strncmp (s, "i", 1) == 0)
+       return ZEBRA_ROUTE_ISIS;
+       else if (strncmp (s, "bg", 2) == 0)
+       return ZEBRA_ROUTE_BGP;
+-      else if (strncmp (s, "ba", 2) == 0)
++      else if (strncmp (s, "h", 1) == 0)
++      return ZEBRA_ROUTE_HSLS;
++      else if (strncmp (s, "ol", 2) == 0)
++      return ZEBRA_ROUTE_OLSR;
++      else if (strncmp (s, "bat", 3) == 0)
++      return ZEBRA_ROUTE_BATMAN;
++      else if (strncmp (s, "bab", 3) == 0)
+       return ZEBRA_ROUTE_BABEL;
+     }
+   if (afi == AFI_IP6)
+@@ -944,13 +950,19 @@ proto_redistnum(int afi, const char *s)
+       return ZEBRA_ROUTE_STATIC;
+       else if (strncmp (s, "r", 1) == 0)
+       return ZEBRA_ROUTE_RIPNG;
+-      else if (strncmp (s, "o", 1) == 0)
++      else if (strncmp (s, "os", 2) == 0)
+       return ZEBRA_ROUTE_OSPF6;
+       else if (strncmp (s, "i", 1) == 0)
+       return ZEBRA_ROUTE_ISIS;
+       else if (strncmp (s, "bg", 2) == 0)
+       return ZEBRA_ROUTE_BGP;
+-      else if (strncmp (s, "ba", 2) == 0)
++      else if (strncmp (s, "h", 1) == 0)
++      return ZEBRA_ROUTE_HSLS;
++      else if (strncmp (s, "ol", 2) == 0)
++      return ZEBRA_ROUTE_OLSR;
++      else if (strncmp (s, "bat", 3) == 0)
++      return ZEBRA_ROUTE_BATMAN;
++      else if (strncmp (s, "bab", 3) == 0)
+       return ZEBRA_ROUTE_BABEL;
+     }
+   return -1;
+--- a/lib/route_types.txt
++++ b/lib/route_types.txt
+@@ -51,13 +51,9 @@ ZEBRA_ROUTE_OSPF,       ospf,      ospfd
+ ZEBRA_ROUTE_OSPF6,      ospf6,     ospf6d, 'O', 0, 1, "OSPFv6"
+ ZEBRA_ROUTE_ISIS,       isis,      isisd,  'I', 1, 1, "IS-IS"
+ ZEBRA_ROUTE_BGP,        bgp,       bgpd,   'B', 1, 1, "BGP"
+-# HSLS and OLSR both are AFI independent (so: 1, 1), however
+-# we want to disable for them for general Quagga distribution.
+-# This at least makes it trivial for users of these protocols
+-# to 'switch on' redist support (direct numeric entry remaining
+-# possible).
+-ZEBRA_ROUTE_HSLS,       hsls,      hslsd,  'H', 0, 0, "HSLS"
+-ZEBRA_ROUTE_OLSR,       olsr,      olsrd,  'o', 0, 0, "OLSR"
++ZEBRA_ROUTE_HSLS,       hsls,      hslsd,  'H', 1, 1, "HSLS"
++ZEBRA_ROUTE_OLSR,       olsr,      olsrd,  'o', 1, 1, "OLSR"
++ZEBRA_ROUTE_BATMAN,     batman,    batmand,'b', 1, 1, "BATMAN"
+ ZEBRA_ROUTE_BABEL,      babel,     babeld, 'A', 1, 1, "Babel"
+ ## help strings
+@@ -72,5 +68,6 @@ ZEBRA_ROUTE_OSPF6,  "Open Shortest Path
+ ZEBRA_ROUTE_ISIS,   "Intermediate System to Intermediate System (IS-IS)"
+ ZEBRA_ROUTE_BGP,    "Border Gateway Protocol (BGP)"
+ ZEBRA_ROUTE_HSLS,   "Hazy-Sighted Link State Protocol (HSLS)"
+-ZEBRA_ROUTE_OLSR,   "Optimised Link State Routing (OLSR)"
++ZEBRA_ROUTE_OLSR,   "Optimized Link State Routing (OLSR)"
++ZEBRA_ROUTE_BATMAN, "Better Approach to Mobile Ad-Hoc Networking (BATMAN)"
+ ZEBRA_ROUTE_BABEL,  "Babel routing protocol (Babel)"
+--- a/ripd/rip_zebra.c
++++ b/ripd/rip_zebra.c
+@@ -206,9 +206,12 @@ static struct {
+   {ZEBRA_ROUTE_KERNEL,  1, "kernel"},
+   {ZEBRA_ROUTE_CONNECT, 1, "connected"},
+   {ZEBRA_ROUTE_STATIC,  1, "static"},
+-  {ZEBRA_ROUTE_OSPF,    1, "ospf"},
++  {ZEBRA_ROUTE_OSPF,    2, "ospf"},
+   {ZEBRA_ROUTE_BGP,     2, "bgp"},
+-  {ZEBRA_ROUTE_BABEL,   2, "babel"},
++  {ZEBRA_ROUTE_HSLS,    1, "hsls"},
++  {ZEBRA_ROUTE_OLSR,    2, "olsr"},
++  {ZEBRA_ROUTE_BATMAN,  3, "batman"},
++  {ZEBRA_ROUTE_BABEL,   3, "babel"},
+   {0, 0, NULL}
+ };
+--- a/ripngd/ripng_zebra.c
++++ b/ripngd/ripng_zebra.c
+@@ -216,9 +216,12 @@ static struct {
+   {ZEBRA_ROUTE_KERNEL,  1, "kernel"},
+   {ZEBRA_ROUTE_CONNECT, 1, "connected"},
+   {ZEBRA_ROUTE_STATIC,  1, "static"},
+-  {ZEBRA_ROUTE_OSPF6,   1, "ospf6"},
++  {ZEBRA_ROUTE_OSPF6,   2, "ospf6"},
+   {ZEBRA_ROUTE_BGP,     2, "bgp"},
+-  {ZEBRA_ROUTE_BABEL,   2, "babel"},
++  {ZEBRA_ROUTE_HSLS,    1, "hsls"},
++  {ZEBRA_ROUTE_OLSR,    2, "olsr"},
++  {ZEBRA_ROUTE_BATMAN,  3, "batman"},
++  {ZEBRA_ROUTE_BABEL,   3, "babel"},
+   {0, 0, NULL}
+ };
+--- a/zebra/rt_netlink.c
++++ b/zebra/rt_netlink.c
+@@ -1609,6 +1609,9 @@ netlink_route_multipath (int cmd, struct
+                         addattr_l (&req.n, sizeof req, RTA_PREFSRC,
+                                &nexthop->src.ipv4, bytelen);
++                    if (rib->type == ZEBRA_ROUTE_OLSR)
++                      req.r.rtm_scope = RT_SCOPE_LINK;
++
+                     if (IS_ZEBRA_DEBUG_KERNEL)
+                       zlog_debug("netlink_route_multipath() (single hop): "
+                                  "nexthop via if %u", nexthop->ifindex);
+--- a/zebra/zebra_rib.c
++++ b/zebra/zebra_rib.c
+@@ -68,6 +68,9 @@ static const struct
+   [ZEBRA_ROUTE_OSPF6]   = {ZEBRA_ROUTE_OSPF6,   110},
+   [ZEBRA_ROUTE_ISIS]    = {ZEBRA_ROUTE_ISIS,    115},
+   [ZEBRA_ROUTE_BGP]     = {ZEBRA_ROUTE_BGP,      20  /* IBGP is 200. */},
++  [ZEBRA_ROUTE_HSLS]    = {ZEBRA_ROUTE_HSLS,      0},
++  [ZEBRA_ROUTE_OLSR]    = {ZEBRA_ROUTE_OLSR,      0},
++  [ZEBRA_ROUTE_BATMAN]  = {ZEBRA_ROUTE_BATMAN,    0},
+   [ZEBRA_ROUTE_BABEL]   = {ZEBRA_ROUTE_BABEL,    95},
+   /* no entry/default: 150 */
+ };
+@@ -456,6 +459,18 @@ nexthop_active_ipv4 (struct rib *rib, st
+                 }
+             return 0;
+           }
++        else if (match->type == ZEBRA_ROUTE_OLSR)
++          {
++            for (newhop = match->nexthop; newhop; newhop = newhop->next)
++              if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)
++                  && newhop->type == NEXTHOP_TYPE_IFINDEX)
++                {
++                  if (nexthop->type == NEXTHOP_TYPE_IPV4)
++                    nexthop->ifindex = newhop->ifindex;
++                  return 1;
++                }
++            return 0;
++          }
+         else
+           {
+             return 0;
+@@ -560,6 +575,18 @@ nexthop_active_ipv6 (struct rib *rib, st
+                 }
+             return 0;
+           }
++        else if (match->type == ZEBRA_ROUTE_OLSR)
++          {
++            for (newhop = match->nexthop; newhop; newhop = newhop->next)
++              if (CHECK_FLAG (newhop->flags, NEXTHOP_FLAG_FIB)
++                  && newhop->type == NEXTHOP_TYPE_IFINDEX)
++                {
++                  if (nexthop->type == NEXTHOP_TYPE_IPV6)
++                    nexthop->ifindex = newhop->ifindex;
++                  return 1;
++                }
++            return 0;
++          }
+         else
+           {
+             return 0;
+@@ -1376,6 +1403,8 @@ static const u_char meta_queue_map[ZEBRA
+   [ZEBRA_ROUTE_ISIS]    = 2,
+   [ZEBRA_ROUTE_BGP]     = 3,
+   [ZEBRA_ROUTE_HSLS]    = 4,
++  [ZEBRA_ROUTE_OLSR]    = 4,
++  [ZEBRA_ROUTE_BATMAN]  = 4,
+   [ZEBRA_ROUTE_BABEL]   = 2,
+ };
+--- a/zebra/zebra_snmp.c
++++ b/zebra/zebra_snmp.c
+@@ -245,6 +245,12 @@ proto_trans(int type)
+       return 1; /* shouldn't happen */
+     case ZEBRA_ROUTE_BGP:
+       return 14; /* bgp */
++    case ZEBRA_ROUTE_HSLS:
++      return 1; /* other */
++    case ZEBRA_ROUTE_OLSR:
++      return 1; /* other */
++    case ZEBRA_ROUTE_BATMAN:
++      return 1; /* other */
+     default:
+       return 1; /* other */
+     }
+--- a/zebra/zebra_vty.c
++++ b/zebra/zebra_vty.c
+@@ -558,7 +558,10 @@ vty_show_ip_route_detail (struct vty *vt
+         || rib->type == ZEBRA_ROUTE_OSPF
+         || rib->type == ZEBRA_ROUTE_BABEL
+         || rib->type == ZEBRA_ROUTE_ISIS
+-        || rib->type == ZEBRA_ROUTE_BGP)
++        || rib->type == ZEBRA_ROUTE_BGP
++        || rib->type == ZEBRA_ROUTE_HSLS
++        || rib->type == ZEBRA_ROUTE_OLSR
++        || rib->type == ZEBRA_ROUTE_BATMAN)
+       {
+         time_t uptime;
+         struct tm *tm;
+@@ -777,7 +780,10 @@ vty_show_ip_route (struct vty *vty, stru
+         || rib->type == ZEBRA_ROUTE_OSPF
+         || rib->type == ZEBRA_ROUTE_BABEL
+         || rib->type == ZEBRA_ROUTE_ISIS
+-        || rib->type == ZEBRA_ROUTE_BGP)
++        || rib->type == ZEBRA_ROUTE_BGP
++        || rib->type == ZEBRA_ROUTE_HSLS
++        || rib->type == ZEBRA_ROUTE_OLSR
++        || rib->type == ZEBRA_ROUTE_BATMAN)
+       {
+         time_t uptime;
+         struct tm *tm;
+@@ -1570,7 +1576,10 @@ vty_show_ipv6_route_detail (struct vty *
+         || rib->type == ZEBRA_ROUTE_OSPF6
+         || rib->type == ZEBRA_ROUTE_BABEL
+         || rib->type == ZEBRA_ROUTE_ISIS
+-        || rib->type == ZEBRA_ROUTE_BGP)
++        || rib->type == ZEBRA_ROUTE_BGP
++        || rib->type == ZEBRA_ROUTE_HSLS
++        || rib->type == ZEBRA_ROUTE_OLSR
++        || rib->type == ZEBRA_ROUTE_BATMAN)
+       {
+         time_t uptime;
+         struct tm *tm;
+@@ -1750,7 +1759,10 @@ vty_show_ipv6_route (struct vty *vty, st
+         || rib->type == ZEBRA_ROUTE_OSPF6
+         || rib->type == ZEBRA_ROUTE_BABEL
+         || rib->type == ZEBRA_ROUTE_ISIS
+-        || rib->type == ZEBRA_ROUTE_BGP)
++        || rib->type == ZEBRA_ROUTE_BGP
++        || rib->type == ZEBRA_ROUTE_HSLS
++        || rib->type == ZEBRA_ROUTE_OLSR
++        || rib->type == ZEBRA_ROUTE_BATMAN)
+       {
+         time_t uptime;
+         struct tm *tm;
diff --git a/quagga/patches/140-holdtimer-set.patch b/quagga/patches/140-holdtimer-set.patch
new file mode 100644 (file)
index 0000000..b699775
--- /dev/null
@@ -0,0 +1,22 @@
+--- a/bgpd/bgp_network.c
++++ b/bgpd/bgp_network.c
+@@ -193,8 +193,7 @@ bgp_accept (struct thread *thread)
+     peer->fd = bgp_sock;
+     peer->status = Active;
+     peer->local_id = peer1->local_id;
+-    peer->v_holdtime = peer1->v_holdtime;
+-    peer->v_keepalive = peer1->v_keepalive;
++    peer->v_holdtime = BGP_LARGE_HOLDTIME;
+     /* Make peer's address string. */
+     sockunion2str (&su, buf, SU_ADDRSTRLEN);
+--- a/bgpd/bgpd.h
++++ b/bgpd/bgpd.h
+@@ -732,6 +732,7 @@ struct bgp_nlri
+ /* BGP timers default value.  */
+ #define BGP_INIT_START_TIMER                     5
+ #define BGP_ERROR_START_TIMER                   30
++#define BGP_LARGE_HOLDTIME                     240
+ #define BGP_DEFAULT_HOLDTIME                   180
+ #define BGP_DEFAULT_KEEPALIVE                   60 
+ #define BGP_DEFAULT_ASORIGINATE                 15
diff --git a/quagga/patches/150-no-cross-fs-link.patch b/quagga/patches/150-no-cross-fs-link.patch
new file mode 100644 (file)
index 0000000..32c1208
--- /dev/null
@@ -0,0 +1,40 @@
+--- a/lib/command.c
++++ b/lib/command.c
+@@ -2527,6 +2527,13 @@ DEFUN (config_write_file,
+                VTY_NEWLINE);
+         goto finished;
+       }
++
++#if 0
++  /* This code fails on UNION MOUNTs and similar filesystems if the
++   * config file is still on the RO layer. Hardlinks across layers
++   * will not work and cause quagga to fail saving the configuration...
++   * should use rename() to move files around...
++   */
+   if (link (config_file, config_file_sav) != 0)
+     {
+       vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav,
+@@ -2540,7 +2547,23 @@ DEFUN (config_write_file,
+               VTY_NEWLINE);
+       goto finished;
+     }
++#else
++  /* And this is the code that hopefully does work */
++  if (rename (config_file, config_file_sav) != 0)
++    {
++      vty_out (vty, "Can't backup old configuration file %s.%s", config_file_sav,
++              VTY_NEWLINE);
++      goto finished;
++    }
++  sync ();
++#endif
++
++#if 0
++  /* same here. Please no cross-filesystem hardlinks... */
+   if (link (config_file_tmp, config_file) != 0)
++#else
++  if (rename (config_file_tmp, config_file) != 0)
++#endif
+     {
+       vty_out (vty, "Can't save configuration file %s.%s", config_file,
+              VTY_NEWLINE);
diff --git a/quagga/patches/170-use-supported-pagers.patch b/quagga/patches/170-use-supported-pagers.patch
new file mode 100644 (file)
index 0000000..15595a7
--- /dev/null
@@ -0,0 +1,29 @@
+--- a/vtysh/vtysh.c
++++ b/vtysh/vtysh.c
+@@ -268,7 +268,7 @@ vtysh_pager_init (void)
+   if (pager_defined)
+     vtysh_pager_name = strdup (pager_defined);
+   else
+-    vtysh_pager_name = strdup ("more");
++    vtysh_pager_name = strdup ("cat");
+ }
+ /* Command execution over the vty interface. */
+@@ -1884,7 +1884,7 @@ DEFUN (vtysh_terminal_length,
+ {
+   int lines;
+   char *endptr = NULL;
+-  char default_pager[10];
++  char default_pager[12];
+   lines = strtol (argv[0], &endptr, 10);
+   if (lines < 0 || lines > 512 || *endptr != '\0')
+@@ -1901,7 +1901,7 @@ DEFUN (vtysh_terminal_length,
+   if (lines != 0)
+     {
+-      snprintf(default_pager, 10, "more -%i", lines);
++      snprintf(default_pager, 12, "head -n %i", lines);
+       vtysh_pager_name = strdup (default_pager);
+     }