# HG changeset patch # User Tomas Zeman # Date 1301838902 -7200 # Node ID cf829ec742b3a87a3cf77ba012eb7e1cc3c4c157 # Parent 69e26359f2c883eb5fd86bfb0e7f0e0cded9361d Main project functionality diff -r 69e26359f2c8 -r cf829ec742b3 src/main/resources/default.props --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/resources/default.props Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,14 @@ +# RadView application properties +# + +db.radacct.url=jdbc:mysql://127.0.0.1:7803/radacct +db.radacct.user=dev +db.radacct.pass=dev + +db.radius.url=jdbc:mysql://127.0.0.1:7803/radius +db.radius.user=dev +db.radius.pass=dev + +db.syslog.url=jdbc:mysql://127.0.0.1:7803/syslog +db.syslog.user=dev +db.syslog.pass=dev diff -r 69e26359f2c8 -r cf829ec742b3 src/main/resources/logback.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/resources/logback.xml Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,14 @@ + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + diff -r 69e26359f2c8 -r cf829ec742b3 src/main/scala/bootstrap/liftweb/Boot.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/bootstrap/liftweb/Boot.scala Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,97 @@ +/* + * Copyright 2011 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package bootstrap.liftweb + +import net.liftweb.common._ +import net.liftweb.http._ +import net.liftweb.mapper._ +import net.liftweb.sitemap._ +import net.liftweb.sitemap.Loc._ +import net.liftweb.util._ +import net.liftweb.widgets.menu.MenuWidget +import net.tz.lift.snippet.ActionLinks +import net.tz.lift.util.YmdDateTimeConverter +import radview.model._ +import radview.snippet._ +import scala.xml.NodeSeq + +class Boot extends Logger { + + def boot = { + /* DB stuff */ + initDb("radacct", RadAcctConnectionIdentifier) + initDb("radius", RadiusConnectionIdentifier) + initDb("syslog", SyslogConnectionIdentifier) + S.addAround(DB.buildLoanWrapper(List(RadAcctConnectionIdentifier, + RadiusConnectionIdentifier, SyslogConnectionIdentifier))) + + if (Props.mode == Props.RunModes.Development) + DB.addLogFunc { (dbLog, l) => dbLog.statementEntries.foreach { e => + debug("Query: " + e.statement) + }} + + /* Date format */ + LiftRules.dateTimeConverter.default.set { () => YmdDateTimeConverter } + + /* Handle end slash and drop it (except for home page) */ + LiftRules.statelessRewrite.append { + case RewriteRequest(ParsePath(xs,_,_,true),_,_) if (xs.size > 1) && + (xs.lastOption == Some("index")) => + RewriteResponse(xs dropRight 1) + } + + /* Snippet dispatch */ + LiftRules.snippetDispatch.append { + case "Menubar" => new AnyRef with DispatchSnippet { + def dispatch: DispatchIt = { + case _ => { xhtml => MenuWidget() } + } + } + case "action-links" => ActionLinks + } + + /* Sitemap */ + SiteMap.enforceUniqueLinks = false + + LiftRules.setSiteMap(SiteMap( + Menu.i("Home") / "index" >> Hidden, + Menu(CellSnippet), + Menu.i("Cells") / "cell", + Menu(SessionSnippet), + Menu(AccountSnippet), + Menu.i("Accounts") / "account" + )) + + /* Menu widget */ + MenuWidget.init() + + /* Http conf */ + LiftRules.logServiceRequestTiming = false + LiftRules.early.append(_.setCharacterEncoding("UTF-8")) + } + + def initDb(ident: String, connId: ConnectionIdentifier) = { + val driver = "com.mysql.jdbc.Driver" + val vendor = new StandardDBVendor(driver, + Props.get("db."+ident+".url") openOr ("jdbc:mysql://localhost/"+ident), + Props.get("db."+ident+".user"), Props.get("db."+ident+".pass")) + //LiftRules.unloadHooks.append(vendor.closeAllConnections_! _) + DB.defineConnectionManager(connId, vendor) + vendor.newConnection(connId) + } +} + +// vim: set ts=2 sw=2 et: diff -r 69e26359f2c8 -r cf829ec742b3 src/main/scala/net/tz/lift/boot/ProtoBoot.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/net/tz/lift/boot/ProtoBoot.scala Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,83 @@ +/* + * Copyright 2011 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.tz.lift.boot + +import net.liftweb.common._ +import net.liftweb.http._ +import net.liftweb.mapper._ +import net.liftweb.sitemap._ +import net.liftweb.sitemap.Loc._ +import net.liftweb.util._ +import net.liftweb.widgets.menu.MenuWidget +import net.tz.lift.snippet.ActionLinks +import net.tz.lift.util.YmdDateTimeConverter + +/** + * Prototype boot class to be either extended or copied. + */ +class ProtoBoot extends Logger { + + def boot = { + /* DB stuff */ + /* + S.addAround(DB.buildLoanWrapper()) + */ + + if (Props.mode == Props.RunModes.Development) + DB.addLogFunc { (dbLog, l) => dbLog.statementEntries.foreach { e => + debug("Query: " + e.statement) + }} + + /* Date format */ + LiftRules.dateTimeConverter.default.set { () => YmdDateTimeConverter } + + /* Handle end slash and drop it (except for home page) */ + LiftRules.statelessRewrite.append { + case RewriteRequest(ParsePath(xs,_,_,true),_,_) if (xs.size > 1) && + (xs.lastOption == Some("index")) => + RewriteResponse(xs dropRight 1) + } + + /* Snippet dispatch */ + LiftRules.snippetDispatch.append { + case "Menubar" => new AnyRef with DispatchSnippet { + def dispatch: DispatchIt = { + case _ => { xhtml => MenuWidget() } + } + } + case "action-links" => ActionLinks + } + + /* Sitemap */ + SiteMap.enforceUniqueLinks = false + + /* + LiftRules.setSiteMap(SiteMap( + Menu.i("Home") / "index" >> Hidden + )) + */ + + /* Menu widget */ + MenuWidget.init() + + /* Http conf */ + LiftRules.logServiceRequestTiming = false + LiftRules.early.append(_.setCharacterEncoding("UTF-8")) + } + +} + +// vim: set ts=2 sw=2 et: diff -r 69e26359f2c8 -r cf829ec742b3 src/main/scala/net/tz/lift/snippet/Panel.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/net/tz/lift/snippet/Panel.scala Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,57 @@ +/* + * Copyright 2011 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.tz.lift.snippet + +import net.liftweb.mapper.MappedField +import scala.xml.{NodeSeq, Text} + +object AttrRow { + def apply(name: => NodeSeq, value: => NodeSeq) = new AttrRow(name, value, + "attr-name", "attr-value") + def apply(f: MappedField[_, _]) = new AttrRow(Text(f.displayName), f.asHtml, + "attr-name", "attr-value") + def formRow(name: => NodeSeq, input: => NodeSeq) = new AttrRow(name, input, + "form-name", "form-value") + def submitRow(button: => NodeSeq) = new SpanRow(button) +} + +class AttrRow(name: => NodeSeq, value: => NodeSeq, nameCss: String, + valueCss: String) extends Function0[NodeSeq] { + def tdN = {name} + def tdV = {value} + def apply(): NodeSeq = {tdN :: tdV :: Nil} +} + +class SpanRow(value: => NodeSeq) extends AttrRow(NodeSeq.Empty, value, "", "") +{ + override def apply(): NodeSeq = {value} +} + +object Panel { + def apply(attrs: Iterable[AttrRow]) = new Panel(attrs) + def fromFields(fields: Iterable[MappedField[_,_]]) = + new Panel(fields.map(AttrRow(_))) +} + +class Panel(attrs: => Iterable[AttrRow]) extends Function1[NodeSeq, NodeSeq] +{ + def apply(in: NodeSeq): NodeSeq = {attrs.map(_())}
+ def &(other: Function1[NodeSeq, NodeSeq]) = new Function1[NodeSeq, NodeSeq] { + def apply(in: NodeSeq): NodeSeq = List(Panel.this, other) flatMap (_(in)) + } +} + +// vim: set ts=2 sw=2 et: diff -r 69e26359f2c8 -r cf829ec742b3 src/main/scala/net/tz/lift/snippet/SnippetHelpers.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/net/tz/lift/snippet/SnippetHelpers.scala Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,74 @@ +/* + * Copyright 2011 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.tz.lift.snippet + +import net.liftweb.http._ +import scala.xml.{NodeSeq, Text} + +trait SnippetHelpers { + def mkPath(prefix: String, l: String*) = + (prefix :: l.toList) mkString ("/", "/", "") +} + +class A(href: => String) extends Function1[NodeSeq, NodeSeq] { + def apply(in: NodeSeq): NodeSeq = {in} +} + +object A { + def apply(href: String)(in: NodeSeq): NodeSeq = (new A(href))(in) + def apply(href: String): A = new A(href) + def apply(href: String, cnt: String): NodeSeq = (new A(href))(Text(cnt)) +} + +object ActionLinks extends DispatchSnippet { + def dispatch: DispatchIt = { + case "are" => { xhtml => if (links.is.isEmpty) NodeSeq.Empty else xhtml } + case _ => render _ + } + + import scala.collection.mutable.{BufferLike, ListBuffer} + + private object links extends RequestVar[ListBuffer[Either[Function0[NodeSeq], Function1[NodeSeq, NodeSeq]]]](new ListBuffer()) + + def render(in: NodeSeq): NodeSeq = links.is.flatMap { _.fold( _(), _(in) ) ++ + Text(" ") } + + def append(l: NodeSeq) = links.is.append(Left( { () => l } )) + def append(l: () => NodeSeq) = links.is.append(Left(l)) + def append(l: NodeSeq => NodeSeq) = links.is.append(Right(l)) +} + +object SimpleForm { + def apply(l: Iterable[AttrRow], submitVal: String, onSubmit: () => Any): + (NodeSeq => NodeSeq) = { + val curS = S.currentSnippet + def doit() = { + onSubmit() + curS foreach { S.mapSnippet(_, loop) } + } + + def loop: (NodeSeq => NodeSeq) = { + Panel(l ++ List(AttrRow.submitRow(SHtml.submit(submitVal, doit)))) + } + + loop + } + + def apply(l: Iterable[AttrRow], submitVal: String): (NodeSeq => NodeSeq) = + apply(l, submitVal, () => ()) +} + +// vim: set ts=2 sw=2 et: diff -r 69e26359f2c8 -r cf829ec742b3 src/main/scala/net/tz/lift/snippet/Table.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/net/tz/lift/snippet/Table.scala Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,78 @@ +/* + * Copyright 2011 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.tz.lift.snippet + +import net.liftweb.common._ +import net.liftweb.util.Helpers._ +import scala.xml.{Elem, NodeSeq, Text} + +object Column { + def apply[T](name: String, f: T => NodeSeq): Column[T] = + new Column[T](Text(name), f, { _ => Empty}) + def apply[T](name: String, f: T => NodeSeq, tdCss: String): Column[T] = + new Column[T](Text(name), f, { _ => Full(tdCss)}) +} + +class Column[T](name: => NodeSeq, f: T => NodeSeq, tdCss: T => Box[String]) + extends Function1[T, NodeSeq] { + + def th: Elem = {name} + def td(in: T): Elem = { + val r = {apply(in)} + tdCss(in) map { css => r % ("class" -> css) } openOr r + } + def apply(in: T): NodeSeq = f(in) +} + +object Table { + def apply[T](cols: Iterable[Column[T]], rows: Iterable[T]) = new Table(Empty, + cols, rows, Empty) + def apply[T](css: String, cols: Iterable[Column[T]], rows: Iterable[T], + foot: NodeSeq) = new Table(Full(css), cols, rows, Full(foot)) +} + +class Table[T](css: Box[String], cols: Iterable[Column[T]], + rows: => Iterable[T], foot: Box[NodeSeq]) extends + Function1[NodeSeq, NodeSeq] { + + def oddEven(i: Int) = (i % 2) match { + case 1 => "even" + case 0 => "odd" + } + + def thead: Elem = { cols.map(c => c.th) } + def tbody: Elem = { + rows.zipWithIndex.map( r => tr(r._1, r._2)) + } + def tr(in: T, idx: Int): Elem = { + cols.map(_.td(in)) + } + + def tfoot: Elem = { + foot map { f => {f} + } openOr NodeSeq.Empty + } + + def apply(ns: NodeSeq): NodeSeq = { + val t = { + List(thead, tfoot, tbody) + }
+ css map { cl => t % ("class" -> cl) } openOr t + } + +} + +// vim: set ts=2 sw=2 et: diff -r 69e26359f2c8 -r cf829ec742b3 src/main/scala/net/tz/lift/util/DateExtension.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/net/tz/lift/util/DateExtension.scala Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,44 @@ +/* + * Refactored from net.liftweb.util.TimeHelpers.scala, which is + * + * Copyright 2006-2011 WorldWide Conferencing, LLC and + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.tz.lift.util + +import java.util.{Calendar, Date} + +/** + * Fixed version of DateExtension (from + * net.liftweb.util.TimeSpan.DateExtension). + */ +class DateExtension(d: Date) { + /** @returns a Date object starting at 00:00 from date */ + def noTime = { + val calendar = Calendar.getInstance + calendar.setTime(d) + calendar.set(Calendar.HOUR_OF_DAY, 0) + calendar.set(Calendar.MINUTE, 0) + calendar.set(Calendar.SECOND, 0) + calendar.set(Calendar.MILLISECOND, 0) + calendar.getTime + } +} + +object DateExtension { + implicit def date2dateExtension(d: Date): DateExtension = + new DateExtension(d) +} + +// vim: set ts=2 sw=2 et: diff -r 69e26359f2c8 -r cf829ec742b3 src/main/scala/net/tz/lift/util/Helpers.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/net/tz/lift/util/Helpers.scala Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,79 @@ +/* + * Copyright 2011 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.tz.lift.util + +import java.text.SimpleDateFormat +import java.util.Date +import net.liftweb.http.LiftRules +import net.liftweb.util.Helpers.tryo +import org.joda.time.{DateMidnight, DateTime, Period, ReadableInstant} +import org.joda.time.format.{DateTimeFormat, PeriodFormatterBuilder} +import scala.xml.{NodeSeq, Text} + +object AsIsoDateTime { + val df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss") + def unapply(in: String): Option[Date] = tryo { df.parse(in) } + def apply(d: Date): String = df.format(d) +} + +object AsDate { + import DateExtension._ + def unapply(in: String): Option[Date] = LiftRules.dateTimeConverter(). + parseDate(in).map(_.noTime) + def apply(d: Date) = LiftRules.dateTimeConverter().formatDate(d) +} + +object AsDateTime { + def unapply(in: String): Option[Date] = LiftRules.dateTimeConverter(). + parseDateTime(in) + def apply(d: Date) = LiftRules.dateTimeConverter().formatDateTime(d) +} + +object AsDateMidnight { + lazy val fmt = DateTimeFormat.forPattern("yyyy-MM-dd") + def unapply(in: String): Option[DateMidnight] = apply(in) + def apply(in: String): Option[DateMidnight] = tryo { + fmt.parseDateTime(in).toDateMidnight + } + def apply(d: ReadableInstant) = fmt.print(d) + + implicit def dt2d(d: DateTime): Date = d.toDate + implicit def dm2d(d: DateMidnight): Date = d.toDate + implicit def d2dm(d: Date): DateMidnight = new DateMidnight(d) + implicit def dm2dt(d: DateMidnight): DateTime = d.toDateTime +} + +object AsTimePeriod { + lazy val fmt = (new PeriodFormatterBuilder).printZeroAlways. + minimumPrintedDigits(2).appendHours. + appendSeparator(":").appendMinutes.toFormatter + lazy val fmtDt = DateTimeFormat.forPattern("HH:mm") + + /** Parses HH:mm time into period. */ + def apply(in: String): Option[Period] = tryo { fmt.parsePeriod(in) } + def apply(p: Period) = fmt.print(p) + def apply(dt: DateTime) = fmtDt.print(dt) +} + +object Bytes { + implicit def dbl2ns(d: Double): NodeSeq = + Text(String.format(fmt, d.asInstanceOf[AnyRef])) + def fmt = "%.2f" + def kb(v: Long): NodeSeq = v.toDouble / 1024 + def mb(v: Long): NodeSeq = v.toDouble / (1024*1024) +} + +// vim: set ts=2 sw=2 et: diff -r 69e26359f2c8 -r cf829ec742b3 src/main/scala/net/tz/lift/util/YmdDateTimeConverter.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/net/tz/lift/util/YmdDateTimeConverter.scala Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,40 @@ +/* + * Copyright 2011 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package net.tz.lift.util + +import java.text.SimpleDateFormat +import java.util.Date +import net.liftweb.common._ +import net.liftweb.util.DateTimeConverter +import net.liftweb.util.TimeHelpers._ + +object YmdDateTimeConverter extends DateTimeConverter { + val df = new SimpleDateFormat("yyyy-MM-dd") + val dtf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss") + + def formatDateTime(d: Date) = dtf.format(d) + def formatDate(d: Date) = df.format(d) + /** Uses Helpers.hourFormat which includes seconds but not time zone */ + def formatTime(d: Date) = hourFormat.format(d) + + def parseDateTime(s: String) = tryo { dtf.parse(s) } + def parseDate(s: String) = tryo { df.parse(s) } + /** Tries Helpers.hourFormat and Helpers.timeFormat */ + def parseTime(s: String) = + tryo{hourFormat.parse(s)} or tryo{timeFormatter.parse(s)} +} + +// vim: set ts=2 sw=2 et: diff -r 69e26359f2c8 -r cf829ec742b3 src/main/scala/radview/model/Account.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/radview/model/Account.scala Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,197 @@ +/* + * Copyright 2011 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package radview.model + +import net.liftweb.common._ +import net.liftweb.mapper._ +import radview.snippet._ +import scala.xml.{Node, NodeSeq, Text} + +trait ImsiAware { + self: BaseMapper => + object imsi extends MappedPoliteStringColName(this.asInstanceOf[MapperType], + 17, "imsi", "IMSI") { + def imsi = is takeRight 12 + override def displayName = calcImsiDisplayName + } + def calcImsiDisplayName = "IMSI" +} + +trait Imsi12Aware { + self: BaseMapper => + object imsi12 extends MappedPoliteStringColName(this.asInstanceOf[MapperType], + 14, "imsi_12", "IMSI") { + } +} + + +trait UsernameAware { + self: BaseMapper => + object username extends MappedPoliteStringColName( + this.asInstanceOf[MapperType], 64, "username", "Username") +} + +trait AppAware { + self: BaseMapper => + object app extends MappedPoliteStringColName(this.asInstanceOf[MapperType], + 5, "appl", "Service") { + override def asHtml = Text(tr) + def tr = is match { + case "BOSS" => "Mobile" + case x => x + } + } +} + +trait ServiceStatus { self: MappedInt[_] => + override def asHtml = Text(is match { + case 0 => "service disabled (0)" + case 1 => "service enabled (1)" + case x => x.toString + }) +} + +/* + Table: radcheck_SUB + */ +object RadCheckSub extends RadCheckSub with LongKeyedMetaMapper[RadCheckSub] { + override def dbTableName = "v_radcheck_SUB" + override def dbDefaultConnectionIdentifier = RadiusConnectionIdentifier + + def getApps = findAllFields(List(app), Distinct[RadCheckSub], + OrderBy(app, Ascending)) + + def byImsi(imsiVal: String) = Box(findAll(By(imsi, imsiVal))) +} + +class RadCheckSub extends LongKeyedMapper[RadCheckSub] with ImsiAware with + UsernameAware with AppAware with IdPK with Imsi12Aware { + + def getSingleton = RadCheckSub + + object userStatus extends MappedIntColName(this, "user_status", + "User Status") with ServiceStatus + + object msisdn extends MappedPoliteStringColName(this, 22, "msisdn", + "Phone No.") + + override def calcImsiDisplayName = ifAdsl("Phone No.", + super.calcImsiDisplayName) + + def fieldsForDisplay = List(msisdn) ++ ifAdsl(Empty, Full(imsi)) ++ + List(app, username, userStatus) + + def fieldsForList = List(username) ++ + ifAdsl(Empty, Full(msisdn)) :+ userStatus + + def ifAdsl[T](yes: => T, no: => T) = app.is match { + case "ADSL" => yes + case _ => no + } + + def isAdsl = app.is == "ADSL" + + lazy val replySub = RadReplySub.byImsi(imsi) + lazy val replyNai = RadReplyNai.byImsi12(imsi12) + lazy val checkNai = replyNai flatMap { rn => RadCheckNai.byEsn(rn.esn) } +} + +/* + Table: radreply_SUB + */ +object RadReplySub extends RadReplySub with LongKeyedMetaMapper[RadReplySub] { + override def dbTableName = "radreply_SUB" + override def dbDefaultConnectionIdentifier = RadiusConnectionIdentifier + + def byImsiMap(imsis: Seq[String]) = Map.empty ++ + (findAll(ByList(imsi, imsis)) map { i => (i.imsi.is, i) }) + + def fieldsForList = List(statIp) + def byImsi(imsiVal: String) = Box(findAll(By(imsi, imsiVal))) +} + +class RadReplySub extends LongKeyedMapper[RadReplySub] with ImsiAware with + AppAware with IdPK { + + def getSingleton = RadReplySub + + object statIp extends MappedPoliteStringColName(this, 253, "value", + "Static IP") + + def fieldsForDisplay = List(statIp) +} + +trait EsnAware { + self: BaseMapper => + object esn extends MappedPoliteStringColName( + this.asInstanceOf[MapperType], 64, "username", "ESN") { + def esn = is.takeWhile {_ != '@'} + override def asHtml: Node = Text(esn) + } +} + +/* + Table: radcheck_NAI + */ +object RadCheckNai extends RadCheckNai with LongKeyedMetaMapper[RadCheckNai] { + override def dbTableName = "radcheck_NAI" + override def dbDefaultConnectionIdentifier = RadiusConnectionIdentifier + + def byRrnMap(rrn: Seq[RadReplyNai]) = Map.empty ++ + (findAll(ByList(esn, rrn map { _.esn.is })) map { i => (i.esn.is, i) }) + + def fieldsForList = List(termStatus) + def byEsn(fullEsn: String) = Box(findAll(By(esn, fullEsn))) +} + +class RadCheckNai extends LongKeyedMapper[RadCheckNai] with EsnAware + with AppAware with IdPK { + + def getSingleton = RadCheckNai + + object termStatus extends MappedIntColName(this, "term_status", + "Term Status") with ServiceStatus + def fieldsForDisplay = List(termStatus) +} + +/* + Table: radreply_NAI + */ +object RadReplyNai extends RadReplyNai with LongKeyedMetaMapper[RadReplyNai] { + override def dbTableName = "v_radreply_NAI" + override def dbDefaultConnectionIdentifier = RadiusConnectionIdentifier + + def byImsi12map(imsis12: Seq[String]) = Map.empty ++ + (findAll(ByList(imsi12, imsis12)) map { i => (i.imsi12.is, i) }) + + def fieldsForList = List(esn) + def byImsi12(in: String) = Box(findAll(By(imsi12, in))) +} + +class RadReplyNai extends LongKeyedMapper[RadReplyNai] with EsnAware with + AppAware with IdPK with Imsi12Aware { + + def getSingleton = RadReplyNai + + object value extends MappedPoliteStringColName(this, 253, "value", "Value") { + def imsi = is takeRight 12 + override def asHtml: Node = Text(imsi) + } + + def fieldsForDisplay = List(esn) +} + +// vim: set ts=2 sw=2 et: diff -r 69e26359f2c8 -r cf829ec742b3 src/main/scala/radview/model/Cdr.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/radview/model/Cdr.scala Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,188 @@ +/* + * Copyright 2011 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package radview.model + +import java.util.Date +import net.liftweb.common._ +import net.liftweb.mapper._ +import radview.snippet.SessionSnippet + +/* + Table: v_radacct + */ +object Cdr extends Cdr with LongKeyedMetaMapper[Cdr] with Loggable { + override def dbTableName = "v_radacct" + override def dbDefaultConnectionIdentifier = RadAcctConnectionIdentifier + + def bySession(s: CdrSession) = findAll(By(sid, s.sid), + OrderBy(radacctid, Ascending)) + + def fieldsForList = List(sid, ts, inBytes, outBytes, statusType, acctuniqueid, + username, groupname, realm, nasipaddress, nasportid, nasporttype, + acctsessiontime, acctauthentic, serviceType, framedProto, framedIp, + statusType, cell) + + import java.sql.{Date => SqlDate} + implicit def d2sqlD(d: Date): SqlDate = new SqlDate(d.getTime) + + private def diff(col: MappedField[_, _]) = String.format( + "MAX(%1$s) - MIN(%1$s) AS %1$s", col.dbColumnName) + private def min(col: MappedField[_, _]) = String.format( + "MIN(%1$s) AS %1$s", col.dbColumnName) + private def max(col: MappedField[_, _]) = String.format( + "MAX(%1$s) AS %1$s", col.dbColumnName) + + def sessions(from: Date, to: Date, field: MappedField[_, Cdr], + value: String) = findAllByPreparedStatement { sc => + val selectS = List(sid.dbColumnName, diff(inBytes), diff(outBytes)) + val sql = String.format( + "SELECT %s FROM %s WHERE %s = ? AND %s BETWEEN ? AND ? GROUP BY %s", + selectS mkString ",", dbTableName, field.dbColumnName, + ts.dbColumnName, sid.dbColumnName) + logger.debug(sql + "; params: " + from + ", " + to + ", " + + field.dbColumnName + "=" + value) + val stm = sc.connection.prepareStatement(sql) + stm.setString(1, value) + stm.setDate(2, from) + stm.setDate(3, to) + stm + } + + def session(sessionId: String): Box[CdrSession] = + Box(sessions(List(sessionId))) + + def sessions(sessionIds: List[String]): List[CdrSession] = sessionIds match { + case Nil => Nil + case xs => + val cdr1 = findAllByPreparedStatement { sc => + val selectS = List(sid.dbColumnName, diff(inBytes), diff(outBytes), + min(radacctid), max(ts)) + val sql = String.format( + "SELECT %s FROM %s WHERE %s IN (%s) GROUP BY %s", + selectS.mkString(","), dbTableName, sid.dbColumnName, + sessionIds.map(r=>"?").mkString(","), sid.dbColumnName) + logger.debug(sql) + val stm = sc.connection.prepareStatement(sql) + sessionIds.zipWithIndex.map { r => stm.setString(r._2 + 1, r._1) } + stm + } + val cdr2 = Cdr.findAll(ByList(radacctid, cdr1 map { _.radacctid.is })) + val tsMap = Map[String, Cdr]() ++ (cdr2 map { c => (c.sid.is, c) }) + for { + c1 <- cdr1 + c2 <- tsMap.get(c1.sid.is) + } yield { + CdrSession(c1.sid, c2.ts, c1.ts, c1.inBytes, c1.outBytes, c2.cell) + } + } +} + +class Cdr extends LongKeyedMapper[Cdr] with CellAware { + def getSingleton = Cdr + def primaryKeyField = radacctid + + object radacctid extends MappedLongIndex(this) { + override def dbColumnName = "radacctid" + } + + object sid extends MappedPoliteStringColName(this, 64, "acctsessionid", + "Session Id") { + override def asHtml = + {super.asHtml} + } + + object acctuniqueid extends MappedPoliteStringColName(this, 32, + "acctuniqueid", "Unique Id") + + object username extends MappedPoliteStringColName(this, 64, "username", + "User") + + object groupname extends MappedPoliteStringColName(this, 64, "groupname", + "Group") + + object realm extends MappedPoliteStringColName(this, 64, "realm", "Realm") + + object nasipaddress extends MappedPoliteStringColName(this, 15, + "nasipaddress", "NAS IP") + + object nasportid extends MappedPoliteStringColName(this, 15, "nasportid", + "NAS Port Id") + + object nasporttype extends MappedPoliteStringColName(this, 32, + "nasporttype", "NAS Port Type") + + object acctsessiontime extends MappedIntColName(this, "acctsessiontime", + "Session Time") + + object acctauthentic extends MappedPoliteStringColName(this, 32, + "acctauthentic", "Authenticator") + + object inBytes extends MappedLongColName(this, "acctinputoctets", + "Input bytes") + + object outBytes extends MappedLongColName(this, "acctoutputoctets", + "Output bytes") + + object calledNo extends MappedPoliteStringColName(this, 50, + "calledstationid", "Called No.") + + object callingNo extends MappedLongColName(this, "callingstationid", + "Calling No.") + + object termCause extends MappedPoliteStringColName(this, 32, + "acctterminatecause", "Termination Cause") + + object serviceType extends MappedPoliteStringColName(this, 32, + "servicetype", "Service Type") + + object framedProto extends MappedPoliteStringColName(this, 32, + "framedprotocol", "Framed Protocol") + + object framedIp extends MappedPoliteStringColName(this, 15, + "framedipaddress", "Framed IP") + + object startDelay extends MappedIntColName(this, "acctstartdelay", + "Start Delay") + + object stopDelay extends MappedIntColName(this, "acctstopdelay", + "Stop Delay") + + object corrId extends MappedPoliteStringColName(this, 64, + "3GPP2_Correlation_Id", "3GPP2 Correlation Id") + + object statusType extends MappedPoliteStringColName(this, 15, + "Acct_Status_Type", "Status Type") + + object ts extends MappedDateTime(this) { + override def dbColumnName = "Event_Timestamp" + override def displayName = "Timestamp" + } + + object releaseInd extends MappedIntColName(this, "3GPP2_Release_Indicator", + "3GPP2 Release Indicator") + + object activeTime extends MappedIntColName(this, "3GPP2_Active_Time", + "3GPP2 Active Time") + +} + +case class CdrSession(sid: String, start: Date, end: Date, inBytes: Long, + outBytes: Long, cellId: String) { + + def cell: Box[Cell] = Cell.findByKey(cellId) +} + +// vim: set ts=2 sw=2 et: diff -r 69e26359f2c8 -r cf829ec742b3 src/main/scala/radview/model/Cell.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/radview/model/Cell.scala Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,88 @@ +/* + * Copyright 2011 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package radview.model + +import net.liftweb.common._ +import net.liftweb.mapper._ +import radview.snippet._ + +/* + Table: v_cells + */ +object Cell extends Cell with StringKeyedMetaMapper[Cell] { + override def dbTableName = "v_cells" + override def dbDefaultConnectionIdentifier = RadAcctConnectionIdentifier + + lazy val fieldsForSearch = List(service, serviceNumber, bscIntId, bssId, + bssIntDesc, btsSysId, btsSysIdHex, btsName, btsDesc, ci, ip) + lazy val fieldsForList = List(service, serviceNumber, bscIntId, bssId, + bssIntDesc, btsSysId, btsSysIdHex, btsName, btsDesc, ci, ip) +} + +class Cell extends StringKeyedMapper[Cell] { + + def getSingleton = Cell + + def primaryKeyField = idpk + + object idpk extends MappedStringIndex(this, 60) { + override def dbColumnName = "idpk" + } + object service extends MappedPoliteStringColName(this, 4, "SERVICE", + "Service") + object serviceNumber extends MappedIntColName(this, "SERVICE_NUMBER", + "Service No.") + object bscIntId extends MappedPoliteStringColName(this, 6, "BSC_INT_ID", + "BSC id") + object bssId extends MappedPoliteStringColName(this, 2, "BSS_ID", "BSS id") + object bssIntDesc extends MappedPoliteStringColName(this, 12, "BSC_INT_DESC", + "BSC Description") + object btsSysId extends MappedIntColName(this, "BTS_SYSTEM_ID", + "BTS system id") + object btsSysIdHex extends MappedPoliteStringColName(this, 12, + "BTS_SYSTEM_ID_HEX", "BTS system id(hex)") + object btsName extends MappedPoliteStringColName(this, 15, "BTS_NAME", + "BTS Name") { + override def asHtml = + {super.asHtml} + } + object btsDesc extends MappedPoliteStringColName(this, 100, "BTS_DESC", + "BTS Description") + object ci extends MappedPoliteStringColName(this, 10, "CI", "CI") + object ip extends MappedPoliteStringColName(this, 15, "IP", "IP") + + def fieldsForDisplay = List(service, serviceNumber, bscIntId, bssId, + bssIntDesc, btsSysId, btsSysIdHex, btsDesc, ci, ip) + +} + +trait CellAware { + self: BaseMapper => + + object cell extends MappedStringForeignKey[MapperType, Cell]( + self.asInstanceOf[MapperType], Cell, 41) { + + override def dbColumnName = "bts_idpk" + override def displayName = "Cell" + def foreignMeta = Cell + override def asHtml = obj.map { c => + {c.btsName} + } openOr super.asHtml + } + +} + +// vim: set ts=2 sw=2 et: diff -r 69e26359f2c8 -r cf829ec742b3 src/main/scala/radview/model/Common.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/radview/model/Common.scala Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,62 @@ +/* + * Copyright 2011 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package radview.model + +import net.liftweb.common._ +import net.liftweb.mapper._ + +case object RadAcctConnectionIdentifier extends ConnectionIdentifier { + val jndiName = "radacct" +} + +case object RadiusConnectionIdentifier extends ConnectionIdentifier { + val jndiName = "radius" +} + +case object SyslogConnectionIdentifier extends ConnectionIdentifier { + val jndiName = "syslog" +} + +trait StringKeyedMapper[OwnerType <: StringKeyedMapper[OwnerType]] extends + KeyedMapper[String, OwnerType] { self: OwnerType => + + override type TheKeyType = String +} + +trait StringKeyedMetaMapper[A <: StringKeyedMapper[A]] extends + KeyedMetaMapper[String, A] { self: A => +} + +abstract class MappedPoliteStringColName[T <: Mapper[T]]( + own: T, maxLen: Int, colName: String, dispName: String) extends + MappedPoliteString[T](own, maxLen) { + override def dbColumnName = colName + override def displayName = dispName +} + +abstract class MappedIntColName[T <: Mapper[T]](own: T, colName: String, + dispName: String) extends MappedInt(own) { + override def dbColumnName = colName + override def displayName = dispName +} + +abstract class MappedLongColName[T <: Mapper[T]](own: T, colName: String, + dispName: String) extends MappedLong(own) { + override def dbColumnName = colName + override def displayName = dispName +} + +// vim: set ts=2 sw=2 et: diff -r 69e26359f2c8 -r cf829ec742b3 src/main/scala/radview/model/Syslog.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/radview/model/Syslog.scala Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,55 @@ +/* + * Copyright 2011 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package radview.model + +import net.liftweb.common._ +import net.liftweb.mapper._ +import radview.snippet._ +import scala.xml.{Node, NodeSeq, Text} + +trait LoginStatus { self: MappedInt[_] => + override def asHtml = Text(is match { + case 0 => "login failure (0)" + case 1 => "login success (1)" + case x => x.toString + }) +} + +/* + Table: AAA_SYSLOG +*/ +object Syslog extends Syslog with MetaMapper[Syslog] { + override def dbTableName = "AAA_SYSLOG" + override def dbDefaultConnectionIdentifier = SyslogConnectionIdentifier + def fieldsForList = List(ts, source, loginStatus, username) +} + +class Syslog extends Mapper[Syslog] { + def getSingleton = Syslog + object ts extends MappedDateTime(this) { + override def dbColumnName = "TIMESTAMP" + override def displayName = "Timestamp" + } + object source extends MappedPoliteStringColName(this, 22, "FROMHOST", + "Source host") + object loginStatus extends MappedIntColName(this, "LOGIN_STATUS", + "Login status") with LoginStatus + object username extends MappedPoliteStringColName(this, 22, "USERNAME", + "Username") + object imsi extends MappedLongColName(this, "IMSI", "Imsi") +} + +// vim: set ts=2 sw=2 et: diff -r 69e26359f2c8 -r cf829ec742b3 src/main/scala/radview/snippet/AccountSnippet.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/radview/snippet/AccountSnippet.scala Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,385 @@ +/* + * Copyright 2011 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package radview.snippet + +import net.liftweb.common._ +import net.liftweb.http._ +import net.liftweb.http.{RewriteRequest => RReq, RewriteResponse => RResp, ParsePath => PP} +import net.liftweb.mapper._ +import net.liftweb.sitemap._ +import net.liftweb.sitemap.Loc._ +import net.liftweb.util._ +import net.liftweb.util.Helpers._ +import net.tz.lift.snippet._ +import net.tz.lift.util._ +import org.joda.time.DateTime +import radview.model._ +import scala.xml.{NodeSeq, Text} + +object AsAccountImsi { + def unapply(in: String): Option[RadCheckSub] = RadCheckSub.byImsi(in) +} + +abstract sealed class AccountLoc +case object AccountSearch extends AccountLoc +case class ViewAccount(acc: RadCheckSub) extends AccountLoc +case class SyslogAccount(acc: RadCheckSub) extends AccountLoc +case class CdrAccount(acc: RadCheckSub) extends AccountLoc + +object AsAccount { + def unapply(loc: Box[AccountLoc]): Option[RadCheckSub] = loc flatMap { _ match { + case SyslogAccount(a) => Some(a) + case ViewAccount(a) => Some(a) + case CdrAccount(a) => Some(a) + case _ => None + }} +} + +object AccountSnippet extends Loc[AccountLoc] with SnippetHelpers { + + val name = "account" + val prefix = "account" + val tpl = "account" + val params = List(Hidden) + val defaultValue = Full(AccountSearch) + val link = new Link[AccountLoc](List(prefix), true) { + override def createPath(l: AccountLoc): String = l match { + case AccountSearch => prefix + case ViewAccount(a) => mkPath(prefix, "imsi", a.imsi.is) + case SyslogAccount(a) => mkPath(prefix, "imsi", a.imsi.is, "syslog") + case CdrAccount(a) => mkPath(prefix, "imsi", a.imsi.is, "cdr") + } + } + + def url(l: AccountLoc) = link.createPath(l) + + val text = LinkText[AccountLoc](l => Text(l match { + case AccountSearch => "Account Search" + case ViewAccount(a) => "Account " + a.imsi + case SyslogAccount(a) => "Syslog search for account " + a.imsi + case CdrAccount(a) => "CDR search for account " + a.imsi + })) + + override def rewrite: LocRewrite = Full({ + case RReq(PP("account" :: Nil, _,_,_), _,_) => + (new RResp(PP(List(tpl, "search"), "", true, false), Map.empty, + true), AccountSearch) + case RReq(PP("account" :: "imsi" :: AsAccountImsi(a) :: xs, _,_,_), _,_) => + val l = xs match { + case "syslog" :: xs => + ActionLinks.append(A(url(ViewAccount(a)), "Back to account")) + SyslogAccount(a) + case "cdr" :: xs => + ActionLinks.append(A(url(ViewAccount(a)), "Back to account")) + CdrAccount(a) + case _ => + ActionLinks.append(A(url(SyslogAccount(a)), "Syslog search")) + ActionLinks.append(A(url(CdrAccount(a)), "CDR search")) + ViewAccount(a) + } + (RResp(List(tpl, "view")), l) + }) + + override def snippets: SnippetTest = { + case ("form", Full(AccountSearch)) => searchForm + case ("list", Full(AccountSearch)) => searchList + case ("sub", AsAccount(a)) => AccountSubPanel(a) + case ("if-nai", AsAccount(a)) if !a.isAdsl => PassThru + case ("nai", AsAccount(a)) => AccountNaiPanel(a) + + case ("syslog-list", Full(SyslogAccount(a))) => syslogList(a) + case ("form", Full(SyslogAccount(a))) => syslogForm(a) + case ("cdr-list", Full(CdrAccount(a))) => cdrList(a) + case ("form", Full(CdrAccount(a))) => cdrForm(a) + + case ("form", _) => ClearNodes + case ("syslog-list", _) => ClearNodes + case ("cdr-list", _) => ClearNodes + case ("sub", _) => ClearNodes + case ("if-nai", _) => ClearNodes + case ("nai", _) => ClearNodes + } + + /* Shared in all searches. */ + object mxCnt extends RequestVar[Int](100) + + /* Search account vars. */ + object searchApp extends RequestVar[String]("BOSS") + object searchFld extends RequestVar[Int](0) + object searchVal extends RequestVar[String]("") + + val fldsRCS = List(RadCheckSub.msisdn, RadCheckSub.imsi) + val fldsRRS = List(RadReplySub.statIp) + val fldsRRN = List(RadReplyNai.esn) + + val searchFlds: List[MappedField[String,_]] = List( + RadCheckSub.msisdn, RadCheckSub.imsi, + RadReplySub.statIp, + RadReplyNai.esn + ) + + def searchForm: (NodeSeq => NodeSeq) = { + import AttrRow._ + SimpleForm(List( + formRow(Text(RadCheckSub.app.displayName), + SHtml.select(RadCheckSub.getApps map(i => (i.app.is, i.app.tr)), + Full(searchApp.is), searchApp(_))), + formRow(SHtml.selectObj[Int]( + searchFlds.zipWithIndex.map(i => (i._2, i._1.displayName)), + Full(searchFld.is), {i => searchFld(i)}), + SHtml.textElem(searchVal) + ), + formRow(Text("Max. results"), SHtml.selectObj[Int]( + List(100, 200, 500) map { i => (i, i.toString) }, + Full(mxCnt.is), mxCnt(_))) + ), "Search") + } + + def searchList: (NodeSeq => NodeSeq) = { + val rowsB = searchVal.is match { + case "" | null => Empty + case v => + val mx = mxCnt.is + val fld = searchFlds(searchFld.is) + val n = fld.name + fld.fieldOwner match { + case o: RadCheckSub => + o.fieldByName(n) map { f: MappedField[String, RadCheckSub] => + RadCheckSub.findAll(Like(f, "%" + v + "%"), + By(RadCheckSub.app, searchApp.is), + OrderBy(RadCheckSub.imsi, Ascending), MaxRows(mx)) + } + case o: RadReplySub => + o.fieldByName(n) map { f: MappedField[String, RadReplySub] => + val imsis = RadReplySub.findAllFields(List(RadReplySub.imsi), + Like(f, "%" + v + "%"), + By(RadReplySub.app, searchApp.is)) map (_.imsi.is) + RadCheckSub.findAll(ByList(RadCheckSub.imsi, imsis), + OrderBy(RadCheckSub.imsi, Ascending), MaxRows(mx)) + } + case o: RadReplyNai => + o.fieldByName(n) map { f: MappedField[String, RadReplyNai] => + val imsis = RadReplyNai.findAllFields(List(RadReplyNai.value), + Like(f, "%" + v + "%"), + By(RadReplyNai.app, searchApp.is)) map (_.value.imsi) + RadCheckSub.findAll(ByList(RadCheckSub.imsi12, imsis), + OrderBy(RadCheckSub.imsi, Ascending), MaxRows(mx)) + } + } + } + rowsB map { rcs => + val rrs = RadReplySub.byImsiMap(rcs map {_.imsi.is}) + val rrn = RadReplyNai.byImsi12map(rcs map {_.imsi12.is}) + val rcn = RadCheckNai.byRrnMap(rrn.values.toSeq) + AccountTable(rcs, rrs, rrn, rcn) + } openOr ClearNodes + } + + /* Vars common to syslog + cdr search */ + object from extends RequestVar[DateTime]((new DateTime).minusDays(1)) + object to extends RequestVar[DateTime](new DateTime) + object fromD extends RequestVar[String](AsDateMidnight(from)) + object fromT extends RequestVar[String](AsTimePeriod(from)) + object toD extends RequestVar[String](AsDateMidnight(to)) + object toT extends RequestVar[String](AsTimePeriod(to)) + private def setFromToDateTimes() = { + import AsDateMidnight._ + AsDateMidnight(fromD) map { from(_) } + AsTimePeriod(fromT) map { p => from(from.plus(p)) } + AsDateMidnight(toD) map { to(_) } + AsTimePeriod(toT) map { p => to(to.plus(p)) } + () + } + + /* Shared between syslogForm + syslogList */ + object syslogQp extends RequestVar[Box[List[QueryParam[Syslog]]]](Empty) + + def syslogForm(a: RadCheckSub): (NodeSeq => NodeSeq) = { + object byQp extends RequestVar[Box[QueryParam[Syslog]]](Empty) + + val selectQp = List[(QueryParam[Syslog], String)]( + (By(Syslog.imsi, asLong(a.imsi).openOr(0L)), a.imsi.displayName)) ++ + a.replyNai.map { n => (By(Syslog.username, n.esn.esn), "ESN") } + + def setD() = { + setFromToDateTimes() + import AsDateMidnight.dt2d + val qp = List[QueryParam[Syslog]]( + By_<(Syslog.ts, to.plusMinutes(1)), + By_>(Syslog.ts, from.minusMinutes(1)), + OrderBy(Syslog.ts, Ascending) + ) ++ byQp.is + syslogQp(Full(qp)) + } + + import AttrRow._ + SimpleForm(List( + formRow(Text("Search by"), SHtml.selectObj[QueryParam[Syslog]](selectQp, + byQp, { qp => byQp(Full(qp)) } )), + formRow(Text("From"), SHtml.textElem(fromD, ("class", "date")) ++ + SHtml.textElem(fromT)), + formRow(Text("To"), SHtml.textElem(toD, ("class", "date")) ++ + SHtml.textElem(toT)), + formRow(Text("Max. results"), SHtml.selectObj[Int]( + List(100, 200, 500) map { i => (i, i.toString) }, + Full(mxCnt.is), mxCnt(_))) + ), "Search", setD) + } + + def syslogList(a: RadCheckSub): (NodeSeq => NodeSeq) = syslogQp map { qp => + ".num-rows-panel" #> Panel(List( + ("Records found", Syslog.count(qp:_*)), + ("Number of successful logins", + Syslog.count((qp:+By(Syslog.loginStatus, 1)):_*)), + ("Number of failed logins", + Syslog.count((qp:+By(Syslog.loginStatus, 0)):_*)) + ) map { r => new AttrRow(Text(r._1), Text(r._2.toString), + "attr-name-wide", "attr-value") }) & + ".list" #> SyslogTable(Syslog.findAll((qp:+MaxRows(mxCnt.is) ):_*), + qp.find { _ match { + case Cmp(f, _, _, _, _) if f.name == Syslog.imsi.name => true + case _ => false + }}.isDefined) + } openOr ClearNodes + + /* Shared between cdrForm and cdrList. */ + object cdrQp extends RequestVar[Box[List[QueryParam[Cdr]]]](Empty) + object byCdrQp extends RequestVar[Box[QueryParam[Cdr]]](Empty) + + def cdrForm(a: RadCheckSub): (NodeSeq => NodeSeq) = { + + val selectQp = List[(QueryParam[Cdr], String)]( + (By(Cdr.callingNo, asLong(a.imsi).openOr(0L)), a.imsi.displayName)) ++ + a.replySub.map { r => (By(Cdr.framedIp, r.statIp.is), + r.statIp.displayName) } + + def setD() = { + setFromToDateTimes() + import AsDateMidnight.dt2d + val qp = List[QueryParam[Cdr]]( + By_<(Cdr.ts, to.plusMinutes(1)), + By_>(Cdr.ts, from.minusMinutes(1)), + OrderBy(Cdr.ts, Ascending) + ) ++ byCdrQp.is + cdrQp(Full(qp)) + } + + import AttrRow._ + SimpleForm(List( + formRow(Text("Search by"), SHtml.selectObj[QueryParam[Cdr]](selectQp, + byCdrQp, { qp => byCdrQp(Full(qp)) } )), + formRow(Text("From"), SHtml.textElem(fromD, ("class", "date")) ++ + SHtml.textElem(fromT)), + formRow(Text("To"), SHtml.textElem(toD, ("class", "date")) ++ + SHtml.textElem(toT)), + formRow(Text("Max. results"), SHtml.selectObj[Int]( + List(100, 200, 500, 1000, 2000) map { i => (i, i.toString) }, + Full(mxCnt.is), mxCnt(_))) + ), "Search", setD) + } + + def cdrList(a: RadCheckSub): (NodeSeq => NodeSeq) = cdrQp map { qp => + import AsDateMidnight.dt2d + val sess = byCdrQp map { _ match { + case Cmp(f, OprEnum.Eql, Full(v), _, _) => + Cdr.sessions(from.is, to.is, f, v.toString) + case _ => Nil + }} openOr Nil + + ".num-rows-panel" #> Panel(List( + ("Records found", Cdr.count(qp:_*)), + ("No. of sessions", sess.length), + ("Total bytes in/out [MB]", + Bytes.mb(sess map { _.inBytes.is } sum) ++ Text(" / ") ++ + Bytes.mb(sess map { _.outBytes.is } sum)) + ) map { r => new AttrRow(Text(r._1), Text(r._2.toString), + "attr-name-wide", "attr-value") } ) & + ".list" #> CdrTable(Cdr.findAll( (qp:+MaxRows(mxCnt.is) ):_*)) + } openOr ClearNodes +} + +object AccountTable { + def apply(rcs: Seq[RadCheckSub], rrs: Map[String, RadReplySub], + rrn: Map[String, RadReplyNai], rcn: Map[String, RadCheckNai]) = { + + val tpl = rcs.headOption.getOrElse(RadCheckSub) + + def valF[T<:Mapper[T]](f: MappedField[_, T])(i: T): NodeSeq = + f.actualField(i).asHtml + + val imsiCol = Column[RadCheckSub](tpl.imsi.displayName, { + i: RadCheckSub => A(AccountSnippet.url(ViewAccount(i)), i.imsi.is) + }) + val colsRcs = tpl.fieldsForList map { f => + Column[RadCheckSub](f.displayName, valF(f) _) + } + val colsRrs = RadReplySub.fieldsForList map { f => + Column[RadCheckSub](f.displayName, { i: RadCheckSub => + rrs.get(i.imsi.is).map { r => + f.actualField(r).asHtml } getOrElse NodeSeq.Empty }) + } + val colsRrn = RadReplyNai.fieldsForList map { f => + Column[RadCheckSub](f.displayName, { i: RadCheckSub => + rrn.get(i.imsi12.is).map { r => + f.actualField(r).asHtml } getOrElse NodeSeq.Empty }) + } + val colsRcn = RadCheckNai.fieldsForList map { f => + Column[RadCheckSub](f.displayName, { i: RadCheckSub => + val o: Option[NodeSeq] = for { + r <- rrn.get(i.imsi12.is) + n <- rcn.get(r.esn.is) + } yield { + f.actualField(n).asHtml + } + o getOrElse NodeSeq.Empty + }) + } + + Table[RadCheckSub](List(imsiCol) ++ colsRcs ++ colsRrs ++ + tpl.ifAdsl(Nil, colsRrn ++ colsRcn), rcs) + } +} + +object AccountSubPanel { + def apply(a: RadCheckSub): (NodeSeq => NodeSeq) = { + val r = a.replySub.openOr(RadReplySub.create) + Panel(a.fieldsForDisplay.map(AttrRow(_)) ++ + r.fieldsForDisplay.map(AttrRow(_))) + } +} + +object AccountNaiPanel { + def apply(a: RadCheckSub): (NodeSeq => NodeSeq) = { + val r = a.replyNai.openOr(RadReplyNai.create) + val ch = a.checkNai.openOr(RadCheckNai.create) + Panel(ch.fieldsForDisplay.map(AttrRow(_)) ++ + r.fieldsForDisplay.map(AttrRow(_))) + } +} + +object SyslogTable { + def apply(rows: Iterable[Syslog], includeImsi: Boolean) = { + val imsiCol: List[MappedField[_, Syslog]] = includeImsi match { + case true => List(Syslog.imsi) + case false => Nil + } + Table[Syslog]((Syslog.fieldsForList ++ imsiCol).map { f => + Column[Syslog](f.displayName, { i: Syslog => f.actualField(i).asHtml }) + }, rows) + } +} + +// vim: set ts=2 sw=2 et: diff -r 69e26359f2c8 -r cf829ec742b3 src/main/scala/radview/snippet/CellSnippet.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/radview/snippet/CellSnippet.scala Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,193 @@ +/* + * Copyright 2011 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package radview.snippet + +import java.util.Date +import net.liftweb.common._ +import net.liftweb.http._ +import net.liftweb.http.{RewriteRequest => RReq, RewriteResponse => RResp, ParsePath => PP} +import net.liftweb.mapper._ +import net.liftweb.sitemap._ +import net.liftweb.sitemap.Loc._ +import net.liftweb.util._ +import net.liftweb.util.Helpers.{asInt, now} +import net.tz.lift.snippet._ +import net.tz.lift.util._ +import org.joda.time.{DateMidnight, DateTime, Period} +import radview.model.{Cdr, Cell} +import scala.xml.{Elem, NodeSeq, Text} + +object AsCell { + def unapply(in: String): Option[Cell] = Cell.findByKey(in) + def unapply(in: CellLoc): Option[Cell] = in match { + case CellView(c) => Some(c) + case ActiveSessions(c) => Some(c) + case _ => None + } +} + +abstract sealed class CellLoc +case object NoSuchCell extends CellLoc +case class CellView(c: Cell) extends CellLoc +case object CellSearch extends CellLoc +case class ActiveSessions(c: Cell) extends CellLoc + +object CellSnippet extends Loc[CellLoc] with SnippetHelpers with Loggable { + + val name = "cell" + val prefix = "cell" + val tpl = "cell" + val params = List(Hidden) + val defaultValue = Full(CellSearch) + val link = new Link[CellLoc](List(prefix), true) { + override def createPath(l: CellLoc): String = l match { + case CellView(c) => mkPath(prefix, c.idpk.is) + case ActiveSessions(c) => mkPath(prefix, c.idpk.is, "active-sessions") + case _ => prefix + } + } + def url(l: CellLoc) = link.createPath(l) + + val text = LinkText[CellLoc](l => Text(l match { + case NoSuchCell => "No such cell" + case CellView(c) => "Cell " + c.btsName + case CellSearch => "Cell search" + case ActiveSessions(c) => "Active sessions on " + c.btsName + })) + + import AsDateMidnight.d2dm + + override def rewrite: LocRewrite = Full({ + case RReq(PP("cell" :: AsCell(c) :: xs, _, _,_), _, _) => xs match { + case "active-sessions" :: Nil => + ActionLinks append A(url(CellView(c)), "Back to cell") + (RResp(List(tpl, "view")), ActiveSessions(c)) + case _ => + ActionLinks append A(url(ActiveSessions(c)), "Active sessions") + (RResp(List(tpl, "view")), CellView(c)) + } + + case RReq(PP(List("cell"), _, _,_), _, _) => + (RResp(List(tpl, "search")), CellSearch) + }) + + override def snippets: SnippetTest = { + case ("panel", Full(AsCell(c))) => CellPanel(c) + + case ("form", Full(CellSearch)) => searchForm + case ("form", Full(ActiveSessions(_))) => activeSessionsForm + case ("list", Full(CellSearch)) => searchList + case ("list", Full(ActiveSessions(c))) => activeSessionsTable(c) + + case ("panel", _) => ClearNodes + case ("form", _) => ClearNodes + case ("list", _) => ClearNodes + } + + object mxCnt extends RequestVar[Int](100) + object searchField extends RequestVar[Box[Int]](Empty) + object searchValue extends RequestVar[String]("") + + lazy val searchFields: List[MappedField[_, Cell]] = Cell.fieldsForSearch + + def searchForm: (NodeSeq => NodeSeq) = { + import AttrRow.{formRow, submitRow} + SimpleForm(List( + formRow(SHtml.selectObj[Int]( + searchFields.zipWithIndex.map(f => (f._2, f._1.displayName)), + searchField.is, { i => searchField(Full(i)) } ), + SHtml.textElem(searchValue) + ), + formRow(Text("Max. results"), SHtml.selectObj[Int]( + List(100, 200, 500) map { i => (i, i.toString) }, Full(mxCnt.is), + mxCnt(_))) + ), "Search") + } + + object from extends RequestVar[DateTime]((new DateTime).minusHours(1)) + object to extends RequestVar[DateTime](new DateTime) + object runQuery extends RequestVar[Box[Any]](Empty) + + def activeSessionsForm: (NodeSeq => NodeSeq) = { + object dateVal extends RequestVar[String](AsDateMidnight(from)) + object fromT extends RequestVar[String](AsTimePeriod(from)) + object toT extends RequestVar[String](AsTimePeriod(to)) + + def setD() = { + for { + d <- AsDateMidnight(dateVal) + f <- AsTimePeriod(fromT) + t <- AsTimePeriod(toT) + } yield { + from(d.toDateTime.plus(f)) + to(d.toDateTime.plus(t)) + } + dateVal(AsDateMidnight(from)) + fromT(AsTimePeriod(from)) + toT(AsTimePeriod(to)) + runQuery(Full(1)) + } + + import AttrRow._ + SimpleForm(List( + formRow(Text("Date"), SHtml.textElem(dateVal, ("class", "date"))), + formRow(Text("Time from"), SHtml.textElem(fromT)), + formRow(Text("Time to"), SHtml.textElem(toT))), "Submit", setD) + } + + def qp(f: MappedField[_, Cell], v: String): Box[QueryParam[Cell]] = { + val strCls = classOf[String] + val intCls = classOf[Int] + val clz = f.dbFieldClass + f match { + case fx: MappedField[String, Cell] if clz == classOf[String] => + Full(Like(fx, "%" + v + "%")) + case fx: MappedField[Int, Cell] if clz == classOf[Int] => + asInt(v) map { By(fx, _) } + case _ => Empty + } + } + + def searchList: (NodeSeq => NodeSeq) = searchField.is map { idx => + val rows = searchValue.is match { + case "" | null => Nil + case v => + qp(searchFields(idx), v) map { p => + Cell.findAll(p, OrderBy(Cell.btsName, Ascending), MaxRows(mxCnt.is)) + } openOr Nil + } + Table[Cell](Cell.fieldsForList.map(f => Column[Cell](f.displayName, + {i: Cell => f.actualField(i).asHtml})), rows) + } openOr ClearNodes + + + def activeSessionsTable(c: Cell): (NodeSeq => NodeSeq) = runQuery map { v => + import AsDateMidnight.dt2d + val sess1 = Cdr.findAllFields(List(Cdr.sid), + By_<(Cdr.ts, to.plusMinutes(1)), + By_>(Cdr.ts, from.minusMinutes(1)), By(Cdr.cell, c.idpk), Distinct[Cdr]) + val sessions = Cdr.sessions(sess1 map { _.sid.is }) + CdrSessionTable(sessions) + } openOr ClearNodes + +} + +object CellPanel { + def apply(c: Cell): (NodeSeq => NodeSeq) = + Panel.fromFields(c.fieldsForDisplay) +} + +// vim: set ts=2 sw=2 et: diff -r 69e26359f2c8 -r cf829ec742b3 src/main/scala/radview/snippet/SessionSnippet.scala --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/scala/radview/snippet/SessionSnippet.scala Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,134 @@ +/* + * Copyright 2011 Tomas Zeman + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package radview.snippet + +import net.liftweb.common._ +import net.liftweb.http._ +import net.liftweb.http.{RewriteRequest => RReq, RewriteResponse => RResp, ParsePath => PP} +import net.liftweb.mapper._ +import net.liftweb.sitemap._ +import net.liftweb.sitemap.Loc._ +import net.liftweb.util._ +import net.liftweb.util.{Cell => _} +import net.tz.lift.snippet._ +import net.tz.lift.util._ +import radview.model.{Cdr, CdrSession, Cell} +import scala.xml.{Elem, NodeSeq, Text} + +object AsCdrSession { + def unapply(in: String): Option[CdrSession] = Cdr.session(in) +} + +object SessionSnippet extends Loc[CdrSession] with SnippetHelpers { + + object showCdr extends RequestVar[Boolean](false) + + val name = "session" + val prefix = "session" + val tpl = "session" + val params = List(Hidden) + val defaultValue = Empty + val link = new Link[CdrSession](List(prefix), true) { + override def createPath(s: CdrSession): String = + mkPath(prefix, s.sid) + } + val text = LinkText[CdrSession](s => Text("Session " + s.sid)) + + override def rewrite: LocRewrite = Full({ + case RReq(PP("session" :: AsCdrSession(s) :: xs, _,_,_), _,_) => + showCdr(xs contains "cdr") + ActionLinks.append(showCdr.is match { + case true => A(url(s, false), "Hide CDR") + case false => A(url(s, true), "Show CDR") + }) + (RResp(List("session")), s) + }) + + def url(sid: String): String = "/" + prefix + "/" + sid + def url(s: CdrSession): String = url(s, false) + def url(s: CdrSession, _showCdr: Boolean): String = link.createPath(s) + + (if (_showCdr) "/cdr" else "") + + override def snippets: SnippetTest = { + case ("panel", Full(s)) => SessionPanel(s) + case ("cdr-list", Full(s)) if showCdr => CdrTable(s) + + case ("panel", _) => ClearNodes + case ("cdr-list", _) => ClearNodes + } + +} + +object SessionPanel { + def apply(s: CdrSession): (NodeSeq => NodeSeq) = { + val l1 = List( + (Cdr.sid.displayName, s.sid), + ("Session start", AsDateTime(s.start)), + ("Session end", AsDateTime(s.end)), + (Cdr.inBytes.displayName + " [MB]", Bytes.mb(s.inBytes)), + (Cdr.outBytes.displayName + " [MB]", Bytes.mb(s.outBytes)) + ) map { r: (String, Any) => + AttrRow(Text(r._1), Text(r._2.toString)) + } + Panel(l1 :+ AttrRow(Text("Cell"), s.cell.map { c => + A(CellSnippet.url(CellView(c)), c.btsName.is) } openOr NodeSeq.Empty)) + } +} + +object CdrSessionTable { + def apply(l: Iterable[CdrSession]): (NodeSeq => NodeSeq) = { + val cells: Map[String, Cell] = Map() ++ + (Cell.findAll(ByList(Cell.idpk, l.map { _.cellId }.toList.distinct)). + map { c => (c.idpk.is, c) }) + Table[CdrSession](List( + Column(Cdr.sid.displayName, { s: CdrSession => + A(SessionSnippet.url(s), s.sid) } ), + Column("Session start", { s: CdrSession => Text(AsDateTime(s.start)) } ), + Column("Session end", { s: CdrSession => Text(AsDateTime(s.end)) } ), + Column(Cdr.inBytes.displayName + " [MB]", + { s: CdrSession => Bytes.mb(s.inBytes) }, "td-right"), + Column(Cdr.outBytes.displayName + " [MB]", + { s: CdrSession => Bytes.mb(s.outBytes) }, "td-right"), + Column("Cell", { s: CdrSession => cells.get(s.cellId).map { c => + A(CellSnippet.url(CellView(c)), c.btsName.is) } getOrElse NodeSeq.Empty }) + ), l) + } +} + +object CdrTable { + import Cdr._ + def apply(cdr: Iterable[Cdr]): (NodeSeq => NodeSeq) = { + + def cssF(f: MappedField[_, Cdr])(cdr: Cdr): Box[String] = { + f.actualField(cdr).is match { + case l: Long => Full("td-right") + case i: Int => Full("td-right") + case _ => Empty + } + } + + def valF(f: MappedField[_, Cdr])(cdr: Cdr): NodeSeq = + f.actualField(cdr).asHtml + + Table[Cdr](fieldsForList.map { f => + new Column[Cdr](Text(f.displayName), valF(f) _, cssF(f) _) }, cdr) + } + + def apply(s: CdrSession): (NodeSeq => NodeSeq) = apply(bySession(s)) +} + + +// vim: set ts=2 sw=2 et: diff -r 69e26359f2c8 -r cf829ec742b3 src/main/webapp/WEB-INF/web.xml --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/WEB-INF/web.xml Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,24 @@ + + + + + + + + LiftFilter + RadView Lift Filter + RadView Lift Filter + net.liftweb.http.LiftFilter + + + + LiftFilter + /* + + + + diff -r 69e26359f2c8 -r cf829ec742b3 src/main/webapp/account/search.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/account/search.html Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,24 @@ + + + + + Account Search + + +
+
+

