Implement detail page for experiments

This commit is contained in:
Eike Foken
2011-09-14 05:44:20 +02:00
parent 92e45e8033
commit a4f41fd392
6 changed files with 473 additions and 133 deletions

View File

@@ -1,6 +1,6 @@
<?php <?php defined('BASEPATH') || exit('No direct script access allowed');
/* /*
* Copyright (c) 2011 Karsten Heiken <karsten@disposed.de> * Copyright (c) 2011 Karsten Heiken, Eike Foken
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -38,6 +38,7 @@ class Experiments extends CI_Controller {
$this->load->model('parameter'); $this->load->model('parameter');
$this->load->model('program'); $this->load->model('program');
$this->load->model('experiment'); $this->load->model('experiment');
$this->load->model('job');
} }
/** /**
@@ -131,6 +132,64 @@ class Experiments extends CI_Controller {
$this->load->view('experiments/new', $data); $this->load->view('experiments/new', $data);
} }
/**
* Copies an experiment.
*
* @param string $experimentId
*/
public function copy($experimentId = '') {
$experiment = $this->experiment->getByID($experimentId);
if (isset($experiment['project_id'])) {
redirect('experiments/create/' . $experiment['project_id'] . '/' . $experimentId);
} else {
show_404();
}
}
/**
* Shows detailed informations about a specific experiment.
*
* @param string $experimentId
*/
public function detail($experimentId = '') {
$experiment = $this->experiment->getByID($experimentId);
if (empty($experimentId) || !isset($experiment['id'])){
show_404();
}
$this->load->helper('typography');
$data['experiment'] = $experiment;
$data['job'] = $this->job->getByExperimentId($experiment['id']);
$data['project'] = $this->project->getById($experiment['project_id']);
$this->load->view('experiments/detail', $data);
}
/**
* Allows users to delete an experiment.
*
* @param string $experimentId
*/
public function delete($experimentId = '') {
// TODO: Check user rights
$experiment = $this->experiment->getByID($experimentId);
if (empty($experimentId) || !isset($experiment['id'])){
show_404();
}
$this->load->helper('file');
$experimentPath = FCPATH . 'uploads/' . $experiment['project_id'] . '/' . $experiment['id'] . '/';
if (delete_files($experimentPath, true)) {
rmdir($experimentPath);
}
$this->experiment->delete($experimentId);
redirect('projects/detail/' . $experiment['project_id'], 303);
}
} }
/* End of file experiments.php */ /* End of file experiments.php */

View File

@@ -112,5 +112,37 @@ if ( ! function_exists('prettyTime'))
} }
} }
/**
* Parses any english textual datetime description into a relative date string.
*
* @author Eike Foken <kontakt@eikefoken.de>
* @param string $date
* @param boolean $show_seconds
* @return string
*/
if (!function_exists('relative_time')) {
function relative_time($date, $show_seconds = false) {
$diff = time() - strtotime($date);
if ($diff < 120 && !$show_seconds) {
$output = _('just now');
} else if ($diff < 60 && $show_seconds) {
$output = sprintf(ngettext('%d second ago', '%d seconds ago', $diff), $diff);
} else if (($diff = round($diff / 60)) < 60) {
$output = sprintf(ngettext('%d minute ago', '%d minutes ago', $diff), $diff);
} else if (($diff = round($diff / 60)) < 24) {
$output = sprintf(ngettext('%d hour ago', '%d hours ago', $diff), $diff);
} else if (($diff = round($diff / 24)) < 7) {
$output = sprintf(ngettext('%d day ago', '%d days ago', $diff), $diff);
} else if (($diff = round($diff / 7)) < 4) {
$output = sprintf(ngettext('%d week ago', '%d weeks ago', $diff), $diff);
} else {
$output = _('on') . ' ' . strftime('%B %Y', strtotime($date));
}
return $output;
}
}
/* End of file MY_date_helper.php */ /* End of file MY_date_helper.php */
/* Location: ./application/helpers/MY_date_helper.php */ /* Location: ./application/helpers/MY_date_helper.php */

View File

