site/cgi-bin/affiliate.pl
site/cgi-bin/api.pl
site/cgi-bin/bse.cfg
+site/cgi-bin/cfg/admin-ui.cfg
site/cgi-bin/fileprogress.fcgi
site/cgi-bin/fileprogress.pl
site/cgi-bin/fmail.fcgi
site/docs/userupdate.html
site/docs/userupdate.pod
site/htdocs/a/.htaccess
+site/htdocs/admin/admin.html
site/htdocs/admin/advanced.html
site/htdocs/admin/help/access.html
site/htdocs/admin/help/addgroup.html
site/htdocs/admin/help/subssend.html
site/htdocs/admin/index.html
site/htdocs/admin/sadmin.html
+site/htdocs/css/admin-ui/admin.css
+site/htdocs/css/admin-ui/extra.css
+site/htdocs/css/admin-ui/reset.css
site/htdocs/css/admin.css
site/htdocs/css/admin.css_natural
site/htdocs/css/admin_messages.css
+site/htdocs/css/bse_adminui.css
site/htdocs/css/sadmin.css
site/htdocs/css/style-main.css
site/htdocs/favicon.ico
site/htdocs/images/admin/move_down.gif
site/htdocs/images/admin/move_up.gif
site/htdocs/images/admin/nothumb.png
+site/htdocs/images/admin/ui/changepw.png
+site/htdocs/images/admin/ui/debug.png
+site/htdocs/images/admin/ui/delete_dk.png
+site/htdocs/images/admin/ui/menu.png
+site/htdocs/images/admin/ui/move_dk.png
+site/htdocs/images/admin/ui/spinner.gif
site/htdocs/images/admin/unchecked.gif
site/htdocs/images/filestatus/download.gif
site/htdocs/images/filestatus/forSale.gif
site/htdocs/images/titles/your_site.gif
site/htdocs/images/trans_pixel.gif
site/htdocs/images/videoclose.png
+site/htdocs/js/admin-ui/debug.js
+site/htdocs/js/admin-ui/menu.js
site/htdocs/js/admin_messages.js
site/htdocs/js/admin_prodopts.js
site/htdocs/js/admin_tools.js
site/htdocs/js/bse.js
+site/htdocs/js/bse_adminui.js
site/htdocs/js/bse_api.js
+site/htdocs/js/bse_dialog.js
site/htdocs/js/bse_flowplayer.js
+site/htdocs/js/bse_loader.js
+site/htdocs/js/bse_menu.js
+site/htdocs/js/bse_validate.js
site/htdocs/js/builder.js
site/htdocs/js/controls.js
site/htdocs/js/date.js
site/util/mysql.str
site/util/update_title_summary.pl
site/util/upgrade_mysql.pl
+t-js/01validate.html
+t-js/01validate.js
+t-js/10menu.html
+t-js/10menu.js
+t-js/menu.css
+t-js/test.js
+t-js/tests.css
t/BSE/Test.pm
t/cfg/bse.cfg
t/cfg/cfg/00start.cfg
--- /dev/null
+[extra a_config]
+admin_ui=admin ui scripts
+
+[admin ui scripts]
+menu=Main Menu;/js/admin-ui/menu.js;am
+debug=Debug Log;/js/admin-ui/debug.js;zm
use BSE::Util::HTML;
use base 'BSE::UI::AdminDispatch';
-our $VERSION = "1.000";
+our $VERSION = "1.001";
my %actions =
(
&& $newpw ne $confirm) {
$errors{confirm} = "Confirmation password does not match new password";
}
- keys %errors
- and return $class->req_form($req, undef, \%errors);
+ if (keys %errors) {
+ $req->is_ajax
+ and return $class->_field_error($req, \%errors);
+ return $class->req_form($req, undef, \%errors);
+ }
$user->changepw($newpw);
$user->save;
+ $req->is_ajax
+ and return $req->json_content(success => 1);
+
my $r = $cgi->param('r');
unless ($r) {
$r = $req->url('menu', { m => "New password saved" });
use strict;
use base "BSE::UI::Dispatch";
-our $VERSION = "1.000";
+our $VERSION = "1.001";
my %actions =
(
my ($self, $req) = @_;
my $cfg = $req->cfg;
- return $req->json_content
+ my %result =
(
success => 1,
perlbal => $cfg->entry("basic", "perlbal", 0),
access_control => $cfg->entry("basic", "access_control", 0),
tracking_uploads => $req->_tracking_uploads,
);
+
+ my %custom = $cfg->entries("extra a_config");
+ for my $key (keys %custom) {
+ exists $result{$key} and next;
+
+ my $section = $custom{$key};
+ $section =~ /\{(level|generator|parentid|template)\}/
+ and next;
+
+ $section eq "db" and die;
+
+ $result{$key} = { $cfg->entries($section) };
+ }
+
+ return $req->json_content(\%result);
}
sub req_fail {
--- /dev/null
+<!DOCTYPE html>
+<html lang="en">
+<head>
+ <meta charset="utf-8" />
+ <title>BSE Administration</title>
+ <script src="/js/prototype.js" type="text/javascript"></script>
+ <script src="/js/scriptaculous.js" type="text/javascript"></script>
+ <script src="/js/bse_api.js" type="text/javascript"></script>
+ <script src="/js/bse_validate.js" type="text/javascript"></script>
+ <script src="/js/bse_dialog.js" type="text/javascript"></script>
+ <script src="/js/bse_loader.js" type="text/javascript"></script>
+ <script src="/js/bse_menu.js" type="text/javascript"></script>
+ <script src="/js/bse_adminui.js" type="text/javascript"></script>
+<link rel="stylesheet" type="text/css" href="/css/admin-ui/reset.css" />
+<link rel="stylesheet" type="text/css" href="/css/admin-ui/admin.css" />
+<link rel="stylesheet" type="text/css" href="/css/admin-ui/extra.css" />
+</head>
+<body>
+ <!-- div id="lightbox" class="lightbox"></div -->
+ <nav id="menu" class="menu">
+ <ul id="nav" class="nav">
+ </ul>
+ <p id="message" class="message green"></p>
+ </nav>
+
+ <div id="base_work" class="preview"></div>
+</body>
+
+</html>
--- /dev/null
+/* @override
+ http://www.spacepark.com/admin/sp/css/admin.css
+ file:///Users/adriann/Work/Spacepark/spadmin/admin.css
+*/
+
+* {
+ -webkit-box-sizing: border-box;
+ -moz-box-sizing: border-box;
+ box-sizing: border-box;
+}
+html {
+ text-align: center;
+ width: 100%;
+}
+body {
+ width: 100%;
+ text-align: left;
+ margin: 0 auto;
+ padding: 0;
+ background-color: #666;
+ font: normal normal 0.75em/1.5 "Lucida Sans Unicode", "Lucida Grande", "Helvetica Neue", Helvetica, Arial, Helvetica, sans-serif;
+ text-shadow: 0 0.083em 0 #fff;
+ color: #585858;
+ position: relative;
+}
+select, input, textarea, button {
+ font-size: 1em;
+ margin: 0;
+}
+.hidden {
+ display: none !important;
+}
+.left {
+ float: left;
+}
+.right {
+ float: right;
+}
+.preview {
+ position: absolute;
+ top: 4em;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ z-index: 1;
+ position: fixed;
+}
+iframe {
+ width: 100%;
+ height: 100%;
+ border: 0; /* ie requires HTML iframe attribute frameborder="0" */
+ z-index: 1;
+ position: absolute;
+}
+.lightbox {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ margin: 0;
+ padding: 0;
+ background-color: rgba(0,0,0,0.75);
+ z-index: 3;
+ overflow: visible;
+ height: 100%;
+ width: 100%;
+}
+.half {
+ width: 50%;
+}
+.full {
+ width: 100%;
+}
+fieldset.error, label.error, .error>label, .error>fieldset, .error>legend {
+ color: #c03 !important;
+ border-color: #c03 !important;
+}
+
+/* @group window */
+
+div.window {
+ position: relative;
+ z-index: 100;
+ margin: 4em auto;
+ margin-top: 8em;
+ width: 95%;
+ max-width: 80em;
+ min-width: 34em;
+ padding: 1em 2em 1.5em;
+ background-color: #e8e8e8;
+ border-top: 0.2em solid #fff;
+
+ overflow: hidden; /* clear contained floats */
+
+ -webkit-border-radius: 1em;
+ -moz-border-radius: 1em;
+ -o-border-radius: 1em;
+ border-radius: 1em;
+
+ -moz-box-shadow: 0.25em 0.25em 0.5em rgba(0,0,0,0.75);
+ -webkit-box-shadow: 0.25em 0.25em 0.5em rgba(0,0,0,0.75);
+ -o-box-shadow: 0.25em 0.25em 0.5em rgba(0,0,0,0.75);
+ box-shadow: 0.25em 0.25em 0.5em rgba(0,0,0,0.75);
+}
+div.window.dialog {
+ max-width: 40em;
+}
+div.window fieldset fieldset {
+ /*display: table; opera encloses the legend if set as table */
+ border: 0.1em solid #ddd;
+ border-color: #b5b5b5 #ddd #fff #ddd;
+ margin-bottom: 1em;
+ padding: 1em;
+ padding-bottom: 0.75em;
+
+ -webkit-border-radius: 0.5em;
+ -moz-border-radius: 0.5em;
+ -o-border-radius: 0.5em;
+ border-radius: 0.5em;
+}
+div.window>form>fieldset>legend {
+ font-size: 2em !important;
+ color: #999;
+}
+div.window fieldset fieldset legend {
+ font-size: 1.25em;
+ color: #333;
+ margin: 0;
+ padding: 0 0.25em;
+}
+div.window fieldset>div {
+ display: table-row;
+}
+div.window fieldset>div>label, div.window fieldset>div>span {
+ display: table-cell;
+ vertical-align: top;
+ /*padding: 0.25em 1em 0.25em 0;*/
+ padding: 0.25em 0;
+}
+div.window fieldset>div>label {
+ padding-right: 0.5em;
+ text-align: right;
+ width: 10%;
+ min-width: 9em;
+ white-space: nowrap;
+}
+div.window fieldset>div>span {
+ width: 100%;
+}
+div.window fieldset>div>span>button {
+ display: none;
+ /*background-color: #ccc;
+ width: 1.75em;
+ height: 1.75em;
+ text-indent: -9999em;
+ overflow: hidden;*/
+}
+div.window fieldset>div:hover>span>button {
+ display: inline;
+}
+div.window fieldset input[type=text], div.window fieldset input[type=password],
+div.window fieldset input[type=file], div.window fieldset select, div.window fieldset textarea {
+ border: 0.1em solid #ddd;
+ border-top-color: #aaa;
+ line-height: 1.2;
+ padding: 0.25em;
+ background-color: #eee;
+ float: left; /* because some odd margins persist
+ display: inline-block;*/
+ min-width: 20em;
+ width: 40%;
+}
+div.window fieldset input[type=file] {
+ padding: 0.1em 0.25em;
+ overflow: hidden;
+ position: relative;
+}
+div.window fieldset div.full input[type=text], div.window fieldset div.full input[type=password],
+div.window fieldset div.full input[type=file], div.window fieldset div.full select, div.window fieldset div.full textarea,
+div.dialog fieldset input[type=text], div.dialog fieldset input[type=password],
+div.dialog fieldset input[type=file], div.dialog fieldset select, div.dialog fieldset textarea {
+ width: 100%;
+}
+div.window fieldset textarea {
+ height: 20em;
+}
+div.window.dialog fieldset textarea {
+ height: 10em;
+}
+div.window fieldset input:focus, div.window fieldset textarea:focus, div.window fieldset select:focus {
+ border-color: rgba(3,153,212,0.75);
+ -moz-box-shadow: 0 0 0.25em rgba(3,153,212,0.75);
+ -webkit-box-shadow: 0 0 0.25em rgba(3,153,212,0.75);
+ -o-box-shadow: 0 0 0.25em rgba(3,153,212,0.75);
+ box-shadow: 0 0 0.25em rgba(3,153,212,0.75);
+}
+div.window fieldset>label {
+ text-align: left;
+ display: block;
+}
+
+/* @end */
+
+/* @group menu */
+
+#menu p.message {
+ position: absolute;
+ top: 4em;
+ width: 100%;
+ padding: 1em 2em;
+ color: #fff;
+ text-shadow: 0 0.1em 0.1em rgba(0,0,0,0.3);
+ font-weight: bold;
+
+ -webkit-box-shadow: 0 0.1em 0.5em rgba(0,0,0,0.4);
+ -moz-box-shadow: 0 0.1em 0.5em rgba(0,0,0,0.4);
+ box-shadow: 0 0.1em 0.5em rgba(0,0,0,0.4);
+}
+#menu {
+ margin: 0;
+ padding: 0;
+ line-height: 1;
+ width: 100%;
+ height: 4em;
+
+ -webkit-box-shadow: 0 0.1em 0.5em rgba(0,0,0,0.4);
+ -moz-box-shadow: 0 0.1em 0.5em rgba(0,0,0,0.4);
+ box-shadow: 0 0.1em 0.5em rgba(0,0,0,0.4);
+
+ background: #8b8b8b; /* for non-css3 browsers */
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#a9a9a9', endColorstr='#7a7a7a'); /* for IE */
+ background: -webkit-gradient(linear, left top, left bottom, from(#a9a9a9), to(#7a7a7a)); /* for webkit browsers */
+ background: -moz-linear-gradient(top, #a9a9a9, #7a7a7a); /* for firefox 3.6+ */
+
+ border-bottom: solid 0.1em #6d6d6d;
+
+ z-index: 2;
+ position: fixed;
+ top: 0;
+ overflow: visible;
+}
+#nav {
+ position: relative;
+ z-index: 1001;
+ overflow: visible;
+}
+#nav li {
+ margin: 0 0 0 1em;
+ padding: 0.75em 0;
+ float: left;
+ position: relative;
+ list-style: none;
+}
+#nav li li.separate {
+ border-bottom: 0.1em solid #b4b4b4;
+}
+#nav li li.separate+li {
+ border-top: 0.1em solid #fff;
+}
+/* main level link */
+#nav a, #nav li > span {
+ position: relative;
+ line-height: 1.5;
+ font-weight: bold;
+ color: #e7e5e5;
+ text-decoration: none;
+ display: block;
+ margin: -0.1em 0;
+ padding: 0.5em 1.5em;
+ -webkit-border-radius: 1.5em;
+ -moz-border-radius: 1.5em;
+ border-radius: 1.5em;
+ text-shadow: 0 0.1em 0.1em rgba(0,0,0,0.5);
+}
+/* main level link hover */
+#nav .current > a, #nav li:hover > a,
+#nav .current > span, #nav li:hover > span {
+ background: #d1d1d1; /* for non-css3 browsers */
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ebebeb', endColorstr='#a1a1a1'); /* for IE */
+ background: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#a1a1a1)); /* for webkit browsers */
+ background: -moz-linear-gradient(top, #ebebeb, #a1a1a1); /* for firefox 3.6+ */
+
+ color: #444;
+ border-top: solid 0.1em #f8f8f8;
+ -webkit-box-shadow: 0 0.1em 0.1em rgba(0,0,0,0.2);
+ -moz-box-shadow: 0 0.1em 0.1em rgba(0,0,0,0.2);
+ box-shadow: 0 0.1em 0.1em rgba(0,0,0,0.2);
+ text-shadow: 0 0.1em 0.1em rgba(255,255,255,1);
+}
+/* sub levels link hover */
+#nav ul li:hover a, #nav li:hover li a,
+#nav ul li:hover>span, #nav li:hover li>span {
+ background: none;
+ border: none;
+ color: #666;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+ margin: 0;
+}
+#nav ul a:hover, #nav ul li>span:hover {
+ background: #0399d4 !important; /* for non-css3 browsers */
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#04acec', endColorstr='#0186ba'); /* for IE */
+ background: -webkit-gradient(linear, left top, left bottom, from(#04acec), to(#0186ba)) !important; /* for webkit browsers */
+ background: -moz-linear-gradient(top, #04acec, #0186ba) !important; /* for firefox 3.6+ */
+
+ color: #fff !important;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+ text-shadow: 0 0.1em 0.1em rgba(0,0,0,0.1);
+}
+/* level 2 list */
+#nav ul {
+ background: #ddd; /* for non-css3 browsers */
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#cfcfcf'); /* for IE */
+ background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#cfcfcf)); /* for webkit browsers */
+ background: -moz-linear-gradient(top, #fff, #cfcfcf); /* for firefox 3.6+ */
+
+ z-index: 100;
+ display: none;
+ margin: 0;
+ padding: 0;
+ width: 18em;
+ position: absolute;
+ top: 3.75em;
+ left: 0;
+ border: solid 0.1em #b4b4b4;
+ -webkit-border-radius: 0.5em;
+ -moz-border-radius: 0.5em;
+ -o-border-radius: 0.5em;
+ border-radius: 0.5em;
+ -webkit-box-shadow: 0 0.16em 0.5em rgba(0,0,0,0.3);
+ -moz-box-shadow: 0 0.16em 0.5em rgba(0,0,0,0.3);
+ box-shadow: 0 0.16em 0.5em rgba(0,0,0,0.3);
+}
+#nav ul ul {
+ max-height: 30.16em;
+ overflow-y: auto;
+ overflow-x: visible;
+}
+#nav ul.full {
+ width: 27em;
+}
+
+/* @group widgets */
+
+#nav li>a>span, #nav li>span>span {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 1.2em;
+ height: 100%;
+ text-indent: -9999em;
+ background: transparent url(../images/move_dk.png) no-repeat 50% 50%;
+ background-size: 1.2em;
+ display: none;
+}
+#nav li>a>span.delete, #nav li>span>span.delete {
+ background-image: url(../images/delete_dk.png);
+ background-size: 1.5em;
+ width: 1.5em;
+ left: auto;
+ right: 0.5em;
+}
+#nav li:hover>a>span, #nav li:hover>span>span {
+ display: block;
+}
+
+/* @end */
+
+/* @group dropdown */
+
+#nav li:hover > ul {
+ display: block;
+}
+#nav ul li {
+ float: none;
+ margin: 0;
+ padding: 0;
+}
+#nav ul a, #nav ul li>span {
+ font-weight: normal;
+ text-shadow: 0 0.1em 0.1em rgba(255,255,255,0.9);
+}
+
+/* @end */
+
+/* @group level 3+ list */
+
+#nav ul ul {
+ left: 17.5em;
+ top: -0.1em;
+}
+/*#nav ul ul.full {
+ left: 35.5em;
+}*/
+
+/* @end */
+
+/* @group rounded corners for first and last child */
+
+#nav ul li:first-child > a {
+ -webkit-border-top-left-radius: 0.5em;
+ -moz-border-radius-topleft: 0.5em;
+ border-top-left-radius: 0.5em;
+ -webkit-border-top-right-radius: 0.5em;
+ -moz-border-radius-topright: 0.5em;
+ border-top-right-radius: 0.5em;
+}
+#nav ul li:last-child > a {
+ -webkit-border-bottom-left-radius: 0.5em;
+ -moz-border-radius-bottomleft: 0.5em;
+ border-bottom-left-radius: 0.5em;
+ -webkit-border-bottom-right-radius: 0.5em;
+ -moz-border-radius-bottomright: 0.5em;
+ border-bottom-right-radius: 0.5em;
+}
+
+/* @end */
+
+/* @end */
+
+/* @group button */
+
+
+/* button
+---------------------------------------------- */
+button[disabled], button[disabled]:hover, button[disabled]:active {
+ opacity: 0.25;
+ top: 0;
+ color: #e9e9e9;
+ border: solid 0.1em #555;
+ background: #6e6e6e;
+ background: -webkit-gradient(linear, left top, left bottom, from(#888), to(#575757));
+ background: -moz-linear-gradient(top, #888, #575757);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#888888', endColorstr='#575757');
+}
+.button {
+ display: inline-block;
+ zoom: 1; /* zoom and *display = ie7 hack for display:inline-block */
+ *display: inline;
+ vertical-align: top;
+ margin: 0 0 0 0.5em;
+ padding: 0.5em 1.5em;
+
+ cursor: pointer;
+ text-align: center;
+ text-decoration: none;
+ text-shadow: 0 0.1em 0.1em rgba(0,0,0,0.3);
+ -webkit-border-radius: 0.5em;
+ -moz-border-radius: 0.5em;
+ border-radius: 0.5em;
+ -webkit-box-shadow: 0 0.1em 0.2em rgba(0,0,0,0.5);
+ -moz-box-shadow: 0 0.1em 0.2em rgba(0,0,0,0.5);
+ box-shadow: 0 0.1em 0.2em rgba(0,0,0,0.5);
+ border-width: 0.1em !important;
+
+ line-height: 1.25; /* Cannot modify because FF doesn't allow override on button element */
+ color: #fff !important;
+}
+.button:hover {
+ text-decoration: none;
+}
+.button:active {
+ position: relative;
+ top: 0.1em;
+}
+.bigrounded {
+ -webkit-border-radius: 2em;
+ -moz-border-radius: 2em;
+ border-radius: 2em;
+}
+.small {
+ font-size: 0.85em;
+ padding: 0.3em 1em;
+}
+
+/* color styles
+---------------------------------------------- */
+
+/* black */
+.black {
+ color: #d7d7d7;
+ border: solid 0.1em #333;
+ background: #333;
+ background: -webkit-gradient(linear, left top, left bottom, from(#666), to(#000));
+ background: -moz-linear-gradient(top, #666, #000);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#666666', endColorstr='#000000');
+}
+.black:hover {
+ background: #000;
+ background: -webkit-gradient(linear, left top, left bottom, from(#444), to(#000));
+ background: -moz-linear-gradient(top, #444, #000);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#444444', endColorstr='#000000');
+}
+.black:active {
+ color: #666;
+ background: -webkit-gradient(linear, left top, left bottom, from(#000), to(#444));
+ background: -moz-linear-gradient(top, #000, #444);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#000000', endColorstr='#666666');
+}
+
+/* gray */
+.gray {
+ color: #e9e9e9;
+ border: solid 0.1em #555;
+ background: #6e6e6e;
+ background: -webkit-gradient(linear, left top, left bottom, from(#888), to(#575757));
+ background: -moz-linear-gradient(top, #888, #575757);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#888888', endColorstr='#575757');
+}
+.gray:hover {
+ background: #616161;
+ background: -webkit-gradient(linear, left top, left bottom, from(#757575), to(#4b4b4b));
+ background: -moz-linear-gradient(top, #757575, #4b4b4b);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#757575', endColorstr='#4b4b4b');
+}
+.gray:active {
+ color: #afafaf;
+ background: -webkit-gradient(linear, left top, left bottom, from(#575757), to(#888));
+ background: -moz-linear-gradient(top, #575757, #888);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#575757', endColorstr='#888888');
+}
+
+/* orange */
+.orange {
+ color: #fef4e9;
+ border: solid 0.1em #da7c0c;
+ background: #f78d1d;
+ background: -webkit-gradient(linear, left top, left bottom, from(#faa51a), to(#f47a20));
+ background: -moz-linear-gradient(top, #faa51a, #f47a20);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#faa51a', endColorstr='#f47a20');
+}
+.orange:hover {
+ background: #f47c20;
+ background: -webkit-gradient(linear, left top, left bottom, from(#f88e11), to(#f06015));
+ background: -moz-linear-gradient(top, #f88e11, #f06015);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f88e11', endColorstr='#f06015');
+}
+.orange:active {
+ color: #fcd3a5;
+ background: -webkit-gradient(linear, left top, left bottom, from(#f47a20), to(#faa51a));
+ background: -moz-linear-gradient(top, #f47a20, #faa51a);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f47a20', endColorstr='#faa51a');
+}
+
+/* red */
+.red {
+ color: #faddde;
+ border: solid 0.1em #980c10;
+ background: #d81b21;
+ background: -webkit-gradient(linear, left top, left bottom, from(#ed1c24), to(#aa1317));
+ background: -moz-linear-gradient(top, #ed1c24, #aa1317);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ed1c24', endColorstr='#aa1317');
+}
+.red:hover {
+ background: #b61318;
+ background: -webkit-gradient(linear, left top, left bottom, from(#c9151b), to(#a11115));
+ background: -moz-linear-gradient(top, #c9151b, #a11115);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#c9151b', endColorstr='#a11115');
+}
+.red:active {
+ color: #de898c;
+ background: -webkit-gradient(linear, left top, left bottom, from(#aa1317), to(#ed1c24));
+ background: -moz-linear-gradient(top, #aa1317, #ed1c24);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#aa1317', endColorstr='#ed1c24');
+}
+
+/* blue */
+.blue {
+ color: #d9eef7;
+ border: solid 0.1em #0076a3;
+ background: #0095cd;
+ background: -webkit-gradient(linear, left top, left bottom, from(#00adee), to(#0078a5));
+ background: -moz-linear-gradient(top, #00adee, #0078a5);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#00adee', endColorstr='#0078a5');
+}
+.blue:hover {
+ background: #007ead;
+ background: -webkit-gradient(linear, left top, left bottom, from(#0095cc), to(#00678e));
+ background: -moz-linear-gradient(top, #0095cc, #00678e);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0095cc', endColorstr='#00678e');
+}
+.blue:active {
+ color: #80bed6;
+ background: -webkit-gradient(linear, left top, left bottom, from(#0078a5), to(#00adee));
+ background: -moz-linear-gradient(top, #0078a5, #00adee);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0078a5', endColorstr='#00adee');
+}
+
+/* rosy */
+.rosy {
+ color: #fae7e9;
+ border: solid 0.1em #b73948;
+ background: #da5867;
+ background: -webkit-gradient(linear, left top, left bottom, from(#f16c7c), to(#bf404f));
+ background: -moz-linear-gradient(top, #f16c7c, #bf404f);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f16c7c', endColorstr='#bf404f');
+}
+.rosy:hover {
+ background: #ba4b58;
+ background: -webkit-gradient(linear, left top, left bottom, from(#cf5d6a), to(#a53845));
+ background: -moz-linear-gradient(top, #cf5d6a, #a53845);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#cf5d6a', endColorstr='#a53845');
+}
+.rosy:active {
+ color: #dca4ab;
+ background: -webkit-gradient(linear, left top, left bottom, from(#bf404f), to(#f16c7c));
+ background: -moz-linear-gradient(top, #bf404f, #f16c7c);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#bf404f', endColorstr='#f16c7c');
+}
+
+/* green */
+.green {
+ color: #e8f0de;
+ border: solid 0.1em #538312;
+ background: #64991e;
+ background: -webkit-gradient(linear, left top, left bottom, from(#7db72f), to(#4e7d0e));
+ background: -moz-linear-gradient(top, #7db72f, #4e7d0e);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#7db72f', endColorstr='#4e7d0e');
+}
+.green:hover {
+ background: #538018;
+ background: -webkit-gradient(linear, left top, left bottom, from(#6b9d28), to(#436b0c));
+ background: -moz-linear-gradient(top, #6b9d28, #436b0c);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#6b9d28', endColorstr='#436b0c');
+}
+.green:active {
+ color: #a9c08c;
+ background: -webkit-gradient(linear, left top, left bottom, from(#4e7d0e), to(#7db72f));
+ background: -moz-linear-gradient(top, #4e7d0e, #7db72f);
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#4e7d0e', endColorstr='#7db72f');
+}
+
+/* @end */
+
+/* @group buttons */
+
+.buttons {
+ display: table;
+ width: 100%;
+ text-align: right;
+ clear: both;
+ margin-top: 1em;
+}
+.buttons>span {
+ display: table-cell;
+ white-space: nowrap;
+ vertical-align: top;
+}
+.buttons>span span {
+ text-align: left;
+ text-indent: -9999em;
+ height: 2.5em;
+ border-width: 0.1em !important;
+}
+.buttons span.progress {
+ background-color: #bbb;
+ width: 100%;
+}
+.buttons>span.progress span {
+ display: block;
+}
+.buttons>span span.status {
+ text-indent: 0.5em;
+ line-height: 2.5;
+ margin-bottom: -2.5em;
+ color: #fff;
+ text-shadow: 0 0.1em 0.1em rgba(0,0,0,0.3);
+}
+.buttons>span span.spinner {
+ display: inline-block;
+ width: 2.5em;
+ background: transparent url(../images/spinner.gif) no-repeat 100% 50%;
+ background-size: 2em;
+}
+
+/* @end */
--- /dev/null
+/* functions not covered by Adrian's base classes.
+ I don't want to modify the original, since he'll make refinements.
+*/
+.bse_modal {
+ position: fixed;
+ top: 0;
+ right: 0;
+ bottom: 0;
+ left: 0;
+ margin: 0;
+ padding: 0;
+ background-color: rgba(0,0,0,0.75);
+ z-index: 3;
+ overflow: visible;
+ height: 100%;
+ width: 100%;
+}
+
+#base_work {
+ color: #FFF;
+}
+
+.buttons>span span.spinner {
+ background: transparent url(/images/admin/ui/spinner.gif) no-repeat 100% 50%;
+}
+
+#nav li>span>span.item {
+ display: block;
+ text-indent: 0em;
+ position: static;
+ width: auto;
+}
+
+#nav li>span>span.move {
+ background: transparent url(/images/admin/ui/move_dk.png) no-repeat 50% 50%;
+
+}
+
+#nav li>span>span.delete {
+ background-image: url(/images/admin/ui/delete_dk.png);
+}
+
+/* undo the CSS popup styling */
+#nav ul, #nav li>a>span, #nav li>span>span {
+ display: block;
+}
+
+.bse_field_error {
+ margin-top: 2em;
+ margin-left: -25em;
+ color: #F00;
+}
+
+.bse_image_field img.display {
+ width: 80px;
+ height: 80px;
+ border: 1px solid #CCC;
+ float: right;
+}
+
+div.window fieldset.bse_image_field > input[type=file] {
+ width: auto;
+}
+
+div.window fieldset.bse_image_field > div.more {
+ clear: both;
+ display: block;
+}
+
+div.window fieldset.bse_image_gallery div.bse_gallery_imagelist {
+ border: 1px solid #808080;
+ display: block;
+ height: 102px;
+}
+
+div.window fieldset.bse_image_gallery div.bse_gallery_imagelist .bse_gallery_image,
+div.window fieldset.bse_image_gallery div.bse_gallery_imagelist .bse_drop_target {
+ width: 81px;
+ height: 100px;
+ float: left;
+ border-right: 1px solid #808080;
+ position: relative;
+}
+
+.bse_gallery_image {
+ background-color: #FFF;
+}
+
+.bse_drop_target {
+ background-color: #CFC;
+ font-weight: bold;
+ padding-top: 40px;
+ text-align: center;
+}
+
+div.window fieldset.bse_image_gallery div.bse_gallery_imagelist > .bse_gallery_image .name {
+ width: 80px;
+ position: absolute;
+ top: 80px;
+ display: block;
+ text-align: center;
+ overflow: hidden;
+ text-size: 70%;
+}
+
+div.window fieldset.bse_image_gallery div.bse_gallery_imagelist > .bse_gallery_image .delete {
+ top: 0px;
+ right: 0px;
+ position: absolute;
+ background-color: #CCC;
+}
+
+div.window fieldset.bse_image_gallery div.bse_gallery_imagelist > .bse_gallery_image .edit {
+ top: 1.5em;
+ right: 0px;
+ position: absolute;
+ background-color: #CCC;
+}
+
--- /dev/null
+html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:'';content:none}:focus{outline:0}ins{text-decoration:none}del{text-decoration:line-through}table{border-collapse:collapse;border-spacing:0}
\ No newline at end of file
--- /dev/null
+#debug_log {
+}
+
+body {
+ font: 12px Arial;
+ margin: 0px;
+ background-color: #E0E0E0;
+}
+
+.bse_modal {
+ position: fixed;
+ top: 0px;
+ background-image: url(/images/50.png);
+ width: 100%;
+ height: 20em;
+}
+
+.bse_dialog .bse_title {
+ text-align: center;
+ font-weight: bold;
+ border-bottom: 1px solid #8080FF;
+}
+
+.bse_dialog .bse_alert {
+ background-color: #F00;
+ color: #FFF;
+}
+
+.bse_dialog {
+ position: absolute;
+ background-color: #F0F0FF;
+ padding: 5px;
+ border: 2px solid #FFF;
+}
+
+.bse_dialog .bse_required label:after {
+ content: "*";
+}
+
+.bse_dialog .bse_invalid {
+ background-color: #FF8080;
+}
+
+.bse_error {
+ padding: 0px 3px;
+ font-weight: bold;
+ color: red;
+}
+
+.bse_dialog input,
+.bse_dialog textarea,
+.bse_dialog select {
+
+}
+
+.bse_field_wrapper{
+ margin-left: 200px;
+ margin-top: 10px;
+}
+
+.bse_dialog label {
+ margin-left: -190px;
+ float: left;
+}
+
+#base_wrapper {
+ width: 1010px;
+ border-left: 5px solid white;
+ border-right: 5px solid white;
+ margin-left: auto;
+ margin-right: auto;
+ background-color: #F0F0FF;
+}
+
+#base_menubar {
+ background-color: #E0E0FF;
+ border-bottom: 1px solid #8080FF;
+ /*position: fixed;
+ width: 1000px; */
+}
+
+#base_menu_wrapper {
+ position: relative;
+ background-color: #C0C0FF;
+ width: 150px;
+ float: left;
+}
+
+#base_menu {
+ display: none;
+ background-color: #C0C0FF;
+ position: absolute;
+ top: 2em;
+ left: 1px;
+ /*border-top: 1px solid #000;*/
+}
+
+#base_menu_wrapper:hover #base_menu {
+ display: block;
+}
+
+#base_menu_current {
+ padding: 0.5em 1em;
+ white-space: nowrap;
+ overflow: hidden;
+ width: 150px;
+ font-weight: bold;
+ cursor: pointer;
+}
+
+#base_menu a,
+#base_logon_menu a {
+ display: block;
+ padding: 5px 1em;
+ /*border-bottom: 1px solid #000;
+ border-left: 1 px solid #000;
+ border-right: 1px solid #000;*/
+ text-decoration: none;
+ font-weight: bold;
+ white-space: nowrap;
+ width: 150px;
+ background-position: 150px;
+ background-repeat: no-repeat;
+}
+
+#base_menu a:hover,
+#base_logon_menu a:hover {
+ background-color: #E0E0FF;
+}
+
+#base_menu_item_debug {
+ background-image: url(/images/admin/ui/debug.png);
+}
+
+#base_menu_item_menu {
+ background-image: url(/images/admin/ui/menu.png);
+}
+
+#base_change_password {
+ background-image: url(/images/admin/ui/changepw.png);
+}
+
+#base_logon_wrapper {
+ position: relative;
+ background-color: #C0C0FF;
+ float: right;
+}
+
+#base_logon {
+ display: none;
+ font-weight: bold;
+ background-color: #C00000;
+ color: #FFFFFF;
+ float: right;
+ padding: 0.5em 1em;
+ cursor: pointer;
+}
+
+#base_logon_menu {
+ display: none;
+ background-color: #C0C0FF;
+ position: absolute;
+ right: 0px;
+ top: 100%;
+}
+
+#base_logon_wrapper:hover #base_logon_menu {
+ display: block;
+}
+
+.clearer { clear: both; }
+
+/* hide the various page components until initialized */
+#base_wrapper.hide #base_menubar,
+#base_wrapper.hide #base_work {
+ display: none;
+}
+
+#base_loading {
+ text-align: center;
+ font: bold 24px Arial;
+ padding-top: 10em;
+ padding-bottom: 10em;
+ color: #8080FF;
+}
+
+#base_messages div.message {
+ background-color: #C0C0C0;
+ padding: 2px 5px;
+}
+
--- /dev/null
+var BSEDebugUI = Class.create
+(BSEUIBase,
+{
+ start: function(ui, div, args) {
+ div.innerHTML = "";
+ this._log = new Element("div", { id: "debug_log" });
+ div.appendChild(this._log);
+ this._load_log(ui);
+ this.display(ui, div);
+ },
+ display: function(ui, div) {
+ this._timer = setInterval(this._load_log.bind(this, ui), 1000);
+ },
+ undisplay: function(ui, div) {
+ clearInterval(this._timer);
+ },
+ needed_content: function(ui, args) {
+ return { };
+ },
+ _load_log: function(ui) {
+ this._log.innerHTML = "";
+ for (var i = 0; i < ui._log.length; ++i) {
+ var entry = new Element("div");
+ entry.appendChild(document.createTextNode(ui._log[i]));
+ this._log.appendChild(entry);
+ }
+ }
+});
+
+ui.register({
+ name: "debug",
+ object: new BSEDebugUI(),
+ logon: false
+});
\ No newline at end of file
--- /dev/null
+var BSEMenuUI = Class.create
+(BSEUIBase,
+{
+ start: function(ui, div, args) {
+ div.innerHTML = "There will be a menu here";
+ },
+ display: function(ui, div) {
+ },
+ needed_content: function(ui, args) {
+ return { menu: "/admin/ui/menu.html" };
+ }
+});
+
+ui.register({
+ name: "menu",
+ object: new BSEMenuUI
+});
\ No newline at end of file
--- /dev/null
+
+var base_logon_fields =
+ [
+ {
+ name: "logon",
+ label: "Logon",
+ required: true
+ },
+ {
+ name: "password",
+ label: "Password",
+ required: true,
+ type: "password"
+ }
+ ];
+
+var BSEAdminUI = Class.create({
+ initialize: function() {
+ this._log = [];
+ this.api = new BSEAPI({onConfig: this._post_start.bind(this)});
+ this._messages = new BSEAdminUI.Messages("message");
+ this._modules = new Hash();
+ this._loaded_scripts = new Hash();
+ this._menubar = new BSEMenuBar({});
+ },
+ register: function (options) {
+ var mod = this._modules.get(options.name);
+ if (mod) {
+ mod.object = options.object;
+ mod.options = Object.extend(
+ Object.extend({}, this.module_defaults()),
+ options);
+ }
+ else {
+ this._log_entry("Attempt to register unknown module " + options.name);
+ }
+ },
+ module_defaults: function() {
+ return {
+ logon: true
+ };
+ },
+ add_menu: function(name, menu) {
+ var mod = this._modules.get(name);
+ if (mod) {
+ this._menubar.add_menu(menu);
+ mod.menus.push(menu);
+ if (this.current === mod) {
+ $("nav").appendChild(menu.element());
+ menu.inDocument();
+ }
+ }
+ else {
+ this._log_entry("Attempt to register menu with unknown module " + name);
+ }
+ },
+ // menu_item: function(options) {
+ // options = Object.extend(Object.extend({}, BSEAdminUI.MenuDefaults), options);
+ // this.handlers.set(
+ // options.name,
+ // {
+ // options: options,
+ // key: options.name,
+ // value: options.object,
+ // started: false,
+ // div: null,
+ // suboptions: new Hash
+ // });
+ // },
+ // submenu_item: function(options) {
+ // options = Object.extend(Object.extend({}, BSEAdminUI.MenuDefaults), options);
+ // this.handlers.get(options.parent).
+ // set(options.name,
+ // {
+ // options: options,
+ // key: options.name,
+ // value: options.object,
+ // started: false,
+ // div: null
+ // });
+ // },
+ //start: function() {},
+ //_bind_static_events: function() {
+ // $("base_logon").observe("click", this._do_logoff.bindAsEventListener(this));
+ // $("base_change_password").observe("click", this._do_changepw.bindAsEventListener(this));
+ //},
+ _post_start: function() {
+ if (this.api.conf.access_control != 0)
+ this._make_logon_menu();
+ // each line is
+ // text;script;sortorder
+ var ui_conf = this.api.conf.admin_ui;
+ var menu_items = [];
+ for (var i in ui_conf) {
+ var entry = ui_conf[i].split(/;/);
+ menu_items.push({
+ id: "base_menu_item_" + i,
+ text: entry[0],
+ _order: entry[2],
+ onClick: this._select.bind(this, { select: i, rest: ""})
+ });
+ this._modules.set(i, {
+ title: entry[0],
+ script: entry[1],
+ name: i,
+ loaded: false,
+ div: null,
+ object: null,
+ menus: []
+ });
+ }
+ menu_items.sort(function(a, b) {
+ return a._order < b._order ? -1 : a._order > b._order ? 1 : 0;
+ });
+
+ this._menu_items = menu_items;
+ this._main_menu = new BSEMenu({
+ title: menu_items[0].text,
+ current: true,
+ items: menu_items
+ });
+ $("nav").appendChild(this._main_menu.element());
+ this._menubar.add_menu(this._main_menu);
+
+ var sel = this._parse_frag(window.location.hash);
+ this._select(sel);
+ },
+ _make_logon_menu: function() {
+ this._logon_menu = new BSEMenu({
+ title: "(none)",
+ current: true,
+ items: [
+ {
+ id: "base_menu_user_logout",
+ text: "Logoff",
+ onClick: this._do_logoff.bind(this)
+ },
+ {
+ id: "base_menu_user_changepw",
+ text: "Change password",
+ onClick: this._do_changepw.bind(this)
+ }
+ ]
+ });
+ $("nav").appendChild(this._logon_menu.element());
+ this._menubar.add_menu(this._logon_menu);
+ },
+ // _finish_load: function() {
+ // this._log_entry("Scripts loaded, proceeding");
+ // this._order_handlers();
+ // this._load_menu();
+ // var sel = this._parse_frag(window.location.hash);
+ // this._select(sel);
+ // $("base_wrapper").removeClassName("hide");
+ // $("base_loading").style.display = "none";
+ // },
+ // _load_scripts: function() {
+ // var ui_conf = this.api.conf.admin_ui;
+ // var to_load = new Array;
+ // for (var i in ui_conf) {
+ // to_load.push(ui_conf[i]);
+ // }
+ // this._log_entry("Loading configured scripts " + to_load.join(" "));
+ // new BSELoader({ scripts: to_load,
+ // onLoaded: this._finish_load.bind(this) });
+ // },
+ load_css: function(css) {
+ css.each(function (e) {
+ var sty = new Element("link", { rel: "stylesheet", type: "text/css", href: e });
+ var head = $$("head")[0];
+ head.appendChild(sty);
+ });
+ },
+ // _order_handlers: function() {
+ // // make an ordered list of the registered handlers
+ // // first a name / object list
+ // var list = this.handlers.values();
+ // list.sort(
+ // function (a, b) {
+ // var aord = a.options.order;
+ // var bord = b.options.order;
+ // return aord < bord ? -1 : aord > bord ? 1 : 0;
+ // });
+ // this.ordered = list;
+ // },
+ // _load_menu: function() {
+ // var menu = $("base_menu");
+ // menu.innerHTML = "";
+ // this.ordered.each(
+ // function(menu, e) {
+ // var a = new Element("a", { href: "#" + e.key, id: "base_menu_item_"+ e.key });
+ // a.update(e.options.text);
+ // a.observe("click", function(e, event) {
+ // this._select({ select: e, rest: ""});
+ // event.stop();
+ // return false;
+ // }.bind(this, e));
+ // menu.appendChild(a);
+ // }.bind(this, menu)
+ // );
+ // },
+ // parse location (or the default "menu") to find something to display
+ _parse_frag: function(frag) {
+ if (!frag) frag = "#menu";
+ frag = frag.replace(/^\#/, '');
+ var m = /^([a-z0-9]+)(?:\/(.*))?$/.exec(frag);
+ if (m &&
+ this._modules.get(m[1]) != null) {
+ var rest = m[2] == null ? "" : m[2];
+
+ return { select: m[1], rest: rest };
+ }
+ else {
+ return { select: "menu", rest: "" };
+ }
+ },
+ // make something active, requiring a logon if the view
+ // requires it, which most do
+ _select: function(what) {
+ var mod = this._modules.get(what.select);
+ if (mod == null) {
+ this._log_entry("attempt to select unknown " + what.select);
+ return;
+ }
+ if (!mod.loaded) {
+ var loader = new BSELoader({
+ scripts: [ mod.script ],
+ onLoaded: function(what, mod) {
+ mod.loaded = true;
+ if (mod.object) {
+ this._select(what);
+ }
+ else {
+ this._log_entry("Loaded " + what.select + " but no object registered");
+ }
+ }.bind(this, what, mod)
+ });
+ }
+ if (!mod.object)
+ return;
+ if (mod.options.logon
+ && this.api.conf.access_control != 0) {
+ if (this._userinfo) {
+ if (this._userinfo.user) {
+ this._do_select(what);
+ }
+ else {
+ this._do_logon_and_select(what);
+ }
+ }
+ else {
+ // get the user info
+ this.api.userinfo(
+ {
+ onSuccess: function(what, result) {
+ this._userinfo = result;
+ if (this._userinfo.user)
+ this._show_current_logon();
+ // try again
+ this._select(what);
+ }.bind(this, what)
+ }
+ );
+ }
+ }
+ else {
+ this._do_select(what);
+ }
+ },
+ _select_none: function() {
+ if (this.current) {
+ this.current.menus.each(function(menu) {
+ menu.element().remove();
+ });
+ this.current.object.undisplay(this, this.current.div);
+ this.current.div.style.display = "none";
+ this.current = null;
+ }
+ },
+ _do_logon_and_select: function(what) {
+ new BSEDialog({
+ onSubmit: this._on_logon_submit.bind(this, what),
+ fields: [
+ {
+ type: "fieldset",
+ legend: "Logon",
+ fields: base_logon_fields,
+ },
+ ],
+ modal: true,
+ title: "Administration",
+ submit: "Logon",
+ submit_class: "blue"
+ });
+ },
+ _on_logon_submit: function(what, dlg) {
+ this.api.logon({
+ logon: dlg.field("logon").value(),
+ password: dlg.field("password").value(),
+ onSuccess: function(what, dlg, user) {
+ this._userinfo.user = user;
+ this._show_current_logon();
+ dlg.close();
+ this._select(what);
+ this.message(user.logon + " successfully logged on");
+ }.bind(this, what, dlg),
+ onFailure: function(dlg, result) {
+ dlg.bse_error(result);
+ }.bind(this, dlg)
+ });
+ },
+ // inner make something active
+ _do_select: function(what) {
+ if (this.current)
+ this._select_none();
+ var mod = this._modules.get(what.select);
+ if (mod.started) {
+ mod.object.display(this, what.select.div, what.rest);
+ mod.div.style.display = "block";
+ }
+ else {
+ var id = mod.name.replace(/\W+/g, "-");
+ mod.div = new Element("div", { id: "app_"+id });
+ mod.object.start(this, mod.div, what.rest);
+ $("base_work").appendChild(mod.div);
+ mod.started = true;
+ this._log_entry("Started "+mod.title);
+ }
+ mod.menus.each(function(menu) {
+ $("nav").appendChild(menu.element());
+ menu.inDocument();
+ });
+ this.current = mod;
+ this._main_menu.setText(mod.title);
+ },
+ _log_entry: function(text) {
+ var now = new Date;
+ this._log.push(now.toISOString() + " " + text);
+ if (this._log.length > 1000)
+ this._log.shift();
+ },
+ _show_current_logon: function() {
+ if (this._userinfo.user) {
+ var user = this._userinfo.user;
+ if (/\S/.test(user.name))
+ this._logon_menu.setText(user.name);
+ else
+ this._logon_menu.setText(user.logon);
+ }
+ else {
+ this._logon_menu.setText("(none)");
+ }
+ },
+ _do_logoff: function(event) {
+ //event.stop();
+ //$("base_logon").update("Logging off...");
+ this._select_none();
+ this.api.logoff({
+ onSuccess: function() {
+ this._userinfo.user = null;
+ this._show_current_logon();
+ this._select(this._parse_frag("#menu"));
+ }.bind(this),
+ onFailure: function(result) {
+ this.alert(result.msg);
+ }.bind(this)
+ });
+ },
+ _do_changepw: function(event) {
+ //event.stop();
+ if (!this._userinfo.user)
+ return;
+ new BSEDialog({
+ fields: [
+ {
+ name: "oldpassword",
+ label: "Old Password",
+ type: "password",
+ required: true
+ },
+ {
+ name: "newpassword",
+ label: "New Password",
+ type: "password",
+ required: true
+ },
+ {
+ name: "confirm",
+ label: "Confirm New Password",
+ type: "password",
+ rules: "confirm:newpassword",
+ required: true
+ }
+ ],
+ modal: true,
+ submit: "Change Password",
+ title: "Change Password",
+ cancel: true,
+ onSubmit: function(dlg) {
+ this._log_entry("Sending change password");
+ this.api.change_password({
+ oldpassword: dlg.field("oldpassword").value(),
+ newpassword: dlg.field("newpassword").value(),
+ onSuccess: function(dlg) {
+ this._log_entry("Successfully changed password");
+ dlg.close();
+ this.message("Password for " + this._userinfo.user.logon + " successfully changed");
+ }.bind(this, dlg),
+ onFailure: function(dlg, result) {
+ dlg.bse_error(result);
+ }.bind(this, dlg)
+ });
+ }.bind(this)
+ });
+ },
+ alert: function(message) {
+ new BSEDialog({
+ fields: [
+ {
+ type: "help",
+ helptext: message
+ }
+ ],
+ title: "Alert!",
+ modal: true,
+ submit: "Dismiss",
+ //submit_class: "dismiss",
+ onSubmit: function(dlg) { dlg.close(); }
+ });
+ },
+ message: function(text) {
+ this._messages.message(text);
+ },
+ busy: function() {
+ },
+ unbusy: function() {
+ }
+});
+
+var ui;
+
+var BSEUIBase = Class.create({
+ undisplay: function(ui, div) {}
+});
+
+BSEAdminUI.MenuDefaults =
+ {
+ logon: true
+ };
+
+document.observe(
+ "dom:loaded",
+ function() {
+ ui = new BSEAdminUI();
+ }
+);
+
+BSEAdminUI.Messages = Class.create({
+ initialize: function(div) {
+ this.div = $(div);
+ this.div.style.display = "none";
+ },
+ message: function(text) {
+ this.div.update(text);
+ Effect.Appear(this.div);
+ setTimeout(this._msg_done.bind(this),
+ 5000);
+ },
+ _msg_done: function() {
+ Effect.Fade(this.div);
+ }
+});
+
+// var BSEContentUI = Class.create
+// (BSEUIBase,
+// {
+// start: function(ui, div, args) {
+// div.innerHTML = "One day I'll do something";
+// },
+// display: function(ui, div) {
+// },
+// needed_content: function(ui, args) {
+// return { menu: "/admin/ui/menu.html" };
+// }
+// });
+
+// document.observe("dom:loaded", function() {
+// var handler = new BSEContentUI;
+// ui.menu_item({
+// name: "content",
+// object: handler,
+// text: "Content",
+// order: "b"
+// });
+// ui.menu_item({
+// name: "users",
+// object: handler,
+// text: "Users",
+// order: "c"
+// });
+// ui.menu_item({
+// name: "system",
+// object: handler,
+// text: "System",
+// order: "d"
+// });
+
+// });
+
this.onException = function(e) {
alert(e);
};
- this.onFailure = function(error) { alert(error.message); };
- this._load_csrfp();
+ this.onFailure = function(error) {
+ alert(error.message);
+ };
+ this._load_csrfp(parameters);
this.onConfig = parameters.onConfig;
- this._load_config();
+ delete parameters.onConfig;
+ this._load_config(parameters);
},
- _load_csrfp: function () {
+ _load_csrfp: function (param) {
this.get_csrfp
- ({
+ (Object.extend
+ ({
id: -1,
name: this._csrfp_names,
onSuccess: function(csrfp) {
// ignore this
this._csrfp = null;
}
- });
+ }, param));
},
- _load_config: function() {
+ _load_config: function(param) {
this.get_base_config
- ({
+ (Object.extend
+ ({
onSuccess:function(conf) {
this.conf = conf;
if (this.onConfig)
}.bind(this),
onFailure: function(err) {
}
- });
+ }, param));
},
// logon to the server
// logon - logon name of user
onException: this.onException
});
},
+ change_password: function(parameters) {
+ var success = parameters.onSuccess;
+ if (!success) this._badparm("change_password() missing onSuccess parameter");
+ var failure = parameters.onFailure;
+ if (!failure) failure = this.onFailure;
+ new Ajax.Request('/cgi-bin/admin/changepw.pl',
+ {
+ parameters: {
+ a_change: 1,
+ oldpassword: parameters.oldpassword,
+ newpassword: parameters.newpassword,
+ confirm: parameters.newpassword
+ },
+ onSuccess: function (success, failure, resp) {
+ if (resp.responseJSON) {
+ if(resp.responseJSON.success != 0) {
+ success();
+ }
+ else {
+ failure(this._wrap_json_failure(resp), resp);
+ }
+ }
+ else {
+ failure(this._wrap_nojson_failure(resp), resp);
+ }
+ }.bind(this, success, failure),
+ onFailure: function (failure, resp) {
+ failure(this._wrap_req_failure(resp), resp);
+ }.bind(this, failure),
+ onException: this.onException
+ });
+ },
// fetch a tree of articles;
// id - parent of tree to fetch
// depth - optional depth of tree to fetch (default is large)
thumb_link: function(im, geoid) {
return "/cgi-bin/thumb.pl?image="+im.id+"&g="+geoid+"&page="+im.articleId+"&f="+encodeURIComponent(im.image);
},
- can_drag_and_drop: function() {
- // hopefully they're implemented at the same time
- if (window.FormData != null)
- return true;
-
- if (bse_use_file_api && window.FileReader != null)
- return true;
-
- return false;
- },
- make_drop_zone: function(options) {
- options.element.addEventListener
- (
- "dragenter",
- function(options, e) {
- e.stopPropagation();
- e.preventDefault();
- }.bind(this, options),
- false
- );
- options.element.addEventListener
- (
- "dragover",
- function(options, e) {
- e.stopPropagation();
- e.preventDefault();
- }.bind(this, options),
- false
- );
- options.element.addEventListener
- (
- "drop",
- function(options, e) {
- e.stopPropagation();
- e.preventDefault();
-
- options.onDrop(e.dataTransfer.files);
- }.bind(this, options),
- false
- );
- },
+
// parameters:
// image - file input element (required)
// id - owner article of the new image (required)
_do_request: function(url, action, other_parms, success, failure) {
if (action != null)
other_parms[action] = 1;
+ var async = true;
+ if (other_parms.hasOwnProperty("_async")) {
+ async = other_parms._async;
+ delete other_parms._async;
+ }
new Ajax.Request(url,
{
+ asynchronous: async,
parameters: other_parms,
onSuccess: function (success, failure, resp) {
if (resp.responseJSON) {
_upload_id: 0
});
+BSEAPI.can_drag_and_drop = function() {
+ // hopefully they're implemented at the same time
+ if (window.FormData != null)
+ return true;
+
+ if (bse_use_file_api && window.FileReader != null)
+ return true;
+
+ return false;
+};
+
+BSEAPI.make_drop_zone = function(options) {
+ options.element.addEventListener
+ (
+ "dragenter",
+ function(options, e) {
+ e.stopPropagation();
+ e.preventDefault();
+ }.bind(this, options),
+ false
+ );
+ options.element.addEventListener
+ (
+ "dragover",
+ function(options, e) {
+ e.stopPropagation();
+ e.preventDefault();
+ }.bind(this, options),
+ false
+ );
+ options.element.addEventListener
+ (
+ "drop",
+ function(options, e) {
+ e.stopPropagation();
+ e.preventDefault();
+
+ options.onDrop(e.dataTransfer.files);
+ }.bind(this, options),
+ false
+ );
+};
\ No newline at end of file
--- /dev/null
+var BSEDialog = Class.create({
+ initialize: function(options) {
+ this.options = Object.extend(
+ Object.extend({}, this.defaults()), options);
+ this._build();
+ if (this.options.dynamic_validation)
+ this._start_validation();
+ this._show();
+ },
+ _reset_errors: function() {
+ this._fields.clear_error();
+ },
+ error: function(msg) {
+ this._reset_errors();
+ this._error.update(msg);
+ this._error.show();
+ this._error_animate();
+ },
+ field_errors: function(errors) {
+ this._reset_errors();
+ if (errors.constructor == Hash) {
+ errors.each(function(entry) {
+ this._fields.set_error(entry.key, entry.value);
+ }.bind(this));
+ }
+ else {
+ for (var i in errors) {
+ this._fields.set_error(i, errors[i]);
+ }
+ }
+ this._error_animate();
+ },
+ _error_animate: function() {
+ this.options.error_animate(this, this.div);
+ },
+ field: function(name) {
+ return this._values.get(name);
+ },
+ bse_error: function(error) {
+ if (error.error_code == "FIELD") {
+ this.field_errors(error.errors);
+ }
+ else if (error.message) {
+ this.error(error.message);
+ }
+ else {
+ this.error(error_code);
+ }
+ },
+ close: function() {
+ this.div.remove();
+ if (this.wrapper)
+ this.wrapper.remove();
+ if (this._interval)
+ window.clearInterval(this._interval);
+ },
+ busy: function() {
+ this._spinner.show();
+ },
+ unbusy: function() {
+ this._spinner.hide();
+ },
+ _build: function() {
+ var top;
+ this.div = new Element("div", { className: this.options.top_class });
+ if (this.options.modal) {
+ this.wrapper = new Element("div", { className: this.options.modal_class });
+ top = this.wrapper;
+ this.wrapper.appendChild(this.div);
+ }
+ else {
+ top = this.div;
+ }
+ this.top = top;
+ this.form = new Element("form", { action: "#" });
+ this.form.observe("submit", this._on_submit.bind(this));
+ this.div.appendChild(this.form);
+
+ var parent;
+ if (this.options.fieldset_wrapper) {
+ var fs = new Element("fieldset");
+ this.title = new Element("legend");
+ this.title.update(this.options.title);
+ fs.appendChild(this.title);
+ this.form.appendChild(fs);
+ parent = fs;
+ }
+ else {
+ parent = this.form;
+ this.title = new Element("div", { className: this.options.title_class });
+ this.title.update(this.options.title);
+ this.div.appendChild(this.title);
+ }
+ this._error = new Element("div", { className: this.options.error_class });
+ this._error.hide();
+ parent.appendChild(this._error);
+ this.field_error_divs = {};
+ this.field_wrapper_divs = {};
+ this.fields = {};
+ this._add_fields(parent, this.options.fields);
+ var button_p = new Element("p", { className: this.options.submit_wrapper_class });
+ var sub_wrapper = new Element("span");
+ this._progress = new BSEDialog.ProgressBar();
+ button_p.appendChild(this._progress.element());
+
+ this._spinner = new Element("span", {className: this.options.spinner_class });
+ this._spinner.hide();
+ this._spinner.update(this.options.spinner_text);
+ sub_wrapper.appendChild(this._spinner);
+ if (this.options.cancel) {
+ this.cancel = this._make_cancel();
+ this.cancel.observe("click", this._on_cancel.bindAsEventListener(this));
+ sub_wrapper.appendChild(this.cancel);
+ }
+ this.submit = this._make_submit();
+ sub_wrapper.appendChild(this.submit);
+ button_p.appendChild(sub_wrapper);
+ this.form.appendChild(button_p);
+ },
+ _make_cancel: function() {
+ var cancel = new Element("button", {
+ type: "button",
+ className: this.options.cancel_base_class
+ });
+ if (this.options.cancel_class)
+ cancel.addClassName(this.options.cancel_class);
+ cancel.update(this.options.cancel_text);
+
+ return cancel;
+ },
+ _make_submit: function() {
+ var submit = new Element("button", {
+ type: "submit",
+ className: this.options.submit_base_class
+ });
+ if (this.options.submit_class)
+ submit.addClassName(this.options.submit_class);
+ submit.update(this.options.submit);
+
+ return submit;
+ },
+ _show: function() {
+ var body = $$("body")[0];
+ body.insertBefore(this.div, body.firstChild);
+ if (this.wrapper)
+ body.insertBefore(this.wrapper, body.firstChild);
+ if (this.options.position) {
+ var top_px = (document.viewport.getHeight() - this.div.getHeight()) / 2;
+ if (top_px < 20) {
+ this.div.style.overflowX = "scroll";
+ this.div.style.top = "10px";
+ this.div.style.height = (this.viewport.getHeight()-20) + "px";
+ }
+ else {
+ this.div.style.top = top_px + "px";
+ }
+ this.div.style.left = (document.viewport.getWidth() - this.div.getWidth()) / 2 + "px";
+ }
+ this._fields.inDocument();
+ //if (this.wrapper) {
+ // this.wrapper.style.height = "100%";
+ //}
+ },
+ _add_fields: function(parent, fields) {
+ this._fields = new BSEDialog.Fields(this.options);
+ this._value_fields = this._fields.value_fields();
+ this._values = new Hash();
+ this._value_fields.each(function(field) {
+ this._values.set(field.name(), field);
+ }.bind(this));
+ this._elements = this._fields.elements();
+ this._elements.each(function(parent, ele) {
+ parent.appendChild(ele);
+ }.bind(this, parent));
+ },
+ _start_validation: function() {
+ if (this.options.validator) {
+ this._update_submit();
+ this._interval = window.setInterval(this._update_submit.bind(this), this.options.dynamic_interval);
+ }
+ },
+ _update_submit: function() {
+ var errors = new Hash();
+ this.submit.disabled = this.options.validator.validate(this._values, errors) ? "" : "disabled";
+ },
+ _on_submit: function(event) {
+ event.stop();
+ if (this.options.validator) {
+ var errors = new Hash();
+ if (!this.options.validator.validate(this._values, errors)) {
+ this.field_errors(errors);
+ return;
+ }
+ }
+ this.options.onSubmit(this);
+ },
+ _on_cancel: function(event) {
+ event.stop();
+ this.options.onCancel(this);
+ },
+ defaults: function() {
+ return BSEDialog.defaults;
+ },
+ progress_start: function(note) {
+ this._progress.start(note);
+ },
+ progress: function(frac, note) {
+ this._progress.progress(frac, note);
+ },
+ progress_end: function() {
+ this._progress.end();
+ },
+ progress_note: function(note) {
+ this._progress.note(note);
+ },
+ enable: function() {
+ this.form.enable();
+ },
+ disable: function() {
+ this.form.disable();
+ }
+});
+
+BSEDialog.defaults = {
+ modal: false,
+ title: "Missing title",
+ validator: new BSEValidator,
+ top_class: "window dialog",
+ modal_class: "bse_modal",
+ title_class: "bse_title",
+ error_class: "bse_error",
+ submit_wrapper_class: "buttons",
+ submit: "Submit",
+ cancel: false,
+ cancel_text: "Cancel",
+ onCancel: function(dlg) { dlg.close(); },
+ dynamic_validation: true,
+ dynamic_interval: 1000,
+ position: false,
+ fieldset_wrapper: true,
+ cancel_base_class: "button bigrounded cancel",
+ cancel_class: "gray",
+ submit_base_class: "button bigrounded",
+ submit_class: "green",
+ error_animate: function(dlg, div) {
+ Effect.Shake(div);
+ },
+ spinner_class: "spinner",
+ spinner_text: "Busy"
+};
+
+
+// wraps one or more fields
+//
+BSEDialog.Fields = Class.create({
+ initialize: function(options) {
+ this.options = Object.extend({}, options);
+
+ this._fields = {};
+ this._value_fields = [];
+ this._elements = [];
+ this._fieldobjs = [];
+ options.fields.each(function(field) {
+ var cls = BSEDialog.FieldTypes[field.type || "text"];
+ var fieldobj = new cls(field);
+ this._fieldobjs.push(fieldobj);
+ fieldobj.value_fields().each(function(field) {
+ this._fields[field.name()] = field;
+ }.bind(this));
+ this._value_fields = this._value_fields.concat(fieldobj.value_fields());
+ this._elements = this._elements.concat(fieldobj.elements());
+ }.bind(this));
+ },
+ set_error: function(name, message) {
+ this._fields[name].set_error(name, message);
+ },
+ clear_error: function() {
+ for (var i in this._fields) {
+ this._fields[i].clear_error();
+ }
+ },
+ value_fields: function() {
+ return this._value_fields;
+ },
+ elements: function() {
+ return this._elements;
+ },
+ inDocument: function() {
+ this._fieldobjs.each(function(f) {
+ f.inDocument();
+ });
+ }
+});
+
+BSEDialog.FieldTypes = {};
+
+// field objects provide the following methods:
+//
+// clear_error() - clear all error indicators
+// set_error(name, message) - set the error indicator for the named field
+// input() - return the underlying input tag (intended for use with file
+// fields, many field types will not have a single input)
+// value_fields() return the fields that actually have a value. For a field
+// this is the child fields of the fieldset. For value fields just return
+// [ this ]
+// elements() returns the top-level elements for each field, for an input
+// this is the wrapper div, for a field set, the fieldset itself
+//
+// Fields returned by value_fields() must also provide:
+// value() - return the value of the given field
+// description() - text description of the field
+// name() - name of the value
+// rules() - validation rules either as a ; separated string or an array
+// has_value() - returns true if the field has a non-empty value
+// required() - returns true if the field is marked required
+//
+BSEDialog.FieldTypes.Base = Class.create({
+ _make_wrapper: function() {
+ var wrapper = new Element("div", { className: this.options.field_wrapper_class });
+ if (this.options.required)
+ wrapper.addClassName(this.options.field_required_class);
+
+ return wrapper;
+ },
+ _make_label: function() {
+ return new Element("label", { htmlFor: this._input.identify() });
+ },
+ _make_error: function() {
+ var err_div = new Element("div", { className: this.options.field_error_class });
+ err_div.hide();
+ return err_div;
+ },
+ name: function() {
+ return this.options.name;
+ },
+ clear_error: function() {
+ this._error.update("");
+ this._error.hide();
+ this.elements().each(function(ele) {
+ ele.removeClassName(this.options.field_invalid_class);
+ }.bind(this));
+ },
+ set_error: function(name, message) {
+ this._error.update(message);
+ this._error.show();
+ this.elements().each(function(ele) {
+ ele.addClassName(this.options.field_invalid_class);
+ }.bind(this));
+ },
+ input: function() {
+ return null;
+ },
+ description: function() {
+ return null;
+ },
+ required: function() {
+ return this.options.required;
+ },
+ value_fields: function() {
+ return [ this ];
+ },
+ defaults: function() {
+ return BSEDialog.FieldTypes.Base.defaults;
+ },
+ set_object: function(object) {
+ this._object = object;
+ },
+ object: function() {
+ return this._object;
+ },
+ // called when the field becomes part of the document
+ inDocument: function() {
+ },
+ default_options: function() {
+ return BSEDialog.FieldTypes.Base.defaults;
+ }
+});
+
+BSEDialog.FieldTypes.Base.defaults = {
+ field_wrapper_class: "bse_field_wrapper",
+ field_error_class: "bse_field_error",
+ field_required_class: "bse_required",
+ field_invalid_class: "bse_invalid",
+ rules: [],
+ required: false
+};
+
+BSEDialog.FieldTypes.input = Class.create(BSEDialog.FieldTypes.Base, {
+ initialize: function(options) {
+ this.options = Object.extend(Object.extend({}, this.defaults()), options);
+ this._div = this._make_wrapper();
+ var span = new Element("span");
+ this._input = this._make_input();
+ this._label = this._make_label();
+ this._label.update(this.description());
+ this._error = this._make_error();
+
+ span.appendChild(this._input);
+ this._div.appendChild(this._label);
+ this._div.appendChild(span);
+ this._div.appendChild(this._error);
+ },
+ _make_input: function() {
+ return new Element(
+ "input",
+ {
+ name: this.options.name,
+ type: this.options.type,
+ value: this.options.value
+ });
+ },
+ value: function() {
+ return this._input.value;
+ },
+ elements: function() {
+ return [ this._div ];
+ },
+ name: function() {
+ return this.options.name;
+ },
+ description: function() {
+ return this.options.label || this.options.name;
+ },
+ rules: function() {
+ return this.options.rules;
+ },
+ has_value: function() {
+ return /\S/.test(this.value());
+ },
+ defaults: function() {
+ return Object.extend(
+ Object.extend(
+ {}, BSEDialog.FieldTypes.Base.defaults
+ ), BSEDialog.FieldTypes.input.defaults);
+ }
+});
+
+BSEDialog.FieldTypes.input.defaults = {
+ type: "text",
+ value: ""
+};
+
+BSEDialog.FieldTypes.text = BSEDialog.FieldTypes.input;
+
+BSEDialog.FieldTypes.password = BSEDialog.FieldTypes.input;
+
+BSEDialog.FieldTypes.textarea = Class.create(BSEDialog.FieldTypes.input, {
+ _make_input: function() {
+ var ta = new Element("textarea", {
+ name: this.options.name,
+ value: this.options.value,
+ cols: this.options.cols,
+ rows: this.options.rows
+ });
+ ta.update(this.options.value);
+
+ return ta;
+ },
+ defaults: function($super) {
+ return Object.extend({}, Object.extend($super(), {
+ rows: 4,
+ cols: 60
+ }));
+ }
+});
+
+BSEDialog.FieldTypes.select = Class.create(BSEDialog.FieldTypes.input, {
+ _make_input: function() {
+ var input = new Element("select", { name: this.options.name });
+ var values = this.options.values;
+ for (var i = 0; i < values.length; ++i) {
+ var val = values[i];
+ var def = this.option.value != null && this.option.value == val.key;
+ input.options[input.options.length] =
+ new Option(val.label, val.value, def);
+ }
+ return input;
+ }
+});
+
+BSEDialog.FieldTypes.radio = Class.create(BSEDialog.FieldTypes.Base, {
+ initialize: function() {
+ },
+ value: function() {
+ },
+ input: function() {
+ }
+});
+
+BSEDialog.FieldTypes.fieldset = Class.create(BSEDialog.FieldTypes.Base, {
+ initialize: function(options) {
+ this.options = Object.extend(Object.extend({}, BSEDialog.FieldTypes.fieldset.defaults), options);
+ this._element = new Element("fieldset");
+ if (this.options.legend) {
+ var legend = new Element("legend")
+ legend.update(this.options.legend);
+ this._element.appendChild(legend);
+ }
+
+ this._fields = new BSEDialog.Fields(options);
+ this._fields.elements().each(function(ele) {
+ this._element.appendChild(ele);
+ }.bind(this));
+ },
+ value_fields: function() {
+ return this._fields.value_fields();
+ },
+ clear_error: function() {
+ this._fields.clear_error();
+ },
+ set_error: function(name, message) {
+ this._fields.set_error(name, message);
+ },
+ elements: function() {
+ return [ this._element ];
+ },
+ inDocument: function() {
+ this._fields.inDocument();
+ }
+});
+
+BSEDialog.FieldTypes.fieldset.defaults = {
+};
+
+BSEDialog.FieldTypes.help = Class.create(BSEDialog.FieldTypes.Base, {
+ initialize: function(options) {
+ this._element = new Element("div");
+ this._element.update(options.helptext);
+ },
+ value_fields: function() {
+ // nothing to see here, move along
+ return [];
+ },
+ elements: function() {
+ return [ this._element ];
+ }
+});
+
+BSEDialog.FieldTypes.image = Class.create(BSEDialog.FieldTypes.Base, {
+ initialize: function(options) {
+ this.options = Object.extend(Object.extend({}, this.default_options()), options);
+ // general container
+ var wrapper = new Element("fieldset", {
+ className: "bse_image_field"
+ });
+ this._element = wrapper;
+ var legend = new Element("legend");
+ legend.update(this.options.label);
+ wrapper.appendChild(legend);
+
+ var disp = new Element("img", {
+ className: "display"
+ });
+ this._image_display = disp;
+ this.options.value = Object.extend({
+ src: "",
+ alt: "",
+ name: "",
+ description: "",
+ display_name: ""
+ }, this.options.value || {});
+ if (this.options.value) {
+ if (this.options.value.src)
+ disp.src = this.options.value.src;
+ else if (this.options.value.file) {
+ new BSEDialog.ImagePlaceholder({
+ file: this.options.value.file,
+ onLoad: function(disp, ph) {
+ disp.src = ph.src();
+ }.bind(this, disp)
+ });
+ }
+
+ disp.alt = this.options.value.alt;
+ }
+
+ wrapper.appendChild(disp);
+ var file = new Element("input", {
+ type: "file"
+ });
+ this._file_input = file;
+ wrapper.appendChild(file);
+
+ if (BSEAPI.can_drag_and_drop()) {
+ BSEAPI.make_drop_zone({
+ element: disp,
+ onDrop: function (files) {
+ this.clear_error();
+ var file = files[0];
+ if (!/\.(jpe?g|png|gif)$/i.test(file.fileName)) {
+ this.set_error("Only image files accepted");
+ return;
+ }
+ this._dropped_file = file;
+ this.value.display_name = file.fileName;
+ if (window.URL && window.URL.createObjectURL) {
+ this._update_thumb_dropped(window.URL.createObjectURL(file));
+ }
+ else if (window.FileReader) {
+ var fr = new FileReader;
+ fr.onload = function(fr) {
+ this._update_thumb_dropped(fr.result);
+ }.bind(this, fr);
+ fr.readAsDataURL(file);
+ }
+
+ this._file_input.hide();
+ this._dropped_name.update(this.value.display_name);
+ this._dropped_name.show();
+ }.bind(this)
+ });
+ this._dropped_name = new Element("span", {
+ className: "dropped_name"
+ });
+ this._dropped_name.hide();
+ wrapper.appendChild(this._dropped_name);
+ }
+
+ var fields = new Array();
+ if (!this.options.hide_alt) {
+ fields.push({
+ label: "Alt",
+ type: "text",
+ name: "alt",
+ value: this.options.value.alt
+ });
+ }
+ if (!this.options.hide_name) {
+ fields.push({
+ label: "Name",
+ type: "text",
+ name: "name",
+ value: this.options.value.name
+ });
+ }
+ if (!this.options.hide_description) {
+ fields.push({
+ label: "Description",
+ type: "text",
+ name: "description",
+ value: this.options.value.description
+ });
+ }
+
+ if (fields.length != 0) {
+ var more = new Element("div", {
+ className: "more"
+ });
+ more.update("more");
+ more.observe("click", function () {
+ if (this._extras_shown)
+ this._extras.hide();
+ else
+ this._extras.show();
+ this._extras_shown = !this._extras_shown;
+ }.bind(this));
+ wrapper.appendChild(more);
+
+ // extra image info
+ var extras = new Element("div", {
+ className: "extras"
+ });
+ this._extras = extras;
+ this._extras_shown = false;
+ this._extra_fields = new BSEDialog.Fields({
+ fields: fields
+ });
+ this._extra_fields.elements().each(function(ele) {
+ this._extras.appendChild(ele);
+ }.bind(this));
+ extras.hide();
+ wrapper.appendChild(extras);
+ }
+ this._error = this._make_error();
+ wrapper.appendChild(this._error);
+ },
+ _update_thumb_dropped: function(url) {
+ // a bit hacky
+ var img = new Element("img");
+ img.onload = function(img) {
+ var canvas = new Element("canvas", {
+ width: 80,
+ height: 80
+ });
+
+ var ctx = canvas.getContext("2d");
+ var max_dim = img.width > img.height ? img.width : img.height;
+ var scale = 80 / max_dim;
+ var sc_width = img.width * scale;
+ var sc_height = img.height * scale;
+ var off_x = (80-sc_width)/2;
+ var off_y = (80-sc_height)/2;
+ ctx.drawImage(img, off_x, off_y, 80-off_x*2, 80-off_y*2);
+ this._image_display.src = canvas.toDataURL();
+ }.bind(this, img);
+ img.src = url
+ },
+ default_options: function() {
+ return {};
+ },
+ elements: function() {
+ return [ this._element ];
+ },
+ rules: function() {
+ return [];
+ },
+ has_value: function() {
+ if (this._dropped_file)
+ return true;
+ if (this._file_input.value.length)
+ return true;
+
+ return false;
+ },
+ value: function() {
+ if (this._dropped_file)
+ return this._dropped_file.fileName;
+
+ return this._file_input.value;
+ },
+ object: function() {
+ var obj = {};
+ if (this._dropped_file) {
+ obj.file = this._dropped_file;
+ obj.display_name = obj.file.fileName;
+ }
+ else if (this._file_input.value != "") {
+ obj.file = this._file_input;
+ obj.display_name = obj.file.value;
+ }
+ if (this._extra_fields) {
+ this._extra_fields.value_fields().each(function(obj, field) {
+ obj[field.name()] = field.value();
+ }.bind(this, obj));
+ }
+
+ return obj;
+ }
+});
+
+BSEDialog.FieldTypes.gallery = Class.create(BSEDialog.FieldTypes.Base, {
+ initialize: function(options) {
+ this.options = Object.extend(
+ Object.extend({}, this.default_options()),
+ options);
+ this._element = new Element("fieldset", {
+ className: "bse_image_gallery"
+ });
+ this._undo_history = [];
+ var legend = new Element("legend");
+ legend.update(this.options.label);
+ this._element.appendChild(legend);
+ this._images_element = new Element("div", {
+ className: "bse_gallery_imagelist"
+ });
+ // original images that have been removed
+ this._deleted = [];
+ this._element.appendChild(this._images_element);
+ this._element.appendChild(this._make_input_div());
+ this._images = this.options.value.map(function(im) {
+ return {
+ type: "old",
+ image: im,
+ display_name: im.display_name,
+ id: im.id,
+ changed: false,
+ alt: im.alt,
+ description: im.description,
+ name: im.name,
+ src: im.src
+ };
+ });
+ this._autoid = 1;
+ this._populate_images();
+ },
+ _make_input_div: function() {
+ var div = new Element("div");
+ this._file_input = this._make_file_input();
+ var label = new Element("label", {
+ "for": this._file_input.identify()
+ });
+ label.update(this.options.file_input_label);
+ var add = new Element("span", {
+ className: "widget add"
+ });
+ add.update("Add");
+ add.observe("click", this._add_file_input.bind(this));
+ div.appendChild(label);
+ div.appendChild(this._file_input);
+ div.appendChild(add);
+
+ return div;
+ },
+ _make_file_input: function() {
+ var file = new Element("input", {
+ type: "file"
+ });
+
+ if (this._file_input)
+ file.id = this._file_input.identify();
+
+ if (BSEAPI.can_drag_and_drop())
+ file.multiple = true;
+
+ return file;
+ },
+ _add_file_input: function() {
+ var file = this._file_input;
+ if (file.value.length > 0) {
+ if (BSEAPI.can_drag_and_drop()) {
+ for (var i = 0; i < file.files.length; ++i) {
+ this._images.push({
+ type: "new",
+ file: file.files[i],
+ display_name: file.files[i].fileName,
+ id: "new" + this._autoid++,
+ alt: "",
+ description: "",
+ name: ""
+ });
+ }
+ }
+ else {
+ this._images.push({
+ type: "new",
+ file: file,
+ display_name: file.value,
+ id: "new" + this._autoid,
+ alt: "",
+ description: "",
+ name: ""
+ });
+ ++this._autoid;
+ }
+ this._populate_images();
+ this._make_sortable();
+
+ this._file_input = this._make_file_input();
+ file.replace(this._file_input);
+ }
+ },
+ _undo_save: function() {
+ this._undo_history.push({
+ images: this._images.clone(),
+ deleted: this._deleted.clone()
+ });
+ },
+ _undo: function() {
+ if (this._undo_history.length > 0) {
+ var entry = this._undo_history.pop();
+ this._images = entry.images;
+ this._deleted = entry.deleted;
+ this._populate_images();
+ this._make_sortable();
+ }
+ },
+ _populate_images: function() {
+ this._images_element.update();
+ this._images.each(function(im) {
+ this._images_element.appendChild(this._make_image_element(im));
+ }.bind(this));
+
+ if (BSEAPI.can_drag_and_drop()) {
+ var targ = new Element("div", {
+ className: this.options.drop_target_class
+ });
+ targ.update("Drop here");
+ BSEAPI.make_drop_zone({
+ element: targ,
+ onDrop: function(targ, files) {
+ for (var i = 0; i < files.length; ++i) {
+ this._images.push({
+ type: "new",
+ file: files[i],
+ display_name: files[i].fileName,
+ id: "new" + this._autoid,
+ alt: "",
+ description: "",
+ name: ""
+ });
+ ++this._autoid;
+ this._populate_images();
+ this._make_sortable();
+ }
+ }.bind(this, targ)
+ });
+ this._images_element.appendChild(targ);
+ }
+ },
+ _make_sortable: function() {
+ Sortable.create(this._images_element.identify(), {
+ tag: "div",
+ only: this.options.image_entry_class,
+ constraint: "horizontal",
+ overlap: "horizontal",
+ onUpdate: function() {
+ }.bind(this)
+ });
+ },
+ _make_image_element: function(im) {
+ var p = new Element("div", {
+ id: "bse_image_"+im.id,
+ className: this.options.image_entry_class
+ });
+ p.appendChild(this._make_thumb_img(im));
+ var del = new Element("span", {
+ className: "widget delete"
+ });
+ del.update("Delete");
+ del.observe("click", this._delete_image.bind(this, im));
+ p.appendChild(del);
+ var edit = new Element("span", {
+ className: "widget edit"
+ });
+ edit.update("Edit");
+ edit.observe("click", this._edit_image.bind(this, im));
+ p.appendChild(edit);
+ var namep = new Element("span", {
+ className: "name"
+ });
+
+ namep.update(im.display_name);
+ p.appendChild(namep);
+
+ return p;
+ },
+ _make_thumb_img: function(im) {
+ if (im.thumb_img)
+ return im.thumb_img;
+
+ if (im.type == "old") {
+ if (this.options.getThumbURL) {
+ var img = new Element("img");
+ img.src = this.options.getThumbURL(im.image);
+ im.thumb_img = img;
+ }
+ else {
+ var thumb = new BSEDialog.ImagePlaceholder({
+ url: im.image.src
+ });
+ im.thumb_img = thumb.element();
+ }
+ }
+ else {
+ var thumb = new BSEDialog.ImagePlaceholder({
+ file: im.file
+ });
+ im.thumb_img = thumb.element();
+ }
+
+ return im.thumb_img;
+ },
+ _delete_image: function(im) {
+ this._undo_save();
+ if (im.type == "old")
+ this._deleted.push(im);
+ this._images = this._images.without(im);
+ this._populate_images();
+ this._make_sortable();
+ },
+ _edit_image: function(im) {
+ new BSEDialog({
+ title: "Edit Gallery Image",
+ modal: true,
+ submit: "Update",
+ cancel: true,
+ fields: [
+ {
+ name: "image",
+ type: "image",
+ value: im,
+ label: "Image"
+ }
+ ],
+ onSubmit: function(im, dlg) {
+ var result = dlg.field("image").object();
+ im.changed = true;
+ im.name = result.name;
+ im.alt = result.alt;
+ im.description = result.description;
+ if (result.file) {
+ im.file = result.file;
+ var thumb = new BSEDialog.ImagePlaceholder({
+ file: im.file
+ });
+ im.thumb_img = thumb.element();
+ }
+ this._populate_images();
+ this._make_sortable();
+ dlg.close();
+ }.bind(this, im)
+ });
+ },
+ default_options: function($super) {
+ return Object.extend(
+ Object.extend({}, $super()), {
+ value: [],
+ image_list_class: "bse_gallery_imagelist",
+ image_entry_class: "bse_gallery_image",
+ drop_target_class: "bse_drop_target",
+ file_input_label: "Add image"
+ });
+ },
+ elements: function () {
+ return [ this._element ];
+ },
+ value: function() {
+ return this._images.length > 0 ? "1" : "";
+ },
+ has_value: function() {
+ return this._value.length != 0;
+ },
+ object: function() {
+ return {
+ images: this._images,
+ deleted: this._deleted
+ };
+ },
+ rules: function() {
+ return [];
+ },
+ inDocument: function() {
+ this._make_sortable();
+ }
+});
+
+BSEDialog.AskYN = Class.create({
+ initialize: function(options) {
+ this.options = Object.extend({
+ submit: "Yes",
+ cancel: true,
+ cancel_text: "No",
+ cancel_class: "rosy",
+ modal: true
+ }, options);
+ this.options.fields = [
+ {
+ type: "help",
+ helptext: options.text
+ }
+ ];
+ if (this.options.onYes)
+ this.options.onSubmit = this.options.onYes;
+ if (this.options.onNo)
+ this.options.onCancel = this.options.onNo;
+ var dlg = new BSEDialog(this.options);
+ }
+});
+
+BSEDialog.ProgressBar = Class.create({
+ initialize: function() {
+ this._progress = new Element("span", {
+ className: "progress"
+ });
+ this._progress.hide();
+ this._progress_status = new Element("span", {
+ className: "status"
+ });
+ this._progress.appendChild(this._progress_status);
+ this._progress_bar = new Element("span", {
+ className: "bar blue"
+ });
+ this._progress.appendChild(this._progress_bar);
+ },
+ element: function() {
+ return this._progress;
+ },
+ start: function(note) {
+ if (note != null)
+ this.note(note);
+ else
+ this._progress_status.update();
+ this._progress.show();
+ this._progress_width = this._progress.getWidth();
+ this._progress_bar.style.width = "0px";
+ },
+ progress: function(frac, note) {
+ if (frac != null) {
+ this._progress_bar.style.width = Math.floor(this._progress_width * frac) + "px";
+ }
+ if (note != null)
+ this.note(note);
+
+ },
+ end: function() {
+ this._progress.hide();
+ },
+ note: function(note) {
+ if (note != null)
+ this._progress_status.update(note);
+ else
+ this._progress_status.update();
+ }
+});
+
+BSEDialog.ImagePlaceholder = Class.create({
+ initialize:function(options) {
+ this.options = Object.extend(this.default_options(), options);
+
+ this._element = new Element("img", {
+ width: this.options.width,
+ height: this.options.height
+ });
+
+ if (this.options.url) {
+ this._update(this.options.url);
+ return;
+ }
+
+ var file = options.file.files ? options.file.files[0] : options.file;
+ if (window.URL && window.URL.createObjectURL) {
+ this._update(window.URL.createObjectURL(file));
+ }
+ else if (window.URL && window.webkitURL.createObjectURL) {
+ this._update(window.webkitURL.createObjectURL(file));
+ }
+ else if (window.FileReader) {
+ var fr = new FileReader;
+ fr.onload = function(fr) {
+ this._update(fr.result);
+ }.bind(this, fr);
+ fr.readAsDataURL(file);
+ }
+ else {
+ this._src = this.options.noapisrc;
+ this._element.src = this._src;
+ this._onload();
+ }
+ },
+ _update: function(url) {
+ var img = new Element("img");
+ img.onload = function(img) {
+ var canvas = new Element("canvas", {
+ width: this.options.width,
+ height: this.options.height
+ });
+
+ var ctx = canvas.getContext("2d");
+ var max_dim = img.width > img.height ? img.width : img.height;
+ var scale = this.options.width / max_dim;
+ var sc_width = img.width * scale;
+ var sc_height = img.height * scale;
+ var off_x = (this.options.width - sc_width)/2;
+ var off_y = (this.options.height - sc_height)/2;
+ ctx.drawImage(img, off_x, off_y, this.options.width-off_x*2, this.options.height-off_y*2);
+ this._src = canvas.toDataURL();
+ this._element.src = this._src;
+ this._onload();
+ }.bind(this, img);
+ img.src = url
+ },
+ _onload: function() {
+ if (this.options.onLoad)
+ this.options.onLoad(this);
+ },
+ element: function() {
+ return this._element;
+ },
+ // only valid once the image is loaded
+ src: function() {
+ return this._src;
+ },
+ default_options: function() {
+ return {
+ width: 80,
+ height: 80,
+ noapisrc: "/images/ph.gif"
+ };
+ }
+});
--- /dev/null
+var BSELoader = Class.create({
+ initialize: function(options) {
+ this.options = Object.extend({}, options);
+ this._scripts = options.scripts.clone();
+ this._load_next_script();
+ },
+ _load_next_script: function() {
+ var uri = this._scripts.shift();
+ if (BSELoader.cache_buster) {
+ uri = uri + "?" + Math.random();
+ }
+ var scr = new Element("script", { src: uri, type: "text/javascript" });
+ scr.loadDone = false;
+ scr.onload = function(scr) {
+ if (!this.loadDone) {
+ scr.loadDone = true;
+ this._script_loaded();
+ }
+ }.bind(this, scr);
+ scr.onreadystatechange = function(scr) {
+ if ((scr.readyState === "loaded" || scr.readyState === "complete")
+ && !scr.loadDone) {
+ scr.loadDone = true;
+ this._script_loaded();
+ }
+ }.bind(this, scr);
+ var head = $$("head")[0];
+ head.appendChild(scr);
+ },
+ _script_loaded: function() {
+ if (this._scripts.length) {
+ this._load_next_script();
+ }
+ else {
+ if (this.options.onLoaded != null)
+ this.options.onLoaded();
+ }
+ }
+});
+
+BSELoader.cache_buster = false;
--- /dev/null
+var BSEMenuBar = Class.create({
+ initialize: function(options) {
+ this.options = Object.extend({
+ hide_timeout: 750
+ }, options);
+ this.menus = [];
+ },
+ add_menu: function(menu) {
+ this.menus.push(menu);
+ menu.element().observe("mouseover", function(ev, menu) {
+ if (this._hide_timer)
+ window.clearTimeout(this._hide_timer);
+ if (this._current_menu) {
+ if (this._current_menu == menu)
+ return;
+
+ this._current_menu.submenu().hide();
+ delete this._current_menu;
+ }
+
+ this._current_menu = menu;
+ this._current_menu.submenu().show();
+ }.bindAsEventListener(this, menu));
+ menu.element().observe("mouseleave", function(ev, menu) {
+ if (this._current_menu) {
+ if (this._hide_timer)
+ window.clearTimeout(this._hide_timer);
+ this._hide_timer = window.setTimeout(function() {
+ this._current_menu.submenu().hide();
+ delete this._current_menu;
+ delete this._hide_timer;
+ }.bind(this, menu), this.options.hide_timeout);
+ }
+ }.bindAsEventListener(this, menu));
+ }
+});
+
+// represents a top-level menu item
+//
+var BSEMenu = Class.create({
+ initialize: function(options) {
+ this.options = Object.extend(
+ Object.extend({}, this.defaults()), options);
+ this._make_element();
+ },
+ // return an element containing the menu
+ // this must be an li
+ element: function() {
+ return this._element;
+ },
+ _make_element: function() {
+ this._element = new Element("li");
+ if (this.options.current)
+ this._element.addClassName(this.options.current_class);
+ this._title_element = new Element("span", { className: this.options.title_class });
+ this._title_element.observe("click", function(ev) { ev.stop(); });
+ this._title_element.update(this.options.title);
+ this._element.appendChild(this._title_element);
+
+ // caller provided their own content for the menu
+ this._submenu = new this.options.submenu_class(this.options);
+ this._element.appendChild(this._submenu.element());
+
+ // this._element.observe("mouseover", function(ev) {
+ // ev.stop();
+ // this._submenu.show();
+ // if (this._hide_timer) {
+ // window.clearTimeout(this._hide_timer);
+ // delete this._hide_timer;
+ // }
+ // }.bind(this));
+ // this._element.observe("mouseout", function(ev) {
+ // ev.stop();
+ // if (this._hide_timer)
+ // window.clearTimeout(this._hide_timer);
+ // this._hide_timer = window.setTimeout(function() {
+ // this._submenu.hide();
+ // delete this._hide_timer;
+ // }.bind(this), this.options.hide_timeout);
+ // }.bind(this));
+ },
+ setText: function(text) {
+ this._title_element.update(text);
+ },
+ defaults: function() {
+ return BSEMenu.defaults;
+ },
+ inDocument: function() {
+ this._submenu.inDocument();
+ },
+ submenu: function() {
+ return this._submenu;
+ },
+ hide: function() {
+ return this._element.hide();
+ },
+ show: function() {
+ return this._element.show();
+ }
+});
+
+// an item in a drop-down menu
+BSEMenu.Item = Class.create({
+ initialize: function(options) {
+ this.original_options = options;
+ this.options = Object.extend(
+ Object.extend({}, this.defaults()), options);
+ this._make_element();
+ this.original_options.object = this;
+ },
+ element: function() {
+ return this._element;
+ },
+ submenu: function() {
+ return this._submenu;
+ },
+ setSubmenu: function(submenu) {
+ if (this._submenu) {
+ this._element.replaceChild(submenu.element(), this._submenu.element());
+ }
+ else {
+ this._element.appendChild(submenu.element());
+ }
+ this._submenu = submenu;
+ },
+ defaults: function() {
+ return BSEMenu.Item.defaults;
+ },
+ setChecked: function(checked) {
+ if (checked) {
+ this._element.removeClassName(this.options.unchecked_class);
+ this._element.addClassName(this.options.checked_class);
+ }
+ else {
+ this._element.removeClassName(this.options.checked_class);
+ this._element.addClassName(this.options.unchecked_class);
+ }
+ this._checked = checked;
+ },
+ checked: function() {
+ return this._checked;
+ },
+ setDisabled: function(disabled) {
+ if (disabled) {
+ this._element.addClassName(this.options.disabled_class);
+ }
+ else {
+ this._element.removeClassName(this.options.disabled_class);
+ }
+ this._disabled = disabled;
+ },
+ disabled: function() {
+ return this._disabled;
+ },
+ setText: function(text) {
+ this._link.update(text);
+ },
+ _make_element: function() {
+ this._element = new Element("li");
+ this._wrapper = new Element("span");
+ this._element.appendChild(this._wrapper);
+ this._link = new Element("span", { className: this.options.item_class });
+ this._link.update(this.options.text);
+ this._link.observe("click", this._onclick.bind(this));
+ this._wrapper.appendChild(this._link);
+ if (this.options.item_class)
+ this._element.addClassName(this.options.item_class);
+ if (this.options.id)
+ this._element.id = this.options.id;
+
+ this.options.widgets.each(function(w) {
+ var ele = new Element("span", { className: w.className });
+ ele.observe("click", function(event, w) {
+ event.stop();
+ w.onClick();
+ }.bindAsEventListener(this, w));
+ this._wrapper.appendChild(ele);
+ }.bind(this));
+
+ if (this.options.check)
+ this.setChecked(this.options.checked);
+ if (this.options.separate)
+ this._element.addClassName(this.options.separate_class);
+ this.setDisabled(this.options.disabled);
+
+ if (this.options.submenu) {
+ this._submenu = new this.options.submenu_class(this.options.submenu);
+ this._element.appendChild(this._submenu.element());
+ }
+ },
+ _onclick: function(ev) {
+ ev.stop();
+ if (!this._disabled && this.options.onClick)
+ this.options.onClick(this.original_options);
+ },
+ inDocument: function() {
+ if (this._submenu)
+ this._submenu.inDocument();
+ }
+});
+
+// a drop-down menu, either from the menu bar, or as a submenu of an item
+BSEMenu.SubMenu = Class.create({
+ initialize: function(options) {
+ this.options = Object.extend(
+ Object.extend({}, this.defaults()), options);
+ this._make_element();
+ },
+ items: function() {
+ return this._items;
+ },
+ element: function() {
+ return this._element;
+ },
+ _make_element: function() {
+ if (this.options.element) {
+ this._element = this.options.element;
+ }
+ else {
+ var ele = new Element("ul");
+ this._items = [];
+ var items = this.options.items;
+ for (i = 0; i < items.length; ++i) {
+ var item = this._make_item(items[i]);
+ ele.appendChild(item.element());
+ this._items.push(item);
+ }
+ this._element = ele;
+ }
+ this._element.hide();
+ },
+ _make_item: function(options) {
+ var item = new this.options.item_class(options);
+
+ item._element.observe("mouseover", function(ev, item) {
+ if (this._shown_submenu) {
+ if (item.submenu() && item.submenu() == this._shown_submenu)
+ return;
+ this._shown_submenu.hide();
+ delete this._shown_submenu;
+ }
+ if (item.submenu()) {
+ item.submenu().show();
+ this._shown_submenu = item.submenu();
+ }
+ }.bindAsEventListener(this, item));
+
+ return item;
+ },
+ defaults: function() {
+ return BSEMenu.SubMenu.defaults;
+ },
+ inDocument: function() {
+ this._items.each(function(item) { item.inDocument() });
+ },
+ show: function() {
+ this._element.show();
+ },
+ hide: function() {
+ this._element.hide();
+ if (this._shown_submenu) {
+ this._shown_submenu.hide();
+ delete this._shown_submenu;
+ }
+ }
+});
+
+BSEMenu.OrderedSubMenu = Class.create(BSEMenu.SubMenu, {
+ //_make_element: function($super) {
+ // $super();
+
+ //},
+ defaults: function($super) {
+ return Object.extend(
+ Object.extend({}, $super()),
+ {
+ item_class: BSEMenu.OrderedItem
+ });
+ },
+ inDocument: function($super) {
+ $super();
+
+ var item_eles = [];
+ for (var i = 0; i < this._items.length; ++i) {
+ item_eles.push(this._items[i].move_handle());
+ }
+ Sortable.create(this._element.identify(), {
+ handles: item_eles
+ });
+ }
+});
+
+BSEMenu.OrderedItem = Class.create(BSEMenu.Item, {
+ defaults: function($super) {
+ return Object.extend(
+ Object.extend({}, $super()),
+ {
+ move_handle_class: "move",
+ move_handle_text: "Move"
+ });
+ },
+ _make_element: function($super) {
+ $super();
+
+ var handle = new Element("span", { className: this.options.move_handle_class });
+ handle.update(this.options.move_handle_text);
+ // avoid sending a click through
+ handle.observe("click", function(ev) { ev.stop; });
+ this._wrapper.appendChild(handle);
+ this._handle = handle;
+ },
+ move_handle: function() {
+ return this._handle;
+ }
+});
+
+BSEMenu.defaults = {
+ current: false,
+ current_class: "current",
+ submenu_class: BSEMenu.SubMenu,
+ title_class: "item",
+ hide_timeout: 750
+};
+
+BSEMenu.SubMenu.defaults = {
+ items: [],
+ item_class: BSEMenu.Item
+};
+
+BSEMenu.Item.defaults = {
+ check: false,
+ separate: false,
+ disabled: false,
+ name: "",
+ checked_class: "checked",
+ unchecked_class: "unchecked",
+ separate_class: "separate",
+ disabled_class: "disabled",
+ submenu_class: BSEMenu.SubMenu,
+ item_class: "item",
+ widgets: []
+};
\ No newline at end of file
--- /dev/null
+var BSEValidator = Class.create({
+ initialize: function(options) {
+ this.options = Object.extend(
+ Object.extend({}, this.defaults()));
+ },
+ _validator: function(rule) {
+ return new BSEValidator.Rules[rule];
+ },
+ _format: function(message, field) {
+ return message.replace(/\$n/, field.description());
+ },
+ validate_one: function(field, fields, errors) {
+ if (field.required() && !field.has_value()) {
+ errors.set(field.name(), this._format(this.options.required_error, field));
+ return false;
+ }
+ var rules = field.rules();
+ var value = field.value();
+ if (typeof(rules) == "string") {
+ rules = rules.split(/;/);
+ }
+ for (var i = 0; i < rules.length; ++i) {
+ var rule = rules[i];
+ if (rule == "")
+ continue;
+ var rest = "";
+ var m = /^([0-9a-z_]+):(.*)$/.exec(rule);
+ if (m) {
+ rule = m[1];
+ rest = m[2];
+ }
+ var cls = this._validator(rule);
+ try {
+ var result = cls.test(value, this.options, rest, fields);
+ try {
+ field.set_object(result);
+ }
+ catch(e) {
+ // ignore
+ }
+ }
+ catch (e) {
+ errors.set(field.name(), e.message.replace(/\$n/, field.description()));
+ return false;
+ }
+ }
+ return true;
+ },
+ validate: function(fields, errors) {
+ fields.each(function(fields, errors, entry) {
+ this.validate_one(entry.value, fields, errors);
+ }.bind(this, fields, errors));
+
+ return errors.values().length == 0;
+ },
+ defaults: function() {
+ return BSEValidator.defaults;
+ }
+});
+
+BSEValidator.defaults = {
+ required_error: "$n is required"
+};
+
+BSEValidator.Rules = {};
+
+BSEValidator.Rules.Base = Class.create({
+});
+
+BSEValidator.Rules["date"] = Class.create(BSEValidator.Rules.Base, {
+ _days: [ 31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 ],
+ _days_in_month: function(year, month) {
+ if (month == 2) {
+ if (year % 4 == 0 && year % 100 != 0 || year % 400 == 0)
+ return 29;
+ else
+ return 28;
+ }
+ else {
+ return this._days[month-1];
+ }
+ },
+ _parse_limit: function(limit) {
+ var m = /^([0-9]+)[^0-9]([0-9]+)[^0-9]([0-9]+)$/.exec(limit);
+ if (m) {
+ // yyyy-mm-dd
+ return new Date(parseInt(m[1]), parseInt(m[2])-1, parseInt(m[3]));
+ }
+
+ var base = new Date();
+ m = /^([+-][0-9]+)y/.match(limit);
+ if (m) {
+ base.setFullYear(base.getFullYear() + parseInt(m[1]));
+ return base;
+ }
+
+ m = /^([+-][0-9]+)y/.match(limit);
+ if (m) {
+ // adjust by ms/day
+ base.setMilliseconds(base.valueOf() + parseInt(m[1]) & 86400000);
+ return base;
+ }
+
+ throw new Error("Cannot parse date limit " + limit);
+ },
+ default_options: function() {
+ return BSEValidator.Rules["date"].defaults;
+ },
+ min_date: function() {
+ return null;
+ },
+ max_date: function() {
+ return null;
+ },
+ test: function(value, options, rest) {
+ options = Object.extend(Object.extend({}, this.default_options()), options);
+ var m = options.date_re.exec(value);
+ if (!m)
+ throw new Error(options.date_format_error);
+
+ var day, month, year;
+ var format = options.date_order.split("");
+ for (var i = 0; i < format.length; ++i) {
+ switch (format[i]) {
+ case "d":
+ day = parseInt(m[1+i]);
+ break;
+ case "m":
+ month = parseInt(m[1+i]);
+ break;
+ case "y":
+ year = parseInt(m[1+i]);
+ break;
+ }
+ }
+ if (month < 1 || month > 12)
+ throw new Error(options.date_month_range_error);
+ if (day < 1 || day > this._days_in_month(year, month))
+ throw new Error(options.date_day_range_error);
+
+ var min_date = this.min_date();
+ if (min_date != null) {
+ var min = this._parse_limit(min_date);
+ if (min.getTime() > result.getTime())
+ throw new Error(options.date_too_low_error);
+ }
+ var max_date = this.max_date();
+ if (options.max_date != null) {
+ var max = this._parse_limit(max_date);
+ if (max.getTime() < result.getTime())
+ throw new Error(options.date_too_high_error);
+ }
+
+ var result = new Date(year, month-1, day);
+
+ return result;
+ }
+});
+
+BSEValidator.Rules["date"].defaults = {
+ date_re: /^\s*([0-9]+)[\/-]([0-9]+)[\/-]([0-9]+)\s*/,
+ date_order: "dmy",
+ date_format_error: "$n must be dd/mm/yyyy",
+ date_month_range_error: "Month out of range for $n",
+ date_day_range_error: "Day out of range for $n",
+ date_too_high_error: "$n too late",
+ date_too_low_error: "$n too early"
+};
+
+BSEValidator.Rules["future"] = Class.create(BSEValidator.Rules["date" ], {
+ min_date: function() {
+ var date = new Date();
+ date.setHours(0, 0, 0, 0);
+ return date;
+ },
+ default_options: function() {
+ return BSEValidator.Rules["future"].defaults;
+ }
+});
+
+BSEValidator.Rules["future"].defaults =
+ Object.extend({
+ date_too_low_error: "$n must be be in the future"
+ }, BSEValidator.Rules["date"].defaults);
+
+BSEValidator.Rules["confirm"] = Class.create(BSEValidator.Rules.Base, {
+ test: function(value, options, rest, fields) {
+ options = Object.extend(Object.extend({}, BSEValidator.Rules["confirm"].defaults), options);
+ var other = fields.get(rest).value();
+
+ if (value != other) {
+ var msg = options.confirm_error;
+ msg = msg.replace(/\$o/, fields.get(rest).description());
+ throw Error(msg);
+ }
+
+ return value;
+ }
+});
+
+BSEValidator.Rules["confirm"].defaults = {
+ confirm_error: "$n must be the same as $o"
+};
+
--- /dev/null
+<html>
+<head>
+<title>BSEValidate tests</title>
+<script src="test.js" type="text/javascript"></script>
+<script src="../site/htdocs/js/prototype.js" type="text/javascript"></script>
+<script src="../site/htdocs/js/bse_validate.js" type="text/javascript"></script>
+<script src="01validate.js" type="text/javascript"></script>
+<link rel="stylesheet" type="text/css" href="tests.css" />
+</head>
+<body>
+<div id="tests"></div>
+</body>
+</html>
--- /dev/null
+function validation_check(test, validator, checker) {
+ if (test.exception) {
+ var msg = ok_exception(function(validator, value) {
+ validator.test(value);
+ }.bind(this, validator, test.value), "parse "+test.name, test.message);
+ if (msg) {
+ like(msg, test.message, "check message " + test.name);
+ }
+ else {
+ ok(false, test.name + " no message");
+ }
+ }
+ else {
+ var a_parsed = ok_noexception(function(validator, value){
+ return validator.test(value, {});
+ }.bind(this, validator, test.value), "parse "+test.name);
+ checker(a_parsed, test);
+ }
+}
+
+document.observe("dom:loaded", function() {
+ plan(55);
+ diag("Date validation");
+ var date_val = new BSEValidator.Rules["date"]();
+ ok(date_val, "make date validator");
+ var date_tests =
+ [
+ {
+ name: "birthday",
+ value: "30/12/1967",
+ exception: false,
+ year: 1967,
+ month: 12,
+ day: 30
+ },
+ {
+ name: "birthday with spaces",
+ value: " 30/12/1967 ",
+ exception: false,
+ year: 1967,
+ month: 12,
+ day: 30
+ },
+ {
+ name: "low month",
+ value: "30/0/1967",
+ exception: true,
+ message: /Month out of range/
+ },
+ {
+ name: "low month in range",
+ value: "30/1/1967",
+ exception: false,
+ year: 1967,
+ month: 1,
+ day: 30
+ },
+ {
+ name: "high month",
+ value: "30/13/1967",
+ exception: true,
+ message: /Month out of range/
+ },
+ {
+ name: "high month in range",
+ value: "30/12/1967",
+ exception: false,
+ year: 1967,
+ month: 12,
+ day: 30
+ },
+ {
+ name: "high day normal",
+ value: "32/12/1967",
+ exception: true,
+ message: /Day out of range/
+ },
+ {
+ name: "high day normal in range",
+ value: "31/12/1967",
+ exception: false,
+ year: 1967,
+ month: 12,
+ day: 31
+ },
+ {
+ name: "high day non-leap",
+ value: "29/2/1970",
+ exception: true,
+ message: /Day out of range/
+ },
+ {
+ name: "high day non-leap in range",
+ value: "28/2/1970",
+ exception: false,
+ year: 1970,
+ month: 2,
+ day: 28
+ },
+ {
+ name: "high day leap",
+ value: "30/2/1980",
+ exception: true,
+ message: /Day out of range/
+ },
+ {
+ name: "high day leap in range",
+ value: "29/2/1980",
+ exception: false,
+ year: 1980,
+ month: 2,
+ day: 29
+ },
+ {
+ name: "low day",
+ value: "0/1/1967",
+ exception: true,
+ message: /Day out of range/
+ },
+ {
+ name: "low day in range",
+ value: "1/1/1967",
+ exception: false,
+ year: 1967,
+ month: 1,
+ day: 1
+ },
+ {
+ name: "bad format",
+ value: "1/1/",
+ exception: true,
+ message: /must be dd\/mm\/yyyy$/
+ }
+ ];
+ for (var i = 0; i < date_tests.length; ++i) {
+ var test = date_tests[i];
+ validation_check(test, date_val, function(a_date, test) {
+ is(a_date.getFullYear(), test.year, test.name + " year");
+ is(a_date.getMonth(), test.month-1, test.name + " month");
+ is(a_date.getDate(), test.day, test.name + " day");
+ });
+ }
+
+ var TestField = Class.create({
+ initialize: function(options) {
+ this.options = Object.extend({ required: false, rules: [] }, options);
+ },
+ value: function() {
+ return this.options.value;
+ },
+ description: function() {
+ return this.options.description
+ },
+ name: function() {
+ return this.options.name;
+ },
+ rules: function() {
+ return this.options.rules;
+ },
+ has_value: function() {
+ return /\S/.test(this.value());
+ },
+ required: function() {
+ return this.options.required;
+ }
+ });
+
+ { // required
+ var fields = new Hash({
+ nr1: new TestField({
+ name: "nr1",
+ value: "",
+ description: "NotRequired1"
+ }),
+ nr2: new TestField({
+ name: "nr2",
+ value: " ",
+ description: "NotRequired2"
+ }),
+ r1: new TestField({
+ name: "r1",
+ value: "",
+ required: true,
+ description: "Required1"
+ }),
+ r2: new TestField({
+ name: "r2",
+ value: " ",
+ required: true,
+ description: "Required2"
+ }),
+ r3: new TestField({
+ name: "r3",
+ value: "x",
+ required: true,
+ description: "Required3"
+ })
+ });
+ var val = new BSEValidator();
+ var errors = new Hash();
+ ok(!val.validate(fields, errors), "should fail validation");
+ is(errors.get("nr1"), null, "no error for nr1");
+ is(errors.get("nr2"), null, "no error for nr2");
+ is(errors.get("r1"), "Required1 is required", "check error for r1");
+ is(errors.get("r2"), "Required2 is required", "check error for r2");
+ is(errors.get("r3"), null, "no error for r3");
+ }
+
+ {
+ // confirm
+ var fields = new Hash({
+ password: new TestField({
+ name: "password",
+ value: "abc",
+ description: "Password",
+ rules: "",
+ required: true
+ }),
+ confirm: new TestField({
+ name: "confirm",
+ value: "abc",
+ description: "Confirm",
+ rules: "confirm:password",
+ }),
+ confirm2: new TestField({
+ name: "confirm2",
+ value: "abcd",
+ description: "Confirm2",
+ rules: "confirm:password",
+ })
+ });
+ var val = new BSEValidator();
+ var errors = new Hash();
+ val.validate(fields, errors);
+ is(errors.get("confirm"), null, "should be no error for confirm");
+ is(errors.get("confirm2"), "Confirm2 must be the same as Password",
+ "confirm2 should have an error");
+ }
+
+ tests_done();
+});
\ No newline at end of file
--- /dev/null
+<html>
+<head>
+<title>BSEMenu tests</title>
+<script src="test.js" type="text/javascript"></script>
+<script src="../site/htdocs/js/prototype.js" type="text/javascript"></script>
+<script src="../site/htdocs/js/bse_menu.js" type="text/javascript"></script>
+<script src="10menu.js" type="text/javascript"></script>
+<link rel="stylesheet" type="text/css" href="tests.css" />
+<link rel="stylesheet" type="text/css" href="menu.css" />
+</head>
+<body>
+<menu id="menu"><ul id="nav"></ul></menu>
+<div id="tests"></div>
+</body>
+</html>
--- /dev/null
+document.observe("dom:loaded", function() {
+ plan(7);
+
+ ok(BSEMenu, "have a BSEMenu class");
+ ok(BSEMenu.Item, "have a BSEMenu.Item class");
+ ok(BSEMenu.SubMenu, "have a BSEMenu.SubMenu class");
+
+ var clicked = function(item) { diag("Item " + item.text + " clicked") };
+
+ var bar = $("nav");
+ {
+ var m1 = new BSEMenu({
+ title: "Test 1",
+ current: true,
+ items: [
+ {
+ text: "Item A",
+ separator: true,
+ onClick: clicked
+ },
+ {
+ text: "Item B",
+ onClick: clicked
+ }
+ ]
+ });
+ ok(m1, "made a menu");
+ bar.appendChild(m1.element());
+ }
+ {
+ var itemfb = {
+ text: "Item F b",
+ check: true,
+ checked: true
+ };
+ itemfb.onClick = function() {
+ var check = !this.object.checked();
+ this.object.setChecked(check);
+ diag("Item F b clicked, now " + (check ? "" : "not ") + "checked");
+ }.bind(itemfb);
+ var items = [
+ {
+ text: "Item E",
+ separate: true,
+ onClick: clicked
+ },
+ {
+ text: "Item F",
+ submenu: {
+ items: [
+ {
+ text: "Item F a",
+ disabled: true,
+ onClick: function() { diag("item F a shouldn't be clickable") }
+ },
+ itemfb
+ ]
+ }
+ }
+ ];
+ var m2 = new BSEMenu({
+ title: "Test 2",
+ items: items
+ });
+ bar.appendChild(m2.element());
+ ok(m2, "made second menu, with submenu");
+ }
+ {
+ var itemM = {
+ text: "item M",
+ onClick: clicked
+ };
+ var itemN = {
+ text: "item N",
+ onClick: clicked
+ };
+ var m3 = new BSEMenu({
+ title: "Test 3",
+ items: [
+ itemM,
+ itemN
+ ]
+ });
+ ok(m3, "made third menu");
+ bar.appendChild(m3.element());
+ m3.setText("Test 3 modified");
+ itemN.object.setText("Item N modified");
+ itemN.object.setSubmenu(new BSEMenu.SubMenu({
+ items: [
+ {
+ text: "item Na added",
+ onClick: clicked
+ },
+ {
+ text: "item Nb added",
+ onClick: clicked
+ },
+ {
+ text: "item Nc added",
+ onClick: clicked
+ },
+ {
+ text: "item Nd added",
+ onClick: clicked
+ },
+ {
+ text: "item Ne added",
+ onClick: clicked
+ }
+ ]
+ }));
+ }
+ {
+ var subT = new Element("ul");
+ var li1 = new Element("li");
+ li1.appendChild(new Element("input", { type: "text" }));
+ subT.appendChild(li1);
+ var li2 = new Element("li");
+ var a2 = new Element("a", { href: "#" });
+ a2.update("Foo");
+ li2.appendChild(a2);
+ subT.appendChild(li2);
+
+ var itemT = {
+ text: "Articles",
+ submenu: {
+ element: subT
+ }
+ };
+ var m4 = new BSEMenu({
+ title: "Articles",
+ items: [
+ {
+ text: "New article",
+ onClick: clicked
+ },
+ itemT
+ ]
+ });
+ bar.appendChild(m4.element());
+ ok(m4, "made m4");
+ }
+ tests_done();
+});
+
--- /dev/null
+html,body,div,span,applet,object,iframe,h1,h2,h3,h4,h5,h6,p,blockquote,pre,a,abbr,acronym,address,big,cite,code,del,dfn,em,font,img,ins,kbd,q,s,samp,small,strike,strong,sub,sup,tt,var,b,u,i,center,dl,dt,dd,ol,ul,li,fieldset,form,label,legend,table,caption,tbody,tfoot,thead,tr,th,td{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent}body{line-height:1}ol,ul{list-style:none}blockquote,q{quotes:none}blockquote:before,blockquote:after,q:before,q:after{content:'';content:none}:focus{outline:0}ins{text-decoration:none}del{text-decoration:line-through}table{border-collapse:collapse;border-spacing:0}
+
+html {
+ text-align: center;
+ width: 100%;
+}
+body {
+ width: 100%;
+ text-align: left;
+ margin: 0 auto;
+ padding: 0;
+ background-color: #666;
+ font: normal normal 0.75em/1.5 "Lucida Sans Unicode", "Lucida Grande", "Helvetica Neue", Helvetica, Arial, Helvetica, sans-serif;
+ text-shadow: 0 0.083em 0 #fff;
+ color: #585858;
+ position: relative;
+}
+
+
+#menu {
+ margin: 0;
+ padding: 0;
+ line-height: 1;
+ width: 100%;
+ height: 4em;
+
+ -webkit-box-shadow: 0 0.1em 0.5em rgba(0,0,0,0.4);
+ -moz-box-shadow: 0 0.1em 0.5em rgba(0,0,0,0.4);
+ box-shadow: 0 0.1em 0.5em rgba(0,0,0,0.4);
+
+ background: #8b8b8b; /* for non-css3 browsers */
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#a9a9a9', endColorstr='#7a7a7a'); /* for IE */
+ background: -webkit-gradient(linear, left top, left bottom, from(#a9a9a9), to(#7a7a7a)); /* for webkit browsers */
+ background: -moz-linear-gradient(top, #a9a9a9, #7a7a7a); /* for firefox 3.6+ */
+
+ border-bottom: solid 0.1em #6d6d6d;
+
+ z-index: 1000;
+ position: fixed;
+ top: 0;
+ overflow: visible;
+}
+#nav {
+ position: relative;
+ z-index: 1001;
+ overflow: visible;
+}
+#nav li {
+ margin: 0 0 0 1em;
+ padding: 0.75em 0;
+ float: left;
+ position: relative;
+ list-style: none;
+}
+#nav li li.separate {
+ border-bottom: 0.1em solid #b4b4b4;
+}
+#nav li li.separate+li {
+ border-top: 0.1em solid #fff;
+}
+/* main level link */
+#nav a {
+ position: relative;
+ line-height: 1.5;
+ font-weight: bold;
+ color: #e7e5e5;
+ text-decoration: none;
+ display: block;
+ margin: -0.1em 0;
+ padding: 0.5em 1.5em;
+ -webkit-border-radius: 1.5em;
+ -moz-border-radius: 1.5em;
+ border-radius: 1.5em;
+ text-shadow: 0 0.1em 0.1em rgba(0,0,0,0.5);
+}
+/* main level link hover */
+#nav .current > a, #nav li:hover > a {
+ background: #d1d1d1; /* for non-css3 browsers */
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ebebeb', endColorstr='#a1a1a1'); /* for IE */
+ background: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#a1a1a1)); /* for webkit browsers */
+ background: -moz-linear-gradient(top, #ebebeb, #a1a1a1); /* for firefox 3.6+ */
+
+ color: #444;
+ border-top: solid 0.1em #f8f8f8;
+ -webkit-box-shadow: 0 0.1em 0.1em rgba(0,0,0,0.2);
+ -moz-box-shadow: 0 0.1em 0.1em rgba(0,0,0,0.2);
+ box-shadow: 0 0.1em 0.1em rgba(0,0,0,0.2);
+ text-shadow: 0 0.1em 0.1em rgba(255,255,255,1);
+}
+/* sub levels link hover */
+#nav ul li:hover a, #nav li:hover li a {
+ background: none;
+ border: none;
+ color: #666;
+ -webkit-box-shadow: none;
+ -moz-box-shadow: none;
+ box-shadow: none;
+ margin: 0;
+}
+#nav ul a:hover {
+ background: #0399d4 !important; /* for non-css3 browsers */
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#04acec', endColorstr='#0186ba'); /* for IE */
+ background: -webkit-gradient(linear, left top, left bottom, from(#04acec), to(#0186ba)) !important; /* for webkit browsers */
+ background: -moz-linear-gradient(top, #04acec, #0186ba) !important; /* for firefox 3.6+ */
+
+ color: #fff !important;
+ -webkit-border-radius: 0;
+ -moz-border-radius: 0;
+ border-radius: 0;
+ text-shadow: 0 0.1em 0.1em rgba(0,0,0,0.1);
+}
+/* level 2 list */
+#nav ul {
+ background: #ddd; /* for non-css3 browsers */
+ filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffff', endColorstr='#cfcfcf'); /* for IE */
+ background: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#cfcfcf)); /* for webkit browsers */
+ background: -moz-linear-gradient(top, #fff, #cfcfcf); /* for firefox 3.6+ */
+
+ z-index: 100;
+ display: none;
+ margin: 0;
+ padding: 0;
+ width: 18em;
+ position: absolute;
+ top: 3.75em;
+ left: 0;
+ border: solid 0.1em #b4b4b4;
+ -webkit-border-radius: 0.5em;
+ -moz-border-radius: 0.5em;
+ -o-border-radius: 0.5em;
+ border-radius: 0.5em;
+ -webkit-box-shadow: 0 0.16em 0.5em rgba(0,0,0,0.3);
+ -moz-box-shadow: 0 0.16em 0.5em rgba(0,0,0,0.3);
+ box-shadow: 0 0.16em 0.5em rgba(0,0,0,0.3);
+}
+#nav ul ul {
+ max-height: 30.16em;
+ overflow-y: auto;
+ overflow-x: visible;
+}
+#nav ul.full {
+ width: 27em;
+}
+
+/* @group widgets */
+
+#nav li>a>span {
+ position: absolute;
+ top: 0;
+ left: 0;
+ width: 1.2em;
+ height: 100%;
+ text-indent: -9999em;
+ background: transparent url(../images/move_dk.png) no-repeat 50% 50%;
+ background-size: 1.2em;
+ display: none;
+}
+#nav li>a>span+span {
+ background-image: url(../images/delete_dk.png);
+ background-size: 1.5em;
+ width: 1.5em;
+ left: auto;
+ right: 0.5em;
+}
+#nav li:hover>a>span {
+ display: block;
+}
+
+/* @end */
+
+/* @group dropdown */
+
+#nav li:hover > ul {
+ display: block;
+}
+#nav ul li {
+ float: none;
+ margin: 0;
+ padding: 0;
+}
+#nav ul a {
+ font-weight: normal;
+ text-shadow: 0 0.1em 0.1em rgba(255,255,255,0.9);
+}
+
+/* @end */
+
+/* @group level 3+ list */
+
+#nav ul ul {
+ left: 17.5em;
+ top: -0.1em;
+}
+/*#nav ul ul.full {
+ left: 35.5em;
+}*/
+
+/* @end */
+
+/* @group rounded corners for first and last child */
+
+#nav ul li:first-child > a {
+ -webkit-border-top-left-radius: 0.5em;
+ -moz-border-radius-topleft: 0.5em;
+ border-top-left-radius: 0.5em;
+ -webkit-border-top-right-radius: 0.5em;
+ -moz-border-radius-topright: 0.5em;
+ border-top-right-radius: 0.5em;
+}
+#nav ul li:last-child > a {
+ -webkit-border-bottom-left-radius: 0.5em;
+ -moz-border-radius-bottomleft: 0.5em;
+ border-bottom-left-radius: 0.5em;
+ -webkit-border-bottom-right-radius: 0.5em;
+ -moz-border-radius-bottomright: 0.5em;
+ border-bottom-right-radius: 0.5em;
+}
+
+/* @end */
+
+/* @end */
+
+#nav input {
+ border: none;
+ padding: 0px;
+}
+
+#tests { margin-top: 10em; }
\ No newline at end of file
--- /dev/null
+function _test_out(text, cls) {
+ var test_ele = document.getElementById('tests');
+ if (test_ele) {
+ var div_tag = document.createElement("div");
+ if (cls != null)
+ div_tag.className = cls;
+ var t = document.createTextNode(text);
+ div_tag.appendChild(t);
+ test_ele.appendChild(div_tag);
+ //var open_tag;
+ //if (cls == null)
+ //open_tag = "<div>";
+ //else
+ //open_tag = '<div class="' + cls + '">';
+ //test_ele.innerHTML = test_ele.innerHTML + open_tag + escape_html(text) + "</div>";
+ }
+}
+
+var test_ok = 0;
+var test_fails = 0;
+var test_skips = 0;
+var test_num = 1;
+var test_count;
+
+function plan(count) {
+ test_count = count;
+ _test_out("1.." + count, "plan");
+}
+
+function ok(test, comment) {
+ if (test) {
+ ++test_ok;
+ _test_out("ok " + test_num + " # " + comment, "ok");
+ }
+ else {
+ _test_out("not ok " + test_num + " # " + comment, "fail");
+ ++test_fails;
+ }
+ ++test_num;
+ return test;
+}
+
+function skip(text, count) {
+ if (count == null)
+ count = 1;
+ for (var i = 0; i < 1; ++i) {
+ _test_out("ok " + test_num + " SKIP text", "skip");
+ ++test_skips;
+ ++test_num;
+ }
+}
+
+function is(left, right, comment) {
+ var test_ok = ok(left == right, comment);
+ if (!test_ok) {
+ _test_out("# should match", "fail");
+ _test_out("# left :'"+encodeURI(left)+"'", "fail");
+ _test_out("# right:'"+encodeURI(right)+"'", "fail");
+ }
+ return test_ok;
+}
+
+function isnt(left, right, comment) {
+ var test_ok = ok(left != right, comment);
+ if (!test_ok) {
+ _test_out("# shouldn't match", "fail");
+ _test_out("# left :"+encodeURI(left), "fail");
+ _test_out("# right:"+encodeURI(right), "fail");
+ }
+ return test_ok;
+}
+
+function like(value, re, comment) {
+ var test_ok = ok(value != null && value.match(re), comment);
+ if (!test_ok) {
+ _test_out("# should match", "fail");
+ _test_out("# value:"+value, "fail");
+ _test_out("# regexp:"+re, "fail");
+ }
+ return test_ok;
+}
+
+function diag(text) {
+ _test_out("# " + text, "diag");
+}
+
+function tests_done() {
+ var cls = "ok";
+ var top_class = "passed";
+ if (test_count != null) {
+ if (test_count != test_num-1) {
+ _test_out("Expected "+test_count+" tests but saw "+(test_num-1), "fail");
+ cls = "fail";
+ top_class = "failed";
+ }
+ }
+ if (test_fails != 0) {
+ cls = "fail";
+ top_class = "failed";
+ }
+ _test_out("Summary: "+(test_num-1)+" tests, "+test_ok+" Ok, "+test_fails+" failures " + test_skips + " skips", cls);
+ var test_ele = document.getElementById('tests');
+ test_ele.className = top_class;
+}
+
+function ok_noexception(f, text) {
+ var result;
+ try {
+ result = f();
+ }
+ catch (e) {
+ ok(false, text);
+ diag(e.message);
+ return false;
+ }
+
+ ok(true, text);
+ return result;
+}
+
+function ok_exception(f, text, match) {
+ var result;
+ try {
+ f();
+ }
+ catch (e) {
+ ok(true, text);
+ diag(e.message);
+ return e.message;
+ }
+
+ ok(false, text);
+ return false;
+}
--- /dev/null
+#tests {
+ font: 10px Arial;
+}
+
+#tests.passed {
+ background-color: #CFC;
+}
+
+#tests.failed {
+ background-color: #FCC;
+}
+
+.ok {
+ color: #080;
+}
+
+.fail {
+ color: #F00;
+ font-weight: bold;
+}
+
+.skip, .plan, .diag {
+ color: #444;
+}