mirror of
https://github.com/openwrt/routing.git
synced 2026-07-18 01:22:05 +04:00
Move bmx6 and luci-app-bmx6 packages into root directory
This commit is contained in:
@@ -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'
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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%>
|
||||
|
||||
@@ -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%>
|
||||
|
||||
@@ -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&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%>
|
||||
|
||||
@@ -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();">  redraw  </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%>
|
||||
@@ -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%>
|
||||
@@ -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%>
|
||||
@@ -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%>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
<%+footer%>
|
||||
@@ -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%>
|
||||
|
||||
@@ -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%>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</td>
|
||||
</tr>
|
||||
<% end %>
|
||||
</table>
|
||||
|
||||
<br />
|
||||
<%+footer%>
|
||||
@@ -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%>
|
||||
|
||||
@@ -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%>
|
||||
@@ -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%>
|
||||
|
||||
@@ -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%>
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
<%+header%>
|
||||
<h2><a id="content" name="content"><%:Wireless%></a></h2>
|
||||
<br />
|
||||
<h3>Wireless</h3>
|
||||
<%=data%>
|
||||
<br />
|
||||
<%+footer%>
|
||||
@@ -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
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 4.9 KiB |
@@ -0,0 +1,29 @@
|
||||
/**
|
||||
* 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);
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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");};
|
||||
@@ -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);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -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
|
||||
);
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
@@ -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;
|
||||
}
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 1.8 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 923 B |
Reference in New Issue
Block a user