Initial commit
|
@ -0,0 +1,9 @@
|
|||
Package: hedera-web
|
||||
Version: 1.0-1
|
||||
Architecture: all
|
||||
Maintainer: Juan Ferrer Toribio <juan@verdnatura.es>
|
||||
Depends: php5-mysql, php5-imap
|
||||
Section: misc
|
||||
Priority: optional
|
||||
Description: Verdnatura's web page
|
||||
Verdnatura's web page.
|
|
@ -0,0 +1,9 @@
|
|||
Alias /hedera-web /usr/share/hedera-web/
|
||||
|
||||
<Directory /usr/share/hedera-web/>
|
||||
Options Indexes
|
||||
Options +FollowSymLinks
|
||||
AllowOverride None
|
||||
Order Allow,Deny
|
||||
Allow From All
|
||||
</Directory>
|
|
@ -0,0 +1,14 @@
|
|||
<Files *.css>
|
||||
SetOutputFilter DEFLATE
|
||||
</Files>
|
||||
<Files *.js>
|
||||
SetOutputFilter DEFLATE
|
||||
</Files>
|
||||
<Files *.php>
|
||||
SetOutputFilter DEFLATE
|
||||
</Files>
|
||||
<FilesMatch "\.(ttf|otf|eot|woff)$">
|
||||
<IfModule mod_headers.c>
|
||||
Header set Access-Control-Allow-Origin "*"
|
||||
</IfModule>
|
||||
</FilesMatch>
|
|
@ -0,0 +1,54 @@
|
|||
<?php
|
||||
|
||||
require_once ('php/web/hedera.php');
|
||||
require_once ('php/web/json.php');
|
||||
|
||||
Hedera::init ();
|
||||
JsonLib::init ();
|
||||
|
||||
if (Hedera::login ())
|
||||
{
|
||||
// Checking the client version
|
||||
|
||||
if (isset ($_COOKIE['hedera_version']))
|
||||
{
|
||||
$clientVersion = $_COOKIE['hedera_version'];
|
||||
set_type ($clientVersion, TYPE_DOUBLE);
|
||||
|
||||
if ($clientVersion < $_SESSION['version'])
|
||||
{
|
||||
$row = Hedera::$sysConn->getRow ('SELECT critical, changelog FROM version LIMIT 1');
|
||||
|
||||
if (!$row || $row['critical'])
|
||||
Error::trigger ('Version', 'criticalVersion', $row['changelog']);
|
||||
else
|
||||
Error::trigger ('Version', 'newVersion', $row['changelog']);
|
||||
}
|
||||
}
|
||||
|
||||
// Getting the action
|
||||
|
||||
$action = NULL;
|
||||
|
||||
if (isset ($_REQUEST['action']))
|
||||
$action = $_REQUEST['action'];
|
||||
|
||||
if ($action)
|
||||
{
|
||||
$actionFile = 'ajax/'. $action .'.php';
|
||||
|
||||
if (file_exists ($actionFile))
|
||||
{
|
||||
Vn\Locale::loadFile ('ajax/'. $action);
|
||||
require_once ($actionFile);
|
||||
actionActivate (Hedera::$conn);
|
||||
}
|
||||
else
|
||||
Error::trigger ('Ajax', 'invalidAction', s('InvalidAction'));
|
||||
}
|
||||
}
|
||||
|
||||
JsonLib::sendReply ();
|
||||
Hedera::deinit ();
|
||||
|
||||
?>
|
|
@ -0,0 +1,179 @@
|
|||
<?php
|
||||
|
||||
const IMG_MAX_SIZE = 3;
|
||||
const IMG_URL_BASE = '/var/www/image/cache';
|
||||
const IMG_URL_FULL = 'full/';
|
||||
const IMG_URL_ICON = 'icon/';
|
||||
const IMG_FULL_HEIGHT = 650;
|
||||
const IMG_FULL_WIDTH = 750;
|
||||
const IMG_ICON_SIZE = 30;
|
||||
|
||||
function actionActivate ($conn)
|
||||
{
|
||||
$data = FALSE;
|
||||
|
||||
$query = "SELECT r.name = 'employee' "
|
||||
.'FROM role r JOIN user_role u ON r.id = u.role_id '
|
||||
.'WHERE u.user_id = account.user_get_id ()';
|
||||
|
||||
if (!$conn->getValue ($query))
|
||||
JsonLib::setError ('Image', 'permissionDenied', s('PermissionDenied'));
|
||||
else if (!isset ($_REQUEST['name']) || !isset ($_REQUEST['directory']))
|
||||
JsonLib::setError ('Image', 'comError', s('ComError'));
|
||||
else if (!(isset ($_FILES['image']['name']) && $_FILES['image']['name'] != ''))
|
||||
JsonLib::setError ('Image', 'fileNotChoosed', s('FileNotChoosed'));
|
||||
else if ($_FILES['image']['error'] != 0)
|
||||
JsonLib::setError ('Image', 'uploadError', s('FileUploadError'));
|
||||
else if ($_FILES['image']['size'] > IMG_MAX_SIZE * 1048576)
|
||||
JsonLib::setError ('Image', 'fileSizeError', sprintf (s('FileSizeError'), IMG_MAX_SIZE));
|
||||
else
|
||||
{
|
||||
$name = $_REQUEST['name'];
|
||||
$directory = $_REQUEST['directory'];
|
||||
$exp = '/[^a-z0-9_]/';
|
||||
|
||||
if (preg_match ($exp, $name) === 0 && preg_match ($exp, $directory) === 0)
|
||||
{
|
||||
$img = & $_FILES['image'];
|
||||
$data = TRUE;
|
||||
|
||||
switch ($img['type'])
|
||||
{
|
||||
case 'image/png':
|
||||
$image = imagecreatefrompng ($img['tmp_name']);
|
||||
break;
|
||||
case 'image/jpeg':
|
||||
$image = imagecreatefromjpeg ($img['tmp_name']);
|
||||
break;
|
||||
case 'image/gif':
|
||||
$image = imagecreatefromgif ($img['tmp_name']);
|
||||
break;
|
||||
default:
|
||||
JsonLib::setError ('Image', 'badFileFormat', s('BadFileFormat'));
|
||||
$data = FALSE;
|
||||
}
|
||||
}
|
||||
else
|
||||
JsonLib::setError ('Image', 'badFileName', s('BadFileName'));
|
||||
|
||||
if ($data)
|
||||
{
|
||||
$data = FALSE;
|
||||
|
||||
if (!$image)
|
||||
JsonLib::setError ('Image', 'openError', s('ImageOpenError'));
|
||||
else
|
||||
{
|
||||
list ($width, $height) = getimagesize ($img['tmp_name']);
|
||||
|
||||
$n = -1;
|
||||
$max_height = array ();
|
||||
$max_width = array ();
|
||||
$url = array ();
|
||||
|
||||
++$n;
|
||||
$max_height[$n] = IMG_ICON_SIZE;
|
||||
$max_width[$n] = IMG_ICON_SIZE;
|
||||
$url[$n] = IMG_URL_ICON;
|
||||
|
||||
++$n;
|
||||
$max_height[$n] = IMG_FULL_HEIGHT;
|
||||
$max_width[$n] = IMG_FULL_WIDTH;
|
||||
$url[$n] = IMG_URL_FULL;
|
||||
|
||||
for ($n = 0; $n < count ($url); $n++)
|
||||
{
|
||||
$reduce = FALSE;
|
||||
$w = $width;
|
||||
$h = $height;
|
||||
|
||||
if ($h > $max_height[$n])
|
||||
{
|
||||
$reduce = $h / $max_height[$n];
|
||||
$h = $max_height[$n];
|
||||
$w = (int) ($w / $reduce);
|
||||
}
|
||||
|
||||
if ($w > $max_width[$n])
|
||||
{
|
||||
$reduce = $w / $max_width[$n];
|
||||
$w = $max_width[$n];
|
||||
$h = (int) ($h / $reduce);
|
||||
}
|
||||
|
||||
if ($reduce !== FALSE)
|
||||
{
|
||||
$image_new = imagecreatetruecolor ($w, $h);
|
||||
imagecopyresized ($image_new, $image, 0, 0, 0, 0, $w, $h, $width, $height);
|
||||
}
|
||||
else
|
||||
$image_new = $image;
|
||||
|
||||
imagealphablending ($image_new, FALSE);
|
||||
imagesavealpha ($image_new, TRUE);
|
||||
|
||||
$dest_url = IMG_URL_BASE .'/'. $directory . '/' . $url[$n] . $name . '.png';
|
||||
$data = imagepng ($image_new, $dest_url);
|
||||
|
||||
if (!$data)
|
||||
JsonLib::setError ('Image', 'saveError', sprintf (s('FileSaveError'), $dest_url));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unlink ($img['tmp_name']);
|
||||
}
|
||||
|
||||
JsonLib::$encoding = JsonLib::ENCODING_HTML;
|
||||
JsonLib::setData ($data);
|
||||
}
|
||||
|
||||
/*
|
||||
// Script to reduce and change format for all images
|
||||
|
||||
define ('IMG_DIR_BASE', '/var/www/test/image/');
|
||||
define ('IMG_DIR_OLD', IMG_DIR_BASE.'gallery/');
|
||||
define ('IMG_DIR_NEW', IMG_DIR_BASE.'icon/');
|
||||
define ('IMG_SIZE', 85);
|
||||
|
||||
if (TRUE)
|
||||
{
|
||||
$directorio = opendir (IMG_DIR_OLD);
|
||||
$n = 1;
|
||||
|
||||
while ($filename = readdir ($directorio))
|
||||
{
|
||||
switch (strtolower(substr ($filename, -3, 3)))
|
||||
{
|
||||
case 'jpg':
|
||||
$image = imagecreatefromjpeg (IMG_DIR_OLD.$filename);
|
||||
break;
|
||||
case 'png':
|
||||
$image = imagecreatefrompng (IMG_DIR_OLD.$filename);
|
||||
break;
|
||||
}
|
||||
|
||||
if (isset ($image))
|
||||
{
|
||||
$n++;
|
||||
|
||||
list ($width, $height) = getimagesize (IMG_DIR_OLD.$filename);
|
||||
|
||||
$siz_max = ($width > $height) ? $width : $height;
|
||||
|
||||
$percent = ($siz_max > IMG_SIZE) ? IMG_SIZE / $siz_max : 1;
|
||||
|
||||
$width_new = $width * $percent;
|
||||
$height_new = $height * $percent;
|
||||
|
||||
$image_new = imagecreatetruecolor ($width_new, $height_new);
|
||||
imagecopyresized ($image_new, $image, 0, 0, 0, 0, $width_new, $height_new, $width, $height);
|
||||
imagesavealpha ($image_new, TRUE);
|
||||
imagepng ($image_new, IMG_DIR_NEW.substr ($filename, 0, -4).'.png');
|
||||
}
|
||||
}
|
||||
|
||||
JsonLib::setError ('Image', 'imagesResized', s('ImagesResized'));
|
||||
}
|
||||
*/
|
||||
?>
|
|
@ -0,0 +1,8 @@
|
|||
<?php
|
||||
|
||||
function actionActivate ($conn)
|
||||
{
|
||||
JsonLib::setData (TRUE);
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,9 @@
|
|||
<?php
|
||||
|
||||
function actionActivate ($conn)
|
||||
{
|
||||
Auth::logout ($conn);
|
||||
JsonLib::setData (TRUE);
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,88 @@
|
|||
<?php
|
||||
|
||||
class JsonResult
|
||||
{
|
||||
var $field;
|
||||
var $data;
|
||||
}
|
||||
|
||||
function actionActivate ($conn)
|
||||
{
|
||||
if (isset ($_REQUEST['sql']) && $_REQUEST['sql'] != '')
|
||||
{
|
||||
$results = array ();
|
||||
|
||||
if ($conn->multiQuery ($_REQUEST['sql']))
|
||||
do {
|
||||
$result = $conn->storeResult ();
|
||||
|
||||
if ($result !== FALSE)
|
||||
{
|
||||
$jsonResult = new JsonResult ();
|
||||
|
||||
$columns = $result->fetch_fields ();
|
||||
$jsonResult->data = array ();
|
||||
$jsonResult->field = $columns;
|
||||
|
||||
for ($i = 0; $row = $result->fetch_row (); $i++)
|
||||
{
|
||||
for ($j = 0; $j < $result->field_count; $j++)
|
||||
{
|
||||
$cell = & $row[$j];
|
||||
|
||||
if ($cell != NULL)
|
||||
{
|
||||
switch ($columns[$j]->type)
|
||||
{
|
||||
case MYSQLI_TYPE_DATE:
|
||||
case MYSQLI_TYPE_DATETIME:
|
||||
case MYSQLI_TYPE_TIMESTAMP:
|
||||
$cell = mktime
|
||||
(
|
||||
substr ($cell, 11 , 2)
|
||||
,substr ($cell, 14 , 2)
|
||||
,substr ($cell, 17 , 2)
|
||||
,substr ($cell, 5 , 2)
|
||||
,substr ($cell, 8 , 2)
|
||||
,substr ($cell, 0 , 4)
|
||||
);
|
||||
break;
|
||||
case MYSQLI_TYPE_BIT:
|
||||
set_type ($cell, TYPE_BOOLEAN);
|
||||
break;
|
||||
case MYSQLI_TYPE_TINY:
|
||||
case MYSQLI_TYPE_SHORT:
|
||||
case MYSQLI_TYPE_LONG:
|
||||
case MYSQLI_TYPE_LONGLONG:
|
||||
case MYSQLI_TYPE_INT24:
|
||||
case MYSQLI_TYPE_YEAR:
|
||||
set_type ($cell, TYPE_INTEGER);
|
||||
break;
|
||||
case MYSQLI_TYPE_FLOAT:
|
||||
case MYSQLI_TYPE_DOUBLE:
|
||||
case MYSQLI_TYPE_DECIMAL:
|
||||
case MYSQLI_TYPE_NEWDECIMAL:
|
||||
set_type ($cell, TYPE_DOUBLE);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$jsonResult->data[$i] = $row;
|
||||
}
|
||||
|
||||
$results[] = $jsonResult;
|
||||
$result->free ();
|
||||
}
|
||||
else
|
||||
$results[] = TRUE;
|
||||
}
|
||||
while ($conn->moreResults () && $conn->nextResult ());
|
||||
|
||||
JsonLib::setData ($results);
|
||||
}
|
||||
else
|
||||
JsonLib::setError ('Query', 'emptyQuery', s('EmptyQuery'));
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,66 @@
|
|||
<?php
|
||||
/**
|
||||
* Configuration file. Be careful to respect the PHP syntax.
|
||||
**/
|
||||
$conf = array (
|
||||
|
||||
/**
|
||||
* The default language.
|
||||
**/
|
||||
'defaultLang' => 'es'
|
||||
|
||||
/**
|
||||
* Compatible browsers.
|
||||
**/
|
||||
,'browser' => array
|
||||
(
|
||||
'Firefox' => 4.0
|
||||
,'Iceweasel' => 4.0
|
||||
,'IE' => 10.0
|
||||
,'Chrome' => 7.0
|
||||
,'Opera' => 11.6
|
||||
)
|
||||
|
||||
/**
|
||||
* Database parameters.
|
||||
**/
|
||||
,'db' => array
|
||||
(
|
||||
'host' => 'db.verdnatura.es'
|
||||
,'name' => 'hedera'
|
||||
,'user' => 'root'
|
||||
,'pass' => 'base64pass'
|
||||
)
|
||||
|
||||
/**
|
||||
* The guest user.
|
||||
**/
|
||||
,'guest' => array
|
||||
(
|
||||
'user' => 'visitor'
|
||||
,'pass' => 'base64pass'
|
||||
)
|
||||
|
||||
/**
|
||||
* Whether to force the use of https on the client.
|
||||
**/
|
||||
,'https' => FALSE
|
||||
|
||||
/**
|
||||
* The lifetime of the session cookie, in days.
|
||||
**/
|
||||
,'cookieLife' => 15
|
||||
|
||||
/**
|
||||
* The module to load if none is specified.
|
||||
**/
|
||||
,'defaultModule' => 'home'
|
||||
|
||||
/**
|
||||
* The directory where images are located.
|
||||
**/
|
||||
,'imageDir' => '/image/cache'
|
||||
|
||||
);
|
||||
|
||||
?>
|
|
@ -0,0 +1,16 @@
|
|||
Copyright (C) 2013 - Juan Ferrer Toribio
|
||||
|
||||
This program is free software; you can redistribute it and/or
|
||||
modify it under the terms of the GNU Lesser General Public
|
||||
License as published by the Free Software Foundation; either
|
||||
version 2.1 of the License, or (at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
Lesser General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU Lesser General Public
|
||||
License along with this program; if not, write to the Free
|
||||
Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
|
||||
02111-1307 USA.
|
|
@ -0,0 +1,9 @@
|
|||
<script>
|
||||
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
||||
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
||||
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
||||
})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
|
||||
|
||||
ga('create', 'UA-49049601-1', 'verdnatura.es');
|
||||
ga('send', 'pageview');
|
||||
</script>
|
|
@ -0,0 +1,13 @@
|
|||
<?php
|
||||
|
||||
if ($result = Hedera::$sysConn->query ('SELECT name, content FROM metatag'))
|
||||
{
|
||||
echo '<meta name="content-language" content="'.$_SESSION['lang'].'"/>'."\n\t";
|
||||
|
||||
while ($row = $result->fetch_assoc ())
|
||||
echo '<meta name="'.$row['name'].'" content="'.$row['content'].'"/>'."\n\t";
|
||||
|
||||
$result->free ();
|
||||
}
|
||||
|
||||
?>
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
/* Global */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
src: url(opensans.ttf) format('truetype');
|
||||
}
|
||||
*
|
||||
{
|
||||
font-size: 13px;
|
||||
font-family: 'Open Sans', 'Verdana', 'Sans';
|
||||
}
|
|
@ -0,0 +1,321 @@
|
|||
|
||||
/* Global */
|
||||
|
||||
@font-face {
|
||||
font-family: 'Open Sans';
|
||||
src: url(opensans.ttf) format('truetype');
|
||||
}
|
||||
body
|
||||
{
|
||||
margin: 0px;
|
||||
}
|
||||
body,
|
||||
label,
|
||||
button,
|
||||
input,
|
||||
textarea
|
||||
{
|
||||
font-size: 13px;
|
||||
font-family: 'Open Sans', 'Verdana', 'Sans';
|
||||
}
|
||||
iframe
|
||||
{
|
||||
border: 0px;
|
||||
}
|
||||
fieldset,
|
||||
div
|
||||
{
|
||||
margin: 0px;
|
||||
}
|
||||
form
|
||||
{
|
||||
padding: 0px;
|
||||
margin: 0px;
|
||||
}
|
||||
table
|
||||
{
|
||||
width: 100%;
|
||||
}
|
||||
a:link,
|
||||
a:visited,
|
||||
a:active
|
||||
{
|
||||
color: #000;
|
||||
text-decoration: none;
|
||||
}
|
||||
a:hover
|
||||
{
|
||||
text-decoration: none;
|
||||
}
|
||||
a img
|
||||
{
|
||||
padding: 1px;
|
||||
border: 1px solid #EEE;
|
||||
border-color: transparent;
|
||||
}
|
||||
a img:hover
|
||||
{
|
||||
border-color: #999;
|
||||
}
|
||||
h1, h2, h3, h4, h5, h6
|
||||
{
|
||||
margin: 0px;
|
||||
padding: 2px;
|
||||
}
|
||||
h1
|
||||
{
|
||||
font-size: 22px;
|
||||
}
|
||||
h2
|
||||
{
|
||||
font-size: 20px;
|
||||
}
|
||||
p
|
||||
{
|
||||
margin: 0px;
|
||||
margin-top: 16px;
|
||||
margin-bottom: 16px;
|
||||
}
|
||||
|
||||
/* Inputs */
|
||||
|
||||
input,
|
||||
textarea,
|
||||
button
|
||||
{
|
||||
border: 1px solid #CCD;
|
||||
margin: 2px;
|
||||
padding: 4px;
|
||||
border-radius: 2px;
|
||||
/* box-shadow: 0px 2px 2px #AAA; */
|
||||
}
|
||||
textarea
|
||||
{
|
||||
height: 40px;
|
||||
width: 200px;
|
||||
}
|
||||
input[type=submit],
|
||||
input[type=button],
|
||||
button
|
||||
{
|
||||
background-color: #EEF;
|
||||
margin: 6px;
|
||||
padding: 4px;
|
||||
}
|
||||
input[type=text]:focus,
|
||||
input[type=password]:focus,
|
||||
textarea:focus
|
||||
{
|
||||
background-color: #EEF;
|
||||
border-color: #BBC;
|
||||
}
|
||||
input[type=submit]:hover,
|
||||
input[type=button]:hover,
|
||||
button:hover
|
||||
{
|
||||
cursor: pointer;
|
||||
background-color: #DDE;
|
||||
}
|
||||
|
||||
/* Grid */
|
||||
|
||||
table.grid
|
||||
{
|
||||
width: 95%;
|
||||
margin: auto;
|
||||
border-collapse: collapse;
|
||||
text-align: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
table.grid thead tr,
|
||||
table.grid tfoot tr
|
||||
{
|
||||
/* background-color: #C0CCB3;
|
||||
background: url(image/gradient.png) repeat-x scroll 0;
|
||||
*/
|
||||
background-color: #EEF;
|
||||
border: 1px solid #CCD;
|
||||
color: black;
|
||||
font-weight: bold;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
height: 30px;
|
||||
}
|
||||
table.grid thead th
|
||||
{
|
||||
cursor: pointer;
|
||||
}
|
||||
table.grid thead th:hover
|
||||
{
|
||||
background-color: #DDE;
|
||||
}
|
||||
table.grid tr
|
||||
{
|
||||
height: 35px;
|
||||
}
|
||||
table.grid tfoot a,
|
||||
table.grid thead a
|
||||
{
|
||||
color: black;
|
||||
}
|
||||
table.grid tr.pair-row
|
||||
{
|
||||
background-color: transparent;
|
||||
}
|
||||
td.grid-message
|
||||
{
|
||||
height: 80px;
|
||||
}
|
||||
td.grid-message img
|
||||
{
|
||||
vertical-align: middle;
|
||||
padding: 10px;
|
||||
}
|
||||
table.grid tbody td
|
||||
{
|
||||
padding-right: 8px;
|
||||
padding-left: 8px;
|
||||
}
|
||||
input.cell-spin
|
||||
{
|
||||
width: 25px;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* Calendar */
|
||||
|
||||
.calendar
|
||||
{
|
||||
width: 220px;
|
||||
background-color: white;
|
||||
border: 1px solid #CCD;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0px 2px 2px #AAA;
|
||||
}
|
||||
.calendar table
|
||||
{
|
||||
border-collapse: collapse;
|
||||
}
|
||||
.calendar thead tr,
|
||||
.calendar tfoot tr
|
||||
{
|
||||
background-color: #EEF;
|
||||
color: black;
|
||||
font-weight: normal;
|
||||
vertical-align: middle;
|
||||
text-align: center;
|
||||
height: 30px;
|
||||
}
|
||||
.calendar thead tr
|
||||
{
|
||||
border-bottom: 1px solid #CCD;
|
||||
}
|
||||
.calendar tfoot tr
|
||||
{
|
||||
border-top: 1px solid #CCD;
|
||||
}
|
||||
.calendar col
|
||||
{
|
||||
width: 14.2%;
|
||||
}
|
||||
.calendar tr
|
||||
{
|
||||
height: 22px;
|
||||
}
|
||||
.calendar tbody td
|
||||
{
|
||||
text-align: right;
|
||||
padding-right: 6px;
|
||||
}
|
||||
.calendar td.highlight,
|
||||
.calendar td:hover,
|
||||
.button:hover
|
||||
{
|
||||
cursor: pointer;
|
||||
background-color: #DDE;
|
||||
}
|
||||
|
||||
/* Date chooser */
|
||||
|
||||
.date-chooser button
|
||||
{
|
||||
margin: 0px;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
/* Full image */
|
||||
|
||||
div.full-image
|
||||
{
|
||||
position: fixed;
|
||||
background-color: #FFF;
|
||||
text-align: center;
|
||||
border: 1px solid #999;
|
||||
border-radius: 2px;
|
||||
z-index: 2;
|
||||
}
|
||||
div.image-loader
|
||||
{
|
||||
position: fixed;
|
||||
background-color: #FFF;
|
||||
border: 1px solid #999;
|
||||
border-radius: 2px;
|
||||
z-index: 3;
|
||||
}
|
||||
div.image-loader img
|
||||
{
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
/* Image editor */
|
||||
|
||||
img.editable
|
||||
{
|
||||
cursor: pointer;
|
||||
}
|
||||
div.image-editor
|
||||
{
|
||||
background-color: #FFF;
|
||||
border: 1px solid #999;
|
||||
border-radius: 2px;
|
||||
padding: 12px;
|
||||
width: 400px;
|
||||
}
|
||||
div.image-editor h3
|
||||
{
|
||||
text-align: center;
|
||||
padding-bottom: 10px;
|
||||
}
|
||||
div.image-editor iframe
|
||||
{
|
||||
display: none;
|
||||
}
|
||||
div.image-editor button
|
||||
{
|
||||
margin-left: 5px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
div.image-editor img
|
||||
{
|
||||
visibility: hidden;
|
||||
vertical-align: middle;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
/* Form */
|
||||
|
||||
table.form
|
||||
{
|
||||
border-collapse: separate;
|
||||
border-spacing: 4px;
|
||||
}
|
||||
table.form td.label
|
||||
{
|
||||
text-align: right;
|
||||
}
|
||||
table.form tr
|
||||
{
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
|
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 597 B |
After Width: | Height: | Size: 590 B |
After Width: | Height: | Size: 825 B |
After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 708 B |
After Width: | Height: | Size: 790 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 935 B |
After Width: | Height: | Size: 397 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 5.3 KiB |
After Width: | Height: | Size: 784 B |
After Width: | Height: | Size: 1.2 KiB |
After Width: | Height: | Size: 215 B |
After Width: | Height: | Size: 3.8 KiB |
After Width: | Height: | Size: 141 B |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 8.9 KiB |
After Width: | Height: | Size: 9.2 KiB |
After Width: | Height: | Size: 4.7 KiB |
After Width: | Height: | Size: 11 KiB |
After Width: | Height: | Size: 6.5 KiB |
After Width: | Height: | Size: 984 B |
After Width: | Height: | Size: 965 B |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 795 B |
After Width: | Height: | Size: 1.0 KiB |
After Width: | Height: | Size: 1005 B |
After Width: | Height: | Size: 1.6 KiB |
After Width: | Height: | Size: 1.1 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 1.3 KiB |
After Width: | Height: | Size: 290 KiB |
After Width: | Height: | Size: 40 KiB |
After Width: | Height: | Size: 1.4 KiB |
After Width: | Height: | Size: 786 B |
After Width: | Height: | Size: 62 KiB |
After Width: | Height: | Size: 510 B |
|
@ -0,0 +1,73 @@
|
|||
<?php
|
||||
|
||||
require_once ('php/web/hedera.php');
|
||||
require_once ('php/web/js.php');
|
||||
|
||||
Hedera::init ();
|
||||
|
||||
if ($conf['https'] && (!isset ($_SERVER['HTTPS']) || $_SERVER['HTTPS'] != 'on'))
|
||||
{
|
||||
header ('Location: https://'.$_SERVER['SERVER_NAME']);
|
||||
exit (0);
|
||||
}
|
||||
|
||||
if (!Hedera::$sysConn->isOpen ())
|
||||
{
|
||||
header ('Location: unavailable.html');
|
||||
exit (0);
|
||||
}
|
||||
|
||||
// Getting the section
|
||||
|
||||
if (isset ($_GET['section']))
|
||||
$section = $_GET['section'];
|
||||
else
|
||||
$section = 'login';
|
||||
|
||||
// Checking the browser version
|
||||
|
||||
if (!isset ($_SESSION['skipBrowser']) && $section != 'update-browser')
|
||||
{
|
||||
$updateBrowser = FALSE;
|
||||
|
||||
if (!isset ($_GET['skipBrowser'])
|
||||
&& ($browser = get_browser ($_SERVER['HTTP_USER_AGENT'])))
|
||||
{
|
||||
$browserVersion = $browser->version;
|
||||
set_type ($browserVersion, TYPE_DOUBLE);
|
||||
|
||||
$updateBrowser = isset ($conf['browser'][$browser->browser])
|
||||
&& $browserVersion < $conf['browser'][$browser->browser];
|
||||
}
|
||||
|
||||
if ($updateBrowser)
|
||||
{
|
||||
header ('Location: ?section=update-browser');
|
||||
exit (0);
|
||||
}
|
||||
else
|
||||
$_SESSION['skipBrowser'] = TRUE;
|
||||
}
|
||||
|
||||
// Loading the section
|
||||
|
||||
$basePath = 'sections/'. $section;
|
||||
|
||||
setcookie ('hedera_version', $_SESSION['version']);
|
||||
header ('Content-Type: text/html;charset=utf-8');
|
||||
|
||||
if (file_exists ($basePath))
|
||||
{
|
||||
Vn\Locale::loadFile ($basePath);
|
||||
|
||||
$phpFile = $basePath .'/'. $section .'.php';
|
||||
|
||||
if (file_exists ($phpFile))
|
||||
require ($phpFile);
|
||||
|
||||
require ($basePath .'/html.php');
|
||||
}
|
||||
|
||||
Hedera::deinit ();
|
||||
|
||||
?>
|
|
@ -0,0 +1,59 @@
|
|||
/**
|
||||
* Computes a sum of data in the model.
|
||||
**/
|
||||
Db.CalcSum = new Class
|
||||
({
|
||||
Extends: Db.Calc
|
||||
,Tag: 'db-calc-sum'
|
||||
|
||||
,getRowValue: function (row)
|
||||
{
|
||||
var value;
|
||||
|
||||
if (this._func)
|
||||
{
|
||||
this.form.row = row;
|
||||
value = this._func (this.form);
|
||||
}
|
||||
else
|
||||
value = this._model.getByIndex (row, this.columnIndex);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
,before: function (row)
|
||||
{
|
||||
var value = this.getRowValue (row)
|
||||
|
||||
if (value !== null)
|
||||
{
|
||||
this.sum -= value;
|
||||
this.numbersCount--;
|
||||
}
|
||||
}
|
||||
|
||||
,after: function (row)
|
||||
{
|
||||
var value = this.getRowValue (row);
|
||||
|
||||
if (value !== null)
|
||||
{
|
||||
this.sum += value;
|
||||
this.numbersCount++;
|
||||
}
|
||||
}
|
||||
|
||||
,init: function ()
|
||||
{
|
||||
this.sum = 0;
|
||||
this.numbersCount = 0;
|
||||
}
|
||||
|
||||
,done: function ()
|
||||
{
|
||||
if (this.numbersCount > 0)
|
||||
this.value = this.sum;
|
||||
else
|
||||
this.value = null;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,149 @@
|
|||
/**
|
||||
* Interface for handle foreach operations on the model.
|
||||
**/
|
||||
Db.Calc = new Class
|
||||
({
|
||||
Extends: Vn.Param
|
||||
,Tag: 'db-calc'
|
||||
,Properties:
|
||||
{
|
||||
model:
|
||||
{
|
||||
type: Db.Model
|
||||
,set: function (x)
|
||||
{
|
||||
this.link ({_model: x},
|
||||
{
|
||||
'status-changed': this.onModelChange
|
||||
,'row-deleted-before': this.onRowDeleteBefore
|
||||
,'row-updated': this.onRowUpdate
|
||||
,'row-updated-before': this.onRowUpdateBefore
|
||||
,'row-inserted': this.onRowInsert
|
||||
});
|
||||
|
||||
this.form = new Db.Form ({model: x});
|
||||
}
|
||||
,get: function ()
|
||||
{
|
||||
return this._model;
|
||||
}
|
||||
},
|
||||
columnIndex:
|
||||
{
|
||||
type: Number
|
||||
,set: function (x)
|
||||
{
|
||||
this.reset ();
|
||||
this._columnIndex = x;
|
||||
this.onModelChange ();
|
||||
}
|
||||
,get: function ()
|
||||
{
|
||||
return this._columnIndex;
|
||||
}
|
||||
},
|
||||
columnName:
|
||||
{
|
||||
type: String
|
||||
,set: function (x)
|
||||
{
|
||||
this.reset ();
|
||||
this._columnName = x;
|
||||
this.onModelChange ();
|
||||
}
|
||||
,get: function ()
|
||||
{
|
||||
return this._columnName;
|
||||
}
|
||||
},
|
||||
func:
|
||||
{
|
||||
type: Function
|
||||
,set: function (x)
|
||||
{
|
||||
this.reset ();
|
||||
this._func = x;
|
||||
this.onModelChange ();
|
||||
}
|
||||
,get: function ()
|
||||
{
|
||||
return this._func;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
,_model: null
|
||||
|
||||
,reset: function ()
|
||||
{
|
||||
delete this._columnIndex;
|
||||
delete this._columnName;
|
||||
delete this._func;
|
||||
}
|
||||
|
||||
,onModelChange: function ()
|
||||
{
|
||||
this.init ();
|
||||
|
||||
if (this._model)
|
||||
{
|
||||
if (this._model.ready && this._columnName)
|
||||
this._columnIndex = this._model.getColumnIndex (this._columnName);
|
||||
|
||||
var rows = this._model.numRows;
|
||||
|
||||
for (var i = 0; i < rows; i++)
|
||||
this.after (i);
|
||||
}
|
||||
|
||||
this.done ();
|
||||
}
|
||||
|
||||
,onRowInsert: function (model, row)
|
||||
{
|
||||
this.after (row);
|
||||
this.done ();
|
||||
}
|
||||
|
||||
,onRowUpdateBefore: function (model, row)
|
||||
{
|
||||
this.before (row);
|
||||
}
|
||||
|
||||
,onRowUpdate: function (model, row)
|
||||
{
|
||||
this.after (row);
|
||||
this.done ();
|
||||
}
|
||||
|
||||
,onRowDeleteBefore: function (model, row)
|
||||
{
|
||||
this.before (row);
|
||||
this.done ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before each update or delete row operation.
|
||||
* You don't need to define it if model isn't updatable.
|
||||
*
|
||||
* @param {integer} row The row number
|
||||
**/
|
||||
,before: function (row) {}
|
||||
|
||||
/**
|
||||
* Called after each update or insert row operation.
|
||||
*
|
||||
* @param {integer} row The row number
|
||||
**/
|
||||
,after: function (row) {}
|
||||
|
||||
/**
|
||||
* Called before each model refresh.
|
||||
**/
|
||||
,init: function () {}
|
||||
|
||||
/**
|
||||
* Called when an operation in the model is complete.
|
||||
**/
|
||||
,done: function () {}
|
||||
});
|
|
@ -0,0 +1,241 @@
|
|||
/**
|
||||
* Simulates a connection to a database by making asynchronous http requests to
|
||||
* a remote PHP script that returns the results in JSON format.
|
||||
* Using this class can perform any operation that can be done with a database,
|
||||
* like open/close a connection or selecion/updating queries.
|
||||
**/
|
||||
Db.Conn = new Class ().extend
|
||||
({
|
||||
Flag:
|
||||
{
|
||||
NOT_NULL : 1
|
||||
,PRI_KEY : 2
|
||||
,AI : 512 | 2 | 1
|
||||
}
|
||||
|
||||
,Type:
|
||||
{
|
||||
TIMESTAMP : 7
|
||||
,DATE : 10
|
||||
,DATE_TIME : 12
|
||||
}
|
||||
});
|
||||
|
||||
Db.Conn.implement
|
||||
({
|
||||
Extends: Vn.Object
|
||||
|
||||
,connected: false
|
||||
,requestsCount: 0
|
||||
|
||||
/**
|
||||
* Initilizes the connection object.
|
||||
**/
|
||||
,initialize: function ()
|
||||
{
|
||||
this.parent ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Opens the connection to the database.
|
||||
*
|
||||
* @param {string} user The user name
|
||||
* @param {String} password The user password
|
||||
* @param {Boolean} remember Specifies if the user should be remembered
|
||||
* @param {Function} openCallback The function to call when operation is done
|
||||
**/
|
||||
,open: function (user, pass, remember, openCallback)
|
||||
{
|
||||
this.signalEmit ('loading-changed', true);
|
||||
|
||||
var request = new Db.HttpRequest ();
|
||||
request.add ({'action': 'login'});
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
request.add
|
||||
({
|
||||
'user': user
|
||||
,'password': pass
|
||||
,'remember': remember
|
||||
});
|
||||
}
|
||||
|
||||
request.send ('ajax.php',
|
||||
this.opened.bind (this, request, openCallback));
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when open operation is done.
|
||||
*/
|
||||
,opened: function (request, openCallback, success)
|
||||
{
|
||||
var openSuccess = false;
|
||||
|
||||
if (success)
|
||||
try {
|
||||
var json = request.getJson ();
|
||||
openSuccess = true /* json.data */;
|
||||
}
|
||||
catch (e) {}
|
||||
|
||||
if (openSuccess)
|
||||
this.connected = true;
|
||||
|
||||
this.signalEmit ('loading-changed', false);
|
||||
|
||||
if (openCallback)
|
||||
openCallback (this, openSuccess);
|
||||
}
|
||||
|
||||
/**
|
||||
* Closes the connection to the database.
|
||||
*
|
||||
* @param {Function} closeCallback The function to call when operation is done
|
||||
**/
|
||||
,close: function (closeCallback)
|
||||
{
|
||||
this.signalEmit ('loading-changed', true);
|
||||
|
||||
var request = new Db.HttpRequest ();
|
||||
request.addValue ({'action': 'close'});
|
||||
request.send ('ajax.php',
|
||||
this.closed.bind (this, closeCallback));
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when close operation is done.
|
||||
*/
|
||||
,closed: function (closeCallback)
|
||||
{
|
||||
this.connected = false;
|
||||
this.signalEmit ('closed');
|
||||
|
||||
this.signalEmit ('loading-changed', false);
|
||||
|
||||
if (closeCallback)
|
||||
closeCallback (this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a SQL query on the database.
|
||||
*
|
||||
* @param {String} sql The SQL statement
|
||||
* @param {Function} callback The function to call when operation is done
|
||||
**/
|
||||
,execSql: function (sql, callback)
|
||||
{
|
||||
this.requestsCount++;
|
||||
|
||||
if (this.requestsCount == 1)
|
||||
this.signalEmit ('loading-changed', true);
|
||||
|
||||
var httpRequest = new Db.HttpRequest ()
|
||||
httpRequest.add
|
||||
({
|
||||
'action': 'query'
|
||||
,'sql': sql
|
||||
});
|
||||
httpRequest.send ('ajax.php',
|
||||
this.execDone.bind (this, httpRequest, callback));
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a stmt on the database.
|
||||
*
|
||||
* @param {Sql.Stmt} stmt The statement
|
||||
* @param {Function} callback The function to call when operation is done
|
||||
* @param {Sql.Batch} batch The batch used to set the parameters
|
||||
**/
|
||||
,execStmt: function (stmt, callback, batch)
|
||||
{
|
||||
this.execSql (stmt.render (batch), callback);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs a query on the database.
|
||||
*
|
||||
* @param {String} query The SQL statement
|
||||
* @param {Function} callback The function to call when operation is done
|
||||
* @param {Sql.Batch} batch The batch used to set the parameters
|
||||
**/
|
||||
,execQuery: function (query, callback, batch)
|
||||
{
|
||||
this.execStmt (new Sql.String ({query: query}), callback, batch);
|
||||
}
|
||||
|
||||
/*
|
||||
* Parses a value to date.
|
||||
*/
|
||||
,valueToDate: function (value)
|
||||
{
|
||||
return new Date (value * 1000);
|
||||
}
|
||||
|
||||
/*
|
||||
* Called when a query is executed.
|
||||
*/
|
||||
,execDone: function (httpRequest, callback, success)
|
||||
{
|
||||
var error = null;
|
||||
var results = null;
|
||||
|
||||
this.requestsCount--;
|
||||
|
||||
if (this.requestsCount == 0)
|
||||
this.signalEmit ('loading-changed', false);
|
||||
|
||||
if (!success)
|
||||
error = new Vn.Error ('Conn', 'connError', _('ConnError'));
|
||||
else
|
||||
try {
|
||||
var json = httpRequest.getJson ();
|
||||
results = json.data;
|
||||
|
||||
if (json.error !== null)
|
||||
error = new Vn.Error (json.error.domain,
|
||||
json.error.code, json.error.message);
|
||||
|
||||
if (results instanceof Array)
|
||||
for (var i = 0; i < results.length; i++)
|
||||
if (results[i] !== true)
|
||||
{
|
||||
var data = results[i].data;
|
||||
var columns = results[i].field;
|
||||
|
||||
for (var j = 0; j < columns.length; j++)
|
||||
{
|
||||
var castFunc = null;
|
||||
|
||||
switch (columns[j].type)
|
||||
{
|
||||
case Db.Conn.Type.DATE:
|
||||
case Db.Conn.Type.DATE_TIME:
|
||||
case Db.Conn.Type.TIMESTAMP:
|
||||
castFunc = this.valueToDate;
|
||||
break;
|
||||
}
|
||||
|
||||
if (castFunc !== null)
|
||||
{
|
||||
if (columns[j].def != null)
|
||||
columns[j].def = castFunc (columns[j].def);
|
||||
|
||||
for (var k = 0; k < data.length; k++)
|
||||
if (data[k][j] != null)
|
||||
data[k][j] = castFunc (data[k][j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (e) {
|
||||
error = new Vn.Error ('Conn', 'badReply', e.message /*_('BadServerReply')*/);
|
||||
}
|
||||
|
||||
if (callback)
|
||||
callback (new Db.ResultSet (results, error));
|
||||
if (error)
|
||||
this.signalEmit ('error', error);
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,212 @@
|
|||
|
||||
Db.Form = new Class
|
||||
({
|
||||
Extends: Vn.Object
|
||||
,Tag: 'db-form'
|
||||
,Child: 'model'
|
||||
,Properties:
|
||||
{
|
||||
/**
|
||||
* The model associated to this form.
|
||||
**/
|
||||
model:
|
||||
{
|
||||
type: Db.Model
|
||||
,set: function (x)
|
||||
{
|
||||
this.link ({_model: x},
|
||||
{
|
||||
'status-changed': this.onModelChange
|
||||
,'row-updated': this.onRowUpdate
|
||||
});
|
||||
}
|
||||
,get: function ()
|
||||
{
|
||||
return this._model;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The row where the form positioned, has -1 if the row is unselected.
|
||||
**/
|
||||
row:
|
||||
{
|
||||
type: Number
|
||||
,set: function (x)
|
||||
{
|
||||
if (!this._model || this._model.numRows <= x || x < -1)
|
||||
x = -1;
|
||||
if (x == this._row)
|
||||
return;
|
||||
|
||||
this._row = x;
|
||||
this.iterChanged ();
|
||||
}
|
||||
,get: function ()
|
||||
{
|
||||
return this._row;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The number of rows in the form.
|
||||
**/
|
||||
numRows:
|
||||
{
|
||||
type: Number
|
||||
,get: function ()
|
||||
{
|
||||
if (this._model)
|
||||
return this._model.numRows;
|
||||
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Checks if the form data is ready.
|
||||
**/
|
||||
ready:
|
||||
{
|
||||
type: Boolean
|
||||
,get: function ()
|
||||
{
|
||||
return this._ready;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
,lastRow: 0
|
||||
,_model: null
|
||||
,_row: -1
|
||||
,_ready: false
|
||||
|
||||
,onModelChange: function ()
|
||||
{
|
||||
var ready = this._model && this._model.ready;
|
||||
|
||||
if (ready != this._ready)
|
||||
{
|
||||
if (this._row != -1)
|
||||
this.lastRow = this._row;
|
||||
|
||||
this._ready = ready;
|
||||
this.signalEmit ('status-changed');
|
||||
|
||||
if (this._row == -1)
|
||||
this.row = this.lastRow;
|
||||
}
|
||||
}
|
||||
|
||||
,onRowUpdate: function (model, row, column)
|
||||
{
|
||||
if (row == this._row)
|
||||
this.iterChanged ();
|
||||
}
|
||||
|
||||
,refresh: function ()
|
||||
{
|
||||
if (this._model)
|
||||
this._model.refresh ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Emits the 'iter-changed' signal on the form.
|
||||
**/
|
||||
,iterChanged: function ()
|
||||
{
|
||||
this.signalEmit ('iter-changed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the index of the column from its name.
|
||||
*
|
||||
* @param {String} columnName The column name
|
||||
* @return {integer} The column index or -1 if column not exists
|
||||
**/
|
||||
,getColumnIndex: function (columnName)
|
||||
{
|
||||
if (this._model)
|
||||
return this._model.getColumnIndex (columnName);
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
,insertRow: function ()
|
||||
{
|
||||
if (this._model)
|
||||
this.row = this._model.insertRow ();
|
||||
}
|
||||
|
||||
,performOperations: function ()
|
||||
{
|
||||
if (this._model)
|
||||
this._model.performOperations ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the current row.
|
||||
**/
|
||||
,deleteRow: function ()
|
||||
{
|
||||
if (this._row >= 0)
|
||||
this._model.deleteRow (this._row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value from the form.
|
||||
*
|
||||
* @param {String} columnName The column name
|
||||
* @return {Object} The value
|
||||
**/
|
||||
,get: function (columnName)
|
||||
{
|
||||
return this._model.get (this._row, columnName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value on the form.
|
||||
*
|
||||
* @param {String} columnName The column name
|
||||
* @param {Object} value The new value
|
||||
**/
|
||||
,set: function (columnName, value)
|
||||
{
|
||||
return this._model.set (this._row, columnName, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value from the form using the column index.
|
||||
*
|
||||
* @param {String} columnName The column index
|
||||
* @return {Object} The value
|
||||
**/
|
||||
,getByIndex: function (column)
|
||||
{
|
||||
return this._model.getByIndex (this._row, column);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a value on the form using the column index.
|
||||
*
|
||||
* @param {String} columnName The column index
|
||||
* @param {Object} value The new value
|
||||
**/
|
||||
,setByIndex: function (column, value)
|
||||
{
|
||||
return this._model.setByIndex (this._row, column, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a param from the form.
|
||||
*
|
||||
* @param {String} columnName The column name
|
||||
* @return {Db.Param} The new parameter
|
||||
**/
|
||||
,getParam: function (columnName)
|
||||
{
|
||||
return new Db.Param
|
||||
({
|
||||
form: this
|
||||
,column: columnName
|
||||
});
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
Db.HttpRequest = new Class
|
||||
({
|
||||
kvPairs: {}
|
||||
|
||||
,add: function (map)
|
||||
{
|
||||
for (var key in map)
|
||||
this.kvPairs[key] = map[key];
|
||||
}
|
||||
|
||||
,send: function (file, callback)
|
||||
{
|
||||
this.request = new XMLHttpRequest ();
|
||||
this.request.open ('post', file, true);
|
||||
this.request.setRequestHeader ('Content-Type', 'application/x-www-form-urlencoded');
|
||||
this.request.onreadystatechange = this.requestStateChanged.bind (this, callback);
|
||||
this.request.send (Vn.Url.makeUri (this.kvPairs));
|
||||
}
|
||||
|
||||
,requestStateChanged: function (callback)
|
||||
{
|
||||
if (this.request.readyState == 4)
|
||||
callback (this, this.request.status == 200);
|
||||
}
|
||||
|
||||
,getJson: function ()
|
||||
{
|
||||
return eval (this.request.responseText);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,5 @@
|
|||
/**
|
||||
* The namespace.
|
||||
**/
|
||||
var Db = {};
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
<?php
|
||||
|
||||
require_once ('js/sql/main.php');
|
||||
|
||||
Js::includeLib ('db'
|
||||
,'main'
|
||||
,'conn'
|
||||
,'result'
|
||||
,'result-set'
|
||||
,'model'
|
||||
,'form'
|
||||
,'param'
|
||||
,'calc'
|
||||
,'calc-sum'
|
||||
,'http-request'
|
||||
);
|
||||
|
||||
?>
|
|
@ -0,0 +1,809 @@
|
|||
|
||||
Db.Model = new Class ().extend
|
||||
({
|
||||
Status:
|
||||
{
|
||||
CLEAN : 1
|
||||
,LOADING : 2
|
||||
,READY : 3
|
||||
,ERROR : 4
|
||||
}
|
||||
});
|
||||
|
||||
Db.Model.implement
|
||||
({
|
||||
Extends: Vn.Object
|
||||
,Tag: 'db-model'
|
||||
,Child: 'query'
|
||||
,Properties:
|
||||
{
|
||||
/**
|
||||
* The connection used to execute the statement.
|
||||
**/
|
||||
conn:
|
||||
{
|
||||
type: Db.Conn
|
||||
,set: function (x)
|
||||
{
|
||||
this._conn = x;
|
||||
this.refresh ();
|
||||
}
|
||||
,get: function ()
|
||||
{
|
||||
return this._conn;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The result index.
|
||||
**/
|
||||
resultIndex:
|
||||
{
|
||||
type: Number
|
||||
,set: function (x)
|
||||
{
|
||||
this._resultIndex = x;
|
||||
}
|
||||
,get: function ()
|
||||
{
|
||||
return this._resultIndex;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The batch used to execute the statement.
|
||||
**/
|
||||
batch:
|
||||
{
|
||||
type: Sql.Batch
|
||||
,set: function (x)
|
||||
{
|
||||
this.link ({_batch: x}, {'changed': this.refresh});
|
||||
this.refresh ();
|
||||
}
|
||||
,get: function ()
|
||||
{
|
||||
return this._batch;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The model select statement.
|
||||
**/
|
||||
stmt:
|
||||
{
|
||||
type: Sql.Stmt
|
||||
,set: function (x)
|
||||
{
|
||||
this._stmt = x;
|
||||
this.refresh ();
|
||||
}
|
||||
,get: function ()
|
||||
{
|
||||
return this._stmt;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The model query.
|
||||
**/
|
||||
query:
|
||||
{
|
||||
type: String
|
||||
,set: function (x)
|
||||
{
|
||||
this._stmt = new Sql.String ({query: x});
|
||||
}
|
||||
,get: function ()
|
||||
{
|
||||
return this._stmt.render (null);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The main table.
|
||||
**/
|
||||
mainTable:
|
||||
{
|
||||
type: String
|
||||
,set: function (x)
|
||||
{
|
||||
this._mainTable = null;
|
||||
this.requestedMainTable = x;
|
||||
this.refreshMainTable ();
|
||||
}
|
||||
,get: function ()
|
||||
{
|
||||
return this._mainTable;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Determines if the model is updatable.
|
||||
**/
|
||||
updatable:
|
||||
{
|
||||
type: Boolean
|
||||
,set: function (x)
|
||||
{
|
||||
this._updatable = false;
|
||||
this.requestedUpdatable = x;
|
||||
this.refreshUpdatable ();
|
||||
}
|
||||
,get: function ()
|
||||
{
|
||||
return this._updatable;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The number of rows in the model.
|
||||
**/
|
||||
numRows:
|
||||
{
|
||||
type: Number
|
||||
,get: function ()
|
||||
{
|
||||
if (this.data)
|
||||
return this.data.length;
|
||||
|
||||
return 0;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The current status of the model.
|
||||
**/
|
||||
status:
|
||||
{
|
||||
type: Number
|
||||
,get: function ()
|
||||
{
|
||||
return this._status;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Checks if the model data is ready.
|
||||
**/
|
||||
ready:
|
||||
{
|
||||
type: Boolean
|
||||
,get: function ()
|
||||
{
|
||||
return this._status == Db.Model.Status.READY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
,_resultIndex: 0
|
||||
,_batch: null
|
||||
,_stmt: null
|
||||
,_status: Db.Model.Status.CLEAN
|
||||
,requestedMainTable: null
|
||||
,requestedUpdatable: true
|
||||
,data: null
|
||||
,columns: null
|
||||
,columnMap: null
|
||||
,insertedRow: -1
|
||||
,defaults: []
|
||||
,columnDefaults: []
|
||||
,sortColumn: -1
|
||||
|
||||
,initialize: function (props)
|
||||
{
|
||||
this.parent (props);
|
||||
this.cleanData (false);
|
||||
}
|
||||
|
||||
,loadXml: function (builder, node)
|
||||
{
|
||||
this.parent (builder, node);
|
||||
|
||||
var query = node.firstChild.nodeValue;
|
||||
|
||||
if (query)
|
||||
this.query = query;
|
||||
}
|
||||
|
||||
,refresh: function ()
|
||||
{
|
||||
if (this._stmt && this._batch)
|
||||
this._stmt.findHolders (this._batch);
|
||||
|
||||
if (this._conn && this._stmt
|
||||
&& (!this._batch || this._batch.isReady ()))
|
||||
{
|
||||
this.setStatus (Db.Model.Status.LOADING);
|
||||
this._conn.execStmt (this._stmt, this.selectDone.bind (this), this._batch);
|
||||
}
|
||||
else
|
||||
this.cleanData (false);
|
||||
}
|
||||
|
||||
,cleanData: function (error)
|
||||
{
|
||||
this.data = null;
|
||||
this.columns = null;
|
||||
this.columnMap = null;
|
||||
|
||||
if (error)
|
||||
this.setStatus (Db.Model.Status.ERROR);
|
||||
else
|
||||
this.setStatus (Db.Model.Status.CLEAN);
|
||||
}
|
||||
|
||||
,refreshUpdatable: function ()
|
||||
{
|
||||
var oldValue = this._updatable;
|
||||
this._updatable = this._mainTable != null && this.requestedUpdatable;
|
||||
|
||||
if (oldValue != this._updatable)
|
||||
this.signalEmit ('updatable-changed');
|
||||
}
|
||||
|
||||
,refreshMainTable: function ()
|
||||
{
|
||||
var newMainTable = null;
|
||||
var newMainSchema = null;
|
||||
var x = this.columns;
|
||||
|
||||
if (x)
|
||||
for (var i = 0; i < x.length; i++)
|
||||
if (x[i].flags & Db.Conn.Flag.PRI_KEY)
|
||||
if (!this.requestedMainTable
|
||||
|| x[i].table === this.requestedMainTable)
|
||||
{
|
||||
newMainTable = x[i].table;
|
||||
break;
|
||||
}
|
||||
|
||||
this._mainTable = newMainTable;
|
||||
this.refreshUpdatable ();
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default value for inserted fields.
|
||||
*
|
||||
* @param {String} field The destination field name
|
||||
* @param {String} table The destination table name
|
||||
* @param {Object} value The default value
|
||||
**/
|
||||
,setDefaultFromValue: function (field, table, value)
|
||||
{
|
||||
this.defaults.push
|
||||
({
|
||||
field: field
|
||||
,table: table
|
||||
,value: value
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default value for inserted fields from another column in the
|
||||
* model.
|
||||
*
|
||||
* @param {String} field The destination field name
|
||||
* @param {String} table The destination table name
|
||||
* @param {String} srcColumn The source column
|
||||
**/
|
||||
,setDefaultFromColumn: function (field, table, srcColumn)
|
||||
{
|
||||
this.columnDefaults.push
|
||||
({
|
||||
field: field
|
||||
,table: table
|
||||
,srcColumn: srcColumn
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the column exists.
|
||||
*
|
||||
* @param {integer} column The column index
|
||||
* @return {Boolean} %true if column exists, %false otherwise
|
||||
**/
|
||||
,checkColExists: function (column)
|
||||
{
|
||||
return this.columns && column >= 0 && column < this.columns.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the row exists.
|
||||
*
|
||||
* @param {integer} row The row index
|
||||
* @return {Boolean} %true if row exists, %false otherwise
|
||||
**/
|
||||
,checkRowExists: function (row)
|
||||
{
|
||||
return this.data && row >= 0 && row < this.data.length;
|
||||
}
|
||||
|
||||
,checkRowUpdatable: function (row)
|
||||
{
|
||||
return this.checkRowExists (row);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the index of the column from its name.
|
||||
*
|
||||
* @param {string} columnName The column name
|
||||
* @return {number} The column index or -1 if column not exists
|
||||
**/
|
||||
,getColumnIndex: function (columnName)
|
||||
{
|
||||
var index;
|
||||
|
||||
if (this.columnMap
|
||||
&& (index = this.columnMap[columnName]) !== undefined)
|
||||
return index;
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value from the model.
|
||||
*
|
||||
* @param {number} row The row index
|
||||
* @param {string} columnName The column name
|
||||
* @return {mixed} The value
|
||||
**/
|
||||
,get: function (row, columnName)
|
||||
{
|
||||
var index = this.getColumnIndex (columnName);
|
||||
|
||||
if (index != -1)
|
||||
return this.getByIndex (row, index);
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a value on the model.
|
||||
*
|
||||
* @param {number} row The row index
|
||||
* @param {string} columnName The column name
|
||||
* @param {mixed} value The new value
|
||||
**/
|
||||
,set: function (row, columnName, value)
|
||||
{
|
||||
var index = this.getColumnIndex (columnName);
|
||||
|
||||
if (index != -1)
|
||||
this.setByIndex (row, index, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value from the model using the column index.
|
||||
*
|
||||
* @param {number} row The row index
|
||||
* @param {number} column The column index
|
||||
* @return {mixed} The value
|
||||
**/
|
||||
,getByIndex: function (row, column)
|
||||
{
|
||||
if (this.checkRowExists (row) && this.checkColExists (column))
|
||||
return this.data[row][column];
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates a value on the model using the column index.
|
||||
*
|
||||
* @param {number} row The row index
|
||||
* @param {number} col The column index
|
||||
* @param {mixed} value The new value
|
||||
**/
|
||||
,setByIndex: function (row, col, value)
|
||||
{
|
||||
if (!(this.checkRowUpdatable (row) && this.checkColExists (col)))
|
||||
return;
|
||||
|
||||
if (row == this.insertedRow)
|
||||
{
|
||||
this.performUpdate (row, [col], [value]);
|
||||
return;
|
||||
}
|
||||
|
||||
var column = this.columns[col];
|
||||
var where = this.getWhere (column.table, row, false);
|
||||
|
||||
if (where)
|
||||
{
|
||||
var multiStmt = new Sql.MultiStmt ();
|
||||
|
||||
var table = new Sql.Table
|
||||
({
|
||||
name: column.orgtable
|
||||
,schema: column.db
|
||||
});
|
||||
|
||||
var update = new Sql.Update ({where: where});
|
||||
update.addTarget (table);
|
||||
update.addSet (column.orgname, value);
|
||||
multiStmt.addStmt (update);
|
||||
|
||||
var select = new Sql.Select ({where: where});
|
||||
select.addTarget (table);
|
||||
select.addField (column.orgname);
|
||||
multiStmt.addStmt (select);
|
||||
|
||||
var updateData = {
|
||||
row: row
|
||||
,columns: [col]
|
||||
};
|
||||
|
||||
this._conn.execStmt (multiStmt,
|
||||
this.updateDone.bind (this, updateData));
|
||||
}
|
||||
else
|
||||
this.sendInsert (column.table, row, col, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a row from the model.
|
||||
*
|
||||
* @param {number} row The row index
|
||||
**/
|
||||
,deleteRow: function (row)
|
||||
{
|
||||
if (!this.checkRowUpdatable (row))
|
||||
return;
|
||||
|
||||
if (row != this.insertedRow)
|
||||
{
|
||||
var where = this.getWhere (this._mainTable, row, false);
|
||||
|
||||
if (where)
|
||||
{
|
||||
var deleteQuery = new Sql.Delete ({where: where});
|
||||
|
||||
var table = this.getTarget (this._mainTable);
|
||||
deleteQuery.addTarget (table);
|
||||
|
||||
this._conn.execStmt (deleteQuery,
|
||||
this.deleteDone.bind (this, row));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this.performDelete (row);
|
||||
this.insertedRow = -1;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Inserts a new row on the model.
|
||||
*
|
||||
* @return The index of the inserted row
|
||||
**/
|
||||
,insertRow: function ()
|
||||
{
|
||||
if (!this._updatable || this.insertedRow != -1)
|
||||
return -1;
|
||||
|
||||
var x = this.columns;
|
||||
var newRow = new Array (x.length);
|
||||
|
||||
for (var i = 0; i < x.length; i++)
|
||||
if (x[i].table === this._mainTable)
|
||||
newRow[i] = x[i].def;
|
||||
else
|
||||
newRow[i] = null;
|
||||
|
||||
this.insertedRow = this.data.push (newRow) - 1;
|
||||
this.signalEmit ('row-inserted', this.insertedRow);
|
||||
|
||||
return this.insertedRow;
|
||||
}
|
||||
|
||||
,performOperations: function ()
|
||||
{
|
||||
if (this.insertedRow == -1)
|
||||
return;
|
||||
|
||||
this.sendInsert (this._mainTable, this.insertedRow, -1, null);
|
||||
}
|
||||
|
||||
/*
|
||||
* Function used to sort the model.
|
||||
*/
|
||||
,sortFunction: function (column, a, b)
|
||||
{
|
||||
if (a[column] < b[column])
|
||||
return -1;
|
||||
else if (a[column] > b[column])
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Orders the model by the specified column.
|
||||
*
|
||||
* @param {integer} column the column index
|
||||
**/
|
||||
,sort: function (column)
|
||||
{
|
||||
if (!this.checkColExists (column))
|
||||
return;
|
||||
|
||||
this.setStatus (Db.Model.Status.LOADING);
|
||||
|
||||
if (column != this.sortColumn)
|
||||
{
|
||||
this.data.sort (this.sortFunction.bind (this, column));
|
||||
this.sortColumn = column;
|
||||
}
|
||||
else
|
||||
this.data.reverse ();
|
||||
|
||||
this.setStatus (Db.Model.Status.READY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searchs a value on the model and returns the row index of the first
|
||||
* ocurrence.
|
||||
*
|
||||
* @param {String} column The column name
|
||||
* @param {Object} value The value to search
|
||||
* @return {integer} The column index
|
||||
**/
|
||||
,search: function (column, value)
|
||||
{
|
||||
var index = this.getColumnIndex (column);
|
||||
return this.searchByIndex (index, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Searchs a value on the model and returns the row index of the first
|
||||
* ocurrence.
|
||||
*
|
||||
* @param {integer} col The column index
|
||||
* @param {Object} value The value to search
|
||||
* @return {integer} The column index
|
||||
**/
|
||||
,searchByIndex: function (col, value)
|
||||
{
|
||||
if (!this.checkColExists (col))
|
||||
return -1;
|
||||
|
||||
var data = this.data;
|
||||
|
||||
switch (this.columns[col].type)
|
||||
{
|
||||
case Db.Conn.Type.TIMESTAMP:
|
||||
case Db.Conn.Type.DATE_TIME:
|
||||
case Db.Conn.Type.DATE:
|
||||
{
|
||||
value = value.toString ();
|
||||
|
||||
for (var i = 0; i < data.length; i++)
|
||||
if (value === data[i][col].toString ());
|
||||
return i;
|
||||
|
||||
break;
|
||||
}
|
||||
default:
|
||||
for (var i = 0; i < data.length; i++)
|
||||
if (value === data[i][col])
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// private:
|
||||
|
||||
,setStatus: function (status)
|
||||
{
|
||||
this._status = status;
|
||||
this.signalEmit ('status-changed', status);
|
||||
}
|
||||
|
||||
,getTarget: function (table)
|
||||
{
|
||||
var x = this.columns;
|
||||
|
||||
for (var i = 0; i < x.length; i++)
|
||||
if (x[i].table == table)
|
||||
return new Sql.Table
|
||||
({
|
||||
name: x[i].orgtable
|
||||
,schema: x[i].db
|
||||
});
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
,getWhere: function (table, rowIndex, forInsert)
|
||||
{
|
||||
var keyFound = false;
|
||||
var row = this.data[rowIndex];
|
||||
var andOp = new Sql.Operation ({type: Sql.Operation.Type.AND});
|
||||
|
||||
var x = this.columns;
|
||||
|
||||
for (var i = 0; i < x.length; i++)
|
||||
if (x[i].flags & Db.Conn.Flag.PRI_KEY
|
||||
&& x[i].table === table)
|
||||
{
|
||||
var equalOp = new Sql.Operation ({type: Sql.Operation.Type.EQUAL});
|
||||
equalOp.exprs.add (new Sql.Field ({name: x[i].orgname}));
|
||||
andOp.exprs.add (equalOp);
|
||||
|
||||
if (row[i])
|
||||
equalOp.exprs.add (new Sql.Value ({value: row[i]}));
|
||||
else if (x[i].flags & Db.Conn.Flag.AI && forInsert)
|
||||
equalOp.exprs.add (new Sql.Func ({name: 'LAST_INSERT_ID'}));
|
||||
else
|
||||
break;
|
||||
|
||||
keyFound = true;
|
||||
}
|
||||
|
||||
return (keyFound) ? andOp : null;
|
||||
}
|
||||
|
||||
,sendInsert: function (table, rowIndex, singleColumn, newValue)
|
||||
{
|
||||
var where = this.getWhere (table, rowIndex, true);
|
||||
|
||||
if (!where)
|
||||
return;
|
||||
|
||||
var multiStmt = new Sql.MultiStmt ();
|
||||
var target = this.getTarget (table);
|
||||
|
||||
var insert = new Sql.Insert ();
|
||||
insert.addTarget (target);
|
||||
multiStmt.addStmt (insert);
|
||||
|
||||
var x = this.defaults;
|
||||
|
||||
for (var i = 0; i < x.length; i++)
|
||||
if (x[i].table === table)
|
||||
insert.addSet (x[i].field, x[i].value);
|
||||
|
||||
var x = this.columnDefaults;
|
||||
|
||||
for (var i = 0; i < x.length; i++)
|
||||
if (x[i].table === table)
|
||||
insert.addSet (x[i].field, this.get (rowIndex, x[i].srcColumn));
|
||||
|
||||
var select = new Sql.Select ({where: where});
|
||||
select.addTarget (target);
|
||||
multiStmt.addStmt (select);
|
||||
|
||||
var columns = [];
|
||||
var row = this.data[rowIndex];
|
||||
var x = this.columns;
|
||||
|
||||
for (var i = 0; i < x.length; i++)
|
||||
if (x[i].table === table)
|
||||
{
|
||||
var setValue = singleColumn != i ? row[i] : newValue;
|
||||
|
||||
if (setValue)
|
||||
insert.addSet (x[i].orgname, setValue);
|
||||
|
||||
select.addField (x[i].orgname);
|
||||
columns.push (i);
|
||||
}
|
||||
|
||||
var updateData = {
|
||||
row: rowIndex
|
||||
,columns: columns
|
||||
};
|
||||
|
||||
this._conn.execStmt (multiStmt,
|
||||
this.updateDone.bind (this, updateData));
|
||||
}
|
||||
|
||||
,selectDone: function (resultSet)
|
||||
{
|
||||
var result;
|
||||
var dataResult;
|
||||
|
||||
for (var i = 0; result = resultSet.fetchResult (); i++)
|
||||
if (i == this._resultIndex)
|
||||
dataResult = result;
|
||||
|
||||
if (dataResult && typeof dataResult === 'object')
|
||||
{
|
||||
this.sortColumn = -1;
|
||||
this.data = dataResult.data;
|
||||
this.columns = dataResult.columns;
|
||||
this.columnMap = dataResult.columnMap;
|
||||
this.repairColumns (this.columns);
|
||||
this.refreshMainTable ();
|
||||
this.setStatus (Db.Model.Status.READY);
|
||||
}
|
||||
else
|
||||
this.cleanData (true);
|
||||
}
|
||||
|
||||
,updateDone: function (updateData, resultSet)
|
||||
{
|
||||
var newValues;
|
||||
|
||||
if (resultSet.fetchResult () && (newValues = resultSet.fetchRow ()))
|
||||
this.performUpdate (updateData.row, updateData.columns, newValues);
|
||||
}
|
||||
|
||||
,performUpdate: function (rowIndex, columns, newValues)
|
||||
{
|
||||
this.signalEmit ('row-updated-before', rowIndex);
|
||||
|
||||
var row = this.data[rowIndex];
|
||||
|
||||
for (var i = 0; i < columns.length; i++)
|
||||
row[columns[i]] = newValues[i];
|
||||
|
||||
this.signalEmit ('row-updated', rowIndex, columns);
|
||||
}
|
||||
|
||||
,deleteDone: function (rowIndex, resultSet)
|
||||
{
|
||||
if (resultSet.fetchResult())
|
||||
this.performDelete (rowIndex);
|
||||
}
|
||||
|
||||
,performDelete: function (rowIndex)
|
||||
{
|
||||
var row = this.data[rowIndex];
|
||||
|
||||
if (!this.requestedMainTable)
|
||||
{
|
||||
this.signalEmit ('row-deleted-before', rowIndex);
|
||||
this.data.splice (rowIndex, 1);
|
||||
this.signalEmit ('row-deleted', rowIndex);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.signalEmit ('row-updated-before', rowIndex);
|
||||
|
||||
var columns = [];
|
||||
|
||||
for (var i = 0; i < this.columns.length; i++)
|
||||
if (this.columns[i].table == this._mainTable)
|
||||
{
|
||||
row[i] = null;
|
||||
columns.push (i);
|
||||
}
|
||||
|
||||
this.signalEmit ('row-updated', rowIndex, columns);
|
||||
}
|
||||
}
|
||||
|
||||
// Delete when MySQL FLAG and view orgname "bugs" are repaired:
|
||||
|
||||
,tableInfo: {}
|
||||
,fieldFlags: {}
|
||||
|
||||
,setTableInfo: function (table, orgtable, db)
|
||||
{
|
||||
this.tableInfo[table] =
|
||||
({
|
||||
orgtable: orgtable
|
||||
,db: db
|
||||
});
|
||||
}
|
||||
|
||||
,setFieldFlags: function (field, flags)
|
||||
{
|
||||
this.fieldFlags[field] = flags;
|
||||
}
|
||||
|
||||
,repairColumns: function (columns)
|
||||
{
|
||||
for (var i = 0; i < columns.length; i++)
|
||||
{
|
||||
var newFlags = this.fieldFlags[columns[i].name];
|
||||
|
||||
if (newFlags)
|
||||
columns[i].flags |= newFlags;
|
||||
|
||||
var tableInfo = this.tableInfo[columns[i].table];
|
||||
|
||||
if (tableInfo)
|
||||
{
|
||||
columns[i].orgtable = tableInfo.orgtable;
|
||||
columns[i].db = tableInfo.db;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
Db.Param = new Class
|
||||
({
|
||||
Extends: Vn.Param
|
||||
,Tag: 'db-param'
|
||||
,Parent: 'form'
|
||||
,Properties:
|
||||
{
|
||||
/**
|
||||
* The form field referenced by this param.
|
||||
**/
|
||||
column:
|
||||
{
|
||||
type: String
|
||||
,set: function (x)
|
||||
{
|
||||
this._columnName = x;
|
||||
this.refresh ();
|
||||
}
|
||||
,get: function ()
|
||||
{
|
||||
this._columnName;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The form referenced by this param.
|
||||
**/
|
||||
form:
|
||||
{
|
||||
type: Db.Form
|
||||
,set: function (x)
|
||||
{
|
||||
this.link ({_form: x},
|
||||
{
|
||||
'status-changed': this.onFormChange
|
||||
,'iter-changed': this.onIterChange
|
||||
});
|
||||
|
||||
this.refresh ();
|
||||
}
|
||||
,get: function ()
|
||||
{
|
||||
return this._form;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
,_columnName: null
|
||||
,_form: null
|
||||
,formLock: false
|
||||
,columnIndex: -1
|
||||
|
||||
,initialize: function (props)
|
||||
{
|
||||
this.parent (props);
|
||||
this.on ('changed', this.onChange, this);
|
||||
}
|
||||
|
||||
,refresh: function ()
|
||||
{
|
||||
if (this._form)
|
||||
{
|
||||
this.onFormChange ();
|
||||
this.onIterChange ();
|
||||
}
|
||||
}
|
||||
|
||||
,onFormChange: function ()
|
||||
{
|
||||
if (this._columnName != null)
|
||||
this.columnIndex = this._form.getColumnIndex (this._columnName);
|
||||
}
|
||||
|
||||
,onIterChange: function ()
|
||||
{
|
||||
this.formLock = true;
|
||||
|
||||
if (this.columnIndex != -1)
|
||||
this.value = this._form.getByIndex (this.columnIndex);
|
||||
else
|
||||
this.value = undefined;
|
||||
|
||||
this.formLock = false;
|
||||
}
|
||||
|
||||
,onChange: function ()
|
||||
{
|
||||
if (!this.formLock && this.columnIndex != -1)
|
||||
this._form.setByIndex (this.columnIndex, this._value);
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,89 @@
|
|||
/**
|
||||
* This class stores the database results.
|
||||
**/
|
||||
Db.ResultSet = new Class
|
||||
({
|
||||
results: null
|
||||
,error: null
|
||||
|
||||
/**
|
||||
* Initilizes the resultset object.
|
||||
**/
|
||||
,initialize: function (results, error)
|
||||
{
|
||||
this.results = results;
|
||||
this.error = error;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the query error.
|
||||
*
|
||||
* @return {Db.Err} the error or null if no errors hapened
|
||||
**/
|
||||
,getError: function ()
|
||||
{
|
||||
return this.error;
|
||||
}
|
||||
|
||||
,fetch: function ()
|
||||
{
|
||||
if (this.results !== null
|
||||
&& this.results.length > 0)
|
||||
return this.results.shift ();
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetchs the next result from the resultset.
|
||||
*
|
||||
* @return {Db.Result} the result or %null if error or there are no more results
|
||||
**/
|
||||
,fetchResult: function ()
|
||||
{
|
||||
var result = this.fetch ();
|
||||
|
||||
if (result !== null)
|
||||
{
|
||||
if (result.data instanceof Array)
|
||||
return new Db.Result (result.data, result.field);
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetchs the first row from the next resultset.
|
||||
*
|
||||
* @return {Array} the row if success, %null otherwise
|
||||
**/
|
||||
,fetchRow: function ()
|
||||
{
|
||||
var result = this.fetch ();
|
||||
|
||||
if (result !== null
|
||||
&& result.data instanceof Array
|
||||
&& result.data.length > 0)
|
||||
return result.data[0];
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetchs the first row and column value from the next resultset.
|
||||
*
|
||||
* @return {Object} the value if success, %null otherwise
|
||||
**/
|
||||
,fetchValue: function ()
|
||||
{
|
||||
var row = this.fetchRow ();
|
||||
|
||||
if (row instanceof Array && row.length > 0)
|
||||
return row[0];
|
||||
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
/**
|
||||
* This class stores a database result.
|
||||
**/
|
||||
Db.Result = new Class
|
||||
({
|
||||
/**
|
||||
* Initilizes the result object.
|
||||
**/
|
||||
initialize: function (data, columns)
|
||||
{
|
||||
this.data = data;
|
||||
this.columns = columns;
|
||||
this.columnMap = null;
|
||||
this.row = -1;
|
||||
|
||||
if (columns)
|
||||
{
|
||||
this.columnMap = {};
|
||||
|
||||
for (var i = 0; i < columns.length; i++)
|
||||
this.columnMap[columns[i].name] = i;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a value from de result.
|
||||
*
|
||||
* @param {String} columnName The column name
|
||||
* @return {Object} The cell value
|
||||
**/
|
||||
,get: function (columnName)
|
||||
{
|
||||
var columnIndex = this.columnMap[columnName];
|
||||
return this.data[this.row][columnIndex];
|
||||
}
|
||||
|
||||
/**
|
||||
* Resets the result iterator.
|
||||
**/
|
||||
,reset: function ()
|
||||
{
|
||||
this.row = -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* Moves the internal iterator to the next row.
|
||||
**/
|
||||
,next: function ()
|
||||
{
|
||||
this.row++;
|
||||
|
||||
if (this.row >= this.data.length)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,84 @@
|
|||
/**
|
||||
* Represents a grid column. This is an abstract class and should not be
|
||||
* instantiated directly.
|
||||
**/
|
||||
Htk.Column = new Class
|
||||
({
|
||||
Extends: Vn.Object
|
||||
,Tag: 'htk-column'
|
||||
,Parent: 'grid'
|
||||
,Properties:
|
||||
{
|
||||
value:
|
||||
{
|
||||
type: Object
|
||||
,value: null
|
||||
},
|
||||
column:
|
||||
{
|
||||
type: String
|
||||
,value: null
|
||||
},
|
||||
columnIndex:
|
||||
{
|
||||
type: Number
|
||||
,value: -1
|
||||
},
|
||||
title:
|
||||
{
|
||||
type: String
|
||||
,value: null
|
||||
},
|
||||
editable:
|
||||
{
|
||||
type: Boolean
|
||||
,value: false
|
||||
},
|
||||
renderer:
|
||||
{
|
||||
type: Function
|
||||
,value: false
|
||||
},
|
||||
grid:
|
||||
{
|
||||
type: Htk.Grid
|
||||
,set: function (x)
|
||||
{
|
||||
if (x)
|
||||
x.appendColumn (this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the column.
|
||||
**/
|
||||
,initialize: function (props)
|
||||
{
|
||||
this.parent (props);
|
||||
this.td = document.createElement ('td');
|
||||
}
|
||||
|
||||
/**
|
||||
* Draws the cell and returns its associated td.
|
||||
*
|
||||
* @param {HTMLTableRow} tr the table row
|
||||
* @return {HTMLTableData} the rendered cell
|
||||
**/
|
||||
,render: function (tr)
|
||||
{
|
||||
return this.td.cloneNode (true);
|
||||
}
|
||||
|
||||
,updateColumnIndex: function (model)
|
||||
{
|
||||
if (this.column)
|
||||
this.columnIndex = model.getColumnIndex (this.column);
|
||||
}
|
||||
|
||||
,changed: function (tr, newValue)
|
||||
{
|
||||
this.signalEmit ('changed', tr.rowIndex - 1, newValue);
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
Htk.ColumnButton = new Class
|
||||
({
|
||||
Extends: Htk.Column
|
||||
,Tag: 'htk-column-button'
|
||||
,Properties:
|
||||
{
|
||||
image:
|
||||
{
|
||||
type: String
|
||||
,value: null
|
||||
},
|
||||
tip:
|
||||
{
|
||||
type: String
|
||||
,value: null
|
||||
}
|
||||
}
|
||||
|
||||
,render: function (tr)
|
||||
{
|
||||
var td = this.parent (tr);
|
||||
|
||||
var button = document.createElement ('button');
|
||||
button.className = 'cell-button';
|
||||
button.addEventListener ('click',
|
||||
this.buttonClicked.bind (this, this.value, tr));
|
||||
td.appendChild (button);
|
||||
|
||||
var img = document.createElement ('img');
|
||||
img.src = this.image;
|
||||
button.appendChild (img);
|
||||
|
||||
if (this.tip)
|
||||
{
|
||||
button.title = _(this.tip);
|
||||
img.title = _(this.tip);
|
||||
}
|
||||
|
||||
return td;
|
||||
}
|
||||
|
||||
,buttonClicked: function (value, tr)
|
||||
{
|
||||
this.signalEmit ('clicked', value, tr.rowIndex - 1);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,28 @@
|
|||
Htk.ColumnCheck = new Class
|
||||
({
|
||||
Extends: Htk.Column
|
||||
,Tag: 'htk-column-check'
|
||||
|
||||
,render: function (tr)
|
||||
{
|
||||
var checkButton = document.createElement ('input');
|
||||
checkButton.type = 'checkbox';
|
||||
checkButton.checked = this.value;
|
||||
|
||||
if (this.editable)
|
||||
checkButton.addEventListener ('changed',
|
||||
this.inputChanged.bind (this, tr, node));
|
||||
else
|
||||
checkButton.disabled = true;
|
||||
|
||||
var td = this.parent (tr);
|
||||
td.style.textAlign = 'center';
|
||||
td.appendChild (checkButton);
|
||||
return td;
|
||||
}
|
||||
|
||||
,inputChanged: function (tr, node)
|
||||
{
|
||||
this.changed (tr, node.value);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,36 @@
|
|||
Htk.ColumnDate = new Class
|
||||
({
|
||||
Extends: Htk.Column
|
||||
,Tag: 'htk-column-date'
|
||||
,Properties:
|
||||
{
|
||||
/**
|
||||
* Format that applies to the value.
|
||||
**/
|
||||
format:
|
||||
{
|
||||
type: String
|
||||
,set: function (x)
|
||||
{
|
||||
this._format = _(x);
|
||||
}
|
||||
,get: function ()
|
||||
{
|
||||
return this._format;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
,_format: _('%a, %e %b %Y')
|
||||
|
||||
,render: function (tr)
|
||||
{
|
||||
var text = Vn.Date.strftime (this.value, this._format);
|
||||
|
||||
var td = this.parent (tr);
|
||||
td.style.textAlign = 'left';
|
||||
td.appendChild (document.createTextNode (text));
|
||||
|
||||
return td;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,159 @@
|
|||
Htk.ColumnImage = new Class
|
||||
({
|
||||
Extends: Htk.Column
|
||||
,Tag: 'htk-column-image'
|
||||
,Properties:
|
||||
{
|
||||
/**
|
||||
* The directory where the images are allocated.
|
||||
**/
|
||||
directory:
|
||||
{
|
||||
type: String
|
||||
,set: function (x)
|
||||
{
|
||||
this._directory = x;
|
||||
this.basedir = Vn.Config['imageDir'] +'/'+ x;
|
||||
}
|
||||
,get: function ()
|
||||
{
|
||||
return this._directory;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* The directory where the images are allocated.
|
||||
**/
|
||||
subdir:
|
||||
{
|
||||
type: String
|
||||
,value: null
|
||||
},
|
||||
/**
|
||||
* Whether to show the full image when mouse hover.
|
||||
**/
|
||||
showFull:
|
||||
{
|
||||
type: Boolean
|
||||
,value: false
|
||||
}
|
||||
}
|
||||
|
||||
,_directory: null
|
||||
,basedir: null
|
||||
|
||||
,initialize: function (props)
|
||||
{
|
||||
this.parent (props);
|
||||
this.fullImage = new Htk.FullImage ();
|
||||
}
|
||||
|
||||
,render: function (tr)
|
||||
{
|
||||
var td = this.parent (tr);
|
||||
|
||||
var img = document.createElement ('img');
|
||||
img.alt = ''
|
||||
td.appendChild (img);
|
||||
|
||||
var cell =
|
||||
{
|
||||
img: img
|
||||
,value: this.value
|
||||
,tr: tr
|
||||
,error: false
|
||||
};
|
||||
|
||||
this.setSrc (cell, true);
|
||||
|
||||
if (this.showFull)
|
||||
{
|
||||
img.addEventListener ('mouseover', this.onMouseOver.bind (this, cell));
|
||||
img.addEventListener ('mouseout', this.onMouseOut.bind (this));
|
||||
}
|
||||
if (this.editable)
|
||||
{
|
||||
img.className = 'editable';
|
||||
img.addEventListener ('dblclick', this.onDoubleClick.bind (this, cell));
|
||||
img.addEventListener ('error', this.onImageError.bind (this, cell));
|
||||
}
|
||||
|
||||
return td;
|
||||
}
|
||||
|
||||
,setSrc: function (cell, useCache)
|
||||
{
|
||||
if (cell.value)
|
||||
{
|
||||
var url = this.basedir +'/';
|
||||
|
||||
if (this.subdir)
|
||||
url += this.subdir +'/';
|
||||
|
||||
url += cell.value;
|
||||
|
||||
if (!useCache)
|
||||
query = '?'+ new Date ().getTime ();
|
||||
|
||||
cell.img.src = url;
|
||||
}
|
||||
else
|
||||
this.onImageError (cell);
|
||||
}
|
||||
|
||||
,onImageError: function (cell)
|
||||
{
|
||||
if (!cell.error)
|
||||
{
|
||||
cell.error = true;
|
||||
cell.img.src = 'image/empty.png';
|
||||
}
|
||||
}
|
||||
|
||||
,onMouseOver: function (cell)
|
||||
{
|
||||
if (!cell.error)
|
||||
this.fullImage.show (this.basedir, cell.value);
|
||||
}
|
||||
|
||||
,onMouseOut: function ()
|
||||
{
|
||||
this.fullImage.hide ();
|
||||
}
|
||||
|
||||
,onDoubleClick: function (cell)
|
||||
{
|
||||
var editor = new Htk.ImageEditor ();
|
||||
editor.setData (cell.value, this._directory);
|
||||
|
||||
this.nameChangedHandler = this.onNameChange.bind (this, cell);
|
||||
editor.on ('name-changed', this.nameChangedHandler);
|
||||
|
||||
this.fileUploadedHandler = this.onFileUpload.bind (this, cell);
|
||||
editor.on ('file-uploaded', this.fileUploadedHandler);
|
||||
|
||||
this.editorClosedHandler = this.onEditorClose.bind (this);
|
||||
editor.on ('closed', this.editorClosedHandler);
|
||||
|
||||
editor.showPopup (cell.img);
|
||||
}
|
||||
|
||||
,onNameChange: function (cell, editor, value)
|
||||
{
|
||||
cell.value = value;
|
||||
this.setSrc (cell, true);
|
||||
this.changed (cell.tr, value);
|
||||
}
|
||||
|
||||
,onFileUpload: function (cell, editor)
|
||||
{
|
||||
this.setSrc (cell, false);
|
||||
editor.hidePopup ();
|
||||
}
|
||||
|
||||
,onEditorClose: function (editor)
|
||||
{
|
||||
editor.disconnect ('name-changed', this.nameChangedHandler);
|
||||
editor.disconnect ('file-uploaded', this.fileUploadedHandler);
|
||||
editor.disconnect ('closed', this.editorClosedHandler);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,41 @@
|
|||
|
||||
Htk.ColumnLink = new Class
|
||||
({
|
||||
Extends: Htk.Column
|
||||
,Tag: 'htk-column-link'
|
||||
,Properties:
|
||||
{
|
||||
/**
|
||||
* The link url.
|
||||
**/
|
||||
href:
|
||||
{
|
||||
type: String
|
||||
,value: null
|
||||
},
|
||||
/**
|
||||
* the target where the link is opened.
|
||||
**/
|
||||
target:
|
||||
{
|
||||
type: String
|
||||
,value: null
|
||||
}
|
||||
}
|
||||
|
||||
,render: function (tr)
|
||||
{
|
||||
var link = document.createElement ('a');
|
||||
link.href = this.href;
|
||||
link.appendChild (document.createTextNode (this.value));
|
||||
|
||||
if (this.target)
|
||||
link.target = this.target;
|
||||
|
||||
var td = this.parent (tr);
|
||||
td.style.textAlign = 'left';
|
||||
td.appendChild (link);
|
||||
return td;
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
Htk.ColumnRadio = new Class
|
||||
({
|
||||
Extends: Htk.Column
|
||||
,Tag: 'htk-column-radio'
|
||||
,Properties:
|
||||
{
|
||||
param:
|
||||
{
|
||||
type: Vn.Param
|
||||
,set: function (x)
|
||||
{
|
||||
this.radioGroup.master = x;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
,initialize: function (props)
|
||||
{
|
||||
this.parent (props);
|
||||
this.radioGroup = new Htk.RadioGroup ();
|
||||
}
|
||||
|
||||
,render: function (tr)
|
||||
{
|
||||
var td = this.parent (tr);
|
||||
|
||||
var radio = this.radioGroup.createButton (this.value);
|
||||
td.appendChild (radio);
|
||||
|
||||
return td;
|
||||
}
|
||||
|
||||
,clear: function ()
|
||||
{
|
||||
this.radioGroup.clear ();
|
||||
}
|
||||
});
|
|
@ -0,0 +1,61 @@
|
|||
Htk.ColumnSpin = new Class
|
||||
({
|
||||
Extends: Htk.Column
|
||||
,Tag: 'htk-column-spin'
|
||||
,Properties:
|
||||
{
|
||||
/**
|
||||
* The text to append to the number.
|
||||
**/
|
||||
unit:
|
||||
{
|
||||
type: String
|
||||
,value: null
|
||||
},
|
||||
/**
|
||||
* The number of decimal places to display.
|
||||
**/
|
||||
digits: {
|
||||
type: Number
|
||||
,value: 0
|
||||
}
|
||||
}
|
||||
|
||||
,render: function (tr)
|
||||
{
|
||||
var td = this.parent (tr);
|
||||
td.style.textAlign = 'right';
|
||||
|
||||
var valueString = null;
|
||||
|
||||
if (this.value !== null && this.value !== undefined)
|
||||
valueString = new Number (this.value).toFixed (this.digits);
|
||||
|
||||
if (this.editable)
|
||||
{
|
||||
var entry = document.createElement ('input');
|
||||
entry.type = 'text';
|
||||
entry.className = 'cell-spin';
|
||||
entry.addEventListener ('change', this.inputChanged.bind (this, tr, entry));
|
||||
td.appendChild (entry);
|
||||
|
||||
if (valueString)
|
||||
entry.value = valueString;
|
||||
}
|
||||
else if (valueString)
|
||||
{
|
||||
if (this.unit)
|
||||
valueString = valueString +' '+ this.unit;
|
||||
|
||||
var text = document.createTextNode (valueString);
|
||||
td.appendChild (text);
|
||||
}
|
||||
|
||||
return td;
|
||||
}
|
||||
|
||||
,inputChanged: function (tr, entry)
|
||||
{
|
||||
this.changed (tr, parseInt (entry.value));
|
||||
}
|
||||
});
|
|
@ -0,0 +1,55 @@
|
|||
Htk.ColumnText = new Class
|
||||
({
|
||||
Extends: Htk.Column
|
||||
,Tag: 'htk-column-text'
|
||||
,Properties:
|
||||
{
|
||||
/**
|
||||
* Format that applies to the value.
|
||||
**/
|
||||
format:
|
||||
{
|
||||
type: String
|
||||
,set: function (x)
|
||||
{
|
||||
this._format = _(x);
|
||||
}
|
||||
,get: function ()
|
||||
{
|
||||
return this._format;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
,_format: null
|
||||
|
||||
,render: function (tr)
|
||||
{
|
||||
var node;
|
||||
|
||||
if (this.editable)
|
||||
{
|
||||
var value = this.value ? this.value : '';
|
||||
|
||||
node = document.createElement ('input');
|
||||
node.type = 'text';
|
||||
node.className = 'cell-text';
|
||||
node.value = value;
|
||||
node.addEventListener ('changed',
|
||||
this.inputChanged.bind (this, tr, node));
|
||||
}
|
||||
else
|
||||
node = document.createTextNode (
|
||||
Vn.Value.format (this.value, this._format));
|
||||
|
||||
var td = this.parent (tr);
|
||||
td.style.textAlign = 'left';
|
||||
td.appendChild (node);
|
||||
return td;
|
||||
}
|
||||
|
||||
,inputChanged: function (tr, node)
|
||||
{
|
||||
this.changed (tr, node.value);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,131 @@
|
|||
|
||||
Htk.Field = new Class
|
||||
({
|
||||
Extends: Htk.Widget
|
||||
,Tag: 'htk-field'
|
||||
,Child: 'param'
|
||||
,Properties:
|
||||
{
|
||||
value:
|
||||
{
|
||||
type: Object
|
||||
,set: function (x)
|
||||
{
|
||||
if (Vn.Value.compare (x, this._value))
|
||||
return;
|
||||
|
||||
if (x instanceof Date)
|
||||
x = x.clone ();
|
||||
|
||||
this.valueChanged (x);
|
||||
this.putValue (x);
|
||||
}
|
||||
,get: function (x)
|
||||
{
|
||||
return this._value;
|
||||
}
|
||||
},
|
||||
param:
|
||||
{
|
||||
type: Vn.Param
|
||||
,set: function (x)
|
||||
{
|
||||
this.link ({_param: x}, {'changed': this.onParamChange});
|
||||
this.onParamChange ();
|
||||
}
|
||||
,get: function ()
|
||||
{
|
||||
return this._param;
|
||||
}
|
||||
},
|
||||
editable:
|
||||
{
|
||||
type: Boolean
|
||||
,set: function (x)
|
||||
{
|
||||
if (x != this._editable)
|
||||
{
|
||||
this._editable = x;
|
||||
this.setEditable (x);
|
||||
}
|
||||
}
|
||||
,get: function ()
|
||||
{
|
||||
return this._editable;
|
||||
}
|
||||
},
|
||||
form:
|
||||
{
|
||||
type: Db.Form
|
||||
,set: function (x)
|
||||
{
|
||||
this._form = x;
|
||||
this.bindToForm ();
|
||||
}
|
||||
},
|
||||
column:
|
||||
{
|
||||
type: String
|
||||
,set: function (x)
|
||||
{
|
||||
this._paramName = x;
|
||||
this.bindToForm ();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
,_value: undefined
|
||||
,_param: null
|
||||
,_editable: true
|
||||
,ignoreParamChange: false
|
||||
|
||||
,onParamChange: function ()
|
||||
{
|
||||
this.ignoreParamChange = true;
|
||||
this.value = this._param.value;
|
||||
this.ignoreParamChange = false;
|
||||
}
|
||||
|
||||
,bindToForm: function ()
|
||||
{
|
||||
if (this._form && this._paramName)
|
||||
this.param = this._form.getParam (this._paramName);
|
||||
}
|
||||
|
||||
/**
|
||||
* Virtual method that must be implemented by class childs to set the entry
|
||||
* editable.
|
||||
*
|
||||
* @param {Boolean} editable Whether the user is allowed to edit the entry
|
||||
**/
|
||||
,setEditable: function (editable) {}
|
||||
|
||||
/**
|
||||
* Virtual method that must be implemented by class childs to put the value
|
||||
* on the associated entry.
|
||||
*
|
||||
* @param {Object} value The new value for the entry
|
||||
**/
|
||||
,putValue: function (value) {}
|
||||
|
||||
/**
|
||||
* Protected method that should be called from class childs when the value
|
||||
* on the associated entry changes.
|
||||
*
|
||||
* @param {Object} value The new entry value
|
||||
**/
|
||||
,valueChanged: function (value)
|
||||
{
|
||||
this._value = value;
|
||||
|
||||
if (this._param && !this.ignoreParamChange)
|
||||
{
|
||||
this._param.blockSignal ('changed', this.onParamChange, true);
|
||||
this._param.value = value;
|
||||
this._param.blockSignal ('changed', this.onParamChange, false);
|
||||
}
|
||||
|
||||
this.signalEmit ('changed');
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,265 @@
|
|||
Htk.Calendar = new Class
|
||||
({
|
||||
Extends: Htk.Field
|
||||
,Implements: Htk.Popup
|
||||
,Tag: 'htk-calendar'
|
||||
|
||||
,tds: []
|
||||
,selectedTd: null
|
||||
,todayTd: null
|
||||
,year: null
|
||||
,month: null
|
||||
|
||||
,initialize: function (props)
|
||||
{
|
||||
this.parent (props);
|
||||
var len = Vn.Date.WDays.length;
|
||||
|
||||
this.createElement ('div');
|
||||
this.node.className = 'calendar';
|
||||
|
||||
var table = document.createElement ('table');
|
||||
this.node.appendChild (table);
|
||||
|
||||
var colgroup = document.createElement ('colgroup');
|
||||
table.appendChild (colgroup);
|
||||
|
||||
for (var i = 0; i < len; i++)
|
||||
colgroup.appendChild (document.createElement ('col'));
|
||||
|
||||
var thead = document.createElement ('thead');
|
||||
table.appendChild (thead);
|
||||
|
||||
var tr = document.createElement ('tr');
|
||||
thead.appendChild (tr);
|
||||
|
||||
var th = document.createElement ('th');
|
||||
th.appendChild (document.createTextNode ('<'));
|
||||
th.className = 'button';
|
||||
th.addEventListener ('click', this.prevMonthClicked.bind (this));
|
||||
tr.appendChild (th);
|
||||
|
||||
var monthNode = document.createElement ('th');
|
||||
monthNode.colSpan = 5;
|
||||
tr.appendChild (monthNode);
|
||||
|
||||
var th = document.createElement ('th');
|
||||
th.appendChild (document.createTextNode ('>'));
|
||||
th.className = 'button';
|
||||
th.addEventListener ('click', this.nextMonthClicked.bind (this));
|
||||
tr.appendChild (th);
|
||||
|
||||
var tr = document.createElement ('tr');
|
||||
thead.appendChild (tr);
|
||||
|
||||
for (i = 1; i <= len; i++)
|
||||
{
|
||||
var th = document.createElement ('th');
|
||||
tr.appendChild (th);
|
||||
|
||||
var weekday = Vn.Date.AbrWDays [i%len];
|
||||
th.appendChild (document.createTextNode (weekday));
|
||||
}
|
||||
|
||||
var tfoot = document.createElement ('tfoot');
|
||||
table.appendChild (tfoot);
|
||||
|
||||
var tr = document.createElement ('tr');
|
||||
tfoot.appendChild (tr);
|
||||
|
||||
var th = document.createElement ('th');
|
||||
th.appendChild (document.createTextNode ('<'));
|
||||
th.className = 'button';
|
||||
th.addEventListener ('click', this.prevYearClicked.bind (this));
|
||||
tr.appendChild (th);
|
||||
|
||||
var yearNode = document.createElement ('th');
|
||||
yearNode.colSpan = 5;
|
||||
tr.appendChild (yearNode);
|
||||
|
||||
var th = document.createElement ('th');
|
||||
th.appendChild (document.createTextNode ('>'));
|
||||
th.className = 'button';
|
||||
th.addEventListener ('click', this.nextYearClicked.bind (this));
|
||||
tr.appendChild (th);
|
||||
|
||||
var tbody = document.createElement ('tbody');
|
||||
table.appendChild (tbody);
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
{
|
||||
tr = document.createElement ('tr');
|
||||
tbody.appendChild (tr);
|
||||
|
||||
for (j = 0; j < len; j++)
|
||||
{
|
||||
td = document.createElement ('td');
|
||||
td.addEventListener ('click', this.dayClicked.bind (this, td, i*len+j));
|
||||
tr.appendChild (td);
|
||||
this.tds.push (td);
|
||||
}
|
||||
}
|
||||
|
||||
this.monthNode = monthNode;
|
||||
this.yearNode = yearNode;
|
||||
this.goToCurrentMonth ();
|
||||
}
|
||||
|
||||
,getFirstWeekDay: function ()
|
||||
{
|
||||
var weekDay = new Date (this.year, this.month, 1).getDay ();
|
||||
return (weekDay != 0) ? weekDay - 1 : 6;
|
||||
}
|
||||
|
||||
,getMonthDays: function ()
|
||||
{
|
||||
if (this.month > 6)
|
||||
return (this.month % 2 != 0) ? 31 : 30;
|
||||
else if (this.month != 1)
|
||||
return (this.month % 2 != 1) ? 31 : 30;
|
||||
else
|
||||
return (this.year % 4 != 0) ? 28 : 29;
|
||||
}
|
||||
|
||||
,goToMonth: function (year, month)
|
||||
{
|
||||
if (year)
|
||||
this.year = year;
|
||||
if (month)
|
||||
this.month = month;
|
||||
|
||||
this.refresh ();
|
||||
}
|
||||
|
||||
,goToSelectedMonth: function ()
|
||||
{
|
||||
var date = this._value;
|
||||
|
||||
if (date instanceof Date)
|
||||
this.goToMonth (date.getFullYear (), date.getMonth ());
|
||||
else
|
||||
this.goToCurrentMonth ();
|
||||
}
|
||||
|
||||
,goToCurrentMonth: function ()
|
||||
{
|
||||
var date = new Date ();
|
||||
this.goToMonth (date.getFullYear (), date.getMonth ());
|
||||
}
|
||||
|
||||
,refresh: function ()
|
||||
{
|
||||
Vn.Node.setText (this.yearNode, this.year);
|
||||
Vn.Node.setText (this.monthNode, Vn.Date.Months[this.month]);
|
||||
|
||||
var firstWeekDay = this.getFirstWeekDay ();
|
||||
var monthDays = this.getMonthDays ();
|
||||
|
||||
var day = 1;
|
||||
|
||||
for (i = 0; i < this.tds.length; i++)
|
||||
{
|
||||
if (firstWeekDay <= i && day <= monthDays)
|
||||
Vn.Node.setText (this.tds[i], day++);
|
||||
else
|
||||
Vn.Node.removeChilds (this.tds[i]);
|
||||
}
|
||||
|
||||
// Marks the current day
|
||||
|
||||
var today = new Date ();
|
||||
|
||||
if (this.year == today.getFullYear ()
|
||||
&& this.month == today.getMonth ())
|
||||
{
|
||||
var tdIndex = (firstWeekDay + today.getDate ()) - 1;
|
||||
this.tds[tdIndex].style.fontWeight = 'bold';
|
||||
this.todayTd = this.tds[tdIndex];
|
||||
}
|
||||
else if (this.todayTd)
|
||||
{
|
||||
this.todayTd.style.fontWeight = '';
|
||||
this.todayTd = null;
|
||||
}
|
||||
|
||||
// Marks the selected day
|
||||
|
||||
var date = this._value;
|
||||
|
||||
if (date instanceof Date
|
||||
&& this.year == date.getFullYear ()
|
||||
&& this.month == date.getMonth ())
|
||||
{
|
||||
var tdIndex = (firstWeekDay + date.getDate ()) - 1;
|
||||
this.selectTd (this.tds[tdIndex]);
|
||||
}
|
||||
else
|
||||
this.selectTd (null);
|
||||
}
|
||||
|
||||
,selectTd: function (td)
|
||||
{
|
||||
if (this.selectedTd)
|
||||
this.selectedTd.className = undefined;
|
||||
if (td)
|
||||
td.className = 'highlight';
|
||||
|
||||
this.selectedTd = td;
|
||||
}
|
||||
|
||||
,putValue: function (value)
|
||||
{
|
||||
this.goToSelectedMonth ();
|
||||
}
|
||||
|
||||
,dayClicked: function (td, tdIndex)
|
||||
{
|
||||
var monthDay = (tdIndex - this.getFirstWeekDay ()) + 1;
|
||||
|
||||
if (monthDay >= 1 && monthDay <= this.getMonthDays ())
|
||||
{
|
||||
this.selectTd (td);
|
||||
|
||||
var newDate = new Date (this.year, this.month, monthDay);
|
||||
this.valueChanged (newDate);
|
||||
}
|
||||
}
|
||||
|
||||
,prevMonthClicked: function ()
|
||||
{
|
||||
if (this.month > 0)
|
||||
this.month--;
|
||||
else
|
||||
{
|
||||
this.month = 11;
|
||||
this.year--;
|
||||
}
|
||||
|
||||
this.refresh ();
|
||||
}
|
||||
|
||||
,nextMonthClicked: function ()
|
||||
{
|
||||
if (this.month < 11)
|
||||
this.month++;
|
||||
else
|
||||
{
|
||||
this.month = 0;
|
||||
this.year++;
|
||||
}
|
||||
|
||||
this.refresh ();
|
||||
}
|
||||
|
||||
,prevYearClicked: function ()
|
||||
{
|
||||
this.year--;
|
||||
this.refresh ();
|
||||
}
|
||||
|
||||
,nextYearClicked: function ()
|
||||
{
|
||||
this.year++;
|
||||
this.refresh ();
|
||||
}
|
||||
});
|
|
@ -0,0 +1,31 @@
|
|||
Htk.Check = new Class
|
||||
({
|
||||
Extends: Htk.Field
|
||||
,Tag: 'htk-check'
|
||||
|
||||
,initialize: function (props)
|
||||
{
|
||||
this.parent (props);
|
||||
this.createElement ('input');
|
||||
this.node.type = 'checkbox';
|
||||
this.node.addEventListener ('change', this.changed.bind (this));
|
||||
}
|
||||
|
||||
,changed: function ()
|
||||
{
|
||||
this.valueChanged (this.node.checked);
|
||||
}
|
||||
|
||||
,putValue: function (value)
|
||||
{
|
||||
if (value)
|
||||
this.node.checked = true;
|
||||
else
|
||||
this.node.checked = false;
|
||||
}
|
||||
|
||||
,setEditable: function (editable)
|
||||
{
|
||||
this.node.disabled = !editable;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,82 @@
|
|||
|
||||
Htk.DateChooser = new Class
|
||||
({
|
||||
Extends: Htk.Field
|
||||
,Tag: 'htk-date-chooser'
|
||||
|
||||
,format: _('%a, %e %b %Y')
|
||||
,calendar: null
|
||||
,ignoreCalendarChange: false
|
||||
|
||||
,initialize: function (props)
|
||||
{
|
||||
this.parent (props);
|
||||
this.createElement ('div');
|
||||
this.node.className = 'date-chooser';
|
||||
|
||||
this.label = document.createElement ('label');
|
||||
this.node.appendChild (this.label);
|
||||
|
||||
this.setEditable (this._editable);
|
||||
}
|
||||
|
||||
,putValue: function (value)
|
||||
{
|
||||
var dateString;
|
||||
|
||||
if (value instanceof Date)
|
||||
dateString = Vn.Date.strftime (value, this.format);
|
||||
else
|
||||
dateString = '';
|
||||
|
||||
Vn.Node.setText (this.label, dateString);
|
||||
}
|
||||
|
||||
,setEditable: function (editable)
|
||||
{
|
||||
if (editable && !this.calendar)
|
||||
{
|
||||
this.button = document.createElement ('button');
|
||||
this.button.title = _('ChangeDate');
|
||||
this.button.addEventListener ('click', this.showCalendar.bind (this));
|
||||
this.node.insertBefore (this.button, this.label);
|
||||
|
||||
var img = document.createElement ('img');
|
||||
img.alt = _('ChangeDate');
|
||||
img.src = 'image/calendar.png';
|
||||
this.button.appendChild (img);
|
||||
|
||||
var calendar = new Htk.Calendar ();
|
||||
calendar.on ('changed', this.calendarChanged.bind (this));
|
||||
this.calendar = calendar;
|
||||
}
|
||||
else if (!editable)
|
||||
{
|
||||
this.calendar = null;
|
||||
this.node.removeChild (this.button);
|
||||
}
|
||||
}
|
||||
|
||||
,calendarChanged: function (calendar)
|
||||
{
|
||||
if (this.ignoreCalendarChange)
|
||||
return;
|
||||
|
||||
this.calendar.hidePopup ();
|
||||
|
||||
var newDate = calendar.value;
|
||||
this.putValue (newDate);
|
||||
this.valueChanged (newDate);
|
||||
}
|
||||
|
||||
,showCalendar: function (event)
|
||||
{
|
||||
this.ignoreCalendarChange = true;
|
||||
this.calendar.value = this._value;
|
||||
this.calendar.goToSelectedMonth ();
|
||||
this.ignoreCalendarChange = false;
|
||||
|
||||
this.calendar.showPopup (this.button);
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,38 @@
|
|||
Htk.Entry = new Class
|
||||
({
|
||||
Extends: Htk.Field
|
||||
,Tag: 'htk-entry'
|
||||
|
||||
,initialize: function (props)
|
||||
{
|
||||
this.parent (props);
|
||||
this.createElement ('input');
|
||||
this.node.type = 'text';
|
||||
this.node.addEventListener ('change', this.changed.bind (this));
|
||||
}
|
||||
|
||||
,changed: function (event)
|
||||
{
|
||||
var newValue;
|
||||
|
||||
if (this.node.value == '')
|
||||
newValue = null;
|
||||
else
|
||||
newValue = this.node.value;
|
||||
|
||||
this.valueChanged (newValue);
|
||||
}
|
||||
|
||||
,putValue: function (value)
|
||||
{
|
||||
if (!value)
|
||||
this.node.value = '';
|
||||
else
|
||||
this.node.value = value;
|
||||
}
|
||||
|
||||
,setEditable: function (editable)
|
||||
{
|
||||
this.node.readOnly = !editable;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,139 @@
|
|||
|
||||
Htk.FullImage = new Class
|
||||
({
|
||||
Extends: Vn.Object
|
||||
|
||||
,img: null
|
||||
,timeout: 0
|
||||
,loading: false
|
||||
,visible: false
|
||||
,hideCalled: false
|
||||
|
||||
,initialize: function (props)
|
||||
{
|
||||
this.parent (props);
|
||||
|
||||
var div = document.createElement ('div');
|
||||
div.className = 'full-image';
|
||||
|
||||
var loadingBox = document.createElement ('div');
|
||||
loadingBox.className = 'image-loader';
|
||||
|
||||
var loadingImg = document.createElement ('img');
|
||||
loadingImg.src = 'image/loader-black.gif';
|
||||
loadingImg.alt = _('Loading');
|
||||
loadingBox.appendChild (loadingImg);
|
||||
|
||||
this.div = div;
|
||||
this.loadingBox = loadingBox;
|
||||
this.loadingImg = loadingImg;
|
||||
}
|
||||
|
||||
,getLeft: function (width)
|
||||
{
|
||||
return parseInt (getPageXOffset () + (getInnerWidth () - width) / 2) +'px';
|
||||
}
|
||||
|
||||
,getTop: function (height)
|
||||
{
|
||||
return parseInt (getPageYOffset () + (getInnerHeight () - height) / 2) +'px';
|
||||
}
|
||||
|
||||
,show: function (basedir, file)
|
||||
{
|
||||
if (this.timeout)
|
||||
{
|
||||
clearTimeout (this.timeout);
|
||||
this.timeout = 0;
|
||||
}
|
||||
|
||||
this.hideCalled = false;
|
||||
this.loadingBox.style.left = this.getLeft (40);
|
||||
this.loadingBox.style.top = this.getTop (40);
|
||||
|
||||
this.img = document.createElement ('img');
|
||||
this.img.src = basedir +'/full/'+ file;
|
||||
this.img.addEventListener ('load', this.imageLoaded.bind (this, this.img));
|
||||
this.img.addEventListener ('error', this.hideLoading.bind (this));
|
||||
|
||||
if (!this.img.complete && !this.loading)
|
||||
{
|
||||
document.body.appendChild (this.loadingBox);
|
||||
this.loading = true;
|
||||
}
|
||||
}
|
||||
|
||||
,imageLoaded: function (img)
|
||||
{
|
||||
if (img != this.img || this.hideCalled)
|
||||
return;
|
||||
|
||||
var scale = 1.0;
|
||||
var width = img.width;
|
||||
var height = img.height;
|
||||
var innerWidth = getInnerWidth () - 350;
|
||||
var innerHeight = getInnerHeight () - 120;
|
||||
|
||||
if (width > innerWidth)
|
||||
{
|
||||
scale = width / innerWidth;
|
||||
height = parseInt (height / scale);
|
||||
width = innerWidth;
|
||||
}
|
||||
if (height > innerHeight)
|
||||
{
|
||||
scale = height / innerHeight;
|
||||
width = parseInt (width / scale);
|
||||
height = innerHeight;
|
||||
}
|
||||
|
||||
this.hideLoading ();
|
||||
this.div.style.left = this.getLeft (width + 2);
|
||||
this.div.style.top = this.getTop (height + 2);
|
||||
this.div.style.width = width +'px';
|
||||
this.div.style.height = height +'px';
|
||||
|
||||
if (scale !== 1.0)
|
||||
{
|
||||
img.style.width = width +'px';
|
||||
img.style.height = height +'px';
|
||||
}
|
||||
|
||||
if (this.div.firstChild != null)
|
||||
this.div.replaceChild (img, this.div.firstChild);
|
||||
else
|
||||
this.div.appendChild (img);
|
||||
|
||||
if (!this.visible)
|
||||
{
|
||||
document.body.appendChild (this.div);
|
||||
this.visible = true;
|
||||
}
|
||||
}
|
||||
|
||||
,hide: function ()
|
||||
{
|
||||
this.hideCalled = true;
|
||||
this.hideLoading ();
|
||||
|
||||
if (this.visible)
|
||||
this.timeout = setTimeout (this.hideTimeout.bind (this), 450);
|
||||
}
|
||||
|
||||
,hideTimeout: function ()
|
||||
{
|
||||
document.body.removeChild (this.div);
|
||||
this.visible = false;
|
||||
this.timeout = 0;
|
||||
}
|
||||
|
||||
,hideLoading: function ()
|
||||
{
|
||||
if (this.loading)
|
||||
{
|
||||
document.body.removeChild (this.loadingBox);
|
||||
this.loading = false;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,141 @@
|
|||
Htk.ImageEditor = new Class
|
||||
({
|
||||
Extends: Htk.Widget
|
||||
,Implements: Htk.Popup
|
||||
|
||||
,maxFileSize: 4 * 1024 * 1024 // 4 MegaBytes
|
||||
|
||||
,initialize: function ()
|
||||
{
|
||||
this.createElement ('div');
|
||||
this.node.className = 'image-editor';
|
||||
|
||||
var h3 = document.createElement ('h3');
|
||||
h3.appendChild (document.createTextNode (_('UpdateImage')));
|
||||
this.node.appendChild (h3);
|
||||
|
||||
var iframe = document.createElement ('iframe');
|
||||
iframe.name = 'image-editor';
|
||||
iframe.addEventListener ('load', this.imageUploaded.bind (this, iframe));
|
||||
this.node.appendChild (iframe);
|
||||
|
||||
var form = document.createElement ('form');
|
||||
form.method = 'post';
|
||||
form.action = 'ajax.php?action=image';
|
||||
form.target = 'image-editor';
|
||||
form.enctype = 'multipart/form-data';
|
||||
form.addEventListener ('submit', this.formSubmit.bind (this));
|
||||
this.node.appendChild (form);
|
||||
|
||||
var directoryInput = document.createElement ('input');
|
||||
directoryInput.type = 'hidden';
|
||||
directoryInput.name = 'directory';
|
||||
form.appendChild (directoryInput);
|
||||
|
||||
var input = document.createElement ('input');
|
||||
input.type = 'hidden';
|
||||
input.name = 'MAX_FILE_SIZE';
|
||||
input.value = this.maxFileSize;
|
||||
form.appendChild (input);
|
||||
|
||||
var table = document.createElement ('table');
|
||||
table.cellSpacing = 5;
|
||||
form.appendChild (table);
|
||||
|
||||
var tbody = document.createElement ('tbody');
|
||||
table.appendChild (tbody);
|
||||
|
||||
var tr = document.createElement ('tr');
|
||||
tbody.appendChild (tr);
|
||||
|
||||
var td = document.createElement ('td');
|
||||
td.style.textAlign = 'right';
|
||||
td.appendChild (document.createTextNode (_('FileName') + ':'));
|
||||
tr.appendChild (td);
|
||||
|
||||
var td = document.createElement ('td');
|
||||
tr.appendChild (td);
|
||||
|
||||
var nameInput = document.createElement ('input');
|
||||
nameInput.type = 'text';
|
||||
nameInput.name = 'name';
|
||||
nameInput.addEventListener ('change', this.nameChanged.bind (this));
|
||||
td.appendChild (nameInput);
|
||||
|
||||
var tr = document.createElement ('tr');
|
||||
tbody.appendChild (tr);
|
||||
|
||||
var td = document.createElement ('td');
|
||||
td.style.textAlign = 'right';
|
||||
td.appendChild (document.createTextNode (_('File') + ':'));
|
||||
tr.appendChild (td);
|
||||
|
||||
var td = document.createElement ('td');
|
||||
tr.appendChild (td);
|
||||
|
||||
var fileInput = document.createElement ('input');
|
||||
fileInput.type = 'file';
|
||||
fileInput.name = 'image';
|
||||
td.appendChild (fileInput);
|
||||
|
||||
var tr = document.createElement ('tr');
|
||||
tbody.appendChild (tr);
|
||||
|
||||
var td = document.createElement ('td');
|
||||
td.style.textAlign = 'center';
|
||||
td.colSpan = 2;
|
||||
tr.appendChild (td);
|
||||
|
||||
var loader = document.createElement ('img');
|
||||
loader.alt = _('Loading');
|
||||
loader.src = 'image/loader-black.gif';
|
||||
td.appendChild (loader);
|
||||
|
||||
var submitButton = document.createElement ('input');
|
||||
submitButton.type = 'submit';
|
||||
submitButton.appendChild (document.createTextNode (_('UploadFile')));
|
||||
td.appendChild (submitButton);
|
||||
|
||||
this.directoryInput = directoryInput;
|
||||
this.fileInput = fileInput;
|
||||
this.nameInput = nameInput;
|
||||
this.submitButton = submitButton;
|
||||
this.loader = loader;
|
||||
}
|
||||
|
||||
,setData: function (image, directory)
|
||||
{
|
||||
this.nameInput.value = image;
|
||||
this.directoryInput.value = directory;
|
||||
}
|
||||
|
||||
,formSubmit: function ()
|
||||
{
|
||||
this.submitButton.disabled = true;
|
||||
this.loader.style.visibility = 'visible';
|
||||
}
|
||||
|
||||
,imageUploaded: function (iframe)
|
||||
{
|
||||
this.submitButton.disabled = false;
|
||||
this.loader.style.visibility = 'hidden';
|
||||
var text = iframe.contentDocument.body.textContent;
|
||||
|
||||
if (!text)
|
||||
return;
|
||||
|
||||
if (text != 1)
|
||||
{
|
||||
alert (text);
|
||||
return;
|
||||
}
|
||||
|
||||
this.signalEmit ('file-uploaded', this.nameInput.value);
|
||||
}
|
||||
|
||||
,nameChanged: function ()
|
||||
{
|
||||
this.signalEmit ('name-changed', this.nameInput.value);
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,93 @@
|
|||
Htk.Image = new Class
|
||||
({
|
||||
Extends: Htk.Entry
|
||||
,Tag: 'htk-image'
|
||||
|
||||
,empty: false
|
||||
,file: null
|
||||
|
||||
,initialize: function (props)
|
||||
{
|
||||
this.parent (props);
|
||||
this.node = document.getElementById (nodeId);
|
||||
this.node.addEventListener ('error', this.error.bind (this));
|
||||
}
|
||||
|
||||
,error: function ()
|
||||
{
|
||||
if (!this.empty)
|
||||
{
|
||||
this.empty = true;
|
||||
this.node.src = 'image/empty.png';
|
||||
}
|
||||
}
|
||||
|
||||
,render: function (force)
|
||||
{
|
||||
if (this.realValue)
|
||||
{
|
||||
this.file = this.realValue + '.png';
|
||||
|
||||
if (force)
|
||||
this.file += '?' + (new Date()).getTime ();
|
||||
|
||||
this.empty = false;
|
||||
this.node.src = this.url + '/' + this.file;
|
||||
}
|
||||
else
|
||||
{
|
||||
this.file = null;
|
||||
this.error ();
|
||||
}
|
||||
}
|
||||
|
||||
,setRealValue: function (value)
|
||||
{
|
||||
this.render (false);
|
||||
}
|
||||
|
||||
,setShowFull: function (show)
|
||||
{
|
||||
if (show)
|
||||
{
|
||||
var obj = this;
|
||||
this.node.addEventListener ('mouseover',
|
||||
function () { obj.mouseOver () }, false);
|
||||
this.node.addEventListener ('mouseout',
|
||||
function () { obj.mouseOut () }, false);
|
||||
}
|
||||
}
|
||||
|
||||
,setEditable: function (editable)
|
||||
{
|
||||
if (editable)
|
||||
{
|
||||
var obj = this;
|
||||
this.style.cursor = 'pointer';
|
||||
this.node.addEventListener ('dblclick',
|
||||
function (e) { obj.dblClicked (e) }, false);
|
||||
}
|
||||
}
|
||||
|
||||
,dblClicked: function (event)
|
||||
{
|
||||
var form = htkImageForm.node;
|
||||
form.style.top = getPageYOffset () + (event.clientY - 80) + 'px';
|
||||
form.style.left = (event.clientX + 30) + 'px';
|
||||
document.body.appendChild (form);
|
||||
htkImageForm.load (this);
|
||||
}
|
||||
|
||||
,mouseOver: function ()
|
||||
{
|
||||
if (!this.empty)
|
||||
htkImageFull.show (this.url + '/../full', this.file);
|
||||
}
|
||||
|
||||
,mouseOut: function ()
|
||||
{
|
||||
if (!this.empty)
|
||||
htkImageFull.hide ();
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
Htk.Label = new Class
|
||||
({
|
||||
Extends: Htk.Field
|
||||
,Tag: 'htk-label'
|
||||
,Properties:
|
||||
{
|
||||
/**
|
||||
* Format that applies to the value.
|
||||
**/
|
||||
format:
|
||||
{
|
||||
type: String
|
||||
,set: function (x)
|
||||
{
|
||||
this._format = _(x);
|
||||
}
|
||||
,get: function ()
|
||||
{
|
||||
return this._format;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
,_format: null
|
||||
|
||||
,initialize: function (props)
|
||||
{
|
||||
this.parent (props);
|
||||
this.createElement ('label');
|
||||
}
|
||||
|
||||
,putValue: function (value)
|
||||
{
|
||||
Vn.Node.setText (this.node,
|
||||
Vn.Value.format (value, this._format));
|
||||
}
|
||||
});
|
|
@ -0,0 +1,57 @@
|
|||
|
||||
Htk.Radio = new Class
|
||||
({
|
||||
Extends: Vn.Object,
|
||||
Implements: Vn.Param
|
||||
,Tag: 'htk-radio'
|
||||
|
||||
,initialize: function (props)
|
||||
{
|
||||
this.parent (props);
|
||||
this.rButton = new Array ();
|
||||
this.uid = ++htkRadioUid;
|
||||
}
|
||||
|
||||
,newRadio: function (value)
|
||||
{
|
||||
var radio;
|
||||
var obj = this;
|
||||
|
||||
radio = createRadio (this.uid);
|
||||
radio.value = value;
|
||||
radio.checked = value == this.realValue;
|
||||
radio.addEventListener ('change',
|
||||
function () { obj.radioChanged (this._value); }, false);
|
||||
this.rButton.push (radio);
|
||||
|
||||
return radio;
|
||||
}
|
||||
|
||||
,radioChanged: function (value)
|
||||
{
|
||||
this.realValue = value;
|
||||
this.signalEmit ('changed');
|
||||
}
|
||||
|
||||
,setRealValue: function (value)
|
||||
{
|
||||
var rButton = this.rButton;
|
||||
|
||||
for (var n = 0; n < rButton.length; n++)
|
||||
{
|
||||
if (rButton[n].value == value)
|
||||
{
|
||||
rButton[n].checked = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
,setEditable: function (editable)
|
||||
{
|
||||
var rButton = this.rButton;
|
||||
|
||||
for (var n = 0; n < rButton.length; n++)
|
||||
rButton[n].disabled = !editable;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,107 @@
|
|||
Htk.Select = new Class
|
||||
({
|
||||
Extends: Htk.Field
|
||||
,Tag: 'htk-combo'
|
||||
,Properties:
|
||||
{
|
||||
/**
|
||||
* The model associated to this form.
|
||||
**/
|
||||
model:
|
||||
{
|
||||
type: Db.Model
|
||||
,set: function (x)
|
||||
{
|
||||
this.link ({_model: x}, {'status-changed': this.onModelChange});
|
||||
this.onModelChange ();
|
||||
}
|
||||
,get: function ()
|
||||
{
|
||||
return this._model;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
,_model: null
|
||||
,valueColumnIndex: 0
|
||||
,valueColumnName: null
|
||||
,showColumnIndex: 1
|
||||
,showColumnName: null
|
||||
|
||||
,initialize: function (props)
|
||||
{
|
||||
this.parent (props);
|
||||
this.createElement ('select');
|
||||
this.node.addEventListener ('change', this.changed.bind (this));
|
||||
}
|
||||
|
||||
,changed: function (event)
|
||||
{
|
||||
var value;
|
||||
var row = this.node.selectedIndex - 1;
|
||||
|
||||
if (row >= 0)
|
||||
value = this._model.getByIndex (row, this.valueColumnIndex);
|
||||
else
|
||||
value = null;
|
||||
|
||||
this.valueChanged (value);
|
||||
}
|
||||
|
||||
,addOption: function (value, text)
|
||||
{
|
||||
var option = document.createElement ('option');
|
||||
option.value = value;
|
||||
option.appendChild (document.createTextNode (text));
|
||||
this.node.appendChild (option);
|
||||
}
|
||||
|
||||
,onModelChange: function ()
|
||||
{
|
||||
var model = this._model;
|
||||
|
||||
if (model.status == Db.Model.Status.LOADING)
|
||||
return;
|
||||
|
||||
Vn.Node.removeChilds (this.node);
|
||||
|
||||
switch (model.status)
|
||||
{
|
||||
case Db.Model.Status.READY:
|
||||
{
|
||||
var data = model.data;
|
||||
this.addOption (null, '');
|
||||
|
||||
for (var i = 0; i < data.length; i++)
|
||||
this.addOption (data[i][this.showColumnIndex], data[i][1]);
|
||||
|
||||
this.selectOption ();
|
||||
break;
|
||||
}
|
||||
case Db.Model.Status.ERROR:
|
||||
this.addOption (null, _('Error'));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
,setEditable: function (editable)
|
||||
{
|
||||
this.node.disabled = !editable;
|
||||
}
|
||||
|
||||
,selectOption: function ()
|
||||
{
|
||||
if (!this._model || this._model.status != Db.Model.Status.READY)
|
||||
return;
|
||||
|
||||
var row = this._model.searchByIndex (this.valueColumnIndex, this._value);
|
||||
|
||||
if (row != -1)
|
||||
this.node.selectedIndex = row + 1;
|
||||
}
|
||||
|
||||
,putValue: function (value)
|
||||
{
|
||||
this.selectOption ();
|
||||
}
|
||||
});
|
|
@ -0,0 +1,63 @@
|
|||
Htk.Spin = new Class
|
||||
({
|
||||
Extends: Htk.Entry
|
||||
,Tag: 'htk-spin'
|
||||
|
||||
,initialize: function (props)
|
||||
{
|
||||
this.parent (props);
|
||||
this.unit = null;
|
||||
this.digits = 0;
|
||||
}
|
||||
|
||||
,changed: function ()
|
||||
{
|
||||
var value = (this.entry.value == '') ? null : parseFloat (this.entry.value);
|
||||
this.entry.value = value;
|
||||
this.realValue = value;
|
||||
this.signalEmit ('changed');
|
||||
}
|
||||
|
||||
,setEditable: function (editable)
|
||||
{
|
||||
if (editable)
|
||||
{
|
||||
var input;
|
||||
var obj = this;
|
||||
|
||||
input = document.createElement ('input');
|
||||
input.style.textAlign = 'right';
|
||||
input.style.width = '100%';
|
||||
setInputTypeNumber (input);
|
||||
input.addEventListener ('change',
|
||||
function () { obj.changed (); }, false);
|
||||
this.node.appendChild (input);
|
||||
this.entry = input;
|
||||
}
|
||||
else
|
||||
{
|
||||
removeChilds (this.node);
|
||||
this.entry = null;
|
||||
}
|
||||
}
|
||||
|
||||
,setRealValue: function (value)
|
||||
{
|
||||
var text;
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
text = (new Number (value)).toFixed (this.digits);
|
||||
|
||||
if (this.unit != null)
|
||||
text += ' ' + this.unit;
|
||||
}
|
||||
else
|
||||
text = '';
|
||||
|
||||
if (!this.editable)
|
||||
setText (this.node, text);
|
||||
else
|
||||
this.entry.value = text;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,56 @@
|
|||
Htk.Table = new Class
|
||||
({
|
||||
Extends: Htk.Entry
|
||||
,Tag: 'htk-table'
|
||||
|
||||
,initialize: function (props)
|
||||
{
|
||||
this.parent (props);
|
||||
|
||||
var tv = new Htk.TreeView ();
|
||||
this.node.appendChild (tv.getNode ());
|
||||
|
||||
var renderer = new Htk.CellRendererRadio ();
|
||||
tv.appendColumn (0, renderer, '');
|
||||
|
||||
var rbGroup = renderer.rbGroup;
|
||||
rbGroup.addSignal ('changed', this.changed, this);
|
||||
|
||||
this.treeview = tv;
|
||||
this.rbGroup = rbGroup;
|
||||
}
|
||||
|
||||
,setModel: function (model)
|
||||
{
|
||||
this.treeview.setModel (model);
|
||||
model.addSignal ('status-changed', this.modelRefresh, this);
|
||||
this.selectValue ();
|
||||
}
|
||||
|
||||
,changed: function (rbGroup)
|
||||
{
|
||||
this.realValue = this.rbGroup.getValue ();
|
||||
this.signalEmit ('changed');
|
||||
}
|
||||
|
||||
,selectValue: function ()
|
||||
{
|
||||
this.rbGroup.setValue (this.realValue);
|
||||
}
|
||||
|
||||
,setRealValue: function ()
|
||||
{
|
||||
this.selectValue ();
|
||||
}
|
||||
|
||||
,modelRefresh: function (model, status)
|
||||
{
|
||||
if (status == DB_MODEL_STATUS_READY)
|
||||
this.selectValue ();
|
||||
}
|
||||
|
||||
,setEditable: function (editable)
|
||||
{
|
||||
this.rbGroup.setEditable (editable);
|
||||
}
|
||||
});
|
|
@ -0,0 +1,37 @@
|
|||
Htk.TextArea = new Class
|
||||
({
|
||||
Extends: Htk.Field
|
||||
,Tag: 'htk-textarea'
|
||||
|
||||
,initialize: function (props)
|
||||
{
|
||||
this.parent (props);
|
||||
this.createElement ('textarea');
|
||||
this.node.addEventListener ('change', this.changed.bind (this));
|
||||
}
|
||||
|
||||
,changed: function (event)
|
||||
{
|
||||
var value;
|
||||
|
||||
if (this.node.value == '')
|
||||
value = null;
|
||||
else
|
||||
value = this.node.value;
|
||||
|
||||
this.valueChanged (value);
|
||||
}
|
||||
|
||||
,setEditable: function (editable)
|
||||
{
|
||||
this.node.readOnly = !editable;
|
||||
}
|
||||
|
||||
,putValue: function (value)
|
||||
{
|
||||
if (!value)
|
||||
this.node.value = '';
|
||||
else
|
||||
this.node.value = value;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,352 @@
|
|||
Htk.Grid = new Class
|
||||
({
|
||||
Extends: Htk.Widget
|
||||
,Tag: 'htk-grid'
|
||||
,Child: 'model'
|
||||
,Properties:
|
||||
{
|
||||
model:
|
||||
{
|
||||
type: Db.Model
|
||||
,set: function (x)
|
||||
{
|
||||
this.link ({_model: x},
|
||||
{
|
||||
'status-changed': this.onModelChange
|
||||
,'row-deleted': this.onRowDelete
|
||||
,'row-updated': this.onRowUpdate
|
||||
,'row-inserted': this.onRowInsert
|
||||
,'updatable-changed': this.onUpdatableChange
|
||||
});
|
||||
|
||||
this.form = new Db.Form ({model: x});
|
||||
this.onUpdatableChange ();
|
||||
this.onModelChange ();
|
||||
}
|
||||
,get: function ()
|
||||
{
|
||||
this._model;
|
||||
}
|
||||
},
|
||||
emptyMessage:
|
||||
{
|
||||
type: String
|
||||
,value: 'NoData'
|
||||
}
|
||||
}
|
||||
|
||||
,_model: null
|
||||
,form: null
|
||||
,columns: new Array ()
|
||||
,internalColumn: null
|
||||
,internalColumns: 0
|
||||
|
||||
,initialize: function ()
|
||||
{
|
||||
this.parent ();
|
||||
|
||||
this.table = this.createElement ('table');
|
||||
this.table.className = 'grid';
|
||||
|
||||
var thead = document.createElement ('thead');
|
||||
this.table.appendChild (thead);
|
||||
|
||||
this.thead = document.createElement ('tr')
|
||||
thead.appendChild (this.thead);
|
||||
|
||||
this.tbody = document.createElement ('tbody');
|
||||
this.table.appendChild (this.tbody);
|
||||
}
|
||||
|
||||
,removeClicked: function (column, value, row)
|
||||
{
|
||||
if (confirm (_('ReallyDelete')))
|
||||
this._model.deleteRow (row);
|
||||
}
|
||||
|
||||
,onRowDelete: function (model, row)
|
||||
{
|
||||
var tableRows = this.tbody.childNodes;
|
||||
this.tbody.removeChild (tableRows[row]);
|
||||
|
||||
for (var i = row; i < tableRows.length; i++)
|
||||
tableRows[i].className = (i % 2) ? 'pair-row' : '';
|
||||
|
||||
this.showNoRecordsFound ();
|
||||
}
|
||||
|
||||
,onRowInsert: function (model, row)
|
||||
{
|
||||
this.buildRow (1);
|
||||
}
|
||||
|
||||
,renderCell: function (row, column, tr)
|
||||
{
|
||||
if (column.columnIndex != -1)
|
||||
column.value = this._model.data[row][column.columnIndex];
|
||||
|
||||
if (column.renderer)
|
||||
{
|
||||
this.form.row = row;
|
||||
column.renderer (column, this.form);
|
||||
}
|
||||
|
||||
return column.render (tr);
|
||||
}
|
||||
|
||||
,refreshRow: function (row, columns)
|
||||
{
|
||||
var x = this.columns;
|
||||
var tr = this.tbody.childNodes[row];
|
||||
|
||||
for (var i = 0; i < x.length; i++)
|
||||
if (x[i].renderer || columns.indexOf (x[i].columnIndex) != -1)
|
||||
{
|
||||
var cell = this.renderCell (row, x[i], tr);
|
||||
tr.replaceChild (cell, tr.childNodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
,onRowUpdate: function (model, row, columns)
|
||||
{
|
||||
this.refreshRow (row, columns);
|
||||
}
|
||||
|
||||
,buildRow: function (count)
|
||||
{
|
||||
for (var i = 0; i < count; i++)
|
||||
{
|
||||
var tr = document.createElement ('tr');
|
||||
|
||||
if (i % 2)
|
||||
tr.className = 'pair-row';
|
||||
|
||||
for (var j = 0; j < this.columns.length; j++)
|
||||
{
|
||||
var cell = this.renderCell (i, this.columns[j], tr);
|
||||
tr.appendChild (cell);
|
||||
}
|
||||
|
||||
this.tbody.appendChild (tr);
|
||||
}
|
||||
}
|
||||
|
||||
,onUpdatableChange: function ()
|
||||
{
|
||||
if (this._model.updatable)
|
||||
{
|
||||
if (!this.internalColumn)
|
||||
{
|
||||
this.internalColumn = new Htk.ColumnButton
|
||||
({
|
||||
image: 'image/remove.png'
|
||||
,tip: _('Remove')
|
||||
});
|
||||
this.internalColumn.on ('clicked', this.removeClicked, this);
|
||||
this.insertInternalColumn (0, this.internalColumn);
|
||||
}
|
||||
}
|
||||
else if (this.internalColumn)
|
||||
{
|
||||
this.internalColumn = null;
|
||||
this.removeInternalColumn (0);
|
||||
}
|
||||
}
|
||||
|
||||
,onModelChange: function ()
|
||||
{
|
||||
if (!this._model)
|
||||
{
|
||||
this.showMessage (this.emptyMessage, 'refresh.png');
|
||||
return;
|
||||
}
|
||||
|
||||
this.table.removeChild (this.tbody);
|
||||
this.tbody = document.createElement ('tbody');
|
||||
|
||||
switch (this._model.status)
|
||||
{
|
||||
case Db.Model.Status.READY:
|
||||
{
|
||||
for (var i = 0; i < this.columns.length; i++)
|
||||
this.columns[i].updateColumnIndex (this._model);
|
||||
|
||||
this.buildRow (this._model.numRows);
|
||||
this.showNoRecordsFound ();
|
||||
break;
|
||||
}
|
||||
case Db.Model.Status.LOADING:
|
||||
this.showMessage (_('Loading'), 'loader-black.gif');
|
||||
break;
|
||||
case Db.Model.Status.CLEAN:
|
||||
this.showMessage (_(this.emptyMessage), 'refresh.png');
|
||||
break;
|
||||
case Db.Model.Status.ERROR:
|
||||
this.showMessage (_('ErrorLoadingData'), 'error.png');
|
||||
break;
|
||||
}
|
||||
|
||||
this.table.appendChild (this.tbody);
|
||||
}
|
||||
|
||||
,showNoRecordsFound: function (count)
|
||||
{
|
||||
if (this._model.numRows == 0)
|
||||
this.showMessage (_('EmptyList'), 'clean.png');
|
||||
}
|
||||
|
||||
,showMessage: function (message, src)
|
||||
{
|
||||
if (this.columns.length == 0)
|
||||
return;
|
||||
|
||||
var tr = document.createElement ('tr');
|
||||
this.tbody.appendChild (tr);
|
||||
|
||||
var td = document.createElement ('td');
|
||||
td.className = 'grid-message';
|
||||
td.colSpan = this.columns.length;
|
||||
tr.appendChild (td);
|
||||
|
||||
var img = document.createElement ('img');
|
||||
img.alt = '';
|
||||
img.src = 'image/'+ src;
|
||||
td.appendChild (img);
|
||||
|
||||
var message = document.createTextNode (message);
|
||||
td.appendChild (message);
|
||||
}
|
||||
|
||||
,scrollToRow: function (row)
|
||||
{
|
||||
if (row >= 0)
|
||||
{
|
||||
var height = parseInt (this.tr.style.height);
|
||||
this.node.scrollTop = (row - 2) * height;
|
||||
|
||||
if (this.selectedRow)
|
||||
this.selectedRow.style.backgroundColor = null;
|
||||
|
||||
this.selectedRow = this.tbody.childNodes[row]
|
||||
this.selectedRow.style.backgroundColor = '#AAD';
|
||||
}
|
||||
}
|
||||
|
||||
,sortModel: function (column)
|
||||
{
|
||||
var columnIndex = column.columnIndex;
|
||||
|
||||
if (this._model && columnIndex != -1)
|
||||
this._model.sort (columnIndex);
|
||||
}
|
||||
|
||||
,columnChanged: function (column, row, newValue)
|
||||
{
|
||||
var columnIndex = column.columnIndex;
|
||||
|
||||
if (columnIndex != -1)
|
||||
this._model.setByIndex (row, columnIndex, newValue);
|
||||
}
|
||||
|
||||
,addColumn: function (pos, column)
|
||||
{
|
||||
var header = document.createElement ('th');
|
||||
|
||||
if (pos == -1 || pos >= this.columns.length)
|
||||
{
|
||||
pos = this.columns.push (column) - 1;
|
||||
this.thead.appendChild (header);
|
||||
}
|
||||
else
|
||||
{
|
||||
this.columns.splice (pos, 0, column);
|
||||
this.thead.insertBefore (header, this.thead.childNodes[pos]);
|
||||
}
|
||||
|
||||
header.addEventListener ('click',
|
||||
this.sortModel.bind (this, column));
|
||||
header.title = _('Sort');
|
||||
|
||||
if (column.title)
|
||||
{
|
||||
var title = document.createTextNode (_(column.title));
|
||||
header.appendChild (title);
|
||||
}
|
||||
|
||||
column.on ('changed', this.columnChanged, this);
|
||||
|
||||
var rows = this.tbody.childNodes;
|
||||
|
||||
if (this._model && this._model.numRows > 0)
|
||||
for (var i = 0; i < rows.length; i++)
|
||||
{
|
||||
var cell = this.renderCell (i, column, rows[i]);
|
||||
rows[i].insertBefore (cell, rows[i].childNodes[pos+1]);
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
,insertInternalColumn: function (pos, column)
|
||||
{
|
||||
if (pos < 0 || pos > this.internalColumns)
|
||||
pos = this.internalColumns;
|
||||
|
||||
this.internalColumns++;
|
||||
return this.addColumn (pos, column);
|
||||
}
|
||||
|
||||
,insertColumn: function (pos, column)
|
||||
{
|
||||
if (pos > 0)
|
||||
pos += this.internalColumns;
|
||||
|
||||
return this.addColumn (pos, column) - this.internalColumns;
|
||||
}
|
||||
|
||||
,appendColumn: function (column)
|
||||
{
|
||||
return this.insertColumn (-1, column);
|
||||
}
|
||||
|
||||
,deleteColumn: function (pos)
|
||||
{
|
||||
if (pos < 0 || pos >= this.columns.length)
|
||||
return;
|
||||
|
||||
this.columns.splice (pos, 1);
|
||||
this.thead.removeChild (this.thead.childNodes[pos]);
|
||||
|
||||
var rows = this.tbody.childNodes;
|
||||
|
||||
if (this._model && this._model.numRows > 0)
|
||||
for (var i = 0; i < rows.length; i++)
|
||||
rows[i].removeChild (rows[i].childNodes[pos]);
|
||||
}
|
||||
|
||||
,removeInternalColumn: function (pos)
|
||||
{
|
||||
if (this.internalColumns == 0)
|
||||
return;
|
||||
|
||||
if (pos < 0 || pos > this.internalColumns)
|
||||
pos = this.internalColumns;
|
||||
|
||||
this.deleteColumn (pos);
|
||||
this.internalColumns--;
|
||||
}
|
||||
|
||||
,removeColumn: function (pos)
|
||||
{
|
||||
if (pos > 0)
|
||||
pos += this.internalColumns;
|
||||
|
||||
this.deleteColumn (pos);
|
||||
}
|
||||
|
||||
,reloadModel: function ()
|
||||
{
|
||||
if (this._model != null)
|
||||
this.onModelChange ();
|
||||
}
|
||||
});
|
|
@ -0,0 +1,4 @@
|
|||
/**
|
||||
* The namespace.
|
||||
**/
|
||||
var Htk = {};
|
|
@ -0,0 +1,36 @@
|
|||
<?php
|
||||
|
||||
require_once ('js/db/main.php');
|
||||
|
||||
Js::includeLib ('htk'
|
||||
,'main'
|
||||
,'widget'
|
||||
,'popup'
|
||||
,'grid'
|
||||
,'radio-group'
|
||||
,'field'
|
||||
,'field/entry'
|
||||
,'field/radio'
|
||||
,'field/label'
|
||||
,'field/text-area'
|
||||
,'field/spin'
|
||||
,'field/check'
|
||||
,'field/select'
|
||||
,'field/calendar'
|
||||
,'field/date-chooser'
|
||||
,'field/full-image'
|
||||
,'field/image'
|
||||
,'field/image-editor'
|
||||
,'field/table'
|
||||
,'column'
|
||||
,'column/button'
|
||||
,'column/link'
|
||||
,'column/date'
|
||||
,'column/image'
|
||||
,'column/radio'
|
||||
,'column/spin'
|
||||
,'column/text'
|
||||
,'column/check'
|
||||
);
|
||||
|
||||
?>
|
|
@ -0,0 +1,46 @@
|
|||
/**
|
||||
* Interface for use a widget as a popup.
|
||||
**/
|
||||
Htk.Popup = new Class
|
||||
({
|
||||
showPopup: function (parent)
|
||||
{
|
||||
document.body.appendChild (this.node);
|
||||
|
||||
this.node.addEventListener ('mousedown', this.stopEvent);
|
||||
|
||||
this.hidePopupHandler = this.hidePopup.bind (this);
|
||||
document.addEventListener ('mousedown', this.hidePopupHandler);
|
||||
|
||||
var spacing = 5;
|
||||
var rect = parent.getBoundingClientRect ();
|
||||
var left = rect.left;
|
||||
var top = rect.top + spacing + parent.offsetHeight;
|
||||
|
||||
var width = this.node.offsetWidth;
|
||||
var height = this.node.offsetHeight;
|
||||
|
||||
if (left + width > getInnerWidth ())
|
||||
left -= width - parent.offsetWidth;
|
||||
if (top + height > getInnerHeight ())
|
||||
top -= height + parent.offsetHeight + spacing * 2;
|
||||
|
||||
this.node.style.top = (top) + 'px';
|
||||
this.node.style.left = (left) + 'px';
|
||||
this.node.style.position = 'fixed';
|
||||
this.node.style.zIndex = 4;
|
||||
}
|
||||
|
||||
,hidePopup: function ()
|
||||
{
|
||||
this.node.removeEventListener ('mousedown', this.stopEvent)
|
||||
document.removeEventListener ('mousedown', this.hidePopupHandler);
|
||||
document.body.removeChild (this.node);
|
||||
this.signalEmit ('closed');
|
||||
}
|
||||
|
||||
,stopEvent: function (event)
|
||||
{
|
||||
event.stopPropagation ();
|
||||
}
|
||||
});
|
|
@ -0,0 +1,48 @@
|
|||
|
||||
Htk.RadioGroup = new Class
|
||||
({
|
||||
Extends: Vn.Param
|
||||
|
||||
,radioLock: false
|
||||
|
||||
,initialize: function (props)
|
||||
{
|
||||
this.parent (props);
|
||||
this.clear ();
|
||||
this.on ('changed', this.onRadioGroupChange, this);
|
||||
}
|
||||
|
||||
,clear: function ()
|
||||
{
|
||||
this.name = Math.random ().toString ();
|
||||
this.buttons = [];
|
||||
}
|
||||
|
||||
,onRadioGroupChange: function ()
|
||||
{
|
||||
for (var i = 0; i < this.buttons.length; i++)
|
||||
if (this.buttons[i].value == this._value)
|
||||
this.buttons[i].checked = true;
|
||||
}
|
||||
|
||||
,onRadioChange: function (value)
|
||||
{
|
||||
if (this.radioLock)
|
||||
return;
|
||||
|
||||
this.radioLock = true;
|
||||
this.value = value;
|
||||
this.radioLock = false;
|
||||
}
|
||||
|
||||
,createButton: function (value)
|
||||
{
|
||||
var radio = createRadio (this.name);
|
||||
radio.value = value;
|
||||
radio.checked = value == this.value;
|
||||
radio.addEventListener ('change', this.onRadioChange.bind (this, value));
|
||||
this.buttons.push (radio);
|
||||
|
||||
return radio;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,18 @@
|
|||
Htk.Widget = new Class
|
||||
({
|
||||
Extends: Vn.Object
|
||||
|
||||
/** Main HTML node that represents the widget **/
|
||||
,node: null
|
||||
|
||||
,createElement: function (tagName)
|
||||
{
|
||||
this.node = document.createElement (tagName);
|
||||
return this.node;
|
||||
}
|
||||
|
||||
,getNode: function ()
|
||||
{
|
||||
return this.node;
|
||||
}
|
||||
});
|
|
@ -0,0 +1,34 @@
|
|||
|
||||
function getPageYOffset ()
|
||||
{
|
||||
return window.pageYOffset;
|
||||
}
|
||||
|
||||
function getPageXOffset ()
|
||||
{
|
||||
return window.pageXOffset;
|
||||
}
|
||||
|
||||
function getInnerHeight ()
|
||||
{
|
||||
return window.innerHeight;
|
||||
}
|
||||
|
||||
function getInnerWidth ()
|
||||
{
|
||||
return window.innerWidth;
|
||||
}
|
||||
|
||||
function createRadio (uid)
|
||||
{
|
||||
var radio = document.createElement ('input');
|
||||
radio.type = 'radio';
|
||||
radio.name = uid;
|
||||
return radio;
|
||||
}
|
||||
|
||||
function setInputTypeNumber (input)
|
||||
{
|
||||
input.type = 'number';
|
||||
}
|
||||
|