+
+
+
+
+
+ +
+
+ + + + + + diff -r 69e26359f2c8 -r cf829ec742b3 src/main/webapp/account/view.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/account/view.html Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,57 @@ + + + + + Account View + + +
+ + + + + +
+

+
+
+
+ Subscriber data + +
+
+
 
+
+ +
+ Network Access Identifier info + +
+
+
+
+ +
+
+
+
+
+
+
+
+
+
+ + + + + + + diff -r 69e26359f2c8 -r cf829ec742b3 src/main/webapp/cell/search.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/cell/search.html Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,24 @@ + + + + + Cell Search + + +
+
+

+
+
+ +
+
+ +
+
+ + + + + + diff -r 69e26359f2c8 -r cf829ec742b3 src/main/webapp/cell/view.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/cell/view.html Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,39 @@ + + + + + Cell view + + +
+ + + + + + +
+

+
+
+ +
+
+ +
+
+ +
+
+ + + + + diff -r 69e26359f2c8 -r cf829ec742b3 src/main/webapp/css/jquery-ui-1.8.9.custom.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/css/jquery-ui-1.8.9.custom.css Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,362 @@ +/* + * jQuery UI CSS Framework 1.8.9 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + */ + +/* Layout helpers +----------------------------------*/ +.ui-helper-hidden { display: none; } +.ui-helper-hidden-accessible { position: absolute !important; clip: rect(1px 1px 1px 1px); clip: rect(1px,1px,1px,1px); } +.ui-helper-reset { margin: 0; padding: 0; border: 0; outline: 0; line-height: 1.3; text-decoration: none; font-size: 100%; list-style: none; } +.ui-helper-clearfix:after { content: "."; display: block; height: 0; clear: both; visibility: hidden; } +.ui-helper-clearfix { display: inline-block; } +/* required comment for clearfix to work in Opera \*/ +* html .ui-helper-clearfix { height:1%; } +.ui-helper-clearfix { display:block; } +/* end clearfix */ +.ui-helper-zfix { width: 100%; height: 100%; top: 0; left: 0; position: absolute; opacity: 0; filter:Alpha(Opacity=0); } + + +/* Interaction Cues +----------------------------------*/ +.ui-state-disabled { cursor: default !important; } + + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { display: block; text-indent: -99999px; overflow: hidden; background-repeat: no-repeat; } + + +/* Misc visuals +----------------------------------*/ + +/* Overlays */ +.ui-widget-overlay { position: absolute; top: 0; left: 0; width: 100%; height: 100%; } + + +/* + * jQuery UI CSS Framework 1.8.9 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Theming/API + * + * To view and modify this theme, visit http://jqueryui.com/themeroller/?ffDefault=Trebuchet%20MS,%20Tahoma,%20Verdana,%20Arial,%20sans-serif&fwDefault=bold&fsDefault=1.1em&cornerRadius=4px&bgColorHeader=f6a828&bgTextureHeader=12_gloss_wave.png&bgImgOpacityHeader=35&borderColorHeader=e78f08&fcHeader=ffffff&iconColorHeader=ffffff&bgColorContent=eeeeee&bgTextureContent=03_highlight_soft.png&bgImgOpacityContent=100&borderColorContent=dddddd&fcContent=333333&iconColorContent=222222&bgColorDefault=f6f6f6&bgTextureDefault=02_glass.png&bgImgOpacityDefault=100&borderColorDefault=cccccc&fcDefault=1c94c4&iconColorDefault=ef8c08&bgColorHover=fdf5ce&bgTextureHover=02_glass.png&bgImgOpacityHover=100&borderColorHover=fbcb09&fcHover=c77405&iconColorHover=ef8c08&bgColorActive=ffffff&bgTextureActive=02_glass.png&bgImgOpacityActive=65&borderColorActive=fbd850&fcActive=eb8f00&iconColorActive=ef8c08&bgColorHighlight=ffe45c&bgTextureHighlight=03_highlight_soft.png&bgImgOpacityHighlight=75&borderColorHighlight=fed22f&fcHighlight=363636&iconColorHighlight=228ef1&bgColorError=b81900&bgTextureError=08_diagonals_thick.png&bgImgOpacityError=18&borderColorError=cd0a0a&fcError=ffffff&iconColorError=ffd27a&bgColorOverlay=666666&bgTextureOverlay=08_diagonals_thick.png&bgImgOpacityOverlay=20&opacityOverlay=50&bgColorShadow=000000&bgTextureShadow=01_flat.png&bgImgOpacityShadow=10&opacityShadow=20&thicknessShadow=5px&offsetTopShadow=-5px&offsetLeftShadow=-5px&cornerRadiusShadow=5px + */ + + +/* Component containers +----------------------------------*/ +.ui-widget { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1.1em; } +.ui-widget .ui-widget { font-size: 1em; } +.ui-widget input, .ui-widget select, .ui-widget textarea, .ui-widget button { font-family: Trebuchet MS, Tahoma, Verdana, Arial, sans-serif; font-size: 1em; } +.ui-widget-content { border: 1px solid #dddddd; background: #eeeeee url(images/ui-bg_highlight-soft_100_eeeeee_1x100.png) 50% top repeat-x; color: #333333; } +.ui-widget-content a { color: #333333; } +.ui-widget-header { border: 1px solid #e78f08; background: #f6a828 url(images/ui-bg_gloss-wave_35_f6a828_500x100.png) 50% 50% repeat-x; color: #ffffff; font-weight: bold; } +.ui-widget-header a { color: #ffffff; } + +/* Interaction states +----------------------------------*/ +.ui-state-default, .ui-widget-content .ui-state-default, .ui-widget-header .ui-state-default { border: 1px solid #cccccc; background: #f6f6f6 url(images/ui-bg_glass_100_f6f6f6_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #1c94c4; } +.ui-state-default a, .ui-state-default a:link, .ui-state-default a:visited { color: #1c94c4; text-decoration: none; } +.ui-state-hover, .ui-widget-content .ui-state-hover, .ui-widget-header .ui-state-hover, .ui-state-focus, .ui-widget-content .ui-state-focus, .ui-widget-header .ui-state-focus { border: 1px solid #fbcb09; background: #fdf5ce url(images/ui-bg_glass_100_fdf5ce_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #c77405; } +.ui-state-hover a, .ui-state-hover a:hover { color: #c77405; text-decoration: none; } +.ui-state-active, .ui-widget-content .ui-state-active, .ui-widget-header .ui-state-active { border: 1px solid #fbd850; background: #ffffff url(images/ui-bg_glass_65_ffffff_1x400.png) 50% 50% repeat-x; font-weight: bold; color: #eb8f00; } +.ui-state-active a, .ui-state-active a:link, .ui-state-active a:visited { color: #eb8f00; text-decoration: none; } +.ui-widget :active { outline: none; } + +/* Interaction Cues +----------------------------------*/ +.ui-state-highlight, .ui-widget-content .ui-state-highlight, .ui-widget-header .ui-state-highlight {border: 1px solid #fed22f; background: #ffe45c url(images/ui-bg_highlight-soft_75_ffe45c_1x100.png) 50% top repeat-x; color: #363636; } +.ui-state-highlight a, .ui-widget-content .ui-state-highlight a,.ui-widget-header .ui-state-highlight a { color: #363636; } +.ui-state-error, .ui-widget-content .ui-state-error, .ui-widget-header .ui-state-error {border: 1px solid #cd0a0a; background: #b81900 url(images/ui-bg_diagonals-thick_18_b81900_40x40.png) 50% 50% repeat; color: #ffffff; } +.ui-state-error a, .ui-widget-content .ui-state-error a, .ui-widget-header .ui-state-error a { color: #ffffff; } +.ui-state-error-text, .ui-widget-content .ui-state-error-text, .ui-widget-header .ui-state-error-text { color: #ffffff; } +.ui-priority-primary, .ui-widget-content .ui-priority-primary, .ui-widget-header .ui-priority-primary { font-weight: bold; } +.ui-priority-secondary, .ui-widget-content .ui-priority-secondary, .ui-widget-header .ui-priority-secondary { opacity: .7; filter:Alpha(Opacity=70); font-weight: normal; } +.ui-state-disabled, .ui-widget-content .ui-state-disabled, .ui-widget-header .ui-state-disabled { opacity: .35; filter:Alpha(Opacity=35); background-image: none; } + +/* Icons +----------------------------------*/ + +/* states and images */ +.ui-icon { width: 16px; height: 16px; background-image: url(images/ui-icons_222222_256x240.png); } +.ui-widget-content .ui-icon {background-image: url(images/ui-icons_222222_256x240.png); } +.ui-widget-header .ui-icon {background-image: url(images/ui-icons_ffffff_256x240.png); } +.ui-state-default .ui-icon { background-image: url(images/ui-icons_ef8c08_256x240.png); } +.ui-state-hover .ui-icon, .ui-state-focus .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); } +.ui-state-active .ui-icon {background-image: url(images/ui-icons_ef8c08_256x240.png); } +.ui-state-highlight .ui-icon {background-image: url(images/ui-icons_228ef1_256x240.png); } +.ui-state-error .ui-icon, .ui-state-error-text .ui-icon {background-image: url(images/ui-icons_ffd27a_256x240.png); } + +/* positioning */ +.ui-icon-carat-1-n { background-position: 0 0; } +.ui-icon-carat-1-ne { background-position: -16px 0; } +.ui-icon-carat-1-e { background-position: -32px 0; } +.ui-icon-carat-1-se { background-position: -48px 0; } +.ui-icon-carat-1-s { background-position: -64px 0; } +.ui-icon-carat-1-sw { background-position: -80px 0; } +.ui-icon-carat-1-w { background-position: -96px 0; } +.ui-icon-carat-1-nw { background-position: -112px 0; } +.ui-icon-carat-2-n-s { background-position: -128px 0; } +.ui-icon-carat-2-e-w { background-position: -144px 0; } +.ui-icon-triangle-1-n { background-position: 0 -16px; } +.ui-icon-triangle-1-ne { background-position: -16px -16px; } +.ui-icon-triangle-1-e { background-position: -32px -16px; } +.ui-icon-triangle-1-se { background-position: -48px -16px; } +.ui-icon-triangle-1-s { background-position: -64px -16px; } +.ui-icon-triangle-1-sw { background-position: -80px -16px; } +.ui-icon-triangle-1-w { background-position: -96px -16px; } +.ui-icon-triangle-1-nw { background-position: -112px -16px; } +.ui-icon-triangle-2-n-s { background-position: -128px -16px; } +.ui-icon-triangle-2-e-w { background-position: -144px -16px; } +.ui-icon-arrow-1-n { background-position: 0 -32px; } +.ui-icon-arrow-1-ne { background-position: -16px -32px; } +.ui-icon-arrow-1-e { background-position: -32px -32px; } +.ui-icon-arrow-1-se { background-position: -48px -32px; } +.ui-icon-arrow-1-s { background-position: -64px -32px; } +.ui-icon-arrow-1-sw { background-position: -80px -32px; } +.ui-icon-arrow-1-w { background-position: -96px -32px; } +.ui-icon-arrow-1-nw { background-position: -112px -32px; } +.ui-icon-arrow-2-n-s { background-position: -128px -32px; } +.ui-icon-arrow-2-ne-sw { background-position: -144px -32px; } +.ui-icon-arrow-2-e-w { background-position: -160px -32px; } +.ui-icon-arrow-2-se-nw { background-position: -176px -32px; } +.ui-icon-arrowstop-1-n { background-position: -192px -32px; } +.ui-icon-arrowstop-1-e { background-position: -208px -32px; } +.ui-icon-arrowstop-1-s { background-position: -224px -32px; } +.ui-icon-arrowstop-1-w { background-position: -240px -32px; } +.ui-icon-arrowthick-1-n { background-position: 0 -48px; } +.ui-icon-arrowthick-1-ne { background-position: -16px -48px; } +.ui-icon-arrowthick-1-e { background-position: -32px -48px; } +.ui-icon-arrowthick-1-se { background-position: -48px -48px; } +.ui-icon-arrowthick-1-s { background-position: -64px -48px; } +.ui-icon-arrowthick-1-sw { background-position: -80px -48px; } +.ui-icon-arrowthick-1-w { background-position: -96px -48px; } +.ui-icon-arrowthick-1-nw { background-position: -112px -48px; } +.ui-icon-arrowthick-2-n-s { background-position: -128px -48px; } +.ui-icon-arrowthick-2-ne-sw { background-position: -144px -48px; } +.ui-icon-arrowthick-2-e-w { background-position: -160px -48px; } +.ui-icon-arrowthick-2-se-nw { background-position: -176px -48px; } +.ui-icon-arrowthickstop-1-n { background-position: -192px -48px; } +.ui-icon-arrowthickstop-1-e { background-position: -208px -48px; } +.ui-icon-arrowthickstop-1-s { background-position: -224px -48px; } +.ui-icon-arrowthickstop-1-w { background-position: -240px -48px; } +.ui-icon-arrowreturnthick-1-w { background-position: 0 -64px; } +.ui-icon-arrowreturnthick-1-n { background-position: -16px -64px; } +.ui-icon-arrowreturnthick-1-e { background-position: -32px -64px; } +.ui-icon-arrowreturnthick-1-s { background-position: -48px -64px; } +.ui-icon-arrowreturn-1-w { background-position: -64px -64px; } +.ui-icon-arrowreturn-1-n { background-position: -80px -64px; } +.ui-icon-arrowreturn-1-e { background-position: -96px -64px; } +.ui-icon-arrowreturn-1-s { background-position: -112px -64px; } +.ui-icon-arrowrefresh-1-w { background-position: -128px -64px; } +.ui-icon-arrowrefresh-1-n { background-position: -144px -64px; } +.ui-icon-arrowrefresh-1-e { background-position: -160px -64px; } +.ui-icon-arrowrefresh-1-s { background-position: -176px -64px; } +.ui-icon-arrow-4 { background-position: 0 -80px; } +.ui-icon-arrow-4-diag { background-position: -16px -80px; } +.ui-icon-extlink { background-position: -32px -80px; } +.ui-icon-newwin { background-position: -48px -80px; } +.ui-icon-refresh { background-position: -64px -80px; } +.ui-icon-shuffle { background-position: -80px -80px; } +.ui-icon-transfer-e-w { background-position: -96px -80px; } +.ui-icon-transferthick-e-w { background-position: -112px -80px; } +.ui-icon-folder-collapsed { background-position: 0 -96px; } +.ui-icon-folder-open { background-position: -16px -96px; } +.ui-icon-document { background-position: -32px -96px; } +.ui-icon-document-b { background-position: -48px -96px; } +.ui-icon-note { background-position: -64px -96px; } +.ui-icon-mail-closed { background-position: -80px -96px; } +.ui-icon-mail-open { background-position: -96px -96px; } +.ui-icon-suitcase { background-position: -112px -96px; } +.ui-icon-comment { background-position: -128px -96px; } +.ui-icon-person { background-position: -144px -96px; } +.ui-icon-print { background-position: -160px -96px; } +.ui-icon-trash { background-position: -176px -96px; } +.ui-icon-locked { background-position: -192px -96px; } +.ui-icon-unlocked { background-position: -208px -96px; } +.ui-icon-bookmark { background-position: -224px -96px; } +.ui-icon-tag { background-position: -240px -96px; } +.ui-icon-home { background-position: 0 -112px; } +.ui-icon-flag { background-position: -16px -112px; } +.ui-icon-calendar { background-position: -32px -112px; } +.ui-icon-cart { background-position: -48px -112px; } +.ui-icon-pencil { background-position: -64px -112px; } +.ui-icon-clock { background-position: -80px -112px; } +.ui-icon-disk { background-position: -96px -112px; } +.ui-icon-calculator { background-position: -112px -112px; } +.ui-icon-zoomin { background-position: -128px -112px; } +.ui-icon-zoomout { background-position: -144px -112px; } +.ui-icon-search { background-position: -160px -112px; } +.ui-icon-wrench { background-position: -176px -112px; } +.ui-icon-gear { background-position: -192px -112px; } +.ui-icon-heart { background-position: -208px -112px; } +.ui-icon-star { background-position: -224px -112px; } +.ui-icon-link { background-position: -240px -112px; } +.ui-icon-cancel { background-position: 0 -128px; } +.ui-icon-plus { background-position: -16px -128px; } +.ui-icon-plusthick { background-position: -32px -128px; } +.ui-icon-minus { background-position: -48px -128px; } +.ui-icon-minusthick { background-position: -64px -128px; } +.ui-icon-close { background-position: -80px -128px; } +.ui-icon-closethick { background-position: -96px -128px; } +.ui-icon-key { background-position: -112px -128px; } +.ui-icon-lightbulb { background-position: -128px -128px; } +.ui-icon-scissors { background-position: -144px -128px; } +.ui-icon-clipboard { background-position: -160px -128px; } +.ui-icon-copy { background-position: -176px -128px; } +.ui-icon-contact { background-position: -192px -128px; } +.ui-icon-image { background-position: -208px -128px; } +.ui-icon-video { background-position: -224px -128px; } +.ui-icon-script { background-position: -240px -128px; } +.ui-icon-alert { background-position: 0 -144px; } +.ui-icon-info { background-position: -16px -144px; } +.ui-icon-notice { background-position: -32px -144px; } +.ui-icon-help { background-position: -48px -144px; } +.ui-icon-check { background-position: -64px -144px; } +.ui-icon-bullet { background-position: -80px -144px; } +.ui-icon-radio-off { background-position: -96px -144px; } +.ui-icon-radio-on { background-position: -112px -144px; } +.ui-icon-pin-w { background-position: -128px -144px; } +.ui-icon-pin-s { background-position: -144px -144px; } +.ui-icon-play { background-position: 0 -160px; } +.ui-icon-pause { background-position: -16px -160px; } +.ui-icon-seek-next { background-position: -32px -160px; } +.ui-icon-seek-prev { background-position: -48px -160px; } +.ui-icon-seek-end { background-position: -64px -160px; } +.ui-icon-seek-start { background-position: -80px -160px; } +/* ui-icon-seek-first is deprecated, use ui-icon-seek-start instead */ +.ui-icon-seek-first { background-position: -80px -160px; } +.ui-icon-stop { background-position: -96px -160px; } +.ui-icon-eject { background-position: -112px -160px; } +.ui-icon-volume-off { background-position: -128px -160px; } +.ui-icon-volume-on { background-position: -144px -160px; } +.ui-icon-power { background-position: 0 -176px; } +.ui-icon-signal-diag { background-position: -16px -176px; } +.ui-icon-signal { background-position: -32px -176px; } +.ui-icon-battery-0 { background-position: -48px -176px; } +.ui-icon-battery-1 { background-position: -64px -176px; } +.ui-icon-battery-2 { background-position: -80px -176px; } +.ui-icon-battery-3 { background-position: -96px -176px; } +.ui-icon-circle-plus { background-position: 0 -192px; } +.ui-icon-circle-minus { background-position: -16px -192px; } +.ui-icon-circle-close { background-position: -32px -192px; } +.ui-icon-circle-triangle-e { background-position: -48px -192px; } +.ui-icon-circle-triangle-s { background-position: -64px -192px; } +.ui-icon-circle-triangle-w { background-position: -80px -192px; } +.ui-icon-circle-triangle-n { background-position: -96px -192px; } +.ui-icon-circle-arrow-e { background-position: -112px -192px; } +.ui-icon-circle-arrow-s { background-position: -128px -192px; } +.ui-icon-circle-arrow-w { background-position: -144px -192px; } +.ui-icon-circle-arrow-n { background-position: -160px -192px; } +.ui-icon-circle-zoomin { background-position: -176px -192px; } +.ui-icon-circle-zoomout { background-position: -192px -192px; } +.ui-icon-circle-check { background-position: -208px -192px; } +.ui-icon-circlesmall-plus { background-position: 0 -208px; } +.ui-icon-circlesmall-minus { background-position: -16px -208px; } +.ui-icon-circlesmall-close { background-position: -32px -208px; } +.ui-icon-squaresmall-plus { background-position: -48px -208px; } +.ui-icon-squaresmall-minus { background-position: -64px -208px; } +.ui-icon-squaresmall-close { background-position: -80px -208px; } +.ui-icon-grip-dotted-vertical { background-position: 0 -224px; } +.ui-icon-grip-dotted-horizontal { background-position: -16px -224px; } +.ui-icon-grip-solid-vertical { background-position: -32px -224px; } +.ui-icon-grip-solid-horizontal { background-position: -48px -224px; } +.ui-icon-gripsmall-diagonal-se { background-position: -64px -224px; } +.ui-icon-grip-diagonal-se { background-position: -80px -224px; } + + +/* Misc visuals +----------------------------------*/ + +/* Corner radius */ +.ui-corner-tl { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; } +.ui-corner-tr { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; } +.ui-corner-bl { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; } +.ui-corner-br { -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } +.ui-corner-top { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; } +.ui-corner-bottom { -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } +.ui-corner-right { -moz-border-radius-topright: 4px; -webkit-border-top-right-radius: 4px; border-top-right-radius: 4px; -moz-border-radius-bottomright: 4px; -webkit-border-bottom-right-radius: 4px; border-bottom-right-radius: 4px; } +.ui-corner-left { -moz-border-radius-topleft: 4px; -webkit-border-top-left-radius: 4px; border-top-left-radius: 4px; -moz-border-radius-bottomleft: 4px; -webkit-border-bottom-left-radius: 4px; border-bottom-left-radius: 4px; } +.ui-corner-all { -moz-border-radius: 4px; -webkit-border-radius: 4px; border-radius: 4px; } + +/* Overlays */ +.ui-widget-overlay { background: #666666 url(images/ui-bg_diagonals-thick_20_666666_40x40.png) 50% 50% repeat; opacity: .50;filter:Alpha(Opacity=50); } +.ui-widget-shadow { margin: -5px 0 0 -5px; padding: 5px; background: #000000 url(images/ui-bg_flat_10_000000_40x100.png) 50% 50% repeat-x; opacity: .20;filter:Alpha(Opacity=20); -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; }/* + * jQuery UI Datepicker 1.8.9 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI/Datepicker#theming + */ +.ui-datepicker { width: 17em; padding: .2em .2em 0; display: none; } +.ui-datepicker .ui-datepicker-header { position:relative; padding:.2em 0; } +.ui-datepicker .ui-datepicker-prev, .ui-datepicker .ui-datepicker-next { position:absolute; top: 2px; width: 1.8em; height: 1.8em; } +.ui-datepicker .ui-datepicker-prev-hover, .ui-datepicker .ui-datepicker-next-hover { top: 1px; } +.ui-datepicker .ui-datepicker-prev { left:2px; } +.ui-datepicker .ui-datepicker-next { right:2px; } +.ui-datepicker .ui-datepicker-prev-hover { left:1px; } +.ui-datepicker .ui-datepicker-next-hover { right:1px; } +.ui-datepicker .ui-datepicker-prev span, .ui-datepicker .ui-datepicker-next span { display: block; position: absolute; left: 50%; margin-left: -8px; top: 50%; margin-top: -8px; } +.ui-datepicker .ui-datepicker-title { margin: 0 2.3em; line-height: 1.8em; text-align: center; } +.ui-datepicker .ui-datepicker-title select { font-size:1em; margin:1px 0; } +.ui-datepicker select.ui-datepicker-month-year {width: 100%;} +.ui-datepicker select.ui-datepicker-month, +.ui-datepicker select.ui-datepicker-year { width: 49%;} +.ui-datepicker table {width: 100%; font-size: .9em; border-collapse: collapse; margin:0 0 .4em; } +.ui-datepicker th { padding: .7em .3em; text-align: center; font-weight: bold; border: 0; } +.ui-datepicker td { border: 0; padding: 1px; } +.ui-datepicker td span, .ui-datepicker td a { display: block; padding: .2em; text-align: right; text-decoration: none; } +.ui-datepicker .ui-datepicker-buttonpane { background-image: none; margin: .7em 0 0 0; padding:0 .2em; border-left: 0; border-right: 0; border-bottom: 0; } +.ui-datepicker .ui-datepicker-buttonpane button { float: right; margin: .5em .2em .4em; cursor: pointer; padding: .2em .6em .3em .6em; width:auto; overflow:visible; } +.ui-datepicker .ui-datepicker-buttonpane button.ui-datepicker-current { float:left; } + +/* with multiple calendars */ +.ui-datepicker.ui-datepicker-multi { width:auto; } +.ui-datepicker-multi .ui-datepicker-group { float:left; } +.ui-datepicker-multi .ui-datepicker-group table { width:95%; margin:0 auto .4em; } +.ui-datepicker-multi-2 .ui-datepicker-group { width:50%; } +.ui-datepicker-multi-3 .ui-datepicker-group { width:33.3%; } +.ui-datepicker-multi-4 .ui-datepicker-group { width:25%; } +.ui-datepicker-multi .ui-datepicker-group-last .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-group-middle .ui-datepicker-header { border-left-width:0; } +.ui-datepicker-multi .ui-datepicker-buttonpane { clear:left; } +.ui-datepicker-row-break { clear:both; width:100%; } + +/* RTL support */ +.ui-datepicker-rtl { direction: rtl; } +.ui-datepicker-rtl .ui-datepicker-prev { right: 2px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next { left: 2px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-prev:hover { right: 1px; left: auto; } +.ui-datepicker-rtl .ui-datepicker-next:hover { left: 1px; right: auto; } +.ui-datepicker-rtl .ui-datepicker-buttonpane { clear:right; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button { float: left; } +.ui-datepicker-rtl .ui-datepicker-buttonpane button.ui-datepicker-current { float:right; } +.ui-datepicker-rtl .ui-datepicker-group { float:right; } +.ui-datepicker-rtl .ui-datepicker-group-last .ui-datepicker-header { border-right-width:0; border-left-width:1px; } +.ui-datepicker-rtl .ui-datepicker-group-middle .ui-datepicker-header { border-right-width:0; border-left-width:1px; } + +/* IE6 IFRAME FIX (taken from datepicker 1.5.3 */ +.ui-datepicker-cover { + display: none; /*sorry for IE5*/ + display/**/: block; /*sorry for IE5*/ + position: absolute; /*must have*/ + z-index: -1; /*must have*/ + filter: mask(); /*must have*/ + top: -4px; /*must have*/ + left: -4px; /*must have*/ + width: 200px; /*must have*/ + height: 200px; /*must have*/ +} \ No newline at end of file diff -r 69e26359f2c8 -r cf829ec742b3 src/main/webapp/css/site.css --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/css/site.css Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,42 @@ +/* + * Copyright 2011 Tomas Zeman + */ + +.list { + border-collapse: collapse; +} + +.list td, .list thead th { + border: 1px solid black; +} + +.attr-name { + font-weight: bold; + width: 150px; +} + +.attr-name-wide { + font-weight: bold; + width: 230px; +} + + +.attr-value { +} + +.form-name { + font-weight: bold; + width: 150px; +} + +.form-value { +} + +td.td-right { + text-align: right; +} + +/* fix for active sf-menu item in span tag */ +.sf-menu span { + padding: 0.75em 1em; +} diff -r 69e26359f2c8 -r cf829ec742b3 src/main/webapp/index.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/index.html Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,14 @@ + + + + + Home + + +
+

Welcome to Radius Viewer

+
+ + + + diff -r 69e26359f2c8 -r cf829ec742b3 src/main/webapp/js/jquery-ui-1.8.9.custom.min.js --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/js/jquery-ui-1.8.9.custom.min.js Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,99 @@ +/*! + * jQuery UI 1.8.9 + * + * Copyright 2011, AUTHORS.txt (http://jqueryui.com/about) + * Dual licensed under the MIT or GPL Version 2 licenses. + * http://jquery.org/license + * + * http://docs.jquery.com/UI + */ +(function(c,j){function k(a){return!c(a).parents().andSelf().filter(function(){return c.curCSS(this,"visibility")==="hidden"||c.expr.filters.hidden(this)}).length}c.ui=c.ui||{};if(!c.ui.version){c.extend(c.ui,{version:"1.8.9",keyCode:{ALT:18,BACKSPACE:8,CAPS_LOCK:20,COMMA:188,COMMAND:91,COMMAND_LEFT:91,COMMAND_RIGHT:93,CONTROL:17,DELETE:46,DOWN:40,END:35,ENTER:13,ESCAPE:27,HOME:36,INSERT:45,LEFT:37,MENU:93,NUMPAD_ADD:107,NUMPAD_DECIMAL:110,NUMPAD_DIVIDE:111,NUMPAD_ENTER:108,NUMPAD_MULTIPLY:106, +NUMPAD_SUBTRACT:109,PAGE_DOWN:34,PAGE_UP:33,PERIOD:190,RIGHT:39,SHIFT:16,SPACE:32,TAB:9,UP:38,WINDOWS:91}});c.fn.extend({_focus:c.fn.focus,focus:function(a,b){return typeof a==="number"?this.each(function(){var d=this;setTimeout(function(){c(d).focus();b&&b.call(d)},a)}):this._focus.apply(this,arguments)},scrollParent:function(){var a;a=c.browser.msie&&/(static|relative)/.test(this.css("position"))||/absolute/.test(this.css("position"))?this.parents().filter(function(){return/(relative|absolute|fixed)/.test(c.curCSS(this, +"position",1))&&/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0):this.parents().filter(function(){return/(auto|scroll)/.test(c.curCSS(this,"overflow",1)+c.curCSS(this,"overflow-y",1)+c.curCSS(this,"overflow-x",1))}).eq(0);return/fixed/.test(this.css("position"))||!a.length?c(document):a},zIndex:function(a){if(a!==j)return this.css("zIndex",a);if(this.length){a=c(this[0]);for(var b;a.length&&a[0]!==document;){b=a.css("position"); +if(b==="absolute"||b==="relative"||b==="fixed"){b=parseInt(a.css("zIndex"),10);if(!isNaN(b)&&b!==0)return b}a=a.parent()}}return 0},disableSelection:function(){return this.bind((c.support.selectstart?"selectstart":"mousedown")+".ui-disableSelection",function(a){a.preventDefault()})},enableSelection:function(){return this.unbind(".ui-disableSelection")}});c.each(["Width","Height"],function(a,b){function d(f,g,l,m){c.each(e,function(){g-=parseFloat(c.curCSS(f,"padding"+this,true))||0;if(l)g-=parseFloat(c.curCSS(f, +"border"+this+"Width",true))||0;if(m)g-=parseFloat(c.curCSS(f,"margin"+this,true))||0});return g}var e=b==="Width"?["Left","Right"]:["Top","Bottom"],h=b.toLowerCase(),i={innerWidth:c.fn.innerWidth,innerHeight:c.fn.innerHeight,outerWidth:c.fn.outerWidth,outerHeight:c.fn.outerHeight};c.fn["inner"+b]=function(f){if(f===j)return i["inner"+b].call(this);return this.each(function(){c(this).css(h,d(this,f)+"px")})};c.fn["outer"+b]=function(f,g){if(typeof f!=="number")return i["outer"+b].call(this,f);return this.each(function(){c(this).css(h, +d(this,f,true,g)+"px")})}});c.extend(c.expr[":"],{data:function(a,b,d){return!!c.data(a,d[3])},focusable:function(a){var b=a.nodeName.toLowerCase(),d=c.attr(a,"tabindex");if("area"===b){b=a.parentNode;d=b.name;if(!a.href||!d||b.nodeName.toLowerCase()!=="map")return false;a=c("img[usemap=#"+d+"]")[0];return!!a&&k(a)}return(/input|select|textarea|button|object/.test(b)?!a.disabled:"a"==b?a.href||!isNaN(d):!isNaN(d))&&k(a)},tabbable:function(a){var b=c.attr(a,"tabindex");return(isNaN(b)||b>=0)&&c(a).is(":focusable")}}); +c(function(){var a=document.body,b=a.appendChild(b=document.createElement("div"));c.extend(b.style,{minHeight:"100px",height:"auto",padding:0,borderWidth:0});c.support.minHeight=b.offsetHeight===100;c.support.selectstart="onselectstart"in b;a.removeChild(b).style.display="none"});c.extend(c.ui,{plugin:{add:function(a,b,d){a=c.ui[a].prototype;for(var e in d){a.plugins[e]=a.plugins[e]||[];a.plugins[e].push([b,d[e]])}},call:function(a,b,d){if((b=a.plugins[b])&&a.element[0].parentNode)for(var e=0;e0)return true;a[b]=1;d=a[b]>0;a[b]=0;return d},isOverAxis:function(a,b,d){return a>b&&a')}function E(a,b){d.extend(a,b);for(var c in b)if(b[c]== +null||b[c]==G)a[c]=b[c];return a}d.extend(d.ui,{datepicker:{version:"1.8.9"}});var y=(new Date).getTime();d.extend(K.prototype,{markerClassName:"hasDatepicker",log:function(){this.debug&&console.log.apply("",arguments)},_widgetDatepicker:function(){return this.dpDiv},setDefaults:function(a){E(this._defaults,a||{});return this},_attachDatepicker:function(a,b){var c=null;for(var e in this._defaults){var f=a.getAttribute("date:"+e);if(f){c=c||{};try{c[e]=eval(f)}catch(h){c[e]=f}}}e=a.nodeName.toLowerCase(); +f=e=="div"||e=="span";if(!a.id){this.uuid+=1;a.id="dp"+this.uuid}var i=this._newInst(d(a),f);i.settings=d.extend({},b||{},c||{});if(e=="input")this._connectDatepicker(a,i);else f&&this._inlineDatepicker(a,i)},_newInst:function(a,b){return{id:a[0].id.replace(/([^A-Za-z0-9_-])/g,"\\\\$1"),input:a,selectedDay:0,selectedMonth:0,selectedYear:0,drawMonth:0,drawYear:0,inline:b,dpDiv:!b?this.dpDiv:d('
')}}, +_connectDatepicker:function(a,b){var c=d(a);b.append=d([]);b.trigger=d([]);if(!c.hasClass(this.markerClassName)){this._attachments(c,b);c.addClass(this.markerClassName).keydown(this._doKeyDown).keypress(this._doKeyPress).keyup(this._doKeyUp).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});this._autoSize(b);d.data(a,"datepicker",b)}},_attachments:function(a,b){var c=this._get(b,"appendText"),e=this._get(b,"isRTL");b.append&& +b.append.remove();if(c){b.append=d(''+c+"");a[e?"before":"after"](b.append)}a.unbind("focus",this._showDatepicker);b.trigger&&b.trigger.remove();c=this._get(b,"showOn");if(c=="focus"||c=="both")a.focus(this._showDatepicker);if(c=="button"||c=="both"){c=this._get(b,"buttonText");var f=this._get(b,"buttonImage");b.trigger=d(this._get(b,"buttonImageOnly")?d("").addClass(this._triggerClass).attr({src:f,alt:c,title:c}):d('').addClass(this._triggerClass).html(f== +""?c:d("").attr({src:f,alt:c,title:c})));a[e?"before":"after"](b.trigger);b.trigger.click(function(){d.datepicker._datepickerShowing&&d.datepicker._lastInput==a[0]?d.datepicker._hideDatepicker():d.datepicker._showDatepicker(a[0]);return false})}},_autoSize:function(a){if(this._get(a,"autoSize")&&!a.inline){var b=new Date(2009,11,20),c=this._get(a,"dateFormat");if(c.match(/[DM]/)){var e=function(f){for(var h=0,i=0,g=0;gh){h=f[g].length;i=g}return i};b.setMonth(e(this._get(a, +c.match(/MM/)?"monthNames":"monthNamesShort")));b.setDate(e(this._get(a,c.match(/DD/)?"dayNames":"dayNamesShort"))+20-b.getDay())}a.input.attr("size",this._formatDate(a,b).length)}},_inlineDatepicker:function(a,b){var c=d(a);if(!c.hasClass(this.markerClassName)){c.addClass(this.markerClassName).append(b.dpDiv).bind("setData.datepicker",function(e,f,h){b.settings[f]=h}).bind("getData.datepicker",function(e,f){return this._get(b,f)});d.data(a,"datepicker",b);this._setDate(b,this._getDefaultDate(b), +true);this._updateDatepicker(b);this._updateAlternate(b);b.dpDiv.show()}},_dialogDatepicker:function(a,b,c,e,f){a=this._dialogInst;if(!a){this.uuid+=1;this._dialogInput=d('');this._dialogInput.keydown(this._doKeyDown);d("body").append(this._dialogInput);a=this._dialogInst=this._newInst(this._dialogInput,false);a.settings={};d.data(this._dialogInput[0],"datepicker",a)}E(a.settings,e||{}); +b=b&&b.constructor==Date?this._formatDate(a,b):b;this._dialogInput.val(b);this._pos=f?f.length?f:[f.pageX,f.pageY]:null;if(!this._pos)this._pos=[document.documentElement.clientWidth/2-100+(document.documentElement.scrollLeft||document.body.scrollLeft),document.documentElement.clientHeight/2-150+(document.documentElement.scrollTop||document.body.scrollTop)];this._dialogInput.css("left",this._pos[0]+20+"px").css("top",this._pos[1]+"px");a.settings.onSelect=c;this._inDialog=true;this.dpDiv.addClass(this._dialogClass); +this._showDatepicker(this._dialogInput[0]);d.blockUI&&d.blockUI(this.dpDiv);d.data(this._dialogInput[0],"datepicker",a);return this},_destroyDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();d.removeData(a,"datepicker");if(e=="input"){c.append.remove();c.trigger.remove();b.removeClass(this.markerClassName).unbind("focus",this._showDatepicker).unbind("keydown",this._doKeyDown).unbind("keypress",this._doKeyPress).unbind("keyup", +this._doKeyUp)}else if(e=="div"||e=="span")b.removeClass(this.markerClassName).empty()}},_enableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=false;c.trigger.filter("button").each(function(){this.disabled=false}).end().filter("img").css({opacity:"1.0",cursor:""})}else if(e=="div"||e=="span")b.children("."+this._inlineClass).children().removeClass("ui-state-disabled");this._disabledInputs=d.map(this._disabledInputs, +function(f){return f==a?null:f})}},_disableDatepicker:function(a){var b=d(a),c=d.data(a,"datepicker");if(b.hasClass(this.markerClassName)){var e=a.nodeName.toLowerCase();if(e=="input"){a.disabled=true;c.trigger.filter("button").each(function(){this.disabled=true}).end().filter("img").css({opacity:"0.5",cursor:"default"})}else if(e=="div"||e=="span")b.children("."+this._inlineClass).children().addClass("ui-state-disabled");this._disabledInputs=d.map(this._disabledInputs,function(f){return f==a?null: +f});this._disabledInputs[this._disabledInputs.length]=a}},_isDisabledDatepicker:function(a){if(!a)return false;for(var b=0;b-1}},_doKeyUp:function(a){a=d.datepicker._getInst(a.target);if(a.input.val()!=a.lastVal)try{if(d.datepicker.parseDate(d.datepicker._get(a,"dateFormat"),a.input?a.input.val():null,d.datepicker._getFormatConfig(a))){d.datepicker._setDateFromField(a);d.datepicker._updateAlternate(a);d.datepicker._updateDatepicker(a)}}catch(b){d.datepicker.log(b)}return true}, +_showDatepicker:function(a){a=a.target||a;if(a.nodeName.toLowerCase()!="input")a=d("input",a.parentNode)[0];if(!(d.datepicker._isDisabledDatepicker(a)||d.datepicker._lastInput==a)){var b=d.datepicker._getInst(a);d.datepicker._curInst&&d.datepicker._curInst!=b&&d.datepicker._curInst.dpDiv.stop(true,true);var c=d.datepicker._get(b,"beforeShow");E(b.settings,c?c.apply(a,[a,b]):{});b.lastVal=null;d.datepicker._lastInput=a;d.datepicker._setDateFromField(b);if(d.datepicker._inDialog)a.value="";if(!d.datepicker._pos){d.datepicker._pos= +d.datepicker._findPos(a);d.datepicker._pos[1]+=a.offsetHeight}var e=false;d(a).parents().each(function(){e|=d(this).css("position")=="fixed";return!e});if(e&&d.browser.opera){d.datepicker._pos[0]-=document.documentElement.scrollLeft;d.datepicker._pos[1]-=document.documentElement.scrollTop}c={left:d.datepicker._pos[0],top:d.datepicker._pos[1]};d.datepicker._pos=null;b.dpDiv.empty();b.dpDiv.css({position:"absolute",display:"block",top:"-1000px"});d.datepicker._updateDatepicker(b);c=d.datepicker._checkOffset(b, +c,e);b.dpDiv.css({position:d.datepicker._inDialog&&d.blockUI?"static":e?"fixed":"absolute",display:"none",left:c.left+"px",top:c.top+"px"});if(!b.inline){c=d.datepicker._get(b,"showAnim");var f=d.datepicker._get(b,"duration"),h=function(){d.datepicker._datepickerShowing=true;var i=b.dpDiv.find("iframe.ui-datepicker-cover");if(i.length){var g=d.datepicker._getBorders(b.dpDiv);i.css({left:-g[0],top:-g[1],width:b.dpDiv.outerWidth(),height:b.dpDiv.outerHeight()})}};b.dpDiv.zIndex(d(a).zIndex()+1);d.effects&& +d.effects[c]?b.dpDiv.show(c,d.datepicker._get(b,"showOptions"),f,h):b.dpDiv[c||"show"](c?f:null,h);if(!c||!f)h();b.input.is(":visible")&&!b.input.is(":disabled")&&b.input.focus();d.datepicker._curInst=b}}},_updateDatepicker:function(a){var b=this,c=d.datepicker._getBorders(a.dpDiv);a.dpDiv.empty().append(this._generateHTML(a));var e=a.dpDiv.find("iframe.ui-datepicker-cover");e.length&&e.css({left:-c[0],top:-c[1],width:a.dpDiv.outerWidth(),height:a.dpDiv.outerHeight()});a.dpDiv.find("button, .ui-datepicker-prev, .ui-datepicker-next, .ui-datepicker-calendar td a").bind("mouseout", +function(){d(this).removeClass("ui-state-hover");this.className.indexOf("ui-datepicker-prev")!=-1&&d(this).removeClass("ui-datepicker-prev-hover");this.className.indexOf("ui-datepicker-next")!=-1&&d(this).removeClass("ui-datepicker-next-hover")}).bind("mouseover",function(){if(!b._isDisabledDatepicker(a.inline?a.dpDiv.parent()[0]:a.input[0])){d(this).parents(".ui-datepicker-calendar").find("a").removeClass("ui-state-hover");d(this).addClass("ui-state-hover");this.className.indexOf("ui-datepicker-prev")!= +-1&&d(this).addClass("ui-datepicker-prev-hover");this.className.indexOf("ui-datepicker-next")!=-1&&d(this).addClass("ui-datepicker-next-hover")}}).end().find("."+this._dayOverClass+" a").trigger("mouseover").end();c=this._getNumberOfMonths(a);e=c[1];e>1?a.dpDiv.addClass("ui-datepicker-multi-"+e).css("width",17*e+"em"):a.dpDiv.removeClass("ui-datepicker-multi-2 ui-datepicker-multi-3 ui-datepicker-multi-4").width("");a.dpDiv[(c[0]!=1||c[1]!=1?"add":"remove")+"Class"]("ui-datepicker-multi");a.dpDiv[(this._get(a, +"isRTL")?"add":"remove")+"Class"]("ui-datepicker-rtl");a==d.datepicker._curInst&&d.datepicker._datepickerShowing&&a.input&&a.input.is(":visible")&&!a.input.is(":disabled")&&a.input.focus();if(a.yearshtml){var f=a.yearshtml;setTimeout(function(){f===a.yearshtml&&a.dpDiv.find("select.ui-datepicker-year:first").replaceWith(a.yearshtml);f=a.yearshtml=null},0)}},_getBorders:function(a){var b=function(c){return{thin:1,medium:2,thick:3}[c]||c};return[parseFloat(b(a.css("border-left-width"))),parseFloat(b(a.css("border-top-width")))]}, +_checkOffset:function(a,b,c){var e=a.dpDiv.outerWidth(),f=a.dpDiv.outerHeight(),h=a.input?a.input.outerWidth():0,i=a.input?a.input.outerHeight():0,g=document.documentElement.clientWidth+d(document).scrollLeft(),j=document.documentElement.clientHeight+d(document).scrollTop();b.left-=this._get(a,"isRTL")?e-h:0;b.left-=c&&b.left==a.input.offset().left?d(document).scrollLeft():0;b.top-=c&&b.top==a.input.offset().top+i?d(document).scrollTop():0;b.left-=Math.min(b.left,b.left+e>g&&g>e?Math.abs(b.left+e- +g):0);b.top-=Math.min(b.top,b.top+f>j&&j>f?Math.abs(f+i):0);return b},_findPos:function(a){for(var b=this._get(this._getInst(a),"isRTL");a&&(a.type=="hidden"||a.nodeType!=1);)a=a[b?"previousSibling":"nextSibling"];a=d(a).offset();return[a.left,a.top]},_hideDatepicker:function(a){var b=this._curInst;if(!(!b||a&&b!=d.data(a,"datepicker")))if(this._datepickerShowing){a=this._get(b,"showAnim");var c=this._get(b,"duration"),e=function(){d.datepicker._tidyDialog(b);this._curInst=null};d.effects&&d.effects[a]? +b.dpDiv.hide(a,d.datepicker._get(b,"showOptions"),c,e):b.dpDiv[a=="slideDown"?"slideUp":a=="fadeIn"?"fadeOut":"hide"](a?c:null,e);a||e();if(a=this._get(b,"onClose"))a.apply(b.input?b.input[0]:null,[b.input?b.input.val():"",b]);this._datepickerShowing=false;this._lastInput=null;if(this._inDialog){this._dialogInput.css({position:"absolute",left:"0",top:"-100px"});if(d.blockUI){d.unblockUI();d("body").append(this.dpDiv)}}this._inDialog=false}},_tidyDialog:function(a){a.dpDiv.removeClass(this._dialogClass).unbind(".ui-datepicker-calendar")}, +_checkExternalClick:function(a){if(d.datepicker._curInst){a=d(a.target);a[0].id!=d.datepicker._mainDivId&&a.parents("#"+d.datepicker._mainDivId).length==0&&!a.hasClass(d.datepicker.markerClassName)&&!a.hasClass(d.datepicker._triggerClass)&&d.datepicker._datepickerShowing&&!(d.datepicker._inDialog&&d.blockUI)&&d.datepicker._hideDatepicker()}},_adjustDate:function(a,b,c){a=d(a);var e=this._getInst(a[0]);if(!this._isDisabledDatepicker(a[0])){this._adjustInstDate(e,b+(c=="M"?this._get(e,"showCurrentAtPos"): +0),c);this._updateDatepicker(e)}},_gotoToday:function(a){a=d(a);var b=this._getInst(a[0]);if(this._get(b,"gotoCurrent")&&b.currentDay){b.selectedDay=b.currentDay;b.drawMonth=b.selectedMonth=b.currentMonth;b.drawYear=b.selectedYear=b.currentYear}else{var c=new Date;b.selectedDay=c.getDate();b.drawMonth=b.selectedMonth=c.getMonth();b.drawYear=b.selectedYear=c.getFullYear()}this._notifyChange(b);this._adjustDate(a)},_selectMonthYear:function(a,b,c){a=d(a);var e=this._getInst(a[0]);e._selectingMonthYear= +false;e["selected"+(c=="M"?"Month":"Year")]=e["draw"+(c=="M"?"Month":"Year")]=parseInt(b.options[b.selectedIndex].value,10);this._notifyChange(e);this._adjustDate(a)},_clickMonthYear:function(a){var b=this._getInst(d(a)[0]);b.input&&b._selectingMonthYear&&setTimeout(function(){b.input.focus()},0);b._selectingMonthYear=!b._selectingMonthYear},_selectDay:function(a,b,c,e){var f=d(a);if(!(d(e).hasClass(this._unselectableClass)||this._isDisabledDatepicker(f[0]))){f=this._getInst(f[0]);f.selectedDay=f.currentDay= +d("a",e).html();f.selectedMonth=f.currentMonth=b;f.selectedYear=f.currentYear=c;this._selectDate(a,this._formatDate(f,f.currentDay,f.currentMonth,f.currentYear))}},_clearDate:function(a){a=d(a);this._getInst(a[0]);this._selectDate(a,"")},_selectDate:function(a,b){a=this._getInst(d(a)[0]);b=b!=null?b:this._formatDate(a);a.input&&a.input.val(b);this._updateAlternate(a);var c=this._get(a,"onSelect");if(c)c.apply(a.input?a.input[0]:null,[b,a]);else a.input&&a.input.trigger("change");if(a.inline)this._updateDatepicker(a); +else{this._hideDatepicker();this._lastInput=a.input[0];typeof a.input[0]!="object"&&a.input.focus();this._lastInput=null}},_updateAlternate:function(a){var b=this._get(a,"altField");if(b){var c=this._get(a,"altFormat")||this._get(a,"dateFormat"),e=this._getDate(a),f=this.formatDate(c,e,this._getFormatConfig(a));d(b).each(function(){d(this).val(f)})}},noWeekends:function(a){a=a.getDay();return[a>0&&a<6,""]},iso8601Week:function(a){a=new Date(a.getTime());a.setDate(a.getDate()+4-(a.getDay()||7));var b= +a.getTime();a.setMonth(0);a.setDate(1);return Math.floor(Math.round((b-a)/864E5)/7)+1},parseDate:function(a,b,c){if(a==null||b==null)throw"Invalid arguments";b=typeof b=="object"?b.toString():b+"";if(b=="")return null;var e=(c?c.shortYearCutoff:null)||this._defaults.shortYearCutoff;e=typeof e!="string"?e:(new Date).getFullYear()%100+parseInt(e,10);for(var f=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,h=(c?c.dayNames:null)||this._defaults.dayNames,i=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort, +g=(c?c.monthNames:null)||this._defaults.monthNames,j=c=-1,l=-1,u=-1,k=false,o=function(p){(p=z+1-1){j=1;l=u;do{e=this._getDaysInMonth(c,j-1);if(l<=e)break;j++;l-=e}while(1)}w=this._daylightSavingAdjust(new Date(c,j-1,l));if(w.getFullYear()!=c||w.getMonth()+1!=j||w.getDate()!=l)throw"Invalid date";return w},ATOM:"yy-mm-dd",COOKIE:"D, dd M yy",ISO_8601:"yy-mm-dd",RFC_822:"D, d M y",RFC_850:"DD, dd-M-y", +RFC_1036:"D, d M y",RFC_1123:"D, d M yy",RFC_2822:"D, d M yy",RSS:"D, d M y",TICKS:"!",TIMESTAMP:"@",W3C:"yy-mm-dd",_ticksTo1970:(718685+Math.floor(492.5)-Math.floor(19.7)+Math.floor(4.925))*24*60*60*1E7,formatDate:function(a,b,c){if(!b)return"";var e=(c?c.dayNamesShort:null)||this._defaults.dayNamesShort,f=(c?c.dayNames:null)||this._defaults.dayNames,h=(c?c.monthNamesShort:null)||this._defaults.monthNamesShort;c=(c?c.monthNames:null)||this._defaults.monthNames;var i=function(o){(o=k+112?a.getHours()+2:0);return a},_setDate:function(a,b,c){var e=!b,f=a.selectedMonth,h=a.selectedYear;b=this._restrictMinMax(a,this._determineDate(a,b,new Date));a.selectedDay= +a.currentDay=b.getDate();a.drawMonth=a.selectedMonth=a.currentMonth=b.getMonth();a.drawYear=a.selectedYear=a.currentYear=b.getFullYear();if((f!=a.selectedMonth||h!=a.selectedYear)&&!c)this._notifyChange(a);this._adjustInstDate(a);if(a.input)a.input.val(e?"":this._formatDate(a))},_getDate:function(a){return!a.currentYear||a.input&&a.input.val()==""?null:this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay))},_generateHTML:function(a){var b=new Date;b=this._daylightSavingAdjust(new Date(b.getFullYear(), +b.getMonth(),b.getDate()));var c=this._get(a,"isRTL"),e=this._get(a,"showButtonPanel"),f=this._get(a,"hideIfNoPrevNext"),h=this._get(a,"navigationAsDateFormat"),i=this._getNumberOfMonths(a),g=this._get(a,"showCurrentAtPos"),j=this._get(a,"stepMonths"),l=i[0]!=1||i[1]!=1,u=this._daylightSavingAdjust(!a.currentDay?new Date(9999,9,9):new Date(a.currentYear,a.currentMonth,a.currentDay)),k=this._getMinMaxDate(a,"min"),o=this._getMinMaxDate(a,"max");g=a.drawMonth-g;var m=a.drawYear;if(g<0){g+=12;m--}if(o){var n= +this._daylightSavingAdjust(new Date(o.getFullYear(),o.getMonth()-i[0]*i[1]+1,o.getDate()));for(n=k&&nn;){g--;if(g<0){g=11;m--}}}a.drawMonth=g;a.drawYear=m;n=this._get(a,"prevText");n=!h?n:this.formatDate(n,this._daylightSavingAdjust(new Date(m,g-j,1)),this._getFormatConfig(a));n=this._canAdjustMonth(a,-1,m,g)?''+n+"":f?"":''+n+"";var r=this._get(a,"nextText");r=!h?r:this.formatDate(r,this._daylightSavingAdjust(new Date(m,g+j,1)),this._getFormatConfig(a));f=this._canAdjustMonth(a,+1,m,g)?''+r+"":f?"":''+r+"";j=this._get(a,"currentText");r=this._get(a,"gotoCurrent")&&a.currentDay?u:b;j=!h?j:this.formatDate(j,r,this._getFormatConfig(a));h=!a.inline?'":"";e=e?'
'+(c?h:"")+(this._isInRange(a,r)?'":"")+(c?"":h)+"
":"";h=parseInt(this._get(a,"firstDay"),10);h=isNaN(h)?0:h;j=this._get(a,"showWeek");r=this._get(a,"dayNames");this._get(a,"dayNamesShort");var s=this._get(a,"dayNamesMin"),z= +this._get(a,"monthNames"),w=this._get(a,"monthNamesShort"),p=this._get(a,"beforeShowDay"),v=this._get(a,"showOtherMonths"),H=this._get(a,"selectOtherMonths");this._get(a,"calculateWeek");for(var L=this._getDefaultDate(a),I="",C=0;C1)switch(D){case 0:x+=" ui-datepicker-group-first";t=" ui-corner-"+(c?"right":"left");break;case i[1]- +1:x+=" ui-datepicker-group-last";t=" ui-corner-"+(c?"left":"right");break;default:x+=" ui-datepicker-group-middle";t="";break}x+='">'}x+='
'+(/all|left/.test(t)&&C==0?c?f:n:"")+(/all|right/.test(t)&&C==0?c?n:f:"")+this._generateMonthYearHeader(a,g,m,k,o,C>0||D>0,z,w)+'
';var A=j?'":"";for(t=0;t<7;t++){var q= +(t+h)%7;A+="=5?' class="ui-datepicker-week-end"':"")+'>'+s[q]+""}x+=A+"";A=this._getDaysInMonth(m,g);if(m==a.selectedYear&&g==a.selectedMonth)a.selectedDay=Math.min(a.selectedDay,A);t=(this._getFirstDayOfMonth(m,g)-h+7)%7;A=l?6:Math.ceil((t+A)/7);q=this._daylightSavingAdjust(new Date(m,g,1-t));for(var O=0;O";var P=!j?"":'";for(t=0;t<7;t++){var F= +p?p.apply(a.input?a.input[0]:null,[q]):[true,""],B=q.getMonth()!=g,J=B&&!H||!F[0]||k&&qo;P+='";q.setDate(q.getDate()+1);q=this._daylightSavingAdjust(q)}x+= +P+""}g++;if(g>11){g=0;m++}x+="
'+this._get(a,"weekHeader")+"
'+this._get(a,"calculateWeek")(q)+""+(B&&!v?" ":J?''+q.getDate()+"":''+q.getDate()+"")+"
"+(l?""+(i[0]>0&&D==i[1]-1?'
':""):"");M+=x}I+=M}I+=e+(d.browser.msie&&parseInt(d.browser.version,10)<7&&!a.inline?'':"");a._keyEvent=false;return I},_generateMonthYearHeader:function(a,b,c,e,f,h,i,g){var j=this._get(a,"changeMonth"),l=this._get(a,"changeYear"),u=this._get(a,"showMonthAfterYear"),k='
', +o="";if(h||!j)o+=''+i[b]+"";else{i=e&&e.getFullYear()==c;var m=f&&f.getFullYear()==c;o+='"}u||(k+=o+(h||!(j&& +l)?" ":""));a.yearshtml="";if(h||!l)k+=''+c+"";else{g=this._get(a,"yearRange").split(":");var r=(new Date).getFullYear();i=function(s){s=s.match(/c[+-].*/)?c+parseInt(s.substring(1),10):s.match(/[+-].*/)?r+parseInt(s,10):parseInt(s,10);return isNaN(s)?r:s};b=i(g[0]);g=Math.max(b,i(g[1]||""));b=e?Math.max(b,e.getFullYear()):b;g=f?Math.min(g,f.getFullYear()):g;for(a.yearshtml+='";if(d.browser.mozilla)k+='";else{k+=a.yearshtml;a.yearshtml=null}}k+=this._get(a,"yearSuffix");if(u)k+=(h||!(j&&l)?" ":"")+o;k+="
";return k},_adjustInstDate:function(a,b,c){var e= +a.drawYear+(c=="Y"?b:0),f=a.drawMonth+(c=="M"?b:0);b=Math.min(a.selectedDay,this._getDaysInMonth(e,f))+(c=="D"?b:0);e=this._restrictMinMax(a,this._daylightSavingAdjust(new Date(e,f,b)));a.selectedDay=e.getDate();a.drawMonth=a.selectedMonth=e.getMonth();a.drawYear=a.selectedYear=e.getFullYear();if(c=="M"||c=="Y")this._notifyChange(a)},_restrictMinMax:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");b=c&&ba?a:b},_notifyChange:function(a){var b=this._get(a, +"onChangeMonthYear");if(b)b.apply(a.input?a.input[0]:null,[a.selectedYear,a.selectedMonth+1,a])},_getNumberOfMonths:function(a){a=this._get(a,"numberOfMonths");return a==null?[1,1]:typeof a=="number"?[1,a]:a},_getMinMaxDate:function(a,b){return this._determineDate(a,this._get(a,b+"Date"),null)},_getDaysInMonth:function(a,b){return 32-(new Date(a,b,32)).getDate()},_getFirstDayOfMonth:function(a,b){return(new Date(a,b,1)).getDay()},_canAdjustMonth:function(a,b,c,e){var f=this._getNumberOfMonths(a); +c=this._daylightSavingAdjust(new Date(c,e+(b<0?b:f[0]*f[1]),1));b<0&&c.setDate(this._getDaysInMonth(c.getFullYear(),c.getMonth()));return this._isInRange(a,c)},_isInRange:function(a,b){var c=this._getMinMaxDate(a,"min");a=this._getMinMaxDate(a,"max");return(!c||b.getTime()>=c.getTime())&&(!a||b.getTime()<=a.getTime())},_getFormatConfig:function(a){var b=this._get(a,"shortYearCutoff");b=typeof b!="string"?b:(new Date).getFullYear()%100+parseInt(b,10);return{shortYearCutoff:b,dayNamesShort:this._get(a, +"dayNamesShort"),dayNames:this._get(a,"dayNames"),monthNamesShort:this._get(a,"monthNamesShort"),monthNames:this._get(a,"monthNames")}},_formatDate:function(a,b,c,e){if(!b){a.currentDay=a.selectedDay;a.currentMonth=a.selectedMonth;a.currentYear=a.selectedYear}b=b?typeof b=="object"?b:this._daylightSavingAdjust(new Date(e,c,b)):this._daylightSavingAdjust(new Date(a.currentYear,a.currentMonth,a.currentDay));return this.formatDate(this._get(a,"dateFormat"),b,this._getFormatConfig(a))}});d.fn.datepicker= +function(a){if(!d.datepicker.initialized){d(document).mousedown(d.datepicker._checkExternalClick).find("body").append(d.datepicker.dpDiv);d.datepicker.initialized=true}var b=Array.prototype.slice.call(arguments,1);if(typeof a=="string"&&(a=="isDisabled"||a=="getDate"||a=="widget"))return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b));if(a=="option"&&arguments.length==2&&typeof arguments[1]=="string")return d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this[0]].concat(b)); +return this.each(function(){typeof a=="string"?d.datepicker["_"+a+"Datepicker"].apply(d.datepicker,[this].concat(b)):d.datepicker._attachDatepicker(this,a)})};d.datepicker=new K;d.datepicker.initialized=false;d.datepicker.uuid=(new Date).getTime();d.datepicker.version="1.8.9";window["DP_jQuery_"+y]=d})(jQuery); +; \ No newline at end of file diff -r 69e26359f2c8 -r cf829ec742b3 src/main/webapp/session.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/session.html Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,26 @@ + + + + + Session + + +
+
+

+
+
+ +
+
+
+
+ +
+
+ + + + + + diff -r 69e26359f2c8 -r cf829ec742b3 src/main/webapp/templates-hidden/default.html --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/main/webapp/templates-hidden/default.html Sun Apr 03 15:55:02 2011 +0200 @@ -0,0 +1,33 @@ + + + + + + + + + + + + +
+
+

Radius Viewer

+
+
+
+ +
+
+
+ +
+
+
+
+

+
+
+ + +