@@ -1,6 +1,6 @@
<?php defined('BASEPATH') || exit('No direct script access allowed'); <?php defined('BASEPATH') || exit('No direct script access allowed');
/* /*
* Copyright (c) 2011 Karsten Heiken <karsten@disposed.de> * Copyright (c) 2011 Karsten Heiken, Eike Foken
* *
* Permission is hereby granted, free of charge, to any person obtaining a copy * Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal * of this software and associated documentation files (the "Software"), to deal
@@ -25,14 +25,15 @@
* Experiments are used to store different variations of the same project. * Experiments are used to store different variations of the same project.
* *
* @author Karsten Heiken <karsten@disposed.de> * @author Karsten Heiken <karsten@disposed.de>
* @author Eike Foken <kontakt@eikefoken.de>
*/ */
class Experiment extends CI_Model { class Experiment extends CI_Model {
/** /**
* Create a new experiment. * Creates a new experiment.
* *
* @param array $data The data of the new experiment * @param array $data The data of the new experiment
* @return boolean Was the insert successful? * @return boolean Returns TRUE if the insert was successful.
*/ */
public function create($data) { public function create($data) {
if (!isset($data['project_id'])) { if (!isset($data['project_id'])) {
@@ -51,13 +52,26 @@ class Experiment extends CI_Model {
} }
/** /**
* Deletes a experiment. * Updates an experiment.
*
* @param array $data
* @param string $experimentId
* @return boolean Returns TRUE if the update was successful.
*/
public function update($data, $experimentId) {
$this->db->update('experiments', $data, array('id' => $experimentId));
return $this->db->affected_rows() == 1;
}
/**
* Deletes an experiment.
* *
* @param string $experimentId The experiments ID to delete * @param string $experimentId The experiments ID to delete
* @return boolean Was the deletion successful? * @return boolean Returns TRUE if the deletion was successful.
*/ */
public function delete($experimentId) { public function delete($experimentId) {
return $this->db->delete('experiments', array('id' => $experimentId)); $this->db->delete('experiments', array('id' => $experimentId));
return $this->db->affected_rows() == 1;
} }
/** /**
@@ -99,14 +113,15 @@ class Experiment extends CI_Model {
} }
/** /**
* Gets an experiment by ID. * Gets an experiment by ID (kept for backwards compatibility).
* *
* @deprecated 14-09-2011
* @see Experiment::getById()
* @param string $experimentId The experiment to get * @param string $experimentId The experiment to get
* @return array The experiment * @return array The experiment
*/ */
public function get($experimentId) { public function get($experimentId) {
$query = $this->db->get_where('experiments', array('id' => $experimentId)); return $this->getById($experimentId);
return $query->row_array();
} }
/** /**
@@ -143,3 +158,6 @@ class Experiment extends CI_Model {
return $results; return $results;
} }
} }
/* End of file group.php */
/* Location: ./application/controllers/group.php */

View File

@@ -66,6 +66,22 @@ class Job extends CI_Model {
return $this->db->where('id', $job_id)->update('jobs', $data); return $this->db->where('id', $job_id)->update('jobs', $data);
} }
/**
* Gets a specific job.
*
* @param string $experimentId
* @return array The job data
*/
public function getByExperimentId($experimentId) {
$this->db->select('jobs.*, users.username, users.firstname, users.lastname');
$this->db->join('users', 'jobs.started_by = users.id', 'left');
$this->db->where('experiment_id', $experimentId);
$query = $this->db->get('jobs');
return $query->row_array();
}
/** /**
* Gets a list of recent jobs. * Gets a list of recent jobs.
* *

View File

@@ -0,0 +1,70 @@
<?php $this->load->view('header');?>
<div id="content">
<div class="title">
<h2><?=anchor('projects', _('Projects'));?> &raquo; <?=anchor('projects/detail/' . $project['id'], $project['name']);?> &raquo; <?=$experiment['name'];?></h2>
</div>
<div class="box">
<h3><?=_('Description');?></h3>
<div class="editInPlace"><?=auto_typography($experiment['description']);?></div>
<p></p>
<h3>Actions</h3>
<p>
<?php
if (isset($job['id'])):
?>
<a class="button disabled job_start"><?=_('Start job');?></a>
<?php
else:
?>
<a href="<?=site_url('jobs/start/' . $experiment['id']);?>" class="button job_start"><?=_('Start job');?></a>
<?php
endif;
?>
<a href="<?=site_url('experiments/copy/' . $experiment['id']);?>" class="button left copy"><?=_('Copy experiment');?>
</a><a href="javascript:deleteConfirm('<?=site_url('experiments/delete/' . $experiment['id']);?>');" class="button right delete"><?=_('Delete experiment');?></a>
</p>
</div>
<?php
if (isset($job['id'])):
?>
<div class="box">
<h3><?=_('Job details');?></h3>
<p>
<strong><?=_('Date started');?>:</strong> <?=relative_time($job['started_at']);?><br />
<strong><?=_('Starter');?>:</strong> <?=anchor('users/profile/' . urldecode($job['username']), $job['firstname'] . ' ' . $job['lastname']);?><br />
<strong><?=_('Server');?>:</strong> <?=(!empty($job['server'])) ? anchor('admin/servers/detail/' . urldecode($job['server']), $job['server']) : _('Not yet picked');?>
<div class="progress_bar" style="width: 300px;">
<strong><?=$job['progress']?>%</strong>
<span style="width: <?=$job['progress']?>%;">&nbsp;</span>
</div>
</p>
<?php
if ($job['finished_at'] != '0000-00-00 00:00:00'):
?>
<p><a href="<?=site_url('results/show/' . $experiment['id']);?>" class="button results"><?=_('Show results');?></a></p>
<?php
endif;
?>
</div>
<?php
endif;
?>
</div>
<script>
$('.editInPlace').editInPlace({
url: BASE_URL + 'ajax/update_experiment/' + '<?=$experiment['id']?>',
saving_image: SITE_URL + 'assets/images/ajax-loader.gif',
update_value: 'description',
value_required: true
});
</script>
<?php $this->load->view('footer');?>

View File

@@ -42,7 +42,7 @@ li {
} }
.editable { .editable {
background: #fffbcc; background: #fffbcc;
} }
.clear { .clear {
@@ -291,10 +291,10 @@ div.error {
} }
#content div.title a.share { #content div.title a.share {
background: url(../images/icons/user-share.png) 6px center no-repeat; background: url(../images/icons/user-share.png) 6px center no-repeat;
padding-left: 28px; padding-left: 28px;
font-size: 12px; font-size: 12px;
font-weight: normal; font-weight: normal;
line-height: 12px; line-height: 12px;
} }
@@ -325,13 +325,13 @@ div.error {
} }
.pagination ul li.clickable { .pagination ul li.clickable {
color: #0088cc; color: #0088cc;
cursor: pointer; cursor: pointer;
} }
.pagination ul li.active { .pagination ul li.active {
color: #222; color: #222;
cursor: default; cursor: default;
} }
/* Footer */ /* Footer */
@@ -357,71 +357,88 @@ div.error {
/* Buttons */ /* Buttons */
.buttons { .buttons {
float: left; float: left;
padding-bottom: 20px; padding-bottom: 20px;
clear: both; clear: both;
} }
a.button { a.button {
color: #6e6e6e; color: #6e6e6e;
font: bold 12px Helvetica, Arial, sans-serif; font: bold 12px Helvetica, Arial, sans-serif;
text-decoration: none; text-decoration: none;
padding: 7px 12px; padding: 7px 12px;
position: relative; position: relative;
display: inline-block; display: inline-block;
text-shadow: 0 1px 0 #fff; text-shadow: 0 1px 0 #fff;
-webkit-transition: border-color .218s; -webkit-transition: border-color .218s;
-moz-transition: border .218s; -moz-transition: border .218s;
-o-transition: border-color .218s; -o-transition: border-color .218s;
transition: border-color .218s; transition: border-color .218s;
background: #f3f3f3; background: #f3f3f3;
background: -webkit-gradient(linear,0% 40%,0% 70%,from(#F5F5F5),to(#F1F1F1)); background: -webkit-gradient(linear,0% 40%,0% 70%,from(#F5F5F5),to(#F1F1F1));
background: -moz-linear-gradient(linear,0% 40%,0% 70%,from(#F5F5F5),to(#F1F1F1)); background: -moz-linear-gradient(linear,0% 40%,0% 70%,from(#F5F5F5),to(#F1F1F1));
border: solid 1px #dcdcdc; border: solid 1px #dcdcdc;
border-radius: 2px; border-radius: 2px;
-webkit-border-radius: 2px; -webkit-border-radius: 2px;
-moz-border-radius: 2px; -moz-border-radius: 2px;
margin-right: 10px; margin-right: 10px;
} }
a.button.locked { a.button.locked {
color: #333; color: #333;
border-color: #999; border-color: #999;
-moz-box-shadow: 0 2px 0 rgba(0, 0, 0, 0.2); -moz-box-shadow: 0 2px 0 rgba(0, 0, 0, 0.2);
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
}
a.button.disabled {
color: #6e6e6e;
background-color: #fff;
/*border: 1px solid rgba(0, 0, 0, 0.05);*/
cursor: default;
opacity: 0.4;
filter: alpha(opacity=40);
}
a.button.disabled:hover, a.button.disabled:active {
color: inherit;
border: 1px solid #dcdcdc;
-moz-box-shadow: inherit;
-webkit-box-shadow: inherit;
box-shadow: inherit;
} }
a.button:hover { a.button:hover {
color: #333; color: #333;
border-color: #999; border-color: #999;
-moz-box-shadow: 0 2px 0 rgba(0, 0, 0, 0.2); -moz-box-shadow: 0 2px 0 rgba(0, 0, 0, 0.2);
-webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2); -webkit-box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15); box-shadow: 0 1px 2px rgba(0, 0, 0, 0.15);
} }
a.button:active { a.button:active {
color: #000; color: #000;
border-color: #444; border-color: #444;
} }
a.left { a.left {
-webkit-border-top-right-radius: 0; -webkit-border-top-right-radius: 0;
-moz-border-radius-topright: 0; -moz-border-radius-topright: 0;
border-top-right-radius: 0; border-top-right-radius: 0;
-webkit-border-bottom-right-radius: 0; -webkit-border-bottom-right-radius: 0;
-moz-border-radius-bottomright: 0; -moz-border-radius-bottomright: 0;
border-bottom-right-radius: 0; border-bottom-right-radius: 0;
margin: 0; margin: 0;
} }
a.middle { a.middle {
border-radius: 0; border-radius: 0;
-webkit-border-radius: 0; -webkit-border-radius: 0;
-moz-border-radius: 0; -moz-border-radius: 0;
border-left: solid 1px #f3f3f3; border-left: solid 1px #f3f3f3;
margin: 0; margin: 0;
border-left: solid 1px rgba(255, 255, 255, 0); border-left: solid 1px rgba(255, 255, 255, 0);
} }
a.middle:hover, a.middle:hover,
@@ -430,107 +447,122 @@ a.right:hover {
} }
a.right { a.right {
-webkit-border-top-left-radius: 0; -webkit-border-top-left-radius: 0;
-moz-border-radius-topleft: 0; -moz-border-radius-topleft: 0;
border-top-left-radius: 0; border-top-left-radius: 0;
-webkit-border-bottom-left-radius: 0; -webkit-border-bottom-left-radius: 0;
-moz-border-radius-bottomleft: 0; -moz-border-radius-bottomleft: 0;
border-bottom-left-radius: 0; border-bottom-left-radius: 0;
border-left: solid 1px #f3f3f3; border-left: solid 1px #f3f3f3;
border-left: solid 1px rgba(255, 255, 255, 0); border-left: solid 1px rgba(255, 255, 255, 0);
} }
a.big { a.big {
font-size: 16px; font-size: 16px;
padding: 10px 15px; padding: 10px 15px;
} }
a.supersize { a.supersize {
font-size: 20px; font-size: 20px;
padding: 15px 20px; padding: 15px 20px;
} }
a.save { a.save {
background: url(../images/icons/tick.png) 10px center no-repeat #f3f3f3; background: url(../images/icons/tick.png) 10px center no-repeat #f3f3f3;
padding-left: 30px; padding-left: 30px;
} }
a.upload { a.upload {
background: url(../images/icons/drive-upload.png) 10px center no-repeat #f3f3f3; background: url(../images/icons/drive-upload.png) 10px center no-repeat #f3f3f3;
padding-left: 30px; padding-left: 30px;
} }
a.add { a.add {
background: url(../images/icons/plus-circle.png) 10px center no-repeat #f3f3f3; background: url(../images/icons/plus-circle.png) 10px center no-repeat #f3f3f3;
padding-left: 30px; padding-left: 30px;
} }
a.delete { a.delete {
background: url(../images/icons/minus-circle.png) 10px center no-repeat #f3f3f3; background: url(../images/icons/minus-circle.png) 10px center no-repeat #f3f3f3;
padding-left: 30px; padding-left: 30px;
} }
a.edit { a.edit {
background: url(../images/icons/pencil.png) 10px center no-repeat #f3f3f3; background: url(../images/icons/pencil.png) 10px center no-repeat #f3f3f3;
padding-left: 30px; padding-left: 30px;
} }
a.flag { a.flag {
background: url(../images/icons/flag.png) 10px center no-repeat #f3f3f3; background: url(../images/icons/flag.png) 10px center no-repeat #f3f3f3;
padding-left: 30px; padding-left: 30px;
} }
a.cancel { a.cancel {
background: url(../images/icons/slash.png) 10px center no-repeat #f3f3f3; background: url(../images/icons/slash.png) 10px center no-repeat #f3f3f3;
padding-left: 30px; padding-left: 30px;
}
a.copy {
background: url(../images/icons/document-copy.png) 10px center no-repeat #f3f3f3;
padding-left: 30px;
}
a.job_start {
background: url(../images/icons/server--arrow.png) 10px center no-repeat #f3f3f3;
padding-left: 30px;
}
a.results {
background: url(../images/icons/blue-folder-open-document-text.png) 10px center no-repeat #f3f3f3;
padding-left: 30px;
} }
a.up { a.up {
background: url(../images/button-sprite.png) 13px -131px no-repeat #f3f3f3; background: url(../images/button-sprite.png) 13px -131px no-repeat #f3f3f3;
width: 18px; width: 18px;
} }
a.down { a.down {
background: url(../images/button-sprite.png) 13px -166px no-repeat #f3f3f3; background: url(../images/button-sprite.png) 13px -166px no-repeat #f3f3f3;
width: 18px; width: 18px;
} }
a.save-big { a.save-big {
background: url(../images/icons/tick.png) 15px center no-repeat #f3f3f3; background: url(../images/icons/tick.png) 15px center no-repeat #f3f3f3;
font-size: 16px; font-size: 16px;
padding: 10px 15px 10px 35px; padding: 10px 15px 10px 35px;
} }
a.add-big { a.add-big {
background: url(../images/icons/plus-circle.png) 15px center no-repeat #f3f3f3; background: url(../images/icons/plus-circle.png) 15px center no-repeat #f3f3f3;
font-size: 16px; font-size: 16px;
padding: 10px 15px 10px 35px; padding: 10px 15px 10px 35px;
} }
a.delete-big { a.delete-big {
background: url(../images/icons/minus-circle.png) 15px center no-repeat #f3f3f3; background: url(../images/icons/minus-circle.png) 15px center no-repeat #f3f3f3;
font-size: 16px; font-size: 16px;
padding: 10px 15px 10px 35px; padding: 10px 15px 10px 35px;
} }
a.flag-big { a.flag-big {
background: url(../images/icons/flag.png) 15px center no-repeat #f3f3f3; background: url(../images/icons/flag.png) 15px center no-repeat #f3f3f3;
font-size: 16px; font-size: 16px;
padding: 10px 15px 10px 35px; padding: 10px 15px 10px 35px;
} }
a.up-big { a.up-big {
background: url(../images/button-sprite.png) 15px -126px no-repeat #f3f3f3; background: url(../images/button-sprite.png) 15px -126px no-repeat #f3f3f3;
width: 18px; width: 18px;
font-size: 16px; font-size: 16px;
padding: 10px 15px; padding: 10px 15px;
} }
a.down-big { a.down-big {
background: url(../images/button-sprite.png) 15px -161px no-repeat #f3f3f3; background: url(../images/button-sprite.png) 15px -161px no-repeat #f3f3f3;
width: 18px; width: 18px;
font-size: 16px; font-size: 16px;
padding: 10px 15px; padding: 10px 15px;
} }
/* Tabs */ /* Tabs */
@@ -595,6 +627,119 @@ html ul.tabs li.active a:hover {
padding: 0; padding: 0;
} }
/* Progress bar */
div.progress_bar {
position: relative;
margin: 0 0 15px 0;
padding: 1px;
width: 250px;
height: 20px;
border: 1px solid #999;
background: #fff;
-webkit-border-radius: 2px;
-moz-border-radius: 2px;
border-radius: 2px;
}
div.progress_bar > span {
display: block;
height: 100%;
background-color: #5b86ac;
background-image: -webkit-gradient(
linear,
left bottom,
left top,
from(#5b86ac),
to(#6891b7)
);
background-image: -moz-linear-gradient(
center bottom,
#5b86ac,
#6891b7
);
background-image: -ms-linear-gradient(
center bottom,
#5b86ac,
#6891b7
);
background-image: -o-linear-gradient(
center bottom,
#5b86ac,
#6891b7
);
position: relative;
overflow: hidden;
}
div.progress_bar > span:after {
content: "";
position: absolute;
top: 0;
left: 0;
bottom: 0;
right: 0;
background-image: -webkit-gradient(
linear,
0 0,
100% 100%,
color-stop(.25, rgba(255, 255, 255, .08)),
color-stop(.25, transparent),
color-stop(.5, transparent),
color-stop(.5, rgba(255, 255, 255, .08)),
color-stop(.75, rgba(255, 255, 255, .08)),
color-stop(.75, transparent),
to(transparent)
);
background-image: -moz-linear-gradient(
-45deg,
rgba(255, 255, 255, .08) 25%,
transparent 25%,
transparent 50%,
rgba(255, 255, 255, .08) 50%,
rgba(255, 255, 255, .08) 75%,
transparent 75%,
transparent
);
background-image: -ms-linear-gradient(
-45deg,
rgba(255, 255, 255, .08) 25%,
transparent 25%,
transparent 50%,
rgba(255, 255, 255, .08) 50%,
rgba(255, 255, 255, .08) 75%,
transparent 75%,
transparent
);
background-image: -o-linear-gradient(
-45deg,
rgba(255, 255, 255, .08) 25%,
transparent 25%,
transparent 50%,
rgba(255, 255, 255, .08) 50%,
rgba(255, 255, 255, .08) 75%,
transparent 75%,
transparent
);
z-index: 1;
-webkit-background-size: 50px 50px;
-moz-background-size: 50px 50px;
-webkit-animation: move 2s linear infinite;
overflow: hidden;
}
div.progress_bar strong {
color: #111;
position: absolute;
top: 0;
left: 0;
z-index: 2;
width: 100%;
display: block;
line-height: 24px;
text-align: center;
}
/* JTip styles */ /* JTip styles */
.form_info a, .form_info a,
@@ -611,7 +756,7 @@ html ul.tabs li.active a:hover {
left: -11px; left: -11px;
height: 23px; height: 23px;
width: 10px; width: 10px;
top: -3px; top: -3px;
} }
#jt_arrow_right { #jt_arrow_right {
@@ -620,7 +765,7 @@ html ul.tabs li.active a:hover {
z-index: 101; z-index: 101;
height: 23px; height: 23px;
width: 11px; width: 11px;
top: -2px; top: -2px;
} }
#jt { #jt {
@@ -695,11 +840,11 @@ input.inplace_field, textarea.inplace_field {
} }
a.inplace_save { a.inplace_save {
background: url(../images/icons/tick.png) 10px center no-repeat #f3f3f3; background: url(../images/icons/tick.png) 10px center no-repeat #f3f3f3;
padding-left: 30px; padding-left: 30px;
} }
a.inplace_cancel { a.inplace_cancel {
background: url(../images/icons/slash.png) 10px center no-repeat #f3f3f3; background: url(../images/icons/slash.png) 10px center no-repeat #f3f3f3;
padding-left: 30px; padding-left: 30px;
} }