Subversion Repositories Sites.obs-saisons.fr

Compare Revisions

No changes between revisions

Ignore whitespace Rev 30 → Rev 31

/trunk/applications/doc/generation_bdd_saisie.sql
New file
0,0 → 1,115
CREATE TABLE obs_stations (
os_id_station INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
os_ce_participant INTEGER UNSIGNED NULL ,
os_nom VARCHAR(255) NULL ,
os_ce_commune INTEGER UNSIGNED NULL ,
os_latitude FLOAT NULL ,
os_longitude FLOAT NULL ,
os_altitude FLOAT NULL ,
os_ce_environnement INTEGER UNSIGNED NULL ,
os_commentaire TEXT NULL ,
PRIMARY KEY(os_id_station));
 
 
 
 
 
 
 
 
 
 
 
 
 
CREATE TABLE ods_communes (
oc_id_commune INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
oc_code_insee INTEGER UNSIGNED NULL ,
oc_nom TEXT NULL ,
oc_secteur VARCHAR(50) NULL ,
oc_latitude FLOAT NULL ,
oc_longitude FLOAT NULL ,
PRIMARY KEY(oc_id_commune));
 
 
 
 
 
 
 
 
 
 
CREATE TABLE ods_especes (
oe_id_espece INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
oe_nom_vernaculaire VARCHAR(255) NULL ,
oe_nom_scientifique VARCHAR(255) NULL ,
oe_espece_ecole BOOL NULL ,
oe_description TEXT NULL ,
oe_ce_climat INTEGER UNSIGNED NULL ,
oe_ce_evenements INTEGER UNSIGNED NULL ,
oe_ce_type INTEGER UNSIGNED NULL ,
PRIMARY KEY(oe_id_espece));
 
 
 
 
 
 
 
 
 
 
 
 
CREATE TABLE ods_individus (
oi_id_individu INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
oi_ce_espece INTEGER UNSIGNED NULL ,
oi_ce_station INTEGER UNSIGNED NULL ,
PRIMARY KEY(oi_id_individu));
 
 
 
 
 
 
 
CREATE TABLE ods_observations (
oo_id_observation INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
oo_ordre INTEGER UNSIGNED NOT NULL ,
oo_ce_individu INTEGER UNSIGNED NULL ,
oo_ce_evenement INTEGER UNSIGNED NULL ,
oo_date DATE NULL ,
oo_commentaire TEXT NULL ,
oo_date_saisie DATE NULL ,
oo_date_modification DATE NULL ,
PRIMARY KEY(oo_id_observation));
 
 
 
 
 
 
 
 
 
 
 
 
CREATE TABLE ods_triples (
ot_id_triple INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
ot_cle VARCHAR(255) NULL ,
ot_ce_parent VARCHAR(255) NULL ,
ot_valeur VARCHAR(255) NULL ,
ot_description TEXT NULL ,
PRIMARY KEY(ot_id_triple));
 
 
 
 
 
 
 
 
 
 
/trunk/applications/doc/test.sql
New file
0,0 → 1,177
CREATE TABLE ods_triples (
ot_id_triple INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
ot_cle VARCHAR(255) NULL ,
ot_ce_parent INTEGER UNSIGNED NULL ,
ot_valeur VARCHAR(255) NULL ,
ot_description TEXT NULL ,
PRIMARY KEY(ot_id_triple),
FOREIGN KEY(ot_ce_parent)
REFERENCES ods_triples(ot_id_triple)
ON DELETE NO ACTION
ON UPDATE NO ACTION);
 
 
 
 
 
 
 
CREATE INDEX IFK_fk_ot_id_triple_ot_ce_pare ON ods_triples (ot_ce_parent);
 
 
CREATE TABLE ods_communes (
oc_id_commune INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
oc_code_insee INTEGER UNSIGNED NULL ,
oc_nom TEXT NULL ,
oc_secteur VARCHAR(50) NULL ,
oc_latitude FLOAT NULL ,
oc_longitude FLOAT NULL ,
PRIMARY KEY(oc_id_commune));
 
 
 
 
 
 
 
 
 
 
CREATE TABLE participants (
id_participant INTEGER UNSIGNED NOT NULL AUTO_INCREMENT ,
PRIMARY KEY(id_participant));
 
 
 
 
CREATE TABLE obs_stations (
os_id_station INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
os_ce_participant INTEGER UNSIGNED NULL ,
os_nom VARCHAR(255) NULL ,
os_ce_commune INTEGER UNSIGNED NULL ,
os_latitude FLOAT NULL ,
os_longitude FLOAT NULL ,
os_altitude FLOAT NULL ,
os_ce_environnement INTEGER UNSIGNED NULL ,
os_commentaire TEXT NULL ,
PRIMARY KEY(os_id_station),
FOREIGN KEY(os_ce_participant)
REFERENCES participants(id_participant)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
FOREIGN KEY(os_ce_commune)
REFERENCES ods_communes(oc_id_commune)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
FOREIGN KEY(os_ce_environnement)
REFERENCES ods_triples(ot_id_triple)
ON DELETE NO ACTION
ON UPDATE NO ACTION);
 
 
 
 
 
 
 
 
 
 
 
CREATE INDEX IFK_fk_id_participant_os_ce_pa ON obs_stations (os_ce_participant);
CREATE INDEX IFK_fk_oc_id_commune_os_ce_com ON obs_stations (os_ce_commune);
CREATE INDEX IFK_fk_ot_id_triple_os_ce_envi ON obs_stations (os_ce_environnement);
 
 
CREATE TABLE ods_especes (
oe_id_espece INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
oe_nom_vernaculaire VARCHAR(255) NULL ,
oe_nom_scientifique VARCHAR(255) NULL ,
oe_espece_ecole BOOL NULL ,
oe_description TEXT NULL ,
oe_ce_climat INTEGER UNSIGNED NULL ,
oe_ce_evenements INTEGER UNSIGNED NULL ,
oe_ce_type INTEGER UNSIGNED NULL ,
PRIMARY KEY(oe_id_espece),
FOREIGN KEY(oe_ce_climat)
REFERENCES ods_triples(ot_id_triple)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
FOREIGN KEY(oe_ce_evenements)
REFERENCES ods_triples(ot_id_triple)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
FOREIGN KEY(oe_ce_type)
REFERENCES ods_triples(ot_id_triple)
ON DELETE NO ACTION
ON UPDATE NO ACTION);
 
 
 
 
 
 
 
 
 
 
CREATE INDEX IFK_fk_ot_id_triple_oe_ce_clim ON ods_especes (oe_ce_climat);
CREATE INDEX IFK_fk_ot_id_triples_oe_ce_eve ON ods_especes (oe_ce_evenements);
CREATE INDEX IFK_fk_ot_id_triple_oe_ce_type ON ods_especes (oe_ce_type);
 
 
CREATE TABLE ods_individus (
oi_id_individu INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
oi_ce_espece INTEGER UNSIGNED NULL ,
oi_ce_station INTEGER UNSIGNED NULL ,
PRIMARY KEY(oi_id_individu),
FOREIGN KEY(oi_ce_espece)
REFERENCES ods_especes(oe_id_espece)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
FOREIGN KEY(oi_ce_station)
REFERENCES obs_stations(os_id_station)
ON DELETE NO ACTION
ON UPDATE NO ACTION);
 
 
 
 
 
CREATE INDEX IFK_fk_oi_ce_espece_oe_id_espe ON ods_individus (oi_ce_espece);
CREATE INDEX IFK_fk_os_id_station_oi_ce_ind ON ods_individus (oi_ce_station);
 
 
CREATE TABLE ods_observations (
oo_id_observation INTEGER UNSIGNED NOT NULL AUTO_INCREMENT,
oo_ordre INTEGER UNSIGNED NOT NULL ,
oo_ce_individu INTEGER UNSIGNED NULL ,
oo_ce_evenement INTEGER UNSIGNED NULL ,
oo_date DATE NULL ,
oo_commentaire TEXT NULL ,
oo_date_saisie DATE NULL ,
oo_date_modification DATE NULL ,
PRIMARY KEY(oo_id_observation),
FOREIGN KEY(oo_ce_evenement)
REFERENCES ods_triples(ot_id_triple)
ON DELETE NO ACTION
ON UPDATE NO ACTION,
FOREIGN KEY(oo_ce_individu)
REFERENCES ods_individus(oi_id_individu)
ON DELETE NO ACTION
ON UPDATE NO ACTION);
 
 
 
 
 
 
 
 
 
 
CREATE INDEX IFK_fk_ot_id_triple_oo_ce_even ON ods_observations (oo_ce_evenement);
CREATE INDEX IFK_fk_oi_id_individu_oo_ce_in ON ods_observations (oo_ce_individu);
 
 
 
/trunk/applications/jrest/.htaccess
New file
0,0 → 1,4
RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-d
RewriteCond %{REQUEST_FILENAME} !-f
RewriteRule ^.*$ /obs_saisons/applications/jrest/index.php/
/trunk/applications/jrest/jrest.ini.php
New file
0,0 → 1,21
;<?/*
[settings]
baseURL = "/obs_saisons/applications/jrest/"
 
; Default
[appli]
phptype = mysql
username = aurelien
password = Canard
hostspec = localhost
database = ods_saisie
; LOGS
[log]
cheminlog = "/home/aurelien/Logs/"
timezone = "Europe/Paris"
taillemax = 100000
 
; ADMIN
[jrest_admin]
admin = aurelien@tela-botanica.org
;*/?>
/trunk/applications/jrest/JRest.php
New file
0,0 → 1,302
<?php
// In : utf8 url_encoded (get et post)
// Out : utf8
 
// TODO : gerer les retours : dans ce controleur : code retour et envoi ...
class JRest {
 
/** Parsed configuration file */
private $config;
 
/** The HTTP request method used. */
private $method = 'GET';
 
/** The HTTP request data sent (if any). */
private $requestData = NULL;
 
/** Array of strings to convert into the HTTP response. */
private $output = array();
 
/** Nom resource. */
private $resource = NULL;
 
/** Identifiant unique resource. */
private $uid = NULL;
 
/**
* Constructor. Parses the configuration file "JRest.ini", grabs any request data sent, records the HTTP
* request method used and parses the request URL to find out the requested resource
* @param str iniFile Configuration file to use
*/
public function JRest($iniFile = 'jrest.ini.php') {
$this->config = parse_ini_file($iniFile, TRUE);
if (isset($_SERVER['REQUEST_URI']) && isset($_SERVER['REQUEST_METHOD']) && isset($_SERVER['QUERY_STRING'])) {
if (isset($_SERVER['CONTENT_LENGTH']) && $_SERVER['CONTENT_LENGTH'] > 0) {
$this->requestData = '';
$httpContent = fopen('php://input', 'r');
while ($data = fread($httpContent, 1024)) {
$this->requestData .= $data;
}
fclose($httpContent);
}
if (strlen($_SERVER['QUERY_STRING']) == 0) {
$len = strlen($_SERVER['REQUEST_URI']);
} else {
$len = -(strlen($_SERVER['QUERY_STRING']) + 1);
}
$urlString = substr($_SERVER['REQUEST_URI'], strlen($this->config['settings']['baseURL']), $len);
 
$urlParts = explode('/', $urlString);
 
if (isset($urlParts[0])) $this->resource = $urlParts[0];
if (count($urlParts) > 1 && $urlParts[1] != '') {
array_shift($urlParts);
foreach ($urlParts as $uid) {
if ($uid != '') {
$this->uid[] = urldecode($uid);
}
}
}
 
$this->method = $_SERVER['REQUEST_METHOD'];
} else {
trigger_error('I require the server variables REQUEST_URI, REQUEST_METHOD and QUERY_STRING to work.', E_USER_ERROR);
}
}
 
/**
* Execute the request.
*/
function exec() {
switch ($this->method) {
case 'GET':
$this->get();
break;
case 'POST':
$this->post();
break;
case 'DELETE':
$this->delete();
break;
case 'PUT':
$this->add();
break;
}
}
 
/**
* Execute a GET request. A GET request fetches a list of resource when no resource name is given, a list of element
* when a resource name is given, or a resource element when a resource and resource unique identifier are given. It does not change the
* database contents.
*/
private function get() {
if ($this->resource) {
$resource_file = 'services/'.ucfirst($this->resource).'.php';
$resource_class = ucfirst($this->resource);
if (file_exists($resource_file)) {
include_once $resource_file;
if (class_exists($resource_class)) {
$service = new $resource_class($this->config);
if ($this->uid) { // get a resource element
if (method_exists($service, 'getElement')) {
$service->getElement($this->uid);
}
} elseif (method_exists($service, 'getRessource')) { // get all elements of a ressource
$service->getRessource();
}
}
}
} else { // get resources
// include set.jrest.php, instanticiation et appel
}
}
 
private function post() {
$pairs = array();
// Récupération des paramètres passés dans le contenu de la requête HTTP (= POST)
if ($this->requestData) {
$pairs = $this->parseRequestData();
}
 
// Ajout des informations concernant l'upload de fichier passées dans la variable $_FILE
if(isset($_FILES)) {
foreach ($_FILES as $v) {
$pairs[$v['name']] = $v;
}
 
// Ne pas effacer cette ligne ! Elle est indispensable pour les services du Carnet en ligne
// qui n'utilisent que le tableau pairs dans les posts
$pairs = array_merge($pairs, $_POST);
}
 
// gestion du contenu du post
if(isset($_POST))
{
// Safari ne sait pas envoyer des DELETE avec gwt...
// Nous utilisons le parametre "action" passé dans le POST qui doit contenir DELETE pour lancer la supression
if ($pairs['action'] == 'DELETE') {
$this->delete();
return;
}
 
if (count($pairs) != 0) {
if ($this->uid) { // get a resource element
$resource_file = 'services/'.ucfirst($this->resource).'.php';
$resource_class = ucfirst($this->resource);
if (file_exists($resource_file)) {
include_once $resource_file;
if (class_exists($resource_class)) {
$service = new $resource_class($this->config);
if (method_exists($service,'updateElement')) { // Update element
// TODO : a voir le retour ...
if ($service->updateElement($this->uid, $pairs)) {
$this->created();
}
}
}
}
} else { // get all elements of a ressource
$this->add($pairs);
}
} else {
$this->lengthRequired();
}
}
}
 
private function delete() {
$resource_file = 'services/'.ucfirst($this->resource).'.php';
$resource_class = ucfirst($this->resource);
if (file_exists($resource_file)) {
include_once $resource_file;
if (class_exists($resource_class)) {
$service = new $resource_class($this->config);
if ($this->uid) { // get a resource element
if (method_exists($service, 'deleteElement')) { // Delete element
if ($service->deleteElement($this->uid)) {
$this->noContent();
}
}
}
}
}
}
 
private function add($pairs = null) {
if (is_null($pairs)) {
$pairs = array();
// Récupération des paramètres passés dans le contenu de la requête HTTP (= POST)
// FIXME : vérifier que l'on récupère bien les données passées par PUT
if ($this->requestData) {
$pairs = $this->parseRequestData();
}
}
 
if (count($pairs) != 0) {
$resource_file = 'services/'.ucfirst($this->resource).'.php';
$resource_class = ucfirst($this->resource);
if (file_exists($resource_file)) {
include_once $resource_file;
if (class_exists($resource_class)) {
$service = new $resource_class($this->config);
if (method_exists($service,'createElement')) { // Create a new element
if ($service->createElement($pairs)) {
$this->created();
}
}
}
}
} else {
$this->lengthRequired();
}
}
 
/**
* Parse the HTTP request data.
* @return str[] Array of name value pairs
*/
private function parseRequestData() {
$values = array();
$pairs = explode('&', $this->requestData);
foreach ($pairs as $pair) {
$parts = explode('=', $pair);
if (isset($parts[0]) && isset($parts[1])) {
$parts[1] = rtrim(urldecode($parts[1]));
$values[$parts[0]] = $parts[1];
}
}
return $values;
}
 
/**
* Send a HTTP 201 response header.
*/
private function created($url = FALSE) {
header('HTTP/1.0 201 Created');
if ($url) {
header('Location: '.$url);
}
}
 
/**
* Send a HTTP 204 response header.
*/
private function noContent() {
header('HTTP/1.0 204 No Content');
}
 
/**
* Send a HTTP 400 response header.
*/
private function badRequest() {
header('HTTP/1.0 400 Bad Request');
}
 
/**
* Send a HTTP 401 response header.
*/
private function unauthorized($realm = 'JRest') {
if (!isset($_SERVER['PHP_AUTH_USER']) || !isset($_SERVER['PHP_AUTH_PW'])) {
header('WWW-Authenticate: Basic realm="'.$realm.'"');
}
header('HTTP/1.0 401 Unauthorized');
}
 
/**
* Send a HTTP 404 response header.
*/
private function notFound() {
header('HTTP/1.0 404 Not Found');
}
 
/**
* Send a HTTP 405 response header.
*/
private function methodNotAllowed($allowed = 'GET, HEAD') {
header('HTTP/1.0 405 Method Not Allowed');
header('Allow: '.$allowed);
}
 
/**
* Send a HTTP 406 response header.
*/
private function notAcceptable() {
header('HTTP/1.0 406 Not Acceptable');
echo join(', ', array_keys($this->config['renderers']));
}
 
/**
* Send a HTTP 411 response header.
*/
private function lengthRequired() {
header('HTTP/1.0 411 Length Required');
}
 
/**
* Send a HTTP 500 response header.
*/
private function internalServerError() {
header('HTTP/1.0 500 Internal Server Error');
}
}
?>
/trunk/applications/jrest/index.php
New file
0,0 → 1,40
<?php
 
// Decommenter ces lignes si version de php < 5
//require_once 'lib/JSON.php';
 
// Lazy require
// TODO : voir si on ne peut pas dépacer ces inclusions directement dans les services.
//require_once 'lib/DBAccessor.php';
//require_once 'lib/SpreadsheetProductor.php';
//require_once 'lib/PDFProductor.php';
//require 'JRest.php';
 
/**
* La fonction __autoload() charge dynamiquement les classes trouvées dans le code.
*
* Cette fonction est appelée par php5 quand il trouve une instanciation de classe dans le code.
*
*@param string le nom de la classe appelée.
*@return void le fichier contenant la classe doit être inclu par la fonction.
*/
function __autoloadJRest($classe)
{
if (class_exists($classe)) {
return null;
}
 
$chemins = array('', 'services/', 'services/include/', 'lib/');
foreach ($chemins as $chemin) {
$chemin = $chemin.$classe.'.php';
if (file_exists($chemin)) {
require_once $chemin;
}
}
}
 
spl_autoload_register('__autoloadJRest');
 
$jRest =& new JRest();
$jRest->exec();
?>
/trunk/applications/jrest/jrest.ini.defaut.php
New file
0,0 → 1,21
;<?/*
[settings]
baseURL = "/obs_saisons/applications/jrest/"
 
; Default
[appli]
phptype = mysql
username =
password =
hostspec =
database = ods_saisie
; LOGS
[log]
cheminlog = "/Logs/"
timezone = "Europe/Paris"
taillemax = 100000
 
; ADMIN
[jrest_admin]
admin =
;*/?>
/trunk/applications/jrest/services/OdsStation.php
New file
0,0 → 1,226
<?php
 
class OdsStation extends GestionTriple {
 
/**
* Méthode appelée avec une requête de type GET.
*
*/
function getElement($param = array()) {
if(isset($param[0])) {
$id_participant = $param[0];
} else {
return;
}
if($param[1] == "*") {
$info = $this->obtenirListeStationPourParticipant($id_participant);
} else if(is_numeric($param[1])) {
$id_station = $param[1];
$info = $this->obtenirInformationsStation($id_station);
}
// Envoi sur la sortie standard
$this->envoyer($info);
}
/**
* Méthode appelée pour ajouter un élément.
*/
public function createElement($params) {
$elements_requis = array('id_participant','station_nom', 'station_commune', 'station_lat', 'station_lon','station_milieu');
$erreurs = array();
foreach($elements_requis as $requis) {
if(!isset($params[$requis])) {
//$erreurs[$requis] = 'erreur ';
}
}
if(!empty($erreurs)) {
$this->envoyer($erreurs);
}
$station['alt'] = '0';
$id_participant = $params['id_participant'];
$requete_creation_station = 'INSERT INTO ods_stations '.
'(os_ce_participant, os_nom, os_ce_commune, os_latitude, os_longitude, os_altitude, os_ce_environnement, os_commentaire) '.
'VALUES ('.
$this->bdd->quote($id_participant).','.
$this->bdd->quote($params['station_nom']).','.
$this->bdd->quote($params['station_commune']).','.
$this->bdd->quote($params['station_lat']).','.
$this->bdd->quote($params['station_lon']).','.
$this->bdd->quote($params['station_alt']).','.
$this->bdd->quote($params['station_milieu']).','.
$this->bdd->quote($params['station_description']).')';
$creation_station = $this->executerRequeteSimple($requete_creation_station);
if(!$creation_station) {
// TODO: comment gère t'on les erreurs ?
}
$this->envoyer();
}
/**
* Méthode appelée pour mettre à jour un élément
*/
public function updateElement($uid, $params) {
if(!isset($uid[0])) {
$id_participant = $uid[0];
} else {
return;
}
if(!isset($uid[1])) {
$id_station = $uid[1];
} else {
return;
}
$requete_modification_station = 'UPDATE ods_stations '.
'SET '.
'os_nom ='.$this->bdd->quote($params['station_nom']).','.
'os_latitude ='.$this->bdd->quote($params['station_commune']).','.
'os_latitude ='.$this->bdd->quote($params['station_lat']).','.
'os_longitude ='.$this->bdd->quote($params['station_lon']).','.
'os_altitude ='.$this->bdd->quote($params['station_alt']).','.
'os_ce_environnement ='.$this->bdd->quote($params['station_milieu']).','.
'os_commentaire ='.$this->bdd->quote($params['station_description']).' '.
'WHERE os_ce_particant = '.$this->bdd->quote($id_participant).' '.
'AND os_id_station = '.$this->bdd->quote($id_station);
$modification_station = $this->executerRequeteSimple($requete_creation_station);
if(!$modification_station) {
// TODO: comment gère t'on les erreurs ?
}
$this->envoyer();
}
/**
* Méthode appelée pour supprimer un élément
*/
public function deleteElement($uid) {
// Pour le moment, pas de suppression des stations
return ;
if(!isset($uid[0])) {
$id_participant = $uid[0];
} else {
return;
}
if(!isset($uid[1])) {
$id_station = $uid[1];
} else {
return;
}
$requete_suppression_station = 'DELETE FROM ods_stations '.
'WHERE os_ce_particant = '.$this->bdd->quote($id_participant).' '.
'AND os_id_station = '.$this->bdd->quote($id_station);
// TODO : supprimer également tout ce qui est associé à la station (observations, etc...)
$suppression_station = $this->executerRequeteSimple($requete_suppression_station);
if(!$suppression_station) {
// TODO: comment gère t'on les erreurs ?
}
$this->envoyer();
}
// +---------------------------------------------------------------------------------------------------------------+
// METHODES D'ACCES A LA BASE DE DONNEES
private function obtenirListeStationPourParticipant($id_participant) {
$requete_liste_station = 'SELECT * FROM ods_stations WHERE os_ce_participant = '.$this->bdd->quote($id_participant);
 
$liste_station = $this->executerRequete($requete_liste_station);
$liste_station_formatees = array();
foreach($liste_station as $indice => $station) {
$station_champs_formates = $this->formaterChampsStationPourEnvoi($station);
$liste_station_formatees[$station['os_id_station']] = $station_champs_formates;
}
return $liste_station_formatees;
}
private function obtenirInformationsStation($id_station) {
$requete_infos_station = 'SELECT * FROM ods_stations WHERE os_id_station = '.$this->bdd->quote($id_station);
 
$infos_station = $this->executerRequete($requete_infos_station);
$infos_station_formatees = array();
if(!empty($infos_station)) {
$infos_station = $infos_station[0];
$infos_station_formatees = $this->formaterChampsStationPourEnvoi($infos_station);
}
return $infos_station_formatees;
}
private function formaterChampsStationPourEnvoi($station) {
$station_champs_formates = array(
'id' => $station['os_id_station'],
'nom' => $station['os_nom'],
'id_commune' => $station['os_ce_commune'],
'commune' => 'Montpellier',//$this->obtenirInformationsCommuneParCodeInsee($station['os_ce_commune']),
'milieu' => 'Urbain',//$this->obtenirInformationsMilieuParAbreviation($station['os_milieu']),
'latitude' => $station['os_latitude'],
'longitude' => $station['os_longitude'],
'altitude' => $station['os_altitude'],
'description' => $station['os_commentaire']
);
return $station_champs_formates;
}
private function obtenirInformationsCommuneParId($id_commune) {
$requete_infos_commune = 'SELECT * FROM ods_communes WHERE oc_id_commune = '.$this->bdd->quote($id_commune);
$infos_commune = $this->executerRequete($requete_infos_commune);
return $infos_commune;
}
private function obtenirInformationsCommuneParCodeInsee($code_insee_commune) {
$requete_infos_commune = 'SELECT * FROM ods_communes WHERE oc_code_insee = '.$this->bdd->quote($code_insee_commune);
$infos_commune = $this->executerRequete($requete_infos_commune);
return $infos_commune;
}
private function obtenirInformationsMilieuParAbreviation($abreviation_milieu) {
$informations_milieu = $this->obtenirValeurTripleParAbreviation($abreviation_milieu);
return $informations_milieu;
}
}
?>
/trunk/applications/jrest/services/GestionTriple.php
New file
0,0 → 1,43
<?php
 
class GestionTriple extends JRestService {
protected function obtenirValeursListeParId($id_liste) {
$requete = 'SELECT * FROM ods_triple WHERE ot_ce_parent ='.$this->proteger($id_liste);
$resultat = $this->executerRequete($requete);
return $resultat;
}
protected function obtenirValeursListeParAbreviation($abreviation_triple) {
$requete = 'SELECT * FROM ods_triple WHERE ot_ce_parent = (SELECT ot_id_triple WHERE ot_cle ='.$this->proteger($abreviation_triple).')';
$resultat = $this->executerRequete($requete);
return $resultat;
}
protected function obtenirValeurTripleParId($id_triple) {
$requete = 'SELECT * FROM ods_triple WHERE ot_id_triple = '.$this->proteger($id_triple);
$resultat = $this->executerRequete($requete);
return $resultat['ot_valeur'];
}
protected function obtenirValeurTripleParAbreviation($abreviation_triple) {
$requete = 'SELECT * FROM ods_triple WHERE ot_cle = '.$this->proteger($abreviation_triple);
$resultat = $this->executerRequete($requete);
return $resultat['ot_valeur'];
}
}
 
?>
/trunk/applications/jrest/services/JRestService.php
New file
0,0 → 1,311
<?php
/**
* Classe mère abstraite contenant les méthodes génériques des services.
* Encodage en entrée : utf8
* Encodage en sortie : utf8
*
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @license GPL v3 <http://www.gnu.org/licenses/gpl.txt>
* @license CECILL v2 <http://www.cecill.info/licences/Licence_CeCILL_V2-en.txt>
* @version $Id$
* @copyright 2009
*/
abstract class JRestService {
 
public $config;
protected $bdd;
protected $log = array();
protected $messages = array();
protected $debug = array();
protected $distinct = false;
protected $orderby = null;
protected $formatRetour = 'objet';
protected $start = 0;
protected $limit = 150;
 
public function __construct($config, $demarrer_session = true) {
// Tableau contenant la config de Jrest
$this->config = $config;
 
// Connection à la base de données
$this->bdd = $this->connecterPDO($this->config, 'appli');
 
// Nettoyage du $_GET (sécurité)
if (isset($_GET)) {
$get_params = array('orderby', 'distinct', 'start', 'limit', 'formatRetour');
foreach ($get_params as $get) {
$verifier = array('NULL', "\n", "\r", "\\", "'", '"', "\x00", "\x1a", ';');
$_GET[$get] = str_replace($verifier, '', $_GET[$get]);
if (isset($_GET[$get]) && $_GET[$get] != '') {
$this->$get = $_GET[$get];
} else {
$_GET[$get] = null;
}
}
}
}
 
/**
* Méthode appelée quand aucun paramètre n'est passé dans l'url et avec une requête de type GET.
*/
public function getRessource() {
$this->getElement(array());
}
 
protected function envoyer($donnees = null, $mime = 'text/html', $encodage = 'utf-8', $json = true) {
// Traitements des messages d'erreurs et données
if (count($this->messages) != 0) {
header('HTTP/1.1 500 Internal Server Error');
$mime = 'text/html';
$encodage = 'utf-8';
$json = true;
$sortie = $this->messages;
} else {
$sortie = $donnees;
if (is_null($donnees)) {
$sortie = 'OK';
}
}
 
// Gestion de l'envoie du déboguage
$this->envoyerDebogage();
 
// Encodage au format et JSON et envoie sur la sortie standard
$contenu = $json ? json_encode($sortie) : $sortie;
$this->envoyerContenu($encodage, $mime, $contenu);
}
 
protected function envoyerDebogage() {
if (!is_array($this->debug)) {
$this->debug[] = $this->debug;
}
if (count($this->debug) != 0) {
foreach ($this->debug as $cle => $val) {
if (is_array($val)) {
$this->debug[$cle] = print_r($val, true);
}
}
header('X-DebugJrest-Data:'.json_encode($this->debug));
}
}
 
protected function envoyerContenu($encodage, $mime, $contenu) {
header("Content-Type: $mime; charset=$encodage");
print $contenu;
}
 
private function connecterPDO($config, $base = 'database') {
$cfg = $config[$base];
$dsn = $cfg['phptype'].':dbname='.$cfg['database'].';host='.$cfg['hostspec'];
try {
$PDO = new PDO($dsn, $cfg['username'], $cfg['password']);
} catch (PDOException $e) {
echo 'La connexion à la base de donnée via PDO a échouée : ' . $e->getMessage();
}
// Passe en UTF-8 la connexion à la BDD
$PDO->exec("SET NAMES 'utf8'");
// Affiche les erreurs détectées par PDO (sinon mode silencieux => aucune erreur affiché)
$PDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $PDO;
}
protected function executerRequete($requete, $retour = 'All', $mode = PDO::FETCH_ASSOC) {
try {
switch ($retour) {
case 'All' :
$resultat = $this->bdd->query($requete)->fetchAll($mode);
break;
case 'Column' :
$resultat = $this->bdd->query($requete)->fetchColumn();
break;
default:
$resultat = false;
$this->messages[] = "Le type de retour '$retour' est inconnu.";
}
if ($resultat === false) {
$this->messages[] = "La requête a retourné aucun résultat.";
}
} catch (PDOException $e) {
$this->messages[] = sprintf($this->getTxt('sql_erreur_requete'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete);
}
return $resultat;
}
protected function executerRequeteSimple($requete) {
try {
$resultat = false;
$resultat = $this->bdd->query($requete);
if ($resultat === false) {
$this->messages[] = "La requête a retourné aucun résultat.";
}
} catch (PDOException $e) {
$this->messages[] = sprintf($this->getTxt('sql_erreur_requete'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete);
}
return $resultat;
}
 
protected function getTxt($id) {
$sortie = '';
switch ($id) {
case 'sql_erreur' : $sortie = 'Requête echec. Fichier : "%s". Ligne : "%s". Message : %s'; break;
default : $sortie = $id;
}
return $sortie;
}
 
protected function traiterParametresUrl($params_attendu, $params, $pourBDD = true) {
$sortie = array();
foreach ($params_attendu as $num => $nom) {
if (isset($params[$num]) && $params[$num] != '*') {
if ($pourBDD) {
$params[$num] = $this->bdd->quote($params[$num]);
}
$sortie[$nom] = $params[$num];
}
}
return $sortie;
}
 
protected function traiterParametresPost($params) {
$sortie = array();
foreach ($params as $cle => $valeur) {
$sortie[$cle] = $this->bdd->quote($valeur);
}
return $sortie;
}
 
protected function getIdentification(&$params) {
// Initialisation des variables
$utilisateur = array(0, session_id());
 
// L'id utilisateur est soit passé par le POST soit dans l'url
if (is_array($params) && isset($params['cmhl_ce_modifier_par'])) {
$utilisateur[0] = $params['cmhl_ce_modifier_par'];
unset($params['cmhl_ce_modifier_par']);
} else if (is_string($params)) {
$utilisateur[0] = $params;
}
 
return $utilisateur;
}
 
protected function etreAutorise($id_utilisateur) {
$autorisation = false;
if (($_SESSION['coel_utilisateur'] != '') && $_SESSION['coel_utilisateur']['id'] != $id_utilisateur) {
$this->messages[] = 'Accès interdit.';
} else if ($_SESSION['coel_utilisateur'] == '') {
$this->messages[] = 'Veuillez vous identifiez pour accéder à cette fonction.';
} else {
$autorisation = true;
}
return $autorisation;
}
 
private function gererIdentificationPermanente() {
// Pour maintenir l'utilisateur tjrs réellement identifié nous sommes obligé de recréer une SESSION et de le recharger depuis la bdd
if ($this->getUtilisateur() == ''
&& isset($_COOKIE['coel_login'])
&& ($utilisateur = $this->chargerUtilisateur($_COOKIE['coel_login'], $_COOKIE['coel_mot_de_passe']))) {
$this->setUtilisateur($utilisateur, $_COOKIE['coel_permanence']);
}
}
 
protected function getUtilisateur() {
return (isset($_SESSION['coel_utilisateur']) ? $_SESSION['coel_utilisateur'] : '');
}
 
/**
* Méthode prenant en paramètre un chemin de fichier squelette et un tableau associatif de données,
* en extrait les variables, charge le squelette et retourne le résultat des deux combinés.
*
* @param String $fichier le chemin du fichier du squelette
* @param Array $donnees un tableau associatif contenant les variables a injecter dans le squelette.
*
* @return boolean false si le squelette n'existe pas, sinon la chaine résultat.
*/
public static function traiterSquelettePhp($fichier, Array $donnees = array()) {
$sortie = false;
if (file_exists($fichier)) {
// Extraction des variables du tableau de données
extract($donnees);
// Démarage de la bufferisation de sortie
ob_start();
// Si les tags courts sont activés
if ((bool) @ini_get('short_open_tag') === true) {
// Simple inclusion du squelette
include $fichier;
} else {
// Sinon, remplacement des tags courts par la syntaxe classique avec echo
$html_et_code_php = self::traiterTagsCourts($fichier);
// Pour évaluer du php mélangé dans du html il est nécessaire de fermer la balise php ouverte par eval
$html_et_code_php = '?>'.$html_et_code_php;
// Interprétation du html et du php dans le buffer
echo eval($html_et_code_php);
}
// Récupèration du contenu du buffer
$sortie = ob_get_contents();
// Suppression du buffer
@ob_end_clean();
} else {
$msg = "Le fichier du squelette '$fichier' n'existe pas.";
trigger_error($msg, E_USER_WARNING);
}
// Retourne le contenu
return $sortie;
}
 
/**
* Fonction chargeant le contenu du squelette et remplaçant les tags court php (<?= ...) par un tag long avec echo.
*
* @param String $chemin_squelette le chemin du fichier du squelette
*
* @return string le contenu du fichier du squelette php avec les tags courts remplacés.
*/
private static function traiterTagsCourts($chemin_squelette) {
$contenu = file_get_contents($chemin_squelette);
// Remplacement de tags courts par un tag long avec echo
$contenu = str_replace('<?=', '<?php echo ', $contenu);
// Ajout systématique d'un point virgule avant la fermeture php
$contenu = preg_replace("/;*\s*\?>/", "; ?>", $contenu);
return $contenu;
}
/* ====================== Fonctions à trier ou factoriser héritées de l'ancienne classe JrestService ======================= */
public function isAdmin($id) {
$admins = $this->config['jrest_admin']['admin'];
$admin_tab = split(',',$admins);
 
if (in_array($id,$admin_tab)) {
return true;
} else {
return false;
}
}
 
public function controleUtilisateur($id) {
if ($_SESSION['user']['name'] == '') {
//cas de la session temporaire, on ne fait rien de particulier
} else {
if (!$this->isAdmin($_SESSION['user']['name']) && $_SESSION['user']['name'] != $id) {
// cas d'usurpation d'identité
print 'Accès interdit';
exit();
}
}
}
 
public function logger($index,$chaine) {
if(!class_exists('Log')) {
include_once('Log.php');
Log::getInstance();
}
 
Log::setCheminLog($this->config['log']['cheminlog']);
Log::setTimeZone($this->config['log']['timezone']);
Log::setTailleMax($this->config['log']['taillemax']);
 
Log::ajouterEntree($index,$chaine);
}
}
?>
/trunk/applications/jrest/services/squelettes/rss1.tpl.xml
New file
0,0 → 1,45
<?php echo '<?xml version="1.0" encoding="UTF-8"?>'."\n";?>
 
<!DOCTYPE rdf:RDF [
<!ENTITY % HTMLlat1 PUBLIC
"-//W3C//ENTITIES Latin 1 for XHTML//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent">
%HTMLlat1;
]>
 
<rdf:RDF
xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
xmlns:dc="http://purl.org/dc/elements/1.1/"
xmlns="http://purl.org/rss/1.0/">
 
<channel rdf:about="<?=$guid?>">
<title><?=$titre?></title>
<link><?=$lien_coel?></link>
<description><?=$description?></description>
<dc:publisher><?=$editeur?></dc:publisher>
<dc:date><?=$date_maj_W3C?></dc:date>
<?php if (isset($items)) : ?>
<items>
<rdf:Seq>
<?php foreach ($items as $item) : ?>
<rdf:li resource="<?=$item['guid']?>" />
<?php endforeach; ?>
</rdf:Seq>
</items>
<?php endif; ?>
 
</channel>
<?php if (isset($items)) : ?>
<?php foreach ($items as $item) : ?>
<item rdf:about="<?=$item['guid']?>">
<title><?=$item['titre']?></title>
<link><?=$item['lien']?></link>
<description><?=$item['description_encodee']?></description>
<dc:date><?=$item['date_maj_W3C']?></dc:date>
</item>
<?php endforeach; ?>
<?php endif; ?>
</rdf:RDF>
/trunk/applications/jrest/services/squelettes/rss2.tpl.xml
New file
0,0 → 1,22
<?php echo '<?xml version="1.0" encoding="UTF-8"?>'."\n";?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
<channel>
<title><?=$titre?></title>
<link><?=$lien_coel?></link>
<atom:link href="<?=$lien_service?>" rel="self" type="application/rss+xml" />
<description><?=$description?></description>
<?php if (isset($items)) : ?>
<?php foreach ($items as $item) : ?>
<item>
<guid><?=$item['guid']?></guid>
<title><?=$item['titre']?></title>
<link><?=$item['lien']?></link>
<description><?=$item['description_encodee']?></description>
<pubDate><?=$item['date_maj_RSS']?></pubDate>
</item>
<?php endforeach; ?>
<?php endif; ?>
</channel>
</rss>
/trunk/applications/jrest/services/squelettes/diff.tpl.html
New file
0,0 → 1,36
<table style="border:1px solid black;border-collapse:collapse;" summary="Différences entre les données du <?=$date_ancienne?> et du <?=$date_nouvelle?>.">
<caption style="text-align:left;font-weight:bold;">Différences</caption>
<thead style="border:1px solid black;">
<tr>
<th rowspan="2" style="border:1px dotted;">Champ</th>
<th rowspan="2" style="border:1px dotted;">Type</th>
<th <?=(($etat == 'M') ? 'colspan="2"' : '');?> style="border:1px dotted;">Valeur</th>
</tr>
<tr>
<?php if ($etat == 'M') : ?>
<th style="border:1px dotted;">Ancienne (<?=$date_ancienne?>)</th>
<?php endif; ?>
<th style="border:1px dotted;">Nouvelle (<?=$date_nouvelle?>)</th>
</tr>
</thead>
<tbody>
<?php foreach ($differences as $champ => $diff) : ?>
<?php if ($diff['type'] == 'A') :
$couleur = CFC;
elseif ($diff['type'] == 'M') :
$couleur = FFC;
elseif ($diff['type'] == 'S') :
$couleur = F99;
endif; ?>
<tr style="background-color:#<?=$couleur?>;">
<td style="border:1px dotted;"><?=$champ?></td>
<td style="text-align:center;border:1px dotted;"><?=$diff['type_txt']?></td>
<?php if ($etat == 'M') : ?>
<td style="border:1px dotted;"><?=$diff['ancien']?></td>
<?php endif; ?>
<td style="border:1px dotted;"><?=$diff['nouveau']?></td>
</tr>
<?php endforeach; ?>
</tbody>
</table>
/trunk/applications/jrest/services/squelettes/atom.tpl.xml
New file
0,0 → 1,33
<?php echo '<?xml version="1.0" encoding="UTF-8"?>'."\n";?>
<feed xmlns="http://www.w3.org/2005/Atom">
<title><?=$titre?></title>
<link href="<?=$lien_coel?>" rel="alternate" type="text/html" hreflang="fr" />
<link href="<?=$lien_service?>" rel="self" type="application/atom+xml"/>
<updated><?=$date_maj_ATOM?></updated>
<author>
<name><?=$editeur?></name>
</author>
<id><?=$guid?></id>
<rights>Copyright (c) <?=$annee_courante?>, <?=$editeur?></rights>
<generator uri="<?=$lien_coel?>" version="<?=$generateur_version?>"><?=$generateur?></generator>
 
<?php if (isset($items)) : ?>
<?php foreach ($items as $item) : ?>
<entry>
<id><?=$item['lien']?></id>
<title><?=$item['titre']?></title>
<link href="<?=$item['lien']?>"/>
<updated><?=$item['date_maj_ATOM']?></updated>
<author><name><?=$item['modifier_par']?></name></author>
<content type="xhtml" xml:lang="fr">
<div xmlns="http://www.w3.org/1999/xhtml">
<?=$item['description'];?>
</div>
</content>
</entry>
<?php endforeach; ?>
<?php endif; ?>
 
</feed>
/trunk/applications/jrest/services/OdsEspece.php
New file
0,0 → 1,201
<?php
 
class OdsEspece extends JRestService {
 
const PREFIXE = 'get';
/**
* Méthode appelée avec une requête de type GET.
*
*/
function getElement($param = array()) {
$type = $param[0];
if ($type == '*' || is_numeric($type)) {
$info = $this->getElementParDefaut($param);
} else {
$methode = self::PREFIXE.$type;
if (method_exists($this, $methode)) {
array_shift($param);
$info = $this->$methode($param);
} else {
$this->messages[] = "Le type d'information demandé '$type' n'est pas disponible.";
}
}
// Envoi sur la sortie standard
$this->envoyer($info);
}
/**
* Méthode appelée pour ajouter un élément.
*/
public function createElement($params) {
// Identification de l'utilisateur
list($id_utilisateur, $id_session) = $this->getIdentification($params);
// Contrôle du non détournement de l'utilisateur
if ($this->etreAutorise($id_utilisateur)) {
try {
} catch (PDOException $e) {
$this->messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete);
}
}
$this->envoyer((string) $id_personne);
}
/**
* Méthode appelée pour mettre à jour un élément
*/
public function updateElement($uid, $params) {
//Mise à jour de la personne
// Identification de l'utilisateur
list($id_utilisateur, $id_session) = $this->getIdentification($params);
// Contrôle du non détournement de l'utilisateur
if ($this->etreAutorise($id_utilisateur)) {
try {
 
} catch (PDOException $e) {
$messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete);
}
}
$this->envoyer();
}
/**
* Méthode appelée pour supprimer un élément
*/
public function deleteElement($uid) {
// Vérification de la présence des id passés par l'url
if (!isset($uid[0]) || !isset($uid[1])) {
$this->messages[] = "Identifiant d'utilisateur ou de personne manquant. Vous ne devriez pas avoir accès à ce service.";
} else {
list($id_utilisateur, $id_session) = $this->getIdentification($uid[0]);
// Contrôle du non détournement de l'utilisateur
if ($this->etreAutorise($id_utilisateur)) {
// Récupération des id passés par l'url
$identifiants = explode(',', rtrim($uid[1], ','));
if (count($identifiants) == 0) {
$this->messages[] = "Aucun enregistrement n'a été supprimé.";
} else {
foreach ($identifiants as $id_personne) {
try {
} catch (PDOException $e) {
$this->messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete);
}
}
}
}
}
// Envoie sur la sortie standard
$this->envoyer();
}
/** ======================= Methodes privées d'accès au informations ================================ */
private function getElementParDefaut() {
return $this->getEspeces();
}
private function getEspeces() {
return array(
'1' => array(
'id' => '1',
'nom_vernaculaire' => 'Erable de Montpellier'
),
'2' => array(
'id' => '2',
'nom_vernaculaire' => 'Herbe à canard'
),
'3' => array(
'id' => '3',
'nom_vernaculaire' => 'Coquelicot'
),
'4' => array(
'id' => '4',
'nom_vernaculaire' => 'Herbe à chat'
),
'5' => array(
'id' => '5',
'nom_vernaculaire' => 'Hibou'
),
'6' => array(
'id' => '6',
'nom_vernaculaire' => 'Coucou'
)
);
}
private function getEspece($params) {
$id_espece = $params[0];
$infos_espece = array(
'id' => $id_espece,
'nom_scientifique' => 'Acer monspessulanum',
'nom_vernaculaire' => 'Erable de Montpellier',
'description' => 'Un arbre bien connu de tous',
'url_image' => 'http://www.tela-botanica.org/sites/eflore/generique/images/projets/eflore/bdnff/4.02/nt/8523/002543.jpg'
);
return $infos_espece;
}
private function getEspecesPourStation($params) {
$id_station = $params[0];
$especes_dans_station = $this->getEspeces();
$modulo = ($id_station+7)%6;
if($modulo == 0) $modulo = 1;
return array_slice($especes_dans_station, 0, $modulo);
}
private function getEspecesParType($params) {
$id_type = $params[0];
return $especes_par_type = array(
'1' => array(
'nom' => 'herbacées',
'especes' => array(
'1' => array('id' => '1',
'nom_vernaculaire' => 'Herbe de Montpellier'
)
)
),
'2' => array(
'nom' => 'arbres',
'especes' => array(
'id' => array('id' => '1',
'nom_vernaculaire' => 'Erable de Montpellier'
)
)
),
'3' => array(
'nom' => 'animaux',
'especes' => array(
'id' => array('id' => '1',
'nom_vernaculaire' => 'Hibou de Montpellier'
)
)
)
);
return $especes_par_type;
}
}
?>
/trunk/applications/jrest/services/OdsEvenement.php
New file
0,0 → 1,131
<?php
 
class OdsEvenement extends JRestService {
 
const PREFIXE = 'get';
/**
* Méthode appelée avec une requête de type GET.
*
*/
function getElement($param = array()) {
$type = $param[0];
if ($type == '*' || is_numeric($type)) {
$info = $this->getElementParDefaut($param);
} else {
$methode = self::PREFIXE.$type;
if (method_exists($this, $methode)) {
array_shift($param);
$info = $this->$methode($param);
} else {
$this->messages[] = "Le type d'information demandé '$type' n'est pas disponible.";
}
}
// Envoi sur la sortie standard
$this->envoyer($info);
}
/**
* Méthode appelée pour ajouter un élément.
*/
public function createElement($params) {
// Identification de l'utilisateur
list($id_utilisateur, $id_session) = $this->getIdentification($params);
// Contrôle du non détournement de l'utilisateur
if ($this->etreAutorise($id_utilisateur)) {
try {
} catch (PDOException $e) {
$this->messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete);
}
}
$this->envoyer((string) $id_personne);
}
/**
* Méthode appelée pour mettre à jour un élément
*/
public function updateElement($uid, $params) {
//Mise à jour de la personne
// Identification de l'utilisateur
list($id_utilisateur, $id_session) = $this->getIdentification($params);
// Contrôle du non détournement de l'utilisateur
if ($this->etreAutorise($id_utilisateur)) {
try {
 
} catch (PDOException $e) {
$messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete);
}
}
$this->envoyer();
}
/**
* Méthode appelée pour supprimer un élément
*/
public function deleteElement($uid) {
// Vérification de la présence des id passés par l'url
if (!isset($uid[0]) || !isset($uid[1])) {
$this->messages[] = "Identifiant d'utilisateur ou de personne manquant. Vous ne devriez pas avoir accès à ce service.";
} else {
list($id_utilisateur, $id_session) = $this->getIdentification($uid[0]);
// Contrôle du non détournement de l'utilisateur
if ($this->etreAutorise($id_utilisateur)) {
// Récupération des id passés par l'url
$identifiants = explode(',', rtrim($uid[1], ','));
if (count($identifiants) == 0) {
$this->messages[] = "Aucun enregistrement n'a été supprimé.";
} else {
foreach ($identifiants as $id_personne) {
try {
} catch (PDOException $e) {
$this->messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete);
}
}
}
}
}
// Envoie sur la sortie standard
$this->envoyer();
}
private function getElementParDefaut($param) {
}
private function getEvenementsPourEspece($id_espece) {
return array(
'1' => array(
'id' => '1',
'nom' => 'feuillaison',
'stades' => array('6','12')
),
'2' => array(
'id' => '2',
'nom' => 'floraison',
'stades' => array('22','33')
),
'3' => array(
'id' => '3',
'nom' => 'fructification',
'stades' => array('85')
),
'4' => array(
'id' => '4',
'nom' => 'senescence',
'stades' => array('90','95')
)
);
}
}
?>
/trunk/applications/jrest/services/OdsIndividu.php
New file
0,0 → 1,147
<?php
 
class OdsIndividu extends JRestService {
 
/**
* Méthode appelée avec une requête de type GET.
*
*/
function getElement($param = array()) {
$donnees_test = $this->retournerDonneesTest();
if($param[0] == "*") {
$info = $donnees_test;
} else if(is_numeric($param[0])) {
$info = $donnees_test[$param[0]];
}
// Envoi sur la sortie standard
$this->envoyer($info);
}
/**
* Méthode appelée pour ajouter un élément.
*/
public function createElement($params) {
// Identification de l'utilisateur
list($id_utilisateur, $id_session) = $this->getIdentification($params);
// Contrôle du non détournement de l'utilisateur
if ($this->etreAutorise($id_utilisateur)) {
try {
} catch (PDOException $e) {
$this->messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete);
}
}
$this->envoyer((string) $id_personne);
}
/**
* Méthode appelée pour mettre à jour un élément
*/
public function updateElement($uid, $params) {
//Mise à jour de la personne
// Identification de l'utilisateur
list($id_utilisateur, $id_session) = $this->getIdentification($params);
// Contrôle du non détournement de l'utilisateur
if ($this->etreAutorise($id_utilisateur)) {
try {
 
} catch (PDOException $e) {
$messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete);
}
}
$this->envoyer();
}
/**
* Méthode appelée pour supprimer un élément
*/
public function deleteElement($uid) {
// Vérification de la présence des id passés par l'url
if (!isset($uid[0]) || !isset($uid[1])) {
$this->messages[] = "Identifiant d'utilisateur ou de personne manquant. Vous ne devriez pas avoir accès à ce service.";
} else {
list($id_utilisateur, $id_session) = $this->getIdentification($uid[0]);
// Contrôle du non détournement de l'utilisateur
if ($this->etreAutorise($id_utilisateur)) {
// Récupération des id passés par l'url
$identifiants = explode(',', rtrim($uid[1], ','));
if (count($identifiants) == 0) {
$this->messages[] = "Aucun enregistrement n'a été supprimé.";
} else {
foreach ($identifiants as $id_personne) {
try {
} catch (PDOException $e) {
$this->messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete);
}
}
}
}
}
// Envoie sur la sortie standard
$this->envoyer();
}
private function retournerDonneesTest() {
return array(
'1' =>
array(
'id' => '1',
'nom' => 'Individu 1',
'nom_scientifique' => 'Acer monspessulanum',
'nom_vernaculaire' => 'Erable de Montpellier',
'description' => 'Quel bel individu!'
),
'2' =>
array(
'id' => '2',
'nom' => 'Individu 2',
'nom_scientifique' => 'Acer monspessulanum',
'nom_vernaculaire' => 'Erable de Montpellier',
'description' => 'Quel bel individu!'
),
'3' =>
array(
'id' => '3',
'nom' => 'Individu 3',
'nom_scientifique' => 'Acer monspessulanum',
'nom_vernaculaire' => 'Erable de Montpellier',
'description' => 'Quel bel individu!'
),
'4' =>
array(
'id' => '4',
'nom' => 'Individu 4',
'nom_scientifique' => 'Acer monspessulanum',
'nom_vernaculaire' => 'Erable de Montpellier',
'description' => 'Quel bel individu!'
),
'5' =>
array(
'id' => '5',
'nom' => 'Individu 5',
'nom_scientifique' => 'Acer monspessulanum',
'nom_vernaculaire' => 'Erable de Montpellier',
'description' => 'Quel bel individu!'
),
'6' =>
array(
'id' => '6',
'nom' => 'Individu 6',
'nom_scientifique' => 'Acer monspessulanum',
'nom_vernaculaire' => 'Erable de Montpellier',
'description' => 'Quel bel individu!'
)
);
}
}
?>
/trunk/applications/jrest/services/OdsObservation.php
New file
0,0 → 1,130
<?php
 
class OdsObservation extends JRestService {
 
const PREFIXE = 'get';
/**
* Méthode appelée avec une requête de type GET.
*
*/
function getElement($param = array()) {
$type = $param[0];
if ($type == '*' || is_numeric($type)) {
$info = $this->getElementParDefaut($param);
} else {
$methode = self::PREFIXE.$type;
if (method_exists($this, $methode)) {
array_shift($param);
$info = $this->$methode($param);
} else {
$this->messages[] = "Le type d'information demandé '$type' n'est pas disponible.";
}
}
// Envoi sur la sortie standard
$this->envoyer($info);
}
/**
* Méthode appelée pour ajouter un élément.
*/
public function createElement($params) {
// Identification de l'utilisateur
list($id_utilisateur, $id_session) = $this->getIdentification($params);
// Contrôle du non détournement de l'utilisateur
if ($this->etreAutorise($id_utilisateur)) {
try {
} catch (PDOException $e) {
$this->messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete);
}
}
$this->envoyer((string) $id_personne);
}
/**
* Méthode appelée pour mettre à jour un élément
*/
public function updateElement($uid, $params) {
//Mise à jour de la personne
// Identification de l'utilisateur
list($id_utilisateur, $id_session) = $this->getIdentification($params);
// Contrôle du non détournement de l'utilisateur
if ($this->etreAutorise($id_utilisateur)) {
try {
 
} catch (PDOException $e) {
$messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete);
}
}
$this->envoyer();
}
/**
* Méthode appelée pour supprimer un élément
*/
public function deleteElement($uid) {
// Vérification de la présence des id passés par l'url
if (!isset($uid[0]) || !isset($uid[1])) {
$this->messages[] = "Identifiant d'utilisateur ou de personne manquant. Vous ne devriez pas avoir accès à ce service.";
} else {
list($id_utilisateur, $id_session) = $this->getIdentification($uid[0]);
// Contrôle du non détournement de l'utilisateur
if ($this->etreAutorise($id_utilisateur)) {
// Récupération des id passés par l'url
$identifiants = explode(',', rtrim($uid[1], ','));
if (count($identifiants) == 0) {
$this->messages[] = "Aucun enregistrement n'a été supprimé.";
} else {
foreach ($identifiants as $id_personne) {
try {
} catch (PDOException $e) {
$this->messages[] = sprintf($this->getTxt('sql_erreur'), $e->getFile(), $e->getLine(), $e->getMessage(), $requete);
}
}
}
}
}
// Envoie sur la sortie standard
$this->envoyer();
}
private function getElementParDefaut($params) {
}
private function getObservationsPourStation($params) {
$id_station = $param[0];
$annee = $param[1];
return ;
 
}
private function getObservationsPourIndividu($params) {
$id_individu = $param[0];
$annee = $param[1];
return array(
'1' => '01/01/2010',
'2' => '13/01/2010',
'3' => '06/02/2010',
'4' => '08/05/2010',
'5' => '09/07/2010',
'6' => '08/08/2010',
'7' => '25/10/2010',
);
}
}
?>
/trunk/applications/jrest/lib/PEAR.php
New file
0,0 → 1,971
<?php
//
// +----------------------------------------------------------------------+
// | PEAR, the PHP Extension and Application Repository |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2004 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.0 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available through the world-wide-web at the following url: |
// | http://www.php.net/license/3_0.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Authors: Sterling Hughes <sterling@php.net> |
// | Stig Bakken <ssb@php.net> |
// | Tomas V.V.Cox <cox@idecnet.com> |
// +----------------------------------------------------------------------+
//
// $Id$
//
 
define('PEAR_ERROR_RETURN', 1);
define('PEAR_ERROR_PRINT', 2);
define('PEAR_ERROR_TRIGGER', 4);
define('PEAR_ERROR_DIE', 8);
define('PEAR_ERROR_CALLBACK', 16);
define('PEAR_ERROR_EXCEPTION', 32);
define('PEAR_ZE2', (function_exists('version_compare') &&
version_compare(zend_version(), "2-dev", "ge")));
 
if (substr(PHP_OS, 0, 3) == 'WIN') {
define('OS_WINDOWS', true);
define('OS_UNIX', false);
define('PEAR_OS', 'Windows');
} else {
define('OS_WINDOWS', false);
define('OS_UNIX', true);
define('PEAR_OS', 'Unix'); // blatant assumption
}
 
// instant backwards compatibility
if (!defined('PATH_SEPARATOR')) {
if (OS_WINDOWS) {
define('PATH_SEPARATOR', ';');
} else {
define('PATH_SEPARATOR', ':');
}
}
 
$GLOBALS['_PEAR_default_error_mode'] = PEAR_ERROR_RETURN;
$GLOBALS['_PEAR_default_error_options'] = E_USER_NOTICE;
$GLOBALS['_PEAR_destructor_object_list'] = array();
$GLOBALS['_PEAR_shutdown_funcs'] = array();
$GLOBALS['_PEAR_error_handler_stack'] = array();
 
ini_set('track_errors', true);
 
/**
* Base class for other PEAR classes. Provides rudimentary
* emulation of destructors.
*
* If you want a destructor in your class, inherit PEAR and make a
* destructor method called _yourclassname (same name as the
* constructor, but with a "_" prefix). Also, in your constructor you
* have to call the PEAR constructor: $this->PEAR();.
* The destructor method will be called without parameters. Note that
* at in some SAPI implementations (such as Apache), any output during
* the request shutdown (in which destructors are called) seems to be
* discarded. If you need to get any debug information from your
* destructor, use error_log(), syslog() or something similar.
*
* IMPORTANT! To use the emulated destructors you need to create the
* objects by reference: $obj =& new PEAR_child;
*
* @since PHP 4.0.2
* @author Stig Bakken <ssb@php.net>
* @see http://pear.php.net/manual/
*/
class PEAR
{
// {{{ properties
 
/**
* Whether to enable internal debug messages.
*
* @var bool
* @access private
*/
var $_debug = false;
 
/**
* Default error mode for this object.
*
* @var int
* @access private
*/
var $_default_error_mode = null;
 
/**
* Default error options used for this object when error mode
* is PEAR_ERROR_TRIGGER.
*
* @var int
* @access private
*/
var $_default_error_options = null;
 
/**
* Default error handler (callback) for this object, if error mode is
* PEAR_ERROR_CALLBACK.
*
* @var string
* @access private
*/
var $_default_error_handler = '';
 
/**
* Which class to use for error objects.
*
* @var string
* @access private
*/
var $_error_class = 'PEAR_Error';
 
/**
* An array of expected errors.
*
* @var array
* @access private
*/
var $_expected_errors = array();
 
// }}}
 
// {{{ constructor
 
/**
* Constructor. Registers this object in
* $_PEAR_destructor_object_list for destructor emulation if a
* destructor object exists.
*
* @param string $error_class (optional) which class to use for
* error objects, defaults to PEAR_Error.
* @access public
* @return void
*/
function PEAR($error_class = null)
{
$classname = get_class($this);
if ($this->_debug) {
print "PEAR constructor called, class=$classname\n";
}
if ($error_class !== null) {
$this->_error_class = $error_class;
}
while ($classname) {
$destructor = "_$classname";
if (method_exists($this, $destructor)) {
global $_PEAR_destructor_object_list;
$_PEAR_destructor_object_list[] = &$this;
break;
} else {
$classname = get_parent_class($classname);
}
}
}
 
// }}}
// {{{ destructor
 
/**
* Destructor (the emulated type of...). Does nothing right now,
* but is included for forward compatibility, so subclass
* destructors should always call it.
*
* See the note in the class desciption about output from
* destructors.
*
* @access public
* @return void
*/
function _PEAR() {
if ($this->_debug) {
printf("PEAR destructor called, class=%s\n", get_class($this));
}
}
 
// }}}
// {{{ getStaticProperty()
 
/**
* If you have a class that's mostly/entirely static, and you need static
* properties, you can use this method to simulate them. Eg. in your method(s)
* do this: $myVar = &PEAR::getStaticProperty('myVar');
* You MUST use a reference, or they will not persist!
*
* @access public
* @param string $class The calling classname, to prevent clashes
* @param string $var The variable to retrieve.
* @return mixed A reference to the variable. If not set it will be
* auto initialised to NULL.
*/
function &getStaticProperty($class, $var)
{
static $properties;
return $properties[$class][$var];
}
 
// }}}
// {{{ registerShutdownFunc()
 
/**
* Use this function to register a shutdown method for static
* classes.
*
* @access public
* @param mixed $func The function name (or array of class/method) to call
* @param mixed $args The arguments to pass to the function
* @return void
*/
function registerShutdownFunc($func, $args = array())
{
$GLOBALS['_PEAR_shutdown_funcs'][] = array($func, $args);
}
 
// }}}
// {{{ isError()
 
/**
* Tell whether a value is a PEAR error.
*
* @param mixed $data the value to test
* @param int $code if $data is an error object, return true
* only if $code is a string and
* $obj->getMessage() == $code or
* $code is an integer and $obj->getCode() == $code
* @access public
* @return bool true if parameter is an error
*/
 
function isError($data, $code = null)
{
if (is_a($data, 'PEAR_Error')) {
if (is_null($code)) {
return true;
} elseif (is_string($code)) {
return $data->getMessage() == $code;
} else {
return $data->getCode() == $code;
}
}
return false;
}
 
// }}}
// {{{ setErrorHandling()
 
/**
* Sets how errors generated by this object should be handled.
* Can be invoked both in objects and statically. If called
* statically, setErrorHandling sets the default behaviour for all
* PEAR objects. If called in an object, setErrorHandling sets
* the default behaviour for that object.
*
* @param int $mode
* One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
* PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
* PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION.
*
* @param mixed $options
* When $mode is PEAR_ERROR_TRIGGER, this is the error level (one
* of E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
*
* When $mode is PEAR_ERROR_CALLBACK, this parameter is expected
* to be the callback function or method. A callback
* function is a string with the name of the function, a
* callback method is an array of two elements: the element
* at index 0 is the object, and the element at index 1 is
* the name of the method to call in the object.
*
* When $mode is PEAR_ERROR_PRINT or PEAR_ERROR_DIE, this is
* a printf format string used when printing the error
* message.
*
* @access public
* @return void
* @see PEAR_ERROR_RETURN
* @see PEAR_ERROR_PRINT
* @see PEAR_ERROR_TRIGGER
* @see PEAR_ERROR_DIE
* @see PEAR_ERROR_CALLBACK
* @see PEAR_ERROR_EXCEPTION
*
* @since PHP 4.0.5
*/
 
function setErrorHandling($mode = null, $options = null)
{
if (isset($this) && is_a($this, 'PEAR')) {
$setmode = &$this->_default_error_mode;
$setoptions = &$this->_default_error_options;
} else {
$setmode = &$GLOBALS['_PEAR_default_error_mode'];
$setoptions = &$GLOBALS['_PEAR_default_error_options'];
}
 
switch ($mode) {
case PEAR_ERROR_RETURN:
case PEAR_ERROR_PRINT:
case PEAR_ERROR_TRIGGER:
case PEAR_ERROR_DIE:
case PEAR_ERROR_EXCEPTION:
case null:
$setmode = $mode;
$setoptions = $options;
break;
 
case PEAR_ERROR_CALLBACK:
$setmode = $mode;
// class/object method callback
if (is_callable($options)) {
$setoptions = $options;
} else {
trigger_error("invalid error callback", E_USER_WARNING);
}
break;
 
default:
trigger_error("invalid error mode", E_USER_WARNING);
break;
}
}
 
// }}}
// {{{ expectError()
 
/**
* This method is used to tell which errors you expect to get.
* Expected errors are always returned with error mode
* PEAR_ERROR_RETURN. Expected error codes are stored in a stack,
* and this method pushes a new element onto it. The list of
* expected errors are in effect until they are popped off the
* stack with the popExpect() method.
*
* Note that this method can not be called statically
*
* @param mixed $code a single error code or an array of error codes to expect
*
* @return int the new depth of the "expected errors" stack
* @access public
*/
function expectError($code = '*')
{
if (is_array($code)) {
array_push($this->_expected_errors, $code);
} else {
array_push($this->_expected_errors, array($code));
}
return sizeof($this->_expected_errors);
}
 
// }}}
// {{{ popExpect()
 
/**
* This method pops one element off the expected error codes
* stack.
*
* @return array the list of error codes that were popped
*/
function popExpect()
{
return array_pop($this->_expected_errors);
}
 
// }}}
// {{{ _checkDelExpect()
 
/**
* This method checks unsets an error code if available
*
* @param mixed error code
* @return bool true if the error code was unset, false otherwise
* @access private
* @since PHP 4.3.0
*/
function _checkDelExpect($error_code)
{
$deleted = false;
 
foreach ($this->_expected_errors AS $key => $error_array) {
if (in_array($error_code, $error_array)) {
unset($this->_expected_errors[$key][array_search($error_code, $error_array)]);
$deleted = true;
}
 
// clean up empty arrays
if (0 == count($this->_expected_errors[$key])) {
unset($this->_expected_errors[$key]);
}
}
return $deleted;
}
 
// }}}
// {{{ delExpect()
 
/**
* This method deletes all occurences of the specified element from
* the expected error codes stack.
*
* @param mixed $error_code error code that should be deleted
* @return mixed list of error codes that were deleted or error
* @access public
* @since PHP 4.3.0
*/
function delExpect($error_code)
{
$deleted = false;
 
if ((is_array($error_code) && (0 != count($error_code)))) {
// $error_code is a non-empty array here;
// we walk through it trying to unset all
// values
foreach($error_code as $key => $error) {
if ($this->_checkDelExpect($error)) {
$deleted = true;
} else {
$deleted = false;
}
}
return $deleted ? true : PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
} elseif (!empty($error_code)) {
// $error_code comes alone, trying to unset it
if ($this->_checkDelExpect($error_code)) {
return true;
} else {
return PEAR::raiseError("The expected error you submitted does not exist"); // IMPROVE ME
}
} else {
// $error_code is empty
return PEAR::raiseError("The expected error you submitted is empty"); // IMPROVE ME
}
}
 
// }}}
// {{{ raiseError()
 
/**
* This method is a wrapper that returns an instance of the
* configured error class with this object's default error
* handling applied. If the $mode and $options parameters are not
* specified, the object's defaults are used.
*
* @param mixed $message a text error message or a PEAR error object
*
* @param int $code a numeric error code (it is up to your class
* to define these if you want to use codes)
*
* @param int $mode One of PEAR_ERROR_RETURN, PEAR_ERROR_PRINT,
* PEAR_ERROR_TRIGGER, PEAR_ERROR_DIE,
* PEAR_ERROR_CALLBACK, PEAR_ERROR_EXCEPTION.
*
* @param mixed $options If $mode is PEAR_ERROR_TRIGGER, this parameter
* specifies the PHP-internal error level (one of
* E_USER_NOTICE, E_USER_WARNING or E_USER_ERROR).
* If $mode is PEAR_ERROR_CALLBACK, this
* parameter specifies the callback function or
* method. In other error modes this parameter
* is ignored.
*
* @param string $userinfo If you need to pass along for example debug
* information, this parameter is meant for that.
*
* @param string $error_class The returned error object will be
* instantiated from this class, if specified.
*
* @param bool $skipmsg If true, raiseError will only pass error codes,
* the error message parameter will be dropped.
*
* @access public
* @return object a PEAR error object
* @see PEAR::setErrorHandling
* @since PHP 4.0.5
*/
function raiseError($message = null,
$code = null,
$mode = null,
$options = null,
$userinfo = null,
$error_class = null,
$skipmsg = false)
{
// The error is yet a PEAR error object
if (is_object($message)) {
$code = $message->getCode();
$userinfo = $message->getUserInfo();
$error_class = $message->getType();
$message->error_message_prefix = '';
$message = $message->getMessage();
}
 
if (isset($this) && isset($this->_expected_errors) && sizeof($this->_expected_errors) > 0 && sizeof($exp = end($this->_expected_errors))) {
if ($exp[0] == "*" ||
(is_int(reset($exp)) && in_array($code, $exp)) ||
(is_string(reset($exp)) && in_array($message, $exp))) {
$mode = PEAR_ERROR_RETURN;
}
}
// No mode given, try global ones
if ($mode === null) {
// Class error handler
if (isset($this) && isset($this->_default_error_mode)) {
$mode = $this->_default_error_mode;
$options = $this->_default_error_options;
// Global error handler
} elseif (isset($GLOBALS['_PEAR_default_error_mode'])) {
$mode = $GLOBALS['_PEAR_default_error_mode'];
$options = $GLOBALS['_PEAR_default_error_options'];
}
}
 
if ($error_class !== null) {
$ec = $error_class;
} elseif (isset($this) && isset($this->_error_class)) {
$ec = $this->_error_class;
} else {
$ec = 'PEAR_Error';
}
if ($skipmsg) {
return new $ec($code, $mode, $options, $userinfo);
} else {
return new $ec($message, $code, $mode, $options, $userinfo);
}
}
 
// }}}
// {{{ throwError()
 
/**
* Simpler form of raiseError with fewer options. In most cases
* message, code and userinfo are enough.
*
* @param string $message
*
*/
function throwError($message = null,
$code = null,
$userinfo = null)
{
if (isset($this) && is_subclass_of($this, 'PEAR_Error')) {
return $this->raiseError($message, $code, null, null, $userinfo);
} else {
return PEAR::raiseError($message, $code, null, null, $userinfo);
}
}
 
// }}}
// {{{ pushErrorHandling()
 
/**
* Push a new error handler on top of the error handler options stack. With this
* you can easily override the actual error handler for some code and restore
* it later with popErrorHandling.
*
* @param mixed $mode (same as setErrorHandling)
* @param mixed $options (same as setErrorHandling)
*
* @return bool Always true
*
* @see PEAR::setErrorHandling
*/
function pushErrorHandling($mode, $options = null)
{
$stack = &$GLOBALS['_PEAR_error_handler_stack'];
if (isset($this) && is_a($this, 'PEAR')) {
$def_mode = &$this->_default_error_mode;
$def_options = &$this->_default_error_options;
} else {
$def_mode = &$GLOBALS['_PEAR_default_error_mode'];
$def_options = &$GLOBALS['_PEAR_default_error_options'];
}
$stack[] = array($def_mode, $def_options);
 
if (isset($this) && is_a($this, 'PEAR')) {
$this->setErrorHandling($mode, $options);
} else {
PEAR::setErrorHandling($mode, $options);
}
$stack[] = array($mode, $options);
return true;
}
 
// }}}
// {{{ popErrorHandling()
 
/**
* Pop the last error handler used
*
* @return bool Always true
*
* @see PEAR::pushErrorHandling
*/
function popErrorHandling()
{
$stack = &$GLOBALS['_PEAR_error_handler_stack'];
array_pop($stack);
list($mode, $options) = $stack[sizeof($stack) - 1];
array_pop($stack);
if (isset($this) && is_a($this, 'PEAR')) {
$this->setErrorHandling($mode, $options);
} else {
PEAR::setErrorHandling($mode, $options);
}
return true;
}
 
// }}}
// {{{ loadExtension()
 
/**
* OS independant PHP extension load. Remember to take care
* on the correct extension name for case sensitive OSes.
*
* @param string $ext The extension name
* @return bool Success or not on the dl() call
*/
function loadExtension($ext)
{
if (!extension_loaded($ext)) {
// if either returns true dl() will produce a FATAL error, stop that
if ((ini_get('enable_dl') != 1) || (ini_get('safe_mode') == 1)) {
return false;
}
if (OS_WINDOWS) {
$suffix = '.dll';
} elseif (PHP_OS == 'HP-UX') {
$suffix = '.sl';
} elseif (PHP_OS == 'AIX') {
$suffix = '.a';
} elseif (PHP_OS == 'OSX') {
$suffix = '.bundle';
} else {
$suffix = '.so';
}
return @dl('php_'.$ext.$suffix) || @dl($ext.$suffix);
}
return true;
}
 
// }}}
}
 
// {{{ _PEAR_call_destructors()
 
function _PEAR_call_destructors()
{
global $_PEAR_destructor_object_list;
if (is_array($_PEAR_destructor_object_list) &&
sizeof($_PEAR_destructor_object_list))
{
reset($_PEAR_destructor_object_list);
while (list($k, $objref) = each($_PEAR_destructor_object_list)) {
$classname = get_class($objref);
while ($classname) {
$destructor = "_$classname";
if (method_exists($objref, $destructor)) {
$objref->$destructor();
break;
} else {
$classname = get_parent_class($classname);
}
}
}
// Empty the object list to ensure that destructors are
// not called more than once.
$_PEAR_destructor_object_list = array();
}
 
// Now call the shutdown functions
if (is_array($GLOBALS['_PEAR_shutdown_funcs']) AND !empty($GLOBALS['_PEAR_shutdown_funcs'])) {
foreach ($GLOBALS['_PEAR_shutdown_funcs'] as $value) {
call_user_func_array($value[0], $value[1]);
}
}
}
 
// }}}
 
class PEAR_Error
{
// {{{ properties
 
var $error_message_prefix = '';
var $mode = PEAR_ERROR_RETURN;
var $level = E_USER_NOTICE;
var $code = -1;
var $message = '';
var $userinfo = '';
var $backtrace = null;
 
// }}}
// {{{ constructor
 
/**
* PEAR_Error constructor
*
* @param string $message message
*
* @param int $code (optional) error code
*
* @param int $mode (optional) error mode, one of: PEAR_ERROR_RETURN,
* PEAR_ERROR_PRINT, PEAR_ERROR_DIE, PEAR_ERROR_TRIGGER,
* PEAR_ERROR_CALLBACK or PEAR_ERROR_EXCEPTION
*
* @param mixed $options (optional) error level, _OR_ in the case of
* PEAR_ERROR_CALLBACK, the callback function or object/method
* tuple.
*
* @param string $userinfo (optional) additional user/debug info
*
* @access public
*
*/
function PEAR_Error($message = 'unknown error', $code = null,
$mode = null, $options = null, $userinfo = null)
{
if ($mode === null) {
$mode = PEAR_ERROR_RETURN;
}
$this->message = $message;
$this->code = $code;
$this->mode = $mode;
$this->userinfo = $userinfo;
if (function_exists("debug_backtrace")) {
$this->backtrace = debug_backtrace();
}
if ($mode & PEAR_ERROR_CALLBACK) {
$this->level = E_USER_NOTICE;
$this->callback = $options;
} else {
if ($options === null) {
$options = E_USER_NOTICE;
}
$this->level = $options;
$this->callback = null;
}
if ($this->mode & PEAR_ERROR_PRINT) {
if (is_null($options) || is_int($options)) {
$format = "%s";
} else {
$format = $options;
}
printf($format, $this->getMessage());
}
if ($this->mode & PEAR_ERROR_TRIGGER) {
trigger_error($this->getMessage(), $this->level);
}
if ($this->mode & PEAR_ERROR_DIE) {
$msg = $this->getMessage();
if (is_null($options) || is_int($options)) {
$format = "%s";
if (substr($msg, -1) != "\n") {
$msg .= "\n";
}
} else {
$format = $options;
}
die(sprintf($format, $msg));
}
if ($this->mode & PEAR_ERROR_CALLBACK) {
if (is_callable($this->callback)) {
call_user_func($this->callback, $this);
}
}
if (PEAR_ZE2 && $this->mode & PEAR_ERROR_EXCEPTION) {
eval('throw $this;');
}
}
 
// }}}
// {{{ getMode()
 
/**
* Get the error mode from an error object.
*
* @return int error mode
* @access public
*/
function getMode() {
return $this->mode;
}
 
// }}}
// {{{ getCallback()
 
/**
* Get the callback function/method from an error object.
*
* @return mixed callback function or object/method array
* @access public
*/
function getCallback() {
return $this->callback;
}
 
// }}}
// {{{ getMessage()
 
 
/**
* Get the error message from an error object.
*
* @return string full error message
* @access public
*/
function getMessage()
{
return ($this->error_message_prefix . $this->message);
}
 
 
// }}}
// {{{ getCode()
 
/**
* Get error code from an error object
*
* @return int error code
* @access public
*/
function getCode()
{
return $this->code;
}
 
// }}}
// {{{ getType()
 
/**
* Get the name of this error/exception.
*
* @return string error/exception name (type)
* @access public
*/
function getType()
{
return get_class($this);
}
 
// }}}
// {{{ getUserInfo()
 
/**
* Get additional user-supplied information.
*
* @return string user-supplied information
* @access public
*/
function getUserInfo()
{
return $this->userinfo;
}
 
// }}}
// {{{ getDebugInfo()
 
/**
* Get additional debug information supplied by the application.
*
* @return string debug information
* @access public
*/
function getDebugInfo()
{
return $this->getUserInfo();
}
 
// }}}
// {{{ getBacktrace()
 
/**
* Get the call backtrace from where the error was generated.
* Supported with PHP 4.3.0 or newer.
*
* @param int $frame (optional) what frame to fetch
* @return array Backtrace, or NULL if not available.
* @access public
*/
function getBacktrace($frame = null)
{
if ($frame === null) {
return $this->backtrace;
}
return $this->backtrace[$frame];
}
 
// }}}
// {{{ addUserInfo()
 
function addUserInfo($info)
{
if (empty($this->userinfo)) {
$this->userinfo = $info;
} else {
$this->userinfo .= " ** $info";
}
}
 
// }}}
// {{{ toString()
 
/**
* Make a string representation of this object.
*
* @return string a string with an object summary
* @access public
*/
function toString() {
$modes = array();
$levels = array(E_USER_NOTICE => 'notice',
E_USER_WARNING => 'warning',
E_USER_ERROR => 'error');
if ($this->mode & PEAR_ERROR_CALLBACK) {
if (is_array($this->callback)) {
$callback = get_class($this->callback[0]) . '::' .
$this->callback[1];
} else {
$callback = $this->callback;
}
return sprintf('[%s: message="%s" code=%d mode=callback '.
'callback=%s prefix="%s" info="%s"]',
get_class($this), $this->message, $this->code,
$callback, $this->error_message_prefix,
$this->userinfo);
}
if ($this->mode & PEAR_ERROR_PRINT) {
$modes[] = 'print';
}
if ($this->mode & PEAR_ERROR_TRIGGER) {
$modes[] = 'trigger';
}
if ($this->mode & PEAR_ERROR_DIE) {
$modes[] = 'die';
}
if ($this->mode & PEAR_ERROR_RETURN) {
$modes[] = 'return';
}
return sprintf('[%s: message="%s" code=%d mode=%s level=%s '.
'prefix="%s" info="%s"]',
get_class($this), $this->message, $this->code,
implode("|", $modes), $levels[$this->level],
$this->error_message_prefix,
$this->userinfo);
}
 
// }}}
}
 
register_shutdown_function("_PEAR_call_destructors");
 
/*
* Local Variables:
* mode: php
* tab-width: 4
* c-basic-offset: 4
* End:
*/
?>
/trunk/applications/jrest/lib/File/PDF/fonts/timesbi.php
New file
0,0 → 1,272
<?php
/**
* @package File_PDF
*/
$font_widths['timesBI'] = array(
chr(0) => 250,
chr(1) => 250,
chr(2) => 250,
chr(3) => 250,
chr(4) => 250,
chr(5) => 250,
chr(6) => 250,
chr(7) => 250,
chr(8) => 250,
chr(9) => 250,
chr(10) => 250,
chr(11) => 250,
chr(12) => 250,
chr(13) => 250,
chr(14) => 250,
chr(15) => 250,
chr(16) => 250,
chr(17) => 250,
chr(18) => 250,
chr(19) => 250,
chr(20) => 250,
chr(21) => 250,
chr(22) => 250,
chr(23) => 250,
chr(24) => 250,
chr(25) => 250,
chr(26) => 250,
chr(27) => 250,
chr(28) => 250,
chr(29) => 250,
chr(30) => 250,
chr(31) => 250,
' ' => 250,
'!' => 389,
'"' => 555,
'#' => 500,
'$' => 500,
'%' => 833,
'&' => 778,
'\'' => 278,
'(' => 333,
')' => 333,
'*' => 500,
'+' => 570,
',' => 250,
'-' => 333,
'.' => 250,
'/' => 278,
'0' => 500,
'1' => 500,
'2' => 500,
'3' => 500,
'4' => 500,
'5' => 500,
'6' => 500,
'7' => 500,
'8' => 500,
'9' => 500,
':' => 333,
';' => 333,
'<' => 570,
'=' => 570,
'>' => 570,
'?' => 500,
'@' => 832,
'A' => 667,
'B' => 667,
'C' => 667,
'D' => 722,
'E' => 667,
'F' => 667,
'G' => 722,
'H' => 778,
'I' => 389,
'J' => 500,
'K' => 667,
'L' => 611,
'M' => 889,
'N' => 722,
'O' => 722,
'P' => 611,
'Q' => 722,
'R' => 667,
'S' => 556,
'T' => 611,
'U' => 722,
'V' => 667,
'W' => 889,
'X' => 667,
'Y' => 611,
'Z' => 611,
'[' => 333,
'\\' => 278,
']' => 333,
'^' => 570,
'_' => 500,
'`' => 333,
'a' => 500,
'b' => 500,
'c' => 444,
'd' => 500,
'e' => 444,
'f' => 333,
'g' => 500,
'h' => 556,
'i' => 278,
'j' => 278,
'k' => 500,
'l' => 278,
'm' => 778,
'n' => 556,
'o' => 500,
'p' => 500,
'q' => 500,
'r' => 389,
's' => 389,
't' => 278,
'u' => 556,
'v' => 444,
'w' => 667,
'x' => 500,
'y' => 444,
'z' => 389,
'{' => 348,
'|' => 220,
'}' => 348,
'~' => 570,
chr(127) => 350,
chr(128) => 500,
chr(129) => 350,
chr(130) => 333,
chr(131) => 500,
chr(132) => 500,
chr(133) => 1000,
chr(134) => 500,
chr(135) => 500,
chr(136) => 333,
chr(137) => 1000,
chr(138) => 556,
chr(139) => 333,
chr(140) => 944,
chr(141) => 350,
chr(142) => 611,
chr(143) => 350,
chr(144) => 350,
chr(145) => 333,
chr(146) => 333,
chr(147) => 500,
chr(148) => 500,
chr(149) => 350,
chr(150) => 500,
chr(151) => 1000,
chr(152) => 333,
chr(153) => 1000,
chr(154) => 389,
chr(155) => 333,
chr(156) => 722,
chr(157) => 350,
chr(158) => 389,
chr(159) => 611,
chr(160) => 250,
chr(161) => 389,
chr(162) => 500,
chr(163) => 500,
chr(164) => 500,
chr(165) => 500,
chr(166) => 220,
chr(167) => 500,
chr(168) => 333,
chr(169) => 747,
chr(170) => 266,
chr(171) => 500,
chr(172) => 606,
chr(173) => 333,
chr(174) => 747,
chr(175) => 333,
chr(176) => 400,
chr(177) => 570,
chr(178) => 300,
chr(179) => 300,
chr(180) => 333,
chr(181) => 576,
chr(182) => 500,
chr(183) => 250,
chr(184) => 333,
chr(185) => 300,
chr(186) => 300,
chr(187) => 500,
chr(188) => 750,
chr(189) => 750,
chr(190) => 750,
chr(191) => 500,
chr(192) => 667,
chr(193) => 667,
chr(194) => 667,
chr(195) => 667,
chr(196) => 667,
chr(197) => 667,
chr(198) => 944,
chr(199) => 667,
chr(200) => 667,
chr(201) => 667,
chr(202) => 667,
chr(203) => 667,
chr(204) => 389,
chr(205) => 389,
chr(206) => 389,
chr(207) => 389,
chr(208) => 722,
chr(209) => 722,
chr(210) => 722,
chr(211) => 722,
chr(212) => 722,
chr(213) => 722,
chr(214) => 722,
chr(215) => 570,
chr(216) => 722,
chr(217) => 722,
chr(218) => 722,
chr(219) => 722,
chr(220) => 722,
chr(221) => 611,
chr(222) => 611,
chr(223) => 500,
chr(224) => 500,
chr(225) => 500,
chr(226) => 500,
chr(227) => 500,
chr(228) => 500,
chr(229) => 500,
chr(230) => 722,
chr(231) => 444,
chr(232) => 444,
chr(233) => 444,
chr(234) => 444,
chr(235) => 444,
chr(236) => 278,
chr(237) => 278,
chr(238) => 278,
chr(239) => 278,
chr(240) => 500,
chr(241) => 556,
chr(242) => 500,
chr(243) => 500,
chr(244) => 500,
chr(245) => 500,
chr(246) => 500,
chr(247) => 570,
chr(248) => 500,
chr(249) => 556,
chr(250) => 556,
chr(251) => 556,
chr(252) => 556,
chr(253) => 444,
chr(254) => 500,
chr(255) => 444);
/trunk/applications/jrest/lib/File/PDF/fonts/zapfdingbats.php
New file
0,0 → 1,272
<?php
/**
* @package File_PDF
*/
$font_widths['zapfdingbats'] = array(
chr(0) => 0,
chr(1) => 0,
chr(2) => 0,
chr(3) => 0,
chr(4) => 0,
chr(5) => 0,
chr(6) => 0,
chr(7) => 0,
chr(8) => 0,
chr(9) => 0,
chr(10) => 0,
chr(11) => 0,
chr(12) => 0,
chr(13) => 0,
chr(14) => 0,
chr(15) => 0,
chr(16) => 0,
chr(17) => 0,
chr(18) => 0,
chr(19) => 0,
chr(20) => 0,
chr(21) => 0,
chr(22) => 0,
chr(23) => 0,
chr(24) => 0,
chr(25) => 0,
chr(26) => 0,
chr(27) => 0,
chr(28) => 0,
chr(29) => 0,
chr(30) => 0,
chr(31) => 0,
' ' => 278,
'!' => 974,
'"' => 961,
'#' => 974,
'$' => 980,
'%' => 719,
'&' => 789,
'\'' => 790,
'(' => 791,
')' => 690,
'*' => 960,
'+' => 939,
',' => 549,
'-' => 855,
'.' => 911,
'/' => 933,
'0' => 911,
'1' => 945,
'2' => 974,
'3' => 755,
'4' => 846,
'5' => 762,
'6' => 761,
'7' => 571,
'8' => 677,
'9' => 763,
':' => 760,
';' => 759,
'<' => 754,
'=' => 494,
'>' => 552,
'?' => 537,
'@' => 577,
'A' => 692,
'B' => 786,
'C' => 788,
'D' => 788,
'E' => 790,
'F' => 793,
'G' => 794,
'H' => 816,
'I' => 823,
'J' => 789,
'K' => 841,
'L' => 823,
'M' => 833,
'N' => 816,
'O' => 831,
'P' => 923,
'Q' => 744,
'R' => 723,
'S' => 749,
'T' => 790,
'U' => 792,
'V' => 695,
'W' => 776,
'X' => 768,
'Y' => 792,
'Z' => 759,
'[' => 707,
'\\' => 708,
']' => 682,
'^' => 701,
'_' => 826,
'`' => 815,
'a' => 789,
'b' => 789,
'c' => 707,
'd' => 687,
'e' => 696,
'f' => 689,
'g' => 786,
'h' => 787,
'i' => 713,
'j' => 791,
'k' => 785,
'l' => 791,
'm' => 873,
'n' => 761,
'o' => 762,
'p' => 762,
'q' => 759,
'r' => 759,
's' => 892,
't' => 892,
'u' => 788,
'v' => 784,
'w' => 438,
'x' => 138,
'y' => 277,
'z' => 415,
'{' => 392,
'|' => 392,
'}' => 668,
'~' => 668,
chr(127) => 0,
chr(128) => 390,
chr(129) => 390,
chr(130) => 317,
chr(131) => 317,
chr(132) => 276,
chr(133) => 276,
chr(134) => 509,
chr(135) => 509,
chr(136) => 410,
chr(137) => 410,
chr(138) => 234,
chr(139) => 234,
chr(140) => 334,
chr(141) => 334,
chr(142) => 0,
chr(143) => 0,
chr(144) => 0,
chr(145) => 0,
chr(146) => 0,
chr(147) => 0,
chr(148) => 0,
chr(149) => 0,
chr(150) => 0,
chr(151) => 0,
chr(152) => 0,
chr(153) => 0,
chr(154) => 0,
chr(155) => 0,
chr(156) => 0,
chr(157) => 0,
chr(158) => 0,
chr(159) => 0,
chr(160) => 0,
chr(161) => 732,
chr(162) => 544,
chr(163) => 544,
chr(164) => 910,
chr(165) => 667,
chr(166) => 760,
chr(167) => 760,
chr(168) => 776,
chr(169) => 595,
chr(170) => 694,
chr(171) => 626,
chr(172) => 788,
chr(173) => 788,
chr(174) => 788,
chr(175) => 788,
chr(176) => 788,
chr(177) => 788,
chr(178) => 788,
chr(179) => 788,
chr(180) => 788,
chr(181) => 788,
chr(182) => 788,
chr(183) => 788,
chr(184) => 788,
chr(185) => 788,
chr(186) => 788,
chr(187) => 788,
chr(188) => 788,
chr(189) => 788,
chr(190) => 788,
chr(191) => 788,
chr(192) => 788,
chr(193) => 788,
chr(194) => 788,
chr(195) => 788,
chr(196) => 788,
chr(197) => 788,
chr(198) => 788,
chr(199) => 788,
chr(200) => 788,
chr(201) => 788,
chr(202) => 788,
chr(203) => 788,
chr(204) => 788,
chr(205) => 788,
chr(206) => 788,
chr(207) => 788,
chr(208) => 788,
chr(209) => 788,
chr(210) => 788,
chr(211) => 788,
chr(212) => 894,
chr(213) => 838,
chr(214) => 1016,
chr(215) => 458,
chr(216) => 748,
chr(217) => 924,
chr(218) => 748,
chr(219) => 918,
chr(220) => 927,
chr(221) => 928,
chr(222) => 928,
chr(223) => 834,
chr(224) => 873,
chr(225) => 828,
chr(226) => 924,
chr(227) => 924,
chr(228) => 917,
chr(229) => 930,
chr(230) => 931,
chr(231) => 463,
chr(232) => 883,
chr(233) => 836,
chr(234) => 836,
chr(235) => 867,
chr(236) => 867,
chr(237) => 696,
chr(238) => 696,
chr(239) => 874,
chr(240) => 0,
chr(241) => 874,
chr(242) => 760,
chr(243) => 946,
chr(244) => 771,
chr(245) => 865,
chr(246) => 771,
chr(247) => 888,
chr(248) => 967,
chr(249) => 888,
chr(250) => 831,
chr(251) => 873,
chr(252) => 927,
chr(253) => 970,
chr(254) => 918,
chr(255) => 0);
/trunk/applications/jrest/lib/File/PDF/fonts/helveticai.php
New file
0,0 → 1,272
<?php
/**
* @package File_PDF
*/
$font_widths['helveticaI'] = array(
chr(0) => 278,
chr(1) => 278,
chr(2) => 278,
chr(3) => 278,
chr(4) => 278,
chr(5) => 278,
chr(6) => 278,
chr(7) => 278,
chr(8) => 278,
chr(9) => 278,
chr(10) => 278,
chr(11) => 278,
chr(12) => 278,
chr(13) => 278,
chr(14) => 278,
chr(15) => 278,
chr(16) => 278,
chr(17) => 278,
chr(18) => 278,
chr(19) => 278,
chr(20) => 278,
chr(21) => 278,
chr(22) => 278,
chr(23) => 278,
chr(24) => 278,
chr(25) => 278,
chr(26) => 278,
chr(27) => 278,
chr(28) => 278,
chr(29) => 278,
chr(30) => 278,
chr(31) => 278,
' ' => 278,
'!' => 278,
'"' => 355,
'#' => 556,
'$' => 556,
'%' => 889,
'&' => 667,
'\'' => 191,
'(' => 333,
')' => 333,
'*' => 389,
'+' => 584,
',' => 278,
'-' => 333,
'.' => 278,
'/' => 278,
'0' => 556,
'1' => 556,
'2' => 556,
'3' => 556,
'4' => 556,
'5' => 556,
'6' => 556,
'7' => 556,
'8' => 556,
'9' => 556,
':' => 278,
';' => 278,
'<' => 584,
'=' => 584,
'>' => 584,
'?' => 556,
'@' => 1015,
'A' => 667,
'B' => 667,
'C' => 722,
'D' => 722,
'E' => 667,
'F' => 611,
'G' => 778,
'H' => 722,
'I' => 278,
'J' => 500,
'K' => 667,
'L' => 556,
'M' => 833,
'N' => 722,
'O' => 778,
'P' => 667,
'Q' => 778,
'R' => 722,
'S' => 667,
'T' => 611,
'U' => 722,
'V' => 667,
'W' => 944,
'X' => 667,
'Y' => 667,
'Z' => 611,
'[' => 278,
'\\' => 278,
']' => 278,
'^' => 469,
'_' => 556,
'`' => 333,
'a' => 556,
'b' => 556,
'c' => 500,
'd' => 556,
'e' => 556,
'f' => 278,
'g' => 556,
'h' => 556,
'i' => 222,
'j' => 222,
'k' => 500,
'l' => 222,
'm' => 833,
'n' => 556,
'o' => 556,
'p' => 556,
'q' => 556,
'r' => 333,
's' => 500,
't' => 278,
'u' => 556,
'v' => 500,
'w' => 722,
'x' => 500,
'y' => 500,
'z' => 500,
'{' => 334,
'|' => 260,
'}' => 334,
'~' => 584,
chr(127) => 350,
chr(128) => 556,
chr(129) => 350,
chr(130) => 222,
chr(131) => 556,
chr(132) => 333,
chr(133) => 1000,
chr(134) => 556,
chr(135) => 556,
chr(136) => 333,
chr(137) => 1000,
chr(138) => 667,
chr(139) => 333,
chr(140) => 1000,
chr(141) => 350,
chr(142) => 611,
chr(143) => 350,
chr(144) => 350,
chr(145) => 222,
chr(146) => 222,
chr(147) => 333,
chr(148) => 333,
chr(149) => 350,
chr(150) => 556,
chr(151) => 1000,
chr(152) => 333,
chr(153) => 1000,
chr(154) => 500,
chr(155) => 333,
chr(156) => 944,
chr(157) => 350,
chr(158) => 500,
chr(159) => 667,
chr(160) => 278,
chr(161) => 333,
chr(162) => 556,
chr(163) => 556,
chr(164) => 556,
chr(165) => 556,
chr(166) => 260,
chr(167) => 556,
chr(168) => 333,
chr(169) => 737,
chr(170) => 370,
chr(171) => 556,
chr(172) => 584,
chr(173) => 333,
chr(174) => 737,
chr(175) => 333,
chr(176) => 400,
chr(177) => 584,
chr(178) => 333,
chr(179) => 333,
chr(180) => 333,
chr(181) => 556,
chr(182) => 537,
chr(183) => 278,
chr(184) => 333,
chr(185) => 333,
chr(186) => 365,
chr(187) => 556,
chr(188) => 834,
chr(189) => 834,
chr(190) => 834,
chr(191) => 611,
chr(192) => 667,
chr(193) => 667,
chr(194) => 667,
chr(195) => 667,
chr(196) => 667,
chr(197) => 667,
chr(198) => 1000,
chr(199) => 722,
chr(200) => 667,
chr(201) => 667,
chr(202) => 667,
chr(203) => 667,
chr(204) => 278,
chr(205) => 278,
chr(206) => 278,
chr(207) => 278,
chr(208) => 722,
chr(209) => 722,
chr(210) => 778,
chr(211) => 778,
chr(212) => 778,
chr(213) => 778,
chr(214) => 778,
chr(215) => 584,
chr(216) => 778,
chr(217) => 722,
chr(218) => 722,
chr(219) => 722,
chr(220) => 722,
chr(221) => 667,
chr(222) => 667,
chr(223) => 611,
chr(224) => 556,
chr(225) => 556,
chr(226) => 556,
chr(227) => 556,
chr(228) => 556,
chr(229) => 556,
chr(230) => 889,
chr(231) => 500,
chr(232) => 556,
chr(233) => 556,
chr(234) => 556,
chr(235) => 556,
chr(236) => 278,
chr(237) => 278,
chr(238) => 278,
chr(239) => 278,
chr(240) => 556,
chr(241) => 556,
chr(242) => 556,
chr(243) => 556,
chr(244) => 556,
chr(245) => 556,
chr(246) => 556,
chr(247) => 584,
chr(248) => 611,
chr(249) => 556,
chr(250) => 556,
chr(251) => 556,
chr(252) => 556,
chr(253) => 500,
chr(254) => 556,
chr(255) => 500);
/trunk/applications/jrest/lib/File/PDF/fonts/helveticabi.php
New file
0,0 → 1,272
<?php
/**
* @package File_PDF
*/
$font_widths['helveticaBI'] = array(
chr(0) => 278,
chr(1) => 278,
chr(2) => 278,
chr(3) => 278,
chr(4) => 278,
chr(5) => 278,
chr(6) => 278,
chr(7) => 278,
chr(8) => 278,
chr(9) => 278,
chr(10) => 278,
chr(11) => 278,
chr(12) => 278,
chr(13) => 278,
chr(14) => 278,
chr(15) => 278,
chr(16) => 278,
chr(17) => 278,
chr(18) => 278,
chr(19) => 278,
chr(20) => 278,
chr(21) => 278,
chr(22) => 278,
chr(23) => 278,
chr(24) => 278,
chr(25) => 278,
chr(26) => 278,
chr(27) => 278,
chr(28) => 278,
chr(29) => 278,
chr(30) => 278,
chr(31) => 278,
' ' => 278,
'!' => 333,
'"' => 474,
'#' => 556,
'$' => 556,
'%' => 889,
'&' => 722,
'\'' => 238,
'(' => 333,
')' => 333,
'*' => 389,
'+' => 584,
',' => 278,
'-' => 333,
'.' => 278,
'/' => 278,
'0' => 556,
'1' => 556,
'2' => 556,
'3' => 556,
'4' => 556,
'5' => 556,
'6' => 556,
'7' => 556,
'8' => 556,
'9' => 556,
':' => 333,
';' => 333,
'<' => 584,
'=' => 584,
'>' => 584,
'?' => 611,
'@' => 975,
'A' => 722,
'B' => 722,
'C' => 722,
'D' => 722,
'E' => 667,
'F' => 611,
'G' => 778,
'H' => 722,
'I' => 278,
'J' => 556,
'K' => 722,
'L' => 611,
'M' => 833,
'N' => 722,
'O' => 778,
'P' => 667,
'Q' => 778,
'R' => 722,
'S' => 667,
'T' => 611,
'U' => 722,
'V' => 667,
'W' => 944,
'X' => 667,
'Y' => 667,
'Z' => 611,
'[' => 333,
'\\' => 278,
']' => 333,
'^' => 584,
'_' => 556,
'`' => 333,
'a' => 556,
'b' => 611,
'c' => 556,
'd' => 611,
'e' => 556,
'f' => 333,
'g' => 611,
'h' => 611,
'i' => 278,
'j' => 278,
'k' => 556,
'l' => 278,
'm' => 889,
'n' => 611,
'o' => 611,
'p' => 611,
'q' => 611,
'r' => 389,
's' => 556,
't' => 333,
'u' => 611,
'v' => 556,
'w' => 778,
'x' => 556,
'y' => 556,
'z' => 500,
'{' => 389,
'|' => 280,
'}' => 389,
'~' => 584,
chr(127) => 350,
chr(128) => 556,
chr(129) => 350,
chr(130) => 278,
chr(131) => 556,
chr(132) => 500,
chr(133) => 1000,
chr(134) => 556,
chr(135) => 556,
chr(136) => 333,
chr(137) => 1000,
chr(138) => 667,
chr(139) => 333,
chr(140) => 1000,
chr(141) => 350,
chr(142) => 611,
chr(143) => 350,
chr(144) => 350,
chr(145) => 278,
chr(146) => 278,
chr(147) => 500,
chr(148) => 500,
chr(149) => 350,
chr(150) => 556,
chr(151) => 1000,
chr(152) => 333,
chr(153) => 1000,
chr(154) => 556,
chr(155) => 333,
chr(156) => 944,
chr(157) => 350,
chr(158) => 500,
chr(159) => 667,
chr(160) => 278,
chr(161) => 333,
chr(162) => 556,
chr(163) => 556,
chr(164) => 556,
chr(165) => 556,
chr(166) => 280,
chr(167) => 556,
chr(168) => 333,
chr(169) => 737,
chr(170) => 370,
chr(171) => 556,
chr(172) => 584,
chr(173) => 333,
chr(174) => 737,
chr(175) => 333,
chr(176) => 400,
chr(177) => 584,
chr(178) => 333,
chr(179) => 333,
chr(180) => 333,
chr(181) => 611,
chr(182) => 556,
chr(183) => 278,
chr(184) => 333,
chr(185) => 333,
chr(186) => 365,
chr(187) => 556,
chr(188) => 834,
chr(189) => 834,
chr(190) => 834,
chr(191) => 611,
chr(192) => 722,
chr(193) => 722,
chr(194) => 722,
chr(195) => 722,
chr(196) => 722,
chr(197) => 722,
chr(198) => 1000,
chr(199) => 722,
chr(200) => 667,
chr(201) => 667,
chr(202) => 667,
chr(203) => 667,
chr(204) => 278,
chr(205) => 278,
chr(206) => 278,
chr(207) => 278,
chr(208) => 722,
chr(209) => 722,
chr(210) => 778,
chr(211) => 778,
chr(212) => 778,
chr(213) => 778,
chr(214) => 778,
chr(215) => 584,
chr(216) => 778,
chr(217) => 722,
chr(218) => 722,
chr(219) => 722,
chr(220) => 722,
chr(221) => 667,
chr(222) => 667,
chr(223) => 611,
chr(224) => 556,
chr(225) => 556,
chr(226) => 556,
chr(227) => 556,
chr(228) => 556,
chr(229) => 556,
chr(230) => 889,
chr(231) => 556,
chr(232) => 556,
chr(233) => 556,
chr(234) => 556,
chr(235) => 556,
chr(236) => 278,
chr(237) => 278,
chr(238) => 278,
chr(239) => 278,
chr(240) => 611,
chr(241) => 611,
chr(242) => 611,
chr(243) => 611,
chr(244) => 611,
chr(245) => 611,
chr(246) => 611,
chr(247) => 584,
chr(248) => 611,
chr(249) => 611,
chr(250) => 611,
chr(251) => 611,
chr(252) => 611,
chr(253) => 556,
chr(254) => 611,
chr(255) => 556);
/trunk/applications/jrest/lib/File/PDF/fonts/times.php
New file
0,0 → 1,272
<?php
/**
* @package File_PDF
*/
$font_widths['times'] = array(
chr(0) => 250,
chr(1) => 250,
chr(2) => 250,
chr(3) => 250,
chr(4) => 250,
chr(5) => 250,
chr(6) => 250,
chr(7) => 250,
chr(8) => 250,
chr(9) => 250,
chr(10) => 250,
chr(11) => 250,
chr(12) => 250,
chr(13) => 250,
chr(14) => 250,
chr(15) => 250,
chr(16) => 250,
chr(17) => 250,
chr(18) => 250,
chr(19) => 250,
chr(20) => 250,
chr(21) => 250,
chr(22) => 250,
chr(23) => 250,
chr(24) => 250,
chr(25) => 250,
chr(26) => 250,
chr(27) => 250,
chr(28) => 250,
chr(29) => 250,
chr(30) => 250,
chr(31) => 250,
' ' => 250,
'!' => 333,
'"' => 408,
'#' => 500,
'$' => 500,
'%' => 833,
'&' => 778,
'\'' => 180,
'(' => 333,
')' => 333,
'*' => 500,
'+' => 564,
',' => 250,
'-' => 333,
'.' => 250,
'/' => 278,
'0' => 500,
'1' => 500,
'2' => 500,
'3' => 500,
'4' => 500,
'5' => 500,
'6' => 500,
'7' => 500,
'8' => 500,
'9' => 500,
':' => 278,
';' => 278,
'<' => 564,
'=' => 564,
'>' => 564,
'?' => 444,
'@' => 921,
'A' => 722,
'B' => 667,
'C' => 667,
'D' => 722,
'E' => 611,
'F' => 556,
'G' => 722,
'H' => 722,
'I' => 333,
'J' => 389,
'K' => 722,
'L' => 611,
'M' => 889,
'N' => 722,
'O' => 722,
'P' => 556,
'Q' => 722,
'R' => 667,
'S' => 556,
'T' => 611,
'U' => 722,
'V' => 722,
'W' => 944,
'X' => 722,
'Y' => 722,
'Z' => 611,
'[' => 333,
'\\' => 278,
']' => 333,
'^' => 469,
'_' => 500,
'`' => 333,
'a' => 444,
'b' => 500,
'c' => 444,
'd' => 500,
'e' => 444,
'f' => 333,
'g' => 500,
'h' => 500,
'i' => 278,
'j' => 278,
'k' => 500,
'l' => 278,
'm' => 778,
'n' => 500,
'o' => 500,
'p' => 500,
'q' => 500,
'r' => 333,
's' => 389,
't' => 278,
'u' => 500,
'v' => 500,
'w' => 722,
'x' => 500,
'y' => 500,
'z' => 444,
'{' => 480,
'|' => 200,
'}' => 480,
'~' => 541,
chr(127) => 350,
chr(128) => 500,
chr(129) => 350,
chr(130) => 333,
chr(131) => 500,
chr(132) => 444,
chr(133) => 1000,
chr(134) => 500,
chr(135) => 500,
chr(136) => 333,
chr(137) => 1000,
chr(138) => 556,
chr(139) => 333,
chr(140) => 889,
chr(141) => 350,
chr(142) => 611,
chr(143) => 350,
chr(144) => 350,
chr(145) => 333,
chr(146) => 333,
chr(147) => 444,
chr(148) => 444,
chr(149) => 350,
chr(150) => 500,
chr(151) => 1000,
chr(152) => 333,
chr(153) => 980,
chr(154) => 389,
chr(155) => 333,
chr(156) => 722,
chr(157) => 350,
chr(158) => 444,
chr(159) => 722,
chr(160) => 250,
chr(161) => 333,
chr(162) => 500,
chr(163) => 500,
chr(164) => 500,
chr(165) => 500,
chr(166) => 200,
chr(167) => 500,
chr(168) => 333,
chr(169) => 760,
chr(170) => 276,
chr(171) => 500,
chr(172) => 564,
chr(173) => 333,
chr(174) => 760,
chr(175) => 333,
chr(176) => 400,
chr(177) => 564,
chr(178) => 300,
chr(179) => 300,
chr(180) => 333,
chr(181) => 500,
chr(182) => 453,
chr(183) => 250,
chr(184) => 333,
chr(185) => 300,
chr(186) => 310,
chr(187) => 500,
chr(188) => 750,
chr(189) => 750,
chr(190) => 750,
chr(191) => 444,
chr(192) => 722,
chr(193) => 722,
chr(194) => 722,
chr(195) => 722,
chr(196) => 722,
chr(197) => 722,
chr(198) => 889,
chr(199) => 667,
chr(200) => 611,
chr(201) => 611,
chr(202) => 611,
chr(203) => 611,
chr(204) => 333,
chr(205) => 333,
chr(206) => 333,
chr(207) => 333,
chr(208) => 722,
chr(209) => 722,
chr(210) => 722,
chr(211) => 722,
chr(212) => 722,
chr(213) => 722,
chr(214) => 722,
chr(215) => 564,
chr(216) => 722,
chr(217) => 722,
chr(218) => 722,
chr(219) => 722,
chr(220) => 722,
chr(221) => 722,
chr(222) => 556,
chr(223) => 500,
chr(224) => 444,
chr(225) => 444,
chr(226) => 444,
chr(227) => 444,
chr(228) => 444,
chr(229) => 444,
chr(230) => 667,
chr(231) => 444,
chr(232) => 444,
chr(233) => 444,
chr(234) => 444,
chr(235) => 444,
chr(236) => 278,
chr(237) => 278,
chr(238) => 278,
chr(239) => 278,
chr(240) => 500,
chr(241) => 500,
chr(242) => 500,
chr(243) => 500,
chr(244) => 500,
chr(245) => 500,
chr(246) => 500,
chr(247) => 564,
chr(248) => 500,
chr(249) => 500,
chr(250) => 500,
chr(251) => 500,
chr(252) => 500,
chr(253) => 500,
chr(254) => 500,
chr(255) => 500);
/trunk/applications/jrest/lib/File/PDF/fonts/timesb.php
New file
0,0 → 1,272
<?php
/**
* @package File_PDF
*/
$font_widths['timesB'] = array(
chr(0) => 250,
chr(1) => 250,
chr(2) => 250,
chr(3) => 250,
chr(4) => 250,
chr(5) => 250,
chr(6) => 250,
chr(7) => 250,
chr(8) => 250,
chr(9) => 250,
chr(10) => 250,
chr(11) => 250,
chr(12) => 250,
chr(13) => 250,
chr(14) => 250,
chr(15) => 250,
chr(16) => 250,
chr(17) => 250,
chr(18) => 250,
chr(19) => 250,
chr(20) => 250,
chr(21) => 250,
chr(22) => 250,
chr(23) => 250,
chr(24) => 250,
chr(25) => 250,
chr(26) => 250,
chr(27) => 250,
chr(28) => 250,
chr(29) => 250,
chr(30) => 250,
chr(31) => 250,
' ' => 250,
'!' => 333,
'"' => 555,
'#' => 500,
'$' => 500,
'%' => 1000,
'&' => 833,
'\'' => 278,
'(' => 333,
')' => 333,
'*' => 500,
'+' => 570,
',' => 250,
'-' => 333,
'.' => 250,
'/' => 278,
'0' => 500,
'1' => 500,
'2' => 500,
'3' => 500,
'4' => 500,
'5' => 500,
'6' => 500,
'7' => 500,
'8' => 500,
'9' => 500,
':' => 333,
';' => 333,
'<' => 570,
'=' => 570,
'>' => 570,
'?' => 500,
'@' => 930,
'A' => 722,
'B' => 667,
'C' => 722,
'D' => 722,
'E' => 667,
'F' => 611,
'G' => 778,
'H' => 778,
'I' => 389,
'J' => 500,
'K' => 778,
'L' => 667,
'M' => 944,
'N' => 722,
'O' => 778,
'P' => 611,
'Q' => 778,
'R' => 722,
'S' => 556,
'T' => 667,
'U' => 722,
'V' => 722,
'W' => 1000,
'X' => 722,
'Y' => 722,
'Z' => 667,
'[' => 333,
'\\' => 278,
']' => 333,
'^' => 581,
'_' => 500,
'`' => 333,
'a' => 500,
'b' => 556,
'c' => 444,
'd' => 556,
'e' => 444,
'f' => 333,
'g' => 500,
'h' => 556,
'i' => 278,
'j' => 333,
'k' => 556,
'l' => 278,
'm' => 833,
'n' => 556,
'o' => 500,
'p' => 556,
'q' => 556,
'r' => 444,
's' => 389,
't' => 333,
'u' => 556,
'v' => 500,
'w' => 722,
'x' => 500,
'y' => 500,
'z' => 444,
'{' => 394,
'|' => 220,
'}' => 394,
'~' => 520,
chr(127) => 350,
chr(128) => 500,
chr(129) => 350,
chr(130) => 333,
chr(131) => 500,
chr(132) => 500,
chr(133) => 1000,
chr(134) => 500,
chr(135) => 500,
chr(136) => 333,
chr(137) => 1000,
chr(138) => 556,
chr(139) => 333,
chr(140) => 1000,
chr(141) => 350,
chr(142) => 667,
chr(143) => 350,
chr(144) => 350,
chr(145) => 333,
chr(146) => 333,
chr(147) => 500,
chr(148) => 500,
chr(149) => 350,
chr(150) => 500,
chr(151) => 1000,
chr(152) => 333,
chr(153) => 1000,
chr(154) => 389,
chr(155) => 333,
chr(156) => 722,
chr(157) => 350,
chr(158) => 444,
chr(159) => 722,
chr(160) => 250,
chr(161) => 333,
chr(162) => 500,
chr(163) => 500,
chr(164) => 500,
chr(165) => 500,
chr(166) => 220,
chr(167) => 500,
chr(168) => 333,
chr(169) => 747,
chr(170) => 300,
chr(171) => 500,
chr(172) => 570,
chr(173) => 333,
chr(174) => 747,
chr(175) => 333,
chr(176) => 400,
chr(177) => 570,
chr(178) => 300,
chr(179) => 300,
chr(180) => 333,
chr(181) => 556,
chr(182) => 540,
chr(183) => 250,
chr(184) => 333,
chr(185) => 300,
chr(186) => 330,
chr(187) => 500,
chr(188) => 750,
chr(189) => 750,
chr(190) => 750,
chr(191) => 500,
chr(192) => 722,
chr(193) => 722,
chr(194) => 722,
chr(195) => 722,
chr(196) => 722,
chr(197) => 722,
chr(198) => 1000,
chr(199) => 722,
chr(200) => 667,
chr(201) => 667,
chr(202) => 667,
chr(203) => 667,
chr(204) => 389,
chr(205) => 389,
chr(206) => 389,
chr(207) => 389,
chr(208) => 722,
chr(209) => 722,
chr(210) => 778,
chr(211) => 778,
chr(212) => 778,
chr(213) => 778,
chr(214) => 778,
chr(215) => 570,
chr(216) => 778,
chr(217) => 722,
chr(218) => 722,
chr(219) => 722,
chr(220) => 722,
chr(221) => 722,
chr(222) => 611,
chr(223) => 556,
chr(224) => 500,
chr(225) => 500,
chr(226) => 500,
chr(227) => 500,
chr(228) => 500,
chr(229) => 500,
chr(230) => 722,
chr(231) => 444,
chr(232) => 444,
chr(233) => 444,
chr(234) => 444,
chr(235) => 444,
chr(236) => 278,
chr(237) => 278,
chr(238) => 278,
chr(239) => 278,
chr(240) => 500,
chr(241) => 556,
chr(242) => 500,
chr(243) => 500,
chr(244) => 500,
chr(245) => 500,
chr(246) => 500,
chr(247) => 570,
chr(248) => 500,
chr(249) => 556,
chr(250) => 556,
chr(251) => 556,
chr(252) => 556,
chr(253) => 500,
chr(254) => 556,
chr(255) => 500);
/trunk/applications/jrest/lib/File/PDF/fonts/helvetica.php
New file
0,0 → 1,272
<?php
/**
* @package File_PDF
*/
$font_widths['helvetica'] = array(
chr(0) => 278,
chr(1) => 278,
chr(2) => 278,
chr(3) => 278,
chr(4) => 278,
chr(5) => 278,
chr(6) => 278,
chr(7) => 278,
chr(8) => 278,
chr(9) => 278,
chr(10) => 278,
chr(11) => 278,
chr(12) => 278,
chr(13) => 278,
chr(14) => 278,
chr(15) => 278,
chr(16) => 278,
chr(17) => 278,
chr(18) => 278,
chr(19) => 278,
chr(20) => 278,
chr(21) => 278,
chr(22) => 278,
chr(23) => 278,
chr(24) => 278,
chr(25) => 278,
chr(26) => 278,
chr(27) => 278,
chr(28) => 278,
chr(29) => 278,
chr(30) => 278,
chr(31) => 278,
' ' => 278,
'!' => 278,
'"' => 355,
'#' => 556,
'$' => 556,
'%' => 889,
'&' => 667,
'\'' => 191,
'(' => 333,
')' => 333,
'*' => 389,
'+' => 584,
',' => 278,
'-' => 333,
'.' => 278,
'/' => 278,
'0' => 556,
'1' => 556,
'2' => 556,
'3' => 556,
'4' => 556,
'5' => 556,
'6' => 556,
'7' => 556,
'8' => 556,
'9' => 556,
':' => 278,
';' => 278,
'<' => 584,
'=' => 584,
'>' => 584,
'?' => 556,
'@' => 1015,
'A' => 667,
'B' => 667,
'C' => 722,
'D' => 722,
'E' => 667,
'F' => 611,
'G' => 778,
'H' => 722,
'I' => 278,
'J' => 500,
'K' => 667,
'L' => 556,
'M' => 833,
'N' => 722,
'O' => 778,
'P' => 667,
'Q' => 778,
'R' => 722,
'S' => 667,
'T' => 611,
'U' => 722,
'V' => 667,
'W' => 944,
'X' => 667,
'Y' => 667,
'Z' => 611,
'[' => 278,
'\\' => 278,
']' => 278,
'^' => 469,
'_' => 556,
'`' => 333,
'a' => 556,
'b' => 556,
'c' => 500,
'd' => 556,
'e' => 556,
'f' => 278,
'g' => 556,
'h' => 556,
'i' => 222,
'j' => 222,
'k' => 500,
'l' => 222,
'm' => 833,
'n' => 556,
'o' => 556,
'p' => 556,
'q' => 556,
'r' => 333,
's' => 500,
't' => 278,
'u' => 556,
'v' => 500,
'w' => 722,
'x' => 500,
'y' => 500,
'z' => 500,
'{' => 334,
'|' => 260,
'}' => 334,
'~' => 584,
chr(127) => 350,
chr(128) => 556,
chr(129) => 350,
chr(130) => 222,
chr(131) => 556,
chr(132) => 333,
chr(133) => 1000,
chr(134) => 556,
chr(135) => 556,
chr(136) => 333,
chr(137) => 1000,
chr(138) => 667,
chr(139) => 333,
chr(140) => 1000,
chr(141) => 350,
chr(142) => 611,
chr(143) => 350,
chr(144) => 350,
chr(145) => 222,
chr(146) => 222,
chr(147) => 333,
chr(148) => 333,
chr(149) => 350,
chr(150) => 556,
chr(151) => 1000,
chr(152) => 333,
chr(153) => 1000,
chr(154) => 500,
chr(155) => 333,
chr(156) => 944,
chr(157) => 350,
chr(158) => 500,
chr(159) => 667,
chr(160) => 278,
chr(161) => 333,
chr(162) => 556,
chr(163) => 556,
chr(164) => 556,
chr(165) => 556,
chr(166) => 260,
chr(167) => 556,
chr(168) => 333,
chr(169) => 737,
chr(170) => 370,
chr(171) => 556,
chr(172) => 584,
chr(173) => 333,
chr(174) => 737,
chr(175) => 333,
chr(176) => 400,
chr(177) => 584,
chr(178) => 333,
chr(179) => 333,
chr(180) => 333,
chr(181) => 556,
chr(182) => 537,
chr(183) => 278,
chr(184) => 333,
chr(185) => 333,
chr(186) => 365,
chr(187) => 556,
chr(188) => 834,
chr(189) => 834,
chr(190) => 834,
chr(191) => 611,
chr(192) => 667,
chr(193) => 667,
chr(194) => 667,
chr(195) => 667,
chr(196) => 667,
chr(197) => 667,
chr(198) => 1000,
chr(199) => 722,
chr(200) => 667,
chr(201) => 667,
chr(202) => 667,
chr(203) => 667,
chr(204) => 278,
chr(205) => 278,
chr(206) => 278,
chr(207) => 278,
chr(208) => 722,
chr(209) => 722,
chr(210) => 778,
chr(211) => 778,
chr(212) => 778,
chr(213) => 778,
chr(214) => 778,
chr(215) => 584,
chr(216) => 778,
chr(217) => 722,
chr(218) => 722,
chr(219) => 722,
chr(220) => 722,
chr(221) => 667,
chr(222) => 667,
chr(223) => 611,
chr(224) => 556,
chr(225) => 556,
chr(226) => 556,
chr(227) => 556,
chr(228) => 556,
chr(229) => 556,
chr(230) => 889,
chr(231) => 500,
chr(232) => 556,
chr(233) => 556,
chr(234) => 556,
chr(235) => 556,
chr(236) => 278,
chr(237) => 278,
chr(238) => 278,
chr(239) => 278,
chr(240) => 556,
chr(241) => 556,
chr(242) => 556,
chr(243) => 556,
chr(244) => 556,
chr(245) => 556,
chr(246) => 556,
chr(247) => 584,
chr(248) => 611,
chr(249) => 556,
chr(250) => 556,
chr(251) => 556,
chr(252) => 556,
chr(253) => 500,
chr(254) => 556,
chr(255) => 500);
/trunk/applications/jrest/lib/File/PDF/fonts/symbol.php
New file
0,0 → 1,272
<?php
/**
* @package File_PDF
*/
$font_widths['symbol'] = array(
chr(0) => 250,
chr(1) => 250,
chr(2) => 250,
chr(3) => 250,
chr(4) => 250,
chr(5) => 250,
chr(6) => 250,
chr(7) => 250,
chr(8) => 250,
chr(9) => 250,
chr(10) => 250,
chr(11) => 250,
chr(12) => 250,
chr(13) => 250,
chr(14) => 250,
chr(15) => 250,
chr(16) => 250,
chr(17) => 250,
chr(18) => 250,
chr(19) => 250,
chr(20) => 250,
chr(21) => 250,
chr(22) => 250,
chr(23) => 250,
chr(24) => 250,
chr(25) => 250,
chr(26) => 250,
chr(27) => 250,
chr(28) => 250,
chr(29) => 250,
chr(30) => 250,
chr(31) => 250,
' ' => 250,
'!' => 333,
'"' => 713,
'#' => 500,
'$' => 549,
'%' => 833,
'&' => 778,
'\'' => 439,
'(' => 333,
')' => 333,
'*' => 500,
'+' => 549,
',' => 250,
'-' => 549,
'.' => 250,
'/' => 278,
'0' => 500,
'1' => 500,
'2' => 500,
'3' => 500,
'4' => 500,
'5' => 500,
'6' => 500,
'7' => 500,
'8' => 500,
'9' => 500,
':' => 278,
';' => 278,
'<' => 549,
'=' => 549,
'>' => 549,
'?' => 444,
'@' => 549,
'A' => 722,
'B' => 667,
'C' => 722,
'D' => 612,
'E' => 611,
'F' => 763,
'G' => 603,
'H' => 722,
'I' => 333,
'J' => 631,
'K' => 722,
'L' => 686,
'M' => 889,
'N' => 722,
'O' => 722,
'P' => 768,
'Q' => 741,
'R' => 556,
'S' => 592,
'T' => 611,
'U' => 690,
'V' => 439,
'W' => 768,
'X' => 645,
'Y' => 795,
'Z' => 611,
'[' => 333,
'\\' => 863,
']' => 333,
'^' => 658,
'_' => 500,
'`' => 500,
'a' => 631,
'b' => 549,
'c' => 549,
'd' => 494,
'e' => 439,
'f' => 521,
'g' => 411,
'h' => 603,
'i' => 329,
'j' => 603,
'k' => 549,
'l' => 549,
'm' => 576,
'n' => 521,
'o' => 549,
'p' => 549,
'q' => 521,
'r' => 549,
's' => 603,
't' => 439,
'u' => 576,
'v' => 713,
'w' => 686,
'x' => 493,
'y' => 686,
'z' => 494,
'{' => 480,
'|' => 200,
'}' => 480,
'~' => 549,
chr(127) => 0,
chr(128) => 0,
chr(129) => 0,
chr(130) => 0,
chr(131) => 0,
chr(132) => 0,
chr(133) => 0,
chr(134) => 0,
chr(135) => 0,
chr(136) => 0,
chr(137) => 0,
chr(138) => 0,
chr(139) => 0,
chr(140) => 0,
chr(141) => 0,
chr(142) => 0,
chr(143) => 0,
chr(144) => 0,
chr(145) => 0,
chr(146) => 0,
chr(147) => 0,
chr(148) => 0,
chr(149) => 0,
chr(150) => 0,
chr(151) => 0,
chr(152) => 0,
chr(153) => 0,
chr(154) => 0,
chr(155) => 0,
chr(156) => 0,
chr(157) => 0,
chr(158) => 0,
chr(159) => 0,
chr(160) => 750,
chr(161) => 620,
chr(162) => 247,
chr(163) => 549,
chr(164) => 167,
chr(165) => 713,
chr(166) => 500,
chr(167) => 753,
chr(168) => 753,
chr(169) => 753,
chr(170) => 753,
chr(171) => 1042,
chr(172) => 987,
chr(173) => 603,
chr(174) => 987,
chr(175) => 603,
chr(176) => 400,
chr(177) => 549,
chr(178) => 411,
chr(179) => 549,
chr(180) => 549,
chr(181) => 713,
chr(182) => 494,
chr(183) => 460,
chr(184) => 549,
chr(185) => 549,
chr(186) => 549,
chr(187) => 549,
chr(188) => 1000,
chr(189) => 603,
chr(190) => 1000,
chr(191) => 658,
chr(192) => 823,
chr(193) => 686,
chr(194) => 795,
chr(195) => 987,
chr(196) => 768,
chr(197) => 768,
chr(198) => 823,
chr(199) => 768,
chr(200) => 768,
chr(201) => 713,
chr(202) => 713,
chr(203) => 713,
chr(204) => 713,
chr(205) => 713,
chr(206) => 713,
chr(207) => 713,
chr(208) => 768,
chr(209) => 713,
chr(210) => 790,
chr(211) => 790,
chr(212) => 890,
chr(213) => 823,
chr(214) => 549,
chr(215) => 250,
chr(216) => 713,
chr(217) => 603,
chr(218) => 603,
chr(219) => 1042,
chr(220) => 987,
chr(221) => 603,
chr(222) => 987,
chr(223) => 603,
chr(224) => 494,
chr(225) => 329,
chr(226) => 790,
chr(227) => 790,
chr(228) => 786,
chr(229) => 713,
chr(230) => 384,
chr(231) => 384,
chr(232) => 384,
chr(233) => 384,
chr(234) => 384,
chr(235) => 384,
chr(236) => 494,
chr(237) => 494,
chr(238) => 494,
chr(239) => 494,
chr(240) => 0,
chr(241) => 329,
chr(242) => 274,
chr(243) => 686,
chr(244) => 686,
chr(245) => 686,
chr(246) => 384,
chr(247) => 384,
chr(248) => 384,
chr(249) => 384,
chr(250) => 384,
chr(251) => 384,
chr(252) => 494,
chr(253) => 494,
chr(254) => 494,
chr(255) => 0);
/trunk/applications/jrest/lib/File/PDF/fonts/helveticab.php
New file
0,0 → 1,272
<?php
/**
* @package File_PDF
*/
$font_widths['helveticaB'] = array(
chr(0) => 278,
chr(1) => 278,
chr(2) => 278,
chr(3) => 278,
chr(4) => 278,
chr(5) => 278,
chr(6) => 278,
chr(7) => 278,
chr(8) => 278,
chr(9) => 278,
chr(10) => 278,
chr(11) => 278,
chr(12) => 278,
chr(13) => 278,
chr(14) => 278,
chr(15) => 278,
chr(16) => 278,
chr(17) => 278,
chr(18) => 278,
chr(19) => 278,
chr(20) => 278,
chr(21) => 278,
chr(22) => 278,
chr(23) => 278,
chr(24) => 278,
chr(25) => 278,
chr(26) => 278,
chr(27) => 278,
chr(28) => 278,
chr(29) => 278,
chr(30) => 278,
chr(31) => 278,
' ' => 278,
'!' => 333,
'"' => 474,
'#' => 556,
'$' => 556,
'%' => 889,
'&' => 722,
'\'' => 238,
'(' => 333,
')' => 333,
'*' => 389,
'+' => 584,
',' => 278,
'-' => 333,
'.' => 278,
'/' => 278,
'0' => 556,
'1' => 556,
'2' => 556,
'3' => 556,
'4' => 556,
'5' => 556,
'6' => 556,
'7' => 556,
'8' => 556,
'9' => 556,
':' => 333,
';' => 333,
'<' => 584,
'=' => 584,
'>' => 584,
'?' => 611,
'@' => 975,
'A' => 722,
'B' => 722,
'C' => 722,
'D' => 722,
'E' => 667,
'F' => 611,
'G' => 778,
'H' => 722,
'I' => 278,
'J' => 556,
'K' => 722,
'L' => 611,
'M' => 833,
'N' => 722,
'O' => 778,
'P' => 667,
'Q' => 778,
'R' => 722,
'S' => 667,
'T' => 611,
'U' => 722,
'V' => 667,
'W' => 944,
'X' => 667,
'Y' => 667,
'Z' => 611,
'[' => 333,
'\\' => 278,
']' => 333,
'^' => 584,
'_' => 556,
'`' => 333,
'a' => 556,
'b' => 611,
'c' => 556,
'd' => 611,
'e' => 556,
'f' => 333,
'g' => 611,
'h' => 611,
'i' => 278,
'j' => 278,
'k' => 556,
'l' => 278,
'm' => 889,
'n' => 611,
'o' => 611,
'p' => 611,
'q' => 611,
'r' => 389,
's' => 556,
't' => 333,
'u' => 611,
'v' => 556,
'w' => 778,
'x' => 556,
'y' => 556,
'z' => 500,
'{' => 389,
'|' => 280,
'}' => 389,
'~' => 584,
chr(127) => 350,
chr(128) => 556,
chr(129) => 350,
chr(130) => 278,
chr(131) => 556,
chr(132) => 500,
chr(133) => 1000,
chr(134) => 556,
chr(135) => 556,
chr(136) => 333,
chr(137) => 1000,
chr(138) => 667,
chr(139) => 333,
chr(140) => 1000,
chr(141) => 350,
chr(142) => 611,
chr(143) => 350,
chr(144) => 350,
chr(145) => 278,
chr(146) => 278,
chr(147) => 500,
chr(148) => 500,
chr(149) => 350,
chr(150) => 556,
chr(151) => 1000,
chr(152) => 333,
chr(153) => 1000,
chr(154) => 556,
chr(155) => 333,
chr(156) => 944,
chr(157) => 350,
chr(158) => 500,
chr(159) => 667,
chr(160) => 278,
chr(161) => 333,
chr(162) => 556,
chr(163) => 556,
chr(164) => 556,
chr(165) => 556,
chr(166) => 280,
chr(167) => 556,
chr(168) => 333,
chr(169) => 737,
chr(170) => 370,
chr(171) => 556,
chr(172) => 584,
chr(173) => 333,
chr(174) => 737,
chr(175) => 333,
chr(176) => 400,
chr(177) => 584,
chr(178) => 333,
chr(179) => 333,
chr(180) => 333,
chr(181) => 611,
chr(182) => 556,
chr(183) => 278,
chr(184) => 333,
chr(185) => 333,
chr(186) => 365,
chr(187) => 556,
chr(188) => 834,
chr(189) => 834,
chr(190) => 834,
chr(191) => 611,
chr(192) => 722,
chr(193) => 722,
chr(194) => 722,
chr(195) => 722,
chr(196) => 722,
chr(197) => 722,
chr(198) => 1000,
chr(199) => 722,
chr(200) => 667,
chr(201) => 667,
chr(202) => 667,
chr(203) => 667,
chr(204) => 278,
chr(205) => 278,
chr(206) => 278,
chr(207) => 278,
chr(208) => 722,
chr(209) => 722,
chr(210) => 778,
chr(211) => 778,
chr(212) => 778,
chr(213) => 778,
chr(214) => 778,
chr(215) => 584,
chr(216) => 778,
chr(217) => 722,
chr(218) => 722,
chr(219) => 722,
chr(220) => 722,
chr(221) => 667,
chr(222) => 667,
chr(223) => 611,
chr(224) => 556,
chr(225) => 556,
chr(226) => 556,
chr(227) => 556,
chr(228) => 556,
chr(229) => 556,
chr(230) => 889,
chr(231) => 556,
chr(232) => 556,
chr(233) => 556,
chr(234) => 556,
chr(235) => 556,
chr(236) => 278,
chr(237) => 278,
chr(238) => 278,
chr(239) => 278,
chr(240) => 611,
chr(241) => 611,
chr(242) => 611,
chr(243) => 611,
chr(244) => 611,
chr(245) => 611,
chr(246) => 611,
chr(247) => 584,
chr(248) => 611,
chr(249) => 611,
chr(250) => 611,
chr(251) => 611,
chr(252) => 611,
chr(253) => 556,
chr(254) => 611,
chr(255) => 556);
/trunk/applications/jrest/lib/File/PDF/fonts/courier.php
New file
0,0 → 1,10
<?php
/**
* @package File_PDF
*/
for ($i = 0; $i <= 255; $i++) {
$font_widths['courier'][chr($i)] = 600;
}
$font_widths['courierB'] = $font_widths['courier'];
$font_widths['courierI'] = $font_widths['courier'];
$font_widths['courierBI'] = $font_widths['courier'];
/trunk/applications/jrest/lib/File/PDF/fonts/timesi.php
New file
0,0 → 1,272
<?php
/**
* @package File_PDF
*/
$font_widths['timesI'] = array(
chr(0) => 250,
chr(1) => 250,
chr(2) => 250,
chr(3) => 250,
chr(4) => 250,
chr(5) => 250,
chr(6) => 250,
chr(7) => 250,
chr(8) => 250,
chr(9) => 250,
chr(10) => 250,
chr(11) => 250,
chr(12) => 250,
chr(13) => 250,
chr(14) => 250,
chr(15) => 250,
chr(16) => 250,
chr(17) => 250,
chr(18) => 250,
chr(19) => 250,
chr(20) => 250,
chr(21) => 250,
chr(22) => 250,
chr(23) => 250,
chr(24) => 250,
chr(25) => 250,
chr(26) => 250,
chr(27) => 250,
chr(28) => 250,
chr(29) => 250,
chr(30) => 250,
chr(31) => 250,
' ' => 250,
'!' => 333,
'"' => 420,
'#' => 500,
'$' => 500,
'%' => 833,
'&' => 778,
'\'' => 214,
'(' => 333,
')' => 333,
'*' => 500,
'+' => 675,
',' => 250,
'-' => 333,
'.' => 250,
'/' => 278,
'0' => 500,
'1' => 500,
'2' => 500,
'3' => 500,
'4' => 500,
'5' => 500,
'6' => 500,
'7' => 500,
'8' => 500,
'9' => 500,
':' => 333,
';' => 333,
'<' => 675,
'=' => 675,
'>' => 675,
'?' => 500,
'@' => 920,
'A' => 611,
'B' => 611,
'C' => 667,
'D' => 722,
'E' => 611,
'F' => 611,
'G' => 722,
'H' => 722,
'I' => 333,
'J' => 444,
'K' => 667,
'L' => 556,
'M' => 833,
'N' => 667,
'O' => 722,
'P' => 611,
'Q' => 722,
'R' => 611,
'S' => 500,
'T' => 556,
'U' => 722,
'V' => 611,
'W' => 833,
'X' => 611,
'Y' => 556,
'Z' => 556,
'[' => 389,
'\\' => 278,
']' => 389,
'^' => 422,
'_' => 500,
'`' => 333,
'a' => 500,
'b' => 500,
'c' => 444,
'd' => 500,
'e' => 444,
'f' => 278,
'g' => 500,
'h' => 500,
'i' => 278,
'j' => 278,
'k' => 444,
'l' => 278,
'm' => 722,
'n' => 500,
'o' => 500,
'p' => 500,
'q' => 500,
'r' => 389,
's' => 389,
't' => 278,
'u' => 500,
'v' => 444,
'w' => 667,
'x' => 444,
'y' => 444,
'z' => 389,
'{' => 400,
'|' => 275,
'}' => 400,
'~' => 541,
chr(127) => 350,
chr(128) => 500,
chr(129) => 350,
chr(130) => 333,
chr(131) => 500,
chr(132) => 556,
chr(133) => 889,
chr(134) => 500,
chr(135) => 500,
chr(136) => 333,
chr(137) => 1000,
chr(138) => 500,
chr(139) => 333,
chr(140) => 944,
chr(141) => 350,
chr(142) => 556,
chr(143) => 350,
chr(144) => 350,
chr(145) => 333,
chr(146) => 333,
chr(147) => 556,
chr(148) => 556,
chr(149) => 350,
chr(150) => 500,
chr(151) => 889,
chr(152) => 333,
chr(153) => 980,
chr(154) => 389,
chr(155) => 333,
chr(156) => 667,
chr(157) => 350,
chr(158) => 389,
chr(159) => 556,
chr(160) => 250,
chr(161) => 389,
chr(162) => 500,
chr(163) => 500,
chr(164) => 500,
chr(165) => 500,
chr(166) => 275,
chr(167) => 500,
chr(168) => 333,
chr(169) => 760,
chr(170) => 276,
chr(171) => 500,
chr(172) => 675,
chr(173) => 333,
chr(174) => 760,
chr(175) => 333,
chr(176) => 400,
chr(177) => 675,
chr(178) => 300,
chr(179) => 300,
chr(180) => 333,
chr(181) => 500,
chr(182) => 523,
chr(183) => 250,
chr(184) => 333,
chr(185) => 300,
chr(186) => 310,
chr(187) => 500,
chr(188) => 750,
chr(189) => 750,
chr(190) => 750,
chr(191) => 500,
chr(192) => 611,
chr(193) => 611,
chr(194) => 611,
chr(195) => 611,
chr(196) => 611,
chr(197) => 611,
chr(198) => 889,
chr(199) => 667,
chr(200) => 611,
chr(201) => 611,
chr(202) => 611,
chr(203) => 611,
chr(204) => 333,
chr(205) => 333,
chr(206) => 333,
chr(207) => 333,
chr(208) => 722,
chr(209) => 667,
chr(210) => 722,
chr(211) => 722,
chr(212) => 722,
chr(213) => 722,
chr(214) => 722,
chr(215) => 675,
chr(216) => 722,
chr(217) => 722,
chr(218) => 722,
chr(219) => 722,
chr(220) => 722,
chr(221) => 556,
chr(222) => 611,
chr(223) => 500,
chr(224) => 500,
chr(225) => 500,
chr(226) => 500,
chr(227) => 500,
chr(228) => 500,
chr(229) => 500,
chr(230) => 667,
chr(231) => 444,
chr(232) => 444,
chr(233) => 444,
chr(234) => 444,
chr(235) => 444,
chr(236) => 278,
chr(237) => 278,
chr(238) => 278,
chr(239) => 278,
chr(240) => 500,
chr(241) => 500,
chr(242) => 500,
chr(243) => 500,
chr(244) => 500,
chr(245) => 500,
chr(246) => 500,
chr(247) => 675,
chr(248) => 500,
chr(249) => 500,
chr(250) => 500,
chr(251) => 500,
chr(252) => 500,
chr(253) => 444,
chr(254) => 500,
chr(255) => 444);
/trunk/applications/jrest/lib/HTTP/Download.php
New file
0,0 → 1,1034
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
/**
* HTTP::Download
*
* PHP versions 4 and 5
*
* @category HTTP
* @package HTTP_Download
* @author Michael Wallner <mike@php.net>
* @copyright 2003-2005 Michael Wallner
* @license BSD, revised
* @version CVS: $Id$
* @link http://pear.php.net/package/HTTP_Download
*/
 
// {{{ includes
/**
* Requires PEAR
*/
require_once 'PEAR.php';
 
/**
* Requires HTTP_Header
*/
require_once 'HTTP/Header.php';
// }}}
 
// {{{ constants
/**#@+ Use with HTTP_Download::setContentDisposition() **/
/**
* Send data as attachment
*/
define('HTTP_DOWNLOAD_ATTACHMENT', 'attachment');
/**
* Send data inline
*/
define('HTTP_DOWNLOAD_INLINE', 'inline');
/**#@-**/
 
/**#@+ Use with HTTP_Download::sendArchive() **/
/**
* Send as uncompressed tar archive
*/
define('HTTP_DOWNLOAD_TAR', 'TAR');
/**
* Send as gzipped tar archive
*/
define('HTTP_DOWNLOAD_TGZ', 'TGZ');
/**
* Send as bzip2 compressed tar archive
*/
define('HTTP_DOWNLOAD_BZ2', 'BZ2');
/**
* Send as zip archive
*/
define('HTTP_DOWNLOAD_ZIP', 'ZIP');
/**#@-**/
 
/**#@+
* Error constants
*/
define('HTTP_DOWNLOAD_E_HEADERS_SENT', -1);
define('HTTP_DOWNLOAD_E_NO_EXT_ZLIB', -2);
define('HTTP_DOWNLOAD_E_NO_EXT_MMAGIC', -3);
define('HTTP_DOWNLOAD_E_INVALID_FILE', -4);
define('HTTP_DOWNLOAD_E_INVALID_PARAM', -5);
define('HTTP_DOWNLOAD_E_INVALID_RESOURCE', -6);
define('HTTP_DOWNLOAD_E_INVALID_REQUEST', -7);
define('HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE', -8);
define('HTTP_DOWNLOAD_E_INVALID_ARCHIVE_TYPE', -9);
/**#@-**/
// }}}
 
/**
* Send HTTP Downloads/Responses.
*
* With this package you can handle (hidden) downloads.
* It supports partial downloads, resuming and sending
* raw data ie. from database BLOBs.
*
* <i>ATTENTION:</i>
* You shouldn't use this package together with ob_gzhandler or
* zlib.output_compression enabled in your php.ini, especially
* if you want to send already gzipped data!
*
* @access public
* @version $Revision$
*/
class HTTP_Download
{
// {{{ protected member variables
/**
* Path to file for download
*
* @see HTTP_Download::setFile()
* @access protected
* @var string
*/
var $file = '';
/**
* Data for download
*
* @see HTTP_Download::setData()
* @access protected
* @var string
*/
var $data = null;
/**
* Resource handle for download
*
* @see HTTP_Download::setResource()
* @access protected
* @var int
*/
var $handle = null;
/**
* Whether to gzip the download
*
* @access protected
* @var bool
*/
var $gzip = false;
/**
* Whether to allow caching of the download on the clients side
*
* @access protected
* @var bool
*/
var $cache = true;
/**
* Size of download
*
* @access protected
* @var int
*/
var $size = 0;
/**
* Last modified
*
* @access protected
* @var int
*/
var $lastModified = 0;
/**
* HTTP headers
*
* @access protected
* @var array
*/
var $headers = array(
'Content-Type' => 'application/x-octetstream',
'Pragma' => 'cache',
'Cache-Control' => 'public, must-revalidate, max-age=0',
'Accept-Ranges' => 'bytes',
'X-Sent-By' => 'PEAR::HTTP::Download'
);
/**
* HTTP_Header
*
* @access protected
* @var object
*/
var $HTTP = null;
/**
* ETag
*
* @access protected
* @var string
*/
var $etag = '';
/**
* Buffer Size
*
* @access protected
* @var int
*/
var $bufferSize = 2097152;
/**
* Throttle Delay
*
* @access protected
* @var float
*/
var $throttleDelay = 0;
/**
* Sent Bytes
*
* @access public
* @var int
*/
var $sentBytes = 0;
// }}}
// {{{ constructor
/**
* Constructor
*
* Set supplied parameters.
*
* @access public
* @param array $params associative array of parameters
*
* <b>one of:</b>
* o 'file' => path to file for download
* o 'data' => raw data for download
* o 'resource' => resource handle for download
* <br/>
* <b>and any of:</b>
* o 'cache' => whether to allow cs caching
* o 'gzip' => whether to gzip the download
* o 'lastmodified' => unix timestamp
* o 'contenttype' => content type of download
* o 'contentdisposition' => content disposition
* o 'buffersize' => amount of bytes to buffer
* o 'throttledelay' => amount of secs to sleep
* o 'cachecontrol' => cache privacy and validity
*
* <br />
* 'Content-Disposition' is not HTTP compliant, but most browsers
* follow this header, so it was borrowed from MIME standard.
*
* It looks like this: <br />
* "Content-Disposition: attachment; filename=example.tgz".
*
* @see HTTP_Download::setContentDisposition()
*/
function HTTP_Download($params = array())
{
$this->HTTP = &new HTTP_Header;
$this->setParams($params);
}
// }}}
// {{{ public methods
/**
* Set parameters
*
* Set supplied parameters through its accessor methods.
*
* @access public
* @return mixed Returns true on success or PEAR_Error on failure.
* @param array $params associative array of parameters
*
* @see HTTP_Download::HTTP_Download()
*/
function setParams($params)
{
foreach((array) $params as $param => $value){
$method = 'set'. $param;
if (!method_exists($this, $method)) {
return PEAR::raiseError(
"Method '$method' doesn't exist.",
HTTP_DOWNLOAD_E_INVALID_PARAM
);
}
$e = call_user_func_array(array(&$this, $method), (array) $value);
if (PEAR::isError($e)) {
return $e;
}
}
return true;
}
/**
* Set path to file for download
*
* The Last-Modified header will be set to files filemtime(), actually.
* Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_FILE) if file doesn't exist.
* Sends HTTP 404 status if $send_404 is set to true.
*
* @access public
* @return mixed Returns true on success or PEAR_Error on failure.
* @param string $file path to file for download
* @param bool $send_404 whether to send HTTP/404 if
* the file wasn't found
*/
function setFile($file, $send_404 = true)
{
$file = realpath($file);
if (!is_file($file)) {
if ($send_404) {
$this->HTTP->sendStatusCode(404);
}
return PEAR::raiseError(
"File '$file' not found.",
HTTP_DOWNLOAD_E_INVALID_FILE
);
}
$this->setLastModified(filemtime($file));
$this->file = $file;
$this->size = filesize($file);
return true;
}
/**
* Set data for download
*
* Set $data to null if you want to unset this.
*
* @access public
* @return void
* @param $data raw data to send
*/
function setData($data = null)
{
$this->data = $data;
$this->size = strlen($data);
}
/**
* Set resource for download
*
* The resource handle supplied will be closed after sending the download.
* Returns a PEAR_Error (HTTP_DOWNLOAD_E_INVALID_RESOURCE) if $handle
* is no valid resource. Set $handle to null if you want to unset this.
*
* @access public
* @return mixed Returns true on success or PEAR_Error on failure.
* @param int $handle resource handle
*/
function setResource($handle = null)
{
if (!isset($handle)) {
$this->handle = null;
$this->size = 0;
return true;
}
if (is_resource($handle)) {
$this->handle = $handle;
$filestats = fstat($handle);
$this->size = $filestats['size'];
return true;
}
 
return PEAR::raiseError(
"Handle '$handle' is no valid resource.",
HTTP_DOWNLOAD_E_INVALID_RESOURCE
);
}
/**
* Whether to gzip the download
*
* Returns a PEAR_Error (HTTP_DOWNLOAD_E_NO_EXT_ZLIB)
* if ext/zlib is not available/loadable.
*
* @access public
* @return mixed Returns true on success or PEAR_Error on failure.
* @param bool $gzip whether to gzip the download
*/
function setGzip($gzip = false)
{
if ($gzip && !PEAR::loadExtension('zlib')){
return PEAR::raiseError(
'GZIP compression (ext/zlib) not available.',
HTTP_DOWNLOAD_E_NO_EXT_ZLIB
);
}
$this->gzip = (bool) $gzip;
return true;
}
 
/**
* Whether to allow caching
*
* If set to true (default) we'll send some headers that are commonly
* used for caching purposes like ETag, Cache-Control and Last-Modified.
*
* If caching is disabled, we'll send the download no matter if it
* would actually be cached at the client side.
*
* @access public
* @return void
* @param bool $cache whether to allow caching
*/
function setCache($cache = true)
{
$this->cache = (bool) $cache;
}
/**
* Whether to allow proxies to cache
*
* If set to 'private' proxies shouldn't cache the response.
* This setting defaults to 'public' and affects only cached responses.
*
* @access public
* @return bool
* @param string $cache private or public
* @param int $maxage maximum age of the client cache entry
*/
function setCacheControl($cache = 'public', $maxage = 0)
{
switch ($cache = strToLower($cache))
{
case 'private':
case 'public':
$this->headers['Cache-Control'] =
$cache .', must-revalidate, max-age='. abs($maxage);
return true;
break;
}
return false;
}
/**
* Set ETag
*
* Sets a user-defined ETag for cache-validation. The ETag is usually
* generated by HTTP_Download through its payload information.
*
* @access public
* @return void
* @param string $etag Entity tag used for strong cache validation.
*/
function setETag($etag = null)
{
$this->etag = (string) $etag;
}
/**
* Set Size of Buffer
*
* The amount of bytes specified as buffer size is the maximum amount
* of data read at once from resources or files. The default size is 2M
* (2097152 bytes). Be aware that if you enable gzip compression and
* you set a very low buffer size that the actual file size may grow
* due to added gzip headers for each sent chunk of the specified size.
*
* Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_PARAM) if $size is not
* greater than 0 bytes.
*
* @access public
* @return mixed Returns true on success or PEAR_Error on failure.
* @param int $bytes Amount of bytes to use as buffer.
*/
function setBufferSize($bytes = 2097152)
{
if (0 >= $bytes) {
return PEAR::raiseError(
'Buffer size must be greater than 0 bytes ('. $bytes .' given)',
HTTP_DOWNLOAD_E_INVALID_PARAM);
}
$this->bufferSize = abs($bytes);
return true;
}
/**
* Set Throttle Delay
*
* Set the amount of seconds to sleep after each chunck that has been
* sent. One can implement some sort of throttle through adjusting the
* buffer size and the throttle delay. With the following settings
* HTTP_Download will sleep a second after each 25 K of data sent.
*
* <code>
* Array(
* 'throttledelay' => 1,
* 'buffersize' => 1024 * 25,
* )
* </code>
*
* Just be aware that if gzipp'ing is enabled, decreasing the chunk size
* too much leads to proportionally increased network traffic due to added
* gzip header and bottom bytes around each chunk.
*
* @access public
* @return void
* @param float $seconds Amount of seconds to sleep after each
* chunk that has been sent.
*/
function setThrottleDelay($seconds = 0)
{
$this->throttleDelay = abs($seconds) * 1000;
}
/**
* Set "Last-Modified"
*
* This is usually determined by filemtime() in HTTP_Download::setFile()
* If you set raw data for download with HTTP_Download::setData() and you
* want do send an appropiate "Last-Modified" header, you should call this
* method.
*
* @access public
* @return void
* @param int unix timestamp
*/
function setLastModified($last_modified)
{
$this->lastModified = $this->headers['Last-Modified'] = (int) $last_modified;
}
/**
* Set Content-Disposition header
*
* @see HTTP_Download::HTTP_Download
*
* @access public
* @return void
* @param string $disposition whether to send the download
* inline or as attachment
* @param string $file_name the filename to display in
* the browser's download window
*
* <b>Example:</b>
* <code>
* $HTTP_Download->setContentDisposition(
* HTTP_DOWNLOAD_ATTACHMENT,
* 'download.tgz'
* );
* </code>
*/
function setContentDisposition( $disposition = HTTP_DOWNLOAD_ATTACHMENT,
$file_name = null)
{
$cd = $disposition;
if (isset($file_name)) {
$cd .= '; filename="' . $file_name . '"';
} elseif ($this->file) {
$cd .= '; filename="' . basename($this->file) . '"';
}
$this->headers['Content-Disposition'] = $cd;
}
/**
* Set content type of the download
*
* Default content type of the download will be 'application/x-octetstream'.
* Returns PEAR_Error (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE) if
* $content_type doesn't seem to be valid.
*
* @access public
* @return mixed Returns true on success or PEAR_Error on failure.
* @param string $content_type content type of file for download
*/
function setContentType($content_type = 'application/x-octetstream')
{
if (!preg_match('/^[a-z]+\w*\/[a-z]+[\w.;= -]*$/', $content_type)) {
return PEAR::raiseError(
"Invalid content type '$content_type' supplied.",
HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE
);
}
$this->headers['Content-Type'] = $content_type;
return true;
}
/**
* Guess content type of file
*
* First we try to use PEAR::MIME_Type, if installed, to detect the content
* type, else we check if ext/mime_magic is loaded and properly configured.
*
* Returns PEAR_Error if:
* o if PEAR::MIME_Type failed to detect a proper content type
* (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE)
* o ext/magic.mime is not installed, or not properly configured
* (HTTP_DOWNLOAD_E_NO_EXT_MMAGIC)
* o mime_content_type() couldn't guess content type or returned
* a content type considered to be bogus by setContentType()
* (HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE)
*
* @access public
* @return mixed Returns true on success or PEAR_Error on failure.
*/
function guessContentType()
{
if (class_exists('MIME_Type') || @include_once 'MIME/Type.php') {
if (PEAR::isError($mime_type = MIME_Type::autoDetect($this->file))) {
return PEAR::raiseError($mime_type->getMessage(),
HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE);
}
return $this->setContentType($mime_type);
}
if (!function_exists('mime_content_type')) {
return PEAR::raiseError(
'This feature requires ext/mime_magic!',
HTTP_DOWNLOAD_E_NO_EXT_MMAGIC
);
}
if (!is_file(ini_get('mime_magic.magicfile'))) {
return PEAR::raiseError(
'ext/mime_magic is loaded but not properly configured!',
HTTP_DOWNLOAD_E_NO_EXT_MMAGIC
);
}
if (!$content_type = @mime_content_type($this->file)) {
return PEAR::raiseError(
'Couldn\'t guess content type with mime_content_type().',
HTTP_DOWNLOAD_E_INVALID_CONTENT_TYPE
);
}
return $this->setContentType($content_type);
}
 
/**
* Send
*
* Returns PEAR_Error if:
* o HTTP headers were already sent (HTTP_DOWNLOAD_E_HEADERS_SENT)
* o HTTP Range was invalid (HTTP_DOWNLOAD_E_INVALID_REQUEST)
*
* @access public
* @return mixed Returns true on success or PEAR_Error on failure.
* @param bool $autoSetContentDisposition Whether to set the
* Content-Disposition header if it isn't already.
*/
function send($autoSetContentDisposition = true)
{
if (headers_sent()) {
return PEAR::raiseError(
'Headers already sent.',
HTTP_DOWNLOAD_E_HEADERS_SENT
);
}
if (!ini_get('safe_mode')) {
@set_time_limit(0);
}
if ($autoSetContentDisposition &&
!isset($this->headers['Content-Disposition'])) {
$this->setContentDisposition();
}
if ($this->cache) {
$this->headers['ETag'] = $this->generateETag();
if ($this->isCached()) {
$this->HTTP->sendStatusCode(304);
$this->sendHeaders();
return true;
}
} else {
unset($this->headers['Last-Modified']);
}
if (ob_get_level()) {
while (@ob_end_clean());
}
if ($this->gzip) {
@ob_start('ob_gzhandler');
} else {
ob_start();
}
$this->sentBytes = 0;
if ($this->isRangeRequest()) {
$this->HTTP->sendStatusCode(206);
$chunks = $this->getChunks();
} else {
$this->HTTP->sendStatusCode(200);
$chunks = array(array(0, $this->size));
if (!$this->gzip && count(ob_list_handlers()) < 2) {
$this->headers['Content-Length'] = $this->size;
}
}
 
if (PEAR::isError($e = $this->sendChunks($chunks))) {
ob_end_clean();
$this->HTTP->sendStatusCode(416);
return $e;
}
ob_end_flush();
flush();
return true;
}
 
/**
* Static send
*
* @see HTTP_Download::HTTP_Download()
* @see HTTP_Download::send()
*
* @static
* @access public
* @return mixed Returns true on success or PEAR_Error on failure.
* @param array $params associative array of parameters
* @param bool $guess whether HTTP_Download::guessContentType()
* should be called
*/
function staticSend($params, $guess = false)
{
$d = &new HTTP_Download();
$e = $d->setParams($params);
if (PEAR::isError($e)) {
return $e;
}
if ($guess) {
$e = $d->guessContentType();
if (PEAR::isError($e)) {
return $e;
}
}
return $d->send();
}
/**
* Send a bunch of files or directories as an archive
*
* Example:
* <code>
* require_once 'HTTP/Download.php';
* HTTP_Download::sendArchive(
* 'myArchive.tgz',
* '/var/ftp/pub/mike',
* HTTP_DOWNLOAD_TGZ,
* '',
* '/var/ftp/pub'
* );
* </code>
*
* @see Archive_Tar::createModify()
* @deprecated use HTTP_Download_Archive::send()
* @static
* @access public
* @return mixed Returns true on success or PEAR_Error on failure.
* @param string $name name the sent archive should have
* @param mixed $files files/directories
* @param string $type archive type
* @param string $add_path path that should be prepended to the files
* @param string $strip_path path that should be stripped from the files
*/
function sendArchive( $name,
$files,
$type = HTTP_DOWNLOAD_TGZ,
$add_path = '',
$strip_path = '')
{
require_once 'HTTP/Download/Archive.php';
return HTTP_Download_Archive::send($name, $files, $type,
$add_path, $strip_path);
}
// }}}
// {{{ protected methods
/**
* Generate ETag
*
* @access protected
* @return string
*/
function generateETag()
{
if (!$this->etag) {
if ($this->data) {
$md5 = md5($this->data);
} else {
$fst = is_resource($this->handle) ?
fstat($this->handle) : stat($this->file);
$md5 = md5($fst['mtime'] .'='. $fst['ino'] .'='. $fst['size']);
}
$this->etag = '"' . $md5 . '-' . crc32($md5) . '"';
}
return $this->etag;
}
/**
* Send multiple chunks
*
* @access protected
* @return mixed Returns true on success or PEAR_Error on failure.
* @param array $chunks
*/
function sendChunks($chunks)
{
if (count($chunks) == 1) {
return $this->sendChunk(current($chunks));
}
 
$bound = uniqid('HTTP_DOWNLOAD-', true);
$cType = $this->headers['Content-Type'];
$this->headers['Content-Type'] =
'multipart/byteranges; boundary=' . $bound;
$this->sendHeaders();
foreach ($chunks as $chunk){
if (PEAR::isError($e = $this->sendChunk($chunk, $cType, $bound))) {
return $e;
}
}
#echo "\r\n--$bound--\r\n";
return true;
}
/**
* Send chunk of data
*
* @access protected
* @return mixed Returns true on success or PEAR_Error on failure.
* @param array $chunk start and end offset of the chunk to send
* @param string $cType actual content type
* @param string $bound boundary for multipart/byteranges
*/
function sendChunk($chunk, $cType = null, $bound = null)
{
list($offset, $lastbyte) = $chunk;
$length = ($lastbyte - $offset) + 1;
if ($length < 1) {
return PEAR::raiseError(
"Error processing range request: $offset-$lastbyte/$length",
HTTP_DOWNLOAD_E_INVALID_REQUEST
);
}
$range = $offset . '-' . $lastbyte . '/' . $this->size;
if (isset($cType, $bound)) {
echo "\r\n--$bound\r\n",
"Content-Type: $cType\r\n",
"Content-Range: bytes $range\r\n\r\n";
} else {
if ($this->isRangeRequest()) {
$this->headers['Content-Length'] = $length;
$this->headers['Content-Range'] = 'bytes '. $range;
}
$this->sendHeaders();
}
 
if ($this->data) {
while (($length -= $this->bufferSize) > 0) {
$this->flush(substr($this->data, $offset, $this->bufferSize));
$this->throttleDelay and $this->sleep();
$offset += $this->bufferSize;
}
if ($length) {
$this->flush(substr($this->data, $offset, $this->bufferSize + $length));
}
} else {
if (!is_resource($this->handle)) {
$this->handle = fopen($this->file, 'rb');
}
fseek($this->handle, $offset);
while (($length -= $this->bufferSize) > 0) {
$this->flush(fread($this->handle, $this->bufferSize));
$this->throttleDelay and $this->sleep();
}
if ($length) {
$this->flush(fread($this->handle, $this->bufferSize + $length));
}
}
return true;
}
/**
* Get chunks to send
*
* @access protected
* @return array
*/
function getChunks()
{
$parts = array();
foreach (explode(',', $this->getRanges()) as $chunk){
list($o, $e) = explode('-', $chunk);
if ($e >= $this->size || (empty($e) && $e !== 0 && $e !== '0')) {
$e = $this->size - 1;
}
if (empty($o) && $o !== 0 && $o !== '0') {
$o = $this->size - $e;
$e = $this->size - 1;
}
$parts[] = array($o, $e);
}
return $parts;
}
/**
* Check if range is requested
*
* @access protected
* @return bool
*/
function isRangeRequest()
{
if (!isset($_SERVER['HTTP_RANGE'])) {
return false;
}
return $this->isValidRange();
}
/**
* Get range request
*
* @access protected
* @return array
*/
function getRanges()
{
return preg_match('/^bytes=((\d*-\d*,? ?)+)$/',
@$_SERVER['HTTP_RANGE'], $matches) ? $matches[1] : array();
}
/**
* Check if entity is cached
*
* @access protected
* @return bool
*/
function isCached()
{
return (
(isset($_SERVER['HTTP_IF_MODIFIED_SINCE']) &&
$this->lastModified == strtotime(current($a = explode(
';', $_SERVER['HTTP_IF_MODIFIED_SINCE'])))) ||
(isset($_SERVER['HTTP_IF_NONE_MATCH']) &&
$this->compareAsterisk('HTTP_IF_NONE_MATCH', $this->etag))
);
}
/**
* Check if entity hasn't changed
*
* @access protected
* @return bool
*/
function isValidRange()
{
if (isset($_SERVER['HTTP_IF_MATCH']) &&
!$this->compareAsterisk('HTTP_IF_MATCH', $this->etag)) {
return false;
}
if (isset($_SERVER['HTTP_IF_RANGE']) &&
$_SERVER['HTTP_IF_RANGE'] !== $this->etag &&
strtotime($_SERVER['HTTP_IF_RANGE']) !== $this->lastModified) {
return false;
}
if (isset($_SERVER['HTTP_IF_UNMODIFIED_SINCE'])) {
$lm = current($a = explode(';', $_SERVER['HTTP_IF_UNMODIFIED_SINCE']));
if (strtotime($lm) !== $this->lastModified) {
return false;
}
}
if (isset($_SERVER['HTTP_UNLESS_MODIFIED_SINCE'])) {
$lm = current($a = explode(';', $_SERVER['HTTP_UNLESS_MODIFIED_SINCE']));
if (strtotime($lm) !== $this->lastModified) {
return false;
}
}
return true;
}
/**
* Compare against an asterisk or check for equality
*
* @access protected
* @return bool
* @param string key for the $_SERVER array
* @param string string to compare
*/
function compareAsterisk($svar, $compare)
{
foreach (array_map('trim', explode(',', $_SERVER[$svar])) as $request) {
if ($request === '*' || $request === $compare) {
return true;
}
}
return false;
}
/**
* Send HTTP headers
*
* @access protected
* @return void
*/
function sendHeaders()
{
foreach ($this->headers as $header => $value) {
$this->HTTP->setHeader($header, $value);
}
$this->HTTP->sendHeaders();
/* NSAPI won't output anything if we did this */
if (strncasecmp(PHP_SAPI, 'nsapi', 5)) {
ob_flush();
flush();
}
}
/**
* Flush
*
* @access protected
* @return void
* @param string $data
*/
function flush($data = '')
{
if ($dlen = strlen($data)) {
$this->sentBytes += $dlen;
echo $data;
}
ob_flush();
flush();
}
/**
* Sleep
*
* @access protected
* @return void
*/
function sleep()
{
if (OS_WINDOWS) {
com_message_pump($this->throttleDelay);
} else {
usleep($this->throttleDelay * 1000);
}
}
// }}}
}
?>
/trunk/applications/jrest/lib/HTTP/HTTP/Header.php
New file
0,0 → 1,531
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
/**
* HTTP::Header
*
* PHP versions 4 and 5
*
* @category HTTP
* @package HTTP_Header
* @author Wolfram Kriesing <wk@visionp.de>
* @author Davey Shafik <davey@php.net>
* @author Michael Wallner <mike@php.net>
* @copyright 2003-2005 The Authors
* @license BSD, revised
* @version CVS: $Id$
* @link http://pear.php.net/package/HTTP_Header
*/
 
/**
* Requires HTTP
*/
require_once 'HTTP.php';
 
/**#@+
* Information Codes
*/
define('HTTP_HEADER_STATUS_100', '100 Continue');
define('HTTP_HEADER_STATUS_101', '101 Switching Protocols');
define('HTTP_HEADER_STATUS_102', '102 Processing');
define('HTTP_HEADER_STATUS_INFORMATIONAL',1);
/**#@-*/
 
/**#+
* Success Codes
*/
define('HTTP_HEADER_STATUS_200', '200 OK');
define('HTTP_HEADER_STATUS_201', '201 Created');
define('HTTP_HEADER_STATUS_202', '202 Accepted');
define('HTTP_HEADER_STATUS_203', '203 Non-Authoritative Information');
define('HTTP_HEADER_STATUS_204', '204 No Content');
define('HTTP_HEADER_STATUS_205', '205 Reset Content');
define('HTTP_HEADER_STATUS_206', '206 Partial Content');
define('HTTP_HEADER_STATUS_207', '207 Multi-Status');
define('HTTP_HEADER_STATUS_SUCCESSFUL',2);
/**#@-*/
 
/**#@+
* Redirection Codes
*/
define('HTTP_HEADER_STATUS_300', '300 Multiple Choices');
define('HTTP_HEADER_STATUS_301', '301 Moved Permanently');
define('HTTP_HEADER_STATUS_302', '302 Found');
define('HTTP_HEADER_STATUS_303', '303 See Other');
define('HTTP_HEADER_STATUS_304', '304 Not Modified');
define('HTTP_HEADER_STATUS_305', '305 Use Proxy');
define('HTTP_HEADER_STATUS_306', '306 (Unused)');
define('HTTP_HEADER_STATUS_307', '307 Temporary Redirect');
define('HTTP_HEADER_STATUS_REDIRECT',3);
/**#@-*/
 
/**#@+
* Error Codes
*/
define('HTTP_HEADER_STATUS_400', '400 Bad Request');
define('HTTP_HEADER_STATUS_401', '401 Unauthorized');
define('HTTP_HEADER_STATUS_402', '402 Payment Granted');
define('HTTP_HEADER_STATUS_403', '403 Forbidden');
define('HTTP_HEADER_STATUS_404', '404 File Not Found');
define('HTTP_HEADER_STATUS_405', '405 Method Not Allowed');
define('HTTP_HEADER_STATUS_406', '406 Not Acceptable');
define('HTTP_HEADER_STATUS_407', '407 Proxy Authentication Required');
define('HTTP_HEADER_STATUS_408', '408 Request Time-out');
define('HTTP_HEADER_STATUS_409', '409 Conflict');
define('HTTP_HEADER_STATUS_410', '410 Gone');
define('HTTP_HEADER_STATUS_411', '411 Length Required');
define('HTTP_HEADER_STATUS_412', '412 Precondition Failed');
define('HTTP_HEADER_STATUS_413', '413 Request Entity Too Large');
define('HTTP_HEADER_STATUS_414', '414 Request-URI Too Large');
define('HTTP_HEADER_STATUS_415', '415 Unsupported Media Type');
define('HTTP_HEADER_STATUS_416', '416 Requested range not satisfiable');
define('HTTP_HEADER_STATUS_417', '417 Expectation Failed');
define('HTTP_HEADER_STATUS_422', '422 Unprocessable Entity');
define('HTTP_HEADER_STATUS_423', '423 Locked');
define('HTTP_HEADER_STATUS_424', '424 Failed Dependency');
define('HTTP_HEADER_STATUS_CLIENT_ERROR',4);
/**#@-*/
 
/**#@+
* Server Errors
*/
define('HTTP_HEADER_STATUS_500', '500 Internal Server Error');
define('HTTP_HEADER_STATUS_501', '501 Not Implemented');
define('HTTP_HEADER_STATUS_502', '502 Bad Gateway');
define('HTTP_HEADER_STATUS_503', '503 Service Unavailable');
define('HTTP_HEADER_STATUS_504', '504 Gateway Time-out');
define('HTTP_HEADER_STATUS_505', '505 HTTP Version not supported');
define('HTTP_HEADER_STATUS_507', '507 Insufficient Storage');
define('HTTP_HEADER_STATUS_SERVER_ERROR',5);
/**#@-*/
 
/**
* HTTP_Header
*
* @package HTTP_Header
* @category HTTP
* @access public
* @version $Revision$
*/
class HTTP_Header extends HTTP
{
/**
* Default Headers
*
* The values that are set as default, are the same as PHP sends by default.
*
* @var array
* @access private
*/
var $_headers = array(
'content-type' => 'text/html',
'pragma' => 'no-cache',
'cache-control' => 'no-store, no-cache, must-revalidate, post-check=0, pre-check=0'
);
 
/**
* HTTP version
*
* @var string
* @access private
*/
var $_httpVersion = '1.0';
 
/**
* Constructor
*
* Sets HTTP version.
*
* @access public
* @return object HTTP_Header
*/
function HTTP_Header()
{
if (isset($_SERVER['SERVER_PROTOCOL'])) {
$this->setHttpVersion(substr($_SERVER['SERVER_PROTOCOL'], -3));
}
}
/**
* Set HTTP version
*
* @access public
* @return bool Returns true on success or false if version doesn't
* match 1.0 or 1.1 (note: 1 will result in 1.0)
* @param mixed $version HTTP version, either 1.0 or 1.1
*/
function setHttpVersion($version)
{
$version = round((float) $version, 1);
if ($version < 1.0 || $version > 1.1) {
return false;
}
$this->_httpVersion = sprintf('%0.1f', $version);
return true;
}
/**
* Get HTTP version
*
* @access public
* @return string
*/
function getHttpVersion()
{
return $this->_httpVersion;
}
/**
* Set Header
*
* The default value for the Last-Modified header will be current
* date and atime if $value is omitted.
*
* @access public
* @return bool Returns true on success or false if $key was empty or
* $value was not of an scalar type.
* @param string $key The name of the header.
* @param string $value The value of the header. (NULL to unset header)
*/
function setHeader($key, $value = null)
{
if (empty($key) || (isset($value) && !is_scalar($value))) {
return false;
}
$key = strToLower($key);
if ($key == 'last-modified') {
if (!isset($value)) {
$value = HTTP::Date(time());
} elseif (is_numeric($value)) {
$value = HTTP::Date($value);
}
}
if (isset($value)) {
$this->_headers[$key] = $value;
} else {
unset($this->_headers[$key]);
}
return true;
}
 
/**
* Get Header
*
* If $key is omitted, all stored headers will be returned.
*
* @access public
* @return mixed Returns string value of the requested header,
* array values of all headers or false if header $key
* is not set.
* @param string $key The name of the header to fetch.
*/
function getHeader($key = null)
{
if (!isset($key)) {
return $this->_headers;
}
$key = strToLower($key);
if (!isset($this->_headers[$key])) {
return false;
}
return $this->_headers[$key];
}
 
/**
* Send Headers
*
* Send out the header that you set via setHeader().
*
* @access public
* @return bool Returns true on success or false if headers are already
* sent.
* @param array $keys Headers to (not) send, see $include.
* @param array $include If true only $keys matching headers will be
* sent, if false only header not matching $keys will be
* sent.
*/
function sendHeaders($keys = array(), $include = true)
{
if (headers_sent()) {
return false;
}
if (count($keys)) {
array_change_key_case($keys, CASE_LOWER);
foreach ($this->_headers as $key => $value) {
if ($include ? in_array($key, $keys) : !in_array($key, $keys)) {
header($key .': '. $value);
}
}
} else {
foreach ($this->_headers as $header => $value) {
header($header .': '. $value);
}
}
return true;
}
 
/**
* Send Satus Code
*
* Send out the given HTTP-Status code. Use this for example when you
* want to tell the client this page is cached, then you would call
* sendStatusCode(304).
*
* @see HTTP_Header_Cache::exitIfCached()
*
* @access public
* @return bool Returns true on success or false if headers are already
* sent.
* @param int $code The status code to send, i.e. 404, 304, 200, etc.
*/
function sendStatusCode($code)
{
if (headers_sent()) {
return false;
}
if ($code == (int) $code && defined('HTTP_HEADER_STATUS_'. $code)) {
$code = constant('HTTP_HEADER_STATUS_'. $code);
}
if (strncasecmp(PHP_SAPI, 'cgi', 3)) {
header('HTTP/'. $this->_httpVersion .' '. $code);
} else {
header('Status: '. $code);
}
return true;
}
 
/**
* Date to Timestamp
*
* Converts dates like
* Mon, 31 Mar 2003 15:26:34 GMT
* Tue, 15 Nov 1994 12:45:26 GMT
* into a timestamp, strtotime() didn't do it in older versions.
*
* @deprecated Use PHPs strtotime() instead.
* @access public
* @return mixed Returns int unix timestamp or false if the date doesn't
* seem to be a valid GMT date.
* @param string $date The GMT date.
*/
function dateToTimestamp($date)
{
static $months = array(
null => 0, 'Jan' => 1, 'Feb' => 2, 'Mar' => 3, 'Apr' => 4,
'May' => 5, 'Jun' => 6, 'Jul' => 7, 'Aug' => 8, 'Sep' => 9,
'Oct' => 10, 'Nov' => 11, 'Dec' => 12
);
if (-1 < $timestamp = strToTime($date)) {
return $timestamp;
}
if (!preg_match('~[^,]*,\s(\d+)\s(\w+)\s(\d+)\s(\d+):(\d+):(\d+).*~',
$date, $m)) {
return false;
}
// [0] => Mon, 31 Mar 2003 15:42:55 GMT
// [1] => 31 [2] => Mar [3] => 2003 [4] => 15 [5] => 42 [6] => 55
return mktime($m[4], $m[5], $m[6], $months[$m[2]], $m[1], $m[3]);
}
 
/**
* Redirect
*
* This function redirects the client. This is done by issuing a Location
* header and exiting. Additionally to HTTP::redirect() you can also add
* parameters to the url.
*
* If you dont need parameters to be added, simply use HTTP::redirect()
* otherwise use HTTP_Header::redirect().
*
* @see HTTP::redirect()
* @author Wolfram Kriesing <wk@visionp.de>
* @access public
* @return void
* @param string $url The URL to redirect to, if none is given it
* redirects to the current page.
* @param array $param Array of query string parameters to add; usually
* a set of key => value pairs; if an array entry consists
* only of an value it is used as key and the respective
* value is fetched from $GLOBALS[$value]
* @param bool $session Whether the session name/id should be added
*/
function redirect($url = null, $param = array(), $session = false)
{
if (!isset($url)) {
$url = $_SERVER['PHP_SELF'];
}
$qs = array();
 
if ($session) {
$qs[] = session_name() .'='. session_id();
}
 
if (is_array($param) && count($param)) {
if (count($param)) {
foreach ($param as $key => $val) {
if (is_string($key)) {
$qs[] = urlencode($key) .'='. urlencode($val);
} else {
$qs[] = urlencode($val) .'='. urlencode(@$GLOBALS[$val]);
}
}
}
}
if ($qstr = implode('&', $qs)) {
$purl = parse_url($url);
$url .= (isset($purl['query']) ? '&' : '?') . $qstr;
}
 
parent::redirect($url);
}
 
/**#@+
* @author Davey Shafik <davey@php.net>
* @param int $http_code HTTP Code to check
* @access public
*/
 
/**
* Return HTTP Status Code Type
*
* @return int|false
*/
function getStatusType($http_code)
{
if(is_int($http_code) && defined('HTTP_HEADER_STATUS_' .$http_code) || defined($http_code)) {
$type = substr($http_code,0,1);
switch ($type) {
case HTTP_HEADER_STATUS_INFORMATIONAL:
case HTTP_HEADER_STATUS_SUCCESSFUL:
case HTTP_HEADER_STATUS_REDIRECT:
case HTTP_HEADER_STATUS_CLIENT_ERROR:
case HTTP_HEADER_STATUS_SERVER_ERROR:
return $type;
break;
default:
return false;
break;
}
} else {
return false;
}
}
 
/**
* Return Status Code Message
*
* @return string|false
*/
function getStatusText($http_code)
{
if ($this->getStatusType($http_code)) {
if (is_int($http_code) && defined('HTTP_HEADER_STATUS_' .$http_code)) {
return substr(constant('HTTP_HEADER_STATUS_' .$http_code),4);
} else {
return substr($http_code,4);
}
} else {
return false;
}
}
 
/**
* Checks if HTTP Status code is Information (1xx)
*
* @return boolean
*/
function isInformational($http_code)
{
if ($status_type = $this->getStatusType($http_code)) {
return $status_type{0} == HTTP_HEADER_STATUS_INFORMATIONAL;
} else {
return false;
}
}
 
/**
* Checks if HTTP Status code is Successful (2xx)
*
* @return boolean
*/
function isSuccessful($http_code)
{
if ($status_type = $this->getStatusType($http_code)) {
return $status_type{0} == HTTP_HEADER_STATUS_SUCCESSFUL;
} else {
return false;
}
}
 
/**
* Checks if HTTP Status code is a Redirect (3xx)
*
* @return boolean
*/
function isRedirect($http_code)
{
if ($status_type = $this->getStatusType($http_code)) {
return $status_type{0} == HTTP_HEADER_STATUS_REDIRECT;
} else {
return false;
}
}
 
/**
* Checks if HTTP Status code is a Client Error (4xx)
*
* @return boolean
*/
function isClientError($http_code)
{
if ($status_type = $this->getStatusType($http_code)) {
return $status_type{0} == HTTP_HEADER_STATUS_CLIENT_ERROR;
} else {
return false;
}
}
 
/**
* Checks if HTTP Status code is Server Error (5xx)
*
* @return boolean
*/
function isServerError($http_code)
{
if ($status_type = $this->getStatusType($http_code)) {
return $status_type{0} == HTTP_HEADER_STATUS_SERVER_ERROR;
} else {
return false;
}
}
 
/**
* Checks if HTTP Status code is Server OR Client Error (4xx or 5xx)
*
* @return boolean
*/
function isError($http_code)
{
if ($status_type = $this->getStatusType($http_code)) {
return (($status_type == HTTP_HEADER_STATUS_CLIENT_ERROR) || ($status_type == HTTP_HEADER_STATUS_SERVER_ERROR)) ? true : false;
} else {
return false;
}
}
/**#@-*/
}
?>
/trunk/applications/jrest/lib/.directory
New file
0,0 → 1,5
[Dolphin]
Timestamp=2010,10,19,15,1,12
 
[Settings]
ShowDotFiles=true
/trunk/applications/jrest/lib/DB/mysql.php
New file
0,0 → 1,916
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
// +----------------------------------------------------------------------+
// | PHP Version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2004 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Author: Stig Bakken <ssb@php.net> |
// | Maintainer: Daniel Convissor <danielc@php.net> |
// +----------------------------------------------------------------------+
//
// $Id$
 
 
// XXX legend:
//
// XXX ERRORMSG: The error message from the mysql function should
// be registered here.
//
// TODO/wishlist:
// longReadlen
// binmode
 
 
require_once 'DB/common.php';
 
/**
* Database independent query interface definition for PHP's MySQL
* extension.
*
* This is for MySQL versions 4.0 and below.
*
* @package DB
* @version $Id$
* @category Database
* @author Stig Bakken <ssb@php.net>
*/
class DB_mysql extends DB_common
{
// {{{ properties
 
var $connection;
var $phptype, $dbsyntax;
var $prepare_tokens = array();
var $prepare_types = array();
var $num_rows = array();
var $transaction_opcount = 0;
var $autocommit = true;
var $fetchmode = DB_FETCHMODE_ORDERED; /* Default fetch mode */
var $_db = false;
 
// }}}
// {{{ constructor
 
/**
* DB_mysql constructor.
*
* @access public
*/
function DB_mysql()
{
$this->DB_common();
$this->phptype = 'mysql';
$this->dbsyntax = 'mysql';
$this->features = array(
'prepare' => false,
'pconnect' => true,
'transactions' => true,
'limit' => 'alter'
);
$this->errorcode_map = array(
1004 => DB_ERROR_CANNOT_CREATE,
1005 => DB_ERROR_CANNOT_CREATE,
1006 => DB_ERROR_CANNOT_CREATE,
1007 => DB_ERROR_ALREADY_EXISTS,
1008 => DB_ERROR_CANNOT_DROP,
1022 => DB_ERROR_ALREADY_EXISTS,
1046 => DB_ERROR_NODBSELECTED,
1048 => DB_ERROR_CONSTRAINT,
1050 => DB_ERROR_ALREADY_EXISTS,
1051 => DB_ERROR_NOSUCHTABLE,
1054 => DB_ERROR_NOSUCHFIELD,
1062 => DB_ERROR_ALREADY_EXISTS,
1064 => DB_ERROR_SYNTAX,
1100 => DB_ERROR_NOT_LOCKED,
1136 => DB_ERROR_VALUE_COUNT_ON_ROW,
1146 => DB_ERROR_NOSUCHTABLE,
1216 => DB_ERROR_CONSTRAINT,
1217 => DB_ERROR_CONSTRAINT,
);
}
 
// }}}
// {{{ connect()
 
/**
* Connect to a database and log in as the specified user.
*
* @param $dsn the data source name (see DB::parseDSN for syntax)
* @param $persistent (optional) whether the connection should
* be persistent
* @access public
* @return int DB_OK on success, a DB error on failure
*/
function connect($dsninfo, $persistent = false)
{
if (!DB::assertExtension('mysql')) {
return $this->raiseError(DB_ERROR_EXTENSION_NOT_FOUND);
}
$this->dsn = $dsninfo;
if ($dsninfo['protocol'] && $dsninfo['protocol'] == 'unix') {
$dbhost = ':' . $dsninfo['socket'];
} else {
$dbhost = $dsninfo['hostspec'] ? $dsninfo['hostspec'] : 'localhost';
if ($dsninfo['port']) {
$dbhost .= ':' . $dsninfo['port'];
}
}
 
$connect_function = $persistent ? 'mysql_pconnect' : 'mysql_connect';
 
if ($dbhost && $dsninfo['username'] && isset($dsninfo['password'])) {
$conn = @$connect_function($dbhost, $dsninfo['username'],
$dsninfo['password']);
} elseif ($dbhost && $dsninfo['username']) {
$conn = @$connect_function($dbhost, $dsninfo['username']);
} elseif ($dbhost) {
$conn = @$connect_function($dbhost);
} else {
$conn = false;
}
if (!$conn) {
if (($err = @mysql_error()) != '') {
return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null,
null, $err);
} elseif (empty($php_errormsg)) {
return $this->raiseError(DB_ERROR_CONNECT_FAILED);
} else {
return $this->raiseError(DB_ERROR_CONNECT_FAILED, null, null,
null, $php_errormsg);
}
}
 
if ($dsninfo['database']) {
if (!@mysql_select_db($dsninfo['database'], $conn)) {
switch(mysql_errno($conn)) {
case 1049:
return $this->raiseError(DB_ERROR_NOSUCHDB, null, null,
null, @mysql_error($conn));
case 1044:
return $this->raiseError(DB_ERROR_ACCESS_VIOLATION, null, null,
null, @mysql_error($conn));
default:
return $this->raiseError(DB_ERROR, null, null,
null, @mysql_error($conn));
}
}
// fix to allow calls to different databases in the same script
$this->_db = $dsninfo['database'];
}
 
$this->connection = $conn;
return DB_OK;
}
 
// }}}
// {{{ disconnect()
 
/**
* Log out and disconnect from the database.
*
* @access public
*
* @return bool true on success, false if not connected.
*/
function disconnect()
{
$ret = @mysql_close($this->connection);
$this->connection = null;
return $ret;
}
 
// }}}
// {{{ simpleQuery()
 
/**
* Send a query to MySQL and return the results as a MySQL resource
* identifier.
*
* @param the SQL query
*
* @access public
*
* @return mixed returns a valid MySQL result for successful SELECT
* queries, DB_OK for other successful queries. A DB error is
* returned on failure.
*/
function simpleQuery($query)
{
$ismanip = DB::isManip($query);
$this->last_query = $query;
$query = $this->modifyQuery($query);
if ($this->_db) {
if (!@mysql_select_db($this->_db, $this->connection)) {
return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
}
}
if (!$this->autocommit && $ismanip) {
if ($this->transaction_opcount == 0) {
$result = @mysql_query('SET AUTOCOMMIT=0', $this->connection);
$result = @mysql_query('BEGIN', $this->connection);
if (!$result) {
return $this->mysqlRaiseError();
}
}
$this->transaction_opcount++;
}
$result = @mysql_query($query, $this->connection);
if (!$result) {
return $this->mysqlRaiseError();
}
if (is_resource($result)) {
$numrows = $this->numrows($result);
if (is_object($numrows)) {
return $numrows;
}
$this->num_rows[(int)$result] = $numrows;
return $result;
}
return DB_OK;
}
 
// }}}
// {{{ nextResult()
 
/**
* Move the internal mysql result pointer to the next available result
*
* This method has not been implemented yet.
*
* @param a valid sql result resource
*
* @access public
*
* @return false
*/
function nextResult($result)
{
return false;
}
 
// }}}
// {{{ fetchInto()
 
/**
* Fetch a row and insert the data into an existing array.
*
* Formating of the array and the data therein are configurable.
* See DB_result::fetchInto() for more information.
*
* @param resource $result query result identifier
* @param array $arr (reference) array where data from the row
* should be placed
* @param int $fetchmode how the resulting array should be indexed
* @param int $rownum the row number to fetch
*
* @return mixed DB_OK on success, null when end of result set is
* reached or on failure
*
* @see DB_result::fetchInto()
* @access private
*/
function fetchInto($result, &$arr, $fetchmode, $rownum=null)
{
if ($rownum !== null) {
if (!@mysql_data_seek($result, $rownum)) {
return null;
}
}
if ($fetchmode & DB_FETCHMODE_ASSOC) {
$arr = @mysql_fetch_array($result, MYSQL_ASSOC);
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE && $arr) {
$arr = array_change_key_case($arr, CASE_LOWER);
}
} else {
$arr = @mysql_fetch_row($result);
}
if (!$arr) {
// See: http://bugs.php.net/bug.php?id=22328
// for why we can't check errors on fetching
return null;
/*
$errno = @mysql_errno($this->connection);
if (!$errno) {
return null;
}
return $this->mysqlRaiseError($errno);
*/
}
if ($this->options['portability'] & DB_PORTABILITY_RTRIM) {
/*
* Even though this DBMS already trims output, we do this because
* a field might have intentional whitespace at the end that
* gets removed by DB_PORTABILITY_RTRIM under another driver.
*/
$this->_rtrimArrayValues($arr);
}
if ($this->options['portability'] & DB_PORTABILITY_NULL_TO_EMPTY) {
$this->_convertNullArrayValuesToEmpty($arr);
}
return DB_OK;
}
 
// }}}
// {{{ freeResult()
 
/**
* Free the internal resources associated with $result.
*
* @param $result MySQL result identifier
*
* @access public
*
* @return bool true on success, false if $result is invalid
*/
function freeResult($result)
{
unset($this->num_rows[(int)$result]);
return @mysql_free_result($result);
}
 
// }}}
// {{{ numCols()
 
/**
* Get the number of columns in a result set.
*
* @param $result MySQL result identifier
*
* @access public
*
* @return int the number of columns per row in $result
*/
function numCols($result)
{
$cols = @mysql_num_fields($result);
 
if (!$cols) {
return $this->mysqlRaiseError();
}
 
return $cols;
}
 
// }}}
// {{{ numRows()
 
/**
* Get the number of rows in a result set.
*
* @param $result MySQL result identifier
*
* @access public
*
* @return int the number of rows in $result
*/
function numRows($result)
{
$rows = @mysql_num_rows($result);
if ($rows === null) {
return $this->mysqlRaiseError();
}
return $rows;
}
 
// }}}
// {{{ autoCommit()
 
/**
* Enable/disable automatic commits
*/
function autoCommit($onoff = false)
{
// XXX if $this->transaction_opcount > 0, we should probably
// issue a warning here.
$this->autocommit = $onoff ? true : false;
return DB_OK;
}
 
// }}}
// {{{ commit()
 
/**
* Commit the current transaction.
*/
function commit()
{
if ($this->transaction_opcount > 0) {
if ($this->_db) {
if (!@mysql_select_db($this->_db, $this->connection)) {
return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
}
}
$result = @mysql_query('COMMIT', $this->connection);
$result = @mysql_query('SET AUTOCOMMIT=1', $this->connection);
$this->transaction_opcount = 0;
if (!$result) {
return $this->mysqlRaiseError();
}
}
return DB_OK;
}
 
// }}}
// {{{ rollback()
 
/**
* Roll back (undo) the current transaction.
*/
function rollback()
{
if ($this->transaction_opcount > 0) {
if ($this->_db) {
if (!@mysql_select_db($this->_db, $this->connection)) {
return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
}
}
$result = @mysql_query('ROLLBACK', $this->connection);
$result = @mysql_query('SET AUTOCOMMIT=1', $this->connection);
$this->transaction_opcount = 0;
if (!$result) {
return $this->mysqlRaiseError();
}
}
return DB_OK;
}
 
// }}}
// {{{ affectedRows()
 
/**
* Gets the number of rows affected by the data manipulation
* query. For other queries, this function returns 0.
*
* @return number of rows affected by the last query
*/
function affectedRows()
{
if (DB::isManip($this->last_query)) {
return @mysql_affected_rows($this->connection);
} else {
return 0;
}
}
 
// }}}
// {{{ errorNative()
 
/**
* Get the native error code of the last error (if any) that
* occured on the current connection.
*
* @access public
*
* @return int native MySQL error code
*/
function errorNative()
{
return @mysql_errno($this->connection);
}
 
// }}}
// {{{ nextId()
 
/**
* Returns the next free id in a sequence
*
* @param string $seq_name name of the sequence
* @param boolean $ondemand when true, the seqence is automatically
* created if it does not exist
*
* @return int the next id number in the sequence. DB_Error if problem.
*
* @internal
* @see DB_common::nextID()
* @access public
*/
function nextId($seq_name, $ondemand = true)
{
$seqname = $this->getSequenceName($seq_name);
do {
$repeat = 0;
$this->pushErrorHandling(PEAR_ERROR_RETURN);
$result = $this->query("UPDATE ${seqname} ".
'SET id=LAST_INSERT_ID(id+1)');
$this->popErrorHandling();
if ($result === DB_OK) {
/** COMMON CASE **/
$id = @mysql_insert_id($this->connection);
if ($id != 0) {
return $id;
}
/** EMPTY SEQ TABLE **/
// Sequence table must be empty for some reason, so fill it and return 1
// Obtain a user-level lock
$result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
if (DB::isError($result)) {
return $this->raiseError($result);
}
if ($result == 0) {
// Failed to get the lock, bail with a DB_ERROR_NOT_LOCKED error
return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
}
 
// add the default value
$result = $this->query("REPLACE INTO ${seqname} (id) VALUES (0)");
if (DB::isError($result)) {
return $this->raiseError($result);
}
 
// Release the lock
$result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
if (DB::isError($result)) {
return $this->raiseError($result);
}
// We know what the result will be, so no need to try again
return 1;
 
/** ONDEMAND TABLE CREATION **/
} elseif ($ondemand && DB::isError($result) &&
$result->getCode() == DB_ERROR_NOSUCHTABLE)
{
$result = $this->createSequence($seq_name);
if (DB::isError($result)) {
return $this->raiseError($result);
} else {
$repeat = 1;
}
 
/** BACKWARDS COMPAT **/
} elseif (DB::isError($result) &&
$result->getCode() == DB_ERROR_ALREADY_EXISTS)
{
// see _BCsequence() comment
$result = $this->_BCsequence($seqname);
if (DB::isError($result)) {
return $this->raiseError($result);
}
$repeat = 1;
}
} while ($repeat);
 
return $this->raiseError($result);
}
 
// }}}
// {{{ createSequence()
 
/**
* Creates a new sequence
*
* @param string $seq_name name of the new sequence
*
* @return int DB_OK on success. A DB_Error object is returned if
* problems arise.
*
* @internal
* @see DB_common::createSequence()
* @access public
*/
function createSequence($seq_name)
{
$seqname = $this->getSequenceName($seq_name);
$res = $this->query("CREATE TABLE ${seqname} ".
'(id INTEGER UNSIGNED AUTO_INCREMENT NOT NULL,'.
' PRIMARY KEY(id))');
if (DB::isError($res)) {
return $res;
}
// insert yields value 1, nextId call will generate ID 2
$res = $this->query("INSERT INTO ${seqname} (id) VALUES (0)");
if (DB::isError($res)) {
return $res;
}
// so reset to zero
return $this->query("UPDATE ${seqname} SET id = 0;");
}
 
// }}}
// {{{ dropSequence()
 
/**
* Deletes a sequence
*
* @param string $seq_name name of the sequence to be deleted
*
* @return int DB_OK on success. DB_Error if problems.
*
* @internal
* @see DB_common::dropSequence()
* @access public
*/
function dropSequence($seq_name)
{
return $this->query('DROP TABLE ' . $this->getSequenceName($seq_name));
}
 
// }}}
// {{{ _BCsequence()
 
/**
* Backwards compatibility with old sequence emulation implementation
* (clean up the dupes)
*
* @param string $seqname The sequence name to clean up
* @return mixed DB_Error or true
*/
function _BCsequence($seqname)
{
// Obtain a user-level lock... this will release any previous
// application locks, but unlike LOCK TABLES, it does not abort
// the current transaction and is much less frequently used.
$result = $this->getOne("SELECT GET_LOCK('${seqname}_lock',10)");
if (DB::isError($result)) {
return $result;
}
if ($result == 0) {
// Failed to get the lock, can't do the conversion, bail
// with a DB_ERROR_NOT_LOCKED error
return $this->mysqlRaiseError(DB_ERROR_NOT_LOCKED);
}
 
$highest_id = $this->getOne("SELECT MAX(id) FROM ${seqname}");
if (DB::isError($highest_id)) {
return $highest_id;
}
// This should kill all rows except the highest
// We should probably do something if $highest_id isn't
// numeric, but I'm at a loss as how to handle that...
$result = $this->query("DELETE FROM ${seqname} WHERE id <> $highest_id");
if (DB::isError($result)) {
return $result;
}
 
// If another thread has been waiting for this lock,
// it will go thru the above procedure, but will have no
// real effect
$result = $this->getOne("SELECT RELEASE_LOCK('${seqname}_lock')");
if (DB::isError($result)) {
return $result;
}
return true;
}
 
// }}}
// {{{ quoteIdentifier()
 
/**
* Quote a string so it can be safely used as a table or column name
*
* Quoting style depends on which database driver is being used.
*
* MySQL can't handle the backtick character (<kbd>`</kbd>) in
* table or column names.
*
* @param string $str identifier name to be quoted
*
* @return string quoted identifier string
*
* @since 1.6.0
* @access public
* @internal
*/
function quoteIdentifier($str)
{
return '`' . $str . '`';
}
 
// }}}
// {{{ quote()
 
/**
* @deprecated Deprecated in release 1.6.0
* @internal
*/
function quote($str) {
return $this->quoteSmart($str);
}
 
// }}}
// {{{ escapeSimple()
 
/**
* Escape a string according to the current DBMS's standards
*
* @param string $str the string to be escaped
*
* @return string the escaped string
*
* @internal
*/
function escapeSimple($str) {
if (function_exists('mysql_real_escape_string')) {
return @mysql_real_escape_string($str, $this->connection);
} else {
return @mysql_escape_string($str);
}
}
 
// }}}
// {{{ modifyQuery()
 
function modifyQuery($query)
{
if ($this->options['portability'] & DB_PORTABILITY_DELETE_COUNT) {
// "DELETE FROM table" gives 0 affected rows in MySQL.
// This little hack lets you know how many rows were deleted.
if (preg_match('/^\s*DELETE\s+FROM\s+(\S+)\s*$/i', $query)) {
$query = preg_replace('/^\s*DELETE\s+FROM\s+(\S+)\s*$/',
'DELETE FROM \1 WHERE 1=1', $query);
}
}
return $query;
}
 
// }}}
// {{{ modifyLimitQuery()
 
function modifyLimitQuery($query, $from, $count, $params = array())
{
if (DB::isManip($query)) {
return $query . " LIMIT $count";
} else {
return $query . " LIMIT $from, $count";
}
}
 
// }}}
// {{{ mysqlRaiseError()
 
/**
* Gather information about an error, then use that info to create a
* DB error object and finally return that object.
*
* @param integer $errno PEAR error number (usually a DB constant) if
* manually raising an error
* @return object DB error object
* @see DB_common::errorCode()
* @see DB_common::raiseError()
*/
function mysqlRaiseError($errno = null)
{
if ($errno === null) {
if ($this->options['portability'] & DB_PORTABILITY_ERRORS) {
$this->errorcode_map[1022] = DB_ERROR_CONSTRAINT;
$this->errorcode_map[1048] = DB_ERROR_CONSTRAINT_NOT_NULL;
$this->errorcode_map[1062] = DB_ERROR_CONSTRAINT;
} else {
// Doing this in case mode changes during runtime.
$this->errorcode_map[1022] = DB_ERROR_ALREADY_EXISTS;
$this->errorcode_map[1048] = DB_ERROR_CONSTRAINT;
$this->errorcode_map[1062] = DB_ERROR_ALREADY_EXISTS;
}
$errno = $this->errorCode(mysql_errno($this->connection));
}
return $this->raiseError($errno, null, null, null,
@mysql_errno($this->connection) . ' ** ' .
@mysql_error($this->connection));
}
 
// }}}
// {{{ tableInfo()
 
/**
* Returns information about a table or a result set.
*
* @param object|string $result DB_result object from a query or a
* string containing the name of a table
* @param int $mode a valid tableInfo mode
* @return array an associative array with the information requested
* or an error object if something is wrong
* @access public
* @internal
* @see DB_common::tableInfo()
*/
function tableInfo($result, $mode = null) {
if (isset($result->result)) {
/*
* Probably received a result object.
* Extract the result resource identifier.
*/
$id = $result->result;
$got_string = false;
} elseif (is_string($result)) {
/*
* Probably received a table name.
* Create a result resource identifier.
*/
$id = @mysql_list_fields($this->dsn['database'],
$result, $this->connection);
$got_string = true;
} else {
/*
* Probably received a result resource identifier.
* Copy it.
* Deprecated. Here for compatibility only.
*/
$id = $result;
$got_string = false;
}
 
if (!is_resource($id)) {
return $this->mysqlRaiseError(DB_ERROR_NEED_MORE_DATA);
}
 
if ($this->options['portability'] & DB_PORTABILITY_LOWERCASE) {
$case_func = 'strtolower';
} else {
$case_func = 'strval';
}
 
$count = @mysql_num_fields($id);
 
// made this IF due to performance (one if is faster than $count if's)
if (!$mode) {
for ($i=0; $i<$count; $i++) {
$res[$i]['table'] = $case_func(@mysql_field_table($id, $i));
$res[$i]['name'] = $case_func(@mysql_field_name($id, $i));
$res[$i]['type'] = @mysql_field_type($id, $i);
$res[$i]['len'] = @mysql_field_len($id, $i);
$res[$i]['flags'] = @mysql_field_flags($id, $i);
}
} else { // full
$res['num_fields']= $count;
 
for ($i=0; $i<$count; $i++) {
$res[$i]['table'] = $case_func(@mysql_field_table($id, $i));
$res[$i]['name'] = $case_func(@mysql_field_name($id, $i));
$res[$i]['type'] = @mysql_field_type($id, $i);
$res[$i]['len'] = @mysql_field_len($id, $i);
$res[$i]['flags'] = @mysql_field_flags($id, $i);
 
if ($mode & DB_TABLEINFO_ORDER) {
$res['order'][$res[$i]['name']] = $i;
}
if ($mode & DB_TABLEINFO_ORDERTABLE) {
$res['ordertable'][$res[$i]['table']][$res[$i]['name']] = $i;
}
}
}
 
// free the result only if we were called on a table
if ($got_string) {
@mysql_free_result($id);
}
return $res;
}
 
// }}}
// {{{ getSpecialQuery()
 
/**
* Returns the query needed to get some backend info
* @param string $type What kind of info you want to retrieve
* @return string The SQL query string
*/
function getSpecialQuery($type)
{
switch ($type) {
case 'tables':
return 'SHOW TABLES';
case 'views':
return DB_ERROR_NOT_CAPABLE;
case 'users':
$sql = 'select distinct User from user';
if ($this->dsn['database'] != 'mysql') {
$dsn = $this->dsn;
$dsn['database'] = 'mysql';
if (DB::isError($db = DB::connect($dsn))) {
return $db;
}
$sql = $db->getCol($sql);
$db->disconnect();
// XXX Fixme the mysql driver should take care of this
if (!@mysql_select_db($this->dsn['database'], $this->connection)) {
return $this->mysqlRaiseError(DB_ERROR_NODBSELECTED);
}
}
return $sql;
case 'databases':
return 'SHOW DATABASES';
default:
return null;
}
}
 
// }}}
 
}
 
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/
 
?>
/trunk/applications/jrest/lib/DB/common.php
New file
0,0 → 1,2040
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
// +----------------------------------------------------------------------+
// | PHP Version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2004 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Author: Stig Bakken <ssb@php.net> |
// | Tomas V.V.Cox <cox@idecnet.com> |
// | Maintainer: Daniel Convissor <danielc@php.net> |
// +----------------------------------------------------------------------+
//
// $Id$
 
require_once 'PEAR.php';
 
/**
* DB_common is a base class for DB implementations, and must be
* inherited by all such
*
* @package DB
* @version $Id$
* @category Database
* @author Stig Bakken <ssb@php.net>
* @author Tomas V.V.Cox <cox@idecnet.com>
*/
class DB_common extends PEAR
{
// {{{ properties
 
/**
* assoc of capabilities for this DB implementation
* $features['limit'] => 'emulate' => emulate with fetch row by number
* 'alter' => alter the query
* false => skip rows
* @var array
*/
var $features = array();
 
/**
* assoc mapping native error codes to DB ones
* @var array
*/
var $errorcode_map = array();
 
/**
* DB type (mysql, oci8, odbc etc.)
* @var string
*/
var $phptype;
 
/**
* @var string
*/
var $prepare_tokens;
 
/**
* @var string
*/
var $prepare_types;
 
/**
* @var string
*/
var $prepared_queries;
 
/**
* @var integer
*/
var $prepare_maxstmt = 0;
 
/**
* @var string
*/
var $last_query = '';
 
/**
* @var integer
*/
var $fetchmode = DB_FETCHMODE_ORDERED;
 
/**
* @var string
*/
var $fetchmode_object_class = 'stdClass';
 
/**
* Run-time configuration options.
*
* The 'optimize' option has been deprecated. Use the 'portability'
* option instead.
*
* @see DB_common::setOption()
* @var array
*/
var $options = array(
'persistent' => false,
'ssl' => false,
'debug' => 0,
'seqname_format' => '%s_seq',
'autofree' => false,
'portability' => DB_PORTABILITY_NONE,
'optimize' => 'performance', // Deprecated. Use 'portability'.
);
 
/**
* DB handle
* @var resource
*/
var $dbh;
 
// }}}
// {{{ toString()
 
/**
* String conversation
*
* @return string
* @access private
*/
function toString()
{
$info = strtolower(get_class($this));
$info .= ': (phptype=' . $this->phptype .
', dbsyntax=' . $this->dbsyntax .
')';
 
if ($this->connection) {
$info .= ' [connected]';
}
 
return $info;
}
 
// }}}
// {{{ constructor
 
/**
* Constructor
*/
function DB_common()
{
$this->PEAR('DB_Error');
}
 
// }}}
// {{{ quoteString()
 
/**
* DEPRECATED: Quotes a string so it can be safely used within string
* delimiters in a query
*
* @return string quoted string
*
* @see DB_common::quoteSmart(), DB_common::escapeSimple()
* @deprecated Deprecated in release 1.2 or lower
* @internal
*/
function quoteString($string)
{
$string = $this->quote($string);
if ($string{0} == "'") {
return substr($string, 1, -1);
}
return $string;
}
 
// }}}
// {{{ quote()
 
/**
* DEPRECATED: Quotes a string so it can be safely used in a query
*
* @param string $string the input string to quote
*
* @return string The NULL string or the string quotes
* in magic_quote_sybase style
*
* @see DB_common::quoteSmart(), DB_common::escapeSimple()
* @deprecated Deprecated in release 1.6.0
* @internal
*/
function quote($string = null)
{
return ($string === null) ? 'NULL' : "'".str_replace("'", "''", $string)."'";
}
 
// }}}
// {{{ quoteIdentifier()
 
/**
* Quote a string so it can be safely used as a table or column name
*
* Delimiting style depends on which database driver is being used.
*
* NOTE: just because you CAN use delimited identifiers doesn't mean
* you SHOULD use them. In general, they end up causing way more
* problems than they solve.
*
* Portability is broken by using the following characters inside
* delimited identifiers:
* + backtick (<kbd>`</kbd>) -- due to MySQL
* + double quote (<kbd>"</kbd>) -- due to Oracle
* + brackets (<kbd>[</kbd> or <kbd>]</kbd>) -- due to Access
*
* Delimited identifiers are known to generally work correctly under
* the following drivers:
* + mssql
* + mysql
* + mysqli
* + oci8
* + odbc(access)
* + odbc(db2)
* + pgsql
* + sqlite
* + sybase
*
* InterBase doesn't seem to be able to use delimited identifiers
* via PHP 4. They work fine under PHP 5.
*
* @param string $str identifier name to be quoted
*
* @return string quoted identifier string
*
* @since 1.6.0
* @access public
*/
function quoteIdentifier($str)
{
return '"' . str_replace('"', '""', $str) . '"';
}
 
// }}}
// {{{ quoteSmart()
 
/**
* Format input so it can be safely used in a query
*
* The output depends on the PHP data type of input and the database
* type being used.
*
* @param mixed $in data to be quoted
*
* @return mixed the format of the results depends on the input's
* PHP type:
*
* <ul>
* <li>
* <kbd>input</kbd> -> <samp>returns</samp>
* </li>
* <li>
* <kbd>null</kbd> -> the string <samp>NULL</samp>
* </li>
* <li>
* <kbd>integer</kbd> or <kbd>double</kbd> -> the unquoted number
* </li>
* <li>
* &type.bool; -> output depends on the driver in use
* Most drivers return integers: <samp>1</samp> if
* <kbd>true</kbd> or <samp>0</samp> if
* <kbd>false</kbd>.
* Some return strings: <samp>TRUE</samp> if
* <kbd>true</kbd> or <samp>FALSE</samp> if
* <kbd>false</kbd>.
* Finally one returns strings: <samp>T</samp> if
* <kbd>true</kbd> or <samp>F</samp> if
* <kbd>false</kbd>. Here is a list of each DBMS,
* the values returned and the suggested column type:
* <ul>
* <li>
* <kbd>dbase</kbd> -> <samp>T/F</samp>
* (<kbd>Logical</kbd>)
* </li>
* <li>
* <kbd>fbase</kbd> -> <samp>TRUE/FALSE</samp>
* (<kbd>BOOLEAN</kbd>)
* </li>
* <li>
* <kbd>ibase</kbd> -> <samp>1/0</samp>
* (<kbd>SMALLINT</kbd>) [1]
* </li>
* <li>
* <kbd>ifx</kbd> -> <samp>1/0</samp>
* (<kbd>SMALLINT</kbd>) [1]
* </li>
* <li>
* <kbd>msql</kbd> -> <samp>1/0</samp>
* (<kbd>INTEGER</kbd>)
* </li>
* <li>
* <kbd>mssql</kbd> -> <samp>1/0</samp>
* (<kbd>BIT</kbd>)
* </li>
* <li>
* <kbd>mysql</kbd> -> <samp>1/0</samp>
* (<kbd>TINYINT(1)</kbd>)
* </li>
* <li>
* <kbd>mysqli</kbd> -> <samp>1/0</samp>
* (<kbd>TINYINT(1)</kbd>)
* </li>
* <li>
* <kbd>oci8</kbd> -> <samp>1/0</samp>
* (<kbd>NUMBER(1)</kbd>)
* </li>
* <li>
* <kbd>odbc</kbd> -> <samp>1/0</samp>
* (<kbd>SMALLINT</kbd>) [1]
* </li>
* <li>
* <kbd>pgsql</kbd> -> <samp>TRUE/FALSE</samp>
* (<kbd>BOOLEAN</kbd>)
* </li>
* <li>
* <kbd>sqlite</kbd> -> <samp>1/0</samp>
* (<kbd>INTEGER</kbd>)
* </li>
* <li>
* <kbd>sybase</kbd> -> <samp>1/0</samp>
* (<kbd>TINYINT(1)</kbd>)
* </li>
* </ul>
* [1] Accommodate the lowest common denominator because not all
* versions of have <kbd>BOOLEAN</kbd>.
* </li>
* <li>
* other (including strings and numeric strings) ->
* the data with single quotes escaped by preceeding
* single quotes, backslashes are escaped by preceeding
* backslashes, then the whole string is encapsulated
* between single quotes
* </li>
* </ul>
*
* @since 1.6.0
* @see DB_common::escapeSimple()
* @access public
*/
function quoteSmart($in)
{
if (is_int($in) || is_double($in)) {
return $in;
} elseif (is_bool($in)) {
return $in ? 1 : 0;
} elseif (is_null($in)) {
return 'NULL';
} else {
return "'" . $this->escapeSimple($in) . "'";
}
}
 
// }}}
// {{{ escapeSimple()
 
/**
* Escape a string according to the current DBMS's standards
*
* In SQLite, this makes things safe for inserts/updates, but may
* cause problems when performing text comparisons against columns
* containing binary data. See the
* {@link http://php.net/sqlite_escape_string PHP manual} for more info.
*
* @param string $str the string to be escaped
*
* @return string the escaped string
*
* @since 1.6.0
* @see DB_common::quoteSmart()
* @access public
*/
function escapeSimple($str) {
return str_replace("'", "''", $str);
}
 
// }}}
// {{{ provides()
 
/**
* Tell whether a DB implementation or its backend extension
* supports a given feature
*
* @param array $feature name of the feature (see the DB class doc)
* @return bool whether this DB implementation supports $feature
* @access public
*/
function provides($feature)
{
return $this->features[$feature];
}
 
// }}}
// {{{ errorCode()
 
/**
* Map native error codes to DB's portable ones
*
* Requires that the DB implementation's constructor fills
* in the <var>$errorcode_map</var> property.
*
* @param mixed $nativecode the native error code, as returned by the
* backend database extension (string or integer)
*
* @return int a portable DB error code, or DB_ERROR if this DB
* implementation has no mapping for the given error code.
*
* @access public
*/
function errorCode($nativecode)
{
if (isset($this->errorcode_map[$nativecode])) {
return $this->errorcode_map[$nativecode];
}
// Fall back to DB_ERROR if there was no mapping.
return DB_ERROR;
}
 
// }}}
// {{{ errorMessage()
 
/**
* Map a DB error code to a textual message. This is actually
* just a wrapper for DB::errorMessage()
*
* @param integer $dbcode the DB error code
*
* @return string the corresponding error message, of false
* if the error code was unknown
*
* @access public
*/
function errorMessage($dbcode)
{
return DB::errorMessage($this->errorcode_map[$dbcode]);
}
 
// }}}
// {{{ raiseError()
 
/**
* Communicate an error and invoke error callbacks, etc
*
* Basically a wrapper for PEAR::raiseError without the message string.
*
* @param mixed integer error code, or a PEAR error object (all
* other parameters are ignored if this parameter is
* an object
*
* @param int error mode, see PEAR_Error docs
*
* @param mixed If error mode is PEAR_ERROR_TRIGGER, this is the
* error level (E_USER_NOTICE etc). If error mode is
* PEAR_ERROR_CALLBACK, this is the callback function,
* either as a function name, or as an array of an
* object and method name. For other error modes this
* parameter is ignored.
*
* @param string Extra debug information. Defaults to the last
* query and native error code.
*
* @param mixed Native error code, integer or string depending the
* backend.
*
* @return object a PEAR error object
*
* @access public
* @see PEAR_Error
*/
function &raiseError($code = DB_ERROR, $mode = null, $options = null,
$userinfo = null, $nativecode = null)
{
// The error is yet a DB error object
if (is_object($code)) {
// because we the static PEAR::raiseError, our global
// handler should be used if it is set
if ($mode === null && !empty($this->_default_error_mode)) {
$mode = $this->_default_error_mode;
$options = $this->_default_error_options;
}
$tmp = PEAR::raiseError($code, null, $mode, $options, null, null, true);
return $tmp;
}
 
if ($userinfo === null) {
$userinfo = $this->last_query;
}
 
if ($nativecode) {
$userinfo .= ' [nativecode=' . trim($nativecode) . ']';
}
 
$tmp = PEAR::raiseError(null, $code, $mode, $options, $userinfo,
'DB_Error', true);
return $tmp;
}
 
// }}}
// {{{ setFetchMode()
 
/**
* Sets which fetch mode should be used by default on queries
* on this connection
*
* @param integer $fetchmode DB_FETCHMODE_ORDERED or
* DB_FETCHMODE_ASSOC, possibly bit-wise OR'ed with
* DB_FETCHMODE_FLIPPED.
*
* @param string $object_class The class of the object
* to be returned by the fetch methods when
* the DB_FETCHMODE_OBJECT mode is selected.
* If no class is specified by default a cast
* to object from the assoc array row will be done.
* There is also the posibility to use and extend the
* 'DB_row' class.
*
* @see DB_FETCHMODE_ORDERED
* @see DB_FETCHMODE_ASSOC
* @see DB_FETCHMODE_FLIPPED
* @see DB_FETCHMODE_OBJECT
* @see DB_row::DB_row()
* @access public
*/
function setFetchMode($fetchmode, $object_class = 'stdClass')
{
switch ($fetchmode) {
case DB_FETCHMODE_OBJECT:
$this->fetchmode_object_class = $object_class;
case DB_FETCHMODE_ORDERED:
case DB_FETCHMODE_ASSOC:
$this->fetchmode = $fetchmode;
break;
default:
return $this->raiseError('invalid fetchmode mode');
}
}
 
// }}}
// {{{ setOption()
 
/**
* Set run-time configuration options for PEAR DB
*
* Options, their data types, default values and description:
* <ul>
* <li>
* <var>autofree</var> <kbd>boolean</kbd> = <samp>false</samp>
* <br />should results be freed automatically when there are no
* more rows?
* </li><li>
* <var>debug</var> <kbd>integer</kbd> = <samp>0</samp>
* <br />debug level
* </li><li>
* <var>persistent</var> <kbd>boolean</kbd> = <samp>false</samp>
* <br />should the connection be persistent?
* </li><li>
* <var>portability</var> <kbd>integer</kbd> = <samp>DB_PORTABILITY_NONE</samp>
* <br />portability mode constant (see below)
* </li><li>
* <var>seqname_format</var> <kbd>string</kbd> = <samp>%s_seq</samp>
* <br />the sprintf() format string used on sequence names. This
* format is applied to sequence names passed to
* createSequence(), nextID() and dropSequence().
* </li><li>
* <var>ssl</var> <kbd>boolean</kbd> = <samp>false</samp>
* <br />use ssl to connect?
* </li>
* </ul>
*
* -----------------------------------------
*
* PORTABILITY MODES
*
* These modes are bitwised, so they can be combined using <kbd>|</kbd>
* and removed using <kbd>^</kbd>. See the examples section below on how
* to do this.
*
* <samp>DB_PORTABILITY_NONE</samp>
* turn off all portability features
*
* This mode gets automatically turned on if the deprecated
* <var>optimize</var> option gets set to <samp>performance</samp>.
*
*
* <samp>DB_PORTABILITY_LOWERCASE</samp>
* convert names of tables and fields to lower case when using
* <kbd>get*()</kbd>, <kbd>fetch*()</kbd> and <kbd>tableInfo()</kbd>
*
* This mode gets automatically turned on in the following databases
* if the deprecated option <var>optimize</var> gets set to
* <samp>portability</samp>:
* + oci8
*
*
* <samp>DB_PORTABILITY_RTRIM</samp>
* right trim the data output by <kbd>get*()</kbd> <kbd>fetch*()</kbd>
*
*
* <samp>DB_PORTABILITY_DELETE_COUNT</samp>
* force reporting the number of rows deleted
*
* Some DBMS's don't count the number of rows deleted when performing
* simple <kbd>DELETE FROM tablename</kbd> queries. This portability
* mode tricks such DBMS's into telling the count by adding
* <samp>WHERE 1=1</samp> to the end of <kbd>DELETE</kbd> queries.
*
* This mode gets automatically turned on in the following databases
* if the deprecated option <var>optimize</var> gets set to
* <samp>portability</samp>:
* + fbsql
* + mysql
* + mysqli
* + sqlite
*
*
* <samp>DB_PORTABILITY_NUMROWS</samp>
* enable hack that makes <kbd>numRows()</kbd> work in Oracle
*
* This mode gets automatically turned on in the following databases
* if the deprecated option <var>optimize</var> gets set to
* <samp>portability</samp>:
* + oci8
*
*
* <samp>DB_PORTABILITY_ERRORS</samp>
* makes certain error messages in certain drivers compatible
* with those from other DBMS's
*
* + mysql, mysqli: change unique/primary key constraints
* DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT
*
* + odbc(access): MS's ODBC driver reports 'no such field' as code
* 07001, which means 'too few parameters.' When this option is on
* that code gets mapped to DB_ERROR_NOSUCHFIELD.
* DB_ERROR_MISMATCH -> DB_ERROR_NOSUCHFIELD
*
*
* <samp>DB_PORTABILITY_NULL_TO_EMPTY</samp>
* convert null values to empty strings in data output by get*() and
* fetch*(). Needed because Oracle considers empty strings to be null,
* while most other DBMS's know the difference between empty and null.
*
*
* <samp>DB_PORTABILITY_ALL</samp>
* turn on all portability features
*
* -----------------------------------------
*
* Example 1. Simple setOption() example
* <code> <?php
* $dbh->setOption('autofree', true);
* ?></code>
*
* Example 2. Portability for lowercasing and trimming
* <code> <?php
* $dbh->setOption('portability',
* DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_RTRIM);
* ?></code>
*
* Example 3. All portability options except trimming
* <code> <?php
* $dbh->setOption('portability',
* DB_PORTABILITY_ALL ^ DB_PORTABILITY_RTRIM);
* ?></code>
*
* @param string $option option name
* @param mixed $value value for the option
*
* @return int DB_OK on success. DB_Error object on failure.
*
* @see DB_common::$options
*/
function setOption($option, $value)
{
if (isset($this->options[$option])) {
$this->options[$option] = $value;
 
/*
* Backwards compatibility check for the deprecated 'optimize'
* option. Done here in case settings change after connecting.
*/
if ($option == 'optimize') {
if ($value == 'portability') {
switch ($this->phptype) {
case 'oci8':
$this->options['portability'] =
DB_PORTABILITY_LOWERCASE |
DB_PORTABILITY_NUMROWS;
break;
case 'fbsql':
case 'mysql':
case 'mysqli':
case 'sqlite':
$this->options['portability'] =
DB_PORTABILITY_DELETE_COUNT;
break;
}
} else {
$this->options['portability'] = DB_PORTABILITY_NONE;
}
}
 
return DB_OK;
}
return $this->raiseError("unknown option $option");
}
 
// }}}
// {{{ getOption()
 
/**
* Returns the value of an option
*
* @param string $option option name
*
* @return mixed the option value
*/
function getOption($option)
{
if (isset($this->options[$option])) {
return $this->options[$option];
}
return $this->raiseError("unknown option $option");
}
 
// }}}
// {{{ prepare()
 
/**
* Prepares a query for multiple execution with execute()
*
* Creates a query that can be run multiple times. Each time it is run,
* the placeholders, if any, will be replaced by the contents of
* execute()'s $data argument.
*
* Three types of placeholders can be used:
* + <kbd>?</kbd> scalar value (i.e. strings, integers). The system
* will automatically quote and escape the data.
* + <kbd>!</kbd> value is inserted 'as is'
* + <kbd>&</kbd> requires a file name. The file's contents get
* inserted into the query (i.e. saving binary
* data in a db)
*
* Example 1.
* <code> <?php
* $sth = $dbh->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)');
* $data = array(
* "John's text",
* "'it''s good'",
* 'filename.txt'
* );
* $res = $dbh->execute($sth, $data);
* ?></code>
*
* Use backslashes to escape placeholder characters if you don't want
* them to be interpreted as placeholders:
* <pre>
* "UPDATE foo SET col=? WHERE col='over \& under'"
* </pre>
*
* With some database backends, this is emulated.
*
* {@internal ibase and oci8 have their own prepare() methods.}}
*
* @param string $query query to be prepared
*
* @return mixed DB statement resource on success. DB_Error on failure.
*
* @see DB_common::execute()
* @access public
*/
function prepare($query)
{
$tokens = preg_split('/((?<!\\\)[&?!])/', $query, -1,
PREG_SPLIT_DELIM_CAPTURE);
$token = 0;
$types = array();
$newtokens = array();
 
foreach ($tokens as $val) {
switch ($val) {
case '?':
$types[$token++] = DB_PARAM_SCALAR;
break;
case '&':
$types[$token++] = DB_PARAM_OPAQUE;
break;
case '!':
$types[$token++] = DB_PARAM_MISC;
break;
default:
$newtokens[] = preg_replace('/\\\([&?!])/', "\\1", $val);
}
}
 
$this->prepare_tokens[] = &$newtokens;
end($this->prepare_tokens);
 
$k = key($this->prepare_tokens);
$this->prepare_types[$k] = $types;
$this->prepared_queries[$k] = implode(' ', $newtokens);
 
return $k;
}
 
// }}}
// {{{ autoPrepare()
 
/**
* Automaticaly generate an insert or update query and pass it to prepare()
*
* @param string $table name of the table
* @param array $table_fields ordered array containing the fields names
* @param int $mode type of query to make (DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE)
* @param string $where in case of update queries, this string will be put after the sql WHERE statement
* @return resource handle for the query
* @see DB_common::prepare(), DB_common::buildManipSQL()
* @access public
*/
function autoPrepare($table, $table_fields, $mode = DB_AUTOQUERY_INSERT, $where = false)
{
$query = $this->buildManipSQL($table, $table_fields, $mode, $where);
return $this->prepare($query);
}
 
// }}}
// {{{ autoExecute()
 
/**
* Automaticaly generate an insert or update query and call prepare()
* and execute() with it
*
* @param string $table name of the table
* @param array $fields_values assoc ($key=>$value) where $key is a field name and $value its value
* @param int $mode type of query to make (DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE)
* @param string $where in case of update queries, this string will be put after the sql WHERE statement
* @return mixed a new DB_Result or a DB_Error when fail
* @see DB_common::autoPrepare(), DB_common::buildManipSQL()
* @access public
*/
function autoExecute($table, $fields_values, $mode = DB_AUTOQUERY_INSERT, $where = false)
{
$sth = $this->autoPrepare($table, array_keys($fields_values), $mode, $where);
$ret =& $this->execute($sth, array_values($fields_values));
$this->freePrepared($sth);
return $ret;
 
}
 
// }}}
// {{{ buildManipSQL()
 
/**
* Make automaticaly an sql query for prepare()
*
* Example : buildManipSQL('table_sql', array('field1', 'field2', 'field3'), DB_AUTOQUERY_INSERT)
* will return the string : INSERT INTO table_sql (field1,field2,field3) VALUES (?,?,?)
* NB : - This belongs more to a SQL Builder class, but this is a simple facility
* - Be carefull ! If you don't give a $where param with an UPDATE query, all
* the records of the table will be updated !
*
* @param string $table name of the table
* @param array $table_fields ordered array containing the fields names
* @param int $mode type of query to make (DB_AUTOQUERY_INSERT or DB_AUTOQUERY_UPDATE)
* @param string $where in case of update queries, this string will be put after the sql WHERE statement
* @return string sql query for prepare()
* @access public
*/
function buildManipSQL($table, $table_fields, $mode, $where = false)
{
if (count($table_fields) == 0) {
$this->raiseError(DB_ERROR_NEED_MORE_DATA);
}
$first = true;
switch ($mode) {
case DB_AUTOQUERY_INSERT:
$values = '';
$names = '';
foreach ($table_fields as $value) {
if ($first) {
$first = false;
} else {
$names .= ',';
$values .= ',';
}
$names .= $value;
$values .= '?';
}
return "INSERT INTO $table ($names) VALUES ($values)";
case DB_AUTOQUERY_UPDATE:
$set = '';
foreach ($table_fields as $value) {
if ($first) {
$first = false;
} else {
$set .= ',';
}
$set .= "$value = ?";
}
$sql = "UPDATE $table SET $set";
if ($where) {
$sql .= " WHERE $where";
}
return $sql;
default:
$this->raiseError(DB_ERROR_SYNTAX);
}
}
 
// }}}
// {{{ execute()
 
/**
* Executes a DB statement prepared with prepare()
*
* Example 1.
* <code> <?php
* $sth = $dbh->prepare('INSERT INTO tbl (a, b, c) VALUES (?, !, &)');
* $data = array(
* "John's text",
* "'it''s good'",
* 'filename.txt'
* );
* $res =& $dbh->execute($sth, $data);
* ?></code>
*
* @param resource $stmt a DB statement resource returned from prepare()
* @param mixed $data array, string or numeric data to be used in
* execution of the statement. Quantity of items
* passed must match quantity of placeholders in
* query: meaning 1 placeholder for non-array
* parameters or 1 placeholder per array element.
*
* @return object a new DB_Result or a DB_Error when fail
*
* {@internal ibase and oci8 have their own execute() methods.}}
*
* @see DB_common::prepare()
* @access public
*/
function &execute($stmt, $data = array())
{
$realquery = $this->executeEmulateQuery($stmt, $data);
if (DB::isError($realquery)) {
return $realquery;
}
$result = $this->simpleQuery($realquery);
 
if (DB::isError($result) || $result === DB_OK) {
return $result;
} else {
$tmp =& new DB_result($this, $result);
return $tmp;
}
}
 
// }}}
// {{{ executeEmulateQuery()
 
/**
* Emulates the execute statement, when not supported
*
* @param resource $stmt a DB statement resource returned from execute()
* @param mixed $data array, string or numeric data to be used in
* execution of the statement. Quantity of items
* passed must match quantity of placeholders in
* query: meaning 1 placeholder for non-array
* parameters or 1 placeholder per array element.
*
* @return mixed a string containing the real query run when emulating
* prepare/execute. A DB error code is returned on failure.
*
* @see DB_common::execute()
* @access private
*/
function executeEmulateQuery($stmt, $data = array())
{
if (!is_array($data)) {
$data = array($data);
}
 
if (count($this->prepare_types[$stmt]) != count($data)) {
$this->last_query = $this->prepared_queries[$stmt];
return $this->raiseError(DB_ERROR_MISMATCH);
}
 
$realquery = $this->prepare_tokens[$stmt][0];
 
$i = 0;
foreach ($data as $value) {
if ($this->prepare_types[$stmt][$i] == DB_PARAM_SCALAR) {
$realquery .= $this->quoteSmart($value);
} elseif ($this->prepare_types[$stmt][$i] == DB_PARAM_OPAQUE) {
$fp = @fopen($value, 'rb');
if (!$fp) {
return $this->raiseError(DB_ERROR_ACCESS_VIOLATION);
}
$realquery .= $this->quoteSmart(fread($fp, filesize($value)));
fclose($fp);
} else {
$realquery .= $value;
}
 
$realquery .= $this->prepare_tokens[$stmt][++$i];
}
 
return $realquery;
}
 
// }}}
// {{{ executeMultiple()
 
/**
* This function does several execute() calls on the same
* statement handle
*
* $data must be an array indexed numerically
* from 0, one execute call is done for every "row" in the array.
*
* If an error occurs during execute(), executeMultiple() does not
* execute the unfinished rows, but rather returns that error.
*
* @param resource $stmt query handle from prepare()
* @param array $data numeric array containing the
* data to insert into the query
*
* @return mixed DB_OK or DB_Error
*
* @see DB_common::prepare(), DB_common::execute()
* @access public
*/
function executeMultiple($stmt, $data)
{
foreach ($data as $value) {
$res =& $this->execute($stmt, $value);
if (DB::isError($res)) {
return $res;
}
}
return DB_OK;
}
 
// }}}
// {{{ freePrepared()
 
/**
* Free the resource used in a prepared query
*
* @param $stmt The resurce returned by the prepare() function
* @see DB_common::prepare()
*/
function freePrepared($stmt)
{
// Free the internal prepared vars
if (isset($this->prepare_tokens[$stmt])) {
unset($this->prepare_tokens[$stmt]);
unset($this->prepare_types[$stmt]);
unset($this->prepared_queries[$stmt]);
return true;
}
return false;
}
 
// }}}
// {{{ modifyQuery()
 
/**
* This method is used by backends to alter queries for various
* reasons
*
* It is defined here to assure that all implementations
* have this method defined.
*
* @param string $query query to modify
*
* @return the new (modified) query
*
* @access private
*/
function modifyQuery($query) {
return $query;
}
 
// }}}
// {{{ modifyLimitQuery()
 
/**
* This method is used by backends to alter limited queries
*
* @param string $query query to modify
* @param integer $from the row to start to fetching
* @param integer $count the numbers of rows to fetch
*
* @return the new (modified) query
*
* @access private
*/
function modifyLimitQuery($query, $from, $count, $params = array())
{
return $query;
}
 
// }}}
// {{{ query()
 
/**
* Send a query to the database and return any results with a
* DB_result object
*
* The query string can be either a normal statement to be sent directly
* to the server OR if <var>$params</var> are passed the query can have
* placeholders and it will be passed through prepare() and execute().
*
* @param string $query the SQL query or the statement to prepare
* @param mixed $params array, string or numeric data to be used in
* execution of the statement. Quantity of items
* passed must match quantity of placeholders in
* query: meaning 1 placeholder for non-array
* parameters or 1 placeholder per array element.
*
* @return mixed a DB_result object or DB_OK on success, a DB
* error on failure
*
* @see DB_result, DB_common::prepare(), DB_common::execute()
* @access public
*/
function &query($query, $params = array())
{
if (sizeof($params) > 0) {
$sth = $this->prepare($query);
if (DB::isError($sth)) {
return $sth;
}
$ret =& $this->execute($sth, $params);
$this->freePrepared($sth);
return $ret;
} else {
$result = $this->simpleQuery($query);
if (DB::isError($result) || $result === DB_OK) {
return $result;
} else {
$tmp =& new DB_result($this, $result);
return $tmp;
}
}
}
 
// }}}
// {{{ limitQuery()
 
/**
* Generates a limited query
*
* @param string $query query
* @param integer $from the row to start to fetching
* @param integer $count the numbers of rows to fetch
* @param mixed $params array, string or numeric data to be used in
* execution of the statement. Quantity of items
* passed must match quantity of placeholders in
* query: meaning 1 placeholder for non-array
* parameters or 1 placeholder per array element.
*
* @return mixed a DB_Result object, DB_OK or a DB_Error
*
* @access public
*/
function &limitQuery($query, $from, $count, $params = array())
{
$query = $this->modifyLimitQuery($query, $from, $count, $params);
if (DB::isError($query)){
return $query;
}
$result =& $this->query($query, $params);
if (is_a($result, 'DB_result')) {
$result->setOption('limit_from', $from);
$result->setOption('limit_count', $count);
}
return $result;
}
 
// }}}
// {{{ getOne()
 
/**
* Fetch the first column of the first row of data returned from
* a query
*
* Takes care of doing the query and freeing the results when finished.
*
* @param string $query the SQL query
* @param mixed $params array, string or numeric data to be used in
* execution of the statement. Quantity of items
* passed must match quantity of placeholders in
* query: meaning 1 placeholder for non-array
* parameters or 1 placeholder per array element.
*
* @return mixed the returned value of the query. DB_Error on failure.
*
* @access public
*/
function &getOne($query, $params = array())
{
settype($params, 'array');
if (sizeof($params) > 0) {
$sth = $this->prepare($query);
if (DB::isError($sth)) {
return $sth;
}
$res =& $this->execute($sth, $params);
$this->freePrepared($sth);
} else {
$res =& $this->query($query);
}
 
if (DB::isError($res)) {
return $res;
}
 
$err = $res->fetchInto($row, DB_FETCHMODE_ORDERED);
$res->free();
 
if ($err !== DB_OK) {
return $err;
}
 
return $row[0];
}
 
// }}}
// {{{ getRow()
 
/**
* Fetch the first row of data returned from a query
*
* Takes care of doing the query and freeing the results when finished.
*
* @param string $query the SQL query
* @param array $params array to be used in execution of the statement.
* Quantity of array elements must match quantity
* of placeholders in query. This function does
* NOT support scalars.
* @param int $fetchmode the fetch mode to use
*
* @return array the first row of results as an array indexed from
* 0, or a DB error code.
*
* @access public
*/
function &getRow($query,
$params = array(),
$fetchmode = DB_FETCHMODE_DEFAULT)
{
// compat check, the params and fetchmode parameters used to
// have the opposite order
if (!is_array($params)) {
if (is_array($fetchmode)) {
if ($params === null) {
$tmp = DB_FETCHMODE_DEFAULT;
} else {
$tmp = $params;
}
$params = $fetchmode;
$fetchmode = $tmp;
} elseif ($params !== null) {
$fetchmode = $params;
$params = array();
}
}
 
if (sizeof($params) > 0) {
$sth = $this->prepare($query);
if (DB::isError($sth)) {
return $sth;
}
$res =& $this->execute($sth, $params);
$this->freePrepared($sth);
} else {
$res =& $this->query($query);
}
 
if (DB::isError($res)) {
return $res;
}
 
$err = $res->fetchInto($row, $fetchmode);
 
$res->free();
 
if ($err !== DB_OK) {
return $err;
}
 
return $row;
}
 
// }}}
// {{{ getCol()
 
/**
* Fetch a single column from a result set and return it as an
* indexed array
*
* @param string $query the SQL query
* @param mixed $col which column to return (integer [column number,
* starting at 0] or string [column name])
* @param mixed $params array, string or numeric data to be used in
* execution of the statement. Quantity of items
* passed must match quantity of placeholders in
* query: meaning 1 placeholder for non-array
* parameters or 1 placeholder per array element.
*
* @return array an indexed array with the data from the first
* row at index 0, or a DB error code
*
* @see DB_common::query()
* @access public
*/
function &getCol($query, $col = 0, $params = array())
{
settype($params, 'array');
if (sizeof($params) > 0) {
$sth = $this->prepare($query);
 
if (DB::isError($sth)) {
return $sth;
}
 
$res =& $this->execute($sth, $params);
$this->freePrepared($sth);
} else {
$res =& $this->query($query);
}
 
if (DB::isError($res)) {
return $res;
}
 
$fetchmode = is_int($col) ? DB_FETCHMODE_ORDERED : DB_FETCHMODE_ASSOC;
 
if (!is_array($row = $res->fetchRow($fetchmode))) {
$ret = array();
} else {
if (!array_key_exists($col, $row)) {
$ret =& $this->raiseError(DB_ERROR_NOSUCHFIELD);
} else {
$ret = array($row[$col]);
while (is_array($row = $res->fetchRow($fetchmode))) {
$ret[] = $row[$col];
}
}
}
 
$res->free();
 
if (DB::isError($row)) {
$ret = $row;
}
 
return $ret;
}
 
// }}}
// {{{ getAssoc()
 
/**
* Fetch the entire result set of a query and return it as an
* associative array using the first column as the key
*
* If the result set contains more than two columns, the value
* will be an array of the values from column 2-n. If the result
* set contains only two columns, the returned value will be a
* scalar with the value of the second column (unless forced to an
* array with the $force_array parameter). A DB error code is
* returned on errors. If the result set contains fewer than two
* columns, a DB_ERROR_TRUNCATED error is returned.
*
* For example, if the table "mytable" contains:
*
* <pre>
* ID TEXT DATE
* --------------------------------
* 1 'one' 944679408
* 2 'two' 944679408
* 3 'three' 944679408
* </pre>
*
* Then the call getAssoc('SELECT id,text FROM mytable') returns:
* <pre>
* array(
* '1' => 'one',
* '2' => 'two',
* '3' => 'three',
* )
* </pre>
*
* ...while the call getAssoc('SELECT id,text,date FROM mytable') returns:
* <pre>
* array(
* '1' => array('one', '944679408'),
* '2' => array('two', '944679408'),
* '3' => array('three', '944679408')
* )
* </pre>
*
* If the more than one row occurs with the same value in the
* first column, the last row overwrites all previous ones by
* default. Use the $group parameter if you don't want to
* overwrite like this. Example:
*
* <pre>
* getAssoc('SELECT category,id,name FROM mytable', false, null,
* DB_FETCHMODE_ASSOC, true) returns:
*
* array(
* '1' => array(array('id' => '4', 'name' => 'number four'),
* array('id' => '6', 'name' => 'number six')
* ),
* '9' => array(array('id' => '4', 'name' => 'number four'),
* array('id' => '6', 'name' => 'number six')
* )
* )
* </pre>
*
* Keep in mind that database functions in PHP usually return string
* values for results regardless of the database's internal type.
*
* @param string $query the SQL query
* @param boolean $force_array used only when the query returns
* exactly two columns. If true, the values
* of the returned array will be one-element
* arrays instead of scalars.
* @param mixed $params array, string or numeric data to be used in
* execution of the statement. Quantity of items
* passed must match quantity of placeholders in
* query: meaning 1 placeholder for non-array
* parameters or 1 placeholder per array element.
* @param int $fetchmode the fetch mode to use
* @param boolean $group if true, the values of the returned array
* is wrapped in another array. If the same
* key value (in the first column) repeats
* itself, the values will be appended to
* this array instead of overwriting the
* existing values.
*
* @return array associative array with results from the query.
* DB Error on failure.
*
* @access public
*/
function &getAssoc($query, $force_array = false, $params = array(),
$fetchmode = DB_FETCHMODE_DEFAULT, $group = false)
{
settype($params, 'array');
if (sizeof($params) > 0) {
$sth = $this->prepare($query);
 
if (DB::isError($sth)) {
return $sth;
}
 
$res =& $this->execute($sth, $params);
$this->freePrepared($sth);
} else {
$res =& $this->query($query);
}
 
if (DB::isError($res)) {
return $res;
}
if ($fetchmode == DB_FETCHMODE_DEFAULT) {
$fetchmode = $this->fetchmode;
}
$cols = $res->numCols();
 
if ($cols < 2) {
$tmp =& $this->raiseError(DB_ERROR_TRUNCATED);
return $tmp;
}
 
$results = array();
 
if ($cols > 2 || $force_array) {
// return array values
// XXX this part can be optimized
if ($fetchmode == DB_FETCHMODE_ASSOC) {
while (is_array($row = $res->fetchRow(DB_FETCHMODE_ASSOC))) {
reset($row);
$key = current($row);
unset($row[key($row)]);
if ($group) {
$results[$key][] = $row;
} else {
$results[$key] = $row;
}
}
} elseif ($fetchmode == DB_FETCHMODE_OBJECT) {
while ($row = $res->fetchRow(DB_FETCHMODE_OBJECT)) {
$arr = get_object_vars($row);
$key = current($arr);
if ($group) {
$results[$key][] = $row;
} else {
$results[$key] = $row;
}
}
} else {
while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) {
// we shift away the first element to get
// indices running from 0 again
$key = array_shift($row);
if ($group) {
$results[$key][] = $row;
} else {
$results[$key] = $row;
}
}
}
if (DB::isError($row)) {
$results = $row;
}
} else {
// return scalar values
while (is_array($row = $res->fetchRow(DB_FETCHMODE_ORDERED))) {
if ($group) {
$results[$row[0]][] = $row[1];
} else {
$results[$row[0]] = $row[1];
}
}
if (DB::isError($row)) {
$results = $row;
}
}
 
$res->free();
 
return $results;
}
 
// }}}
// {{{ getAll()
 
/**
* Fetch all the rows returned from a query
*
* @param string $query the SQL query
* @param array $params array to be used in execution of the statement.
* Quantity of array elements must match quantity
* of placeholders in query. This function does
* NOT support scalars.
* @param int $fetchmode the fetch mode to use
*
* @return array an nested array. DB error on failure.
*
* @access public
*/
function &getAll($query,
$params = array(),
$fetchmode = DB_FETCHMODE_DEFAULT)
{
// compat check, the params and fetchmode parameters used to
// have the opposite order
if (!is_array($params)) {
if (is_array($fetchmode)) {
if ($params === null) {
$tmp = DB_FETCHMODE_DEFAULT;
} else {
$tmp = $params;
}
$params = $fetchmode;
$fetchmode = $tmp;
} elseif ($params !== null) {
$fetchmode = $params;
$params = array();
}
}
 
if (sizeof($params) > 0) {
$sth = $this->prepare($query);
 
if (DB::isError($sth)) {
return $sth;
}
 
$res =& $this->execute($sth, $params);
$this->freePrepared($sth);
} else {
$res =& $this->query($query);
}
 
if (DB::isError($res) || $res === DB_OK) {
return $res;
}
 
$results = array();
while (DB_OK === $res->fetchInto($row, $fetchmode)) {
if ($fetchmode & DB_FETCHMODE_FLIPPED) {
foreach ($row as $key => $val) {
$results[$key][] = $val;
}
} else {
$results[] = $row;
}
}
 
$res->free();
 
if (DB::isError($row)) {
$tmp =& $this->raiseError($row);
return $tmp;
}
return $results;
}
 
// }}}
// {{{ autoCommit()
 
/**
* enable automatic Commit
*
* @param boolean $onoff
* @return mixed DB_Error
*
* @access public
*/
function autoCommit($onoff=false)
{
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
 
// }}}
// {{{ commit()
 
/**
* starts a Commit
*
* @return mixed DB_Error
*
* @access public
*/
function commit()
{
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
 
// }}}
// {{{ rollback()
 
/**
* starts a rollback
*
* @return mixed DB_Error
*
* @access public
*/
function rollback()
{
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
 
// }}}
// {{{ numRows()
 
/**
* Returns the number of rows in a result object
*
* @param object DB_Result the result object to check
*
* @return mixed DB_Error or the number of rows
*
* @access public
*/
function numRows($result)
{
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
 
// }}}
// {{{ affectedRows()
 
/**
* Returns the affected rows of a query
*
* @return mixed DB_Error or number of rows
*
* @access public
*/
function affectedRows()
{
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
 
// }}}
// {{{ errorNative()
 
/**
* Returns an errormessage, provides by the database
*
* @return mixed DB_Error or message
*
* @access public
*/
function errorNative()
{
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
 
// }}}
// {{{ getSequenceName()
 
/**
* Generate the name used inside the database for a sequence
*
* The createSequence() docblock contains notes about storing sequence
* names.
*
* @param string $sqn the sequence's public name
*
* @return string the sequence's name in the backend
*
* @see DB_common::createSequence(), DB_common::dropSequence(),
* DB_common::nextID(), DB_common::setOption()
* @access private
*/
function getSequenceName($sqn)
{
return sprintf($this->getOption('seqname_format'),
preg_replace('/[^a-z0-9_.]/i', '_', $sqn));
}
 
// }}}
// {{{ nextId()
 
/**
* Returns the next free id in a sequence
*
* @param string $seq_name name of the sequence
* @param boolean $ondemand when true, the seqence is automatically
* created if it does not exist
*
* @return int the next id number in the sequence. DB_Error if problem.
*
* @see DB_common::createSequence(), DB_common::dropSequence(),
* DB_common::getSequenceName()
* @access public
*/
function nextId($seq_name, $ondemand = true)
{
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
 
// }}}
// {{{ createSequence()
 
/**
* Creates a new sequence
*
* The name of a given sequence is determined by passing the string
* provided in the <var>$seq_name</var> argument through PHP's sprintf()
* function using the value from the <var>seqname_format</var> option as
* the sprintf()'s format argument.
*
* <var>seqname_format</var> is set via setOption().
*
* @param string $seq_name name of the new sequence
*
* @return int DB_OK on success. A DB_Error object is returned if
* problems arise.
*
* @see DB_common::dropSequence(), DB_common::getSequenceName(),
* DB_common::nextID()
* @access public
*/
function createSequence($seq_name)
{
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
 
// }}}
// {{{ dropSequence()
 
/**
* Deletes a sequence
*
* @param string $seq_name name of the sequence to be deleted
*
* @return int DB_OK on success. DB_Error if problems.
*
* @see DB_common::createSequence(), DB_common::getSequenceName(),
* DB_common::nextID()
* @access public
*/
function dropSequence($seq_name)
{
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
 
// }}}
// {{{ tableInfo()
 
/**
* Returns information about a table or a result set
*
* The format of the resulting array depends on which <var>$mode</var>
* you select. The sample output below is based on this query:
* <pre>
* SELECT tblFoo.fldID, tblFoo.fldPhone, tblBar.fldId
* FROM tblFoo
* JOIN tblBar ON tblFoo.fldId = tblBar.fldId
* </pre>
*
* <ul>
* <li>
*
* <kbd>null</kbd> (default)
* <pre>
* [0] => Array (
* [table] => tblFoo
* [name] => fldId
* [type] => int
* [len] => 11
* [flags] => primary_key not_null
* )
* [1] => Array (
* [table] => tblFoo
* [name] => fldPhone
* [type] => string
* [len] => 20
* [flags] =>
* )
* [2] => Array (
* [table] => tblBar
* [name] => fldId
* [type] => int
* [len] => 11
* [flags] => primary_key not_null
* )
* </pre>
*
* </li><li>
*
* <kbd>DB_TABLEINFO_ORDER</kbd>
*
* <p>In addition to the information found in the default output,
* a notation of the number of columns is provided by the
* <samp>num_fields</samp> element while the <samp>order</samp>
* element provides an array with the column names as the keys and
* their location index number (corresponding to the keys in the
* the default output) as the values.</p>
*
* <p>If a result set has identical field names, the last one is
* used.</p>
*
* <pre>
* [num_fields] => 3
* [order] => Array (
* [fldId] => 2
* [fldTrans] => 1
* )
* </pre>
*
* </li><li>
*
* <kbd>DB_TABLEINFO_ORDERTABLE</kbd>
*
* <p>Similar to <kbd>DB_TABLEINFO_ORDER</kbd> but adds more
* dimensions to the array in which the table names are keys and
* the field names are sub-keys. This is helpful for queries that
* join tables which have identical field names.</p>
*
* <pre>
* [num_fields] => 3
* [ordertable] => Array (
* [tblFoo] => Array (
* [fldId] => 0
* [fldPhone] => 1
* )
* [tblBar] => Array (
* [fldId] => 2
* )
* )
* </pre>
*
* </li>
* </ul>
*
* The <samp>flags</samp> element contains a space separated list
* of extra information about the field. This data is inconsistent
* between DBMS's due to the way each DBMS works.
* + <samp>primary_key</samp>
* + <samp>unique_key</samp>
* + <samp>multiple_key</samp>
* + <samp>not_null</samp>
*
* Most DBMS's only provide the <samp>table</samp> and <samp>flags</samp>
* elements if <var>$result</var> is a table name. The following DBMS's
* provide full information from queries:
* + fbsql
* + mysql
*
* If the 'portability' option has <samp>DB_PORTABILITY_LOWERCASE</samp>
* turned on, the names of tables and fields will be lowercased.
*
* @param object|string $result DB_result object from a query or a
* string containing the name of a table.
* While this also accepts a query result
* resource identifier, this behavior is
* deprecated.
* @param int $mode either unused or one of the tableInfo modes:
* <kbd>DB_TABLEINFO_ORDERTABLE</kbd>,
* <kbd>DB_TABLEINFO_ORDER</kbd> or
* <kbd>DB_TABLEINFO_FULL</kbd> (which does both).
* These are bitwise, so the first two can be
* combined using <kbd>|</kbd>.
* @return array an associative array with the information requested.
* If something goes wrong an error object is returned.
*
* @see DB_common::setOption()
* @access public
*/
function tableInfo($result, $mode = null)
{
/*
* If the DB_<driver> class has a tableInfo() method, that one
* overrides this one. But, if the driver doesn't have one,
* this method runs and tells users about that fact.
*/
return $this->raiseError(DB_ERROR_NOT_CAPABLE);
}
 
// }}}
// {{{ getTables()
 
/**
* @deprecated Deprecated in release 1.2 or lower
*/
function getTables()
{
return $this->getListOf('tables');
}
 
// }}}
// {{{ getListOf()
 
/**
* list internal DB info
* valid values for $type are db dependent,
* often: databases, users, view, functions
*
* @param string $type type of requested info
*
* @return mixed DB_Error or the requested data
*
* @access public
*/
function getListOf($type)
{
$sql = $this->getSpecialQuery($type);
if ($sql === null) { // No support
return $this->raiseError(DB_ERROR_UNSUPPORTED);
} elseif (is_int($sql) || DB::isError($sql)) { // Previous error
return $this->raiseError($sql);
} elseif (is_array($sql)) { // Already the result
return $sql;
}
return $this->getCol($sql); // Launch this query
}
 
// }}}
// {{{ getSpecialQuery()
 
/**
* Returns the query needed to get some backend info
*
* @param string $type What kind of info you want to retrieve
*
* @return string The SQL query string
*
* @access public
*/
function getSpecialQuery($type)
{
return $this->raiseError(DB_ERROR_UNSUPPORTED);
}
 
// }}}
// {{{ _rtrimArrayValues()
 
/**
* Right trim all strings in an array
*
* @param array $array the array to be trimmed (passed by reference)
* @return void
* @access private
*/
function _rtrimArrayValues(&$array)
{
foreach ($array as $key => $value) {
if (is_string($value)) {
$array[$key] = rtrim($value);
}
}
}
 
// }}}
// {{{ _convertNullArrayValuesToEmpty()
 
/**
* Convert all null values in an array to empty strings
*
* @param array $array the array to be de-nullified (passed by reference)
* @return void
* @access private
*/
function _convertNullArrayValuesToEmpty(&$array)
{
foreach ($array as $key => $value) {
if (is_null($value)) {
$array[$key] = '';
}
}
}
 
// }}}
}
 
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/
 
?>
/trunk/applications/jrest/lib/zip.php
New file
0,0 → 1,190
<?php
/* vim: set expandtab sw=4 ts=4 sts=4: */
/**
*
* @version $Id: zip.lib.php 10240 2007-04-01 11:02:46Z cybot_tm $
*/
 
/**
* Zip file creation class.
* Makes zip files.
*
* Based on :
*
* http://www.zend.com/codex.php?id=535&single=1
* By Eric Mueller <eric@themepark.com>
*
* http://www.zend.com/codex.php?id=470&single=1
* by Denis125 <webmaster@atlant.ru>
*
* a patch from Peter Listiak <mlady@users.sourceforge.net> for last modified
* date and time of the compressed file
*
* Official ZIP file format: http://www.pkware.com/appnote.txt
*
* @access public
*/
class zipfile
{
/**
* Array to store compressed data
*
* @var array $datasec
*/
var $datasec = array();
 
/**
* Central directory
*
* @var array $ctrl_dir
*/
var $ctrl_dir = array();
 
/**
* End of central directory record
*
* @var string $eof_ctrl_dir
*/
var $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00";
 
/**
* Last offset position
*
* @var integer $old_offset
*/
var $old_offset = 0;
 
 
/**
* Converts an Unix timestamp to a four byte DOS date and time format (date
* in high two bytes, time in low two bytes allowing magnitude comparison).
*
* @param integer the current Unix timestamp
*
* @return integer the current date in a four byte DOS format
*
* @access private
*/
function unix2DosTime($unixtime = 0) {
$timearray = ($unixtime == 0) ? getdate() : getdate($unixtime);
 
if ($timearray['year'] < 1980) {
$timearray['year'] = 1980;
$timearray['mon'] = 1;
$timearray['mday'] = 1;
$timearray['hours'] = 0;
$timearray['minutes'] = 0;
$timearray['seconds'] = 0;
} // end if
 
return (($timearray['year'] - 1980) << 25) | ($timearray['mon'] << 21) | ($timearray['mday'] << 16) |
($timearray['hours'] << 11) | ($timearray['minutes'] << 5) | ($timearray['seconds'] >> 1);
} // end of the 'unix2DosTime()' method
 
 
/**
* Adds "file" to archive
*
* @param string file contents
* @param string name of the file in the archive (may contains the path)
* @param integer the current timestamp
*
* @access public
*/
function addFile($data, $name, $time = 0)
{
$name = str_replace('\\', '/', $name);
 
$dtime = dechex($this->unix2DosTime($time));
$hexdtime = '\x' . $dtime[6] . $dtime[7]
. '\x' . $dtime[4] . $dtime[5]
. '\x' . $dtime[2] . $dtime[3]
. '\x' . $dtime[0] . $dtime[1];
eval('$hexdtime = "' . $hexdtime . '";');
 
$fr = "\x50\x4b\x03\x04";
$fr .= "\x14\x00"; // ver needed to extract
$fr .= "\x00\x00"; // gen purpose bit flag
$fr .= "\x08\x00"; // compression method
$fr .= $hexdtime; // last mod time and date
 
// "local file header" segment
$unc_len = strlen($data);
$crc = crc32($data);
$zdata = gzcompress($data);
$zdata = substr(substr($zdata, 0, strlen($zdata) - 4), 2); // fix crc bug
$c_len = strlen($zdata);
$fr .= pack('V', $crc); // crc32
$fr .= pack('V', $c_len); // compressed filesize
$fr .= pack('V', $unc_len); // uncompressed filesize
$fr .= pack('v', strlen($name)); // length of filename
$fr .= pack('v', 0); // extra field length
$fr .= $name;
 
// "file data" segment
$fr .= $zdata;
 
// "data descriptor" segment (optional but necessary if archive is not
// served as file)
// nijel(2004-10-19): this seems not to be needed at all and causes
// problems in some cases (bug #1037737)
//$fr .= pack('V', $crc); // crc32
//$fr .= pack('V', $c_len); // compressed filesize
//$fr .= pack('V', $unc_len); // uncompressed filesize
 
// add this entry to array
$this -> datasec[] = $fr;
 
// now add to central directory record
$cdrec = "\x50\x4b\x01\x02";
$cdrec .= "\x00\x00"; // version made by
$cdrec .= "\x14\x00"; // version needed to extract
$cdrec .= "\x00\x00"; // gen purpose bit flag
$cdrec .= "\x08\x00"; // compression method
$cdrec .= $hexdtime; // last mod time & date
$cdrec .= pack('V', $crc); // crc32
$cdrec .= pack('V', $c_len); // compressed filesize
$cdrec .= pack('V', $unc_len); // uncompressed filesize
$cdrec .= pack('v', strlen($name)); // length of filename
$cdrec .= pack('v', 0); // extra field length
$cdrec .= pack('v', 0); // file comment length
$cdrec .= pack('v', 0); // disk number start
$cdrec .= pack('v', 0); // internal file attributes
$cdrec .= pack('V', 32); // external file attributes - 'archive' bit set
 
$cdrec .= pack('V', $this -> old_offset); // relative offset of local header
$this -> old_offset += strlen($fr);
 
$cdrec .= $name;
 
// optional extra field, file comment goes here
// save to central directory
$this -> ctrl_dir[] = $cdrec;
} // end of the 'addFile()' method
 
 
/**
* Dumps out file
*
* @return string the zipped file
*
* @access public
*/
function file()
{
$data = implode('', $this -> datasec);
$ctrldir = implode('', $this -> ctrl_dir);
 
return
$data .
$ctrldir .
$this -> eof_ctrl_dir .
pack('v', sizeof($this -> ctrl_dir)) . // total # of entries "on this disk"
pack('v', sizeof($this -> ctrl_dir)) . // total # of entries overall
pack('V', strlen($ctrldir)) . // size of central dir
pack('V', strlen($data)) . // offset to start of central dir
"\x00\x00"; // .zip file comment length
} // end of the 'file()' method
 
} // end of the 'zipfile' class
?>
/trunk/applications/jrest/lib/DBAccessor.php
New file
0,0 → 1,34
<?php
require_once 'JrestService.php';
 
class DBAccessor extends JrestService {
public function connectDB($config, $base = 'database') {
require_once 'DB.php';
$dsn = $config[$base];
 
$DB =& DB::connect($dsn);
if (DB::isError($DB)) {
die($DB->getMessage());
}
$DB->query("SET NAMES 'utf8'");
return $DB;
}
public function connecterPDO($config, $base = 'database') {
$cfg = $config[$base];
$dsn = $cfg['phptype'].':dbname='.$cfg['database'].';host='.$cfg['hostspec'];
try {
$PDO = new PDO($dsn, $cfg['username'], $cfg['password']);
} catch (PDOException $e) {
echo 'La connexion à la base de donnée via PDO a échouée : ' . $e->getMessage();
}
// Passe en UTF-8 la connexion à la BDD
$PDO->exec("SET NAMES 'utf8'");
// Affiche les erreurs détectées par PDO (sinon mode silencieux => aucune erreur affiché)
$PDO->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
return $PDO;
}
}
?>
/trunk/applications/jrest/lib/JrestService.php
New file
0,0 → 1,128
<?php
/**
* PHP Version 5
*
* @category PHP
* @package jrest
* @author aurelien <aurelien@tela-botanica.org>
* @copyright 2009 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @version $$id$$
* @link /doc/jrest/
*/
 
class JrestService {
 
protected $config;
protected $script_time;
protected $max_exec_time;
 
public function JrestService($config) {
$this->config = config;
$this->script_time = microtime(true);
$this->max_exec_time = ini_get('max_execution_time');
}
 
public function isAdmin($id) {
$admins = $this->config['jrest_admin']['admin'];
$admin_tab = split(',',$admins);
 
if (in_array($id,$admin_tab)) {
return true;
} else {
return false;
}
}
 
public function controleUtilisateur($id) {
if ($_SESSION['user']['name'] == '') {
//cas de la session temporaire, on ne fait rien de particulier
} else {
if (!$this->isAdmin($_SESSION['user']['name']) && $_SESSION['user']['name'] != $id) {
// cas d'usurpation d'identité
print 'Accès interdit';
exit();
}
}
}
 
public function logger($index,$chaine) {
if(!class_exists('Log')) {
include_once('Log.php');
Log::getInstance();
}
 
Log::setCheminLog($this->config['log']['cheminlog']);
Log::setTimeZone($this->config['log']['timezone']);
Log::setTailleMax($this->config['log']['taillemax']);
 
Log::ajouterEntree($index,$chaine);
}
 
public function verifierOuRelancerExecution() {
 
if((microtime(true) - $this->script_time) > ($this->max_exec_time - 5)*100) {
set_time_limit(2);
$this->logger('JRestService','Durée du script augmentée :'.microtime(true).' - '.$this->script_time.'.) > ('.$this->max_exec_time.' - 5)*1000000');
return true;
}
return false;
}
/**
* Méthode prenant en paramètre un chemin de fichier squelette et un tableau associatif de données,
* en extrait les variables, charge le squelette et retourne le résultat des deux combinés.
*
* @param String $fichier le chemin du fichier du squelette
* @param Array $donnees un tableau associatif contenant les variables a injecter dans le squelette.
*
* @return boolean false si le squelette n'existe pas, sinon la chaine résultat.
*/
public static function traiterSquelettePhp($fichier, Array $donnees = array()) {
$sortie = false;
if (file_exists($fichier)) {
// Extraction des variables du tableau de données
extract($donnees);
// Démarage de la bufferisation de sortie
ob_start();
// Si les tags courts sont activés
if ((bool) @ini_get('short_open_tag') === true) {
// Simple inclusion du squelette
include $fichier;
} else {
// Sinon, remplacement des tags courts par la syntaxe classique avec echo
$html_et_code_php = self::traiterTagsCourts($fichier);
// Pour évaluer du php mélangé dans du html il est nécessaire de fermer la balise php ouverte par eval
$html_et_code_php = '?>'.$html_et_code_php;
// Interprétation du html et du php dans le buffer
echo eval($html_et_code_php);
}
// Récupèration du contenu du buffer
$sortie = ob_get_contents();
// Suppression du buffer
@ob_end_clean();
} else {
$msg = "Le fichier du squelette '$fichier' n'existe pas.";
trigger_error($msg, E_USER_WARNING);
}
// Retourne le contenu
return $sortie;
}
/**
* Fonction chargeant le contenu du squelette et remplaçant les tags court php (<?= ...) par un tag long avec echo.
*
* @param String $chemin_squelette le chemin du fichier du squelette
*
* @return string le contenu du fichier du squelette php avec les tags courts remplacés.
*/
private static function traiterTagsCourts($chemin_squelette) {
$contenu = file_get_contents($chemin_squelette);
// Remplacement de tags courts par un tag long avec echo
$contenu = str_replace('<?=', '<?php echo ', $contenu);
// Ajout systématique d'un point virgule avant la fermeture php
$contenu = preg_replace("/;*\s*\?>/", "; ?>", $contenu);
return $contenu;
}
}
?>
/trunk/applications/jrest/lib/JSON.php
New file
0,0 → 1,806
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
/**
* Converts to and from JSON format.
*
* JSON (JavaScript Object Notation) is a lightweight data-interchange
* format. It is easy for humans to read and write. It is easy for machines
* to parse and generate. It is based on a subset of the JavaScript
* Programming Language, Standard ECMA-262 3rd Edition - December 1999.
* This feature can also be found in Python. JSON is a text format that is
* completely language independent but uses conventions that are familiar
* to programmers of the C-family of languages, including C, C++, C#, Java,
* JavaScript, Perl, TCL, and many others. These properties make JSON an
* ideal data-interchange language.
*
* This package provides a simple encoder and decoder for JSON notation. It
* is intended for use with client-side Javascript applications that make
* use of HTTPRequest to perform server communication functions - data can
* be encoded into JSON notation for use in a client-side javascript, or
* decoded from incoming Javascript requests. JSON format is native to
* Javascript, and can be directly eval()'ed with no further parsing
* overhead
*
* All strings should be in ASCII or UTF-8 format!
*
* LICENSE: Redistribution and use in source and binary forms, with or
* without modification, are permitted provided that the following
* conditions are met: Redistributions of source code must retain the
* above copyright notice, this list of conditions and the following
* disclaimer. Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following disclaimer
* in the documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
* USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*
* @category
* @package Services_JSON
* @author Michal Migurski <mike-json@teczno.com>
* @author Matt Knapp <mdknapp[at]gmail[dot]com>
* @author Brett Stimmerman <brettstimmerman[at]gmail[dot]com>
* @copyright 2005 Michal Migurski
* @version CVS: $Id$
* @license http://www.opensource.org/licenses/bsd-license.php
* @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198
*/
 
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_SLICE', 1);
 
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_IN_STR', 2);
 
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_IN_ARR', 3);
 
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_IN_OBJ', 4);
 
/**
* Marker constant for Services_JSON::decode(), used to flag stack state
*/
define('SERVICES_JSON_IN_CMT', 5);
 
/**
* Behavior switch for Services_JSON::decode()
*/
define('SERVICES_JSON_LOOSE_TYPE', 16);
 
/**
* Behavior switch for Services_JSON::decode()
*/
define('SERVICES_JSON_SUPPRESS_ERRORS', 32);
 
/**
* Converts to and from JSON format.
*
* Brief example of use:
*
* <code>
* // create a new instance of Services_JSON
* $json = new Services_JSON();
*
* // convert a complexe value to JSON notation, and send it to the browser
* $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4)));
* $output = $json->encode($value);
*
* print($output);
* // prints: ["foo","bar",[1,2,"baz"],[3,[4]]]
*
* // accept incoming POST data, assumed to be in JSON notation
* $input = file_get_contents('php://input', 1000000);
* $value = $json->decode($input);
* </code>
*/
class Services_JSON
{
/**
* constructs a new JSON instance
*
* @param int $use object behavior flags; combine with boolean-OR
*
* possible values:
* - SERVICES_JSON_LOOSE_TYPE: loose typing.
* "{...}" syntax creates associative arrays
* instead of objects in decode().
* - SERVICES_JSON_SUPPRESS_ERRORS: error suppression.
* Values which can't be encoded (e.g. resources)
* appear as NULL instead of throwing errors.
* By default, a deeply-nested resource will
* bubble up with an error, so all return values
* from encode() should be checked with isError()
*/
function Services_JSON($use = 0)
{
$this->use = $use;
}
 
/**
* convert a string from one UTF-16 char to one UTF-8 char
*
* Normally should be handled by mb_convert_encoding, but
* provides a slower PHP-only method for installations
* that lack the multibye string extension.
*
* @param string $utf16 UTF-16 character
* @return string UTF-8 character
* @access private
*/
function utf162utf8($utf16)
{
// oh please oh please oh please oh please oh please
if(function_exists('mb_convert_encoding')) {
return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16');
}
 
$bytes = (ord($utf16{0}) << 8) | ord($utf16{1});
 
switch(true) {
case ((0x7F & $bytes) == $bytes):
// this case should never be reached, because we are in ASCII range
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0x7F & $bytes);
 
case (0x07FF & $bytes) == $bytes:
// return a 2-byte UTF-8 character
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0xC0 | (($bytes >> 6) & 0x1F))
. chr(0x80 | ($bytes & 0x3F));
 
case (0xFFFF & $bytes) == $bytes:
// return a 3-byte UTF-8 character
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0xE0 | (($bytes >> 12) & 0x0F))
. chr(0x80 | (($bytes >> 6) & 0x3F))
. chr(0x80 | ($bytes & 0x3F));
}
 
// ignoring UTF-32 for now, sorry
return '';
}
 
/**
* convert a string from one UTF-8 char to one UTF-16 char
*
* Normally should be handled by mb_convert_encoding, but
* provides a slower PHP-only method for installations
* that lack the multibye string extension.
*
* @param string $utf8 UTF-8 character
* @return string UTF-16 character
* @access private
*/
function utf82utf16($utf8)
{
// oh please oh please oh please oh please oh please
if(function_exists('mb_convert_encoding')) {
return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8');
}
 
switch(strlen($utf8)) {
case 1:
// this case should never be reached, because we are in ASCII range
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return $utf8;
 
case 2:
// return a UTF-16 character from a 2-byte UTF-8 char
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr(0x07 & (ord($utf8{0}) >> 2))
. chr((0xC0 & (ord($utf8{0}) << 6))
| (0x3F & ord($utf8{1})));
 
case 3:
// return a UTF-16 character from a 3-byte UTF-8 char
// see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
return chr((0xF0 & (ord($utf8{0}) << 4))
| (0x0F & (ord($utf8{1}) >> 2)))
. chr((0xC0 & (ord($utf8{1}) << 6))
| (0x7F & ord($utf8{2})));
}
 
// ignoring UTF-32 for now, sorry
return '';
}
 
/**
* encodes an arbitrary variable into JSON format
*
* @param mixed $var any number, boolean, string, array, or object to be encoded.
* see argument 1 to Services_JSON() above for array-parsing behavior.
* if var is a strng, note that encode() always expects it
* to be in ASCII or UTF-8 format!
*
* @return mixed JSON string representation of input var or an error if a problem occurs
* @access public
*/
function encode($var)
{
switch (gettype($var)) {
case 'boolean':
return $var ? 'true' : 'false';
 
case 'NULL':
return 'null';
 
case 'integer':
return (int) $var;
 
case 'double':
case 'float':
return (float) $var;
 
case 'string':
// STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT
$ascii = '';
$strlen_var = strlen($var);
 
/*
* Iterate over every character in the string,
* escaping with a slash or encoding to UTF-8 where necessary
*/
for ($c = 0; $c < $strlen_var; ++$c) {
 
$ord_var_c = ord($var{$c});
 
switch (true) {
case $ord_var_c == 0x08:
$ascii .= '\b';
break;
case $ord_var_c == 0x09:
$ascii .= '\t';
break;
case $ord_var_c == 0x0A:
$ascii .= '\n';
break;
case $ord_var_c == 0x0C:
$ascii .= '\f';
break;
case $ord_var_c == 0x0D:
$ascii .= '\r';
break;
 
case $ord_var_c == 0x22:
case $ord_var_c == 0x2F:
case $ord_var_c == 0x5C:
// double quote, slash, slosh
$ascii .= '\\'.$var{$c};
break;
 
case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)):
// characters U-00000000 - U-0000007F (same as ASCII)
$ascii .= $var{$c};
break;
 
case (($ord_var_c & 0xE0) == 0xC0):
// characters U-00000080 - U-000007FF, mask 110XXXXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c, ord($var{$c + 1}));
$c += 1;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
 
case (($ord_var_c & 0xF0) == 0xE0):
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c,
ord($var{$c + 1}),
ord($var{$c + 2}));
$c += 2;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
 
case (($ord_var_c & 0xF8) == 0xF0):
// characters U-00010000 - U-001FFFFF, mask 11110XXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c,
ord($var{$c + 1}),
ord($var{$c + 2}),
ord($var{$c + 3}));
$c += 3;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
 
case (($ord_var_c & 0xFC) == 0xF8):
// characters U-00200000 - U-03FFFFFF, mask 111110XX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c,
ord($var{$c + 1}),
ord($var{$c + 2}),
ord($var{$c + 3}),
ord($var{$c + 4}));
$c += 4;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
 
case (($ord_var_c & 0xFE) == 0xFC):
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$char = pack('C*', $ord_var_c,
ord($var{$c + 1}),
ord($var{$c + 2}),
ord($var{$c + 3}),
ord($var{$c + 4}),
ord($var{$c + 5}));
$c += 5;
$utf16 = $this->utf82utf16($char);
$ascii .= sprintf('\u%04s', bin2hex($utf16));
break;
}
}
 
return '"'.$ascii.'"';
 
case 'array':
/*
* As per JSON spec if any array key is not an integer
* we must treat the the whole array as an object. We
* also try to catch a sparsely populated associative
* array with numeric keys here because some JS engines
* will create an array with empty indexes up to
* max_index which can cause memory issues and because
* the keys, which may be relevant, will be remapped
* otherwise.
*
* As per the ECMA and JSON specification an object may
* have any string as a property. Unfortunately due to
* a hole in the ECMA specification if the key is a
* ECMA reserved word or starts with a digit the
* parameter is only accessible using ECMAScript's
* bracket notation.
*/
 
// treat as a JSON object
if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) {
$properties = array_map(array($this, 'name_value'),
array_keys($var),
array_values($var));
 
foreach($properties as $property) {
if(Services_JSON::isError($property)) {
return $property;
}
}
 
return '{' . join(',', $properties) . '}';
}
 
// treat it like a regular array
$elements = array_map(array($this, 'encode'), $var);
 
foreach($elements as $element) {
if(Services_JSON::isError($element)) {
return $element;
}
}
 
return '[' . join(',', $elements) . ']';
 
case 'object':
$vars = get_object_vars($var);
 
$properties = array_map(array($this, 'name_value'),
array_keys($vars),
array_values($vars));
 
foreach($properties as $property) {
if(Services_JSON::isError($property)) {
return $property;
}
}
 
return '{' . join(',', $properties) . '}';
 
default:
return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
? 'null'
: new Services_JSON_Error(gettype($var)." can not be encoded as JSON string");
}
}
 
/**
* array-walking function for use in generating JSON-formatted name-value pairs
*
* @param string $name name of key to use
* @param mixed $value reference to an array element to be encoded
*
* @return string JSON-formatted name-value pair, like '"name":value'
* @access private
*/
function name_value($name, $value)
{
$encoded_value = $this->encode($value);
 
if(Services_JSON::isError($encoded_value)) {
return $encoded_value;
}
 
return $this->encode(strval($name)) . ':' . $encoded_value;
}
 
/**
* reduce a string by removing leading and trailing comments and whitespace
*
* @param $str string string value to strip of comments and whitespace
*
* @return string string value stripped of comments and whitespace
* @access private
*/
function reduce_string($str)
{
$str = preg_replace(array(
 
// eliminate single line comments in '// ...' form
'#^\s*//(.+)$#m',
 
// eliminate multi-line comments in '/* ... */' form, at start of string
'#^\s*/\*(.+)\*/#Us',
 
// eliminate multi-line comments in '/* ... */' form, at end of string
'#/\*(.+)\*/\s*$#Us'
 
), '', $str);
 
// eliminate extraneous space
return trim($str);
}
 
/**
* decodes a JSON string into appropriate variable
*
* @param string $str JSON-formatted string
*
* @return mixed number, boolean, string, array, or object
* corresponding to given JSON input string.
* See argument 1 to Services_JSON() above for object-output behavior.
* Note that decode() always returns strings
* in ASCII or UTF-8 format!
* @access public
*/
function decode($str)
{
$str = $this->reduce_string($str);
 
switch (strtolower($str)) {
case 'true':
return true;
 
case 'false':
return false;
 
case 'null':
return null;
 
default:
$m = array();
 
if (is_numeric($str)) {
// Lookie-loo, it's a number
 
// This would work on its own, but I'm trying to be
// good about returning integers where appropriate:
// return (float)$str;
 
// Return float or int, as appropriate
return ((float)$str == (integer)$str)
? (integer)$str
: (float)$str;
 
} elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) {
// STRINGS RETURNED IN UTF-8 FORMAT
$delim = substr($str, 0, 1);
$chrs = substr($str, 1, -1);
$utf8 = '';
$strlen_chrs = strlen($chrs);
 
for ($c = 0; $c < $strlen_chrs; ++$c) {
 
$substr_chrs_c_2 = substr($chrs, $c, 2);
$ord_chrs_c = ord($chrs{$c});
 
switch (true) {
case $substr_chrs_c_2 == '\b':
$utf8 .= chr(0x08);
++$c;
break;
case $substr_chrs_c_2 == '\t':
$utf8 .= chr(0x09);
++$c;
break;
case $substr_chrs_c_2 == '\n':
$utf8 .= chr(0x0A);
++$c;
break;
case $substr_chrs_c_2 == '\f':
$utf8 .= chr(0x0C);
++$c;
break;
case $substr_chrs_c_2 == '\r':
$utf8 .= chr(0x0D);
++$c;
break;
 
case $substr_chrs_c_2 == '\\"':
case $substr_chrs_c_2 == '\\\'':
case $substr_chrs_c_2 == '\\\\':
case $substr_chrs_c_2 == '\\/':
if (($delim == '"' && $substr_chrs_c_2 != '\\\'') ||
($delim == "'" && $substr_chrs_c_2 != '\\"')) {
$utf8 .= $chrs{++$c};
}
break;
 
case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)):
// single, escaped unicode character
$utf16 = chr(hexdec(substr($chrs, ($c + 2), 2)))
. chr(hexdec(substr($chrs, ($c + 4), 2)));
$utf8 .= $this->utf162utf8($utf16);
$c += 5;
break;
 
case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F):
$utf8 .= $chrs{$c};
break;
 
case ($ord_chrs_c & 0xE0) == 0xC0:
// characters U-00000080 - U-000007FF, mask 110XXXXX
//see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 2);
++$c;
break;
 
case ($ord_chrs_c & 0xF0) == 0xE0:
// characters U-00000800 - U-0000FFFF, mask 1110XXXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 3);
$c += 2;
break;
 
case ($ord_chrs_c & 0xF8) == 0xF0:
// characters U-00010000 - U-001FFFFF, mask 11110XXX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 4);
$c += 3;
break;
 
case ($ord_chrs_c & 0xFC) == 0xF8:
// characters U-00200000 - U-03FFFFFF, mask 111110XX
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 5);
$c += 4;
break;
 
case ($ord_chrs_c & 0xFE) == 0xFC:
// characters U-04000000 - U-7FFFFFFF, mask 1111110X
// see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8
$utf8 .= substr($chrs, $c, 6);
$c += 5;
break;
 
}
 
}
 
return $utf8;
 
} elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) {
// array, or object notation
 
if ($str{0} == '[') {
$stk = array(SERVICES_JSON_IN_ARR);
$arr = array();
} else {
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
$stk = array(SERVICES_JSON_IN_OBJ);
$obj = array();
} else {
$stk = array(SERVICES_JSON_IN_OBJ);
$obj = new stdClass();
}
}
 
array_push($stk, array('what' => SERVICES_JSON_SLICE,
'where' => 0,
'delim' => false));
 
$chrs = substr($str, 1, -1);
$chrs = $this->reduce_string($chrs);
 
if ($chrs == '') {
if (reset($stk) == SERVICES_JSON_IN_ARR) {
return $arr;
 
} else {
return $obj;
 
}
}
 
//print("\nparsing {$chrs}\n");
 
$strlen_chrs = strlen($chrs);
 
for ($c = 0; $c <= $strlen_chrs; ++$c) {
 
$top = end($stk);
$substr_chrs_c_2 = substr($chrs, $c, 2);
 
if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) {
// found a comma that is not inside a string, array, etc.,
// OR we've reached the end of the character list
$slice = substr($chrs, $top['where'], ($c - $top['where']));
array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false));
//print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
 
if (reset($stk) == SERVICES_JSON_IN_ARR) {
// we are in an array, so just push an element onto the stack
array_push($arr, $this->decode($slice));
 
} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
// we are in an object, so figure
// out the property name and set an
// element in an associative array,
// for now
$parts = array();
if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
// "name":value pair
$key = $this->decode($parts[1]);
$val = $this->decode($parts[2]);
 
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
$obj[$key] = $val;
} else {
$obj->$key = $val;
}
} elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) {
// name:value pair, where name is unquoted
$key = $parts[1];
$val = $this->decode($parts[2]);
 
if ($this->use & SERVICES_JSON_LOOSE_TYPE) {
$obj[$key] = $val;
} else {
$obj->$key = $val;
}
}
 
}
 
} elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) {
// found a quote, and we are not inside a string
array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c}));
//print("Found start of string at {$c}\n");
 
} elseif (($chrs{$c} == $top['delim']) &&
($top['what'] == SERVICES_JSON_IN_STR) &&
((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) {
// found a quote, we're in a string, and it's not escaped
// we know that it's not escaped becase there is _not_ an
// odd number of backslashes at the end of the string so far
array_pop($stk);
//print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n");
 
} elseif (($chrs{$c} == '[') &&
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
// found a left-bracket, and we are in an array, object, or slice
array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false));
//print("Found start of array at {$c}\n");
 
} elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) {
// found a right-bracket, and we're in an array
array_pop($stk);
//print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
 
} elseif (($chrs{$c} == '{') &&
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
// found a left-brace, and we are in an array, object, or slice
array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false));
//print("Found start of object at {$c}\n");
 
} elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) {
// found a right-brace, and we're in an object
array_pop($stk);
//print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
 
} elseif (($substr_chrs_c_2 == '/*') &&
in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) {
// found a comment start, and we are in an array, object, or slice
array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false));
$c++;
//print("Found start of comment at {$c}\n");
 
} elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) {
// found a comment end, and we're in one now
array_pop($stk);
$c++;
 
for ($i = $top['where']; $i <= $c; ++$i)
$chrs = substr_replace($chrs, ' ', $i, 1);
 
//print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n");
 
}
 
}
 
if (reset($stk) == SERVICES_JSON_IN_ARR) {
return $arr;
 
} elseif (reset($stk) == SERVICES_JSON_IN_OBJ) {
return $obj;
 
}
 
}
}
}
 
/**
* @todo Ultimately, this should just call PEAR::isError()
*/
function isError($data, $code = null)
{
if (class_exists('pear')) {
return PEAR::isError($data, $code);
} elseif (is_object($data) && (get_class($data) == 'services_json_error' ||
is_subclass_of($data, 'services_json_error'))) {
return true;
}
 
return false;
}
}
 
if (class_exists('PEAR_Error')) {
 
class Services_JSON_Error extends PEAR_Error
{
function Services_JSON_Error($message = 'unknown error', $code = null,
$mode = null, $options = null, $userinfo = null)
{
parent::PEAR_Error($message, $code, $mode, $options, $userinfo);
}
}
 
} else {
 
/**
* @todo Ultimately, this class shall be descended from PEAR_Error
*/
class Services_JSON_Error
{
function Services_JSON_Error($message = 'unknown error', $code = null,
$mode = null, $options = null, $userinfo = null)
{
 
}
}
 
}
?>
/trunk/applications/jrest/lib/PDF.php
New file
0,0 → 1,3001
<?php
/**
* File_PDF::
*
* The File_PDF:: class provides a PHP-only implementation of a PDF library.
* No external libs or PHP extensions are required.
*
* Based on the FPDF class by Olivier Plathey (http://www.fpdf.org).
*
* Copyright 2001-2003 Olivier Plathey <olivier@fpdf.org>
* Copyright 2003-2007 The Horde Project (http://www.horde.org/)
*
* See the enclosed file COPYING for license information (LGPL). If you
* did not receive this file, see http://www.fsf.org/copyleft/lgpl.html.
*
* $Horde: framework/File_PDF/PDF.php,v 1.48 2007/01/05 13:12:21 jan Exp $
*
* @author Olivier Plathey <olivier@fpdf.org>
* @author Marko Djukic <marko@oblo.com>
* @author Jan Schneider <jan@horde.org>
* @package File_PDF
* @category Fileformats
*/
class File_PDF {
 
/**
* Current page number.
*
* @var integer
*/
var $_page = 0;
 
/**
* Current object number.
*
* @var integer
*/
var $_n = 2;
 
/**
* Array of object offsets.
*
* @var array
*/
var $_offsets = array();
 
/**
* Buffer holding in-memory PDF.
*
* @var string
*/
var $_buffer = '';
 
/**
* Array containing the pages.
*
* @var array
*/
var $_pages = array();
 
/**
* Current document state.
* 0 - initial state
* 1 - document opened
* 2 - page opened
* 3 - document closed
*
* @var integer
*/
var $_state = 0;
 
/**
* Flag indicating if PDF file is to be compressed or not.
*
* @var boolean
*/
var $_compress;
 
/**
* The default page orientation.
*
* @var string
*/
var $_default_orientation;
 
/**
* The current page orientation.
*
* @var string
*/
var $_current_orientation;
 
/**
* Array indicating orientation changes.
*
* @var array
*/
var $_orientation_changes = array();
 
/**
* Current width of page format in points.
*
* @var float
*/
var $fwPt;
 
/**
* Current height of page format in points.
*
* @var float
*/
var $fhPt;
 
/**
* Current width of page format in user units.
*
* @var float
*/
var $fw;
 
/**
* Current height of page format in user units.
*
* @var float
*/
var $fh;
 
/**
* Current width of page in points.
*
* @var float
*/
var $wPt;
 
/**
* Current height of page in points.
*
* @var float
*/
var $hPt;
 
/**
* Current width of page in user units
*
* @var float
*/
var $w;
 
/**
* Current height of page in user units
*
* @var float
*/
var $h;
 
/**
* Scale factor (number of points in user units).
*
* @var float
*/
var $_scale;
 
/**
* Left page margin size.
*
* @var float
*/
var $_left_margin;
 
/**
* Top page margin size.
*
* @var float
*/
var $_top_margin;
 
/**
* Right page margin size.
*
* @var float
*/
var $_right_margin;
 
/**
* Break page margin size, the bottom margin which triggers a page break.
*
* @var float
*/
var $_break_margin;
 
/**
* Cell margin size.
*
* @var float
*/
var $_cell_margin;
 
/**
* The current horizontal position for cell positioning.
* Value is set in user units and is calculated from the top left corner
* as origin.
*
* @var float
*/
var $x;
 
/**
* The current vertical position for cell positioning.
* Value is set in user units and is calculated from the top left corner
* as origin.
*
* @var float
*/
var $y;
 
/**
* The height of the last cell printed.
*
* @var float
*/
var $_last_height;
 
/**
* Line width in user units.
*
* @var float
*/
var $_line_width;
 
/**
* An array of standard font names.
*
* @var array
*/
var $_core_fonts = array('courier' => 'Courier',
'courierB' => 'Courier-Bold',
'courierI' => 'Courier-Oblique',
'courierBI' => 'Courier-BoldOblique',
'helvetica' => 'Helvetica',
'helveticaB' => 'Helvetica-Bold',
'helveticaI' => 'Helvetica-Oblique',
'helveticaBI' => 'Helvetica-BoldOblique',
'times' => 'Times-Roman',
'timesB' => 'Times-Bold',
'timesI' => 'Times-Italic',
'timesBI' => 'Times-BoldItalic',
'symbol' => 'Symbol',
'zapfdingbats' => 'ZapfDingbats');
 
/**
* An array of used fonts.
*
* @var array
*/
var $_fonts = array();
 
/**
* An array of font files.
*
* @var array
*/
var $_font_files = array();
 
/**
* An array of encoding differences.
*
* @var array
*/
var $_diffs = array();
 
/**
* An array of used images.
*
* @var array
*/
var $_images = array();
 
/**
* An array of links in pages.
*
* @var array
*/
var $_page_links;
 
/**
* An array of internal links.
*
* @var array
*/
var $_links = array();
 
/**
* Current font family.
*
* @var string
*/
var $_font_family = '';
 
/**
* Current font style.
*
* @var string
*/
var $_font_style = '';
 
/**
* Underlining flag.
*
* @var boolean
*/
var $_underline = false;
 
/**
* An array containing current font info.
*
* @var array
*/
var $_current_font;
 
/**
* Current font size in points.
*
* @var float
*/
var $_font_size_pt = 12;
 
/**
* Current font size in user units.
*
* @var float
*/
var $_font_size;
 
/**
* Commands for filling color.
*
* @var string
*/
var $_fill_color = '0 g';
 
/**
* Commands for text color.
*
* @var string
*/
var $_text_color = '0 g';
 
/**
* Whether text color is different from fill color.
*
* @var boolean
*/
var $_color_flag = false;
 
/**
* Commands for drawing color.
*
* @var string
*/
var $_draw_color = '0 G';
 
/**
* Word spacing.
*
* @var integer
*/
var $_word_spacing = 0;
 
/**
* Automatic page breaking.
*
* @var boolean
*/
var $_auto_page_break;
 
/**
* Threshold used to trigger page breaks.
*
* @var float
*/
var $_page_break_trigger;
 
/**
* Flag set when processing footer.
*
* @var boolean
*/
var $_in_footer = false;
 
/**
* Zoom display mode.
*
* @var string
*/
var $_zoom_mode;
 
/**
* Layout display mode.
*
* @var string
*/
var $_layout_mode;
 
/**
* An array containing the document info, consisting of:
* - title
* - subject
* - author
* - keywords
* - creator
*
* @var array
*/
var $_info = array();
 
/**
* Alias for total number of pages.
*
* @var string
*/
var $_alias_nb_pages = '{nb}';
 
/**
* Attempts to return a conrete PDF instance. It allows to set up the page
* format, the orientation and the units of measurement used in all the
* methods (except for the font sizes).
*
* Example:<pre>
* $pdf = &File_PDF::factory(array('orientation' => 'P',
* 'unit' => 'mm',
* 'format' => 'A4'));</pre>
*
* @param array $params A hash with parameters for the created PDF object.
* Possible parameters are:
* orientation - Default page orientation. Possible
* values are (case insensitive):
* <pre>
* - P or Portrait (default)
* - L or Landscape
* </pre>
* unit - User measure units. Possible values values
* are:
* <pre>
* - pt: point
* - mm: millimeter (default)
* - cm: centimeter
* - in: inch
* </pre>
* A point equals 1/72 of inch, that is to say about
* 0.35 mm (an inch being 2.54 cm). This is a very
* common unit in typography; font sizes are
* expressed in that unit.
* format - The format used for pages. It can be
* either one of the following values (case
* insensitive):
* <pre>
* - A3
* - A4 (default)
* - A5
* - Letter
* - Legal
* </pre>
* or a custom format in the form of a two-element
* array containing the width and the height
* (expressed in the unit given by the unit
* parameter).
* @param string $class The concrete class name to return an instance of.
* Defaults to File_PDF.
*/
function &factory($params = array(), $class = 'File_PDF')
{
/* Check for PHP locale-related bug. */
if (1.1 == 1) {
$error = File_PDF::raiseError('Do not alter the locale before including the class file.');
return $error;
}
 
/* Default parameters. */
$defaults = array('orientation' => 'P', 'unit' => 'mm', 'format' => 'A4');
 
/* Backward compatibility with old method signature. */
/* Should be removed a few versions later. */
if (!is_array($params)) {
$class = 'File_PDF';
$params = $defaults;
$names = array_keys($defaults);
for ($i = 0; $i < func_num_args(); $i++) {
$params[$names[$i]] = func_get_arg($i);
}
} else {
$params = array_merge($defaults, $params);
}
 
/* Create the PDF object. */
$pdf = &new $class();
 
/* Scale factor. */
if ($params['unit'] == 'pt') {
$pdf->_scale = 1;
} elseif ($params['unit'] == 'mm') {
$pdf->_scale = 72 / 25.4;
} elseif ($params['unit'] == 'cm') {
$pdf->_scale = 72 / 2.54;
} elseif ($params['unit'] == 'in') {
$pdf->_scale = 72;
} else {
$error = File_PDF::raiseError(sprintf('Incorrect units: %s', $params['unit']));
return $error;
}
/* Page format. */
if (is_string($params['format'])) {
$params['format'] = strtolower($params['format']);
if ($params['format'] == 'a3') {
$params['format'] = array(841.89, 1190.55);
} elseif ($params['format'] == 'a4') {
$params['format'] = array(595.28, 841.89);
} elseif ($params['format'] == 'a5') {
$params['format'] = array(420.94, 595.28);
} elseif ($params['format'] == 'letter') {
$params['format'] = array(612, 792);
} elseif ($params['format'] == 'legal') {
$params['format'] = array(612, 1008);
} else {
$error = File_PDF::raiseError(sprintf('Unknown page format: %s', $params['format']));
return $error;
}
$pdf->fwPt = $params['format'][0];
$pdf->fhPt = $params['format'][1];
} else {
$pdf->fwPt = $params['format'][0] * $pdf->_scale;
$pdf->fhPt = $params['format'][1] * $pdf->_scale;
}
$pdf->fw = $pdf->fwPt / $pdf->_scale;
$pdf->fh = $pdf->fhPt / $pdf->_scale;
 
/* Page orientation. */
$params['orientation'] = strtolower($params['orientation']);
if ($params['orientation'] == 'p' || $params['orientation'] == 'portrait') {
$pdf->_default_orientation = 'P';
$pdf->wPt = $pdf->fwPt;
$pdf->hPt = $pdf->fhPt;
} elseif ($params['orientation'] == 'l' || $params['orientation'] == 'landscape') {
$pdf->_default_orientation = 'L';
$pdf->wPt = $pdf->fhPt;
$pdf->hPt = $pdf->fwPt;
} else {
$error = File_PDF::raiseError(sprintf('Incorrect orientation: %s', $params['orientation']));
return $error;
}
$pdf->_current_orientation = $pdf->_default_orientation;
$pdf->w = $pdf->wPt / $pdf->_scale;
$pdf->h = $pdf->hPt / $pdf->_scale;
 
/* Page margins (1 cm) */
$margin = 28.35 / $pdf->_scale;
$pdf->setMargins($margin, $margin);
 
/* Interior cell margin (1 mm) */
$pdf->_cell_margin = $margin / 10;
 
/* Line width (0.2 mm) */
$pdf->_line_width = .567 / $pdf->_scale;
 
/* Automatic page break */
$pdf->setAutoPageBreak(true, 2 * $margin);
 
/* Full width display mode */
$pdf->setDisplayMode('fullwidth');
 
/* Compression */
$pdf->setCompression(true);
 
return $pdf;
}
 
/**
* Returns a PEAR_Error object. Wraps around PEAR::raiseError() to
* avoid having to include PEAR.php unless an error occurs.
*
* @param mixed $error The error message.
*
* @return object PEAR_Error
*/
function raiseError($error)
{
require_once 'PEAR.php';
return PEAR::raiseError($error);
}
 
/**
* Defines the left, top and right margins. By default, they equal 1 cm.
* Call this method to change them.
*
* @param float $left Left margin.
* @param float $top Top margin.
* @param float $right Right margin. If not specified default to the value
* of the left one.
*
* @see File_PDF::setAutoPageBreak
* @see File_PDF::setLeftMargin
* @see File_PDF::setRightMargin
* @see File_PDF::setTopMargin
*/
function setMargins($left, $top, $right = null)
{
/* Set left and top margins. */
$this->_left_margin = $left;
$this->_top_margin = $top;
/* If no right margin set default to same as left. */
$this->_right_margin = (is_null($right) ? $left : $right);
}
 
/**
* Defines the left margin. The method can be called before creating the
* first page.
* If the current abscissa gets out of page, it is brought back to the
* margin.
*
* @param float $margin The margin.
*
* @see File_PDF::setAutoPageBreak
* @see File_PDF::setMargins
* @see File_PDF::setRightMargin
* @see File_PDF::setTopMargin
*/
function setLeftMargin($margin)
{
$this->_left_margin = $margin;
/* If there is a current page and the current X position is less than
* margin set the X position to the margin value. */
if ($this->_page > 0 && $this->x < $margin) {
$this->x = $margin;
}
}
 
/**
* Defines the top margin. The method can be called before creating the
* first page.
*
* @param float $margin The margin.
*/
function setTopMargin($margin)
{
$this->_top_margin = $margin;
}
 
/**
* Defines the right margin. The method can be called before creating the
* first page.
*
* @param float $margin The margin.
*/
function setRightMargin($margin)
{
$this->_right_margin = $margin;
}
 
/**
* Returns the actual page width.
*
* @since File_PDF 0.2.0
* @since Horde 3.2
*
* @return float The page width.
*/
function getPageWidth()
{
return ($this->w - $this->_right_margin - $this->_left_margin);
}
 
/**
* Returns the actual page height.
*
* @since File_PDF 0.2.0
* @since Horde 3.2
*
* @return float The page height.
*/
function getPageHeight()
{
return ($this->h - $this->_top_margin - $this->_break_margin);
}
 
/**
* Enables or disables the automatic page breaking mode. When enabling,
* the second parameter is the distance from the bottom of the page that
* defines the triggering limit. By default, the mode is on and the margin
* is 2 cm.
*
* @param boolean auto Boolean indicating if mode should be on or off.
* @param float $margin Distance from the bottom of the page.
*/
function setAutoPageBreak($auto, $margin = 0)
{
$this->_auto_page_break = $auto;
$this->_break_margin = $margin;
$this->_page_break_trigger = $this->h - $margin;
}
 
/**
* Defines the way the document is to be displayed by the viewer. The zoom
* level can be set: pages can be displayed entirely on screen, occupy the
* full width of the window, use real size, be scaled by a specific
* zooming factor or use viewer default (configured in the Preferences
* menu of Acrobat). The page layout can be specified too: single at once,
* continuous display, two columns or viewer default.
* By default, documents use the full width mode with continuous display.
*
* @param mixed $zoom The zoom to use. It can be one of the
* following string values:
* - fullpage: entire page on screen
* - fullwidth: maximum width of window
* - real: uses real size (100% zoom)
* - default: uses viewer default mode
* or a number indicating the zooming factor.
* @param string layout The page layout. Possible values are:
* - single: one page at once
* - continuous: pages in continuously
* - two: two pages on two columns
* - default: uses viewer default mode
* Default value is continuous.
*/
function setDisplayMode($zoom, $layout = 'continuous')
{
$zoom = strtolower($zoom);
if ($zoom == 'fullpage' || $zoom == 'fullwidth' || $zoom == 'real'
|| $zoom == 'default' || !is_string($zoom)) {
$this->_zoom_mode = $zoom;
} elseif ($zoom == 'zoom') {
$this->_zoom_mode = $layout;
} else {
return $this->raiseError(sprintf('Incorrect zoom display mode: %s', $zoom));
}
 
$layout = strtolower($layout);
if ($layout == 'single' || $layout == 'continuous' || $layout == 'two'
|| $layout == 'default') {
$this->_layout_mode = $layout;
} elseif ($zoom != 'zoom') {
return $this->raiseError(sprintf('Incorrect layout display mode: %s', $layout));
}
}
 
/**
* Activates or deactivates page compression. When activated, the internal
* representation of each page is compressed, which leads to a compression
* ratio of about 2 for the resulting document.
* Compression is on by default.
* Note: the Zlib extension is required for this feature. If not present,
* compression will be turned off.
*
* @param boolean $compress Boolean indicating if compression must be
* enabled or not.
*/
function setCompression($compress)
{
/* If no gzcompress function is available then default to false. */
$this->_compress = (function_exists('gzcompress') ? $compress : false);
}
 
/**
* Set the info to a document. Possible info settings are:
* - title
* - subject
* - author
* - keywords
* - creator
*
* @param mixed $info If passed as an array then the complete hash
* containing the info to be inserted into the
* document. Otherwise the name of setting to be set.
* @param string $value The value of the setting.
*/
function setInfo($info, $value = '')
{
if (is_array($info)) {
$this->_info = $info;
} else {
$this->_info[$info] = $value;
}
}
 
/**
* Defines an alias for the total number of pages. It will be substituted
* as the document is closed.
*
* Example:
* class My_File_PDF extends File_PDF {
* function footer()
* {
* // Go to 1.5 cm from bottom
* $this->setY(-15);
* // Select Arial italic 8
* $this->setFont('Arial', 'I', 8);
* // Print current and total page numbers
* $this->cell(0, 10, 'Page ' . $this->getPageNo() . '/{nb}', 0,
* 0, 'C');
* }
* }
* $pdf = &My_File_PDF::factory();
* $pdf->aliasNbPages();
*
* @param string $alias The alias. Default value: {nb}.
*
* @see File_PDF::getPageNo
* @see File_PDF::footer
*/
function aliasNbPages($alias = '{nb}')
{
$this->_alias_nb_pages = $alias;
}
 
/**
* This method begins the generation of the PDF document; it must be
* called before any output commands. No page is created by this method,
* therefore it is necessary to call File_PDF::addPage.
*
* @see File_PDF::addPage
* @see File_PDF::close
*/
function open()
{
$this->_beginDoc();
}
 
/**
* Terminates the PDF document. It is not necessary to call this method
* explicitly because File_PDF::output does it automatically.
* If the document contains no page, File_PDF::addPage is called to prevent
* from getting an invalid document.
*
* @see File_PDF::open
* @see File_PDF::output
*/
function close()
{
/* Terminate document */
if ($this->_page == 0) {
$this->addPage();
}
/* Page footer */
$this->_in_footer = true;
$this->footer();
$this->_in_footer = false;
/* Close page */
$this->_endPage();
/* Close document */
$this->_endDoc();
}
 
/**
* Adds a new page to the document. If a page is already present, the
* File_PDF::footer method is called first to output the footer. Then the
* page is added, the current position set to the top-left corner according
* to the left and top margins, and File_PDF::header is called to display
* the header.
* The font which was set before calling is automatically restored. There
* is no need to call File_PDF::setFont again if you want to continue with
* the same font. The same is true for colors and line width.
* The origin of the coordinate system is at the top-left corner and
* increasing ordinates go downwards.
*
* @param string $orientation Page orientation. Possible values
* are (case insensitive):
* - P or Portrait
* - L or Landscape
* The default value is the one passed to the
* constructor.
*
* @see File_PDF::PDF
* @see File_PDF::header
* @see File_PDF::footer
* @see File_PDF::setMargins
*/
function addPage($orientation = '')
{
/* For good measure make sure this is called. */
$this->_beginDoc();
 
/* Save style settings so that they are not overridden by footer(). */
$lw = $this->_line_width;
$dc = $this->_draw_color;
$fc = $this->_fill_color;
$tc = $this->_text_color;
$cf = $this->_color_flag;
if ($this->_page > 0) {
/* Page footer. */
$this->_in_footer = true;
$this->footer();
$this->_in_footer = false;
/* Close page. */
$this->_endPage();
}
/* Start new page. */
$this->_beginPage($orientation);
/* Set line cap style to square. */
$this->_out('2 J');
/* Set line width. */
$this->_line_width = $lw;
$this->_out(sprintf('%.2f w', $lw * $this->_scale));
/* Set font for the beginning of the page. */
$font_family = null;
if ($this->_font_family) {
$font_family = $this->_font_family;
$font_style = $this->_font_style . ($this->_underline ? 'U' : '');
$font_size = $this->_font_size_pt;
$this->setFont($font_family, $font_style, $font_size);
}
/* Set colors. */
$this->_fill_color = $fc;
/* Check if fill color has been set before this page. */
if ($this->_fill_color != '0 g') {
$this->_out($this->_fill_color);
}
$this->_draw_color = $dc;
/* Check if draw color has been set before this page. */
if ($this->_draw_color != '0 G') {
$this->_out($this->_draw_color);
}
$this->_text_color = $tc;
$this->_color_flag = $cf;
/* Page header. */
$this->header();
/* Restore line width. */
if ($this->_line_width != $lw) {
$this->_line_width = $lw;
$this->_out(sprintf('%.2f w', $lw * $this->_scale));
}
/* Make sure the font is set for this page as it was before the
* header. */
if ($font_family) {
$this->setFont($font_family, $font_style, $font_size, true);
}
/* Restore colors. */
if ($this->_draw_color != $dc) {
$this->_draw_color = $dc;
$this->_out($dc);
}
if ($this->_fill_color != $fc) {
$this->_fill_color = $fc;
$this->_out($fc);
}
$this->_text_color = $tc;
$this->_color_flag = $cf;
}
 
/**
* This method is used to render the page header. It is automatically
* called by File_PDF::addPage and should not be called directly by the
* application. The implementation in File_PDF:: is empty, so you have to
* subclass it and override the method if you want a specific processing.
*
* Example:
*
* class My_File_PDF extends File_PDF {
* function header()
* {
* // Select Arial bold 15
* $this->setFont('Arial', 'B', 15);
* // Move to the right
* $this->cell(80);
* // Framed title
* $this->cell(30, 10, 'Title', 1, 0, 'C');
* // Line break
* $this->newLine(20);
* }
* }
*
* @see File_PDF::footer
*/
function header()
{
/* To be implemented in your own inherited class. */
}
 
/**
* This method is used to render the page footer. It is automatically
* called by File_PDF::addPage and File_PDF::close and should not be called
* directly by the application. The implementation in File_PDF:: is empty,
* so you have to subclass it and override the method if you want a specific
* processing.
*
* Example:
*
* class My_File_PDF extends File_PDF {
* function footer()
* {
* // Go to 1.5 cm from bottom
* $this->setY(-15);
* // Select Arial italic 8
* $this->setFont('Arial', 'I', 8);
* // Print centered page number
* $this->cell(0, 10, 'Page ' . $this->getPageNo(), 0, 0, 'C');
* }
* }
*
* @see File_PDF::header
*/
function footer()
{
/* To be implemented in your own inherited class. */
}
 
/**
* Returns the current page number.
*
* @return integer
*
* @see File_PDF::aliasNbPages
*/
function getPageNo()
{
return $this->_page;
}
 
/**
* Sets the fill color.
*
* Depending on the colorspace called, the number of color component
* parameters required can be either 1, 3 or 4. The method can be called
* before the first page is created and the color is retained from page to
* page.
*
* @param string $cs Indicates the colorspace which can be either 'rgb',
* 'cmyk' or 'gray'. Defaults to 'rgb'.
* @param float $c1 First color component, floating point value between 0
* and 1. Required for gray, rgb and cmyk.
* @param float $c2 Second color component, floating point value between
* 0 and 1. Required for rgb and cmyk.
* @param float $c3 Third color component, floating point value between
* 0 and 1. Required for rgb and cmyk.
* @param float $c4 Fourth color component, floating point value between
* 0 and 1. Required for cmyk.
*
* @see File_PDF::setTextColor
* @see File_PDF::setDrawColor
* @see File_PDF::rect
* @see File_PDF::cell
* @see File_PDF::multiCell
*/
function setFillColor($cs = 'rgb', $c1, $c2 = 0, $c3 = 0, $c4 = 0)
{
$cs = strtolower($cs);
if ($cs == 'rgb') {
$this->_fill_color = sprintf('%.3f %.3f %.3f rg', $c1, $c2, $c3);
} elseif ($cs == 'cmyk') {
$this->_fill_color = sprintf('%.3f %.3f %.3f %.3f k', $c1, $c2, $c3, $c4);
} else {
$this->_fill_color = sprintf('%.3f g', $c1);
}
if ($this->_page > 0) {
$this->_out($this->_fill_color);
}
$this->_color_flag = $this->_fill_color != $this->_text_color;
}
 
/**
* Sets the text color.
*
* Depending on the colorspace called, the number of color component
* parameters required can be either 1, 3 or 4. The method can be called
* before the first page is created and the color is retained from page to
* page.
*
* @param string $cs Indicates the colorspace which can be either 'rgb',
* 'cmyk' or 'gray'. Defaults to 'rgb'.
* @param float $c1 First color component, floating point value between 0
* and 1. Required for gray, rgb and cmyk.
* @param float $c2 Second color component, floating point value between
* 0 and 1. Required for rgb and cmyk.
* @param float $c3 Third color component, floating point value between
* 0 and 1. Required for rgb and cmyk.
* @param float $c4 Fourth color component, floating point value between
* 0 and 1. Required for cmyk.
*
* @since File_PDF 0.2.0
* @since Horde 3.2
* @see File_PDF::setFillColor
* @see File_PDF::setDrawColor
* @see File_PDF::rect
* @see File_PDF::cell
* @see File_PDF::multiCell
*/
function setTextColor($cs = 'rgb', $c1, $c2 = 0, $c3 = 0, $c4 = 0)
{
$cs = strtolower($cs);
if ($cs == 'rgb') {
$this->_text_color = sprintf('%.3f %.3f %.3f rg', $c1, $c2, $c3);
} elseif ($cs == 'cmyk') {
$this->_text_color = sprintf('%.3f %.3f %.3f %.3f k', $c1, $c2, $c3, $c4);
} else {
$this->_text_color = sprintf('%.3f g', $c1);
}
if ($this->_page > 0) {
$this->_out($this->_text_color);
}
$this->_color_flag = $this->_fill_color != $this->_text_color;
}
 
/**
* Sets the draw color, used when drawing lines. Depending on the
* colorspace called, the number of color component parameters required
* can be either 1, 3 or 4. The method can be called before the first page
* is created and the color is retained from page to page.
*
* @param string $cs Indicates the colorspace which can be either 'rgb',
* 'cmyk' or 'gray'. Defaults to 'rgb'.
* @param float $c1 First color component, floating point value between 0
* and 1. Required for gray, rgb and cmyk.
* @param float $c2 Second color component, floating point value between
* 0 and 1. Required for rgb and cmyk.
* @param float $c3 Third color component, floating point value between 0
* and 1. Required for rgb and cmyk.
* @param float $c4 Fourth color component, floating point value between
* 0 and 1. Required for cmyk.
*
* @see File_PDF::setFillColor
* @see File_PDF::line
* @see File_PDF::rect
* @see File_PDF::cell
* @see File_PDF::multiCell
*/
function setDrawColor($cs = 'rgb', $c1, $c2 = 0, $c3 = 0, $c4 = 0)
{
$cs = strtolower($cs);
if ($cs == 'rgb') {
$this->_draw_color = sprintf('%.3f %.3f %.3f RG', $c1, $c2, $c3);
} elseif ($cs == 'cmyk') {
$this->_draw_color = sprintf('%.3f %.3f %.3f %.3f K', $c1, $c2, $c3, $c4);
} else {
$this->_draw_color = sprintf('%.3f G', $c1);
}
if ($this->_page > 0) {
$this->_out($this->_draw_color);
}
}
 
/**
* Returns the length of a text string. A font must be selected.
*
* @param string $text The text whose length is to be computed.
* @param boolean $pt Boolean to indicate if the width should be returned
* in points or user units. Default is 'false'.
*
* @return float
*/
function getStringWidth($text, $pt = false)
{
$text = (string)$text;
$width = 0;
$length = strlen($text);
for ($i = 0; $i < $length; $i++) {
$width += $this->_current_font['cw'][$text{$i}];
}
 
/* Adjust for word spacing. */
$width += $this->_word_spacing * substr_count($text, ' ') * $this->_current_font['cw'][' '];
 
if ($pt) {
return $width * $this->_font_size_pt / 1000;
} else {
return $width * $this->_font_size / 1000;
}
}
 
/**
* Defines the line width. By default, the value equals 0.2 mm. The method
* can be called before the first page is created and the value is
* retained from page to page.
*
* @param float $width The width.
*
* @see File_PDF::line
* @see File_PDF::rect
* @see File_PDF::cell
* @see File_PDF::multiCell
*/
function setLineWidth($width)
{
$this->_line_width = $width;
if ($this->_page > 0) {
$this->_out(sprintf('%.2f w', $width * $this->_scale));
}
}
 
/**
* Draws a line between two points.
*
* All coordinates can be negative to provide values from the right or
* bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
*
* @param float $x1 Abscissa of first point.
* @param float $y1 Ordinate of first point.
* @param float $x2 Abscissa of second point.
* @param float $y2 Ordinate of second point.
*
* @see File_PDF::setLineWidth
* @see File_PDF::setDrawColor.
*/
function line($x1, $y1, $x2, $y2)
{
if ($x1 < 0) {
$x1 += $this->w;
}
if ($y1 < 0) {
$y1 += $this->h;
}
if ($x2 < 0) {
$x2 += $this->w;
}
if ($y2 < 0) {
$y2 += $this->h;
}
 
$this->_out(sprintf('%.2f %.2f m %.2f %.2f l S', $x1 * $this->_scale, ($this->h - $y1) * $this->_scale, $x2 * $this->_scale, ($this->h - $y2) * $this->_scale));
}
 
/**
* Outputs a rectangle. It can be drawn (border only), filled (with no
* border) or both.
*
* All coordinates can be negative to provide values from the right or
* bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
*
* @param float $x Abscissa of upper-left corner.
* @param float $y Ordinate of upper-left corner.
* @param float $width Width.
* @param float $height Height.
* @param float $style Style of rendering. Possible values are:
* - D or empty string: draw (default)
* - F: fill
* - DF or FD: draw and fill
*
* @see File_PDF::setLineWidth
* @see File_PDF::setDrawColor
* @see File_PDF::setFillColor
*/
function rect($x, $y, $width, $height, $style = '')
{
if ($x < 0) {
$x += $this->w;
}
if ($y < 0) {
$y += $this->h;
}
 
$style = strtoupper($style);
if ($style == 'F') {
$op = 'f';
} elseif ($style == 'FD' || $style == 'DF') {
$op = 'B';
} else {
$op = 'S';
}
 
$x = $this->_toPt($x);
$y = $this->_toPt($y);
$width = $this->_toPt($width);
$height = $this->_toPt($height);
 
$this->_out(sprintf('%.2f %.2f %.2f %.2f re %s', $x, $this->hPt - $y, $width, -$height, $op));
}
 
/**
* Outputs a circle. It can be drawn (border only), filled (with no
* border) or both.
*
* All coordinates can be negative to provide values from the right or
* bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
*
* @param float $x Abscissa of the center of the circle.
* @param float $y Ordinate of the center of the circle.
* @param float $r Circle radius.
* @param string $style Style of rendering. Possible values are:
* - D or empty string: draw (default)
* - F: fill
* - DF or FD: draw and fill
*/
function circle($x, $y, $r, $style = '')
{
if ($x < 0) {
$x += $this->w;
}
if ($y < 0) {
$y += $this->h;
}
 
$style = strtolower($style);
if ($style == 'f') {
$op = 'f'; // Style is fill only.
} elseif ($style == 'fd' || $style == 'df') {
$op = 'B'; // Style is fill and stroke.
} else {
$op = 'S'; // Style is stroke only.
}
 
$x = $this->_toPt($x);
$y = $this->_toPt($y);
$r = $this->_toPt($r);
 
/* Invert the y scale. */
$y = $this->hPt - $y;
/* Length of the Bezier control. */
$b = $r * 0.552;
 
/* Move from the given origin and set the current point
* to the start of the first Bezier curve. */
$c = sprintf('%.2f %.2f m', $x - $r, $y);
$x = $x - $r;
/* First circle quarter. */
$c .= sprintf(' %.2f %.2f %.2f %.2f %.2f %.2f c',
$x, $y + $b, // First control point.
$x + $r - $b, $y + $r, // Second control point.
$x + $r, $y + $r); // Final point.
/* Set x/y to the final point. */
$x = $x + $r;
$y = $y + $r;
/* Second circle quarter. */
$c .= sprintf(' %.2f %.2f %.2f %.2f %.2f %.2f c',
$x + $b, $y,
$x + $r, $y - $r + $b,
$x + $r, $y - $r);
/* Set x/y to the final point. */
$x = $x + $r;
$y = $y - $r;
/* Third circle quarter. */
$c .= sprintf(' %.2f %.2f %.2f %.2f %.2f %.2f c',
$x, $y - $b,
$x - $r + $b, $y - $r,
$x - $r, $y - $r);
/* Set x/y to the final point. */
$x = $x - $r;
$y = $y - $r;
/* Fourth circle quarter. */
$c .= sprintf(' %.2f %.2f %.2f %.2f %.2f %.2f c %s',
$x - $b, $y,
$x - $r, $y + $r - $b,
$x - $r, $y + $r,
$op);
/* Output the whole string. */
$this->_out($c);
}
 
/**
* Imports a TrueType or Type1 font and makes it available. It is
* necessary to generate a font definition file first with the
* makefont.php utility.
* The location of the definition file (and the font file itself when
* embedding) must be found at the full path name included.
*
* Example:
* $pdf->addFont('Comic', 'I');
* is equivalent to:
* $pdf->addFont('Comic', 'I', 'comici.php');
*
* @param string $family Font family. The name can be chosen arbitrarily.
* If it is a standard family name, it will
* override the corresponding font.
* @param string $style Font style. Possible values are (case
* insensitive):
* - empty string: regular (default)
* - B: bold
* - I: italic
* - BI or IB: bold italic
* @param string $file The font definition file. By default, the name is
* built from the family and style, in lower case
* with no space.
*
* @see File_PDF::setFont
*/
function addFont($family, $style = '', $file = '')
{
$family = strtolower($family);
if ($family == 'arial') {
$family = 'helvetica';
}
 
$style = strtoupper($style);
if ($style == 'IB') {
$style = 'BI';
}
if (isset($this->_fonts[$family . $style])) {
return $this->raiseError(sprintf('Font already added: %s %s', $family, $style));
}
if ($file == '') {
$file = str_replace(' ', '', $family) . strtolower($style) . '.php';
}
include($file);
if (!isset($name)) {
return $this->raiseError('Could not include font definition file.');
}
$i = count($this->_fonts) + 1;
$this->_fonts[$family . $style] = array('i' => $i, 'type' => $type, 'name' => $name, 'desc' => $desc, 'up' => $up, 'ut' => $ut, 'cw' => $cw, 'enc' => $enc, 'file' => $file);
if ($diff) {
/* Search existing encodings. */
$d = 0;
$nb = count($this->_diffs);
for ($i = 1; $i <= $nb; $i++) {
if ($this->_diffs[$i] == $diff) {
$d = $i;
break;
}
}
if ($d == 0) {
$d = $nb + 1;
$this->_diffs[$d] = $diff;
}
$this->_fonts[$family.$style]['diff'] = $d;
}
if ($file) {
if ($type == 'TrueType') {
$this->_font_files[$file] = array('length1' => $originalsize);
} else {
$this->_font_files[$file] = array('length1' => $size1, 'length2' => $size2);
}
}
}
 
/**
* Sets the font used to print character strings. It is mandatory to call
* this method at least once before printing text or the resulting
* document would not be valid. The font can be either a standard one or a
* font added via the File_PDF::addFont method. Standard fonts use Windows
* encoding cp1252 (Western Europe).
* The method can be called before the first page is created and the font
* is retained from page to page.
* If you just wish to change the current font size, it is simpler to call
* File_PDF::setFontSize.
*
* @param string $family Family font. It can be either a name defined by
* File_PDF::addFont or one of the standard families
* (case insensitive):
* - Courier (fixed-width)
* - Helvetica or Arial (sans serif)
* - Times (serif)
* - Symbol (symbolic)
* - ZapfDingbats (symbolic)
* It is also possible to pass an empty string. In
* that case, the current family is retained.
* @param string $style Font style. Possible values are (case
* insensitive):
* - empty string: regular
* - B: bold
* - I: italic
* - U: underline
* or any combination. The default value is regular.
* Bold and italic styles do not apply to Symbol and
* ZapfDingbats.
* @param integer $size Font size in points. The default value is the
* current size. If no size has been specified since
* the beginning of the document, the value taken
* is 12.
* @param boolean $force Force the setting of the font. Each new page will
* require a new call to File_PDF::setFont and
* settings this to true will make sure that the
* checks for same font calls will be skipped.
*
* @see File_PDF::addFont
* @see File_PDF::setFontSize
* @see File_PDF::cell
* @see File_PDF::multiCell
* @see File_PDF::Write
*/
function setFont($family, $style = '', $size = null, $force = false)
{
$family = strtolower($family);
if ($family == 'arial') {
/* Use helvetica instead of arial. */
$family = 'helvetica';
} elseif ($family == 'symbol' || $family == 'zapfdingbats') {
/* These two fonts do not have styles available. */
$style = '';
}
 
$style = strtoupper($style);
 
/* Underline is handled separately, if specified in the style var
* remove it from the style and set the underline flag. */
if (strpos($style, 'U') !== false) {
$this->_underline = true;
$style = str_replace('U', '', $style);
} else {
$this->_underline = false;
}
 
if ($style == 'IB') {
$style = 'BI';
}
 
/* If no size specified, use current size. */
if (is_null($size)) {
$size = $this->_font_size_pt;
}
 
/* If font requested is already the current font and no force setting
* of the font is requested (eg. when adding a new page) don't bother
* with the rest of the function and simply return. */
if ($this->_font_family == $family && $this->_font_style == $style &&
$this->_font_size_pt == $size && !$force) {
return;
}
 
/* Set the font key. */
$fontkey = $family . $style;
 
/* Test if already cached. */
if (!isset($this->_fonts[$fontkey])) {
/* Get the character width definition file. */
$font_widths = &File_PDF::_getFontFile($fontkey);
if (is_a($font_widths, 'PEAR_Error')) {
return $font_widths;
}
 
$i = count($this->_fonts) + 1;
$this->_fonts[$fontkey] = array(
'i' => $i,
'type' => 'core',
'name' => $this->_core_fonts[$fontkey],
'up' => -100,
'ut' => 50,
'cw' => $font_widths[$fontkey]);
}
 
/* Store font information as current font. */
$this->_font_family = $family;
$this->_font_style = $style;
$this->_font_size_pt = $size;
$this->_font_size = $size / $this->_scale;
$this->_current_font = &$this->_fonts[$fontkey];
 
/* Output font information if at least one page has been defined. */
if ($this->_page > 0) {
$this->_out(sprintf('BT /F%d %.2f Tf ET', $this->_current_font['i'], $this->_font_size_pt));
}
}
 
/**
* Defines the size of the current font.
*
* @param float $size The size (in points).
*
* @see File_PDF::setFont
*/
function setFontSize($size)
{
/* If the font size is already the current font size, just return. */
if ($this->_font_size_pt == $size) {
return;
}
/* Set the current font size, both in points and scaled to user
* units. */
$this->_font_size_pt = $size;
$this->_font_size = $size / $this->_scale;
 
/* Output font information if at least one page has been defined. */
if ($this->_page > 0) {
$this->_out(sprintf('BT /F%d %.2f Tf ET', $this->_current_font['i'], $this->_font_size_pt));
}
}
 
/**
* Defines the style of the current font.
*
* @param string $style The font style.
*
* @see File_PDF::setFont
* @since File_PDF 0.2.0
* @since Horde 3.2
*/
function setFontStyle($style)
{
$this->setFont($this->_font_family, $style);
}
 
/**
* Creates a new internal link and returns its identifier. An internal
* link is a clickable area which directs to another place within the
* document.
* The identifier can then be passed to File_PDF::cell, File_PDF::write,
* File_PDF::image or File_PDF::link. The destination is defined with
* File_PDF::setLink.
*
* @see File_PDF::cell
* @see File_PDF::Write
* @see File_PDF::image
* @see File_PDF::Link
* @see File_PDF::SetLink
*/
function addLink()
{
$n = count($this->_links) + 1;
$this->_links[$n] = array(0, 0);
return $n;
}
 
/**
* Defines the page and position a link points to.
*
* @param integer $link The link identifier returned by File_PDF::addLink.
* @param float $y Ordinate of target position; -1 indicates the
* current position. The default value is 0 (top of
* page).
* @param integer $page Number of target page; -1 indicates the current
* page. This is the default value.
*
* @see File_PDF::addLink
*/
function setLink($link, $y = 0, $page = -1)
{
if ($y == -1) {
$y = $this->y;
}
if ($page == -1) {
$page = $this->_page;
}
$this->_links[$link] = array($page, $y);
}
 
/**
* Puts a link on a rectangular area of the page. Text or image links are
* generally put via File_PDF::cell, File_PDF::Write or File_PDF::image,
* but this method can be useful for instance to define a clickable area
* inside an image.
*
* All coordinates can be negative to provide values from the right or
* bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
*
* @param float $x Abscissa of the upper-left corner of the rectangle.
* @param float $y Ordinate of the upper-left corner of the rectangle.
* @param float $width Width of the rectangle.
* @param float $height Height of the rectangle.
* @param mixed $link URL or identifier returned by File_PDF::addLink.
*
* @see File_PDF::addLink
* @see File_PDF::cell
* @see File_PDF::Write
* @see File_PDF::image
*/
function link($x, $y, $width, $height, $link)
{
if ($x < 0) {
$x += $this->w;
}
if ($y < 0) {
$y += $this->h;
}
 
/* Set up the coordinates with correct scaling in pt. */
$x = $this->_toPt($x);
$y = $this->hPt - $this->_toPt($y);
$width = $this->_toPt($width);
$height = $this->_toPt($height);
 
/* Save link to page links array. */
$this->_link($x, $y, $width, $height, $link);
}
 
/**
* Prints a character string. The origin is on the left of the first
* character, on the baseline. This method allows to place a string
* precisely on the page, but it is usually easier to use File_PDF::cell,
* File_PDF::multiCell or File_PDF::Write which are the standard methods to
* print text.
*
* All coordinates can be negative to provide values from the right or
* bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
*
* @param float $x Abscissa of the origin.
* @param float $y Ordinate of the origin.
* @param string $text String to print.
*
* @see File_PDF::setFont
* @see File_PDF::cell
* @see File_PDF::multiCell
* @see File_PDF::Write
*/
function text($x, $y, $text)
{
if ($x < 0) {
$x += $this->w;
}
if ($y < 0) {
$y += $this->h;
}
 
/* Scale coordinates into points and set correct Y position. */
$x = $this->_toPt($x);
$y = $this->hPt - $this->_toPt($y);
 
/* Escape any potentially harmful characters. */
$text = $this->_escape($text);
 
$out = sprintf('BT %.2f %.2f Td (%s) Tj ET', $x, $y, $text);
if ($this->_underline && $text != '') {
$out .= ' ' . $this->_doUnderline($x, $y, $text);
}
if ($this->_color_flag) {
$out = sprintf('q %s %s Q', $this->_text_color, $out);
}
$this->_out($out);
}
 
/**
* Whenever a page break condition is met, the method is called, and the
* break is issued or not depending on the returned value. The default
* implementation returns a value according to the mode selected by
* File_PDF:setAutoPageBreak.
* This method is called automatically and should not be called directly
* by the application.
*
* @return boolean
*
* @see File_PDF::setAutoPageBreak.
*/
function acceptPageBreak()
{
return $this->_auto_page_break;
}
 
/**
* Prints a cell (rectangular area) with optional borders, background
* color and character string. The upper-left corner of the cell
* corresponds to the current position. The text can be aligned or
* centered. After the call, the current position moves to the right or to
* the next line. It is possible to put a link on the text.
* If automatic page breaking is enabled and the cell goes beyond the
* limit, a page break is done before outputting.
*
* @param float $width Cell width. If 0, the cell extends up to the right
* margin.
* @param float $height Cell height. Default value: 0.
* @param string $text String to print. Default value: empty.
* @param mixed $border Indicates if borders must be drawn around the
* cell. The value can be either a number:
* - 0: no border (default)
* - 1: frame
* or a string containing some or all of the
* following characters (in any order):
* - L: left
* - T: top
* - R: right
* - B: bottom
* @param integer $ln Indicates where the current position should go
* after the call. Possible values are:
* - 0: to the right (default)
* - 1: to the beginning of the next line
* - 2: below
* Putting 1 is equivalent to putting 0 and calling
* File_PDF::newLine just after.
* @param string $align Allows to center or align the text. Possible
* values are:
* - L or empty string: left (default)
* - C: center
* - R: right
* @param integer $fill Indicates if the cell fill type. Possible values
* are:
* - 0: transparent (default)
* - 1: painted
* @param string $link URL or identifier returned by
* File_PDF:addLink.
*
* @see File_PDF::setFont
* @see File_PDF::setDrawColor
* @see File_PDF::setFillColor
* @see File_PDF::setLineWidth
* @see File_PDF::addLink
* @see File_PDF::newLine
* @see File_PDF::multiCell
* @see File_PDF::Write
* @see File_PDF::setAutoPageBreak
*/
function cell($width, $height = 0, $text = '', $border = 0, $ln = 0,
$align = '', $fill = 0, $link = '')
{
$k = $this->_scale;
if ($this->y + $height > $this->_page_break_trigger &&
!$this->_in_footer && $this->AcceptPageBreak()) {
$x = $this->x;
$ws = $this->_word_spacing;
if ($ws > 0) {
$this->_word_spacing = 0;
$this->_out('0 Tw');
}
$this->addPage($this->_current_orientation);
$this->x = $x;
if ($ws > 0) {
$this->_word_spacing = $ws;
$this->_out(sprintf('%.3f Tw', $ws * $k));
}
}
if ($width == 0) {
$width = $this->w - $this->_right_margin - $this->x;
}
$s = '';
if ($fill == 1 || $border == 1) {
if ($fill == 1) {
$op = ($border == 1) ? 'B' : 'f';
} else {
$op = 'S';
}
$s = sprintf('%.2f %.2f %.2f %.2f re %s ', $this->x * $k, ($this->h - $this->y) * $k, $width * $k, -$height * $k, $op);
}
if (is_string($border)) {
if (strpos($border, 'L') !== false) {
$s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $this->x * $k, ($this->h - $this->y) * $k, $this->x * $k, ($this->h - ($this->y + $height)) * $k);
}
if (strpos($border, 'T') !== false) {
$s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $this->x * $k, ($this->h - $this->y) * $k, ($this->x + $width) * $k, ($this->h - $this->y) * $k);
}
if (strpos($border, 'R') !== false) {
$s .= sprintf('%.2f %.2f m %.2f %.2f l S ', ($this->x + $width) * $k, ($this->h - $this->y) * $k, ($this->x + $width) * $k, ($this->h - ($this->y + $height)) * $k);
}
if (strpos($border, 'B') !== false) {
$s .= sprintf('%.2f %.2f m %.2f %.2f l S ', $this->x * $k, ($this->h - ($this->y + $height)) * $k, ($this->x + $width) * $k, ($this->h - ($this->y + $height)) * $k);
}
}
if ($text != '') {
if ($align == 'R') {
$dx = $width - $this->_cell_margin - $this->getStringWidth($text);
} elseif ($align == 'C') {
$dx = ($width - $this->getStringWidth($text)) / 2;
} else {
$dx = $this->_cell_margin;
}
if ($this->_color_flag) {
$s .= 'q ' . $this->_text_color . ' ';
}
$text = str_replace(')', '\\)', str_replace('(', '\\(', str_replace('\\', '\\\\', $text)));
$test2 = ((.5 * $height) + (.3 * $this->_font_size));
$test1 = $this->fhPt - (($this->y + $test2) * $k);
$s .= sprintf('BT %.2f %.2f Td (%s) Tj ET', ($this->x + $dx) * $k, ($this->h - ($this->y + .5 * $height + .3 * $this->_font_size)) * $k, $text);
if ($this->_underline) {
$s .= ' ' . $this->_doUnderline($this->x + $dx, $this->y + .5 * $height + .3 * $this->_font_size, $text);
}
if ($this->_color_flag) {
$s .= ' Q';
}
if ($link) {
$this->link($this->x + $dx, $this->y + .5 * $height-.5 * $this->_font_size, $this->getStringWidth($text), $this->_font_size, $link);
}
}
if ($s) {
$this->_out($s);
}
$this->_last_height = $height;
if ($ln > 0) {
/* Go to next line. */
$this->y += $height;
if ($ln == 1) {
$this->x = $this->_left_margin;
}
} else {
$this->x += $width;
}
}
 
/**
* This method allows printing text with line breaks. They can be
* automatic (as soon as the text reaches the right border of the cell) or
* explicit (via the \n character). As many cells as necessary are output,
* one below the other.
* Text can be aligned, centered or justified. The cell block can be
* framed and the background painted.
*
* @param float $width Width of cells. If 0, they extend up to the right
* margin of the page.
* @param float $height Height of cells.
* @param string $text String to print.
* @param mixed $border Indicates if borders must be drawn around the cell
* block. The value can be either a number:
* - 0: no border (default)
* - 1: frame
* or a string containing some or all of the
* following characters (in any order):
* - L: left
* - T: top
* - R: right
* - B: bottom
* @param string $align Sets the text alignment. Possible values are:
* - L: left alignment
* - C: center
* - R: right alignment
* - J: justification (default value)
* @param integer $fill Indicates if the cell background must:
* - 0: transparent (default)
* - 1: painted
*
* @see File_PDF::setFont
* @see File_PDF::setDrawColor
* @see File_PDF::setFillColor
* @see File_PDF::setLineWidth
* @see File_PDF::cell
* @see File_PDF::write
* @see File_PDF::setAutoPageBreak
*/
function multiCell($width, $height, $text, $border = 0, $align = 'J',
$fill = 0)
{
$cw = &$this->_current_font['cw'];
if ($width == 0) {
$width = $this->w - $this->_right_margin - $this->x;
}
$wmax = ($width-2 * $this->_cell_margin) * 1000 / $this->_font_size;
$s = str_replace("\r", '', $text);
$nb = strlen($s);
if ($nb > 0 && $s[$nb-1] == "\n") {
$nb--;
}
$b = 0;
if ($border) {
if ($border == 1) {
$border = 'LTRB';
$b = 'LRT';
$b2 = 'LR';
} else {
$b2 = '';
if (strpos($border, 'L') !== false) {
$b2 .= 'L';
}
if (strpos($border, 'R') !== false) {
$b2 .= 'R';
}
$b = (strpos($border, 'T') !== false) ? $b2 . 'T' : $b2;
}
}
$sep = -1;
$i = 0;
$j = 0;
$l = 0;
$ns = 0;
$nl = 1;
while ($i < $nb) {
/* Get next character. */
$c = $s[$i];
if ($c == "\n") {
/* Explicit line break. */
if ($this->_word_spacing > 0) {
$this->_word_spacing = 0;
$this->_out('0 Tw');
}
$this->cell($width, $height, substr($s, $j, $i-$j), $b, 2, $align, $fill);
$i++;
$sep = -1;
$j = $i;
$l = 0;
$ns = 0;
$nl++;
if ($border && $nl == 2) {
$b = $b2;
}
continue;
}
if ($c == ' ') {
$sep = $i;
$ls = $l;
$ns++;
}
$l += $cw[$c];
if ($l > $wmax) {
/* Automatic line break. */
if ($sep == -1) {
if ($i == $j) {
$i++;
}
if ($this->_word_spacing > 0) {
$this->_word_spacing = 0;
$this->_out('0 Tw');
}
$this->cell($width, $height, substr($s, $j, $i - $j), $b, 2, $align, $fill);
} else {
if ($align == 'J') {
$this->_word_spacing = ($ns>1) ? ($wmax - $ls)/1000 * $this->_font_size / ($ns - 1) : 0;
$this->_out(sprintf('%.3f Tw', $this->_word_spacing * $this->_scale));
}
$this->cell($width, $height, substr($s, $j, $sep - $j), $b, 2, $align, $fill);
$i = $sep + 1;
}
$sep = -1;
$j = $i;
$l = 0;
$ns = 0;
$nl++;
if ($border && $nl == 2) {
$b = $b2;
}
} else {
$i++;
}
}
/* Last chunk. */
if ($this->_word_spacing > 0) {
$this->_word_spacing = 0;
$this->_out('0 Tw');
}
if ($border && strpos($border, 'B') !== false) {
$b .= 'B';
}
$this->cell($width, $height, substr($s, $j, $i), $b, 2, $align, $fill);
$this->x = $this->_left_margin;
}
 
/**
* This method prints text from the current position. When the right
* margin is reached (or the \n character is met) a line break occurs and
* text continues from the left margin. Upon method exit, the current
* position is left just at the end of the text.
* It is possible to put a link on the text.
*
* Example:
* //Begin with regular font
* $pdf->setFont('Arial','',14);
* $pdf->write(5,'Visit ');
* //Then put a blue underlined link
* $pdf->setTextColor(0,0,255);
* $pdf->setFont('','U');
* $pdf->write(5,'www.fpdf.org','http://www.fpdf.org');
*
* @param float $height Line height.
* @param string $text String to print.
* @param mixed $link URL or identifier returned by AddLink().
*
* @see File_PDF::setFont
* @see File_PDF::addLink
* @see File_PDF::multiCell
* @see File_PDF::setAutoPageBreak
*/
function write($height, $text, $link = '')
{
$cw = &$this->_current_font['cw'];
$width = $this->w - $this->_right_margin - $this->x;
$wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size;
$s = str_replace("\r", '', $text);
$nb = strlen($s);
$sep = -1;
$i = 0;
$j = 0;
$l = 0;
$nl = 1;
while ($i < $nb) {
/* Get next character. */
$c = $s{$i};
if ($c == "\n") {
/* Explicit line break. */
$this->cell($width, $height, substr($s, $j, $i - $j), 0, 2, '', 0, $link);
$i++;
$sep = -1;
$j = $i;
$l = 0;
if ($nl == 1) {
$this->x = $this->_left_margin;
$width = $this->w - $this->_right_margin - $this->x;
$wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size;
}
$nl++;
continue;
}
if ($c == ' ') {
$sep = $i;
$ls = $l;
}
$l += (isset($cw[$c]) ? $cw[$c] : 0);
if ($l > $wmax) {
/* Automatic line break. */
if ($sep == -1) {
if ($this->x > $this->_left_margin) {
/* Move to next line. */
$this->x = $this->_left_margin;
$this->y += $height;
$width = $this->w - $this->_right_margin - $this->x;
$wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size;
$i++;
$nl++;
continue;
}
if ($i == $j) {
$i++;
}
$this->cell($width, $height, substr($s, $j, $i - $j), 0, 2, '', 0, $link);
} else {
$this->cell($width, $height, substr($s, $j, $sep - $j), 0, 2, '', 0, $link);
$i = $sep + 1;
}
$sep = -1;
$j = $i;
$l = 0;
if ($nl == 1) {
$this->x = $this->_left_margin;
$width = $this->w - $this->_right_margin - $this->x;
$wmax = ($width - 2 * $this->_cell_margin) * 1000 / $this->_font_size;
}
$nl++;
} else {
$i++;
}
}
/* Last chunk. */
if ($i != $j) {
$this->cell($l / 1000 * $this->_font_size, $height, substr($s, $j, $i), 0, 0, '', 0, $link);
}
}
 
/**
* Writes text at an angle.
*
* All coordinates can be negative to provide values from the right or
* bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
*
* @param integer $x X coordinate.
* @param integer $y Y coordinate.
* @param string $text Text to write.
* @param float $text_angle Angle to rotate (Eg. 90 = bottom to top).
* @param float $font_angle Rotate characters as well as text.
*
* @see File_PDF::setFont
*/
function writeRotated($x, $y, $text, $text_angle, $font_angle = 0)
{
if ($x < 0) {
$x += $this->w;
}
if ($y < 0) {
$y += $this->h;
}
 
/* Escape text. */
$text = $this->_escape($text);
 
$font_angle += 90 + $text_angle;
$text_angle *= M_PI / 180;
$font_angle *= M_PI / 180;
 
$text_dx = cos($text_angle);
$text_dy = sin($text_angle);
$font_dx = cos($font_angle);
$font_dy = sin($font_angle);
 
$s= sprintf('BT %.2f %.2f %.2f %.2f %.2f %.2f Tm (%s) Tj ET',
$text_dx, $text_dy, $font_dx, $font_dy,
$x * $this->_scale, ($this->h-$y) * $this->_scale, $text);
 
if ($this->_draw_color) {
$s = 'q ' . $this->_draw_color . ' ' . $s . ' Q';
}
$this->_out($s);
}
 
/**
* Prints an image in the page. The upper-left corner and at least one of
* the dimensions must be specified; the height or the width can be
* calculated automatically in order to keep the image proportions.
* Supported formats are JPEG and PNG.
*
* All coordinates can be negative to provide values from the right or
* bottom edge of the page (since File_PDF 0.2.0, Horde 3.2).
*
* For JPEG, all flavors are allowed:
* - gray scales
* - true colors (24 bits)
* - CMYK (32 bits)
*
* For PNG, are allowed:
* - gray scales on at most 8 bits (256 levels)
* - indexed colors
* - true colors (24 bits)
* but are not supported:
* - Interlacing
* - Alpha channel
*
* If a transparent color is defined, it will be taken into account (but
* will be only interpreted by Acrobat 4 and above).
* The format can be specified explicitly or inferred from the file
* extension.
* It is possible to put a link on the image.
*
* Remark: if an image is used several times, only one copy will be
* embedded in the file.
*
* @param string $file Name of the file containing the image.
* @param float $x Abscissa of the upper-left corner.
* @param float $y Ordinate of the upper-left corner.
* @param float $width Width of the image in the page. If equal to zero,
* it is automatically calculated to keep the
* original proportions.
* @param float $height Height of the image in the page. If not specified
* or equal to zero, it is automatically calculated
* to keep the original proportions.
* @param string $type Image format. Possible values are (case
* insensitive) : JPG, JPEG, PNG. If not specified,
* the type is inferred from the file extension.
* @param mixed $link URL or identifier returned by File_PDF::addLink.
*
* @see File_PDF::addLink
*/
function image($file, $x, $y, $width = 0, $height = 0, $type = '',
$link = '')
{
if ($x < 0) {
$x += $this->w;
}
if ($y < 0) {
$y += $this->h;
}
 
if (!isset($this->_images[$file])) {
/* First use of image, get some file info. */
if ($type == '') {
$pos = strrpos($file, '.');
if ($pos === false) {
return $this->raiseError(sprintf('Image file has no extension and no type was specified: %s', $file));
}
$type = substr($file, $pos + 1);
}
$type = strtolower($type);
$mqr = get_magic_quotes_runtime();
set_magic_quotes_runtime(0);
if ($type == 'jpg' || $type == 'jpeg') {
$info = $this->_parseJPG($file);
} elseif ($type == 'png') {
$info = $this->_parsePNG($file);
} else {
return $this->raiseError(sprintf('Unsupported image file type: %s', $type));
}
if (is_a($info, 'PEAR_Error')) {
return $info;
}
set_magic_quotes_runtime($mqr);
$info['i'] = count($this->_images) + 1;
$this->_images[$file] = $info;
} else {
$info = $this->_images[$file];
}
 
/* Make sure all vars are converted to pt scale. */
$x = $this->_toPt($x);
$y = $this->_toPt($y);
$width = $this->_toPt($width);
$height = $this->_toPt($height);
 
/* If not specified do automatic width and height calculations. */
if (empty($width) && empty($height)) {
$width = $info['w'];
$height = $info['h'];
} elseif (empty($width)) {
$width = $height * $info['w'] / $info['h'];
} elseif (empty($height)) {
$height = $width * $info['h'] / $info['w'];
}
 
$this->_out(sprintf('q %.2f 0 0 %.2f %.2f %.2f cm /I%d Do Q', $width, $height, $x, $this->hPt - ($y + $height), $info['i']));
 
/* Set any link if requested. */
if ($link) {
$this->_link($x, $y, $width, $height, $link);
}
}
 
/**
* Performs a line break. The current abscissa goes back to the left
* margin and the ordinate increases by the amount passed in parameter.
*
* @param float $height The height of the break. By default, the value
* equals the height of the last printed cell.
*
* @see File_PDF::cell
*/
function newLine($height = '')
{
$this->x = $this->_left_margin;
if (is_string($height)) {
$this->y += $this->_last_height;
} else {
$this->y += $height;
}
}
 
/**
* Returns the abscissa of the current position in user units.
*
* @return float
*
* @see File_PDF::setX
* @see File_PDF::getY
* @see File_PDF::setY
*/
function getX()
{
return $this->x;
}
 
/**
* Defines the abscissa of the current position. If the passed value is
* negative, it is relative to the right of the page.
*
* @param float $x The value of the abscissa.
*
* @see File_PDF::getX
* @see File_PDF::getY
* @see File_PDF::setY
* @see File_PDF::setXY
*/
function setX($x)
{
if ($x >= 0) {
/* Absolute value. */
$this->x = $x;
} else {
/* Negative, so relative to right edge of the page. */
$this->x = $this->w + $x;
}
}
 
/**
* Returns the ordinate of the current position in user units.
*
* @return float
*
* @see File_PDF::setY
* @see File_PDF::getX
* @see File_PDF::setX
*/
function getY()
{
return $this->y;
}
 
/**
* Defines the ordinate of the current position. If the passed value is
* negative, it is relative to the bottom of the page.
*
* @param float $y The value of the ordinate.
*
* @see File_PDF::getX
* @see File_PDF::getY
* @see File_PDF::setY
* @see File_PDF::setXY
*/
function setY($y)
{
if ($y >= 0) {
/* Absolute value. */
$this->y = $y;
} else {
/* Negative, so relative to bottom edge of the page. */
$this->y = $this->h + $y;
}
}
 
/**
* Defines the abscissa and ordinate of the current position. If the
* passed values are negative, they are relative respectively to the right
* and bottom of the page.
*
* @param float $x The value of the abscissa.
* @param float $y The value of the ordinate.
*
* @see File_PDF::setX
* @see File_PDF::setY
*/
function setXY($x, $y)
{
$this->setY($y);
$this->setX($x);
}
 
/**
* Returns the raw PDF file.
*
* @see File_PDF::output
*/
function getOutput()
{
/* Check whether file has been closed. */
if ($this->_state < 3) {
$this->close();
}
 
return $this->_buffer;
}
 
/**
* Function to output the buffered data to the browser.
*
* @param string $filename The filename for the output file.
* @param boolean $inline True if inline, false if attachment.
*/
function output($filename = 'unknown.pdf', $inline = false)
{
/* Check whether file has been closed. */
if ($this->_state < 3) {
$this->close();
}
 
/* Check if headers have been sent. */
if (headers_sent()) {
return $this->raiseError('Unable to send PDF file, some data has already been output to browser.');
}
 
/* If HTTP_Download is not available return a PEAR_Error. */
if (!include_once 'HTTP/Download.php') {
return $this->raiseError('Missing PEAR package HTTP_Download.');
}
 
/* Params for the output. */
$disposition = !$inline ? HTTP_DOWNLOAD_ATTACHMENT : HTTP_DOWNLOAD_INLINE;
$params = array('data' => $this->_buffer,
'contenttype' => 'application/pdf',
'contentdisposition' => array($disposition, $filename));
/* Output the file. */
return HTTP_Download::staticSend($params);
}
 
/**
* Function to save the PDF file somewhere local to the server.
*
* @param string $filename The filename for the output file.
*/
function save($filename = 'unknown.pdf')
{
/* Check whether file has been closed. */
if ($this->_state < 3) {
$this->close();
}
 
$f = fopen($filename, 'wb');
if (!$f) {
return $this->raiseError(sprintf('Unable to save PDF file: %s', $filename));
}
fwrite($f, $this->_buffer, strlen($this->_buffer));
fclose($f);
}
 
function _toPt($val)
{
return $val * $this->_scale;
}
 
function &_getFontFile($fontkey, $path = '')
{
static $font_widths;
 
if (!isset($font_widths[$fontkey])) {
if (!empty($path)) {
$file = $path . strtolower($fontkey) . '.php';
} else {
$file = 'File/PDF/fonts/' . strtolower($fontkey) . '.php';
}
include $file;
if (!isset($font_widths[$fontkey])) {
return $this->raiseError(sprintf('Could not include font metric file: %s', $file));
}
}
 
return $font_widths;
}
 
function _link($x, $y, $width, $height, $link)
{
/* Save link to page links array. */
$this->_page_links[$this->_page][] = array($x, $y, $width, $height, $link);
}
 
function _beginDoc()
{
/* Start document, but only if not yet started. */
if ($this->_state < 1) {
$this->_state = 1;
$this->_out('%PDF-1.3');
}
}
 
function _putPages()
{
$nb = $this->_page;
if (!empty($this->_alias_nb_pages)) {
/* Replace number of pages. */
for ($n = 1; $n <= $nb; $n++) {
$this->_pages[$n] = str_replace($this->_alias_nb_pages, $nb, $this->_pages[$n]);
}
}
if ($this->_default_orientation == 'P') {
$wPt = $this->fwPt;
$hPt = $this->fhPt;
} else {
$wPt = $this->fhPt;
$hPt = $this->fwPt;
}
$filter = ($this->_compress) ? '/Filter /FlateDecode ' : '';
for ($n = 1; $n <= $nb; $n++) {
/* Page */
$this->_newobj();
$this->_out('<</Type /Page');
$this->_out('/Parent 1 0 R');
if (isset($this->_orientation_changes[$n])) {
$this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]', $hPt, $wPt));
}
$this->_out('/Resources 2 0 R');
if (isset($this->_page_links[$n])) {
/* Links */
$annots = '/Annots [';
foreach ($this->_page_links[$n] as $pl) {
$rect = sprintf('%.2f %.2f %.2f %.2f', $pl[0], $pl[1], $pl[0] + $pl[2], $pl[1] - $pl[3]);
$annots .= '<</Type /Annot /Subtype /Link /Rect [' . $rect . '] /Border [0 0 0] ';
if (is_string($pl[4])) {
$annots .= '/A <</S /URI /URI ' . $this->_textString($pl[4]) . '>>>>';
} else {
$l = $this->_links[$pl[4]];
$height = isset($this->_orientation_changes[$l[0]]) ? $wPt : $hPt;
$annots .= sprintf('/Dest [%d 0 R /XYZ 0 %.2f null]>>', 1 + 2 * $l[0], $height - $l[1] * $this->_scale);
}
}
$this->_out($annots.']');
}
$this->_out('/Contents ' . ($this->_n + 1) . ' 0 R>>');
$this->_out('endobj');
/* Page content */
$p = ($this->_compress) ? gzcompress($this->_pages[$n]) : $this->_pages[$n];
$this->_newobj();
$this->_out('<<' . $filter . '/Length ' . strlen($p) . '>>');
$this->_putStream($p);
$this->_out('endobj');
}
/* Pages root */
$this->_offsets[1] = strlen($this->_buffer);
$this->_out('1 0 obj');
$this->_out('<</Type /Pages');
$kids = '/Kids [';
for ($i = 0; $i < $nb; $i++) {
$kids .= (3 + 2 * $i) . ' 0 R ';
}
$this->_out($kids . ']');
$this->_out('/Count ' . $nb);
$this->_out(sprintf('/MediaBox [0 0 %.2f %.2f]', $wPt, $hPt));
$this->_out('>>');
$this->_out('endobj');
}
 
function _putFonts()
{
$nf = $this->_n;
foreach ($this->_diffs as $diff) {
/* Encodings */
$this->_newobj();
$this->_out('<</Type /Encoding /BaseEncoding /WinAnsiEncoding /Differences [' . $diff . ']>>');
$this->_out('endobj');
}
$mqr = get_magic_quotes_runtime();
set_magic_quotes_runtime(0);
foreach ($this->_font_files as $file => $info) {
/* Font file embedding. */
$this->_newobj();
$this->_font_files[$file]['n'] = $this->_n;
$size = filesize($file);
if (!$size) {
return $this->raiseError('Font file not found.');
}
$this->_out('<</Length ' . $size);
if (substr($file, -2) == '.z') {
$this->_out('/Filter /FlateDecode');
}
$this->_out('/Length1 ' . $info['length1']);
if (isset($info['length2'])) {
$this->_out('/Length2 ' . $info['length2'] . ' /Length3 0');
}
$this->_out('>>');
$f = fopen($file, 'rb');
$this->_putStream(fread($f, $size));
fclose($f);
$this->_out('endobj');
}
set_magic_quotes_runtime($mqr);
foreach ($this->_fonts as $k => $font) {
/* Font objects */
$this->_newobj();
$this->_fonts[$k]['n'] = $this->_n;
$name = $font['name'];
$this->_out('<</Type /Font');
$this->_out('/BaseFont /' . $name);
if ($font['type'] == 'core') {
/* Standard font. */
$this->_out('/Subtype /Type1');
if ($name != 'Symbol' && $name != 'ZapfDingbats') {
$this->_out('/Encoding /WinAnsiEncoding');
}
} else {
/* Additional font. */
$this->_out('/Subtype /' . $font['type']);
$this->_out('/FirstChar 32');
$this->_out('/LastChar 255');
$this->_out('/Widths ' . ($this->_n + 1) . ' 0 R');
$this->_out('/FontDescriptor ' . ($this->_n + 2) . ' 0 R');
if ($font['enc']) {
if (isset($font['diff'])) {
$this->_out('/Encoding ' . ($nf + $font['diff']).' 0 R');
} else {
$this->_out('/Encoding /WinAnsiEncoding');
}
}
}
$this->_out('>>');
$this->_out('endobj');
if ($font['type'] != 'core') {
/* Widths. */
$this->_newobj();
$cw = &$font['cw'];
$s = '[';
for ($i = 32; $i <= 255; $i++) {
$s .= $cw[chr($i)] . ' ';
}
$this->_out($s . ']');
$this->_out('endobj');
/* Descriptor. */
$this->_newobj();
$s = '<</Type /FontDescriptor /FontName /' . $name;
foreach ($font['desc'] as $k => $v) {
$s .= ' /' . $k . ' ' . $v;
}
$file = $font['file'];
if ($file) {
$s .= ' /FontFile' . ($font['type'] == 'Type1' ? '' : '2') . ' ' . $this->_font_files[$file]['n'] . ' 0 R';
}
$this->_out($s . '>>');
$this->_out('endobj');
}
}
}
 
function _putImages()
{
$filter = ($this->_compress) ? '/Filter /FlateDecode ' : '';
foreach ($this->_images as $file => $info) {
$this->_newobj();
$this->_images[$file]['n'] = $this->_n;
$this->_out('<</Type /XObject');
$this->_out('/Subtype /Image');
$this->_out('/Width ' . $info['w']);
$this->_out('/Height ' . $info['h']);
if ($info['cs'] == 'Indexed') {
$this->_out('/ColorSpace [/Indexed /DeviceRGB ' . (strlen($info['pal'])/3 - 1) . ' ' . ($this->_n + 1).' 0 R]');
} else {
$this->_out('/ColorSpace /' . $info['cs']);
if ($info['cs'] == 'DeviceCMYK') {
$this->_out('/Decode [1 0 1 0 1 0 1 0]');
}
}
$this->_out('/BitsPerComponent ' . $info['bpc']);
$this->_out('/Filter /' . $info['f']);
if (isset($info['parms'])) {
$this->_out($info['parms']);
}
if (isset($info['trns']) && is_array($info['trns'])) {
$trns = '';
$i_max = count($info['trns']);
for ($i = 0; $i < $i_max; $i++) {
$trns .= $info['trns'][$i] . ' ' . $info['trns'][$i].' ';
}
$this->_out('/Mask [' . $trns . ']');
}
$this->_out('/Length ' . strlen($info['data']) . '>>');
$this->_putStream($info['data']);
$this->_out('endobj');
 
/* Palette. */
if ($info['cs'] == 'Indexed') {
$this->_newobj();
$pal = ($this->_compress) ? gzcompress($info['pal']) : $info['pal'];
$this->_out('<<' . $filter . '/Length ' . strlen($pal) . '>>');
$this->_putStream($pal);
$this->_out('endobj');
}
}
}
 
function _putResources()
{
$this->_putFonts();
$this->_putImages();
/* Resource dictionary */
$this->_offsets[2] = strlen($this->_buffer);
$this->_out('2 0 obj');
$this->_out('<</ProcSet [/PDF /Text /ImageB /ImageC /ImageI]');
$this->_out('/Font <<');
foreach ($this->_fonts as $font) {
$this->_out('/F' . $font['i'] . ' ' . $font['n'] . ' 0 R');
}
$this->_out('>>');
if (count($this->_images)) {
$this->_out('/XObject <<');
foreach ($this->_images as $image) {
$this->_out('/I' . $image['i'] . ' ' . $image['n'] . ' 0 R');
}
$this->_out('>>');
}
$this->_out('>>');
$this->_out('endobj');
}
 
function _putInfo()
{
$this->_out('/Producer ' . $this->_textString('Horde PDF'));
if (!empty($this->_info['title'])) {
$this->_out('/Title ' . $this->_textString($this->_info['title']));
}
if (!empty($this->_info['subject'])) {
$this->_out('/Subject ' . $this->_textString($this->_info['subject']));
}
if (!empty($this->_info['author'])) {
$this->_out('/Author ' . $this->_textString($this->_info['author']));
}
if (!empty($this->keywords)) {
$this->_out('/Keywords ' . $this->_textString($this->keywords));
}
if (!empty($this->creator)) {
$this->_out('/Creator ' . $this->_textString($this->creator));
}
$this->_out('/CreationDate ' . $this->_textString('D:' . date('YmdHis')));
}
 
function _putCatalog()
{
$this->_out('/Type /Catalog');
$this->_out('/Pages 1 0 R');
if ($this->_zoom_mode == 'fullpage') {
$this->_out('/OpenAction [3 0 R /Fit]');
} elseif ($this->_zoom_mode == 'fullwidth') {
$this->_out('/OpenAction [3 0 R /FitH null]');
} elseif ($this->_zoom_mode == 'real') {
$this->_out('/OpenAction [3 0 R /XYZ null null 1]');
} elseif (!is_string($this->_zoom_mode)) {
$this->_out('/OpenAction [3 0 R /XYZ null null ' . ($this->_zoom_mode / 100).']');
}
if ($this->_layout_mode == 'single') {
$this->_out('/PageLayout /SinglePage');
} elseif ($this->_layout_mode == 'continuous') {
$this->_out('/PageLayout /OneColumn');
} elseif ($this->_layout_mode == 'two') {
$this->_out('/PageLayout /TwoColumnLeft');
}
}
 
function _putTrailer()
{
$this->_out('/Size ' . ($this->_n + 1));
$this->_out('/Root ' . $this->_n . ' 0 R');
$this->_out('/Info ' . ($this->_n - 1) . ' 0 R');
}
 
function _endDoc()
{
$this->_putPages();
$this->_putResources();
/* Info */
$this->_newobj();
$this->_out('<<');
$this->_putInfo();
$this->_out('>>');
$this->_out('endobj');
/* Catalog */
$this->_newobj();
$this->_out('<<');
$this->_putCatalog();
$this->_out('>>');
$this->_out('endobj');
/* Cross-ref */
$o = strlen($this->_buffer);
$this->_out('xref');
$this->_out('0 ' . ($this->_n + 1));
$this->_out('0000000000 65535 f ');
for ($i = 1; $i <= $this->_n; $i++) {
$this->_out(sprintf('%010d 00000 n ', $this->_offsets[$i]));
}
/* Trailer */
$this->_out('trailer');
$this->_out('<<');
$this->_putTrailer();
$this->_out('>>');
$this->_out('startxref');
$this->_out($o);
$this->_out('%%EOF');
$this->_state = 3;
}
 
function _beginPage($orientation)
{
$this->_page++;
$this->_pages[$this->_page] = '';
$this->_state = 2;
$this->x = $this->_left_margin;
$this->y = $this->_top_margin;
$this->_last_height = 0;
/* Page orientation */
if (!$orientation) {
$orientation = $this->_default_orientation;
} else {
$orientation = strtoupper($orientation[0]);
if ($orientation != $this->_default_orientation) {
$this->_orientation_changes[$this->_page] = true;
}
}
if ($orientation != $this->_current_orientation) {
/* Change orientation */
if ($orientation == 'P') {
$this->wPt = $this->fwPt;
$this->hPt = $this->fhPt;
$this->w = $this->fw;
$this->h = $this->fh;
} else {
$this->wPt = $this->fhPt;
$this->hPt = $this->fwPt;
$this->w = $this->fh;
$this->h = $this->fw;
}
$this->_page_break_trigger = $this->h - $this->_break_margin;
$this->_current_orientation = $orientation;
}
}
 
function _endPage()
{
/* End of page contents */
$this->_state = 1;
}
 
function _newobj()
{
/* Begin a new object */
$this->_n++;
$this->_offsets[$this->_n] = strlen($this->_buffer);
$this->_out($this->_n . ' 0 obj');
}
 
function _doUnderline($x, $y, $text)
{
/* Set the rectangle width according to text width. */
$width = $this->getStringWidth($text, true);
 
/* Set rectangle position and height, using underline position and
* thickness settings scaled by the font size. */
$y = $y + ($this->_current_font['up'] * $this->_font_size_pt / 1000);
$height = -$this->_current_font['ut'] * $this->_font_size_pt / 1000;
 
return sprintf('%.2f %.2f %.2f %.2f re f', $x, $y, $width, $height);
}
 
function _parseJPG($file)
{
/* Extract info from a JPEG file. */
$img = @getimagesize($file);
if (!$img) {
return $this->raiseError(sprintf('Missing or incorrect image file: %s', $file));
}
if ($img[2] != 2) {
return $this->raiseError(sprintf('Not a JPEG file: %s', $file));
}
if (!isset($img['channels']) || $img['channels'] == 3) {
$colspace = 'DeviceRGB';
} elseif ($img['channels'] == 4) {
$colspace = 'DeviceCMYK';
} else {
$colspace = 'DeviceGray';
}
$bpc = isset($img['bits']) ? $img['bits'] : 8;
 
/* Read whole file. */
$f = fopen($file, 'rb');
$data = fread($f, filesize($file));
fclose($f);
 
return array('w' => $img[0], 'h' => $img[1], 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'DCTDecode', 'data' => $data);
}
 
function _parsePNG($file)
{
/* Extract info from a PNG file. */
$f = fopen($file, 'rb');
if (!$f) {
return $this->raiseError(sprintf('Unable to open image file: %s', $file));
}
 
/* Check signature. */
if (fread($f, 8) != chr(137) . 'PNG' . chr(13) . chr(10) . chr(26) . chr(10)) {
return $this->raiseError(sprintf('Not a PNG file: %s', $file));
}
 
/* Read header chunk. */
fread($f, 4);
if (fread($f, 4) != 'IHDR') {
return $this->raiseError(sprintf('Incorrect PNG file: %s', $file));
}
$width = $this->_freadInt($f);
$height = $this->_freadInt($f);
$bpc = ord(fread($f, 1));
if ($bpc > 8) {
return $this->raiseError(sprintf('16-bit depth not supported: %s', $file));
}
$ct = ord(fread($f, 1));
if ($ct == 0) {
$colspace = 'DeviceGray';
} elseif ($ct == 2) {
$colspace = 'DeviceRGB';
} elseif ($ct == 3) {
$colspace = 'Indexed';
} else {
return $this->raiseError(sprintf('Alpha channel not supported: %s', $file));
}
if (ord(fread($f, 1)) != 0) {
return $this->raiseError(sprintf('Unknown compression method: %s', $file));
}
if (ord(fread($f, 1)) != 0) {
return $this->raiseError(sprintf('Unknown filter method: %s', $file));
}
if (ord(fread($f, 1)) != 0) {
return $this->raiseError(sprintf('Interlacing not supported: %s', $file));
}
fread($f, 4);
$parms = '/DecodeParms <</Predictor 15 /Colors ' . ($ct == 2 ? 3 : 1).' /BitsPerComponent ' . $bpc . ' /Columns ' . $width.'>>';
/* Scan chunks looking for palette, transparency and image data. */
$pal = '';
$trns = '';
$data = '';
do {
$n = $this->_freadInt($f);
$type = fread($f, 4);
if ($type == 'PLTE') {
/* Read palette */
$pal = fread($f, $n);
fread($f, 4);
} elseif ($type == 'tRNS') {
/* Read transparency info */
$t = fread($f, $n);
if ($ct == 0) {
$trns = array(ord(substr($t, 1, 1)));
} elseif ($ct == 2) {
$trns = array(ord(substr($t, 1, 1)), ord(substr($t, 3, 1)), ord(substr($t, 5, 1)));
} else {
$pos = strpos($t, chr(0));
if (is_int($pos)) {
$trns = array($pos);
}
}
fread($f, 4);
} elseif ($type == 'IDAT') {
/* Read image data block */
$data .= fread($f, $n);
fread($f, 4);
} elseif ($type == 'IEND') {
break;
} else {
fread($f, $n + 4);
}
} while ($n);
 
if ($colspace == 'Indexed' && empty($pal)) {
return $this->raiseError(sprintf('Missing palette in: %s', $file));
}
fclose($f);
 
return array('w' => $width, 'h' => $height, 'cs' => $colspace, 'bpc' => $bpc, 'f' => 'FlateDecode', 'parms' => $parms, 'pal' => $pal, 'trns' => $trns, 'data' => $data);
}
 
function _freadInt($f)
{
/* Read a 4-byte integer from file. */
$i = ord(fread($f, 1)) << 24;
$i += ord(fread($f, 1)) << 16;
$i += ord(fread($f, 1)) << 8;
$i += ord(fread($f, 1));
return $i;
}
 
function _textString($s)
{
/* Format a text string */
return '(' . $this->_escape($s) . ')';
}
 
function _escape($s)
{
/* Add \ before \, ( and ) */
return str_replace(array(')','(','\\'),
array('\\)','\\(','\\\\'),
$s);
}
 
function _putStream($s)
{
$this->_out('stream');
$this->_out($s);
$this->_out('endstream');
}
 
function _out($s)
{
/* Add a line to the document. */
if ($this->_state == 2) {
$this->_pages[$this->_page] .= $s . "\n";
} else {
$this->_buffer .= $s . "\n";
}
}
 
}
/trunk/applications/jrest/lib/SpreadsheetProductor.php
New file
0,0 → 1,15
<?php
 
 
Class SpreadsheetProductor {
function initSpreadsheet() {
require_once("Spreadsheet/Excel/Writer.php");
}
}
 
 
?>
/trunk/applications/jrest/lib/PDFProductor.php
New file
0,0 → 1,14
<?php
 
 
Class PDFProductor {
function initPDF() {
require_once("PDF.php");
}
}
 
 
?>
/trunk/applications/jrest/lib/Writer.php
New file
0,0 → 1,104
<?php
/*
* Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
*
* PERL Spreadsheet::WriteExcel module.
*
* The author of the Spreadsheet::WriteExcel module is John McNamara
* <jmcnamara@cpan.org>
*
* I _DO_ maintain this code, and John McNamara has nothing to do with the
* porting of this code to PHP. Any questions directly related to this
* class library should be directed to me.
*
* License Information:
*
* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets
* Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
 
require_once 'PEAR.php';
require_once 'Spreadsheet/Excel/Writer/Workbook.php';
 
/**
* Class for writing Excel Spreadsheets. This class should change COMPLETELY.
*
* @author Xavier Noguer <xnoguer@rezebra.com>
* @category FileFormats
* @package Spreadsheet_Excel_Writer
*/
 
class Spreadsheet_Excel_Writer extends Spreadsheet_Excel_Writer_Workbook
{
/**
* The constructor. It just creates a Workbook
*
* @param string $filename The optional filename for the Workbook.
* @return Spreadsheet_Excel_Writer_Workbook The Workbook created
*/
function Spreadsheet_Excel_Writer($filename = '')
{
$this->_filename = $filename;
$this->Spreadsheet_Excel_Writer_Workbook($filename);
}
 
/**
* Send HTTP headers for the Excel file.
*
* @param string $filename The filename to use for HTTP headers
* @access public
*/
function send($filename)
{
header("Content-type: application/vnd.ms-excel");
header("Content-Disposition: attachment; filename=\"$filename\"");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0,pre-check=0");
header("Pragma: public");
}
 
/**
* Utility function for writing formulas
* Converts a cell's coordinates to the A1 format.
*
* @access public
* @static
* @param integer $row Row for the cell to convert (0-indexed).
* @param integer $col Column for the cell to convert (0-indexed).
* @return string The cell identifier in A1 format
*/
function rowcolToCell($row, $col)
{
if ($col > 255) { //maximum column value exceeded
return new PEAR_Error("Maximum column value exceeded: $col");
}
 
$int = (int)($col / 26);
$frac = $col % 26;
$chr1 = '';
 
if ($int > 0) {
$chr1 = chr(ord('A') + $int - 1);
}
 
$chr2 = chr(ord('A') + $frac);
$row++;
 
return $chr1 . $chr2 . $row;
}
}
?>
/trunk/applications/jrest/lib/WdHTMLParser.php
New file
0,0 → 1,144
<?php
/**
*
* @author Olivier Laviale
* @see http://www.weirdog.com/blog/php/un-parser-html-des-plus-leger.html
*
*/
class WdHTMLParser {
private $encoding;
private $matches;
private $escaped;
private $opened = array();
public $malformed;
 
public function parse($html, $namespace=NULL, $encoding='utf-8') {
$this->malformed = false;
$this->encoding = $encoding;
// we take care of escaping comments and processing options. they will not be parsed
// and will end as text nodes
$html = $this->escapeSpecials($html);
// in order to create a tree, we first need to split the HTML using the markups,
// creating a nice flat array of texts and opening and closing markups.
//
// the array can be read as follows :
//
// i+0 => some text
// i+1 => '/' for closing markups, nothing otherwise
// i+2 => the markup it self, without the '<' '>'
//
// note that i+2 might end with a '/' indicating an auto-closing markup
$this->matches = preg_split('#<(/?)' . $namespace . '([^>]*)>#', $html, -1, PREG_SPLIT_DELIM_CAPTURE);
// the flat representation is now ready, we can create our tree
$tree = $this->buildTree();
 
// if comments or processing options where escaped, we can
// safely unescape them now
if ($this->escaped) {
$tree = $this->unescapeSpecials($tree);
}
return $tree;
}
private function escapeSpecials($html) {
// here we escape comments
$html = preg_replace_callback('#<\!--.+-->#sU', array($this, 'escapeSpecials_callback'), $html);
 
// and processing options
$html = preg_replace_callback('#<\?.+\?>#sU', array($this, 'escapeSpecials_callback'), $html);
return $html;
}
private function escapeSpecials_callback($m) {
$this->escaped = true;
$text = $m[0];
$text = str_replace(array('<', '>'), array("\x01", "\x02"), $text);
return $text;
}
 
private function unescapeSpecials($tree) {
return is_array($tree) ? array_map(array($this, 'unescapeSpecials'), $tree) : str_replace(array("\x01", "\x02"), array('<', '>'), $tree);
}
 
private function buildTree() {
$nodes = array();
$i = 0;
$text = NULL;
while (($value = array_shift($this->matches)) !== NULL) {
switch ($i++ % 3) {
case 0:
// if the trimed value is not empty we preserve the value,
// otherwise we discard it.
if (trim($value)){
$nodes[] = $value;
}
break;
case 1:
$closing = ($value == '/');
break;
case 2:
if (substr($value, -1, 1) == '/') {
// auto closing
$nodes[] = $this->parseMarkup(substr($value, 0, -1));
} else if ($closing) {
// closing markup
$open = array_pop($this->opened);
if ($value != $open) {
$this->error($value, $open);
}
 
return $nodes;
} else {
// this is an open markup with possible children
$node = $this->parseMarkup($value);
// push the markup name into the opened markups
$this->opened[] = $node['name'];
// create the node and parse its children
$node['children'] = $this->buildTree($this->matches);
$nodes[] = $node;
}
break;
}
}
return $nodes;
}
public function parseMarkup($markup) {
// get markup's name
preg_match('#^[^\s]+#', $markup, $matches);
$name = $matches[0];
 
// get markup's arguments
preg_match_all('#\s+([^=]+)\s*=\s*"([^"]+)"#', $markup, $matches, PREG_SET_ORDER);
// transform the matches into a nice key/value array
$args = array();
foreach ($matches as $m) {
// we unescape the html entities of the argument's value
$args[$m[1]] = html_entity_decode($m[2], ENT_QUOTES, $this->encoding);
}
 
return array('name' => $name, 'args' => $args);
}
public function error($markup, $expected) {
$this->malformed = true;
printf('unexpected closing markup "%s", should be "%s"', $markup, $expected);
}
}
 
?>
/trunk/applications/jrest/lib/Log.php
New file
0,0 → 1,195
<?php
//declare(encoding='UTF-8');
/**
* Classe permettant de logger des messages dans les fichier situés dans le dossier de log
*
* PHP Version 5
*
* @category PHP
* @package Framework
* @author aurelien <aurelien@tela-botanica.org>
* @copyright 2009 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @version SVN: <svn_id>
* @link /doc/framework/
*/
 
class Log {
 
/**
* Tableau associatif stockant les descripteurs de fichiers
*/
private static $fichiersLog = array();
 
/**
* Chemin de base du dossier log de l'application
*/
private static $cheminLogs = '';
 
/**
* Booleen indiquant si l'on peut correctement écrire dans les fichiers de logs
*/
private static $droitLogger = true;
 
/**
* Zone horaire (pour éviter des avertissements dans les dates)
*/
private static $timeZone = '';
 
/**
* Taille maximum d'un fichier de log avant que celui ne soit archivé (en octets)
*/
private static $tailleMax = 10000;
 
/**
* séparateur de chemin
*/
private static $sd = DIRECTORY_SEPARATOR;
 
/**
* Extension des fichiers de log
*/
private static $ext = '.log';
 
/**
* La classe registre se contient elle-même, (pour le pattern singleton)
*/
private static $log;
 
/**
* Constructeur par défaut, privé, car on accède à la classe par le getInstance
*/
private function __construct() {
 
self::$sd = $sd;
// gestion de la timezone pour éviter des erreurs
if(function_exists("date_default_timezone_set") and function_exists("date_default_timezone_get")) {
date_default_timezone_set(self::$timeZone);
}
 
if(!is_dir(self::$cheminLogs) || !is_writable(self::$cheminLogs)) {
self::desactiverEcriture();
}
}
 
public static function setCheminLog($nouveauCheminLogs) {
self::$cheminLogs = $nouveauCheminLogs;
}
 
public static function getCheminLog() {
return self::$cheminLogs;
}
 
public static function setTimeZone($NouvelleTimeZone) {
self::$timeZone = $NouvelleTimeZone;
}
 
public static function setTailleMax($nouvelleTailleMax) {
self::$tailleMax = $nouvelleTailleMax;
}
 
/**
* Fonction qui renvoie l'instance de classe en assurant son unicité, c'est l'unique méthode qui doit être
* utilisée pour récupérer l'objet Registre
* @return Log le gestionnaire de log en cours
*/
public static function getInstance() {
if (self::$log instanceof Log) {
return self::$log;
}
self::$log = new Log();
return self::$log;
}
 
/**
* Ajoute une entrée au log spécifié par le paramètre $nomFichier
* @param string $nomFichier le nom du fichier dans lequel écrire
*/
public static function ajouterEntree($nomFichier,$entree,$mode='a+') {
if(self::$droitLogger) {
$date = "\n"."\n".date('d m Y H:i')."\n" ;
 
// si le fichier est déjà dans le tableau et qu'on peut y écrire
if(self::verifierOuvrirFichier($nomFichier,$mode)) {
// on y écrit le message de log
fwrite(self::$fichiersLog[$nomFichier],$date.$entree);
// on vérifie si le fichier ne dépasse pas la taille maximale
self::verifierTailleFichierOuArchiver($nomFichier);
} else {
// sinon on interdit l'écriture
self::desactiverEcriture($nomFichier);
}
}
}
 
/**
* Vide un fichier log indiqué
* @param string $nomFichier le nom du fichier à vider
*/
public static function viderLog($nomFichier) {
ajouterEntree($nomFichier,'','w');
}
 
/**
* Vérifie la présence d'un fichier dans le tableau, ses droits d'écriture,
* l'ouvre si nécessaire
* @param string $nomFichier le nom du fichier dont on doit vérifier la présence
* @return boolean true si le fichier est ouvert ou maintenant accessible, false sinon
*/
public static function verifierOuvrirFichier($nomFichier,$mode) {
// le fichier est il déjà ouvert ?
if(in_array($nomFichier,self::$fichiersLog)) {
// si oui peut on y écrire ?
if(is_writable(self::$cheminLogs.$nomFichier.self::$ext)) {
// si oui on renvoie le descripteur
return true;
}
return false;
} else {
// sinon on l'ouvre
$fp = @fopen(self::$cheminLogs.$nomFichier.self::$ext,$mode);
// si l'ouverture a réussi et si le fichier a les droits d'écriture
if($fp && is_writable(self::$cheminLogs.$nomFichier.self::$ext)) {
// si oui on renvoie le descripteur qu'on ajoute au tableau
self::$fichiersLog[$nomFichier] = $fp;
return true;
}
return false;
}
}
 
/**
* Vérifie la taille d'un fichier donné et si celle ci est trop importante
* archive le fichier de log
* @param string $nomFichier nom du fichier à vérifier
*/
private static function verifierTailleFichierOuArchiver($nomFichier) {
if(filesize(self::$cheminLogs.$nomFichier.self::$ext) > self::$tailleMax) {
rename(self::$cheminLogs.$nomFichier.self::$ext,self::$cheminLogs.$nomFichier.date('d_m_Y_H:i').self::$ext);
self::ajouterEntree($nomFichier,'');
}
}
 
/**
* Désactive l'écriture du log et envoie un message au gestionnaire d'erreurs
* @param string $nomFichier le nom du fichier qui a causé l'erreur
*/
private static function desactiverEcriture($nomFichier = '') {
self::$droitLogger = false;
if($nomFichier != '') {
$fichierDossier = 'fichier '.$nomFichier ;
} else {
$fichierDossier = 'dossier des logs';
}
}
 
/**
* destructeur de classe, ferme les descripteurs ouverts
*/
public function __destruct() {
foreach(self::$fichiersLog as $nomFichier => $fp) {
fclose($fp);
}
}
}
?>
/trunk/applications/jrest/lib/Spreadsheet/Excel/Writer/BIFFwriter.php
New file
0,0 → 1,235
<?php
/*
* Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
*
* The majority of this is _NOT_ my code. I simply ported it from the
* PERL Spreadsheet::WriteExcel module.
*
* The author of the Spreadsheet::WriteExcel module is John McNamara
* <jmcnamara@cpan.org>
*
* I _DO_ maintain this code, and John McNamara has nothing to do with the
* porting of this code to PHP. Any questions directly related to this
* class library should be directed to me.
*
* License Information:
*
* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets
* Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
 
require_once('PEAR.php');
 
/**
* Class for writing Excel BIFF records.
*
* From "MICROSOFT EXCEL BINARY FILE FORMAT" by Mark O'Brien (Microsoft Corporation):
*
* BIFF (BInary File Format) is the file format in which Excel documents are
* saved on disk. A BIFF file is a complete description of an Excel document.
* BIFF files consist of sequences of variable-length records. There are many
* different types of BIFF records. For example, one record type describes a
* formula entered into a cell; one describes the size and location of a
* window into a document; another describes a picture format.
*
* @author Xavier Noguer <xnoguer@rezebra.com>
* @category FileFormats
* @package Spreadsheet_Excel_Writer
*/
 
class Spreadsheet_Excel_Writer_BIFFwriter extends PEAR
{
/**
* The BIFF/Excel version (5).
* @var integer
*/
var $_BIFF_version = 0x0500;
 
/**
* The byte order of this architecture. 0 => little endian, 1 => big endian
* @var integer
*/
var $_byte_order;
 
/**
* The string containing the data of the BIFF stream
* @var string
*/
var $_data;
 
/**
* The size of the data in bytes. Should be the same as strlen($this->_data)
* @var integer
*/
var $_datasize;
 
/**
* The maximun length for a BIFF record. See _addContinue()
* @var integer
* @see _addContinue()
*/
var $_limit;
/**
* Constructor
*
* @access public
*/
function Spreadsheet_Excel_Writer_BIFFwriter()
{
$this->_byte_order = '';
$this->_data = '';
$this->_datasize = 0;
$this->_limit = 2080;
// Set the byte order
$this->_setByteOrder();
}
 
/**
* Determine the byte order and store it as class data to avoid
* recalculating it for each call to new().
*
* @access private
*/
function _setByteOrder()
{
if ($this->_byte_order == '')
{
// Check if "pack" gives the required IEEE 64bit float
$teststr = pack("d", 1.2345);
$number = pack("C8", 0x8D, 0x97, 0x6E, 0x12, 0x83, 0xC0, 0xF3, 0x3F);
if ($number == $teststr) {
$byte_order = 0; // Little Endian
}
elseif ($number == strrev($teststr)){
$byte_order = 1; // Big Endian
}
else {
// Give up. I'll fix this in a later version.
$this->raiseError("Required floating point format not supported ".
"on this platform.");
}
}
$this->_byte_order = $byte_order;
}
 
/**
* General storage function
*
* @param string $data binary data to prepend
* @access private
*/
function _prepend($data)
{
if (strlen($data) > $this->_limit) {
$data = $this->_addContinue($data);
}
$this->_data = $data.$this->_data;
$this->_datasize += strlen($data);
}
 
/**
* General storage function
*
* @param string $data binary data to append
* @access private
*/
function _append($data)
{
if (strlen($data) > $this->_limit) {
$data = $this->_addContinue($data);
}
$this->_data = $this->_data.$data;
$this->_datasize += strlen($data);
}
 
/**
* Writes Excel BOF record to indicate the beginning of a stream or
* sub-stream in the BIFF file.
*
* @param integer $type type of BIFF file to write: 0x0005 Workbook, 0x0010 Worksheet.
* @access private
*/
function _storeBof($type)
{
$record = 0x0809; // Record identifier
$length = 0x0008; // Number of bytes to follow
$version = $this->_BIFF_version;
// According to the SDK $build and $year should be set to zero.
// However, this throws a warning in Excel 5. So, use these
// magic numbers.
$build = 0x096C;
$year = 0x07C9;
$header = pack("vv", $record, $length);
$data = pack("vvvv", $version, $type, $build, $year);
$this->_prepend($header.$data);
}
 
/**
* Writes Excel EOF record to indicate the end of a BIFF stream.
*
* @access private
*/
function _storeEof()
{
$record = 0x000A; // Record identifier
$length = 0x0000; // Number of bytes to follow
$header = pack("vv", $record, $length);
$this->_append($header);
}
 
/**
* Excel limits the size of BIFF records. In Excel 5 the limit is 2084 bytes. In
* Excel 97 the limit is 8228 bytes. Records that are longer than these limits
* must be split up into CONTINUE blocks.
*
* This function takes a long BIFF record and inserts CONTINUE records as
* necessary.
*
* @param string $data The original binary data to be written
* @return string A very convenient string of continue blocks
* @access private
*/
function _addContinue($data)
{
$limit = $this->_limit;
$record = 0x003C; // Record identifier
// The first 2080/8224 bytes remain intact. However, we have to change
// the length field of the record.
$tmp = substr($data, 0, 2).pack("v", $limit-4).substr($data, 4, $limit - 4);
$header = pack("vv", $record, $limit); // Headers for continue records
// Retrieve chunks of 2080/8224 bytes +4 for the header.
for($i = $limit; $i < strlen($data) - $limit; $i += $limit)
{
$tmp .= $header;
$tmp .= substr($data, $i, $limit);
}
// Retrieve the last chunk of data
$header = pack("vv", $record, strlen($data) - $i);
$tmp .= $header;
$tmp .= substr($data,$i,strlen($data) - $i);
return($tmp);
}
}
?>
/trunk/applications/jrest/lib/Spreadsheet/Excel/Writer/Format.php
New file
0,0 → 1,940
<?php
/*
* Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
*
* The majority of this is _NOT_ my code. I simply ported it from the
* PERL Spreadsheet::WriteExcel module.
*
* The author of the Spreadsheet::WriteExcel module is John McNamara
* <jmcnamara@cpan.org>
*
* I _DO_ maintain this code, and John McNamara has nothing to do with the
* porting of this code to PHP. Any questions directly related to this
* class library should be directed to me.
*
* License Information:
*
* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets
* Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
 
require_once('PEAR.php');
 
/**
* Class for generating Excel XF records (formats)
*
* @author Xavier Noguer <xnoguer@rezebra.com>
* @category FileFormats
* @package Spreadsheet_Excel_Writer
*/
 
class Spreadsheet_Excel_Writer_Format extends PEAR
{
/**
* The index given by the workbook when creating a new format.
* @var integer
*/
var $_xf_index;
 
/**
* Index to the FONT record.
* @var integer
*/
var $font_index;
 
/**
* The font name (ASCII).
* @var string
*/
var $_font_name;
 
/**
* Height of font (1/20 of a point)
* @var integer
*/
var $_size;
 
/**
* Bold style
* @var integer
*/
var $_bold;
 
/**
* Bit specifiying if the font is italic.
* @var integer
*/
var $_italic;
 
/**
* Index to the cell's color
* @var integer
*/
var $_color;
 
/**
* The text underline property
* @var integer
*/
var $_underline;
 
/**
* Bit specifiying if the font has strikeout.
* @var integer
*/
var $_font_strikeout;
 
/**
* Bit specifiying if the font has outline.
* @var integer
*/
var $_font_outline;
 
/**
* Bit specifiying if the font has shadow.
* @var integer
*/
var $_font_shadow;
 
/**
* 2 bytes specifiying the script type for the font.
* @var integer
*/
var $_font_script;
 
/**
* Byte specifiying the font family.
* @var integer
*/
var $_font_family;
 
/**
* Byte specifiying the font charset.
* @var integer
*/
var $_font_charset;
 
/**
* An index (2 bytes) to a FORMAT record (number format).
* @var integer
*/
var $_num_format;
 
/**
* Bit specifying if formulas are hidden.
* @var integer
*/
var $_hidden;
 
/**
* Bit specifying if the cell is locked.
* @var integer
*/
var $_locked;
 
/**
* The three bits specifying the text horizontal alignment.
* @var integer
*/
var $_text_h_align;
 
/**
* Bit specifying if the text is wrapped at the right border.
* @var integer
*/
var $_text_wrap;
 
/**
* The three bits specifying the text vertical alignment.
* @var integer
*/
var $_text_v_align;
 
/**
* 1 bit, apparently not used.
* @var integer
*/
var $_text_justlast;
 
/**
* The two bits specifying the text rotation.
* @var integer
*/
var $_rotation;
 
/**
* The cell's foreground color.
* @var integer
*/
var $_fg_color;
 
/**
* The cell's background color.
* @var integer
*/
var $_bg_color;
 
/**
* The cell's background fill pattern.
* @var integer
*/
var $_pattern;
 
/**
* Style of the bottom border of the cell
* @var integer
*/
var $_bottom;
 
/**
* Color of the bottom border of the cell.
* @var integer
*/
var $_bottom_color;
 
/**
* Style of the top border of the cell
* @var integer
*/
var $_top;
 
/**
* Color of the top border of the cell.
* @var integer
*/
var $_top_color;
 
/**
* Style of the left border of the cell
* @var integer
*/
var $_left;
 
/**
* Color of the left border of the cell.
* @var integer
*/
var $_left_color;
 
/**
* Style of the right border of the cell
* @var integer
*/
var $_right;
 
/**
* Color of the right border of the cell.
* @var integer
*/
var $_right_color;
 
/**
* Constructor
*
* @access public
* @param integer $index the XF index for the format.
* @param array $properties array with properties to be set on initialization.
*/
function Spreadsheet_Excel_Writer_Format($index = 0,$properties = array())
{
$this->_xf_index = $index;
$this->font_index = 0;
$this->_font_name = 'Arial';
$this->_size = 10;
$this->_bold = 0x0190;
$this->_italic = 0;
$this->_color = 0x7FFF;
$this->_underline = 0;
$this->_font_strikeout = 0;
$this->_font_outline = 0;
$this->_font_shadow = 0;
$this->_font_script = 0;
$this->_font_family = 0;
$this->_font_charset = 0;
$this->_num_format = 0;
$this->_hidden = 0;
$this->_locked = 1;
 
$this->_text_h_align = 0;
$this->_text_wrap = 0;
$this->_text_v_align = 2;
$this->_text_justlast = 0;
$this->_rotation = 0;
 
$this->_fg_color = 0x40;
$this->_bg_color = 0x41;
 
$this->_pattern = 0;
$this->_bottom = 0;
$this->_top = 0;
$this->_left = 0;
$this->_right = 0;
$this->_bottom_color = 0x40;
$this->_top_color = 0x40;
$this->_left_color = 0x40;
$this->_right_color = 0x40;
// Set properties passed to Spreadsheet_Excel_Writer_Workbook::addFormat()
foreach($properties as $property => $value)
{
if(method_exists($this,'set'.ucwords($property)))
{
$method_name = 'set'.ucwords($property);
$this->$method_name($value);
}
}
}
 
 
/**
* Generate an Excel BIFF XF record (style or cell).
*
* @param string $style The type of the XF record ('style' or 'cell').
* @return string The XF record
*/
function getXf($style)
{
// Set the type of the XF record and some of the attributes.
if ($style == "style") {
$style = 0xFFF5;
}
else {
$style = $this->_locked;
$style |= $this->_hidden << 1;
}
// Flags to indicate if attributes have been set.
$atr_num = ($this->_num_format != 0)?1:0;
$atr_fnt = ($this->font_index != 0)?1:0;
$atr_alc = ($this->_text_wrap)?1:0;
$atr_bdr = ($this->_bottom ||
$this->_top ||
$this->_left ||
$this->_right)?1:0;
$atr_pat = (($this->_fg_color != 0x40) ||
($this->_bg_color != 0x41) ||
$this->_pattern)?1:0;
$atr_prot = 0;
// Zero the default border colour if the border has not been set.
if ($this->_bottom == 0) {
$this->_bottom_color = 0;
}
if ($this->_top == 0) {
$this->_top_color = 0;
}
if ($this->_right == 0) {
$this->_right_color = 0;
}
if ($this->_left == 0) {
$this->_left_color = 0;
}
$record = 0x00E0; // Record identifier
$length = 0x0010; // Number of bytes to follow
$ifnt = $this->font_index; // Index to FONT record
$ifmt = $this->_num_format; // Index to FORMAT record
$align = $this->_text_h_align; // Alignment
$align |= $this->_text_wrap << 3;
$align |= $this->_text_v_align << 4;
$align |= $this->_text_justlast << 7;
$align |= $this->_rotation << 8;
$align |= $atr_num << 10;
$align |= $atr_fnt << 11;
$align |= $atr_alc << 12;
$align |= $atr_bdr << 13;
$align |= $atr_pat << 14;
$align |= $atr_prot << 15;
$icv = $this->_fg_color; // fg and bg pattern colors
$icv |= $this->_bg_color << 7;
$fill = $this->_pattern; // Fill and border line style
$fill |= $this->_bottom << 6;
$fill |= $this->_bottom_color << 9;
$border1 = $this->_top; // Border line style and color
$border1 |= $this->_left << 3;
$border1 |= $this->_right << 6;
$border1 |= $this->_top_color << 9;
$border2 = $this->_left_color; // Border color
$border2 |= $this->_right_color << 7;
$header = pack("vv", $record, $length);
$data = pack("vvvvvvvv", $ifnt, $ifmt, $style, $align,
$icv, $fill,
$border1, $border2);
return($header.$data);
}
/**
* Generate an Excel BIFF FONT record.
*
* @return string The FONT record
*/
function getFont()
{
$dyHeight = $this->_size * 20; // Height of font (1/20 of a point)
$icv = $this->_color; // Index to color palette
$bls = $this->_bold; // Bold style
$sss = $this->_font_script; // Superscript/subscript
$uls = $this->_underline; // Underline
$bFamily = $this->_font_family; // Font family
$bCharSet = $this->_font_charset; // Character set
$rgch = $this->_font_name; // Font name
$cch = strlen($rgch); // Length of font name
$record = 0x31; // Record identifier
$length = 0x0F + $cch; // Record length
$reserved = 0x00; // Reserved
$grbit = 0x00; // Font attributes
if ($this->_italic) {
$grbit |= 0x02;
}
if ($this->_font_strikeout) {
$grbit |= 0x08;
}
if ($this->_font_outline) {
$grbit |= 0x10;
}
if ($this->_font_shadow) {
$grbit |= 0x20;
}
$header = pack("vv", $record, $length);
$data = pack("vvvvvCCCCC", $dyHeight, $grbit, $icv, $bls,
$sss, $uls, $bFamily,
$bCharSet, $reserved, $cch);
return($header . $data. $this->_font_name);
}
/**
* Returns a unique hash key for a font.
* Used by Spreadsheet_Excel_Writer_Workbook::_storeAllFonts()
*
* The elements that form the key are arranged to increase the probability of
* generating a unique key. Elements that hold a large range of numbers
* (eg. _color) are placed between two binary elements such as _italic
*
* @return string A key for this font
*/
function getFontKey()
{
$key = "$this->_font_name$this->_size";
$key .= "$this->_font_script$this->_underline";
$key .= "$this->_font_strikeout$this->_bold$this->_font_outline";
$key .= "$this->_font_family$this->_font_charset";
$key .= "$this->_font_shadow$this->_color$this->_italic";
$key = str_replace(" ","_",$key);
return ($key);
}
/**
* Returns the index used by Spreadsheet_Excel_Writer_Worksheet::_XF()
*
* @return integer The index for the XF record
*/
function getXfIndex()
{
return($this->_xf_index);
}
/**
* Used in conjunction with the set_xxx_color methods to convert a color
* string into a number. Color range is 0..63 but we will restrict it
* to 8..63 to comply with Gnumeric. Colors 0..7 are repeated in 8..15.
*
* @access private
* @param string $name_color name of the color (i.e.: 'blue', 'red', etc..). Optional.
* @return integer The color index
*/
function _getColor($name_color = '')
{
$colors = array(
'aqua' => 0x0F,
'cyan' => 0x0F,
'black' => 0x08,
'blue' => 0x0C,
'brown' => 0x10,
'magenta' => 0x0E,
'fuchsia' => 0x0E,
'gray' => 0x17,
'grey' => 0x17,
'green' => 0x11,
'lime' => 0x0B,
'navy' => 0x12,
'orange' => 0x35,
'purple' => 0x14,
'red' => 0x0A,
'silver' => 0x16,
'white' => 0x09,
'yellow' => 0x0D
);
// Return the default color, 0x7FFF, if undef,
if($name_color == '') {
return(0x7FFF);
}
// or the color string converted to an integer,
if(isset($colors[$name_color])) {
return($colors[$name_color]);
}
// or the default color if string is unrecognised,
if(preg_match("/\D/",$name_color)) {
return(0x7FFF);
}
// or an index < 8 mapped into the correct range,
if($name_color < 8) {
return($name_color + 8);
}
// or the default color if arg is outside range,
if($name_color > 63) {
return(0x7FFF);
}
// or an integer in the valid range
return($name_color);
}
/**
* Set cell alignment.
*
* @access public
* @param string $location alignment for the cell ('left', 'right', etc...).
*/
function setAlign($location)
{
if (preg_match("/\d/",$location)) {
return; // Ignore numbers
}
$location = strtolower($location);
if ($location == 'left') {
$this->_text_h_align = 1;
}
if ($location == 'centre') {
$this->_text_h_align = 2;
}
if ($location == 'center') {
$this->_text_h_align = 2;
}
if ($location == 'right') {
$this->_text_h_align = 3;
}
if ($location == 'fill') {
$this->_text_h_align = 4;
}
if ($location == 'justify') {
$this->_text_h_align = 5;
}
if ($location == 'merge') {
$this->_text_h_align = 6;
}
if ($location == 'equal_space') { // For T.K.
$this->_text_h_align = 7;
}
if ($location == 'top') {
$this->_text_v_align = 0;
}
if ($location == 'vcentre') {
$this->_text_v_align = 1;
}
if ($location == 'vcenter') {
$this->_text_v_align = 1;
}
if ($location == 'bottom') {
$this->_text_v_align = 2;
}
if ($location == 'vjustify') {
$this->_text_v_align = 3;
}
if ($location == 'vequal_space') { // For T.K.
$this->_text_v_align = 4;
}
}
/**
* This is an alias for the unintuitive setAlign('merge')
*
* @access public
*/
function setMerge()
{
$this->setAlign('merge');
}
/**
* Sets the boldness of the text.
* Bold has a range 100..1000.
* 0 (400) is normal. 1 (700) is bold.
*
* @access public
* @param integer $weight Weight for the text, 0 maps to 400 (normal text),
1 maps to 700 (bold text). Valid range is: 100-1000.
It's Optional, default is 1 (bold).
*/
function setBold($weight = 1)
{
if($weight == 1) {
$weight = 0x2BC; // Bold text
}
if($weight == 0) {
$weight = 0x190; // Normal text
}
if($weight < 0x064) {
$weight = 0x190; // Lower bound
}
if($weight > 0x3E8) {
$weight = 0x190; // Upper bound
}
$this->_bold = $weight;
}
/************************************
* FUNCTIONS FOR SETTING CELLS BORDERS
*/
/**
* Sets the width for the bottom border of the cell
*
* @access public
* @param integer $style style of the cell border. 1 => thin, 2 => thick.
*/
function setBottom($style)
{
$this->_bottom = $style;
}
/**
* Sets the width for the top border of the cell
*
* @access public
* @param integer $style style of the cell top border. 1 => thin, 2 => thick.
*/
function setTop($style)
{
$this->_top = $style;
}
/**
* Sets the width for the left border of the cell
*
* @access public
* @param integer $style style of the cell left border. 1 => thin, 2 => thick.
*/
function setLeft($style)
{
$this->_left = $style;
}
/**
* Sets the width for the right border of the cell
*
* @access public
* @param integer $style style of the cell right border. 1 => thin, 2 => thick.
*/
function setRight($style)
{
$this->_right = $style;
}
/**
* Set cells borders to the same style
*
* @access public
* @param integer $style style to apply for all cell borders. 1 => thin, 2 => thick.
*/
function setBorder($style)
{
$this->setBottom($style);
$this->setTop($style);
$this->setLeft($style);
$this->setRight($style);
}
/*******************************************
* FUNCTIONS FOR SETTING CELLS BORDERS COLORS
*/
/**
* Sets all the cell's borders to the same color
*
* @access public
* @param mixed $color The color we are setting. Either a string (like 'blue'),
* or an integer (range is [8...63]).
*/
function setBorderColor($color)
{
$this->setBottomColor($color);
$this->setTopColor($color);
$this->setLeftColor($color);
$this->setRightColor($color);
}
/**
* Sets the cell's bottom border color
*
* @access public
* @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]).
*/
function setBottomColor($color)
{
$value = $this->_getColor($color);
$this->_bottom_color = $value;
}
/**
* Sets the cell's top border color
*
* @access public
* @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]).
*/
function setTopColor($color)
{
$value = $this->_getColor($color);
$this->_top_color = $value;
}
/**
* Sets the cell's left border color
*
* @access public
* @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]).
*/
function setLeftColor($color)
{
$value = $this->_getColor($color);
$this->_left_color = $value;
}
/**
* Sets the cell's right border color
*
* @access public
* @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]).
*/
function setRightColor($color)
{
$value = $this->_getColor($color);
$this->_right_color = $value;
}
/**
* Sets the cell's foreground color
*
* @access public
* @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]).
*/
function setFgColor($color)
{
$value = $this->_getColor($color);
$this->_fg_color = $value;
}
/**
* Sets the cell's background color
*
* @access public
* @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]).
*/
function setBgColor($color)
{
$value = $this->_getColor($color);
$this->_bg_color = $value;
}
/**
* Sets the cell's color
*
* @access public
* @param mixed $color either a string (like 'blue'), or an integer (range is [8...63]).
*/
function setColor($color)
{
$value = $this->_getColor($color);
$this->_color = $value;
}
/**
* Sets the fill pattern attribute of a cell
*
* @access public
* @param integer $arg Optional. Defaults to 1. Meaningful values are: 0-18,
* 0 meaning no background.
*/
function setPattern($arg = 1)
{
$this->_pattern = $arg;
}
/**
* Sets the underline of the text
*
* @access public
* @param integer $underline The value for underline. Possible values are:
* 1 => underline, 2 => double underline.
*/
function setUnderline($underline)
{
$this->_underline = $underline;
}
/**
* Sets the font style as italic
*
* @access public
*/
function setItalic()
{
$this->_italic = 1;
}
 
/**
* Sets the font size
*
* @access public
* @param integer $size The font size (in pixels I think).
*/
function setSize($size)
{
$this->_size = $size;
}
/**
* Sets text wrapping
*
* @access public
*/
function setTextWrap()
{
$this->_text_wrap = 1;
}
 
/**
* Sets the orientation of the text
*
* @access public
* @param integer $angle The rotation angle for the text (clockwise). Possible
values are: 0, 90, 270 and -1 for stacking top-to-bottom.
*/
function setTextRotation($angle)
{
switch ($angle)
{
case 0:
$this->_rotation = 0;
break;
case 90:
$this->_rotation = 3;
break;
case 270:
$this->_rotation = 2;
break;
case -1:
$this->_rotation = 1;
break;
default :
$this->raiseError("Invalid value for angle.".
" Possible values are: 0, 90, 270 and -1 ".
"for stacking top-to-bottom.");
$this->_rotation = 0;
break;
}
}
 
/**
* Sets the numeric format.
* It can be date, time, currency, etc...
*
* @access public
* @param integer $num_format The numeric format.
*/
function setNumFormat($num_format)
{
$this->_num_format = $num_format;
}
 
/**
* Sets font as strikeout.
*
* @access public
*/
function setStrikeOut()
{
$this->_font_strikeout = 1;
}
 
/**
* Sets outlining for a font.
*
* @access public
*/
function setOutLine()
{
$this->_font_outline = 1;
}
 
/**
* Sets font as shadow.
*
* @access public
*/
function setShadow()
{
$this->_font_shadow = 1;
}
 
/**
* Sets the script type of the text
*
* @access public
* @param integer $script The value for script type. Possible values are:
* 1 => superscript, 2 => subscript.
*/
function setScript($script)
{
$this->_font_script = $script;
}
 
/**
* Unlocks a cell. Useful for unprotecting particular cells of a protected sheet.
*
* @access public
*/
function setUnLocked()
{
$this->_locked = 0;
}
}
?>
/trunk/applications/jrest/lib/Spreadsheet/Excel/Writer/Worksheet.php
New file
0,0 → 1,3095
<?php
/*
* Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
*
* The majority of this is _NOT_ my code. I simply ported it from the
* PERL Spreadsheet::WriteExcel module.
*
* The author of the Spreadsheet::WriteExcel module is John McNamara
* <jmcnamara@cpan.org>
*
* I _DO_ maintain this code, and John McNamara has nothing to do with the
* porting of this code to PHP. Any questions directly related to this
* class library should be directed to me.
*
* License Information:
*
* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets
* Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
 
require_once('Parser.php');
require_once('BIFFwriter.php');
 
/**
* Class for generating Excel Spreadsheets
*
* @author Xavier Noguer <xnoguer@rezebra.com>
* @category FileFormats
* @package Spreadsheet_Excel_Writer
*/
 
class Spreadsheet_Excel_Writer_Worksheet extends Spreadsheet_Excel_Writer_BIFFwriter
{
/**
* Name of the Worksheet
* @var string
*/
var $name;
 
/**
* Index for the Worksheet
* @var integer
*/
var $index;
 
/**
* Reference to the (default) Format object for URLs
* @var object Format
*/
var $_url_format;
 
/**
* Reference to the parser used for parsing formulas
* @var object Format
*/
var $_parser;
 
/**
* Filehandle to the temporary file for storing data
* @var resource
*/
var $_filehandle;
 
/**
* Boolean indicating if we are using a temporary file for storing data
* @var bool
*/
var $_using_tmpfile;
 
/**
* Maximum number of rows for an Excel spreadsheet (BIFF5)
* @var integer
*/
var $_xls_rowmax;
 
/**
* Maximum number of columns for an Excel spreadsheet (BIFF5)
* @var integer
*/
var $_xls_colmax;
 
/**
* Maximum number of characters for a string (LABEL record in BIFF5)
* @var integer
*/
var $_xls_strmax;
 
/**
* First row for the DIMENSIONS record
* @var integer
* @see storeDimensions()
*/
var $_dim_rowmin;
 
/**
* Last row for the DIMENSIONS record
* @var integer
* @see storeDimensions()
*/
var $_dim_rowmax;
 
/**
* First column for the DIMENSIONS record
* @var integer
* @see storeDimensions()
*/
var $_dim_colmin;
 
/**
* Last column for the DIMENSIONS record
* @var integer
* @see storeDimensions()
*/
var $_dim_colmax;
 
/**
* Array containing format information for columns
* @var array
*/
var $_colinfo;
 
/**
* Array containing the selected area for the worksheet
* @var array
*/
var $_selection;
 
/**
* Array containing the panes for the worksheet
* @var array
*/
var $_panes;
 
/**
* The active pane for the worksheet
* @var integer
*/
var $_active_pane;
 
/**
* Bit specifying if panes are frozen
* @var integer
*/
var $_frozen;
 
/**
* Bit specifying if the worksheet is selected
* @var integer
*/
var $selected;
 
/**
* The paper size (for printing) (DOCUMENT!!!)
* @var integer
*/
var $_paper_size;
 
/**
* Bit specifying paper orientation (for printing). 0 => landscape, 1 => portrait
* @var integer
*/
var $_orientation;
 
/**
* The page header caption
* @var string
*/
var $_header;
 
/**
* The page footer caption
* @var string
*/
var $_footer;
 
/**
* The horizontal centering value for the page
* @var integer
*/
var $_hcenter;
 
/**
* The vertical centering value for the page
* @var integer
*/
var $_vcenter;
 
/**
* The margin for the header
* @var float
*/
var $_margin_head;
 
/**
* The margin for the footer
* @var float
*/
var $_margin_foot;
 
/**
* The left margin for the worksheet in inches
* @var float
*/
var $_margin_left;
 
/**
* The right margin for the worksheet in inches
* @var float
*/
var $_margin_right;
 
/**
* The top margin for the worksheet in inches
* @var float
*/
var $_margin_top;
 
/**
* The bottom margin for the worksheet in inches
* @var float
*/
var $_margin_bottom;
 
/**
* First row to reapeat on each printed page
* @var integer
*/
var $title_rowmin;
 
/**
* Last row to reapeat on each printed page
* @var integer
*/
var $title_rowmax;
 
/**
* First column to reapeat on each printed page
* @var integer
*/
var $title_colmin;
 
/**
* First row of the area to print
* @var integer
*/
var $print_rowmin;
 
/**
* Last row to of the area to print
* @var integer
*/
var $print_rowmax;
 
/**
* First column of the area to print
* @var integer
*/
var $print_colmin;
 
/**
* Last column of the area to print
* @var integer
*/
var $print_colmax;
 
/**
* Constructor
*
* @param string $name The name of the new worksheet
* @param integer $index The index of the new worksheet
* @param mixed &$activesheet The current activesheet of the workbook we belong to
* @param mixed &$firstsheet The first worksheet in the workbook we belong to
* @param mixed &$url_format The default format for hyperlinks
* @param mixed &$parser The formula parser created for the Workbook
*/
function Spreadsheet_Excel_Writer_Worksheet($name, $index, &$activesheet,
&$firstsheet, &$url_format,
&$parser)
{
// It needs to call its parent's constructor explicitly
$this->Spreadsheet_Excel_Writer_BIFFwriter();
$rowmax = 65536; // 16384 in Excel 5
$colmax = 256;
$this->name = $name;
$this->index = $index;
$this->activesheet = &$activesheet;
$this->firstsheet = &$firstsheet;
$this->_url_format = &$url_format;
$this->_parser = &$parser;
//$this->ext_sheets = array();
$this->_filehandle = "";
$this->_using_tmpfile = true;
//$this->fileclosed = 0;
//$this->offset = 0;
$this->_xls_rowmax = $rowmax;
$this->_xls_colmax = $colmax;
$this->_xls_strmax = 255;
$this->_dim_rowmin = $rowmax + 1;
$this->_dim_rowmax = 0;
$this->_dim_colmin = $colmax + 1;
$this->_dim_colmax = 0;
$this->_colinfo = array();
$this->_selection = array(0,0,0,0);
$this->_panes = array();
$this->_active_pane = 3;
$this->_frozen = 0;
$this->selected = 0;
$this->_paper_size = 0x0;
$this->_orientation = 0x1;
$this->_header = '';
$this->_footer = '';
$this->_hcenter = 0;
$this->_vcenter = 0;
$this->_margin_head = 0.50;
$this->_margin_foot = 0.50;
$this->_margin_left = 0.75;
$this->_margin_right = 0.75;
$this->_margin_top = 1.00;
$this->_margin_bottom = 1.00;
$this->title_rowmin = NULL;
$this->title_rowmax = NULL;
$this->title_colmin = NULL;
$this->title_colmax = NULL;
$this->print_rowmin = NULL;
$this->print_rowmax = NULL;
$this->print_colmin = NULL;
$this->print_colmax = NULL;
$this->_print_gridlines = 1;
$this->_print_headers = 0;
$this->_fit_page = 0;
$this->_fit_width = 0;
$this->_fit_height = 0;
$this->_hbreaks = array();
$this->_vbreaks = array();
$this->_protect = 0;
$this->_password = NULL;
$this->col_sizes = array();
$this->row_sizes = array();
$this->_zoom = 100;
$this->_print_scale = 100;
$this->_initialize();
}
/**
* Open a tmp file to store the majority of the Worksheet data. If this fails,
* for example due to write permissions, store the data in memory. This can be
* slow for large files.
*
* @access private
*/
function _initialize()
{
// Open tmp file for storing Worksheet data
$fh = tmpfile();
if ( $fh) {
// Store filehandle
$this->_filehandle = $fh;
}
else {
// If tmpfile() fails store data in memory
$this->_using_tmpfile = false;
}
}
/**
* Add data to the beginning of the workbook (note the reverse order)
* and to the end of the workbook.
*
* @access public
* @see Spreadsheet_Excel_Writer_Workbook::storeWorkbook()
* @param array $sheetnames The array of sheetnames from the Workbook this
* worksheet belongs to
*/
function close($sheetnames)
{
$num_sheets = count($sheetnames);
/***********************************************
* Prepend in reverse order!!
*/
// Prepend the sheet dimensions
$this->storeDimensions();
// Prepend the sheet password
$this->_storePassword();
// Prepend the sheet protection
$this->_storeProtect();
// Prepend the page setup
$this->_storeSetup();
// Prepend the bottom margin
$this->_storeMarginBottom();
// Prepend the top margin
$this->_storeMarginTop();
// Prepend the right margin
$this->_storeMarginRight();
// Prepend the left margin
$this->_storeMarginLeft();
// Prepend the page vertical centering
$this->_storeVcenter();
// Prepend the page horizontal centering
$this->_storeHcenter();
// Prepend the page footer
$this->_storeFooter();
// Prepend the page header
$this->_storeHeader();
// Prepend the vertical page breaks
$this->_storeVbreak();
// Prepend the horizontal page breaks
$this->_storeHbreak();
// Prepend WSBOOL
$this->_storeWsbool();
// Prepend GRIDSET
$this->_storeGridset();
// Prepend PRINTGRIDLINES
$this->_storePrintGridlines();
// Prepend PRINTHEADERS
$this->_storePrintHeaders();
// Prepend EXTERNSHEET references
for ($i = $num_sheets; $i > 0; $i--)
{
$sheetname = $sheetnames[$i-1];
$this->_storeExternsheet($sheetname);
}
// Prepend the EXTERNCOUNT of external references.
$this->_storeExterncount($num_sheets);
// Prepend the COLINFO records if they exist
if (!empty($this->_colinfo))
{
for($i=0; $i < count($this->_colinfo); $i++) {
$this->_storeColinfo($this->_colinfo[$i]);
}
$this->_storeDefcol();
}
// Prepend the BOF record
$this->_storeBof(0x0010);
/*
* End of prepend. Read upwards from here.
***********************************************/
// Append
$this->_storeWindow2();
$this->_storeZoom();
if (!empty($this->_panes)) {
$this->_storePanes($this->_panes);
}
$this->_storeSelection($this->_selection);
$this->_storeEof();
}
/**
* Retrieve the worksheet name.
* This is usefull when creating worksheets without a name.
*
* @access public
* @return string The worksheet's name
*/
function getName()
{
return $this->name;
}
/**
* Retrieves data from memory in one chunk, or from disk in $buffer
* sized chunks.
*
* @return string The data
*/
function getData()
{
$buffer = 4096;
// Return data stored in memory
if (isset($this->_data))
{
$tmp = $this->_data;
unset($this->_data);
$fh = $this->_filehandle;
if ($this->_using_tmpfile) {
fseek($fh, 0);
}
return($tmp);
}
// Return data stored on disk
if ($this->_using_tmpfile)
{
if ($tmp = fread($this->_filehandle, $buffer)) {
return($tmp);
}
}
// No data to return
return('');
}
/**
* Set this worksheet as a selected worksheet,
* i.e. the worksheet has its tab highlighted.
*
* @access public
*/
function select()
{
$this->selected = 1;
}
/**
* Set this worksheet as the active worksheet,
* i.e. the worksheet that is displayed when the workbook is opened.
* Also set it as selected.
*
* @access public
*/
function activate()
{
$this->selected = 1;
$this->activesheet = $this->index;
}
/**
* Set this worksheet as the first visible sheet.
* This is necessary when there are a large number of worksheets and the
* activated worksheet is not visible on the screen.
*
* @access public
*/
function setFirstSheet()
{
$this->firstsheet = $this->index;
}
/**
* Set the worksheet protection flag
* to prevent accidental modification and to
* hide formulas if the locked and hidden format properties have been set.
*
* @access public
* @param string $password The password to use for protecting the sheet.
*/
function protect($password)
{
$this->_protect = 1;
$this->_password = $this->_encodePassword($password);
}
/**
* Set the width of a single column or a range of columns.
*
* @access public
* @param integer $firstcol first column on the range
* @param integer $lastcol last column on the range
* @param integer $width width to set
* @param mixed $format The optional XF format to apply to the columns
* @param integer $hidden The optional hidden atribute
*/
function setColumn($firstcol, $lastcol, $width, $format = 0, $hidden = 0)
{
$this->_colinfo[] = array($firstcol, $lastcol, $width, &$format, $hidden);
// Set width to zero if column is hidden
$width = ($hidden) ? 0 : $width;
for($col = $firstcol; $col <= $lastcol; $col++) {
$this->col_sizes[$col] = $width;
}
}
/**
* Set which cell or cells are selected in a worksheet
*
* @access public
* @param integer $first_row first row in the selected quadrant
* @param integer $first_column first column in the selected quadrant
* @param integer $last_row last row in the selected quadrant
* @param integer $last_column last column in the selected quadrant
*/
function setSelection($first_row,$first_column,$last_row,$last_column)
{
$this->_selection = array($first_row,$first_column,$last_row,$last_column);
}
/**
* Set panes and mark them as frozen.
*
* @access public
* @param array $panes This is the only parameter received and is composed of the following:
* 0 => Vertical split position,
* 1 => Horizontal split position
* 2 => Top row visible
* 3 => Leftmost column visible
* 4 => Active pane
*/
function freezePanes($panes)
{
$this->_frozen = 1;
$this->_panes = $panes;
}
/**
* Set panes and mark them as unfrozen.
*
* @access public
* @param array $panes This is the only parameter received and is composed of the following:
* 0 => Vertical split position,
* 1 => Horizontal split position
* 2 => Top row visible
* 3 => Leftmost column visible
* 4 => Active pane
*/
function thawPanes($panes)
{
$this->_frozen = 0;
$this->_panes = $panes;
}
/**
* Set the page orientation as portrait.
*
* @access public
*/
function setPortrait()
{
$this->_orientation = 1;
}
/**
* Set the page orientation as landscape.
*
* @access public
*/
function setLandscape()
{
$this->_orientation = 0;
}
/**
* Set the paper type. Ex. 1 = US Letter, 9 = A4
*
* @access public
* @param integer $size The type of paper size to use
*/
function setPaper($size = 0)
{
$this->_paper_size = $size;
}
/**
* Set the page header caption and optional margin.
*
* @access public
* @param string $string The header text
* @param float $margin optional head margin in inches.
*/
function setHeader($string,$margin = 0.50)
{
if (strlen($string) >= 255) {
//carp 'Header string must be less than 255 characters';
return;
}
$this->_header = $string;
$this->_margin_head = $margin;
}
/**
* Set the page footer caption and optional margin.
*
* @access public
* @param string $string The footer text
* @param float $margin optional foot margin in inches.
*/
function setFooter($string,$margin = 0.50)
{
if (strlen($string) >= 255) {
//carp 'Footer string must be less than 255 characters';
return;
}
$this->_footer = $string;
$this->_margin_foot = $margin;
}
/**
* Center the page horinzontally.
*
* @access public
* @param integer $center the optional value for centering. Defaults to 1 (center).
*/
function centerHorizontally($center = 1)
{
$this->_hcenter = $center;
}
/**
* Center the page vertically.
*
* @access public
* @param integer $center the optional value for centering. Defaults to 1 (center).
*/
function centerVertically($center = 1)
{
$this->_vcenter = $center;
}
/**
* Set all the page margins to the same value in inches.
*
* @access public
* @param float $margin The margin to set in inches
*/
function setMargins($margin)
{
$this->setMarginLeft($margin);
$this->setMarginRight($margin);
$this->setMarginTop($margin);
$this->setMarginBottom($margin);
}
/**
* Set the left and right margins to the same value in inches.
*
* @access public
* @param float $margin The margin to set in inches
*/
function setMargins_LR($margin)
{
$this->setMarginLeft($margin);
$this->setMarginRight($margin);
}
/**
* Set the top and bottom margins to the same value in inches.
*
* @access public
* @param float $margin The margin to set in inches
*/
function setMargins_TB($margin)
{
$this->setMarginTop($margin);
$this->setMarginBottom($margin);
}
/**
* Set the left margin in inches.
*
* @access public
* @param float $margin The margin to set in inches
*/
function setMarginLeft($margin = 0.75)
{
$this->_margin_left = $margin;
}
/**
* Set the right margin in inches.
*
* @access public
* @param float $margin The margin to set in inches
*/
function setMarginRight($margin = 0.75)
{
$this->_margin_right = $margin;
}
/**
* Set the top margin in inches.
*
* @access public
* @param float $margin The margin to set in inches
*/
function setMarginTop($margin = 1.00)
{
$this->_margin_top = $margin;
}
/**
* Set the bottom margin in inches.
*
* @access public
* @param float $margin The margin to set in inches
*/
function setMarginBottom($margin = 1.00)
{
$this->_margin_bottom = $margin;
}
/**
* Set the rows to repeat at the top of each printed page.
*
* @access public
* @param integer $first_row First row to repeat
* @param integer $last_row Last row to repeat. Optional.
*/
function repeatRows($first_row, $last_row = NULL)
{
$this->title_rowmin = $first_row;
if (isset($last_row)) { //Second row is optional
$this->title_rowmax = $last_row;
}
else {
$this->title_rowmax = $first_row;
}
}
/**
* Set the columns to repeat at the left hand side of each printed page.
*
* @access public
* @param integer $first_col First column to repeat
* @param integer $last_col Last column to repeat. Optional.
*/
function repeatColumns($first_col, $last_col = NULL)
{
$this->title_colmin = $first_col;
if (isset($last_col)) { // Second col is optional
$this->title_colmax = $last_col;
}
else {
$this->title_colmax = $first_col;
}
}
/**
* Set the area of each worksheet that will be printed.
*
* @access public
* @param integer $first_row First row of the area to print
* @param integer $first_col First column of the area to print
* @param integer $last_row Last row of the area to print
* @param integer $last_col Last column of the area to print
*/
function printArea($first_row, $first_col, $last_row, $last_col)
{
$this->print_rowmin = $first_row;
$this->print_colmin = $first_col;
$this->print_rowmax = $last_row;
$this->print_colmax = $last_col;
}
/**
* Set the option to hide gridlines on the printed page.
*
* @access public
*/
function hideGridlines()
{
$this->_print_gridlines = 0;
}
/**
* Set the option to print the row and column headers on the printed page.
*
* @access public
* @param integer $print Whether to print the headers or not. Defaults to 1 (print).
*/
function printRowColHeaders($print = 1)
{
$this->_print_headers = $print;
}
/**
* Set the vertical and horizontal number of pages that will define the maximum area printed.
* It doesn't seem to work with OpenOffice.
*
* @access public
* @param integer $width Maximun width of printed area in pages
* @param integer $height Maximun heigth of printed area in pages
* @see setPrintScale()
*/
function fitToPages($width, $height)
{
$this->_fit_page = 1;
$this->_fit_width = $width;
$this->_fit_height = $height;
}
/**
* Store the horizontal page breaks on a worksheet (for printing).
* The breaks represent the row after which the break is inserted.
*
* @access public
* @param array $breaks Array containing the horizontal page breaks
*/
function setHPagebreaks($breaks)
{
foreach($breaks as $break) {
array_push($this->_hbreaks,$break);
}
}
/**
* Store the vertical page breaks on a worksheet (for printing).
* The breaks represent the column after which the break is inserted.
*
* @access public
* @param array $breaks Array containing the vertical page breaks
*/
function setVPagebreaks($breaks)
{
foreach($breaks as $break) {
array_push($this->_vbreaks,$break);
}
}
/**
* Set the worksheet zoom factor.
*
* @access public
* @param integer $scale The zoom factor
*/
function setZoom($scale = 100)
{
// Confine the scale to Excel's range
if ($scale < 10 or $scale > 400)
{
$this->raiseError("Zoom factor $scale outside range: 10 <= zoom <= 400");
$scale = 100;
}
$this->_zoom = floor($scale);
}
/**
* Set the scale factor for the printed page.
* It turns off the "fit to page" option
*
* @access public
* @param integer $scale The optional scale factor. Defaults to 100
*/
function setPrintScale($scale = 100)
{
// Confine the scale to Excel's range
if ($scale < 10 or $scale > 400)
{
$this->raiseError("Print scale $scale outside range: 10 <= zoom <= 400");
$scale = 100;
}
// Turn off "fit to page" option
$this->_fit_page = 0;
$this->_print_scale = floor($scale);
}
/**
* Map to the appropriate write method acording to the token recieved.
*
* @access public
* @param integer $row The row of the cell we are writing to
* @param integer $col The column of the cell we are writing to
* @param mixed $token What we are writing
* @param mixed $format The optional format to apply to the cell
*/
function write($row, $col, $token, $format = 0)
{
// Check for a cell reference in A1 notation and substitute row and column
/*if ($_[0] =~ /^\D/) {
@_ = $this->_substituteCellref(@_);
}*/
// Match number
if (preg_match("/^([+-]?)(?=\d|\.\d)\d*(\.\d*)?([Ee]([+-]?\d+))?$/",$token)) {
return $this->writeNumber($row,$col,$token,$format);
}
// Match http or ftp URL
elseif (preg_match("/^[fh]tt?p:\/\//",$token)) {
return $this->writeUrl($row, $col, $token, $format);
}
// Match mailto:
elseif (preg_match("/^mailto:/",$token)) {
return $this->writeUrl($row, $col, $token, $format);
}
// Match internal or external sheet link
elseif (preg_match("/^(?:in|ex)ternal:/",$token)) {
return $this->writeUrl($row, $col, $token, $format);
}
// Match formula
elseif (preg_match("/^=/",$token)) {
return $this->writeFormula($row, $col, $token, $format);
}
// Match formula
elseif (preg_match("/^@/",$token)) {
return $this->writeFormula($row, $col, $token, $format);
}
// Match blank
elseif ($token == '') {
return $this->writeBlank($row,$col,$format);
}
// Default: match string
else {
return $this->writeString($row,$col,$token,$format);
}
}
/**
* Write an array of values as a row
*
* @access public
* @param
* @return
*/
 
function writeRow($row, $col, $val, $format=0)
{
if (is_array($val)) {
foreach($val as $v) {
if (is_array($v)) {
$this->writeCol($row, $col, $v, $format);
} else {
$this->write($row, $col, $v, $format);
}
$col++;
}
} else {
$retval = new PEAR_Error('$val needs to be an array');
}
return($retval);
}
/**
* Write an array of values as a column
*
* @access public
* @param
* @return
*/
function writeCol($row, $col, $val, $format=0)
{
if (is_array($val)) {
foreach($val as $v) {
$this->write($row, $col, $v, $format);
$row++;
}
} else {
$retval = new PEAR_Error('$val needs to be an array');
}
return($retval);
}
/**
* Returns an index to the XF record in the workbook
*
* @access private
* @param mixed &$format The optional XF format
* @return integer The XF record index
*/
function _XF(&$format)
{
if ($format != 0) {
return($format->getXfIndex());
}
else {
return(0x0F);
}
}
/******************************************************************************
*******************************************************************************
*
* Internal methods
*/
/**
* Store Worksheet data in memory using the parent's class append() or to a
* temporary file, the default.
*
* @access private
* @param string $data The binary data to append
*/
function _append($data)
{
if ($this->_using_tmpfile)
{
// Add CONTINUE records if necessary
if (strlen($data) > $this->_limit) {
$data = $this->_addContinue($data);
}
fwrite($this->_filehandle,$data);
$this->_datasize += strlen($data);
}
else {
parent::_append($data);
}
}
/**
* Substitute an Excel cell reference in A1 notation for zero based row and
* column values in an argument list.
*
* Ex: ("A4", "Hello") is converted to (3, 0, "Hello").
*
* @access private
* @param string $cell The cell reference. Or range of cells.
* @return array
*/
function _substituteCellref($cell)
{
$cell = strtoupper($cell);
// Convert a column range: 'A:A' or 'B:G'
if (preg_match("/([A-I]?[A-Z]):([A-I]?[A-Z])/",$cell,$match)) {
list($no_use, $col1) = $this->_cellToRowcol($match[1] .'1'); // Add a dummy row
list($no_use, $col2) = $this->_cellToRowcol($match[2] .'1'); // Add a dummy row
return(array($col1, $col2));
}
// Convert a cell range: 'A1:B7'
if (preg_match("/\$?([A-I]?[A-Z]\$?\d+):\$?([A-I]?[A-Z]\$?\d+)/",$cell,$match)) {
list($row1, $col1) = $this->_cellToRowcol($match[1]);
list($row2, $col2) = $this->_cellToRowcol($match[2]);
return(array($row1, $col1, $row2, $col2));
}
// Convert a cell reference: 'A1' or 'AD2000'
if (preg_match("/\$?([A-I]?[A-Z]\$?\d+)/",$cell)) {
list($row1, $col1) = $this->_cellToRowcol($match[1]);
return(array($row1, $col1));
}
// TODO use real error codes
$this->raiseError("Unknown cell reference $cell", 0, PEAR_ERROR_DIE);
}
/**
* Convert an Excel cell reference in A1 notation to a zero based row and column
* reference; converts C1 to (0, 2).
*
* @access private
* @param string $cell The cell reference.
* @return array containing (row, column)
*/
function _cellToRowcol($cell)
{
preg_match("/\$?([A-I]?[A-Z])\$?(\d+)/",$cell,$match);
$col = $match[1];
$row = $match[2];
// Convert base26 column string to number
$chars = split('', $col);
$expn = 0;
$col = 0;
while ($chars) {
$char = array_pop($chars); // LS char first
$col += (ord($char) -ord('A') +1) * pow(26,$expn);
$expn++;
}
// Convert 1-index to zero-index
$row--;
$col--;
return(array($row, $col));
}
/**
* Based on the algorithm provided by Daniel Rentz of OpenOffice.
*
* @access private
* @param string $plaintext The password to be encoded in plaintext.
* @return string The encoded password
*/
function _encodePassword($plaintext)
{
$password = 0x0000;
$i = 1; // char position
// split the plain text password in its component characters
$chars = preg_split('//', $plaintext, -1, PREG_SPLIT_NO_EMPTY);
foreach($chars as $char)
{
$value = ord($char) << $i; // shifted ASCII value
$rotated_bits = $value >> 15; // rotated bits beyond bit 15
$value &= 0x7fff; // first 15 bits
$password ^= ($value | $rotated_bits);
$i++;
}
$password ^= strlen($plaintext);
$password ^= 0xCE4B;
 
return($password);
}
 
/******************************************************************************
*******************************************************************************
*
* BIFF RECORDS
*/
/**
* Write a double to the specified row and column (zero indexed).
* An integer can be written as a double. Excel will display an
* integer. $format is optional.
*
* Returns 0 : normal termination
* -2 : row or column out of range
*
* @access public
* @param integer $row Zero indexed row
* @param integer $col Zero indexed column
* @param float $num The number to write
* @param mixed $format The optional XF format
* @return integer
*/
function writeNumber($row, $col, $num, $format = 0)
{
$record = 0x0203; // Record identifier
$length = 0x000E; // Number of bytes to follow
$xf = $this->_XF($format); // The cell format
// Check that row and col are valid and store max and min values
if ($row >= $this->_xls_rowmax)
{
return(-2);
}
if ($col >= $this->_xls_colmax)
{
return(-2);
}
if ($row < $this->_dim_rowmin)
{
$this->_dim_rowmin = $row;
}
if ($row > $this->_dim_rowmax)
{
$this->_dim_rowmax = $row;
}
if ($col < $this->_dim_colmin)
{
$this->_dim_colmin = $col;
}
if ($col > $this->_dim_colmax)
{
$this->_dim_colmax = $col;
}
$header = pack("vv", $record, $length);
$data = pack("vvv", $row, $col, $xf);
$xl_double = pack("d", $num);
if ($this->_byte_order) // if it's Big Endian
{
$xl_double = strrev($xl_double);
}
$this->_append($header.$data.$xl_double);
return(0);
}
/**
* Write a string to the specified row and column (zero indexed).
* NOTE: there is an Excel 5 defined limit of 255 characters.
* $format is optional.
* Returns 0 : normal termination
* -2 : row or column out of range
* -3 : long string truncated to 255 chars
*
* @access public
* @param integer $row Zero indexed row
* @param integer $col Zero indexed column
* @param string $str The string to write
* @param mixed $format The XF format for the cell
* @return integer
*/
function writeString($row, $col, $str, $format = 0)
{
$strlen = strlen($str);
$record = 0x0204; // Record identifier
$length = 0x0008 + $strlen; // Bytes to follow
$xf = $this->_XF($format); // The cell format
$str_error = 0;
// Check that row and col are valid and store max and min values
if ($row >= $this->_xls_rowmax)
{
return(-2);
}
if ($col >= $this->_xls_colmax)
{
return(-2);
}
if ($row < $this->_dim_rowmin)
{
$this->_dim_rowmin = $row;
}
if ($row > $this->_dim_rowmax)
{
$this->_dim_rowmax = $row;
}
if ($col < $this->_dim_colmin)
{
$this->_dim_colmin = $col;
}
if ($col > $this->_dim_colmax)
{
$this->_dim_colmax = $col;
}
if ($strlen > $this->_xls_strmax) // LABEL must be < 255 chars
{
$str = substr($str, 0, $this->_xls_strmax);
$length = 0x0008 + $this->_xls_strmax;
$strlen = $this->_xls_strmax;
$str_error = -3;
}
$header = pack("vv", $record, $length);
$data = pack("vvvv", $row, $col, $xf, $strlen);
$this->_append($header.$data.$str);
return($str_error);
}
 
/**
* Writes a note associated with the cell given by the row and column.
* NOTE records don't have a length limit.
*
* @access public
* @param integer $row Zero indexed row
* @param integer $col Zero indexed column
* @param string $note The note to write
*/
function writeNote($row, $col, $note)
{
$note_length = strlen($note);
$record = 0x001C; // Record identifier
$max_length = 2048; // Maximun length for a NOTE record
//$length = 0x0006 + $note_length; // Bytes to follow
 
// Check that row and col are valid and store max and min values
if ($row >= $this->_xls_rowmax)
{
return(-2);
}
if ($col >= $this->_xls_colmax)
{
return(-2);
}
if ($row < $this->_dim_rowmin)
{
$this->_dim_rowmin = $row;
}
if ($row > $this->_dim_rowmax)
{
$this->_dim_rowmax = $row;
}
if ($col < $this->_dim_colmin)
{
$this->_dim_colmin = $col;
}
if ($col > $this->_dim_colmax)
{
$this->_dim_colmax = $col;
}
// Length for this record is no more than 2048 + 6
$length = 0x0006 + min($note_length, 2048);
$header = pack("vv", $record, $length);
$data = pack("vvv", $row, $col, $note_length);
$this->_append($header.$data.substr($note, 0, 2048));
 
for($i = $max_length; $i < $note_length; $i += $max_length)
{
$chunk = substr($note, $i, $max_length);
$length = 0x0006 + strlen($chunk);
$header = pack("vv", $record, $length);
$data = pack("vvv", -1, 0, strlen($chunk));
$this->_append($header.$data.$chunk);
}
return(0);
}
 
/**
* Write a blank cell to the specified row and column (zero indexed).
* A blank cell is used to specify formatting without adding a string
* or a number.
*
* A blank cell without a format serves no purpose. Therefore, we don't write
* a BLANK record unless a format is specified.
*
* Returns 0 : normal termination (including no format)
* -1 : insufficient number of arguments
* -2 : row or column out of range
*
* @access public
* @param integer $row Zero indexed row
* @param integer $col Zero indexed column
* @param mixed $format The XF format
*/
function writeBlank($row, $col, $format)
{
// Don't write a blank cell unless it has a format
if ($format == 0)
{
return(0);
}
$record = 0x0201; // Record identifier
$length = 0x0006; // Number of bytes to follow
$xf = $this->_XF($format); // The cell format
// Check that row and col are valid and store max and min values
if ($row >= $this->_xls_rowmax)
{
return(-2);
}
if ($col >= $this->_xls_colmax)
{
return(-2);
}
if ($row < $this->_dim_rowmin)
{
$this->_dim_rowmin = $row;
}
if ($row > $this->_dim_rowmax)
{
$this->_dim_rowmax = $row;
}
if ($col < $this->_dim_colmin)
{
$this->_dim_colmin = $col;
}
if ($col > $this->_dim_colmax)
{
$this->_dim_colmax = $col;
}
$header = pack("vv", $record, $length);
$data = pack("vvv", $row, $col, $xf);
$this->_append($header.$data);
return 0;
}
 
/**
* Write a formula to the specified row and column (zero indexed).
* The textual representation of the formula is passed to the parser in
* Parser.php which returns a packed binary string.
*
* Returns 0 : normal termination
* -1 : formula errors (bad formula)
* -2 : row or column out of range
*
* @access public
* @param integer $row Zero indexed row
* @param integer $col Zero indexed column
* @param string $formula The formula text string
* @param mixed $format The optional XF format
* @return integer
*/
function writeFormula($row, $col, $formula, $format = 0)
{
$record = 0x0006; // Record identifier
// Excel normally stores the last calculated value of the formula in $num.
// Clearly we are not in a position to calculate this a priori. Instead
// we set $num to zero and set the option flags in $grbit to ensure
// automatic calculation of the formula when the file is opened.
//
$xf = $this->_XF($format); // The cell format
$num = 0x00; // Current value of formula
$grbit = 0x03; // Option flags
$chn = 0x0000; // Must be zero
// Check that row and col are valid and store max and min values
if ($row >= $this->_xls_rowmax)
{
return(-2);
}
if ($col >= $this->_xls_colmax)
{
return(-2);
}
if ($row < $this->_dim_rowmin)
{
$this->_dim_rowmin = $row;
}
if ($row > $this->_dim_rowmax)
{
$this->_dim_rowmax = $row;
}
if ($col < $this->_dim_colmin)
{
$this->_dim_colmin = $col;
}
if ($col > $this->_dim_colmax)
{
$this->_dim_colmax = $col;
}
// Strip the '=' or '@' sign at the beginning of the formula string
if (preg_match("/^=/",$formula)) {
$formula = preg_replace("/(^=)/","",$formula);
}
elseif (preg_match("/^@/",$formula)) {
$formula = preg_replace("/(^@)/","",$formula);
}
else
{
// Error handling
$this->writeString($row, $col, 'Unrecognised character for formula');
return -1;
}
// Parse the formula using the parser in Parser.php
$error = $this->_parser->parse($formula);
if ($this->isError($error))
{
$this->writeString($row, $col, $error->getMessage());
return -1;
}
$formula = $this->_parser->toReversePolish();
if ($this->isError($formula))
{
$this->writeString($row, $col, $formula->getMessage());
return -1;
}
$formlen = strlen($formula); // Length of the binary string
$length = 0x16 + $formlen; // Length of the record data
$header = pack("vv", $record, $length);
$data = pack("vvvdvVv", $row, $col, $xf, $num,
$grbit, $chn, $formlen);
$this->_append($header.$data.$formula);
return 0;
}
/**
* Write a hyperlink.
* This is comprised of two elements: the visible label and
* the invisible link. The visible label is the same as the link unless an
* alternative string is specified. The label is written using the
* writeString() method. Therefore the 255 characters string limit applies.
* $string and $format are optional.
*
* The hyperlink can be to a http, ftp, mail, internal sheet (not yet), or external
* directory url.
*
* Returns 0 : normal termination
* -2 : row or column out of range
* -3 : long string truncated to 255 chars
*
* @access public
* @param integer $row Row
* @param integer $col Column
* @param string $url URL string
* @param string $string Alternative label
* @param mixed $format The cell format
* @return integer
*/
function writeUrl($row, $col, $url, $string = '', $format = 0)
{
// Add start row and col to arg list
return($this->_writeUrl_range($row, $col, $row, $col, $url, $string, $format));
}
/**
* This is the more general form of writeUrl(). It allows a hyperlink to be
* written to a range of cells. This function also decides the type of hyperlink
* to be written. These are either, Web (http, ftp, mailto), Internal
* (Sheet1!A1) or external ('c:\temp\foo.xls#Sheet1!A1').
*
* @access private
* @see writeUrl()
* @param integer $row1 Start row
* @param integer $col1 Start column
* @param integer $row2 End row
* @param integer $col2 End column
* @param string $url URL string
* @param string $string Alternative label
* @param mixed $format The cell format
* @return integer
*/
function _writeUrl_range($row1, $col1, $row2, $col2, $url, $string = '', $format = 0)
{
// Check for internal/external sheet links or default to web link
if (preg_match('[^internal:]', $url)) {
return($this->_writeUrlInternal($row1, $col1, $row2, $col2, $url, $string, $format));
}
if (preg_match('[^external:]', $url)) {
return($this->_writeUrlExternal($row1, $col1, $row2, $col2, $url, $string, $format));
}
return($this->_writeUrlWeb($row1, $col1, $row2, $col2, $url, $string, $format));
}
/**
* Used to write http, ftp and mailto hyperlinks.
* The link type ($options) is 0x03 is the same as absolute dir ref without
* sheet. However it is differentiated by the $unknown2 data stream.
*
* @access private
* @see writeUrl()
* @param integer $row1 Start row
* @param integer $col1 Start column
* @param integer $row2 End row
* @param integer $col2 End column
* @param string $url URL string
* @param string $str Alternative label
* @param mixed $format The cell format
* @return integer
*/
function _writeUrlWeb($row1, $col1, $row2, $col2, $url, $str, $format = 0)
{
$record = 0x01B8; // Record identifier
$length = 0x00000; // Bytes to follow
if ($format == 0) {
$format = $this->_url_format;
}
// Write the visible label using the writeString() method.
if ($str == '') {
$str = $url;
}
$str_error = $this->writeString($row1, $col1, $str, $format);
if (($str_error == -2) or ($str_error == -3)) {
return $str_error;
}
// Pack the undocumented parts of the hyperlink stream
$unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000");
$unknown2 = pack("H*", "E0C9EA79F9BACE118C8200AA004BA90B");
// Pack the option flags
$options = pack("V", 0x03);
// Convert URL to a null terminated wchar string
$url = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY));
$url = $url . "\0\0\0";
// Pack the length of the URL
$url_len = pack("V", strlen($url));
// Calculate the data length
$length = 0x34 + strlen($url);
// Pack the header data
$header = pack("vv", $record, $length);
$data = pack("vvvv", $row1, $row2, $col1, $col2);
// Write the packed data
$this->_append( $header. $data.
$unknown1. $options.
$unknown2. $url_len. $url);
return($str_error);
}
/**
* Used to write internal reference hyperlinks such as "Sheet1!A1".
*
* @access private
* @see writeUrl()
* @param integer $row1 Start row
* @param integer $col1 Start column
* @param integer $row2 End row
* @param integer $col2 End column
* @param string $url URL string
* @param string $str Alternative label
* @param mixed $format The cell format
* @return integer
*/
function _writeUrlInternal($row1, $col1, $row2, $col2, $url, $str, $format = 0)
{
$record = 0x01B8; // Record identifier
$length = 0x00000; // Bytes to follow
if ($format == 0) {
$format = $this->_url_format;
}
// Strip URL type
$url = preg_replace('s[^internal:]', '', $url);
// Write the visible label
if ($str == '') {
$str = $url;
}
$str_error = $this->writeString($row1, $col1, $str, $format);
if (($str_error == -2) or ($str_error == -3)) {
return $str_error;
}
// Pack the undocumented parts of the hyperlink stream
$unknown1 = pack("H*", "D0C9EA79F9BACE118C8200AA004BA90B02000000");
// Pack the option flags
$options = pack("V", 0x08);
// Convert the URL type and to a null terminated wchar string
$url = join("\0", preg_split("''", $url, -1, PREG_SPLIT_NO_EMPTY));
$url = $url . "\0\0\0";
// Pack the length of the URL as chars (not wchars)
$url_len = pack("V", floor(strlen($url)/2));
// Calculate the data length
$length = 0x24 + strlen($url);
// Pack the header data
$header = pack("vv", $record, $length);
$data = pack("vvvv", $row1, $row2, $col1, $col2);
// Write the packed data
$this->_append($header. $data.
$unknown1. $options.
$url_len. $url);
return($str_error);
}
/**
* Write links to external directory names such as 'c:\foo.xls',
* c:\foo.xls#Sheet1!A1', '../../foo.xls'. and '../../foo.xls#Sheet1!A1'.
*
* Note: Excel writes some relative links with the $dir_long string. We ignore
* these cases for the sake of simpler code.
*
* @access private
* @see writeUrl()
* @param integer $row1 Start row
* @param integer $col1 Start column
* @param integer $row2 End row
* @param integer $col2 End column
* @param string $url URL string
* @param string $str Alternative label
* @param mixed $format The cell format
* @return integer
*/
function _writeUrlExternal($row1, $col1, $row2, $col2, $url, $str, $format = 0)
{
// Network drives are different. We will handle them separately
// MS/Novell network drives and shares start with \\
if (preg_match('[^external:\\\\]', $url)) {
return; //($this->_writeUrlExternal_net($row1, $col1, $row2, $col2, $url, $str, $format));
}
$record = 0x01B8; // Record identifier
$length = 0x00000; // Bytes to follow
if ($format == 0) {
$format = $this->_url_format;
}
// Strip URL type and change Unix dir separator to Dos style (if needed)
//
$url = preg_replace('[^external:]', '', $url);
$url = preg_replace('[/]', "\\", $url);
// Write the visible label
if ($str == '') {
$str = preg_replace('[\#]', ' - ', $url);
}
$str_error = $this->writeString($row1, $col1, $str, $format);
if (($str_error == -2) or ($str_error == -3)) {
return $str_error;
}
// Determine if the link is relative or absolute:
// relative if link contains no dir separator, "somefile.xls"
// relative if link starts with up-dir, "..\..\somefile.xls"
// otherwise, absolute
$absolute = 0x02; // Bit mask
if (!preg_match('[\\]', $url)) {
$absolute = 0x00;
}
if (preg_match('[^\.\.\\]', $url)) {
$absolute = 0x00;
}
// Determine if the link contains a sheet reference and change some of the
// parameters accordingly.
// Split the dir name and sheet name (if it exists)
list($dir_long , $sheet) = split('/\#/', $url);
$link_type = 0x01 | $absolute;
if (isset($sheet)) {
$link_type |= 0x08;
$sheet_len = pack("V", strlen($sheet) + 0x01);
$sheet = join("\0", split('', $sheet));
$sheet .= "\0\0\0";
}
else {
$sheet_len = '';
$sheet = '';
}
// Pack the link type
$link_type = pack("V", $link_type);
// Calculate the up-level dir count e.g.. (..\..\..\ == 3)
$up_count = preg_match_all("/\.\.\\/", $dir_long, $useless);
$up_count = pack("v", $up_count);
// Store the short dos dir name (null terminated)
$dir_short = preg_replace('/\.\.\\/', '', $dir_long) . "\0";
// Store the long dir name as a wchar string (non-null terminated)
$dir_long = join("\0", split('', $dir_long));
$dir_long = $dir_long . "\0";
// Pack the lengths of the dir strings
$dir_short_len = pack("V", strlen($dir_short) );
$dir_long_len = pack("V", strlen($dir_long) );
$stream_len = pack("V", strlen($dir_long) + 0x06);
// Pack the undocumented parts of the hyperlink stream
$unknown1 = pack("H*",'D0C9EA79F9BACE118C8200AA004BA90B02000000' );
$unknown2 = pack("H*",'0303000000000000C000000000000046' );
$unknown3 = pack("H*",'FFFFADDE000000000000000000000000000000000000000');
$unknown4 = pack("v", 0x03 );
// Pack the main data stream
$data = pack("vvvv", $row1, $row2, $col1, $col2) .
$unknown1 .
$link_type .
$unknown2 .
$up_count .
$dir_short_len.
$dir_short .
$unknown3 .
$stream_len .
$dir_long_len .
$unknown4 .
$dir_long .
$sheet_len .
$sheet ;
// Pack the header data
$length = strlen($data);
$header = pack("vv", $record, $length);
// Write the packed data
$this->_append($header. $data);
return($str_error);
}
/**
* This method is used to set the height and format for a row.
*
* @access public
* @param integer $row The row to set
* @param integer $height Height we are giving to the row.
* Use NULL to set XF without setting height
* @param mixed $format XF format we are giving to the row
*/
function setRow($row, $height, $format = 0)
{
$record = 0x0208; // Record identifier
$length = 0x0010; // Number of bytes to follow
$colMic = 0x0000; // First defined column
$colMac = 0x0000; // Last defined column
$irwMac = 0x0000; // Used by Excel to optimise loading
$reserved = 0x0000; // Reserved
$grbit = 0x01C0; // Option flags. (monkey) see $1 do
$ixfe = $this->_XF($format); // XF index
// Use setRow($row, NULL, $XF) to set XF format without setting height
if ($height != NULL) {
$miyRw = $height * 20; // row height
}
else {
$miyRw = 0xff; // default row height is 256
}
$header = pack("vv", $record, $length);
$data = pack("vvvvvvvv", $row, $colMic, $colMac, $miyRw,
$irwMac,$reserved, $grbit, $ixfe);
$this->_append($header.$data);
}
/**
* Writes Excel DIMENSIONS to define the area in which there is data.
*
* @access private
*/
function storeDimensions()
{
$record = 0x0000; // Record identifier
$length = 0x000A; // Number of bytes to follow
$row_min = $this->_dim_rowmin; // First row
$row_max = $this->_dim_rowmax; // Last row plus 1
$col_min = $this->_dim_colmin; // First column
$col_max = $this->_dim_colmax; // Last column plus 1
$reserved = 0x0000; // Reserved by Excel
$header = pack("vv", $record, $length);
$data = pack("vvvvv", $row_min, $row_max,
$col_min, $col_max, $reserved);
$this->_prepend($header.$data);
}
/**
* Write BIFF record Window2.
*
* @access private
*/
function _storeWindow2()
{
$record = 0x023E; // Record identifier
$length = 0x000A; // Number of bytes to follow
$grbit = 0x00B6; // Option flags
$rwTop = 0x0000; // Top row visible in window
$colLeft = 0x0000; // Leftmost column visible in window
$rgbHdr = 0x00000000; // Row/column heading and gridline color
// The options flags that comprise $grbit
$fDspFmla = 0; // 0 - bit
$fDspGrid = 1; // 1
$fDspRwCol = 1; // 2
$fFrozen = $this->_frozen; // 3
$fDspZeros = 1; // 4
$fDefaultHdr = 1; // 5
$fArabic = 0; // 6
$fDspGuts = 1; // 7
$fFrozenNoSplit = 0; // 0 - bit
$fSelected = $this->selected; // 1
$fPaged = 1; // 2
$grbit = $fDspFmla;
$grbit |= $fDspGrid << 1;
$grbit |= $fDspRwCol << 2;
$grbit |= $fFrozen << 3;
$grbit |= $fDspZeros << 4;
$grbit |= $fDefaultHdr << 5;
$grbit |= $fArabic << 6;
$grbit |= $fDspGuts << 7;
$grbit |= $fFrozenNoSplit << 8;
$grbit |= $fSelected << 9;
$grbit |= $fPaged << 10;
$header = pack("vv", $record, $length);
$data = pack("vvvV", $grbit, $rwTop, $colLeft, $rgbHdr);
$this->_append($header.$data);
}
/**
* Write BIFF record DEFCOLWIDTH if COLINFO records are in use.
*
* @access private
*/
function _storeDefcol()
{
$record = 0x0055; // Record identifier
$length = 0x0002; // Number of bytes to follow
$colwidth = 0x0008; // Default column width
$header = pack("vv", $record, $length);
$data = pack("v", $colwidth);
$this->_prepend($header.$data);
}
/**
* Write BIFF record COLINFO to define column widths
*
* Note: The SDK says the record length is 0x0B but Excel writes a 0x0C
* length record.
*
* @access private
* @param array $col_array This is the only parameter received and is composed of the following:
* 0 => First formatted column,
* 1 => Last formatted column,
* 2 => Col width (8.43 is Excel default),
* 3 => The optional XF format of the column,
* 4 => Option flags.
*/
function _storeColinfo($col_array)
{
if (isset($col_array[0])) {
$colFirst = $col_array[0];
}
if (isset($col_array[1])) {
$colLast = $col_array[1];
}
if (isset($col_array[2])) {
$coldx = $col_array[2];
}
else {
$coldx = 8.43;
}
if (isset($col_array[3])) {
$format = $col_array[3];
}
else {
$format = 0;
}
if (isset($col_array[4])) {
$grbit = $col_array[4];
}
else {
$grbit = 0;
}
$record = 0x007D; // Record identifier
$length = 0x000B; // Number of bytes to follow
$coldx += 0.72; // Fudge. Excel subtracts 0.72 !?
$coldx *= 256; // Convert to units of 1/256 of a char
$ixfe = $this->_XF($format);
$reserved = 0x00; // Reserved
$header = pack("vv", $record, $length);
$data = pack("vvvvvC", $colFirst, $colLast, $coldx,
$ixfe, $grbit, $reserved);
$this->_prepend($header.$data);
}
/**
* Write BIFF record SELECTION.
*
* @access private
* @param array $array array containing ($rwFirst,$colFirst,$rwLast,$colLast)
* @see setSelection()
*/
function _storeSelection($array)
{
list($rwFirst,$colFirst,$rwLast,$colLast) = $array;
$record = 0x001D; // Record identifier
$length = 0x000F; // Number of bytes to follow
$pnn = $this->_active_pane; // Pane position
$rwAct = $rwFirst; // Active row
$colAct = $colFirst; // Active column
$irefAct = 0; // Active cell ref
$cref = 1; // Number of refs
if (!isset($rwLast)) {
$rwLast = $rwFirst; // Last row in reference
}
if (!isset($colLast)) {
$colLast = $colFirst; // Last col in reference
}
// Swap last row/col for first row/col as necessary
if ($rwFirst > $rwLast)
{
list($rwFirst, $rwLast) = array($rwLast, $rwFirst);
}
if ($colFirst > $colLast)
{
list($colFirst, $colLast) = array($colLast, $colFirst);
}
$header = pack("vv", $record, $length);
$data = pack("CvvvvvvCC", $pnn, $rwAct, $colAct,
$irefAct, $cref,
$rwFirst, $rwLast,
$colFirst, $colLast);
$this->_append($header.$data);
}
/**
* Write BIFF record EXTERNCOUNT to indicate the number of external sheet
* references in a worksheet.
*
* Excel only stores references to external sheets that are used in formulas.
* For simplicity we store references to all the sheets in the workbook
* regardless of whether they are used or not. This reduces the overall
* complexity and eliminates the need for a two way dialogue between the formula
* parser the worksheet objects.
*
* @access private
* @param integer $count The number of external sheet references in this worksheet
*/
function _storeExterncount($count)
{
$record = 0x0016; // Record identifier
$length = 0x0002; // Number of bytes to follow
$header = pack("vv", $record, $length);
$data = pack("v", $count);
$this->_prepend($header.$data);
}
/**
* Writes the Excel BIFF EXTERNSHEET record. These references are used by
* formulas. A formula references a sheet name via an index. Since we store a
* reference to all of the external worksheets the EXTERNSHEET index is the same
* as the worksheet index.
*
* @access private
* @param string $sheetname The name of a external worksheet
*/
function _storeExternsheet($sheetname)
{
$record = 0x0017; // Record identifier
// References to the current sheet are encoded differently to references to
// external sheets.
//
if ($this->name == $sheetname) {
$sheetname = '';
$length = 0x02; // The following 2 bytes
$cch = 1; // The following byte
$rgch = 0x02; // Self reference
}
else {
$length = 0x02 + strlen($sheetname);
$cch = strlen($sheetname);
$rgch = 0x03; // Reference to a sheet in the current workbook
}
$header = pack("vv", $record, $length);
$data = pack("CC", $cch, $rgch);
$this->_prepend($header.$data.$sheetname);
}
/**
* Writes the Excel BIFF PANE record.
* The panes can either be frozen or thawed (unfrozen).
* Frozen panes are specified in terms of an integer number of rows and columns.
* Thawed panes are specified in terms of Excel's units for rows and columns.
*
* @access private
* @param array $panes This is the only parameter received and is composed of the following:
* 0 => Vertical split position,
* 1 => Horizontal split position
* 2 => Top row visible
* 3 => Leftmost column visible
* 4 => Active pane
*/
function _storePanes($panes)
{
$y = $panes[0];
$x = $panes[1];
$rwTop = $panes[2];
$colLeft = $panes[3];
if (count($panes) > 4) { // if Active pane was received
$pnnAct = $panes[4];
}
else {
$pnnAct = NULL;
}
$record = 0x0041; // Record identifier
$length = 0x000A; // Number of bytes to follow
// Code specific to frozen or thawed panes.
if ($this->_frozen)
{
// Set default values for $rwTop and $colLeft
if (!isset($rwTop)) {
$rwTop = $y;
}
if (!isset($colLeft)) {
$colLeft = $x;
}
}
else
{
// Set default values for $rwTop and $colLeft
if (!isset($rwTop)) {
$rwTop = 0;
}
if (!isset($colLeft)) {
$colLeft = 0;
}
// Convert Excel's row and column units to the internal units.
// The default row height is 12.75
// The default column width is 8.43
// The following slope and intersection values were interpolated.
//
$y = 20*$y + 255;
$x = 113.879*$x + 390;
}
// Determine which pane should be active. There is also the undocumented
// option to override this should it be necessary: may be removed later.
//
if (!isset($pnnAct))
{
if ($x != 0 and $y != 0)
$pnnAct = 0; // Bottom right
if ($x != 0 and $y == 0)
$pnnAct = 1; // Top right
if ($x == 0 and $y != 0)
$pnnAct = 2; // Bottom left
if ($x == 0 and $y == 0)
$pnnAct = 3; // Top left
}
$this->_active_pane = $pnnAct; // Used in _storeSelection
$header = pack("vv", $record, $length);
$data = pack("vvvvv", $x, $y, $rwTop, $colLeft, $pnnAct);
$this->_append($header.$data);
}
/**
* Store the page setup SETUP BIFF record.
*
* @access private
*/
function _storeSetup()
{
$record = 0x00A1; // Record identifier
$length = 0x0022; // Number of bytes to follow
$iPaperSize = $this->_paper_size; // Paper size
$iScale = $this->_print_scale; // Print scaling factor
$iPageStart = 0x01; // Starting page number
$iFitWidth = $this->_fit_width; // Fit to number of pages wide
$iFitHeight = $this->_fit_height; // Fit to number of pages high
$grbit = 0x00; // Option flags
$iRes = 0x0258; // Print resolution
$iVRes = 0x0258; // Vertical print resolution
$numHdr = $this->_margin_head; // Header Margin
$numFtr = $this->_margin_foot; // Footer Margin
$iCopies = 0x01; // Number of copies
$fLeftToRight = 0x0; // Print over then down
$fLandscape = $this->_orientation; // Page orientation
$fNoPls = 0x0; // Setup not read from printer
$fNoColor = 0x0; // Print black and white
$fDraft = 0x0; // Print draft quality
$fNotes = 0x0; // Print notes
$fNoOrient = 0x0; // Orientation not set
$fUsePage = 0x0; // Use custom starting page
$grbit = $fLeftToRight;
$grbit |= $fLandscape << 1;
$grbit |= $fNoPls << 2;
$grbit |= $fNoColor << 3;
$grbit |= $fDraft << 4;
$grbit |= $fNotes << 5;
$grbit |= $fNoOrient << 6;
$grbit |= $fUsePage << 7;
$numHdr = pack("d", $numHdr);
$numFtr = pack("d", $numFtr);
if ($this->_byte_order) // if it's Big Endian
{
$numHdr = strrev($numHdr);
$numFtr = strrev($numFtr);
}
$header = pack("vv", $record, $length);
$data1 = pack("vvvvvvvv", $iPaperSize,
$iScale,
$iPageStart,
$iFitWidth,
$iFitHeight,
$grbit,
$iRes,
$iVRes);
$data2 = $numHdr.$numFtr;
$data3 = pack("v", $iCopies);
$this->_prepend($header.$data1.$data2.$data3);
}
/**
* Store the header caption BIFF record.
*
* @access private
*/
function _storeHeader()
{
$record = 0x0014; // Record identifier
$str = $this->_header; // header string
$cch = strlen($str); // Length of header string
$length = 1 + $cch; // Bytes to follow
$header = pack("vv", $record, $length);
$data = pack("C", $cch);
$this->_append($header.$data.$str);
}
/**
* Store the footer caption BIFF record.
*
* @access private
*/
function _storeFooter()
{
$record = 0x0015; // Record identifier
$str = $this->_footer; // Footer string
$cch = strlen($str); // Length of footer string
$length = 1 + $cch; // Bytes to follow
$header = pack("vv", $record, $length);
$data = pack("C", $cch);
$this->_append($header.$data.$str);
}
/**
* Store the horizontal centering HCENTER BIFF record.
*
* @access private
*/
function _storeHcenter()
{
$record = 0x0083; // Record identifier
$length = 0x0002; // Bytes to follow
$fHCenter = $this->_hcenter; // Horizontal centering
$header = pack("vv", $record, $length);
$data = pack("v", $fHCenter);
$this->_append($header.$data);
}
/**
* Store the vertical centering VCENTER BIFF record.
*
* @access private
*/
function _storeVcenter()
{
$record = 0x0084; // Record identifier
$length = 0x0002; // Bytes to follow
$fVCenter = $this->_vcenter; // Horizontal centering
$header = pack("vv", $record, $length);
$data = pack("v", $fVCenter);
$this->_append($header.$data);
}
/**
* Store the LEFTMARGIN BIFF record.
*
* @access private
*/
function _storeMarginLeft()
{
$record = 0x0026; // Record identifier
$length = 0x0008; // Bytes to follow
$margin = $this->_margin_left; // Margin in inches
$header = pack("vv", $record, $length);
$data = pack("d", $margin);
if ($this->_byte_order) // if it's Big Endian
{
$data = strrev($data);
}
$this->_append($header.$data);
}
/**
* Store the RIGHTMARGIN BIFF record.
*
* @access private
*/
function _storeMarginRight()
{
$record = 0x0027; // Record identifier
$length = 0x0008; // Bytes to follow
$margin = $this->_margin_right; // Margin in inches
$header = pack("vv", $record, $length);
$data = pack("d", $margin);
if ($this->_byte_order) // if it's Big Endian
{
$data = strrev($data);
}
$this->_append($header.$data);
}
/**
* Store the TOPMARGIN BIFF record.
*
* @access private
*/
function _storeMarginTop()
{
$record = 0x0028; // Record identifier
$length = 0x0008; // Bytes to follow
$margin = $this->_margin_top; // Margin in inches
$header = pack("vv", $record, $length);
$data = pack("d", $margin);
if ($this->_byte_order) // if it's Big Endian
{
$data = strrev($data);
}
$this->_append($header.$data);
}
/**
* Store the BOTTOMMARGIN BIFF record.
*
* @access private
*/
function _storeMarginBottom()
{
$record = 0x0029; // Record identifier
$length = 0x0008; // Bytes to follow
$margin = $this->_margin_bottom; // Margin in inches
$header = pack("vv", $record, $length);
$data = pack("d", $margin);
if ($this->_byte_order) // if it's Big Endian
{
$data = strrev($data);
}
$this->_append($header.$data);
}
 
/**
* Merges the area given by its arguments.
* This is an Excel97/2000 method. It is required to perform more complicated
* merging than the normal setAlign('merge').
*
* @access public
* @param integer $first_row First row of the area to merge
* @param integer $first_col First column of the area to merge
* @param integer $last_row Last row of the area to merge
* @param integer $last_col Last column of the area to merge
*/
function mergeCells($first_row, $first_col, $last_row, $last_col)
{
$record = 0x00E5; // Record identifier
$length = 0x000A; // Bytes to follow
$cref = 1; // Number of refs
 
// Swap last row/col for first row/col as necessary
if ($first_row > $last_row) {
list($first_row, $last_row) = array($last_row, $first_row);
}
if ($first_col > $last_col) {
list($first_col, $last_col) = array($last_col, $first_col);
}
$header = pack("vv", $record, $length);
$data = pack("vvvvv", $cref, $first_row, $last_row,
$first_col, $last_col);
$this->_append($header.$data);
}
 
/**
* Write the PRINTHEADERS BIFF record.
*
* @access private
*/
function _storePrintHeaders()
{
$record = 0x002a; // Record identifier
$length = 0x0002; // Bytes to follow
$fPrintRwCol = $this->_print_headers; // Boolean flag
$header = pack("vv", $record, $length);
$data = pack("v", $fPrintRwCol);
$this->_prepend($header.$data);
}
/**
* Write the PRINTGRIDLINES BIFF record. Must be used in conjunction with the
* GRIDSET record.
*
* @access private
*/
function _storePrintGridlines()
{
$record = 0x002b; // Record identifier
$length = 0x0002; // Bytes to follow
$fPrintGrid = $this->_print_gridlines; // Boolean flag
$header = pack("vv", $record, $length);
$data = pack("v", $fPrintGrid);
$this->_prepend($header.$data);
}
/**
* Write the GRIDSET BIFF record. Must be used in conjunction with the
* PRINTGRIDLINES record.
*
* @access private
*/
function _storeGridset()
{
$record = 0x0082; // Record identifier
$length = 0x0002; // Bytes to follow
$fGridSet = !($this->_print_gridlines); // Boolean flag
$header = pack("vv", $record, $length);
$data = pack("v", $fGridSet);
$this->_prepend($header.$data);
}
/**
* Write the WSBOOL BIFF record, mainly for fit-to-page. Used in conjunction
* with the SETUP record.
*
* @access private
*/
function _storeWsbool()
{
$record = 0x0081; // Record identifier
$length = 0x0002; // Bytes to follow
// The only option that is of interest is the flag for fit to page. So we
// set all the options in one go.
//
if ($this->_fit_page) {
$grbit = 0x05c1;
}
else {
$grbit = 0x04c1;
}
$header = pack("vv", $record, $length);
$data = pack("v", $grbit);
$this->_prepend($header.$data);
}
/**
* Write the HORIZONTALPAGEBREAKS BIFF record.
*
* @access private
*/
function _storeHbreak()
{
// Return if the user hasn't specified pagebreaks
if (empty($this->_hbreaks)) {
return;
}
// Sort and filter array of page breaks
$breaks = $this->_hbreaks;
sort($breaks,SORT_NUMERIC);
if ($breaks[0] == 0) { // don't use first break if it's 0
array_shift($breaks);
}
$record = 0x001b; // Record identifier
$cbrk = count($breaks); // Number of page breaks
$length = ($cbrk + 1) * 2; // Bytes to follow
$header = pack("vv", $record, $length);
$data = pack("v", $cbrk);
// Append each page break
foreach($breaks as $break) {
$data .= pack("v", $break);
}
$this->_prepend($header.$data);
}
/**
* Write the VERTICALPAGEBREAKS BIFF record.
*
* @access private
*/
function _storeVbreak()
{
// Return if the user hasn't specified pagebreaks
if (empty($this->_vbreaks)) {
return;
}
// 1000 vertical pagebreaks appears to be an internal Excel 5 limit.
// It is slightly higher in Excel 97/200, approx. 1026
$breaks = array_slice($this->_vbreaks,0,1000);
// Sort and filter array of page breaks
sort($breaks,SORT_NUMERIC);
if ($breaks[0] == 0) { // don't use first break if it's 0
array_shift($breaks);
}
$record = 0x001a; // Record identifier
$cbrk = count($breaks); // Number of page breaks
$length = ($cbrk + 1) * 2; // Bytes to follow
$header = pack("vv", $record, $length);
$data = pack("v", $cbrk);
// Append each page break
foreach ($breaks as $break) {
$data .= pack("v", $break);
}
$this->_prepend($header.$data);
}
/**
* Set the Biff PROTECT record to indicate that the worksheet is protected.
*
* @access private
*/
function _storeProtect()
{
// Exit unless sheet protection has been specified
if ($this->_protect == 0) {
return;
}
$record = 0x0012; // Record identifier
$length = 0x0002; // Bytes to follow
$fLock = $this->_protect; // Worksheet is protected
$header = pack("vv", $record, $length);
$data = pack("v", $fLock);
$this->_prepend($header.$data);
}
/**
* Write the worksheet PASSWORD record.
*
* @access private
*/
function _storePassword()
{
// Exit unless sheet protection and password have been specified
if (($this->_protect == 0) or (!isset($this->_password))) {
return;
}
$record = 0x0013; // Record identifier
$length = 0x0002; // Bytes to follow
$wPassword = $this->_password; // Encoded password
$header = pack("vv", $record, $length);
$data = pack("v", $wPassword);
$this->_prepend($header.$data);
}
 
/**
* Insert a 24bit bitmap image in a worksheet.
*
* @access public
* @param integer $row The row we are going to insert the bitmap into
* @param integer $col The column we are going to insert the bitmap into
* @param string $bitmap The bitmap filename
* @param integer $x The horizontal position (offset) of the image inside the cell.
* @param integer $y The vertical position (offset) of the image inside the cell.
* @param integer $scale_x The horizontal scale
* @param integer $scale_y The vertical scale
*/
function insertBitmap($row, $col, $bitmap, $x = 0, $y = 0, $scale_x = 1, $scale_y = 1)
{
$bitmap_array = $this->_processBitmap($bitmap);
if ($this->isError($bitmap_array))
{
$this->writeString($row, $col, $bitmap_array->getMessage());
return;
}
list($width, $height, $size, $data) = $bitmap_array; //$this->_processBitmap($bitmap);
// Scale the frame of the image.
$width *= $scale_x;
$height *= $scale_y;
// Calculate the vertices of the image and write the OBJ record
$this->_positionImage($col, $row, $x, $y, $width, $height);
// Write the IMDATA record to store the bitmap data
$record = 0x007f;
$length = 8 + $size;
$cf = 0x09;
$env = 0x01;
$lcb = $size;
$header = pack("vvvvV", $record, $length, $cf, $env, $lcb);
$this->_append($header.$data);
}
/**
* Calculate the vertices that define the position of the image as required by
* the OBJ record.
*
* +------------+------------+
* | A | B |
* +-----+------------+------------+
* | |(x1,y1) | |
* | 1 |(A1)._______|______ |
* | | | | |
* | | | | |
* +-----+----| BITMAP |-----+
* | | | | |
* | 2 | |______________. |
* | | | (B2)|
* | | | (x2,y2)|
* +---- +------------+------------+
*
* Example of a bitmap that covers some of the area from cell A1 to cell B2.
*
* Based on the width and height of the bitmap we need to calculate 8 vars:
* $col_start, $row_start, $col_end, $row_end, $x1, $y1, $x2, $y2.
* The width and height of the cells are also variable and have to be taken into
* account.
* The values of $col_start and $row_start are passed in from the calling
* function. The values of $col_end and $row_end are calculated by subtracting
* the width and height of the bitmap from the width and height of the
* underlying cells.
* The vertices are expressed as a percentage of the underlying cell width as
* follows (rhs values are in pixels):
*
* x1 = X / W *1024
* y1 = Y / H *256
* x2 = (X-1) / W *1024
* y2 = (Y-1) / H *256
*
* Where: X is distance from the left side of the underlying cell
* Y is distance from the top of the underlying cell
* W is the width of the cell
* H is the height of the cell
*
* @access private
* @note the SDK incorrectly states that the height should be expressed as a
* percentage of 1024.
* @param integer $col_start Col containing upper left corner of object
* @param integer $row_start Row containing top left corner of object
* @param integer $x1 Distance to left side of object
* @param integer $y1 Distance to top of object
* @param integer $width Width of image frame
* @param integer $height Height of image frame
*/
function _positionImage($col_start, $row_start, $x1, $y1, $width, $height)
{
// Initialise end cell to the same as the start cell
$col_end = $col_start; // Col containing lower right corner of object
$row_end = $row_start; // Row containing bottom right corner of object
// Zero the specified offset if greater than the cell dimensions
if ($x1 >= $this->_sizeCol($col_start))
{
$x1 = 0;
}
if ($y1 >= $this->_sizeRow($row_start))
{
$y1 = 0;
}
$width = $width + $x1 -1;
$height = $height + $y1 -1;
// Subtract the underlying cell widths to find the end cell of the image
while ($width >= $this->_sizeCol($col_end)) {
$width -= $this->_sizeCol($col_end);
$col_end++;
}
// Subtract the underlying cell heights to find the end cell of the image
while ($height >= $this->_sizeRow($row_end)) {
$height -= $this->_sizeRow($row_end);
$row_end++;
}
// Bitmap isn't allowed to start or finish in a hidden cell, i.e. a cell
// with zero eight or width.
//
if ($this->_sizeCol($col_start) == 0)
return;
if ($this->_sizeCol($col_end) == 0)
return;
if ($this->_sizeRow($row_start) == 0)
return;
if ($this->_sizeRow($row_end) == 0)
return;
// Convert the pixel values to the percentage value expected by Excel
$x1 = $x1 / $this->_sizeCol($col_start) * 1024;
$y1 = $y1 / $this->_sizeRow($row_start) * 256;
$x2 = $width / $this->_sizeCol($col_end) * 1024; // Distance to right side of object
$y2 = $height / $this->_sizeRow($row_end) * 256; // Distance to bottom of object
$this->_storeObjPicture( $col_start, $x1,
$row_start, $y1,
$col_end, $x2,
$row_end, $y2
);
}
/**
* Convert the width of a cell from user's units to pixels. By interpolation
* the relationship is: y = 7x +5. If the width hasn't been set by the user we
* use the default value. If the col is hidden we use a value of zero.
*
* @access private
* @param integer $col The column
* @return integer The width in pixels
*/
function _sizeCol($col)
{
// Look up the cell value to see if it has been changed
if (isset($this->col_sizes[$col])) {
if ($this->col_sizes[$col] == 0) {
return(0);
}
else {
return(floor(7 * $this->col_sizes[$col] + 5));
}
}
else {
return(64);
}
}
/**
* Convert the height of a cell from user's units to pixels. By interpolation
* the relationship is: y = 4/3x. If the height hasn't been set by the user we
* use the default value. If the row is hidden we use a value of zero. (Not
* possible to hide row yet).
*
* @access private
* @param integer $row The row
* @return integer The width in pixels
*/
function _sizeRow($row)
{
// Look up the cell value to see if it has been changed
if (isset($this->row_sizes[$row])) {
if ($this->row_sizes[$row] == 0) {
return(0);
}
else {
return(floor(4/3 * $this->row_sizes[$row]));
}
}
else {
return(17);
}
}
/**
* Store the OBJ record that precedes an IMDATA record. This could be generalise
* to support other Excel objects.
*
* @access private
* @param integer $colL Column containing upper left corner of object
* @param integer $dxL Distance from left side of cell
* @param integer $rwT Row containing top left corner of object
* @param integer $dyT Distance from top of cell
* @param integer $colR Column containing lower right corner of object
* @param integer $dxR Distance from right of cell
* @param integer $rwB Row containing bottom right corner of object
* @param integer $dyB Distance from bottom of cell
*/
function _storeObjPicture($colL,$dxL,$rwT,$dyT,$colR,$dxR,$rwB,$dyB)
{
$record = 0x005d; // Record identifier
$length = 0x003c; // Bytes to follow
$cObj = 0x0001; // Count of objects in file (set to 1)
$OT = 0x0008; // Object type. 8 = Picture
$id = 0x0001; // Object ID
$grbit = 0x0614; // Option flags
$cbMacro = 0x0000; // Length of FMLA structure
$Reserved1 = 0x0000; // Reserved
$Reserved2 = 0x0000; // Reserved
$icvBack = 0x09; // Background colour
$icvFore = 0x09; // Foreground colour
$fls = 0x00; // Fill pattern
$fAuto = 0x00; // Automatic fill
$icv = 0x08; // Line colour
$lns = 0xff; // Line style
$lnw = 0x01; // Line weight
$fAutoB = 0x00; // Automatic border
$frs = 0x0000; // Frame style
$cf = 0x0009; // Image format, 9 = bitmap
$Reserved3 = 0x0000; // Reserved
$cbPictFmla = 0x0000; // Length of FMLA structure
$Reserved4 = 0x0000; // Reserved
$grbit2 = 0x0001; // Option flags
$Reserved5 = 0x0000; // Reserved
$header = pack("vv", $record, $length);
$data = pack("V", $cObj);
$data .= pack("v", $OT);
$data .= pack("v", $id);
$data .= pack("v", $grbit);
$data .= pack("v", $colL);
$data .= pack("v", $dxL);
$data .= pack("v", $rwT);
$data .= pack("v", $dyT);
$data .= pack("v", $colR);
$data .= pack("v", $dxR);
$data .= pack("v", $rwB);
$data .= pack("v", $dyB);
$data .= pack("v", $cbMacro);
$data .= pack("V", $Reserved1);
$data .= pack("v", $Reserved2);
$data .= pack("C", $icvBack);
$data .= pack("C", $icvFore);
$data .= pack("C", $fls);
$data .= pack("C", $fAuto);
$data .= pack("C", $icv);
$data .= pack("C", $lns);
$data .= pack("C", $lnw);
$data .= pack("C", $fAutoB);
$data .= pack("v", $frs);
$data .= pack("V", $cf);
$data .= pack("v", $Reserved3);
$data .= pack("v", $cbPictFmla);
$data .= pack("v", $Reserved4);
$data .= pack("v", $grbit2);
$data .= pack("V", $Reserved5);
$this->_append($header.$data);
}
/**
* Convert a 24 bit bitmap into the modified internal format used by Windows.
* This is described in BITMAPCOREHEADER and BITMAPCOREINFO structures in the
* MSDN library.
*
* @access private
* @param string $bitmap The bitmap to process
* @return array Array with data and properties of the bitmap
*/
function _processBitmap($bitmap)
{
// Open file.
$bmp_fd = @fopen($bitmap,"rb");
if (!$bmp_fd) {
return($this->raiseError("Couldn't import $bitmap"));
}
// Slurp the file into a string.
$data = fread($bmp_fd, filesize($bitmap));
// Check that the file is big enough to be a bitmap.
if (strlen($data) <= 0x36) {
return($this->raiseError("$bitmap doesn't contain enough data.\n"));
}
// The first 2 bytes are used to identify the bitmap.
$identity = unpack("A2", $data);
if ($identity[''] != "BM") {
return($this->raiseError("$bitmap doesn't appear to be a valid bitmap image.\n"));
}
// Remove bitmap data: ID.
$data = substr($data, 2);
// Read and remove the bitmap size. This is more reliable than reading
// the data size at offset 0x22.
//
$size_array = unpack("V", substr($data, 0, 4));
$size = $size_array[''];
$data = substr($data, 4);
$size -= 0x36; // Subtract size of bitmap header.
$size += 0x0C; // Add size of BIFF header.
// Remove bitmap data: reserved, offset, header length.
$data = substr($data, 12);
// Read and remove the bitmap width and height. Verify the sizes.
$width_and_height = unpack("V2", substr($data, 0, 8));
$width = $width_and_height[1];
$height = $width_and_height[2];
$data = substr($data, 8);
if ($width > 0xFFFF) {
return($this->raiseError("$bitmap: largest image width supported is 65k.\n"));
}
if ($height > 0xFFFF) {
return($this->raiseError("$bitmap: largest image height supported is 65k.\n"));
}
// Read and remove the bitmap planes and bpp data. Verify them.
$planes_and_bitcount = unpack("v2", substr($data, 0, 4));
$data = substr($data, 4);
if ($planes_and_bitcount[2] != 24) { // Bitcount
return($this->raiseError("$bitmap isn't a 24bit true color bitmap.\n"));
}
if ($planes_and_bitcount[1] != 1) {
return($this->raiseError("$bitmap: only 1 plane nupported in bitmap image.\n"));
}
// Read and remove the bitmap compression. Verify compression.
$compression = unpack("V", substr($data, 0, 4));
$data = substr($data, 4);
//$compression = 0;
if ($compression[""] != 0) {
return($this->raiseError("$bitmap: compression not supported in bitmap image.\n"));
}
// Remove bitmap data: data size, hres, vres, colours, imp. colours.
$data = substr($data, 20);
// Add the BITMAPCOREHEADER data
$header = pack("Vvvvv", 0x000c, $width, $height, 0x01, 0x18);
$data = $header . $data;
return (array($width, $height, $size, $data));
}
/**
* Store the window zoom factor. This should be a reduced fraction but for
* simplicity we will store all fractions with a numerator of 100.
*
* @access private
*/
function _storeZoom()
{
// If scale is 100 we don't need to write a record
if ($this->_zoom == 100) {
return;
}
$record = 0x00A0; // Record identifier
$length = 0x0004; // Bytes to follow
$header = pack("vv", $record, $length);
$data = pack("vv", $this->_zoom, 100);
$this->_append($header.$data);
}
}
?>
/trunk/applications/jrest/lib/Spreadsheet/Excel/Writer/OLEwriter.php
New file
0,0 → 1,428
<?php
/*
* Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
*
* The majority of this is _NOT_ my code. I simply ported it from the
* PERL Spreadsheet::WriteExcel module.
*
* The author of the Spreadsheet::WriteExcel module is John McNamara
* <jmcnamara@cpan.org>
*
* I _DO_ maintain this code, and John McNamara has nothing to do with the
* porting of this code to PHP. Any questions directly related to this
* class library should be directed to me.
*
* License Information:
*
* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets
* Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
 
require_once('PEAR.php');
 
/**
* Class for creating OLE streams for Excel Spreadsheets
*
* @author Xavier Noguer <xnoguer@rezebra.com>
* @category FileFormats
* @package Spreadsheet_Excel_Writer
*/
 
class Spreadsheet_Excel_Writer_OLEwriter extends PEAR
{
/**
* Filename for the OLE stream
* @var string
* @see _initialize()
*/
var $_OLEfilename;
 
/**
* Filehandle for the OLE stream
* @var resource
*/
var $_filehandle;
 
/**
* Name of the temporal file in case OLE stream goes to stdout
* @var string
*/
var $_tmp_filename;
 
/**
* Variable for preventing closing two times
* @var integer
*/
var $_fileclosed;
 
/**
* Size of the data to be written to the OLE stream
* @var integer
*/
var $_biffsize;
 
/**
* Real data size to be written to the OLE stream
* @var integer
*/
var $_booksize;
 
/**
* Number of big blocks in the OLE stream
* @var integer
*/
var $_big_blocks;
 
/**
* Number of list blocks in the OLE stream
* @var integer
*/
var $_list_blocks;
 
/**
* Number of big blocks in the OLE stream
* @var integer
*/
var $_root_start;
 
/**
* Constructor for the OLEwriter class
*
* @param string $OLEfilename the name of the file for the OLE stream
*/
function Spreadsheet_Excel_Writer_OLEwriter($OLEfilename)
{
$this->_OLEfilename = $OLEfilename;
$this->_filehandle = "";
$this->_tmp_filename = "";
$this->_fileclosed = 0;
$this->_biff_only = 0;
//$this->_size_allowed = 0;
$this->_biffsize = 0;
$this->_booksize = 0;
$this->_big_blocks = 0;
$this->_list_blocks = 0;
$this->_root_start = 0;
//$this->_block_count = 4;
$this->_initialize();
}
 
/**
* Check for a valid filename and store the filehandle.
* Filehandle "-" writes to STDOUT
*
* @access private
*/
function _initialize()
{
$OLEfile = $this->_OLEfilename;
if(($OLEfile == '-') or ($OLEfile == ''))
{
$this->_tmp_filename = tempnam("/tmp", "OLEwriter");
$fh = fopen($this->_tmp_filename,"wb");
if ($fh == false) {
$this->raiseError("Can't create temporary file.");
}
}
else
{
// Create a new file, open for writing (in binmode)
$fh = fopen($OLEfile,"wb");
if ($fh == false) {
$this->raiseError("Can't open $OLEfile. It may be in use or protected.");
}
}
// Store filehandle
$this->_filehandle = $fh;
}
 
 
/**
* Set the size of the data to be written to the OLE stream.
* The maximun size comes from this:
* $big_blocks = (109 depot block x (128 -1 marker word)
* - (1 x end words)) = 13842
* $maxsize = $big_blocks * 512 bytes = 7087104
*
* @access public
* @see Spreadsheet_Excel_Writer_Workbook::store_OLE_file()
* @param integer $biffsize The size of the data to be written to the OLE stream
* @return integer 1 for success
*/
function setSize($biffsize)
{
$maxsize = 7087104; // TODO: extend max size
if ($biffsize > $maxsize) {
$this->raiseError("Maximum file size, $maxsize, exceeded.");
}
$this->_biffsize = $biffsize;
// Set the min file size to 4k to avoid having to use small blocks
if ($biffsize > 4096) {
$this->_booksize = $biffsize;
}
else {
$this->_booksize = 4096;
}
//$this->_size_allowed = 1;
return(1);
}
 
 
/**
* Calculate various sizes needed for the OLE stream
*
* @access private
*/
function _calculateSizes()
{
$datasize = $this->_booksize;
if ($datasize % 512 == 0) {
$this->_big_blocks = $datasize/512;
}
else {
$this->_big_blocks = floor($datasize/512) + 1;
}
// There are 127 list blocks and 1 marker blocks for each big block
// depot + 1 end of chain block
$this->_list_blocks = floor(($this->_big_blocks)/127) + 1;
$this->_root_start = $this->_big_blocks;
}
 
/**
* Write root entry, big block list and close the filehandle.
* This routine is used to explicitly close the open filehandle without
* having to wait for DESTROY.
*
* @access public
* @see Spreadsheet_Excel_Writer_Workbook::store_OLE_file()
*/
function close()
{
//return if not $this->{_size_allowed};
$this->_writePadding();
$this->_writePropertyStorage();
$this->_writeBigBlockDepot();
// Close the filehandle
fclose($this->_filehandle);
if(($this->_OLEfilename == '-') or ($this->_OLEfilename == ''))
{
$fh = fopen($this->_tmp_filename, "rb");
if ($fh == false) {
$this->raiseError("Can't read temporary file.");
}
fpassthru($fh);
@unlink($this->_tmp_filename);
}
$this->_fileclosed = 1;
}
 
 
/**
* Write BIFF data to OLE file.
*
* @param string $data string of bytes to be written
*/
function write($data)
{
fwrite($this->_filehandle,$data,strlen($data));
}
 
 
/**
* Write OLE header block.
*/
function writeHeader()
{
$this->_calculateSizes();
$root_start = $this->_root_start;
$num_lists = $this->_list_blocks;
$id = pack("nnnn", 0xD0CF, 0x11E0, 0xA1B1, 0x1AE1);
$unknown1 = pack("VVVV", 0x00, 0x00, 0x00, 0x00);
$unknown2 = pack("vv", 0x3E, 0x03);
$unknown3 = pack("v", -2);
$unknown4 = pack("v", 0x09);
$unknown5 = pack("VVV", 0x06, 0x00, 0x00);
$num_bbd_blocks = pack("V", $num_lists);
$root_startblock = pack("V", $root_start);
$unknown6 = pack("VV", 0x00, 0x1000);
$sbd_startblock = pack("V", -2);
$unknown7 = pack("VVV", 0x00, -2 ,0x00);
$unused = pack("V", -1);
fwrite($this->_filehandle,$id);
fwrite($this->_filehandle,$unknown1);
fwrite($this->_filehandle,$unknown2);
fwrite($this->_filehandle,$unknown3);
fwrite($this->_filehandle,$unknown4);
fwrite($this->_filehandle,$unknown5);
fwrite($this->_filehandle,$num_bbd_blocks);
fwrite($this->_filehandle,$root_startblock);
fwrite($this->_filehandle,$unknown6);
fwrite($this->_filehandle,$sbd_startblock);
fwrite($this->_filehandle,$unknown7);
for($i=1; $i <= $num_lists; $i++)
{
$root_start++;
fwrite($this->_filehandle,pack("V",$root_start));
}
for($i = $num_lists; $i <=108; $i++)
{
fwrite($this->_filehandle,$unused);
}
}
 
 
/**
* Write big block depot.
*
* @access private
*/
function _writeBigBlockDepot()
{
$num_blocks = $this->_big_blocks;
$num_lists = $this->_list_blocks;
$total_blocks = $num_lists *128;
$used_blocks = $num_blocks + $num_lists +2;
$marker = pack("V", -3);
$end_of_chain = pack("V", -2);
$unused = pack("V", -1);
for($i=1; $i < $num_blocks; $i++)
{
fwrite($this->_filehandle,pack("V",$i));
}
fwrite($this->_filehandle,$end_of_chain);
fwrite($this->_filehandle,$end_of_chain);
for($i=0; $i < $num_lists; $i++)
{
fwrite($this->_filehandle,$marker);
}
for($i=$used_blocks; $i <= $total_blocks; $i++)
{
fwrite($this->_filehandle,$unused);
}
}
 
/**
* Write property storage. TODO: add summary sheets
*
* @access private
*/
function _writePropertyStorage()
{
//$rootsize = -2;
/*************** name type dir start size */
$this->_writePps("Root Entry", 0x05, 1, -2, 0x00);
$this->_writePps("Book", 0x02, -1, 0x00, $this->_booksize);
$this->_writePps('', 0x00, -1, 0x00, 0x0000);
$this->_writePps('', 0x00, -1, 0x00, 0x0000);
}
 
/**
* Write property sheet in property storage
*
* @param string $name name of the property storage.
* @param integer $type type of the property storage.
* @param integer $dir dir of the property storage.
* @param integer $start start of the property storage.
* @param integer $size size of the property storage.
* @access private
*/
function _writePps($name,$type,$dir,$start,$size)
{
$length = 0;
$rawname = '';
if ($name != '')
{
$name = $name . "\0";
for($i=0;$i<strlen($name);$i++)
{
// Simulate a Unicode string
$rawname .= pack("H*",dechex(ord($name{$i}))).pack("C",0);
}
$length = strlen($name) * 2;
}
$zero = pack("C", 0);
$pps_sizeofname = pack("v", $length); // 0x40
$pps_type = pack("v", $type); // 0x42
$pps_prev = pack("V", -1); // 0x44
$pps_next = pack("V", -1); // 0x48
$pps_dir = pack("V", $dir); // 0x4c
$unknown1 = pack("V", 0);
$pps_ts1s = pack("V", 0); // 0x64
$pps_ts1d = pack("V", 0); // 0x68
$pps_ts2s = pack("V", 0); // 0x6c
$pps_ts2d = pack("V", 0); // 0x70
$pps_sb = pack("V", $start); // 0x74
$pps_size = pack("V", $size); // 0x78
fwrite($this->_filehandle,$rawname);
for($i=0; $i < (64 -$length); $i++) {
fwrite($this->_filehandle,$zero);
}
fwrite($this->_filehandle,$pps_sizeofname);
fwrite($this->_filehandle,$pps_type);
fwrite($this->_filehandle,$pps_prev);
fwrite($this->_filehandle,$pps_next);
fwrite($this->_filehandle,$pps_dir);
for($i=0; $i < 5; $i++) {
fwrite($this->_filehandle,$unknown1);
}
fwrite($this->_filehandle,$pps_ts1s);
fwrite($this->_filehandle,$pps_ts1d);
fwrite($this->_filehandle,$pps_ts2d);
fwrite($this->_filehandle,$pps_ts2d);
fwrite($this->_filehandle,$pps_sb);
fwrite($this->_filehandle,$pps_size);
fwrite($this->_filehandle,$unknown1);
}
 
/**
* Pad the end of the file
*
* @access private
*/
function _writePadding()
{
$biffsize = $this->_biffsize;
if ($biffsize < 4096) {
$min_size = 4096;
}
else {
$min_size = 512;
}
if ($biffsize % $min_size != 0)
{
$padding = $min_size - ($biffsize % $min_size);
for($i=0; $i < $padding; $i++) {
fwrite($this->_filehandle,"\0");
}
}
}
}
?>
/trunk/applications/jrest/lib/Spreadsheet/Excel/Writer/Parser.php
New file
0,0 → 1,1551
<?php
/**
* Class for parsing Excel formulas
*
* License Information:
*
* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets
* Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
 
/**
* @const SPREADSHEET_EXCEL_WRITER_ADD token identifier for character "+"
*/
define('SPREADSHEET_EXCEL_WRITER_ADD',"+");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_SUB token identifier for character "-"
*/
define('SPREADSHEET_EXCEL_WRITER_SUB',"-");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_MUL token identifier for character "*"
*/
define('SPREADSHEET_EXCEL_WRITER_MUL',"*");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_DIV token identifier for character "/"
*/
define('SPREADSHEET_EXCEL_WRITER_DIV',"/");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_OPEN token identifier for character "("
*/
define('SPREADSHEET_EXCEL_WRITER_OPEN',"(");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_CLOSE token identifier for character ")"
*/
define('SPREADSHEET_EXCEL_WRITER_CLOSE',")");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_COMA token identifier for character ","
*/
define('SPREADSHEET_EXCEL_WRITER_COMA',",");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_GT token identifier for character ">"
*/
define('SPREADSHEET_EXCEL_WRITER_GT',">");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_LT token identifier for character "<"
*/
define('SPREADSHEET_EXCEL_WRITER_LT',"<");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_LE token identifier for character "<="
*/
define('SPREADSHEET_EXCEL_WRITER_LE',"<=");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_GE token identifier for character ">="
*/
define('SPREADSHEET_EXCEL_WRITER_GE',">=");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_EQ token identifier for character "="
*/
define('SPREADSHEET_EXCEL_WRITER_EQ',"=");
 
/**
* @const SPREADSHEET_EXCEL_WRITER_NE token identifier for character "<>"
*/
define('SPREADSHEET_EXCEL_WRITER_NE',"<>");
 
 
require_once('PEAR.php');
 
/**
* Class for parsing Excel formulas
*
* @author Xavier Noguer <xnoguer@rezebra.com>
* @category FileFormats
* @package Spreadsheet_Excel_Writer
*/
 
class Spreadsheet_Excel_Writer_Parser extends PEAR
{
/**
* The index of the character we are currently looking at
* @var integer
*/
var $_current_char;
 
/**
* The token we are working on.
* @var string
*/
var $_current_token;
 
/**
* The formula to parse
* @var string
*/
var $_formula;
 
/**
* The character ahead of the current char
* @var string
*/
var $_lookahead;
 
/**
* The parse tree to be generated
* @var string
*/
var $_parse_tree;
 
/**
* The byte order. 1 => big endian, 0 => little endian.
* @var integer
*/
var $_byte_order;
 
/**
* Number of arguments for the current function
* @var integer
*/
var $_func_args;
 
/**
* Array of external sheets
* @var array
*/
var $_ext_sheets;
 
/**
* The class constructor
*
* @param integer $byte_order The byte order (Little endian or Big endian) of the architecture
(optional). 1 => big endian, 0 (default) => little endian.
*/
function Spreadsheet_Excel_Writer_Parser($byte_order = 0)
{
$this->_current_char = 0;
$this->_current_token = ''; // The token we are working on.
$this->_formula = ""; // The formula to parse.
$this->_lookahead = ''; // The character ahead of the current char.
$this->_parse_tree = ''; // The parse tree to be generated.
$this->_initializeHashes(); // Initialize the hashes: ptg's and function's ptg's
$this->_byte_order = $byte_order; // Little Endian or Big Endian
$this->_func_args = 0; // Number of arguments for the current function
$this->_ext_sheets = array();
}
/**
* Initialize the ptg and function hashes.
*
* @access private
*/
function _initializeHashes()
{
// The Excel ptg indices
$this->ptg = array(
'ptgExp' => 0x01,
'ptgTbl' => 0x02,
'ptgAdd' => 0x03,
'ptgSub' => 0x04,
'ptgMul' => 0x05,
'ptgDiv' => 0x06,
'ptgPower' => 0x07,
'ptgConcat' => 0x08,
'ptgLT' => 0x09,
'ptgLE' => 0x0A,
'ptgEQ' => 0x0B,
'ptgGE' => 0x0C,
'ptgGT' => 0x0D,
'ptgNE' => 0x0E,
'ptgIsect' => 0x0F,
'ptgUnion' => 0x10,
'ptgRange' => 0x11,
'ptgUplus' => 0x12,
'ptgUminus' => 0x13,
'ptgPercent' => 0x14,
'ptgParen' => 0x15,
'ptgMissArg' => 0x16,
'ptgStr' => 0x17,
'ptgAttr' => 0x19,
'ptgSheet' => 0x1A,
'ptgEndSheet' => 0x1B,
'ptgErr' => 0x1C,
'ptgBool' => 0x1D,
'ptgInt' => 0x1E,
'ptgNum' => 0x1F,
'ptgArray' => 0x20,
'ptgFunc' => 0x21,
'ptgFuncVar' => 0x22,
'ptgName' => 0x23,
'ptgRef' => 0x24,
'ptgArea' => 0x25,
'ptgMemArea' => 0x26,
'ptgMemErr' => 0x27,
'ptgMemNoMem' => 0x28,
'ptgMemFunc' => 0x29,
'ptgRefErr' => 0x2A,
'ptgAreaErr' => 0x2B,
'ptgRefN' => 0x2C,
'ptgAreaN' => 0x2D,
'ptgMemAreaN' => 0x2E,
'ptgMemNoMemN' => 0x2F,
'ptgNameX' => 0x39,
'ptgRef3d' => 0x3A,
'ptgArea3d' => 0x3B,
'ptgRefErr3d' => 0x3C,
'ptgAreaErr3d' => 0x3D,
'ptgArrayV' => 0x40,
'ptgFuncV' => 0x41,
'ptgFuncVarV' => 0x42,
'ptgNameV' => 0x43,
'ptgRefV' => 0x44,
'ptgAreaV' => 0x45,
'ptgMemAreaV' => 0x46,
'ptgMemErrV' => 0x47,
'ptgMemNoMemV' => 0x48,
'ptgMemFuncV' => 0x49,
'ptgRefErrV' => 0x4A,
'ptgAreaErrV' => 0x4B,
'ptgRefNV' => 0x4C,
'ptgAreaNV' => 0x4D,
'ptgMemAreaNV' => 0x4E,
'ptgMemNoMemN' => 0x4F,
'ptgFuncCEV' => 0x58,
'ptgNameXV' => 0x59,
'ptgRef3dV' => 0x5A,
'ptgArea3dV' => 0x5B,
'ptgRefErr3dV' => 0x5C,
'ptgAreaErr3d' => 0x5D,
'ptgArrayA' => 0x60,
'ptgFuncA' => 0x61,
'ptgFuncVarA' => 0x62,
'ptgNameA' => 0x63,
'ptgRefA' => 0x64,
'ptgAreaA' => 0x65,
'ptgMemAreaA' => 0x66,
'ptgMemErrA' => 0x67,
'ptgMemNoMemA' => 0x68,
'ptgMemFuncA' => 0x69,
'ptgRefErrA' => 0x6A,
'ptgAreaErrA' => 0x6B,
'ptgRefNA' => 0x6C,
'ptgAreaNA' => 0x6D,
'ptgMemAreaNA' => 0x6E,
'ptgMemNoMemN' => 0x6F,
'ptgFuncCEA' => 0x78,
'ptgNameXA' => 0x79,
'ptgRef3dA' => 0x7A,
'ptgArea3dA' => 0x7B,
'ptgRefErr3dA' => 0x7C,
'ptgAreaErr3d' => 0x7D
);
// Thanks to Michael Meeks and Gnumeric for the initial arg values.
//
// The following hash was generated by "function_locale.pl" in the distro.
// Refer to function_locale.pl for non-English function names.
//
// The array elements are as follow:
// ptg: The Excel function ptg code.
// args: The number of arguments that the function takes:
// >=0 is a fixed number of arguments.
// -1 is a variable number of arguments.
// class: The reference, value or array class of the function args.
// vol: The function is volatile.
//
$this->_functions = array(
// function ptg args class vol
'COUNT' => array( 0, -1, 0, 0 ),
'IF' => array( 1, -1, 1, 0 ),
'ISNA' => array( 2, 1, 1, 0 ),
'ISERROR' => array( 3, 1, 1, 0 ),
'SUM' => array( 4, -1, 0, 0 ),
'AVERAGE' => array( 5, -1, 0, 0 ),
'MIN' => array( 6, -1, 0, 0 ),
'MAX' => array( 7, -1, 0, 0 ),
'ROW' => array( 8, -1, 0, 0 ),
'COLUMN' => array( 9, -1, 0, 0 ),
'NA' => array( 10, 0, 0, 0 ),
'NPV' => array( 11, -1, 1, 0 ),
'STDEV' => array( 12, -1, 0, 0 ),
'DOLLAR' => array( 13, -1, 1, 0 ),
'FIXED' => array( 14, -1, 1, 0 ),
'SIN' => array( 15, 1, 1, 0 ),
'COS' => array( 16, 1, 1, 0 ),
'TAN' => array( 17, 1, 1, 0 ),
'ATAN' => array( 18, 1, 1, 0 ),
'PI' => array( 19, 0, 1, 0 ),
'SQRT' => array( 20, 1, 1, 0 ),
'EXP' => array( 21, 1, 1, 0 ),
'LN' => array( 22, 1, 1, 0 ),
'LOG10' => array( 23, 1, 1, 0 ),
'ABS' => array( 24, 1, 1, 0 ),
'INT' => array( 25, 1, 1, 0 ),
'SIGN' => array( 26, 1, 1, 0 ),
'ROUND' => array( 27, 2, 1, 0 ),
'LOOKUP' => array( 28, -1, 0, 0 ),
'INDEX' => array( 29, -1, 0, 1 ),
'REPT' => array( 30, 2, 1, 0 ),
'MID' => array( 31, 3, 1, 0 ),
'LEN' => array( 32, 1, 1, 0 ),
'VALUE' => array( 33, 1, 1, 0 ),
'TRUE' => array( 34, 0, 1, 0 ),
'FALSE' => array( 35, 0, 1, 0 ),
'AND' => array( 36, -1, 0, 0 ),
'OR' => array( 37, -1, 0, 0 ),
'NOT' => array( 38, 1, 1, 0 ),
'MOD' => array( 39, 2, 1, 0 ),
'DCOUNT' => array( 40, 3, 0, 0 ),
'DSUM' => array( 41, 3, 0, 0 ),
'DAVERAGE' => array( 42, 3, 0, 0 ),
'DMIN' => array( 43, 3, 0, 0 ),
'DMAX' => array( 44, 3, 0, 0 ),
'DSTDEV' => array( 45, 3, 0, 0 ),
'VAR' => array( 46, -1, 0, 0 ),
'DVAR' => array( 47, 3, 0, 0 ),
'TEXT' => array( 48, 2, 1, 0 ),
'LINEST' => array( 49, -1, 0, 0 ),
'TREND' => array( 50, -1, 0, 0 ),
'LOGEST' => array( 51, -1, 0, 0 ),
'GROWTH' => array( 52, -1, 0, 0 ),
'PV' => array( 56, -1, 1, 0 ),
'FV' => array( 57, -1, 1, 0 ),
'NPER' => array( 58, -1, 1, 0 ),
'PMT' => array( 59, -1, 1, 0 ),
'RATE' => array( 60, -1, 1, 0 ),
'MIRR' => array( 61, 3, 0, 0 ),
'IRR' => array( 62, -1, 0, 0 ),
'RAND' => array( 63, 0, 1, 1 ),
'MATCH' => array( 64, -1, 0, 0 ),
'DATE' => array( 65, 3, 1, 0 ),
'TIME' => array( 66, 3, 1, 0 ),
'DAY' => array( 67, 1, 1, 0 ),
'MONTH' => array( 68, 1, 1, 0 ),
'YEAR' => array( 69, 1, 1, 0 ),
'WEEKDAY' => array( 70, -1, 1, 0 ),
'HOUR' => array( 71, 1, 1, 0 ),
'MINUTE' => array( 72, 1, 1, 0 ),
'SECOND' => array( 73, 1, 1, 0 ),
'NOW' => array( 74, 0, 1, 1 ),
'AREAS' => array( 75, 1, 0, 1 ),
'ROWS' => array( 76, 1, 0, 1 ),
'COLUMNS' => array( 77, 1, 0, 1 ),
'OFFSET' => array( 78, -1, 0, 1 ),
'SEARCH' => array( 82, -1, 1, 0 ),
'TRANSPOSE' => array( 83, 1, 1, 0 ),
'TYPE' => array( 86, 1, 1, 0 ),
'ATAN2' => array( 97, 2, 1, 0 ),
'ASIN' => array( 98, 1, 1, 0 ),
'ACOS' => array( 99, 1, 1, 0 ),
'CHOOSE' => array( 100, -1, 1, 0 ),
'HLOOKUP' => array( 101, -1, 0, 0 ),
'VLOOKUP' => array( 102, -1, 0, 0 ),
'ISREF' => array( 105, 1, 0, 0 ),
'LOG' => array( 109, -1, 1, 0 ),
'CHAR' => array( 111, 1, 1, 0 ),
'LOWER' => array( 112, 1, 1, 0 ),
'UPPER' => array( 113, 1, 1, 0 ),
'PROPER' => array( 114, 1, 1, 0 ),
'LEFT' => array( 115, -1, 1, 0 ),
'RIGHT' => array( 116, -1, 1, 0 ),
'EXACT' => array( 117, 2, 1, 0 ),
'TRIM' => array( 118, 1, 1, 0 ),
'REPLACE' => array( 119, 4, 1, 0 ),
'SUBSTITUTE' => array( 120, -1, 1, 0 ),
'CODE' => array( 121, 1, 1, 0 ),
'FIND' => array( 124, -1, 1, 0 ),
'CELL' => array( 125, -1, 0, 1 ),
'ISERR' => array( 126, 1, 1, 0 ),
'ISTEXT' => array( 127, 1, 1, 0 ),
'ISNUMBER' => array( 128, 1, 1, 0 ),
'ISBLANK' => array( 129, 1, 1, 0 ),
'T' => array( 130, 1, 0, 0 ),
'N' => array( 131, 1, 0, 0 ),
'DATEVALUE' => array( 140, 1, 1, 0 ),
'TIMEVALUE' => array( 141, 1, 1, 0 ),
'SLN' => array( 142, 3, 1, 0 ),
'SYD' => array( 143, 4, 1, 0 ),
'DDB' => array( 144, -1, 1, 0 ),
'INDIRECT' => array( 148, -1, 1, 1 ),
'CALL' => array( 150, -1, 1, 0 ),
'CLEAN' => array( 162, 1, 1, 0 ),
'MDETERM' => array( 163, 1, 2, 0 ),
'MINVERSE' => array( 164, 1, 2, 0 ),
'MMULT' => array( 165, 2, 2, 0 ),
'IPMT' => array( 167, -1, 1, 0 ),
'PPMT' => array( 168, -1, 1, 0 ),
'COUNTA' => array( 169, -1, 0, 0 ),
'PRODUCT' => array( 183, -1, 0, 0 ),
'FACT' => array( 184, 1, 1, 0 ),
'DPRODUCT' => array( 189, 3, 0, 0 ),
'ISNONTEXT' => array( 190, 1, 1, 0 ),
'STDEVP' => array( 193, -1, 0, 0 ),
'VARP' => array( 194, -1, 0, 0 ),
'DSTDEVP' => array( 195, 3, 0, 0 ),
'DVARP' => array( 196, 3, 0, 0 ),
'TRUNC' => array( 197, -1, 1, 0 ),
'ISLOGICAL' => array( 198, 1, 1, 0 ),
'DCOUNTA' => array( 199, 3, 0, 0 ),
'ROUNDUP' => array( 212, 2, 1, 0 ),
'ROUNDDOWN' => array( 213, 2, 1, 0 ),
'RANK' => array( 216, -1, 0, 0 ),
'ADDRESS' => array( 219, -1, 1, 0 ),
'DAYS360' => array( 220, -1, 1, 0 ),
'TODAY' => array( 221, 0, 1, 1 ),
'VDB' => array( 222, -1, 1, 0 ),
'MEDIAN' => array( 227, -1, 0, 0 ),
'SUMPRODUCT' => array( 228, -1, 2, 0 ),
'SINH' => array( 229, 1, 1, 0 ),
'COSH' => array( 230, 1, 1, 0 ),
'TANH' => array( 231, 1, 1, 0 ),
'ASINH' => array( 232, 1, 1, 0 ),
'ACOSH' => array( 233, 1, 1, 0 ),
'ATANH' => array( 234, 1, 1, 0 ),
'DGET' => array( 235, 3, 0, 0 ),
'INFO' => array( 244, 1, 1, 1 ),
'DB' => array( 247, -1, 1, 0 ),
'FREQUENCY' => array( 252, 2, 0, 0 ),
'ERROR.TYPE' => array( 261, 1, 1, 0 ),
'REGISTER.ID' => array( 267, -1, 1, 0 ),
'AVEDEV' => array( 269, -1, 0, 0 ),
'BETADIST' => array( 270, -1, 1, 0 ),
'GAMMALN' => array( 271, 1, 1, 0 ),
'BETAINV' => array( 272, -1, 1, 0 ),
'BINOMDIST' => array( 273, 4, 1, 0 ),
'CHIDIST' => array( 274, 2, 1, 0 ),
'CHIINV' => array( 275, 2, 1, 0 ),
'COMBIN' => array( 276, 2, 1, 0 ),
'CONFIDENCE' => array( 277, 3, 1, 0 ),
'CRITBINOM' => array( 278, 3, 1, 0 ),
'EVEN' => array( 279, 1, 1, 0 ),
'EXPONDIST' => array( 280, 3, 1, 0 ),
'FDIST' => array( 281, 3, 1, 0 ),
'FINV' => array( 282, 3, 1, 0 ),
'FISHER' => array( 283, 1, 1, 0 ),
'FISHERINV' => array( 284, 1, 1, 0 ),
'FLOOR' => array( 285, 2, 1, 0 ),
'GAMMADIST' => array( 286, 4, 1, 0 ),
'GAMMAINV' => array( 287, 3, 1, 0 ),
'CEILING' => array( 288, 2, 1, 0 ),
'HYPGEOMDIST' => array( 289, 4, 1, 0 ),
'LOGNORMDIST' => array( 290, 3, 1, 0 ),
'LOGINV' => array( 291, 3, 1, 0 ),
'NEGBINOMDIST' => array( 292, 3, 1, 0 ),
'NORMDIST' => array( 293, 4, 1, 0 ),
'NORMSDIST' => array( 294, 1, 1, 0 ),
'NORMINV' => array( 295, 3, 1, 0 ),
'NORMSINV' => array( 296, 1, 1, 0 ),
'STANDARDIZE' => array( 297, 3, 1, 0 ),
'ODD' => array( 298, 1, 1, 0 ),
'PERMUT' => array( 299, 2, 1, 0 ),
'POISSON' => array( 300, 3, 1, 0 ),
'TDIST' => array( 301, 3, 1, 0 ),
'WEIBULL' => array( 302, 4, 1, 0 ),
'SUMXMY2' => array( 303, 2, 2, 0 ),
'SUMX2MY2' => array( 304, 2, 2, 0 ),
'SUMX2PY2' => array( 305, 2, 2, 0 ),
'CHITEST' => array( 306, 2, 2, 0 ),
'CORREL' => array( 307, 2, 2, 0 ),
'COVAR' => array( 308, 2, 2, 0 ),
'FORECAST' => array( 309, 3, 2, 0 ),
'FTEST' => array( 310, 2, 2, 0 ),
'INTERCEPT' => array( 311, 2, 2, 0 ),
'PEARSON' => array( 312, 2, 2, 0 ),
'RSQ' => array( 313, 2, 2, 0 ),
'STEYX' => array( 314, 2, 2, 0 ),
'SLOPE' => array( 315, 2, 2, 0 ),
'TTEST' => array( 316, 4, 2, 0 ),
'PROB' => array( 317, -1, 2, 0 ),
'DEVSQ' => array( 318, -1, 0, 0 ),
'GEOMEAN' => array( 319, -1, 0, 0 ),
'HARMEAN' => array( 320, -1, 0, 0 ),
'SUMSQ' => array( 321, -1, 0, 0 ),
'KURT' => array( 322, -1, 0, 0 ),
'SKEW' => array( 323, -1, 0, 0 ),
'ZTEST' => array( 324, -1, 0, 0 ),
'LARGE' => array( 325, 2, 0, 0 ),
'SMALL' => array( 326, 2, 0, 0 ),
'QUARTILE' => array( 327, 2, 0, 0 ),
'PERCENTILE' => array( 328, 2, 0, 0 ),
'PERCENTRANK' => array( 329, -1, 0, 0 ),
'MODE' => array( 330, -1, 2, 0 ),
'TRIMMEAN' => array( 331, 2, 0, 0 ),
'TINV' => array( 332, 2, 1, 0 ),
'CONCATENATE' => array( 336, -1, 1, 0 ),
'POWER' => array( 337, 2, 1, 0 ),
'RADIANS' => array( 342, 1, 1, 0 ),
'DEGREES' => array( 343, 1, 1, 0 ),
'SUBTOTAL' => array( 344, -1, 0, 0 ),
'SUMIF' => array( 345, -1, 0, 0 ),
'COUNTIF' => array( 346, 2, 0, 0 ),
'COUNTBLANK' => array( 347, 1, 0, 0 ),
'ROMAN' => array( 354, -1, 1, 0 )
);
}
/**
* Convert a token to the proper ptg value.
*
* @access private
* @param mixed $token The token to convert.
*/
function _convert($token)
{
if (preg_match("/^\"[^\"]{0,255}\"$/", $token))
{
return $this->_convertString($token);
}
elseif (is_numeric($token))
{
return $this->_convertNumber($token);
}
// match references like A1 or $A$1
elseif(preg_match('/^\$?([A-I]?[A-Z])\$?(\d+)$/',$token))
{
return($this->_convertRef2d($token));
}
// match external references like Sheet1:Sheet2!A1
elseif (preg_match("/^[A-Za-z0-9_]+(\:[A-Za-z0-9_]+)?\![A-I]?[A-Z](\d+)$/",$token))
{
return $this->_convertRef3d($token);
}
// match ranges like A1:B2
elseif(preg_match("/^(\$)?[A-I]?[A-Z](\$)?(\d+)\:(\$)?[A-I]?[A-Z](\$)?(\d+)$/",$token))
{
return($this->_convertRange2d($token));
}
// match ranges like A1..B2
elseif(preg_match("/^(\$)?[A-I]?[A-Z](\$)?(\d+)\.\.(\$)?[A-I]?[A-Z](\$)?(\d+)$/",$token))
{
return($this->_convertRange2d($token));
}
// match external ranges like Sheet1:Sheet2!A1:B2
elseif (preg_match("/^[A-Za-z0-9_]+(\:[A-Za-z0-9_]+)?\!([A-I]?[A-Z])?(\d+)\:([A-I]?[A-Z])?(\d+)$/",$token))
{
return $this->_convertRange3d($token);
}
elseif(isset($this->ptg[$token])) // operators (including parentheses)
{
return(pack("C", $this->ptg[$token]));
}
elseif(preg_match("/[A-Z0-9\xc0-\xdc\.]+/",$token))
{
return($this->_convertFunction($token,$this->_func_args));
}
// if it's an argument, ignore the token (the argument remains)
elseif($token == 'arg')
{
$this->_func_args++;
return('');
}
// TODO: use real error codes
$this->raiseError("Unknown token $token", 0, PEAR_ERROR_DIE);
}
/**
* Convert a number token to ptgInt or ptgNum
*
* @access private
* @param mixed $num an integer or double for conversion to its ptg value
*/
function _convertNumber($num)
{
// Integer in the range 0..2**16-1
if ((preg_match("/^\d+$/",$num)) and ($num <= 65535)) {
return(pack("Cv", $this->ptg['ptgInt'], $num));
}
else // A float
{
if($this->_byte_order) // if it's Big Endian
{
$num = strrev($num);
}
return(pack("Cd", $this->ptg['ptgNum'], $num));
}
}
/**
* Convert a string token to ptgStr
*
* @access private
* @param string $string A string for conversion to its ptg value
*/
function _convertString($string)
{
// chop away beggining and ending quotes
$string = substr($string, 1, strlen($string) - 2);
return pack("CC", $this->ptg['ptgStr'], strlen($string)).$string;
}
/**
* Convert a function to a ptgFunc or ptgFuncVarV depending on the number of
* args that it takes.
*
* @access private
* @param string $token The name of the function for convertion to ptg value.
* @param integer $num_args The number of arguments the function recieves.
*/
function _convertFunction($token, $num_args)
{
$this->_func_args = 0; // re initialize the number of arguments
$args = $this->_functions[$token][1];
$volatile = $this->_functions[$token][3];
// Fixed number of args eg. TIME($i,$j,$k).
if ($args >= 0) {
return(pack("Cv", $this->ptg['ptgFuncV'], $this->_functions[$token][0]));
}
// Variable number of args eg. SUM($i,$j,$k, ..).
if ($args == -1) {
return(pack("CCv", $this->ptg['ptgFuncVarV'], $num_args, $this->_functions[$token][0]));
}
}
/**
* Convert an Excel range such as A1:D4 to a ptgRefV.
*
* @access private
* @param string $range An Excel range in the A1:A2 or A1..A2 format.
*/
function _convertRange2d($range)
{
$class = 2; // as far as I know, this is magick.
// Split the range into 2 cell refs
if(preg_match("/^([A-I]?[A-Z])(\d+)\:([A-I]?[A-Z])(\d+)$/",$range)) {
list($cell1, $cell2) = split(':', $range);
}
elseif(preg_match("/^([A-I]?[A-Z])(\d+)\.\.([A-I]?[A-Z])(\d+)$/",$range)) {
list($cell1, $cell2) = split('\.\.', $range);
}
else {
// TODO: use real error codes
$this->raiseError("Unknown range separator", 0, PEAR_ERROR_DIE);
}
// Convert the cell references
$cell_array1 = $this->_cellToPackedRowcol($cell1);
if($this->isError($cell_array1)) {
return($cell_array1);
}
list($row1, $col1) = $cell_array1; //$this->_cellToPackedRowcol($cell1);
$cell_array2 = $this->_cellToPackedRowcol($cell2);
if($this->isError($cell_array2)) {
return($cell_array2);
}
list($row2, $col2) = $cell_array2; //$this->_cellToPackedRowcol($cell2);
// The ptg value depends on the class of the ptg.
if ($class == 0) {
$ptgArea = pack("C", $this->ptg['ptgArea']);
}
elseif ($class == 1) {
$ptgArea = pack("C", $this->ptg['ptgAreaV']);
}
elseif ($class == 2) {
$ptgArea = pack("C", $this->ptg['ptgAreaA']);
}
else {
// TODO: use real error codes
$this->raiseError("Unknown class $class", 0, PEAR_ERROR_DIE);
}
return($ptgArea . $row1 . $row2 . $col1. $col2);
}
/**
* Convert an Excel 3d range such as "Sheet1!A1:D4" or "Sheet1:Sheet2!A1:D4" to
* a ptgArea3dV.
*
* @access private
* @param string $token An Excel range in the Sheet1!A1:A2 format.
*/
function _convertRange3d($token)
{
$class = 2; // as far as I know, this is magick.
// Split the ref at the ! symbol
list($ext_ref, $range) = split('!', $token);
// Convert the external reference part
$ext_ref = $this->_packExtRef($ext_ref);
if ($this->isError($ext_ref)) {
return $ext_ref;
}
// Split the range into 2 cell refs
list($cell1, $cell2) = split(':', $range);
// Convert the cell references
if (preg_match("/^(\$)?[A-I]?[A-Z](\$)?(\d+)$/", $cell1))
{
$cell_array1 = $this->_cellToPackedRowcol($cell1);
if (PEAR::isError($cell_array1)) {
return $cell_array1;
}
list($row1, $col1) = $cell_array1;
$cell_array2 = $this->_cellToPackedRowcol($cell2);
if (PEAR::isError($cell_array2)) {
return $cell_array2;
}
list($row2, $col2) = $cell_array2;
}
else { // It's a columns range (like 26:27)
$cells_array = $this->_rangeToPackedRange($cell1.':'.$cell2);
if (PEAR::isError($cells_array)) {
return $cells_array;
}
list($row1, $col1, $row2, $col2) = $cells_array;
}
// The ptg value depends on the class of the ptg.
if ($class == 0) {
$ptgArea = pack("C", $this->ptg['ptgArea3d']);
}
elseif ($class == 1) {
$ptgArea = pack("C", $this->ptg['ptgArea3dV']);
}
elseif ($class == 2) {
$ptgArea = pack("C", $this->ptg['ptgArea3dA']);
}
else {
$this->raiseError("Unknown class $class", 0, PEAR_ERROR_DIE);
}
return $ptgArea . $ext_ref . $row1 . $row2 . $col1. $col2;
}
 
/**
* Convert an Excel reference such as A1, $B2, C$3 or $D$4 to a ptgRefV.
*
* @access private
* @param string $cell An Excel cell reference
* @return string The cell in packed() format with the corresponding ptg
*/
function _convertRef2d($cell)
{
$class = 2; // as far as I know, this is magick.
// Convert the cell reference
$cell_array = $this->_cellToPackedRowcol($cell);
if($this->isError($cell_array)) {
return($cell_array);
}
list($row, $col) = $cell_array;
// The ptg value depends on the class of the ptg.
if ($class == 0) {
$ptgRef = pack("C", $this->ptg['ptgRef']);
}
elseif ($class == 1) {
$ptgRef = pack("C", $this->ptg['ptgRefV']);
}
elseif ($class == 2) {
$ptgRef = pack("C", $this->ptg['ptgRefA']);
}
else {
// TODO: use real error codes
$this->raiseError("Unknown class $class", 0, PEAR_ERROR_DIE);
}
return($ptgRef.$row.$col);
}
/**
* Convert an Excel 3d reference such as "Sheet1!A1" or "Sheet1:Sheet2!A1" to a
* ptgRef3dV.
*
* @access private
* @param string $cell An Excel cell reference
* @return string The cell in packed() format with the corresponding ptg
*/
function _convertRef3d($cell)
{
$class = 2; // as far as I know, this is magick.
// Split the ref at the ! symbol
list($ext_ref, $cell) = split('!', $cell);
// Convert the external reference part
$ext_ref = $this->_packExtRef($ext_ref);
if ($this->isError($ext_ref)) {
return $ext_ref;
}
// Convert the cell reference part
list($row, $col) = $this->_cellToPackedRowcol($cell);
// The ptg value depends on the class of the ptg.
if ($class == 0) {
$ptgRef = pack("C", $this->ptg['ptgRef3d']);
}
elseif ($class == 1) {
$ptgRef = pack("C", $this->ptg['ptgRef3dV']);
}
elseif ($class == 2) {
$ptgRef = pack("C", $this->ptg['ptgRef3dA']);
}
else {
$this->raiseError("Unknown class $class", 0, PEAR_ERROR_DIE);
}
 
return $ptgRef . $ext_ref. $row . $col;
}
 
/**
* Convert the sheet name part of an external reference, for example "Sheet1" or
* "Sheet1:Sheet2", to a packed structure.
*
* @access private
* @param string $ext_ref The name of the external reference
* @return string The reference index in packed() format
*/
function _packExtRef($ext_ref)
{
$ext_ref = preg_replace("/^'/", '', $ext_ref); // Remove leading ' if any.
$ext_ref = preg_replace("/'$/", '', $ext_ref); // Remove trailing ' if any.
// Check if there is a sheet range eg., Sheet1:Sheet2.
if (preg_match("/:/", $ext_ref))
{
list($sheet_name1, $sheet_name2) = split(':', $ext_ref);
$sheet1 = $this->_getSheetIndex($sheet_name1);
if ($sheet1 == -1) {
return $this->raiseError("Unknown sheet name $sheet_name1 in formula");
}
$sheet2 = $this->_getSheetIndex($sheet_name2);
if ($sheet2 == -1) {
return $this->raiseError("Unknown sheet name $sheet_name2 in formula");
}
// Reverse max and min sheet numbers if necessary
if ($sheet1 > $sheet2) {
list($sheet1, $sheet2) = array($sheet2, $sheet1);
}
}
else // Single sheet name only.
{
$sheet1 = $this->_getSheetIndex($ext_ref);
if ($sheet1 == -1) {
return $this->raiseError("Unknown sheet name $ext_ref in formula");
}
$sheet2 = $sheet1;
}
// References are stored relative to 0xFFFF.
$offset = -1 - $sheet1;
 
return pack('vdvv', $offset, 0x00, $sheet1, $sheet2);
}
 
/**
* Look up the index that corresponds to an external sheet name. The hash of
* sheet names is updated by the addworksheet() method of the
* Spreadsheet_Excel_Writer_Workbook class.
*
* @access private
* @return integer
*/
function _getSheetIndex($sheet_name)
{
if (!isset($this->_ext_sheets[$sheet_name])) {
return -1;
}
else {
return $this->_ext_sheets[$sheet_name];
}
}
 
/**
* This method is used to update the array of sheet names. It is
* called by the addWorksheet() method of the Spreadsheet_Excel_Writer_Workbook class.
*
* @access private
* @param string $name The name of the worksheet being added
* @param integer $index The index of the worksheet being added
*/
function setExtSheet($name, $index)
{
$this->_ext_sheets[$name] = $index;
}
 
/**
* pack() row and column into the required 3 byte format.
*
* @access private
* @param string $cell The Excel cell reference to be packed
* @return array Array containing the row and column in packed() format
*/
function _cellToPackedRowcol($cell)
{
list($row, $col, $row_rel, $col_rel) = $this->_cellToRowcol($cell);
if ($col >= 256) {
return($this->raiseError("Column in: $cell greater than 255"));
}
if ($row >= 16384) {
return($this->raiseError("Row in: $cell greater than 16384 "));
}
// Set the high bits to indicate if row or col are relative.
$row |= $col_rel << 14;
$row |= $row_rel << 15;
$row = pack('v', $row);
$col = pack('C', $col);
return(array($row, $col));
}
/**
* pack() row range into the required 3 byte format.
* Just using maximun col/rows, which is probably not the correct solution
*
* @access private
* @param string $range The Excel range to be packed
* @return array Array containing (row1,col1,row2,col2) in packed() format
*/
function _rangeToPackedRange($range)
{
preg_match('/(\$)?(\d+)\:(\$)?(\d+)/', $range, $match);
// return absolute rows if there is a $ in the ref
$row1_rel = empty($match[1]) ? 1 : 0;
$row1 = $match[2];
$row2_rel = empty($match[3]) ? 1 : 0;
$row2 = $match[4];
// Convert 1-index to zero-index
$row1--;
$row2--;
// Trick poor inocent Excel
$col1 = 0;
$col2 = 16383; // maximum possible value for Excel 5 (change this!!!)
 
//list($row, $col, $row_rel, $col_rel) = $this->_cellToRowcol($cell);
if (($row1 >= 16384) or ($row2 >= 16384)) {
return new PEAR_Error("Row in: $range greater than 16384 ");
}
// Set the high bits to indicate if rows are relative.
$row1 |= $row1_rel << 14;
$row2 |= $row2_rel << 15;
$row1 = pack('v', $row1);
$row2 = pack('v', $row2);
$col1 = pack('C', $col1);
$col2 = pack('C', $col2);
return array($row1, $col1, $row2, $col2);
}
 
/**
* Convert an Excel cell reference such as A1 or $B2 or C$3 or $D$4 to a zero
* indexed row and column number. Also returns two (0,1) values to indicate
* whether the row or column are relative references.
*
* @access private
* @param string $cell The Excel cell reference in A1 format.
* @return array
*/
function _cellToRowcol($cell)
{
preg_match('/(\$)?([A-I]?[A-Z])(\$)?(\d+)/',$cell,$match);
// return absolute column if there is a $ in the ref
$col_rel = empty($match[1]) ? 1 : 0;
$col_ref = $match[2];
$row_rel = empty($match[3]) ? 1 : 0;
$row = $match[4];
// Convert base26 column string to a number.
$expn = strlen($col_ref) - 1;
$col = 0;
for($i=0; $i < strlen($col_ref); $i++)
{
$col += (ord($col_ref{$i}) - ord('A') + 1) * pow(26, $expn);
$expn--;
}
// Convert 1-index to zero-index
$row--;
$col--;
return(array($row, $col, $row_rel, $col_rel));
}
/**
* Advance to the next valid token.
*
* @access private
*/
function _advance()
{
$i = $this->_current_char;
// eat up white spaces
if($i < strlen($this->_formula))
{
while($this->_formula{$i} == " ") {
$i++;
}
if($i < strlen($this->_formula) - 1) {
$this->_lookahead = $this->_formula{$i+1};
}
$token = "";
}
while($i < strlen($this->_formula))
{
$token .= $this->_formula{$i};
if($this->_match($token) != '')
{
if($i < strlen($this->_formula) - 1) {
$this->_lookahead = $this->_formula{$i+1};
}
$this->_current_char = $i + 1;
$this->_current_token = $token;
return(1);
}
if ($i < strlen($this->_formula) - 2) {
$this->_lookahead = $this->_formula{$i+2};
}
// if we run out of characters _lookahead becomes empty
else {
$this->_lookahead = '';
}
$i++;
}
//die("Lexical error ".$this->_current_char);
}
/**
* Checks if it's a valid token.
*
* @access private
* @param mixed $token The token to check.
* @return mixed The checked token or false on failure
*/
function _match($token)
{
switch($token)
{
case SPREADSHEET_EXCEL_WRITER_ADD:
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_SUB:
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_MUL:
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_DIV:
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_OPEN:
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_CLOSE:
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_COMA:
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_GT:
if ($this->_lookahead == '=') { // it's a GE token
break;
}
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_LT:
// it's a LE or a NE token
if (($this->_lookahead == '=') or ($this->_lookahead == '>')) {
break;
}
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_GE:
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_LE:
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_EQ:
return($token);
break;
case SPREADSHEET_EXCEL_WRITER_NE:
return($token);
break;
default:
// if it's a reference
if (preg_match('/^\$?[A-I]?[A-Z]\$?[0-9]+$/',$token) and
!ereg("[0-9]",$this->_lookahead) and
($this->_lookahead != ':') and ($this->_lookahead != '.') and
($this->_lookahead != '!'))
{
return $token;
}
// If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1)
elseif (preg_match("/^[A-Za-z0-9_]+(\:[A-Za-z0-9_]+)?\![A-I]?[A-Z][0-9]+$/",$token) and
!ereg("[0-9]",$this->_lookahead) and
($this->_lookahead != ':') and ($this->_lookahead != '.'))
{
return $token;
}
// if it's a range (A1:A2)
elseif (preg_match("/^(\$)?[A-I]?[A-Z](\$)?[0-9]+:(\$)?[A-I]?[A-Z](\$)?[0-9]+$/",$token) and
!ereg("[0-9]",$this->_lookahead))
{
return $token;
}
// if it's a range (A1..A2)
elseif (preg_match("/^(\$)?[A-I]?[A-Z](\$)?[0-9]+\.\.(\$)?[A-I]?[A-Z](\$)?[0-9]+$/",$token) and
!ereg("[0-9]",$this->_lookahead))
{
return $token;
}
// If it's an external range
elseif (preg_match("/^[A-Za-z0-9_]+(\:[A-Za-z0-9_]+)?\!([A-I]?[A-Z])?[0-9]+:([A-I]?[A-Z])?[0-9]+$/",$token) and
!ereg("[0-9]",$this->_lookahead))
{
return $token;
}
// If it's a number (check that it's not a sheet name or range)
elseif (is_numeric($token) and !is_numeric($token.$this->_lookahead) and
($this->_lookahead != '!') and (($this->_lookahead != ':')))
{
return $token;
}
// If it's a string (of maximum 255 characters)
elseif(ereg("^\"[^\"]{0,255}\"$",$token))
{
return($token);
}
// if it's a function call
elseif(eregi("^[A-Z0-9\xc0-\xdc\.]+$",$token) and ($this->_lookahead == "("))
{
return($token);
}
return '';
}
}
/**
* The parsing method. It parses a formula.
*
* @access public
* @param string $formula The formula to parse, without the initial equal sign (=).
*/
function parse($formula)
{
$this->_current_char = 0;
$this->_formula = $formula;
$this->_lookahead = $formula{1};
$this->_advance();
$this->_parse_tree = $this->_condition();
if ($this->isError($this->_parse_tree)) {
return $this->_parse_tree;
}
}
/**
* It parses a condition. It assumes the following rule:
* Cond -> Expr [(">" | "<") Expr]
*
* @access private
* @return mixed The parsed ptg'd tree
*/
function _condition()
{
$result = $this->_expression();
if($this->isError($result)) {
return $result;
}
if ($this->_current_token == SPREADSHEET_EXCEL_WRITER_LT)
{
$this->_advance();
$result2 = $this->_expression();
if($this->isError($result2)) {
return $result2;
}
$result = $this->_createTree('ptgLT', $result, $result2);
}
elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_GT)
{
$this->_advance();
$result2 = $this->_expression();
if($this->isError($result2)) {
return $result2;
}
$result = $this->_createTree('ptgGT', $result, $result2);
}
elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_LE)
{
$this->_advance();
$result2 = $this->_expression();
if($this->isError($result2)) {
return $result2;
}
$result = $this->_createTree('ptgLE', $result, $result2);
}
elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_GE)
{
$this->_advance();
$result2 = $this->_expression();
if($this->isError($result2)) {
return $result2;
}
$result = $this->_createTree('ptgGE', $result, $result2);
}
elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_EQ)
{
$this->_advance();
$result2 = $this->_expression();
if($this->isError($result2)) {
return $result2;
}
$result = $this->_createTree('ptgEQ', $result, $result2);
}
elseif ($this->_current_token == SPREADSHEET_EXCEL_WRITER_NE)
{
$this->_advance();
$result2 = $this->_expression();
if($this->isError($result2)) {
return $result2;
}
$result = $this->_createTree('ptgNE', $result, $result2);
}
return $result;
}
/**
* It parses a expression. It assumes the following rule:
* Expr -> Term [("+" | "-") Term]
*
* @access private
* @return mixed The parsed ptg'd tree
*/
function _expression()
{
// If it's a string return a string node
if (ereg("^\"[^\"]{0,255}\"$", $this->_current_token))
{
$result = $this->_createTree($this->_current_token, '', '');
$this->_advance();
return($result);
}
$result = $this->_term();
if($this->isError($result)) {
return($result);
}
while (($this->_current_token == SPREADSHEET_EXCEL_WRITER_ADD) or
($this->_current_token == SPREADSHEET_EXCEL_WRITER_SUB))
{
if ($this->_current_token == SPREADSHEET_EXCEL_WRITER_ADD)
{
$this->_advance();
$result2 = $this->_term();
if($this->isError($result2)) {
return($result2);
}
$result = $this->_createTree('ptgAdd', $result, $result2);
}
else
{
$this->_advance();
$result2 = $this->_term();
if($this->isError($result2)) {
return($result2);
}
$result = $this->_createTree('ptgSub', $result, $result2);
}
}
return($result);
}
/**
* This function just introduces a ptgParen element in the tree, so that Excel
* doesn't get confused when working with a parenthesized formula afterwards.
*
* @access private
* @see _fact()
* @return mixed The parsed ptg'd tree
*/
function _parenthesizedExpression()
{
$result = $this->_createTree('ptgParen', $this->_expression(), '');
return($result);
}
/**
* It parses a term. It assumes the following rule:
* Term -> Fact [("*" | "/") Fact]
*
* @access private
* @return mixed The parsed ptg'd tree
*/
function _term()
{
$result = $this->_fact();
if($this->isError($result)) {
return($result);
}
while (($this->_current_token == SPREADSHEET_EXCEL_WRITER_MUL) or
($this->_current_token == SPREADSHEET_EXCEL_WRITER_DIV))
{
if ($this->_current_token == SPREADSHEET_EXCEL_WRITER_MUL)
{
$this->_advance();
$result2 = $this->_fact();
if($this->isError($result2)) {
return($result2);
}
$result = $this->_createTree('ptgMul', $result, $result2);
}
else
{
$this->_advance();
$result2 = $this->_fact();
if($this->isError($result2)) {
return($result2);
}
$result = $this->_createTree('ptgDiv', $result, $result2);
}
}
return($result);
}
/**
* It parses a factor. It assumes the following rule:
* Fact -> ( Expr )
* | CellRef
* | CellRange
* | Number
* | Function
*
* @access private
* @return mixed The parsed ptg'd tree
*/
function _fact()
{
if ($this->_current_token == SPREADSHEET_EXCEL_WRITER_OPEN)
{
$this->_advance(); // eat the "("
$result = $this->_parenthesizedExpression();
if ($this->_current_token != SPREADSHEET_EXCEL_WRITER_CLOSE) {
return($this->raiseError("')' token expected."));
}
$this->_advance(); // eat the ")"
return $result;
}
// if it's a reference
if (preg_match('/^\$?[A-I]?[A-Z]\$?[0-9]+$/',$this->_current_token))
{
$result = $this->_createTree($this->_current_token, '', '');
$this->_advance();
return $result;
}
// If it's an external reference (Sheet1!A1 or Sheet1:Sheet2!A1)
elseif (preg_match("/^[A-Za-z0-9_]+(\:[A-Za-z0-9_]+)?\![A-I]?[A-Z][0-9]+$/",$this->_current_token))
{
$result = $this->_createTree($this->_current_token, '', '');
$this->_advance();
return $result;
}
// if it's a range
elseif (preg_match("/^(\$)?[A-I]?[A-Z](\$)?[0-9]+:(\$)?[A-I]?[A-Z](\$)?[0-9]+$/",$this->_current_token) or
preg_match("/^(\$)?[A-I]?[A-Z](\$)?[0-9]+\.\.(\$)?[A-I]?[A-Z](\$)?[0-9]+$/",$this->_current_token))
{
$result = $this->_current_token;
$this->_advance();
return $result;
}
// If it's an external range (Sheet1!A1:B2)
elseif (preg_match("/^[A-Za-z0-9_]+(\:[A-Za-z0-9_]+)?\!([A-I]?[A-Z])?[0-9]+:([A-I]?[A-Z])?[0-9]+$/",$this->_current_token))
{
$result = $this->_current_token;
$this->_advance();
return($result);
}
elseif (is_numeric($this->_current_token))
{
$result = $this->_createTree($this->_current_token, '', '');
$this->_advance();
return($result);
}
// if it's a function call
elseif (eregi("^[A-Z0-9\xc0-\xdc\.]+$",$this->_current_token))
{
$result = $this->_func();
return($result);
}
return($this->raiseError("Sintactic error: ".$this->_current_token.", lookahead: ".
$this->_lookahead.", current char: ".$this->_current_char));
}
/**
* It parses a function call. It assumes the following rule:
* Func -> ( Expr [,Expr]* )
*
* @access private
*/
function _func()
{
$num_args = 0; // number of arguments received
$function = $this->_current_token;
$this->_advance();
$this->_advance(); // eat the "("
while($this->_current_token != ')')
{
if($num_args > 0)
{
if($this->_current_token == SPREADSHEET_EXCEL_WRITER_COMA) {
$this->_advance(); // eat the ","
}
else {
return new PEAR_Error("Sintactic error: coma expected in ".
"function $function, {$num_args}º arg");
}
$result2 = $this->_condition();
if($this->isError($result2)) {
return($result2);
}
$result = $this->_createTree('arg', $result, $result2);
}
else // first argument
{
$result2 = $this->_condition();
if($this->isError($result2)) {
return($result2);
}
$result = $this->_createTree('arg', '', $result2);
}
$num_args++;
}
$args = $this->_functions[$function][1];
// If fixed number of args eg. TIME($i,$j,$k). Check that the number of args is valid.
if (($args >= 0) and ($args != $num_args))
{
return($this->raiseError("Incorrect number of arguments in function $function() "));
}
$result = $this->_createTree($function, $result, '');
$this->_advance(); // eat the ")"
return($result);
}
/**
* Creates a tree. In fact an array which may have one or two arrays (sub-trees)
* as elements.
*
* @access private
* @param mixed $value The value of this node.
* @param mixed $left The left array (sub-tree) or a final node.
* @param mixed $right The right array (sub-tree) or a final node.
*/
function _createTree($value, $left, $right)
{
return(array('value' => $value, 'left' => $left, 'right' => $right));
}
/**
* Builds a string containing the tree in reverse polish notation (What you
* would use in a HP calculator stack).
* The following tree:
*
* +
* / \
* 2 3
*
* produces: "23+"
*
* The following tree:
*
* +
* / \
* 3 *
* / \
* 6 A1
*
* produces: "36A1*+"
*
* In fact all operands, functions, references, etc... are written as ptg's
*
* @access public
* @param array $tree The optional tree to convert.
* @return string The tree in reverse polish notation
*/
function toReversePolish($tree = array())
{
$polish = ""; // the string we are going to return
if (empty($tree)) // If it's the first call use _parse_tree
{
$tree = $this->_parse_tree;
}
if (is_array($tree['left']))
{
$converted_tree = $this->toReversePolish($tree['left']);
if($this->isError($converted_tree)) {
return($converted_tree);
}
$polish .= $converted_tree;
}
elseif($tree['left'] != '') // It's a final node
{
$converted_tree = $this->_convert($tree['left']);
if($this->isError($converted_tree)) {
return($converted_tree);
}
$polish .= $converted_tree;
}
if (is_array($tree['right']))
{
$converted_tree = $this->toReversePolish($tree['right']);
if($this->isError($converted_tree)) {
return($converted_tree);
}
$polish .= $converted_tree;
}
elseif($tree['right'] != '') // It's a final node
{
$converted_tree = $this->_convert($tree['right']);
if($this->isError($converted_tree)) {
return($converted_tree);
}
$polish .= $converted_tree;
}
$converted_tree = $this->_convert($tree['value']);
if($this->isError($converted_tree)) {
return($converted_tree);
}
$polish .= $converted_tree;
return($polish);
}
}
?>
/trunk/applications/jrest/lib/Spreadsheet/Excel/Writer/Workbook.php
New file
0,0 → 1,1042
<?php
/*
* Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
*
* The majority of this is _NOT_ my code. I simply ported it from the
* PERL Spreadsheet::WriteExcel module.
*
* The author of the Spreadsheet::WriteExcel module is John McNamara
* <jmcnamara@cpan.org>
*
* I _DO_ maintain this code, and John McNamara has nothing to do with the
* porting of this code to PHP. Any questions directly related to this
* class library should be directed to me.
*
* License Information:
*
* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets
* Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
 
require_once('Format.php');
require_once('OLEwriter.php');
require_once('BIFFwriter.php');
require_once('Worksheet.php');
require_once('Parser.php');
 
/**
* Class for generating Excel Spreadsheets
*
* @author Xavier Noguer <xnoguer@rezebra.com>
* @category FileFormats
* @package Spreadsheet_Excel_Writer
*/
 
class Spreadsheet_Excel_Writer_Workbook extends Spreadsheet_Excel_Writer_BIFFwriter
{
/**
* Filename for the Workbook
* @var string
*/
var $_filename;
 
/**
* Formula parser
* @var object Parser
*/
var $_parser;
 
/**
* Flag for 1904 date system
* @var integer
*/
var $_1904;
 
/**
* The active worksheet of the workbook (0 indexed)
* @var integer
*/
var $_activesheet;
 
/**
* 1st displayed worksheet in the workbook (0 indexed)
* @var integer
*/
var $_firstsheet;
 
/**
* Number of workbook tabs selected
* @var integer
*/
var $_selected;
 
/**
* Index for creating adding new formats to the workbook
* @var integer
*/
var $_xf_index;
 
/**
* Flag for preventing close from being called twice.
* @var integer
* @see close()
*/
var $_fileclosed;
 
/**
* The BIFF file size for the workbook.
* @var integer
* @see _calcSheetOffsets()
*/
var $_biffsize;
 
/**
* The default sheetname for all sheets created.
* @var string
*/
var $_sheetname;
 
/**
* The default XF format.
* @var object Format
*/
var $_tmp_format;
 
/**
* Array containing references to all of this workbook's worksheets
* @var array
*/
var $_worksheets;
 
/**
* Array of sheetnames for creating the EXTERNSHEET records
* @var array
*/
var $_sheetnames;
 
/**
* Array containing references to all of this workbook's formats
* @var array
*/
var $_formats;
 
/**
* Array containing the colour palette
* @var array
*/
var $_palette;
 
/**
* The default format for URLs.
* @var object Format
*/
var $_url_format;
 
/**
* Class constructor
*
* @param string filename for storing the workbook. "-" for writing to stdout.
* @access public
*/
function Spreadsheet_Excel_Writer_Workbook($filename)
{
// It needs to call its parent's constructor explicitly
$this->Spreadsheet_Excel_Writer_BIFFwriter();
$this->_filename = $filename;
$this->_parser =& new Spreadsheet_Excel_Writer_Parser($this->_byte_order);
$this->_1904 = 0;
$this->_activesheet = 0;
$this->_firstsheet = 0;
$this->_selected = 0;
$this->_xf_index = 16; // 15 style XF's and 1 cell XF.
$this->_fileclosed = 0;
$this->_biffsize = 0;
$this->_sheetname = "Sheet";
$this->_tmp_format =& new Spreadsheet_Excel_Writer_Format();
$this->_worksheets = array();
$this->_sheetnames = array();
$this->_formats = array();
$this->_palette = array();
// Add the default format for hyperlinks
$this->_url_format =& $this->addFormat(array('color' => 'blue', 'underline' => 1));
$this->_setPaletteXl97();
}
/**
* Calls finalization methods.
* This method should always be the last one to be called on every workbook
*
* @access public
*/
function close()
{
if ($this->_fileclosed) { // Prevent close() from being called twice.
return;
}
$this->_storeWorkbook();
$this->_fileclosed = 1;
}
/**
* An accessor for the _worksheets[] array
* Returns an array of the worksheet objects in a workbook
* It actually calls to worksheets()
*
* @access public
* @see worksheets()
* @return array
*/
function sheets()
{
return $this->worksheets();
}
/**
* An accessor for the _worksheets[] array.
* Returns an array of the worksheet objects in a workbook
*
* @access public
* @return array
*/
function worksheets()
{
return($this->_worksheets);
}
/**
* Add a new worksheet to the Excel workbook.
* If no name is given the name of the worksheet will be Sheeti$i, with
* $i in [1..].
*
* @access public
* @param string $name the optional name of the worksheet
* @return &Spreadsheet_Excel_Writer_Worksheet reference to a worksheet object
*/
function &addWorksheet($name = '')
{
$index = count($this->_worksheets);
$sheetname = $this->_sheetname;
if($name == '') {
$name = $sheetname.($index+1);
}
// Check that sheetname is <= 31 chars (Excel limit).
if(strlen($name) > 31) {
$this->raiseError("Sheetname $name must be <= 31 chars");
}
// Check that the worksheet name doesn't already exist: a fatal Excel error.
for($i=0; $i < count($this->_worksheets); $i++)
{
if($name == $this->_worksheets[$i]->getName()) {
$this->raiseError("Worksheet '$name' already exists");
}
}
$worksheet = new Spreadsheet_Excel_Writer_Worksheet($name,$index,$this->_activesheet,
$this->_firstsheet,$this->_url_format,
$this->_parser);
 
$this->_worksheets[$index] = &$worksheet; // Store ref for iterator
$this->_sheetnames[$index] = $name; // Store EXTERNSHEET names
$this->_parser->setExtSheet($name, $index); // Register worksheet name with parser
return($worksheet);
}
/**
* Add a new format to the Excel workbook.
* Also, pass any properties to the Format constructor.
*
* @access public
* @param array $properties array with properties for initializing the format.
* @return &Spreadsheet_Excel_Writer_Format reference to an Excel Format
*/
function &addFormat($properties = array())
{
$format = new Spreadsheet_Excel_Writer_Format($this->_xf_index,$properties);
$this->_xf_index += 1;
$this->_formats[] = &$format;
return($format);
}
/**
* Change the RGB components of the elements in the colour palette.
*
* @access public
* @param integer $index colour index
* @param integer $red red RGB value [0-255]
* @param integer $green green RGB value [0-255]
* @param integer $blue blue RGB value [0-255]
* @return integer The palette index for the custom color
*/
function setCustomColor($index,$red,$green,$blue)
{
// Match a HTML #xxyyzz style parameter
/*if (defined $_[1] and $_[1] =~ /^#(\w\w)(\w\w)(\w\w)/ ) {
@_ = ($_[0], hex $1, hex $2, hex $3);
}*/
// Check that the colour index is the right range
if ($index < 8 or $index > 64) {
// TODO: assign real error codes
$this->raiseError("Color index $index outside range: 8 <= index <= 64",0,PEAR_ERROR_DIE);
}
// Check that the colour components are in the right range
if ( ($red < 0 or $red > 255) or
($green < 0 or $green > 255) or
($blue < 0 or $blue > 255) )
{
$this->raiseError("Color component outside range: 0 <= color <= 255");
}
$index -= 8; // Adjust colour index (wingless dragonfly)
// Set the RGB value
$this->_palette[$index] = array($red, $green, $blue, 0);
return($index + 8);
}
/**
* Sets the colour palette to the Excel 97+ default.
*
* @access private
*/
function _setPaletteXl97()
{
$this->_palette = array(
array(0x00, 0x00, 0x00, 0x00), // 8
array(0xff, 0xff, 0xff, 0x00), // 9
array(0xff, 0x00, 0x00, 0x00), // 10
array(0x00, 0xff, 0x00, 0x00), // 11
array(0x00, 0x00, 0xff, 0x00), // 12
array(0xff, 0xff, 0x00, 0x00), // 13
array(0xff, 0x00, 0xff, 0x00), // 14
array(0x00, 0xff, 0xff, 0x00), // 15
array(0x80, 0x00, 0x00, 0x00), // 16
array(0x00, 0x80, 0x00, 0x00), // 17
array(0x00, 0x00, 0x80, 0x00), // 18
array(0x80, 0x80, 0x00, 0x00), // 19
array(0x80, 0x00, 0x80, 0x00), // 20
array(0x00, 0x80, 0x80, 0x00), // 21
array(0xc0, 0xc0, 0xc0, 0x00), // 22
array(0x80, 0x80, 0x80, 0x00), // 23
array(0x99, 0x99, 0xff, 0x00), // 24
array(0x99, 0x33, 0x66, 0x00), // 25
array(0xff, 0xff, 0xcc, 0x00), // 26
array(0xcc, 0xff, 0xff, 0x00), // 27
array(0x66, 0x00, 0x66, 0x00), // 28
array(0xff, 0x80, 0x80, 0x00), // 29
array(0x00, 0x66, 0xcc, 0x00), // 30
array(0xcc, 0xcc, 0xff, 0x00), // 31
array(0x00, 0x00, 0x80, 0x00), // 32
array(0xff, 0x00, 0xff, 0x00), // 33
array(0xff, 0xff, 0x00, 0x00), // 34
array(0x00, 0xff, 0xff, 0x00), // 35
array(0x80, 0x00, 0x80, 0x00), // 36
array(0x80, 0x00, 0x00, 0x00), // 37
array(0x00, 0x80, 0x80, 0x00), // 38
array(0x00, 0x00, 0xff, 0x00), // 39
array(0x00, 0xcc, 0xff, 0x00), // 40
array(0xcc, 0xff, 0xff, 0x00), // 41
array(0xcc, 0xff, 0xcc, 0x00), // 42
array(0xff, 0xff, 0x99, 0x00), // 43
array(0x99, 0xcc, 0xff, 0x00), // 44
array(0xff, 0x99, 0xcc, 0x00), // 45
array(0xcc, 0x99, 0xff, 0x00), // 46
array(0xff, 0xcc, 0x99, 0x00), // 47
array(0x33, 0x66, 0xff, 0x00), // 48
array(0x33, 0xcc, 0xcc, 0x00), // 49
array(0x99, 0xcc, 0x00, 0x00), // 50
array(0xff, 0xcc, 0x00, 0x00), // 51
array(0xff, 0x99, 0x00, 0x00), // 52
array(0xff, 0x66, 0x00, 0x00), // 53
array(0x66, 0x66, 0x99, 0x00), // 54
array(0x96, 0x96, 0x96, 0x00), // 55
array(0x00, 0x33, 0x66, 0x00), // 56
array(0x33, 0x99, 0x66, 0x00), // 57
array(0x00, 0x33, 0x00, 0x00), // 58
array(0x33, 0x33, 0x00, 0x00), // 59
array(0x99, 0x33, 0x00, 0x00), // 60
array(0x99, 0x33, 0x66, 0x00), // 61
array(0x33, 0x33, 0x99, 0x00), // 62
array(0x33, 0x33, 0x33, 0x00), // 63
);
}
/**
* Assemble worksheets into a workbook and send the BIFF data to an OLE
* storage.
*
* @access private
*/
function _storeWorkbook()
{
// Ensure that at least one worksheet has been selected.
if ($this->_activesheet == 0)
{
$this->_worksheets[0]->selected = 1;
}
// Calculate the number of selected worksheet tabs and call the finalization
// methods for each worksheet
for($i=0; $i < count($this->_worksheets); $i++)
{
if($this->_worksheets[$i]->selected) {
$this->_selected++;
}
$this->_worksheets[$i]->close($this->_sheetnames);
}
// Add Workbook globals
$this->_storeBof(0x0005);
$this->_storeExterns(); // For print area and repeat rows
$this->_storeNames(); // For print area and repeat rows
$this->_storeWindow1();
$this->_store1904();
$this->_storeAllFonts();
$this->_storeAllNumFormats();
$this->_storeAllXfs();
$this->_storeAllStyles();
$this->_storePalette();
$this->_calcSheetOffsets();
// Add BOUNDSHEET records
for($i=0; $i < count($this->_worksheets); $i++) {
$this->_storeBoundsheet($this->_worksheets[$i]->name,$this->_worksheets[$i]->offset);
}
// End Workbook globals
$this->_storeEof();
// Store the workbook in an OLE container
$this->_storeOLEFile();
}
/**
* Store the workbook in an OLE container if the total size of the workbook data
* is less than ~ 7MB.
*
* @access private
*/
function _storeOLEFile()
{
$OLE = new Spreadsheet_Excel_Writer_OLEwriter($this->_filename);
$this->_tmp_filename = $OLE->_tmp_filename;
// Write Worksheet data if data <~ 7MB
if ($OLE->setSize($this->_biffsize))
{
$OLE->writeHeader();
$OLE->write($this->_data);
foreach($this->_worksheets as $sheet)
{
while ($tmp = $sheet->getData()) {
$OLE->write($tmp);
}
}
}
$OLE->close();
}
/**
* Calculate offsets for Worksheet BOF records.
*
* @access private
*/
function _calcSheetOffsets()
{
$BOF = 11;
$EOF = 4;
$offset = $this->_datasize;
for($i=0; $i < count($this->_worksheets); $i++) {
$offset += $BOF + strlen($this->_worksheets[$i]->name);
}
$offset += $EOF;
for($i=0; $i < count($this->_worksheets); $i++) {
$this->_worksheets[$i]->offset = $offset;
$offset += $this->_worksheets[$i]->_datasize;
}
$this->_biffsize = $offset;
}
/**
* Store the Excel FONT records.
*
* @access private
*/
function _storeAllFonts()
{
// tmp_format is added by the constructor. We use this to write the default XF's
$format = $this->_tmp_format;
$font = $format->getFont();
// Note: Fonts are 0-indexed. According to the SDK there is no index 4,
// so the following fonts are 0, 1, 2, 3, 5
//
for($i=1; $i <= 5; $i++){
$this->_append($font);
}
// Iterate through the XF objects and write a FONT record if it isn't the
// same as the default FONT and if it hasn't already been used.
//
$fonts = array();
$index = 6; // The first user defined FONT
$key = $format->getFontKey(); // The default font from _tmp_format
$fonts[$key] = 0; // Index of the default font
for($i=0; $i < count($this->_formats); $i++) {
$key = $this->_formats[$i]->getFontKey();
if (isset($fonts[$key])) {
// FONT has already been used
$this->_formats[$i]->font_index = $fonts[$key];
}
else {
// Add a new FONT record
$fonts[$key] = $index;
$this->_formats[$i]->font_index = $index;
$index++;
$font = $this->_formats[$i]->getFont();
$this->_append($font);
}
}
}
/**
* Store user defined numerical formats i.e. FORMAT records
*
* @access private
*/
function _storeAllNumFormats()
{
// Leaning num_format syndrome
$hash_num_formats = array();
$num_formats = array();
$index = 164;
// Iterate through the XF objects and write a FORMAT record if it isn't a
// built-in format type and if the FORMAT string hasn't already been used.
//
for($i=0; $i < count($this->_formats); $i++)
{
$num_format = $this->_formats[$i]->_num_format;
// Check if $num_format is an index to a built-in format.
// Also check for a string of zeros, which is a valid format string
// but would evaluate to zero.
//
if (!preg_match("/^0+\d/",$num_format))
{
if (preg_match("/^\d+$/",$num_format)) { // built-in format
continue;
}
}
if (isset($hash_num_formats[$num_format])) {
// FORMAT has already been used
$this->_formats[$i]->_num_format = $hash_num_formats[$num_format];
}
else{
// Add a new FORMAT
$hash_num_formats[$num_format] = $index;
$this->_formats[$i]->_num_format = $index;
array_push($num_formats,$num_format);
$index++;
}
}
// Write the new FORMAT records starting from 0xA4
$index = 164;
foreach ($num_formats as $num_format) {
$this->_storeNumFormat($num_format,$index);
$index++;
}
}
/**
* Write all XF records.
*
* @access private
*/
function _storeAllXfs()
{
// _tmp_format is added by the constructor. We use this to write the default XF's
// The default font index is 0
//
$format = $this->_tmp_format;
for ($i=0; $i <= 14; $i++) {
$xf = $format->getXf('style'); // Style XF
$this->_append($xf);
}
$xf = $format->getXf('cell'); // Cell XF
$this->_append($xf);
// User defined XFs
for($i=0; $i < count($this->_formats); $i++) {
$xf = $this->_formats[$i]->getXf('cell');
$this->_append($xf);
}
}
/**
* Write all STYLE records.
*
* @access private
*/
function _storeAllStyles()
{
$this->_storeStyle();
}
/**
* Write the EXTERNCOUNT and EXTERNSHEET records. These are used as indexes for
* the NAME records.
*
* @access private
*/
function _storeExterns()
{
// Create EXTERNCOUNT with number of worksheets
$this->_storeExterncount(count($this->_worksheets));
// Create EXTERNSHEET for each worksheet
foreach ($this->_sheetnames as $sheetname) {
$this->_storeExternsheet($sheetname);
}
}
/**
* Write the NAME record to define the print area and the repeat rows and cols.
*
* @access private
*/
function _storeNames()
{
// Create the print area NAME records
foreach ($this->_worksheets as $worksheet) {
// Write a Name record if the print area has been defined
if (isset($worksheet->print_rowmin))
{
$this->_storeNameShort(
$worksheet->index,
0x06, // NAME type
$worksheet->print_rowmin,
$worksheet->print_rowmax,
$worksheet->print_colmin,
$worksheet->print_colmax
);
}
}
// Create the print title NAME records
foreach ($this->_worksheets as $worksheet)
{
$rowmin = $worksheet->title_rowmin;
$rowmax = $worksheet->title_rowmax;
$colmin = $worksheet->title_colmin;
$colmax = $worksheet->title_colmax;
// Determine if row + col, row, col or nothing has been defined
// and write the appropriate record
//
if (isset($rowmin) and isset($colmin)) {
// Row and column titles have been defined.
// Row title has been defined.
$this->_storeNameLong(
$worksheet->index,
0x07, // NAME type
$rowmin,
$rowmax,
$colmin,
$colmax
);
}
elseif (isset($rowmin)) {
// Row title has been defined.
$this->_storeNameShort(
$worksheet->index,
0x07, // NAME type
$rowmin,
$rowmax,
0x00,
0xff
);
}
elseif (isset($colmin)) {
// Column title has been defined.
$this->_storeNameShort(
$worksheet->index,
0x07, // NAME type
0x0000,
0x3fff,
$colmin,
$colmax
);
}
else {
// Print title hasn't been defined.
}
}
}
/******************************************************************************
*
* BIFF RECORDS
*
*/
/**
* Write Excel BIFF WINDOW1 record.
*
* @access private
*/
function _storeWindow1()
{
$record = 0x003D; // Record identifier
$length = 0x0012; // Number of bytes to follow
$xWn = 0x0000; // Horizontal position of window
$yWn = 0x0000; // Vertical position of window
$dxWn = 0x25BC; // Width of window
$dyWn = 0x1572; // Height of window
$grbit = 0x0038; // Option flags
$ctabsel = $this->_selected; // Number of workbook tabs selected
$wTabRatio = 0x0258; // Tab to scrollbar ratio
$itabFirst = $this->_firstsheet; // 1st displayed worksheet
$itabCur = $this->_activesheet; // Active worksheet
$header = pack("vv", $record, $length);
$data = pack("vvvvvvvvv", $xWn, $yWn, $dxWn, $dyWn,
$grbit,
$itabCur, $itabFirst,
$ctabsel, $wTabRatio);
$this->_append($header.$data);
}
/**
* Writes Excel BIFF BOUNDSHEET record.
*
* @param string $sheetname Worksheet name
* @param integer $offset Location of worksheet BOF
* @access private
*/
function _storeBoundsheet($sheetname,$offset)
{
$record = 0x0085; // Record identifier
$length = 0x07 + strlen($sheetname); // Number of bytes to follow
$grbit = 0x0000; // Sheet identifier
$cch = strlen($sheetname); // Length of sheet name
$header = pack("vv", $record, $length);
$data = pack("VvC", $offset, $grbit, $cch);
$this->_append($header.$data.$sheetname);
}
/**
* Write Excel BIFF STYLE records.
*
* @access private
*/
function _storeStyle()
{
$record = 0x0293; // Record identifier
$length = 0x0004; // Bytes to follow
$ixfe = 0x8000; // Index to style XF
$BuiltIn = 0x00; // Built-in style
$iLevel = 0xff; // Outline style level
$header = pack("vv", $record, $length);
$data = pack("vCC", $ixfe, $BuiltIn, $iLevel);
$this->_append($header.$data);
}
/**
* Writes Excel FORMAT record for non "built-in" numerical formats.
*
* @param string $format Custom format string
* @param integer $ifmt Format index code
* @access private
*/
function _storeNumFormat($format,$ifmt)
{
$record = 0x041E; // Record identifier
$length = 0x03 + strlen($format); // Number of bytes to follow
$cch = strlen($format); // Length of format string
$header = pack("vv", $record, $length);
$data = pack("vC", $ifmt, $cch);
$this->_append($header.$data.$format);
}
/**
* Write Excel 1904 record to indicate the date system in use.
*
* @access private
*/
function _store1904()
{
$record = 0x0022; // Record identifier
$length = 0x0002; // Bytes to follow
$f1904 = $this->_1904; // Flag for 1904 date system
$header = pack("vv", $record, $length);
$data = pack("v", $f1904);
$this->_append($header.$data);
}
/**
* Write BIFF record EXTERNCOUNT to indicate the number of external sheet
* references in the workbook.
*
* Excel only stores references to external sheets that are used in NAME.
* The workbook NAME record is required to define the print area and the repeat
* rows and columns.
*
* A similar method is used in Worksheet.php for a slightly different purpose.
*
* @param integer $cxals Number of external references
* @access private
*/
function _storeExterncount($cxals)
{
$record = 0x0016; // Record identifier
$length = 0x0002; // Number of bytes to follow
$header = pack("vv", $record, $length);
$data = pack("v", $cxals);
$this->_append($header.$data);
}
/**
* Writes the Excel BIFF EXTERNSHEET record. These references are used by
* formulas. NAME record is required to define the print area and the repeat
* rows and columns.
*
* A similar method is used in Worksheet.php for a slightly different purpose.
*
* @param string $sheetname Worksheet name
* @access private
*/
function _storeExternsheet($sheetname)
{
$record = 0x0017; // Record identifier
$length = 0x02 + strlen($sheetname); // Number of bytes to follow
$cch = strlen($sheetname); // Length of sheet name
$rgch = 0x03; // Filename encoding
$header = pack("vv", $record, $length);
$data = pack("CC", $cch, $rgch);
$this->_append($header.$data.$sheetname);
}
/**
* Store the NAME record in the short format that is used for storing the print
* area, repeat rows only and repeat columns only.
*
* @param integer $index Sheet index
* @param integer $type Built-in name type
* @param integer $rowmin Start row
* @param integer $rowmax End row
* @param integer $colmin Start colum
* @param integer $colmax End column
* @access private
*/
function _storeNameShort($index,$type,$rowmin,$rowmax,$colmin,$colmax)
{
$record = 0x0018; // Record identifier
$length = 0x0024; // Number of bytes to follow
$grbit = 0x0020; // Option flags
$chKey = 0x00; // Keyboard shortcut
$cch = 0x01; // Length of text name
$cce = 0x0015; // Length of text definition
$ixals = $index + 1; // Sheet index
$itab = $ixals; // Equal to ixals
$cchCustMenu = 0x00; // Length of cust menu text
$cchDescription = 0x00; // Length of description text
$cchHelptopic = 0x00; // Length of help topic text
$cchStatustext = 0x00; // Length of status bar text
$rgch = $type; // Built-in name type
$unknown03 = 0x3b;
$unknown04 = 0xffff-$index;
$unknown05 = 0x0000;
$unknown06 = 0x0000;
$unknown07 = 0x1087;
$unknown08 = 0x8005;
$header = pack("vv", $record, $length);
$data = pack("v", $grbit);
$data .= pack("C", $chKey);
$data .= pack("C", $cch);
$data .= pack("v", $cce);
$data .= pack("v", $ixals);
$data .= pack("v", $itab);
$data .= pack("C", $cchCustMenu);
$data .= pack("C", $cchDescription);
$data .= pack("C", $cchHelptopic);
$data .= pack("C", $cchStatustext);
$data .= pack("C", $rgch);
$data .= pack("C", $unknown03);
$data .= pack("v", $unknown04);
$data .= pack("v", $unknown05);
$data .= pack("v", $unknown06);
$data .= pack("v", $unknown07);
$data .= pack("v", $unknown08);
$data .= pack("v", $index);
$data .= pack("v", $index);
$data .= pack("v", $rowmin);
$data .= pack("v", $rowmax);
$data .= pack("C", $colmin);
$data .= pack("C", $colmax);
$this->_append($header.$data);
}
/**
* Store the NAME record in the long format that is used for storing the repeat
* rows and columns when both are specified. This shares a lot of code with
* _storeNameShort() but we use a separate method to keep the code clean.
* Code abstraction for reuse can be carried too far, and I should know. ;-)
*
* @param integer $index Sheet index
* @param integer $type Built-in name type
* @param integer $rowmin Start row
* @param integer $rowmax End row
* @param integer $colmin Start colum
* @param integer $colmax End column
* @access private
*/
function _storeNameLong($index,$type,$rowmin,$rowmax,$colmin,$colmax)
{
$record = 0x0018; // Record identifier
$length = 0x003d; // Number of bytes to follow
$grbit = 0x0020; // Option flags
$chKey = 0x00; // Keyboard shortcut
$cch = 0x01; // Length of text name
$cce = 0x002e; // Length of text definition
$ixals = $index + 1; // Sheet index
$itab = $ixals; // Equal to ixals
$cchCustMenu = 0x00; // Length of cust menu text
$cchDescription = 0x00; // Length of description text
$cchHelptopic = 0x00; // Length of help topic text
$cchStatustext = 0x00; // Length of status bar text
$rgch = $type; // Built-in name type
$unknown01 = 0x29;
$unknown02 = 0x002b;
$unknown03 = 0x3b;
$unknown04 = 0xffff-$index;
$unknown05 = 0x0000;
$unknown06 = 0x0000;
$unknown07 = 0x1087;
$unknown08 = 0x8008;
$header = pack("vv", $record, $length);
$data = pack("v", $grbit);
$data .= pack("C", $chKey);
$data .= pack("C", $cch);
$data .= pack("v", $cce);
$data .= pack("v", $ixals);
$data .= pack("v", $itab);
$data .= pack("C", $cchCustMenu);
$data .= pack("C", $cchDescription);
$data .= pack("C", $cchHelptopic);
$data .= pack("C", $cchStatustext);
$data .= pack("C", $rgch);
$data .= pack("C", $unknown01);
$data .= pack("v", $unknown02);
// Column definition
$data .= pack("C", $unknown03);
$data .= pack("v", $unknown04);
$data .= pack("v", $unknown05);
$data .= pack("v", $unknown06);
$data .= pack("v", $unknown07);
$data .= pack("v", $unknown08);
$data .= pack("v", $index);
$data .= pack("v", $index);
$data .= pack("v", 0x0000);
$data .= pack("v", 0x3fff);
$data .= pack("C", $colmin);
$data .= pack("C", $colmax);
// Row definition
$data .= pack("C", $unknown03);
$data .= pack("v", $unknown04);
$data .= pack("v", $unknown05);
$data .= pack("v", $unknown06);
$data .= pack("v", $unknown07);
$data .= pack("v", $unknown08);
$data .= pack("v", $index);
$data .= pack("v", $index);
$data .= pack("v", $rowmin);
$data .= pack("v", $rowmax);
$data .= pack("C", 0x00);
$data .= pack("C", 0xff);
// End of data
$data .= pack("C", 0x10);
$this->_append($header.$data);
}
/**
* Stores the PALETTE biff record.
*
* @access private
*/
function _storePalette()
{
$aref = $this->_palette;
$record = 0x0092; // Record identifier
$length = 2 + 4 * count($aref); // Number of bytes to follow
$ccv = count($aref); // Number of RGB values to follow
$data = ''; // The RGB data
// Pack the RGB data
foreach($aref as $color)
{
foreach($color as $byte) {
$data .= pack("C",$byte);
}
}
$header = pack("vvv", $record, $length, $ccv);
$this->_append($header.$data);
}
}
?>
/trunk/applications/jrest/lib/Spreadsheet/Excel/Writer.php
New file
0,0 → 1,75
<?php
/*
* Module written/ported by Xavier Noguer <xnoguer@rezebra.com>
*
* PERL Spreadsheet::WriteExcel module.
*
* The author of the Spreadsheet::WriteExcel module is John McNamara
* <jmcnamara@cpan.org>
*
* I _DO_ maintain this code, and John McNamara has nothing to do with the
* porting of this code to PHP. Any questions directly related to this
* class library should be directed to me.
*
* License Information:
*
* Spreadsheet_Excel_Writer: A library for generating Excel Spreadsheets
* Copyright (c) 2002-2003 Xavier Noguer xnoguer@rezebra.com
*
* This library 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 library 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 library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
 
require_once('PEAR.php');
require_once('Writer/Workbook.php');
 
/**
* Class for writing Excel Spreadsheets. This class should change COMPLETELY.
*
* @author Xavier Noguer <xnoguer@rezebra.com>
* @category FileFormats
* @package Spreadsheet_Excel_Writer
*/
 
class Spreadsheet_Excel_Writer extends Spreadsheet_Excel_Writer_Workbook
{
/**
* The constructor. It just creates a Workbook
*
* @param string $filename The optional filename for the Workbook.
* @return Spreadsheet_Excel_Writer_Workbook The Workbook created
*/
function Spreadsheet_Excel_Writer($filename = '')
{
$this->_filename = $filename;
$this->Spreadsheet_Excel_Writer_Workbook($filename);
}
 
/**
* Send HTTP headers for the Excel file.
*
* @param string $filename The filename to use for HTTP headers
* @access public
*/
function send($filename)
{
header("Content-type: application/vnd.ms-excel");
header("Content-Disposition: attachment; filename=$filename");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0,pre-check=0");
header("Pragma: public");
}
 
}
?>
/trunk/applications/jrest/lib/DB.php
New file
0,0 → 1,1114
<?php
/* vim: set expandtab tabstop=4 shiftwidth=4 foldmethod=marker: */
// +----------------------------------------------------------------------+
// | PHP Version 4 |
// +----------------------------------------------------------------------+
// | Copyright (c) 1997-2004 The PHP Group |
// +----------------------------------------------------------------------+
// | This source file is subject to version 2.02 of the PHP license, |
// | that is bundled with this package in the file LICENSE, and is |
// | available at through the world-wide-web at |
// | http://www.php.net/license/2_02.txt. |
// | If you did not receive a copy of the PHP license and are unable to |
// | obtain it through the world-wide-web, please send a note to |
// | license@php.net so we can mail you a copy immediately. |
// +----------------------------------------------------------------------+
// | Authors: Stig Bakken <ssb@php.net> |
// | Tomas V.V.Cox <cox@idecnet.com> |
// | Maintainer: Daniel Convissor <danielc@php.net> |
// +----------------------------------------------------------------------+
//
// $Id$
//
// Database independent query interface.
 
 
require_once 'PEAR.php';
 
// {{{ constants
// {{{ error codes
 
/*
* The method mapErrorCode in each DB_dbtype implementation maps
* native error codes to one of these.
*
* If you add an error code here, make sure you also add a textual
* version of it in DB::errorMessage().
*/
define('DB_OK', 1);
define('DB_ERROR', -1);
define('DB_ERROR_SYNTAX', -2);
define('DB_ERROR_CONSTRAINT', -3);
define('DB_ERROR_NOT_FOUND', -4);
define('DB_ERROR_ALREADY_EXISTS', -5);
define('DB_ERROR_UNSUPPORTED', -6);
define('DB_ERROR_MISMATCH', -7);
define('DB_ERROR_INVALID', -8);
define('DB_ERROR_NOT_CAPABLE', -9);
define('DB_ERROR_TRUNCATED', -10);
define('DB_ERROR_INVALID_NUMBER', -11);
define('DB_ERROR_INVALID_DATE', -12);
define('DB_ERROR_DIVZERO', -13);
define('DB_ERROR_NODBSELECTED', -14);
define('DB_ERROR_CANNOT_CREATE', -15);
define('DB_ERROR_CANNOT_DELETE', -16);
define('DB_ERROR_CANNOT_DROP', -17);
define('DB_ERROR_NOSUCHTABLE', -18);
define('DB_ERROR_NOSUCHFIELD', -19);
define('DB_ERROR_NEED_MORE_DATA', -20);
define('DB_ERROR_NOT_LOCKED', -21);
define('DB_ERROR_VALUE_COUNT_ON_ROW', -22);
define('DB_ERROR_INVALID_DSN', -23);
define('DB_ERROR_CONNECT_FAILED', -24);
define('DB_ERROR_EXTENSION_NOT_FOUND',-25);
define('DB_ERROR_ACCESS_VIOLATION', -26);
define('DB_ERROR_NOSUCHDB', -27);
define('DB_ERROR_CONSTRAINT_NOT_NULL',-29);
 
 
// }}}
// {{{ prepared statement-related
 
 
/*
* These constants are used when storing information about prepared
* statements (using the "prepare" method in DB_dbtype).
*
* The prepare/execute model in DB is mostly borrowed from the ODBC
* extension, in a query the "?" character means a scalar parameter.
* There are two extensions though, a "&" character means an opaque
* parameter. An opaque parameter is simply a file name, the real
* data are in that file (useful for putting uploaded files into your
* database and such). The "!" char means a parameter that must be
* left as it is.
* They modify the quote behavoir:
* DB_PARAM_SCALAR (?) => 'original string quoted'
* DB_PARAM_OPAQUE (&) => 'string from file quoted'
* DB_PARAM_MISC (!) => original string
*/
define('DB_PARAM_SCALAR', 1);
define('DB_PARAM_OPAQUE', 2);
define('DB_PARAM_MISC', 3);
 
 
// }}}
// {{{ binary data-related
 
 
/*
* These constants define different ways of returning binary data
* from queries. Again, this model has been borrowed from the ODBC
* extension.
*
* DB_BINMODE_PASSTHRU sends the data directly through to the browser
* when data is fetched from the database.
* DB_BINMODE_RETURN lets you return data as usual.
* DB_BINMODE_CONVERT returns data as well, only it is converted to
* hex format, for example the string "123" would become "313233".
*/
define('DB_BINMODE_PASSTHRU', 1);
define('DB_BINMODE_RETURN', 2);
define('DB_BINMODE_CONVERT', 3);
 
 
// }}}
// {{{ fetch modes
 
 
/**
* This is a special constant that tells DB the user hasn't specified
* any particular get mode, so the default should be used.
*/
define('DB_FETCHMODE_DEFAULT', 0);
 
/**
* Column data indexed by numbers, ordered from 0 and up
*/
define('DB_FETCHMODE_ORDERED', 1);
 
/**
* Column data indexed by column names
*/
define('DB_FETCHMODE_ASSOC', 2);
 
/**
* Column data as object properties
*/
define('DB_FETCHMODE_OBJECT', 3);
 
/**
* For multi-dimensional results: normally the first level of arrays
* is the row number, and the second level indexed by column number or name.
* DB_FETCHMODE_FLIPPED switches this order, so the first level of arrays
* is the column name, and the second level the row number.
*/
define('DB_FETCHMODE_FLIPPED', 4);
 
/* for compatibility */
define('DB_GETMODE_ORDERED', DB_FETCHMODE_ORDERED);
define('DB_GETMODE_ASSOC', DB_FETCHMODE_ASSOC);
define('DB_GETMODE_FLIPPED', DB_FETCHMODE_FLIPPED);
 
 
// }}}
// {{{ tableInfo() && autoPrepare()-related
 
 
/**
* these are constants for the tableInfo-function
* they are bitwised or'ed. so if there are more constants to be defined
* in the future, adjust DB_TABLEINFO_FULL accordingly
*/
define('DB_TABLEINFO_ORDER', 1);
define('DB_TABLEINFO_ORDERTABLE', 2);
define('DB_TABLEINFO_FULL', 3);
 
/*
* Used by autoPrepare()
*/
define('DB_AUTOQUERY_INSERT', 1);
define('DB_AUTOQUERY_UPDATE', 2);
 
 
// }}}
// {{{ portability modes
 
 
/**
* Portability: turn off all portability features.
* @see DB_common::setOption()
*/
define('DB_PORTABILITY_NONE', 0);
 
/**
* Portability: convert names of tables and fields to lower case
* when using the get*(), fetch*() and tableInfo() methods.
* @see DB_common::setOption()
*/
define('DB_PORTABILITY_LOWERCASE', 1);
 
/**
* Portability: right trim the data output by get*() and fetch*().
* @see DB_common::setOption()
*/
define('DB_PORTABILITY_RTRIM', 2);
 
/**
* Portability: force reporting the number of rows deleted.
* @see DB_common::setOption()
*/
define('DB_PORTABILITY_DELETE_COUNT', 4);
 
/**
* Portability: enable hack that makes numRows() work in Oracle.
* @see DB_common::setOption()
*/
define('DB_PORTABILITY_NUMROWS', 8);
 
/**
* Portability: makes certain error messages in certain drivers compatible
* with those from other DBMS's.
*
* + mysql, mysqli: change unique/primary key constraints
* DB_ERROR_ALREADY_EXISTS -> DB_ERROR_CONSTRAINT
*
* + odbc(access): MS's ODBC driver reports 'no such field' as code
* 07001, which means 'too few parameters.' When this option is on
* that code gets mapped to DB_ERROR_NOSUCHFIELD.
*
* @see DB_common::setOption()
*/
define('DB_PORTABILITY_ERRORS', 16);
 
/**
* Portability: convert null values to empty strings in data output by
* get*() and fetch*().
* @see DB_common::setOption()
*/
define('DB_PORTABILITY_NULL_TO_EMPTY', 32);
 
/**
* Portability: turn on all portability features.
* @see DB_common::setOption()
*/
define('DB_PORTABILITY_ALL', 63);
 
// }}}
 
 
// }}}
// {{{ class DB
 
/**
* The main "DB" class is simply a container class with some static
* methods for creating DB objects as well as some utility functions
* common to all parts of DB.
*
* The object model of DB is as follows (indentation means inheritance):
*
* DB The main DB class. This is simply a utility class
* with some "static" methods for creating DB objects as
* well as common utility functions for other DB classes.
*
* DB_common The base for each DB implementation. Provides default
* | implementations (in OO lingo virtual methods) for
* | the actual DB implementations as well as a bunch of
* | query utility functions.
* |
* +-DB_mysql The DB implementation for MySQL. Inherits DB_common.
* When calling DB::factory or DB::connect for MySQL
* connections, the object returned is an instance of this
* class.
*
* @package DB
* @author Stig Bakken <ssb@php.net>
* @author Tomas V.V.Cox <cox@idecnet.com>
* @since PHP 4.0
* @version $Id$
* @category Database
*/
class DB
{
// {{{ &factory()
 
/**
* Create a new DB object for the specified database type.
*
* Allows creation of a DB_<driver> object from which the object's
* methods can be utilized without actually connecting to a database.
*
* @param string $type database type, for example "mysql"
* @param array $options associative array of option names and values
*
* @return object a new DB object. On error, an error object.
*
* @see DB_common::setOption()
* @access public
*/
function &factory($type, $options = false)
{
if (!is_array($options)) {
$options = array('persistent' => $options);
}
 
if (isset($options['debug']) && $options['debug'] >= 2) {
// expose php errors with sufficient debug level
include_once "DB/{$type}.php";
} else {
@include_once "DB/{$type}.php";
}
 
$classname = "DB_${type}";
 
if (!class_exists($classname)) {
$tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null,
"Unable to include the DB/{$type}.php file",
'DB_Error', true);
return $tmp;
}
 
@$obj =& new $classname;
 
foreach ($options as $option => $value) {
$test = $obj->setOption($option, $value);
if (DB::isError($test)) {
return $test;
}
}
 
return $obj;
}
 
// }}}
// {{{ &connect()
 
/**
* Create a new DB object and connect to the specified database.
*
* Example 1.
* <code> <?php
* require_once 'DB.php';
*
* $dsn = 'mysql://user:password@host/database'
* $options = array(
* 'debug' => 2,
* 'portability' => DB_PORTABILITY_ALL,
* );
*
* $dbh =& DB::connect($dsn, $options);
* if (DB::isError($dbh)) {
* die($dbh->getMessage());
* }
* ?></code>
*
* @param mixed $dsn string "data source name" or an array in the
* format returned by DB::parseDSN()
*
* @param array $options an associative array of option names and
* their values
*
* @return object a newly created DB connection object, or a DB
* error object on error
*
* @see DB::parseDSN(), DB_common::setOption(), DB::isError()
* @access public
*/
function &connect($dsn, $options = array())
{
$dsninfo = DB::parseDSN($dsn);
$type = $dsninfo['phptype'];
 
if (!is_array($options)) {
/*
* For backwards compatibility. $options used to be boolean,
* indicating whether the connection should be persistent.
*/
$options = array('persistent' => $options);
}
 
if (isset($options['debug']) && $options['debug'] >= 2) {
// expose php errors with sufficient debug level
include_once "DB/${type}.php";
} else {
@include_once "DB/${type}.php";
}
 
$classname = "DB_${type}";
if (!class_exists($classname)) {
$tmp = PEAR::raiseError(null, DB_ERROR_NOT_FOUND, null, null,
"Unable to include the DB/{$type}.php file for `$dsn'",
'DB_Error', true);
return $tmp;
}
 
@$obj =& new $classname;
 
foreach ($options as $option => $value) {
$test = $obj->setOption($option, $value);
if (DB::isError($test)) {
return $test;
}
}
 
$err = $obj->connect($dsninfo, $obj->getOption('persistent'));
if (DB::isError($err)) {
$err->addUserInfo($dsn);
return $err;
}
 
return $obj;
}
 
// }}}
// {{{ apiVersion()
 
/**
* Return the DB API version
*
* @return int the DB API version number
*
* @access public
*/
function apiVersion()
{
return 2;
}
 
// }}}
// {{{ isError()
 
/**
* Tell whether a result code from a DB method is an error
*
* @param int $value result code
*
* @return bool whether $value is an error
*
* @access public
*/
function isError($value)
{
return is_a($value, 'DB_Error');
}
 
// }}}
// {{{ isConnection()
 
/**
* Tell whether a value is a DB connection
*
* @param mixed $value value to test
*
* @return bool whether $value is a DB connection
*
* @access public
*/
function isConnection($value)
{
return (is_object($value) &&
is_subclass_of($value, 'db_common') &&
method_exists($value, 'simpleQuery'));
}
 
// }}}
// {{{ isManip()
 
/**
* Tell whether a query is a data manipulation query (insert,
* update or delete) or a data definition query (create, drop,
* alter, grant, revoke).
*
* @access public
*
* @param string $query the query
*
* @return boolean whether $query is a data manipulation query
*/
function isManip($query)
{
$manips = 'INSERT|UPDATE|DELETE|LOAD DATA|'.'REPLACE|CREATE|DROP|'.
'ALTER|GRANT|REVOKE|'.'LOCK|UNLOCK';
if (preg_match('/^\s*"?('.$manips.')\s+/i', $query)) {
return true;
}
return false;
}
 
// }}}
// {{{ errorMessage()
 
/**
* Return a textual error message for a DB error code
*
* @param integer $value error code
*
* @return string error message, or false if the error code was
* not recognized
*/
function errorMessage($value)
{
static $errorMessages;
if (!isset($errorMessages)) {
$errorMessages = array(
DB_ERROR => 'unknown error',
DB_ERROR_ALREADY_EXISTS => 'already exists',
DB_ERROR_CANNOT_CREATE => 'can not create',
DB_ERROR_CANNOT_DELETE => 'can not delete',
DB_ERROR_CANNOT_DROP => 'can not drop',
DB_ERROR_CONSTRAINT => 'constraint violation',
DB_ERROR_CONSTRAINT_NOT_NULL=> 'null value violates not-null constraint',
DB_ERROR_DIVZERO => 'division by zero',
DB_ERROR_INVALID => 'invalid',
DB_ERROR_INVALID_DATE => 'invalid date or time',
DB_ERROR_INVALID_NUMBER => 'invalid number',
DB_ERROR_MISMATCH => 'mismatch',
DB_ERROR_NODBSELECTED => 'no database selected',
DB_ERROR_NOSUCHFIELD => 'no such field',
DB_ERROR_NOSUCHTABLE => 'no such table',
DB_ERROR_NOT_CAPABLE => 'DB backend not capable',
DB_ERROR_NOT_FOUND => 'not found',
DB_ERROR_NOT_LOCKED => 'not locked',
DB_ERROR_SYNTAX => 'syntax error',
DB_ERROR_UNSUPPORTED => 'not supported',
DB_ERROR_VALUE_COUNT_ON_ROW => 'value count on row',
DB_ERROR_INVALID_DSN => 'invalid DSN',
DB_ERROR_CONNECT_FAILED => 'connect failed',
DB_OK => 'no error',
DB_ERROR_NEED_MORE_DATA => 'insufficient data supplied',
DB_ERROR_EXTENSION_NOT_FOUND=> 'extension not found',
DB_ERROR_NOSUCHDB => 'no such database',
DB_ERROR_ACCESS_VIOLATION => 'insufficient permissions',
DB_ERROR_TRUNCATED => 'truncated'
);
}
 
if (DB::isError($value)) {
$value = $value->getCode();
}
 
return isset($errorMessages[$value]) ? $errorMessages[$value] : $errorMessages[DB_ERROR];
}
 
// }}}
// {{{ parseDSN()
 
/**
* Parse a data source name.
*
* Additional keys can be added by appending a URI query string to the
* end of the DSN.
*
* The format of the supplied DSN is in its fullest form:
* <code>
* phptype(dbsyntax)://username:password@protocol+hostspec/database?option=8&another=true
* </code>
*
* Most variations are allowed:
* <code>
* phptype://username:password@protocol+hostspec:110//usr/db_file.db?mode=0644
* phptype://username:password@hostspec/database_name
* phptype://username:password@hostspec
* phptype://username@hostspec
* phptype://hostspec/database
* phptype://hostspec
* phptype(dbsyntax)
* phptype
* </code>
*
* @param string $dsn Data Source Name to be parsed
*
* @return array an associative array with the following keys:
* + phptype: Database backend used in PHP (mysql, odbc etc.)
* + dbsyntax: Database used with regards to SQL syntax etc.
* + protocol: Communication protocol to use (tcp, unix etc.)
* + hostspec: Host specification (hostname[:port])
* + database: Database to use on the DBMS server
* + username: User name for login
* + password: Password for login
*
* @author Tomas V.V.Cox <cox@idecnet.com>
*/
function parseDSN($dsn)
{
$parsed = array(
'phptype' => false,
'dbsyntax' => false,
'username' => false,
'password' => false,
'protocol' => false,
'hostspec' => false,
'port' => false,
'socket' => false,
'database' => false,
);
 
if (is_array($dsn)) {
$dsn = array_merge($parsed, $dsn);
if (!$dsn['dbsyntax']) {
$dsn['dbsyntax'] = $dsn['phptype'];
}
return $dsn;
}
 
// Find phptype and dbsyntax
if (($pos = strpos($dsn, '://')) !== false) {
$str = substr($dsn, 0, $pos);
$dsn = substr($dsn, $pos + 3);
} else {
$str = $dsn;
$dsn = null;
}
 
// Get phptype and dbsyntax
// $str => phptype(dbsyntax)
if (preg_match('|^(.+?)\((.*?)\)$|', $str, $arr)) {
$parsed['phptype'] = $arr[1];
$parsed['dbsyntax'] = !$arr[2] ? $arr[1] : $arr[2];
} else {
$parsed['phptype'] = $str;
$parsed['dbsyntax'] = $str;
}
 
if (!count($dsn)) {
return $parsed;
}
 
// Get (if found): username and password
// $dsn => username:password@protocol+hostspec/database
if (($at = strrpos($dsn,'@')) !== false) {
$str = substr($dsn, 0, $at);
$dsn = substr($dsn, $at + 1);
if (($pos = strpos($str, ':')) !== false) {
$parsed['username'] = rawurldecode(substr($str, 0, $pos));
$parsed['password'] = rawurldecode(substr($str, $pos + 1));
} else {
$parsed['username'] = rawurldecode($str);
}
}
 
// Find protocol and hostspec
 
// $dsn => proto(proto_opts)/database
if (preg_match('|^([^(]+)\((.*?)\)/?(.*?)$|', $dsn, $match)) {
$proto = $match[1];
$proto_opts = $match[2] ? $match[2] : false;
$dsn = $match[3];
 
// $dsn => protocol+hostspec/database (old format)
} else {
if (strpos($dsn, '+') !== false) {
list($proto, $dsn) = explode('+', $dsn, 2);
}
if (strpos($dsn, '/') !== false) {
list($proto_opts, $dsn) = explode('/', $dsn, 2);
} else {
$proto_opts = $dsn;
$dsn = null;
}
}
 
// process the different protocol options
$parsed['protocol'] = (!empty($proto)) ? $proto : 'tcp';
$proto_opts = rawurldecode($proto_opts);
if ($parsed['protocol'] == 'tcp') {
if (strpos($proto_opts, ':') !== false) {
list($parsed['hostspec'], $parsed['port']) = explode(':', $proto_opts);
} else {
$parsed['hostspec'] = $proto_opts;
}
} elseif ($parsed['protocol'] == 'unix') {
$parsed['socket'] = $proto_opts;
}
 
// Get dabase if any
// $dsn => database
if ($dsn) {
// /database
if (($pos = strpos($dsn, '?')) === false) {
$parsed['database'] = $dsn;
// /database?param1=value1&param2=value2
} else {
$parsed['database'] = substr($dsn, 0, $pos);
$dsn = substr($dsn, $pos + 1);
if (strpos($dsn, '&') !== false) {
$opts = explode('&', $dsn);
} else { // database?param1=value1
$opts = array($dsn);
}
foreach ($opts as $opt) {
list($key, $value) = explode('=', $opt);
if (!isset($parsed[$key])) {
// don't allow params overwrite
$parsed[$key] = rawurldecode($value);
}
}
}
}
 
return $parsed;
}
 
// }}}
// {{{ assertExtension()
 
/**
* Load a PHP database extension if it is not loaded already.
*
* @access public
*
* @param string $name the base name of the extension (without the .so or
* .dll suffix)
*
* @return boolean true if the extension was already or successfully
* loaded, false if it could not be loaded
*/
function assertExtension($name)
{
if (!extension_loaded($name)) {
$dlext = OS_WINDOWS ? '.dll' : '.so';
$dlprefix = OS_WINDOWS ? 'php_' : '';
@dl($dlprefix . $name . $dlext);
return extension_loaded($name);
}
return true;
}
// }}}
}
 
// }}}
// {{{ class DB_Error
 
/**
* DB_Error implements a class for reporting portable database error
* messages.
*
* @package DB
* @author Stig Bakken <ssb@php.net>
*/
class DB_Error extends PEAR_Error
{
// {{{ constructor
 
/**
* DB_Error constructor.
*
* @param mixed $code DB error code, or string with error message.
* @param integer $mode what "error mode" to operate in
* @param integer $level what error level to use for $mode & PEAR_ERROR_TRIGGER
* @param mixed $debuginfo additional debug info, such as the last query
*
* @access public
*
* @see PEAR_Error
*/
function DB_Error($code = DB_ERROR, $mode = PEAR_ERROR_RETURN,
$level = E_USER_NOTICE, $debuginfo = null)
{
if (is_int($code)) {
$this->PEAR_Error('DB Error: ' . DB::errorMessage($code), $code, $mode, $level, $debuginfo);
} else {
$this->PEAR_Error("DB Error: $code", DB_ERROR, $mode, $level, $debuginfo);
}
}
// }}}
}
 
// }}}
// {{{ class DB_result
 
/**
* This class implements a wrapper for a DB result set.
* A new instance of this class will be returned by the DB implementation
* after processing a query that returns data.
*
* @package DB
* @author Stig Bakken <ssb@php.net>
*/
class DB_result
{
// {{{ properties
 
var $dbh;
var $result;
var $row_counter = null;
 
/**
* for limit queries, the row to start fetching
* @var integer
*/
var $limit_from = null;
 
/**
* for limit queries, the number of rows to fetch
* @var integer
*/
var $limit_count = null;
 
// }}}
// {{{ constructor
 
/**
* DB_result constructor.
* @param resource &$dbh DB object reference
* @param resource $result result resource id
* @param array $options assoc array with optional result options
*/
function DB_result(&$dbh, $result, $options = array())
{
$this->dbh = &$dbh;
$this->result = $result;
foreach ($options as $key => $value) {
$this->setOption($key, $value);
}
$this->limit_type = $dbh->features['limit'];
$this->autofree = $dbh->options['autofree'];
$this->fetchmode = $dbh->fetchmode;
$this->fetchmode_object_class = $dbh->fetchmode_object_class;
}
 
function setOption($key, $value = null)
{
switch ($key) {
case 'limit_from':
$this->limit_from = $value; break;
case 'limit_count':
$this->limit_count = $value; break;
}
}
 
// }}}
// {{{ fetchRow()
 
/**
* Fetch a row of data and return it by reference into an array.
*
* The type of array returned can be controlled either by setting this
* method's <var>$fetchmode</var> parameter or by changing the default
* fetch mode setFetchMode() before calling this method.
*
* There are two options for standardizing the information returned
* from databases, ensuring their values are consistent when changing
* DBMS's. These portability options can be turned on when creating a
* new DB object or by using setOption().
*
* + <samp>DB_PORTABILITY_LOWERCASE</samp>
* convert names of fields to lower case
*
* + <samp>DB_PORTABILITY_RTRIM</samp>
* right trim the data
*
* @param int $fetchmode how the resulting array should be indexed
* @param int $rownum the row number to fetch
*
* @return array a row of data, null on no more rows or PEAR_Error
* object on error
*
* @see DB_common::setOption(), DB_common::setFetchMode()
* @access public
*/
function &fetchRow($fetchmode = DB_FETCHMODE_DEFAULT, $rownum=null)
{
if ($fetchmode === DB_FETCHMODE_DEFAULT) {
$fetchmode = $this->fetchmode;
}
if ($fetchmode === DB_FETCHMODE_OBJECT) {
$fetchmode = DB_FETCHMODE_ASSOC;
$object_class = $this->fetchmode_object_class;
}
if ($this->limit_from !== null) {
if ($this->row_counter === null) {
$this->row_counter = $this->limit_from;
// Skip rows
if ($this->limit_type == false) {
$i = 0;
while ($i++ < $this->limit_from) {
$this->dbh->fetchInto($this->result, $arr, $fetchmode);
}
}
}
if ($this->row_counter >= (
$this->limit_from + $this->limit_count))
{
if ($this->autofree) {
$this->free();
}
$tmp = null;
return $tmp;
}
if ($this->limit_type == 'emulate') {
$rownum = $this->row_counter;
}
$this->row_counter++;
}
$res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum);
if ($res === DB_OK) {
if (isset($object_class)) {
// default mode specified in DB_common::fetchmode_object_class property
if ($object_class == 'stdClass') {
$arr = (object) $arr;
} else {
$arr = &new $object_class($arr);
}
}
return $arr;
}
if ($res == null && $this->autofree) {
$this->free();
}
return $res;
}
 
// }}}
// {{{ fetchInto()
 
/**
* Fetch a row of data into an array which is passed by reference.
*
* The type of array returned can be controlled either by setting this
* method's <var>$fetchmode</var> parameter or by changing the default
* fetch mode setFetchMode() before calling this method.
*
* There are two options for standardizing the information returned
* from databases, ensuring their values are consistent when changing
* DBMS's. These portability options can be turned on when creating a
* new DB object or by using setOption().
*
* + <samp>DB_PORTABILITY_LOWERCASE</samp>
* convert names of fields to lower case
*
* + <samp>DB_PORTABILITY_RTRIM</samp>
* right trim the data
*
* @param array &$arr (reference) array where data from the row
* should be placed
* @param int $fetchmode how the resulting array should be indexed
* @param int $rownum the row number to fetch
*
* @return mixed DB_OK on success, null on no more rows or
* a DB_Error object on error
*
* @see DB_common::setOption(), DB_common::setFetchMode()
* @access public
*/
function fetchInto(&$arr, $fetchmode = DB_FETCHMODE_DEFAULT, $rownum=null)
{
if ($fetchmode === DB_FETCHMODE_DEFAULT) {
$fetchmode = $this->fetchmode;
}
if ($fetchmode === DB_FETCHMODE_OBJECT) {
$fetchmode = DB_FETCHMODE_ASSOC;
$object_class = $this->fetchmode_object_class;
}
if ($this->limit_from !== null) {
if ($this->row_counter === null) {
$this->row_counter = $this->limit_from;
// Skip rows
if ($this->limit_type == false) {
$i = 0;
while ($i++ < $this->limit_from) {
$this->dbh->fetchInto($this->result, $arr, $fetchmode);
}
}
}
if ($this->row_counter >= (
$this->limit_from + $this->limit_count))
{
if ($this->autofree) {
$this->free();
}
return null;
}
if ($this->limit_type == 'emulate') {
$rownum = $this->row_counter;
}
 
$this->row_counter++;
}
$res = $this->dbh->fetchInto($this->result, $arr, $fetchmode, $rownum);
if ($res === DB_OK) {
if (isset($object_class)) {
// default mode specified in DB_common::fetchmode_object_class property
if ($object_class == 'stdClass') {
$arr = (object) $arr;
} else {
$arr = new $object_class($arr);
}
}
return DB_OK;
}
if ($res == null && $this->autofree) {
$this->free();
}
return $res;
}
 
// }}}
// {{{ numCols()
 
/**
* Get the the number of columns in a result set.
*
* @return int the number of columns, or a DB error
*
* @access public
*/
function numCols()
{
return $this->dbh->numCols($this->result);
}
 
// }}}
// {{{ numRows()
 
/**
* Get the number of rows in a result set.
*
* @return int the number of rows, or a DB error
*
* @access public
*/
function numRows()
{
return $this->dbh->numRows($this->result);
}
 
// }}}
// {{{ nextResult()
 
/**
* Get the next result if a batch of queries was executed.
*
* @return bool true if a new result is available or false if not.
*
* @access public
*/
function nextResult()
{
return $this->dbh->nextResult($this->result);
}
 
// }}}
// {{{ free()
 
/**
* Frees the resources allocated for this result set.
* @return int error code
*
* @access public
*/
function free()
{
$err = $this->dbh->freeResult($this->result);
if (DB::isError($err)) {
return $err;
}
$this->result = false;
return true;
}
 
// }}}
// {{{ tableInfo()
 
/**
* @deprecated
* @internal
* @see DB_common::tableInfo()
*/
function tableInfo($mode = null)
{
if (is_string($mode)) {
return $this->dbh->raiseError(DB_ERROR_NEED_MORE_DATA);
}
return $this->dbh->tableInfo($this, $mode);
}
 
// }}}
// {{{ getRowCounter()
 
/**
* returns the actual row number
* @return integer
*/
function getRowCounter()
{
return $this->row_counter;
}
// }}}
}
 
// }}}
// {{{ class DB_row
 
/**
* Pear DB Row Object
* @see DB_common::setFetchMode()
*/
class DB_row
{
// {{{ constructor
 
/**
* constructor
*
* @param resource row data as array
*/
function DB_row(&$arr)
{
foreach ($arr as $key => $value) {
$this->$key = &$arr[$key];
}
}
 
// }}}
}
 
// }}}
 
/*
* Local variables:
* tab-width: 4
* c-basic-offset: 4
* End:
*/
 
?>
/trunk/applications/saisie/configurations/config.ini
New file
0,0 → 1,53
; +------------------------------------------------------------------------------------------------------+
; Général
; Séparateur de dossier
ds = DIRECTORY_SEPARATOR
 
; +------------------------------------------------------------------------------------------------------+
; Info sur l'application
info.nom = Saisie des observations
; Abréviation de l'application
info.abr = SODS
; Code numérique de la version de l'application
info.version.code.num = "0.1"
; Code alphabétique de la version de l'application
info.version.code.alpha = ""
; Nom de la version de l'application
info.version.nom = ""
; Version du Framework nécessaire au fonctionnement de cette application
info.framework.version = 0.2
;Encodage de l'application
appli_encodage = "UTF-8"
; Nom de domaine pour l'URL de base de l'application : 162.38.234.9
domaine = "www.tela-botanica.org"
; URL de base de l'application, si elle est laissée vide, l'application fonctionnera en Stand-alone
url_base = "http://{ref:domaine}/client/collection/v{ref:info.version.code.num}-{ref:info.version.code.alpha}/"
; URL de base de l'application avec l'indication du fichier de départ
url_base_index = "{ref:url_base}index.php"
; URL de base où se situe le .htacces réalisant la réecriture d'URL pour les permaliens de l'application
url_base_permalien = "{ref:url_base}"
 
 
; Mettre à true si l'application nécessite de s'identifier.
identification = true
; Nom de la classe à utiliser pour gérer les utilisateurs
objet_utilisateur = DrupalUtilisateur
 
; +------------------------------------------------------------------------------------------------------+
; Paramétrage de la session
; Devons nous démarrer une session : oui (true) ou non (false)
session_demarrage = "php:true"
; Définition du nom de la session à utiliser
session_nom = "ods_saisie"
 
; +------------------------------------------------------------------------------------------------------+
; Débogage
; Indique si oui ou non on veut afficher le débogage.
fw_debogage = true
; Indique si oui ou non on veut lancer le chronométrage
chronometrage = false
 
; +------------------------------------------------------------------------------------------------------+
; Spécifique à l'application
; Url du Jrest utilisé pour les services web fournissant les données à l'application
url_jrest = "http://162.38.234.9/obs_saisons/applications/jrest/"
/trunk/applications/saisie/configurations/config.default.ini
New file
0,0 → 1,53
; +------------------------------------------------------------------------------------------------------+
; Général
; Séparateur de dossier
ds = DIRECTORY_SEPARATOR
 
; +------------------------------------------------------------------------------------------------------+
; Info sur l'application
info.nom = Saisie des observations
; Abréviation de l'application
info.abr = SODS
; Code numérique de la version de l'application
info.version.code.num = "0.1"
; Code alphabétique de la version de l'application
info.version.code.alpha = ""
; Nom de la version de l'application
info.version.nom = ""
; Version du Framework nécessaire au fonctionnement de cette application
info.framework.version = 0.2
;Encodage de l'application
appli_encodage = "UTF-8"
; Nom de domaine pour l'URL de base de l'application : localhost
domaine = "www.tela-botanica.org"
; URL de base de l'application, si elle est laissée vide, l'application fonctionnera en Stand-alone
url_base = "http://{ref:domaine}/client/collection/v{ref:info.version.code.num}-{ref:info.version.code.alpha}/"
; URL de base de l'application avec l'indication du fichier de départ
url_base_index = "{ref:url_base}index.php"
; URL de base où se situe le .htacces réalisant la réecriture d'URL pour les permaliens de l'application
url_base_permalien = "{ref:url_base}"
 
 
; Mettre à true si l'application nécessite de s'identifier.
identification = true
; Nom de la classe à utiliser pour gérer les utilisateurs
objet_utilisateur = DrupalUtilisateur
 
; +------------------------------------------------------------------------------------------------------+
; Paramétrage de la session
; Devons nous démarrer une session : oui (true) ou non (false)
session_demarrage = "php:true"
; Définition du nom de la session à utiliser
session_nom = "ods_saisie"
 
; +------------------------------------------------------------------------------------------------------+
; Débogage
; Indique si oui ou non on veut afficher le débogage.
fw_debogage = true
; Indique si oui ou non on veut lancer le chronométrage
chronometrage = false
 
; +------------------------------------------------------------------------------------------------------+
; Spécifique à l'application
; Url du Jrest utilisé pour les services web fournissant les données à l'application
url_jrest = "http://localhost/obs_saisons/applications/jrest/"
/trunk/applications/saisie/framework.defaut.php
New file
0,0 → 1,6
<?php
// Inclusion du Framework
// Renomer ce fichier en "framework.php"
// Indiquer ci-dessous le chemin vers le fichier autoload.inc.php de la bonne version du Framework
require_once '/framework/0.2/autoload.inc.php';
?>
/trunk/applications/saisie/bibliotheque/dao/EvenementDao.php
New file
0,0 → 1,34
<?php
// declare(encoding='UTF-8');
/**
* Modèle d'accès à la base de données de saisies pour le module evenement.
*
* @package ODS_saisie
* @category php 5.2
* @author Aurélien Peronnet <aurelien@tela-botanica.org>
* @copyright 2010 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version SVN: $Id: StationDao.php 154 2010-09-13 12:15:11Z aurelien $
*
*/
class EvenementDao extends Dao {
const SERVICE_ESPECE = 'OdsEvenement';
const METHODE_EVENEMENTS_ESPECE = 'EvenementsPourEspece';
 
/**
* Retourne l'ensemble des especes.
*
* @return array un tableau contenant les informations sur les evenement d'une espece.
*/
public function getListeEvenementPourEspece($id_espece) {
$url = $this->url_jrest.self::SERVICE_ESPECE.'/'.self::METHODE_EVENEMENTS_ESPECE.'/'.$id_espece;
$json = $this->envoyerRequeteConsultation($url);
$donnees = json_decode($json, true);
return $donnees;
}
}
?>
/trunk/applications/saisie/bibliotheque/dao/IndividuDao.php
New file
0,0 → 1,74
<?php
// declare(encoding='UTF-8');
/**
* Modèle d'accès à la base de données de saisies pour le module individus.
*
* @package ODS_saisie
* @category php 5.2
* @author Aurélien Peronnet <aurelien@tela-botanica.org>
* @copyright 2010 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version SVN: $Id: StationDao.php 154 2010-09-13 12:15:11Z aurelien $
*
*/
class IndividuDao extends Dao {
const SERVICE_INDIVIDU = 'OdsIndividu';
/**
* Retourne l'ensemble des informations d'un individu.
*
* @param integer l'id d'une station.
* @return array un tableau contenant les informations sur les individus de cette station.
*/
public function getListeIndividusPourStation($id_station) {
$url = $this->url_jrest.self::SERVICE_INDIVIDU."/*/";
$json = $this->envoyerRequeteConsultation($url);
$donnees = json_decode($json, true);
return $donnees;
}
public function getInformationsIndividu($id_individu) {
if (is_numeric($id_individu)) {
$url = $this->url_jrest.self::SERVICE_INDIVIDU."/$id_individu";
$json = $this->envoyerRequeteConsultation($url);
$donnees = json_decode($json, true);
return $donnees;
}
}
public function ajouterIndividu($valeurs_individu_verifiees) {
$donnees = $valeurs_individu_verifiees;
$url = $this->url_jrest.self::SERVICE_INDIVIDU."/";
$json = $this->envoyerRequeteAjout($url, $donnees);
$donnees = json_decode($json, true);
if (true) {
//TODO: verifier ajout
}
return true;
}
public function modifierIndividu($id_individu, $valeurs_individu_verifiees) {
$donnees = $valeurs_individu_verifiees;
if (is_numeric($id_individu)) {
$url = $this->url_jrest.self::SERVICE_INDIVIDU."/$id_individu";
$json = $this->envoyerRequeteModif($url, $donnees);
$donnees = json_decode($json, true);
if (true) {
//TODO: verifier modification
}
}
return true;
}
}
?>
/trunk/applications/saisie/bibliotheque/dao/ObservationDao.php
New file
0,0 → 1,75
<?php
// declare(encoding='UTF-8');
/**
* Modèle d'accès à la base de données de saisies pour le module observation.
*
* @package ODS_saisie
* @category php 5.2
* @author Aurélien Peronnet <aurelien@tela-botanica.org>
* @copyright 2010 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version SVN: $Id: StationDao.php 154 2010-09-13 12:15:11Z aurelien $
*
*/
class ObservationDao extends Dao {
const SERVICE_OBSERVATION = 'OdsObservation';
const METHODE_OBSERVATION_INDIVIDU= 'ObservationsPourIndividu';
/**
* Retourne l'ensemble des observations d'une station.
*
* @param integer l'id d'une station.
* @return array un tableau contenant les informations sur les observations de cette station.
*/
public function getListeObservationsPourStation($id_station) {
$url = $this->url_jrest.self::SERVICE_OBSERVATION."/*/";
$json = $this->envoyerRequeteConsultation($url);
$donnees = json_decode($json, true);
return $donnees;
}
public function getListeObservationsPourIndividu($id_individu, $annee = null) {
if (is_numeric($id_individu)) {
$url = $this->url_jrest.self::SERVICE_OBSERVATION.'/'.self::METHODE_OBSERVATION_INDIVIDU.'/'.$id_individu;
$json = $this->envoyerRequeteConsultation($url);
$donnees = json_decode($json, true);
return $donnees;
}
}
public function ajouterObservation($valeurs_observation_verifiees) {
$donnees = $valeurs_individu_verifiees;
$url = $this->url_jrest.self::SERVICE_OBSERVATION."/";
$json = $this->envoyerRequeteAjout($url, $donnees);
$donnees = json_decode($json, true);
if (true) {
//TODO: verifier ajout
}
return true;
}
public function modifierObservation($id_observation, $valeurs_observation_verifiees) {
$donnees = observation;
if (is_numeric($id_individu)) {
$url = $this->url_jrest.self::SERVICE_OBSERVATION."/$id_observation";
$json = $this->envoyerRequeteModif($url, $donnees);
$donnees = json_decode($json, true);
if (true) {
//TODO: verifier modification
}
}
return true;
}
}
?>
/trunk/applications/saisie/bibliotheque/dao/Dao.php
New file
0,0 → 1,219
<?php
// declare(encoding='UTF-8');
/**
* Classe modèle spécifique à l'application, donc d'accés au données, elle ne devrait pas être appelée de l'extérieur.
* Elle est abstraite donc doit obligatoirement être étendue.
*
* @category Php5
* @package ODS_saisie
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @copyright 2010 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version SVN: $Id$
*/
abstract class Dao {
const ORDRE_ASCENDANT = 'ASC';
const ORDRE_DESCENDANT = 'DESC';
const HTTP_REQUETE_SEPARATEUR = '&';
protected $distinction = '0';
protected $limite_debut = null;
protected $limite_nbre = null;
protected $ordre = null;
protected $url_jrest = null;
public function __construct() {
$this->url_jrest = Config::get('url_jrest');
}
//+----------------------------------------------------------------------------------------------------------------+
// ACCESSEURS
public function setDistinction($distinct) {
$this->distinction = $distinct;
}
public function getDistinction() {
return $this->distinction;
}
public function viderDistinction() {
$this->distinction = null;
}
 
public function avoirLimitation() {
$limitation = false;
if (!is_null($this->limite_debut) && !is_null($this->limite_nbre)) {
$limitation = true;
}
return $limitation;
}
public function setLimitation($limite_debut, $limite_nbre) {
$this->limite_debut = $limite_debut;
$this->limite_nbre = $limite_nbre;
}
public function getLimiteDebut() {
return $this->limite_debut;
}
public function getLimiteNbre() {
return $this->limite_nbre;
}
public function viderLimite() {
$this->limite_debut = null;
$this->limite_nbre = null;
}
public function addOrdre($champ, $trie = self::ORDRE_ASCENDANT) {
if (!isset($this->ordre[$champ])) {
if (self::ORDRE_ASCENDANT == $trie || self::ORDRE_DESCENDANT == $trie) {
$this->ordre[$champ] = $trie;
} else {
$e = "La valeur pour le tri doit être : {self::ORDRE_ASCENDANT} ou {self::ORDRE_DESCENDANT}.";
trigger_error($e, E_USER_WARNING);
}
} else {
$e = "Le champ $champ existe déjà dans le tableau des ordres.";
trigger_error($e, E_USER_WARNING);
}
}
public function getOrdre() {
$champs = array();
foreach ($this->ordre as $champ => $trie) {
$champs[] = "$champ $trie";
}
return implode(', ', $champs);
}
public function viderOrdre() {
$this->ordre = null;
}
//+----------------------------------------------------------------------------------------------------------------+
// MÉTHODES
protected function envoyerRequeteConsultation($url) {
$url = $this->traiterUrlParametres($url);
$retour = $this->envoyerRequete($url, 'GET');
return $retour;
}
protected function envoyerRequeteAjout($url, Array $donnees) {
$retour = $this->envoyerRequete($url, 'PUT', $donnees);
return $retour;
}
protected function envoyerRequeteModif($url, Array $donnees) {
$retour = $this->envoyerRequete($url, 'POST', $donnees);
return $retour;
}
protected function envoyerRequeteSuppression($url) {
$retour = $this->envoyerRequete($url, 'DELETE');
return $retour;
}
private function envoyerRequete($url, $mode, Array $donnees = array()) {
$contenu = false;
if ($mode != 'GET' && $mode != 'PUT' && $mode != 'POST' && $mode != 'DELETE') {
$e = "Le mode de requête '$mode' n'est pas accepté!";
trigger_error($e, E_USER_WARNING);
} else {
$contexte = stream_context_create(array(
'http' => array(
'method' => $mode,
'header' => "Content-type: application/x-www-form-urlencoded\r\n",
'content' => http_build_query($donnees, null, self::HTTP_REQUETE_SEPARATEUR))));
$flux = @fopen($url, 'r', false, $contexte);
if (!$flux) {
$this->traiterEntete($http_response_header, $url);
$e = "L'ouverture de l'url '$url' par la méthode HTTP '$mode' a échoué!";
trigger_error($e, E_USER_WARNING);
} else {
// Informations sur les en-têtes et métadonnées du flux
$entetes = stream_get_meta_data($flux);
$this->traiterEntete($entetes, $url);
// Contenu actuel de $url
$contenu = stream_get_contents($flux);
fclose($flux);
}
}
$this->reinitialiser();
return $contenu;
}
private function traiterUrlParametres($url) {
$parametres = array();
if (! is_null($this->getLimiteDebut())) {
$parametres[] = 'start='.$this->getLimiteDebut();
}
if (! is_null($this->getLimiteNbre())) {
$parametres[] = 'limit='.$this->getLimiteNbre();
}
if (! is_null($this->ordre)) {
$parametres[] = 'orderby='.urlencode($this->getOrdre());
}
if ($this->getDistinction() != 0) {
$parametres[] = 'distinct='.$this->getDistinction();
}
if (count($parametres) > 0) {
$url_parametres = implode('&', $parametres);
$url = $url.'?'.$url_parametres;
}
return $url;
}
private function traiterEntete($entetes, $uri) {
$infos = $this->analyserEntete($entetes, $uri);
$this->traiterEnteteDebug($infos);
$this->traiterEnteteMessage($infos);
}
private function analyserEntete($entetes, $uri) {
$infos = array('date' => null, 'uri' => $uri, 'debugs' => null, 'messages' => null);
if (isset($entetes['wrapper_data'])) {
$entetes = $entetes['wrapper_data'];
}
foreach ($entetes as $entete) {
if (preg_match('/^X-DebugJrest-Data: (.+)$/', $entete, $match)) {
$infos['debugs'] = json_decode($match[1]);
}
if (preg_match('/^X-MessageJrest-Data: (.+)$/', $entete, $match)) {
$infos['messages'] = json_decode($match[1]);
}
if (preg_match('/^Date: .+ ([012][0-9]:[012345][0-9]:[012345][0-9]) .*$/', $entete, $match)) {
$infos['date'] = $match[1];
}
}
return $infos;
}
private function traiterEnteteDebug($entetes) {
if (isset($entetes['debugs'])) {
$date = $entetes['date'];
$uri = $entetes['uri'];
$debugs = $entetes['debugs'];
foreach ($debugs as $debug) {
Debug::printr("DEBUG : $date - $uri :\n$debug");
}
}
}
private function traiterEnteteMessage($entetes) {
if (isset($entetes['messages'])) {
$date = $entetes['date'];
$uri = $entetes['uri'];
$messages = $entetes['messages'];
foreach ($messages as $message) {
Debug::printr("MESSAGE : $date - $uri :\n$message");
}
}
}
private function reinitialiser() {
$this->viderDistinction();
$this->viderLimite();
$this->viderOrdre();
}
}
/trunk/applications/saisie/bibliotheque/dao/StationDao.php
New file
0,0 → 1,76
<?php
// declare(encoding='UTF-8');
/**
* Modèle d'accès à la base de données de saisies pour le module stations.
*
* @package ODS_saisie
* @category php 5.2
* @author Aurélien Peronnet <aurelien@tela-botanica.org>
* @copyright 2010 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version SVN: $Id: StationDao.php 154 2010-09-13 12:15:11Z aurelien $
*
*/
class StationDao extends Dao {
const SERVICE_STATION = 'OdsStation';
 
/**
* Retourne l'ensemble des stations d'un participant.
*
* @return array un tableau contenant les informations sur les stations du participant.
*/
public function getListeStations() {
$url = $this->url_jrest.self::SERVICE_STATION.'/'.AppControleur::getIdUtilisateur().'/*/';
$json = $this->envoyerRequeteConsultation($url);
$donnees = json_decode($json, true);
return $donnees;
}
public function getInformationsStation($id_station) {
$url = $this->url_jrest.self::SERVICE_STATION.'/'.AppControleur::getIdUtilisateur().'/'.$id_station.'/';
$json = $this->envoyerRequeteConsultation($url);
$donnees = json_decode($json, true);
return $donnees;
}
public function ajouterStation($valeurs_station_verifiees) {
$donnees = $valeurs_station_verifiees;
$donnees['id_participant'] = AppControleur::getIdUtilisateur();
$url = $this->url_jrest.self::SERVICE_STATION."/";
$json = $this->envoyerRequeteAjout($url, $donnees);
$donnees = json_decode($json, true);
if (true) {
//TODO: verifier ajout
}
return true;
}
public function modifierStation($id_station, $valeurs_station_verifiees) {
$donnees = $valeurs_station_verifiees;
$donnees['id_participant'] = AppControleur::getIdUtilisateur();
if (is_numeric($id_station)) {
$url = $this->url_jrest.self::SERVICE_STATION."/$id_station";
$json = $this->envoyerRequeteModif($url, $donnees);
$donnees = json_decode($json, true);
if (true) {
//TODO: verifier modification
}
}
return true;
}
}
?>
/trunk/applications/saisie/bibliotheque/dao/EspeceDao.php
New file
0,0 → 1,73
<?php
// declare(encoding='UTF-8');
/**
* Modèle d'accès à la base de données de saisies pour le module espece.
*
* @package ODS_saisie
* @category php 5.2
* @author Aurélien Peronnet <aurelien@tela-botanica.org>
* @copyright 2010 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version SVN: $Id: StationDao.php 154 2010-09-13 12:15:11Z aurelien $
*
*/
class EspeceDao extends Dao {
const SERVICE_ESPECE = 'OdsEspece';
const METHODE_ESPECES_STATION = 'EspecesPourStation';
const METHODE_ESPECES_TYPE = 'EspecesParType';
const METHODE_INFOS_ESPECE = 'Espece';
 
/**
* Retourne l'ensemble des especes.
*
* @return array un tableau contenant les informations sur les especes.
*/
public function getListeEspeces() {
$url = $this->url_jrest.self::SERVICE_ESPECE."/*/";
$json = $this->envoyerRequeteConsultation($url);
$donnees = json_decode($json, true);
return $donnees;
}
/**
* Retourne l'ensemble des especes présentes dans une station.
*
* @param integer l'id de de la station.
* @return array un tableau contenant les informations sur les especes de cette station.
*/
public function getListeEspecesPourStation($id_station) {
 
$url = $this->url_jrest.self::SERVICE_ESPECE.'/'.self::METHODE_ESPECES_STATION.'/'.$id_station;
$json = $this->envoyerRequeteConsultation($url);
$donnees = json_decode($json, true);
return $donnees;
}
/**
* Retourne l'ensemble des especes présentes organisées hierarchiquement par type.
*
* @return array un tableau contenant les informations sur les especes.
*/
public function getListeEspecesParType() {
$url = $this->url_jrest.self::SERVICE_ESPECE.'/'.self::METHODE_ESPECES_TYPE.'/';
$json = $this->envoyerRequeteConsultation($url);
$donnees = json_decode($json, true);
return $donnees;
}
public function getInformationsEspece($id_espece) {
$url = $this->url_jrest.self::SERVICE_ESPECE.'/'.self::METHODE_INFOS_ESPECE.'/'.$id_espece;
$json = $this->envoyerRequeteConsultation($url);
$donnees = json_decode($json, true);
return $donnees;
}
}
?>
/trunk/applications/saisie/index.php
New file
0,0 → 1,79
<?php
// declare(encoding='UTF-8');
/** Inclusion du fichier principal de l'application*/
require_once 'saisie.php';
?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr" lang="fr">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta http-equiv="Content-style-type" content="text/css" />
<meta http-equiv="Content-script-type" content="text/javascript" />
<meta http-equiv="Content-language" content="fr" />
 
<title><?php echo AppControleur::getMetaTitre(); ?></title>
<meta name="description" content="<?php echo AppControleur::getMetaDescription();?>" />
<meta name="keywords" content="<?php echo AppControleur::getMetaTags();?>" />
<meta name="revisit-after" content="15 days" />
<meta name="robots" content="index,follow" />
<meta name="author" content="Tela Botanica" />
 
<link rel="shortcut icon" type="image/x-icon" href="http://www.tela-botanica.org/favicon.ico" />
<link rel="icon" type="image/png" href="http://www.tela-botanica.org/sites/commun/generique/images/favicones/tela_botanica.png" />
<script type="text/javascript" src="squelettes/js/jquery-1.4.2.min.js"></script>
</head>
<body>
<div id="reducteur">
 
<div id="logo_tela">
<a href="/" title="Retour à l'accueil du site">
<img src="http://www.tela-botanica.org/sites/reseau/generique/images/graphisme/logo_jaune.gif" alt="le logo de Tela Botanica"/>
</a>
</div>
<div id="bandeau">
<div id="bandeau_contenu">
<div id="titre_monde">
<h1>Collections</h1>
</div>
</div>
</div>
<div id="droite">
<div id="onglets">
<?php echo AppControleur::getContenuNavigation(); ?>
</div>
<div id="contenu">
<div id="entete">
<?php echo AppControleur::getContenuTete(); ?>
</div>
<div id="texte">
<?php echo AppControleur::getContenuCorps(); ?>
</div>
<div id="pied_texte">
<?php echo AppControleur::getContenuPied(); ?>
</div>
</div>
<div>
<?php echo AppControleur::getChrono(); ?>
<?php echo AppControleur::getExceptions(); ?>
</div>
<div id="pied">
<p> &copy;<a href="http://www.tela-botanica.org/" accesskey="1">Tela Botanica</a> / 2000-<?=date('Y')?> - Le réseau des Botanistes Francophones</p>
</div>
</div>
<div id="nav_gauche">
<ul>
<li> Stations</li>
<li> Espèces</li>
<li> Observations </li>
</ul>
</div>
</div>
</body>
</html>
/trunk/applications/saisie/squelettes/images/voir_details_liste.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
/trunk/applications/saisie/squelettes/images/voir_details_liste.png
New file
Property changes:
Added: svn:mime-type
+image/png
\ No newline at end of property
/trunk/applications/saisie/squelettes/images/modification.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
/trunk/applications/saisie/squelettes/images/modification.png
New file
Property changes:
Added: svn:mime-type
+image/png
\ No newline at end of property
/trunk/applications/saisie/squelettes/images/afficher.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
/trunk/applications/saisie/squelettes/images/afficher.png
New file
Property changes:
Added: svn:mime-type
+image/png
\ No newline at end of property
/trunk/applications/saisie/squelettes/images/suppression.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
/trunk/applications/saisie/squelettes/images/suppression.png
New file
Property changes:
Added: svn:mime-type
+image/png
\ No newline at end of property
/trunk/applications/saisie/squelettes/images/voir_champs.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
/trunk/applications/saisie/squelettes/images/voir_champs.png
New file
Property changes:
Added: svn:mime-type
+image/png
\ No newline at end of property
/trunk/applications/saisie/squelettes/images/voir_fiche.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
/trunk/applications/saisie/squelettes/images/voir_fiche.png
New file
Property changes:
Added: svn:mime-type
+image/png
\ No newline at end of property
/trunk/applications/saisie/squelettes/images/cadenas.png
Cannot display: file marked as a binary type.
svn:mime-type = image/png
/trunk/applications/saisie/squelettes/images/cadenas.png
New file
Property changes:
Added: svn:mime-type
+image/png
\ No newline at end of property
/trunk/applications/saisie/squelettes/corps.tpl.html
New file
0,0 → 1,5
<!-- SAISIE - DEBUT CORPS DE PAGE -->
<div id="saisie_corps">
<?= $corps ?>
</div>
<!-- SAISIE - FIN CORPS DE PAGE -->
/trunk/applications/saisie/squelettes/css/saisie.css
New file
0,0 → 1,137
/* =========================== Balises ============================
 
/* =========================== Style général ============================ */
 
.gauche {
float:left;
}
 
.droite {
float:right;
}
 
 
/* =========================== Titres ============================ */
 
.titre_saisie {
padding-bottom: 10px;
}
 
.etape {
color: #9AC343;
padding-bottom:20px;
}
 
 
/* =========================== Formulaires ============================ */
 
.element_formulaire_en_ligne {
padding-left: 10px;
padding-right: 10px;
}
 
.element_formulaire, #conteneur_liens_lat_lon {
padding-bottom : 5px;
}
 
.conteneur_carte {
height: 600px;
}
 
.gros_bouton_validation {
height: 50px;
width: 200px;
font-size: 20px;
margin-top: 10x;
}
 
#saisie_liste_evenements {
text-align: center;
}
 
#saisie_liste_evenements tr {
height: 40px;
}
 
 
/* =========================== Fiches ============================== */
 
.element_fiche {
padding-bottom : 5px;
}
 
.intitule_fiche {
}
 
.valeur_fiche {
font-weight: bold;
}
 
.titre_section_fiche {
padding-bottom : 10px;
padding-top : 10px;
}
 
.titre_fiche {
}
 
hr.separation_section {
margin-top: 25px;
}
 
 
/* =========================== Tableaux d'évenements ============================ */
 
tr:nth-child(odd) {
background-color: #CFCFCF ;
}
 
tr:nth-child(1) {
color: #FFFFFF;
}
 
tr:nth-child(1), tr:nth-child(2) {
background-color: #FFFFFF ;
}
 
div.evenement {
text-align: center;
}
 
.stade_evenement {
text-align: center;
}
 
/* =========================== Styles des différents stades ============================ */
 
.stade1 {
background-color: #9AC343;
}
 
.stade2 {
background-color: #F08080;
}
 
.stade3 {
background-color: #8B008B;
}
 
.stade4 {
background-color: #800000;
}
 
.stade5 {
}
 
/* ============ Styles crées par des effets javascripts automatiques ===== */
 
h4.lien_pliage {
text-decoration: underline;
}
 
h4.lien_pliage:hover {
cursor: pointer;
}
/trunk/applications/saisie/squelettes/formulaires/espece_saisie.tpl.html
New file
0,0 → 1,24
<!-- ODS_SAISIE - DEBUT SAISIE ESPECE -->
<div id="saisie_espece">
<h2 class="etape"> Etape 2 : Saisie d'une nouvelle espece </h2>
<form action="" method="post" id="form_saisie_espece">
<div class="element_formulaire">
<select name="espece" id="espece">
<?php foreach($types as $id_type => $type) : ?>
<optgroup id="<?= $type['id_type'] ?>" label="<?= $type['nom'] ?>">
<?php foreach($type['especes'] as $espece) : ?>
<option label="<?= $espece['nom_vernaculaire'] ?>"> <?= $espece['nom_vernaculaire'] ?> </option>
<?php endforeach ?>
</optgroup>
<?php endforeach ?>
</select>
</div>
<div class="element_formulaire">
<img alt="image de l'espece" width="100%" src="http://www.tela-botanica.org/sites/eflore/generique/images/projets/eflore/bdnff/4.02/nt/8523/002543.jpg" />
</div>
<input class="droite gros_bouton_validation" type="submit" value="Suivant" />
</form>
</div>
<!-- ODS_SAISIE - FIN SAISIE ESPECE -->
/trunk/applications/saisie/squelettes/formulaires/station_modification.tpl.html
New file
0,0 → 1,45
<!-- ODS_SAISIE - DEBUT MODIFICATION STATION -->
<script type="text/javascript" src="http://162.38.234.9/obs_saisons/applications/saisie/squelettes/js/saisie.js"></script>
<div id="saisie_station">
<h2 class="etape"> Modification de la station <?= $infos_station['nom'] ?> </h2>
<form class="form_saisie" action="" method="post" id="form_modif_station">
<div class="element_formulaire">
<span class="element_formulaire element_formulaire_en_ligne">
<label for="station_nom">Commune / ville :</label>
<input type="text" name="station_commune" id="station_commune" value="<?= $infos_station['commune']; ?>" />
</span>
<span class="element_formulaire element_formulaire_en_ligne">
<label for="station_nom">Milieu :</label>
<input type="text" name="station_milieu" id="station_milieu" value="<?= $infos_station['milieu']; ?>" /><br />
</span>
</div>
<div class="element_formulaire">
<label for="station_nom">Nom de la station :</label>
<input type="text" name="station_nom" id="station_nom" value="<?= $infos_station['nom']; ?>" /><br />
</div>
<span id="conteneur_lien_cache">
<a id="cacher_afficher_lien" href="#"> Saisir les coordonnées </a>
</span>
<div id="conteneur_liens_lat_lon">
<label for="station_lat">Latitude :</label>
<input type="text" name="station_lat" id="station_lat" value="<?= $infos_station['latitude']; ?>" />
<label for="station_lon">Longitude :</label>
<input type="text" name="station_lon" id="station_lon" value="<?= $infos_station['longitude']; ?>" />
<input type="button" name="localiser_lat_lon" id="localiser_lat_lon" value="localiser" />
</div>
<div class="element_formulaire conteneur_carte">
<div id="map_canvas" style="width:100%; height:100%"></div>
</div>
<input class="droite gros_bouton_validation" type="submit" value="Valider" />
</form>
</div>
<!-- ODS_SAISIE - FIN MODIFICATION STATION -->
/trunk/applications/saisie/squelettes/formulaires/individu_saisie.tpl.html
New file
0,0 → 1,11
<!-- ODS_SAISIE - DEBUT SAISIE INDIVIDU -->
<div id="saisie_station">
<h2> Saisie d'une nouvel individu </h2>
<form action="" id="form_saisie_individu">
<label for="individu_nom">Nom de l'individu :</label>
<input type="text" name="individu_nom" id="individu_nom" /><br />
<input type="submit" value="Valider" />
</form>
</div>
<!-- ODS_SAISIE - FIN SAISIE INDIVIDU -->
/trunk/applications/saisie/squelettes/formulaires/evenement_modification.tpl.html
New file
0,0 → 1,58
<!-- ODS_SAISIE - DEBUT MODIFICATION EVENEMENTS -->
<?php $stades = 0; ?>
<div >
<table id="saisie_modif_evenements">
<tr>
<th></th>
<?php $classe_evenement = 1 ; ?>
<?php foreach($evenements as $evenement) : ?>
<th class="stade<?=$classe_evenement ?>" colspan="<?=count($evenement['stades']) ?>">
<div class="evenement"><?= $evenement['nom'] ?></div>
</th>
<?php $classe_evenement++ ; ?>
<?php endforeach; ?>
<th></th>
</tr>
<tr>
<th> stades </th>
<?php foreach($evenements as $evenement) : ?>
<?php foreach($evenement['stades'] as $stade) : ?>
<th class="stade_evenement">
<?= $stade ?>
</th>
<?php $stades++; ?>
<?php endforeach; ?>
<?php endforeach; ?>
</tr>
 
<?php foreach ($individus as $individu) : ?>
<tr>
<td>
<a href="<?= aControleur::getUrlConsultationFicheIndividu($id_station, $individu['id']); ?>"><?= $individu['nom'] ?></a>
</td>
<?php if($individu['id'] == $id_individu_a_modifier_observation) { ?>
<form method="post" action="<?= aControleur::getUrlConsultationEspeceStation($id_station, $id_espece); ?>">
<?php for( $i = 0; $i < $stades; $i++) : ?>
<td>
<input type="text" maxlength="8" size="8" id="observation_<?= $i ?>" value="<?= array_shift($individu['observations']); ?>" />
</td>
<?php endfor; ?>
<td><input type="submit" value="OK" /></td>
</form>
<?php } else { ?>
<?php for( $i = 0; $i < $stades; $i++) : ?>
<td>
<?= array_shift($individu['observations']); ?>
</td>
<?php endfor; ?>
<td></td>
<?php } ?>
</tr>
<?php endforeach; ?>
</table>
 
<a href="<?= aControleur::getUrlFormulaireAjoutIndividu($id_station,$id_espece); ?>"> Nouvel individu </a>
</div>
<!-- ODS_SAISIE - FIN MODIFICATION EVENEMENTS -->
/trunk/applications/saisie/squelettes/formulaires/station_saisie.tpl.html
New file
0,0 → 1,45
<!-- ODS_SAISIE - DEBUT SAISIE STATION -->
<script type="text/javascript" src="http://162.38.234.9/obs_saisons/applications/saisie/squelettes/js/saisie.js"></script>
<div id="saisie_station">
<h2 class="etape"> Etape 1 : placez votre station </h2>
<form class="form_saisie" action="<?= aControleur::getUrlValidationFormulaireSaisieStation(); ?>" method="post" id="form_saisie_station">
<div class="element_formulaire">
<span class="element_formulaire element_formulaire_en_ligne">
<label for="station_nom">Commune / ville :</label>
<input type="text" name="station_commune" id="station_commune" />
</span>
<span class="element_formulaire element_formulaire_en_ligne">
<label for="station_milieu">Milieu :</label>
<input type="text" name="station_milieu" id="station_milieu" /><br />
</span>
</div>
<div class="element_formulaire">
<label for="station_nom">Nom de la station :</label>
<input type="text" name="station_nom" id="station_nom" /><br />
</div>
<span id="conteneur_lien_cache">
<a id="cacher_afficher_lien" href="#"> Saisir les coordonnées </a>
</span>
<div id="conteneur_liens_lat_lon">
<label for="station_lat">Latitude :</label>
<input type="text" name="station_lat" id="station_lat" />
<label for="station_lon">Longitude :</label>
<input type="text" name="station_lon" id="station_lon" />
<input type="button" name="localiser_lat_lon" id="localiser_lat_lon" value="localiser" />
</div>
<div class="element_formulaire conteneur_carte">
<div id="map_canvas" style="width:100%; height:100%"></div>
</div>
<input class="droite gros_bouton_validation" type="submit" value="Suivant" />
</form>
</div>
<!-- ODS_SAISIE - FIN SAISIE STATION -->
/trunk/applications/saisie/squelettes/js/googlemaps.js
--- trunk/applications/saisie/squelettes/js/saisie.js (revision 0)
+++ trunk/applications/saisie/squelettes/js/saisie.js (revision 31)
@@ -0,0 +1,85 @@
+var map;
+var marker;
+
+function ajouterListenerFormulaireSaisieLatLon() {
+
+ $('#cacher_afficher_lien').bind('click', function() {
+ $('#conteneur_liens_lat_lon').slideToggle();
+ return false;
+ });
+
+ /*$('#station_lat').bind('blur', function() {
+ verifierEtLocaliserCoordonnees();
+ });*/
+
+ /*$('#station_lon').bind('blur', function() {
+ verifierEtLocaliserCoordonnees();
+ });*/
+
+ $('#localiser_lat_lon').click(function() {
+ verifierEtLocaliserCoordonnees();
+ });
+
+ $('#conteneur_liens_lat_lon').hide();
+}
+
+function verifierEtLocaliserCoordonnees() {
+
+ var lat = $('#station_lat').val();
+ var lon = $('#station_lon').val();
+
+ if(!isNaN(lat) && lat.length > 0 && !isNaN(lon) && lon.length > 0) {
+
+ } else {
+ window.alert("coordonnées invalides");
+ }
+
+ var positionMarker = new google.maps.LatLng(lat, lon);
+
+ marker.setPosition(positionMarker);
+ map.setCenter(positionMarker);
+}
+
+function mettreAJourValeursFormulaire(latlon) {
+
+ latlon = latlon.toString().split(',');
+
+ $('#station_lat').val(latlon[0].replace('(', ''));
+ $('#station_lon').val(latlon[1].replace(')', ''));
+}
+
+function initialiserCarte() {
+
+ var latlng = new google.maps.LatLng(47.0504, 2.2347);
+ var myOptions = {
+ zoom: 6,
+ center: latlng,
+ mapTypeId: google.maps.MapTypeId.HYBRID
+ };
+
+ map = new google.maps.Map(document.getElementById("map_canvas"),
+ myOptions);
+
+ marker = new google.maps.Marker({
+ position: latlng,
+ title:""
+ });
+
+ marker.setDraggable(true);
+ marker.setClickable(true);
+
+ google.maps.event.addListener(marker, 'dragend', function() {
+ mettreAJourValeursFormulaire(marker.getPosition())
+ });
+
+ // To add the marker to the map, call setMap();
+ marker.setMap(map);
+}
+
+$(document).ready(function() {
+ initialiserCarte();
+});
+
+$('#conteneur_liens_lat_lon').ready(function() {
+ ajouterListenerFormulaireSaisieLatLon();
+});
/trunk/applications/saisie/squelettes/js/commun.js
New file
0,0 → 1,31
function initialiserElementsPliables() {
 
$('.pliage h4').addClass('lien_pliage');
 
$('.pliage ul').hide();
$('.pliage > ul:first-child').hide();
 
$('.pliage').bind('click', function() {
$(this).children('ul').slideToggle();
return false;
});
}
 
function initialiserLignesCliquables() {
$('.observations_individu').bind('click', function() {
//$(this).children('td a').click();
window.alert($(this).children('td').attr('href'));
//window.location = $(this).children('td a').attr('href');
return false;
});
}
 
$(document).ready(function() {
initialiserElementsPliables();
initialiserLignesCliquables();
});
/trunk/applications/saisie/squelettes/js/fiche.js
New file
0,0 → 1,51
var map;
var marker;
 
function verifierEtLocaliserCoordonnees() {
var lat = $('#station_lat').html();
var lon = $('#station_lon').html();
if(!isNaN(lat) && lat.length > 0 && !isNaN(lon) && lon.length > 0) {
} else {
//window.alert("coordonnées invalides");
}
var positionMarker = new google.maps.LatLng(lat, lon);
marker.setPosition(positionMarker);
map.setCenter(positionMarker);
}
 
function initialiserCarte() {
 
var latlng = new google.maps.LatLng(47.0504, 2.2347);
var myOptions = {
zoom: 6,
center: latlng,
mapTypeId: google.maps.MapTypeId.HYBRID
};
 
map = new google.maps.Map(document.getElementById("map_canvas"),
myOptions);
marker = new google.maps.Marker({
position: latlng,
title:""
});
marker.setDraggable(false);
marker.setClickable(true);
// To add the marker to the map, call setMap();
marker.setMap(map);
}
 
$(document).ready(function() {
initialiserCarte();
});
 
$('#conteneur_liens_lat_lon').ready(function() {
verifierEtLocaliserCoordonnees();
});
/trunk/applications/saisie/squelettes/js/jquery-1.4.2.min.js
New file
0,0 → 1,6240
/*!
* jQuery JavaScript Library v1.4.2
* http://jquery.com/
*
* Copyright 2010, John Resig
* Dual licensed under the MIT or GPL Version 2 licenses.
* http://jquery.org/license
*
* Includes Sizzle.js
* http://sizzlejs.com/
* Copyright 2010, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses.
*
* Date: Sat Feb 13 22:33:48 2010 -0500
*/
(function( window, undefined ) {
 
// Define a local copy of jQuery
var jQuery = function( selector, context ) {
// The jQuery object is actually just the init constructor 'enhanced'
return new jQuery.fn.init( selector, context );
},
 
// Map over jQuery in case of overwrite
_jQuery = window.jQuery,
 
// Map over the $ in case of overwrite
_$ = window.$,
 
// Use the correct document accordingly with window argument (sandbox)
document = window.document,
 
// A central reference to the root jQuery(document)
rootjQuery,
 
// A simple way to check for HTML strings or ID strings
// (both of which we optimize for)
quickExpr = /^[^<]*(<[\w\W]+>)[^>]*$|^#([\w-]+)$/,
 
// Is it a simple selector
isSimple = /^.[^:#\[\.,]*$/,
 
// Check if a string has a non-whitespace character in it
rnotwhite = /\S/,
 
// Used for trimming whitespace
rtrim = /^(\s|\u00A0)+|(\s|\u00A0)+$/g,
 
// Match a standalone tag
rsingleTag = /^<(\w+)\s*\/?>(?:<\/\1>)?$/,
 
// Keep a UserAgent string for use with jQuery.browser
userAgent = navigator.userAgent,
 
// For matching the engine and version of the browser
browserMatch,
// Has the ready events already been bound?
readyBound = false,
// The functions to execute on DOM ready
readyList = [],
 
// The ready event handler
DOMContentLoaded,
 
// Save a reference to some core methods
toString = Object.prototype.toString,
hasOwnProperty = Object.prototype.hasOwnProperty,
push = Array.prototype.push,
slice = Array.prototype.slice,
indexOf = Array.prototype.indexOf;
 
jQuery.fn = jQuery.prototype = {
init: function( selector, context ) {
var match, elem, ret, doc;
 
// Handle $(""), $(null), or $(undefined)
if ( !selector ) {
return this;
}
 
// Handle $(DOMElement)
if ( selector.nodeType ) {
this.context = this[0] = selector;
this.length = 1;
return this;
}
// The body element only exists once, optimize finding it
if ( selector === "body" && !context ) {
this.context = document;
this[0] = document.body;
this.selector = "body";
this.length = 1;
return this;
}
 
// Handle HTML strings
if ( typeof selector === "string" ) {
// Are we dealing with HTML string or an ID?
match = quickExpr.exec( selector );
 
// Verify a match, and that no context was specified for #id
if ( match && (match[1] || !context) ) {
 
// HANDLE: $(html) -> $(array)
if ( match[1] ) {
doc = (context ? context.ownerDocument || context : document);
 
// If a single string is passed in and it's a single tag
// just do a createElement and skip the rest
ret = rsingleTag.exec( selector );
 
if ( ret ) {
if ( jQuery.isPlainObject( context ) ) {
selector = [ document.createElement( ret[1] ) ];
jQuery.fn.attr.call( selector, context, true );
 
} else {
selector = [ doc.createElement( ret[1] ) ];
}
 
} else {
ret = buildFragment( [ match[1] ], [ doc ] );
selector = (ret.cacheable ? ret.fragment.cloneNode(true) : ret.fragment).childNodes;
}
return jQuery.merge( this, selector );
// HANDLE: $("#id")
} else {
elem = document.getElementById( match[2] );
 
if ( elem ) {
// Handle the case where IE and Opera return items
// by name instead of ID
if ( elem.id !== match[2] ) {
return rootjQuery.find( selector );
}
 
// Otherwise, we inject the element directly into the jQuery object
this.length = 1;
this[0] = elem;
}
 
this.context = document;
this.selector = selector;
return this;
}
 
// HANDLE: $("TAG")
} else if ( !context && /^\w+$/.test( selector ) ) {
this.selector = selector;
this.context = document;
selector = document.getElementsByTagName( selector );
return jQuery.merge( this, selector );
 
// HANDLE: $(expr, $(...))
} else if ( !context || context.jquery ) {
return (context || rootjQuery).find( selector );
 
// HANDLE: $(expr, context)
// (which is just equivalent to: $(context).find(expr)
} else {
return jQuery( context ).find( selector );
}
 
// HANDLE: $(function)
// Shortcut for document ready
} else if ( jQuery.isFunction( selector ) ) {
return rootjQuery.ready( selector );
}
 
if (selector.selector !== undefined) {
this.selector = selector.selector;
this.context = selector.context;
}
 
return jQuery.makeArray( selector, this );
},
 
// Start with an empty selector
selector: "",
 
// The current version of jQuery being used
jquery: "1.4.2",
 
// The default length of a jQuery object is 0
length: 0,
 
// The number of elements contained in the matched element set
size: function() {
return this.length;
},
 
toArray: function() {
return slice.call( this, 0 );
},
 
// Get the Nth element in the matched element set OR
// Get the whole matched element set as a clean array
get: function( num ) {
return num == null ?
 
// Return a 'clean' array
this.toArray() :
 
// Return just the object
( num < 0 ? this.slice(num)[ 0 ] : this[ num ] );
},
 
// Take an array of elements and push it onto the stack
// (returning the new matched element set)
pushStack: function( elems, name, selector ) {
// Build a new jQuery matched element set
var ret = jQuery();
 
if ( jQuery.isArray( elems ) ) {
push.apply( ret, elems );
} else {
jQuery.merge( ret, elems );
}
 
// Add the old object onto the stack (as a reference)
ret.prevObject = this;
 
ret.context = this.context;
 
if ( name === "find" ) {
ret.selector = this.selector + (this.selector ? " " : "") + selector;
} else if ( name ) {
ret.selector = this.selector + "." + name + "(" + selector + ")";
}
 
// Return the newly-formed element set
return ret;
},
 
// Execute a callback for every element in the matched set.
// (You can seed the arguments with an array of args, but this is
// only used internally.)
each: function( callback, args ) {
return jQuery.each( this, callback, args );
},
ready: function( fn ) {
// Attach the listeners
jQuery.bindReady();
 
// If the DOM is already ready
if ( jQuery.isReady ) {
// Execute the function immediately
fn.call( document, jQuery );
 
// Otherwise, remember the function for later
} else if ( readyList ) {
// Add the function to the wait list
readyList.push( fn );
}
 
return this;
},
eq: function( i ) {
return i === -1 ?
this.slice( i ) :
this.slice( i, +i + 1 );
},
 
first: function() {
return this.eq( 0 );
},
 
last: function() {
return this.eq( -1 );
},
 
slice: function() {
return this.pushStack( slice.apply( this, arguments ),
"slice", slice.call(arguments).join(",") );
},
 
map: function( callback ) {
return this.pushStack( jQuery.map(this, function( elem, i ) {
return callback.call( elem, i, elem );
}));
},
end: function() {
return this.prevObject || jQuery(null);
},
 
// For internal use only.
// Behaves like an Array's method, not like a jQuery method.
push: push,
sort: [].sort,
splice: [].splice
};
 
// Give the init function the jQuery prototype for later instantiation
jQuery.fn.init.prototype = jQuery.fn;
 
jQuery.extend = jQuery.fn.extend = function() {
// copy reference to target object
var target = arguments[0] || {}, i = 1, length = arguments.length, deep = false, options, name, src, copy;
 
// Handle a deep copy situation
if ( typeof target === "boolean" ) {
deep = target;
target = arguments[1] || {};
// skip the boolean and the target
i = 2;
}
 
// Handle case when target is a string or something (possible in deep copy)
if ( typeof target !== "object" && !jQuery.isFunction(target) ) {
target = {};
}
 
// extend jQuery itself if only one argument is passed
if ( length === i ) {
target = this;
--i;
}
 
for ( ; i < length; i++ ) {
// Only deal with non-null/undefined values
if ( (options = arguments[ i ]) != null ) {
// Extend the base object
for ( name in options ) {
src = target[ name ];
copy = options[ name ];
 
// Prevent never-ending loop
if ( target === copy ) {
continue;
}
 
// Recurse if we're merging object literal values or arrays
if ( deep && copy && ( jQuery.isPlainObject(copy) || jQuery.isArray(copy) ) ) {
var clone = src && ( jQuery.isPlainObject(src) || jQuery.isArray(src) ) ? src
: jQuery.isArray(copy) ? [] : {};
 
// Never move original objects, clone them
target[ name ] = jQuery.extend( deep, clone, copy );
 
// Don't bring in undefined values
} else if ( copy !== undefined ) {
target[ name ] = copy;
}
}
}
}
 
// Return the modified object
return target;
};
 
jQuery.extend({
noConflict: function( deep ) {
window.$ = _$;
 
if ( deep ) {
window.jQuery = _jQuery;
}
 
return jQuery;
},
// Is the DOM ready to be used? Set to true once it occurs.
isReady: false,
// Handle when the DOM is ready
ready: function() {
// Make sure that the DOM is not already loaded
if ( !jQuery.isReady ) {
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
if ( !document.body ) {
return setTimeout( jQuery.ready, 13 );
}
 
// Remember that the DOM is ready
jQuery.isReady = true;
 
// If there are functions bound, to execute
if ( readyList ) {
// Execute all of them
var fn, i = 0;
while ( (fn = readyList[ i++ ]) ) {
fn.call( document, jQuery );
}
 
// Reset the list of functions
readyList = null;
}
 
// Trigger any bound ready events
if ( jQuery.fn.triggerHandler ) {
jQuery( document ).triggerHandler( "ready" );
}
}
},
bindReady: function() {
if ( readyBound ) {
return;
}
 
readyBound = true;
 
// Catch cases where $(document).ready() is called after the
// browser event has already occurred.
if ( document.readyState === "complete" ) {
return jQuery.ready();
}
 
// Mozilla, Opera and webkit nightlies currently support this event
if ( document.addEventListener ) {
// Use the handy event callback
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
// A fallback to window.onload, that will always work
window.addEventListener( "load", jQuery.ready, false );
 
// If IE event model is used
} else if ( document.attachEvent ) {
// ensure firing before onload,
// maybe late but safe also for iframes
document.attachEvent("onreadystatechange", DOMContentLoaded);
// A fallback to window.onload, that will always work
window.attachEvent( "onload", jQuery.ready );
 
// If IE and not a frame
// continually check to see if the document is ready
var toplevel = false;
 
try {
toplevel = window.frameElement == null;
} catch(e) {}
 
if ( document.documentElement.doScroll && toplevel ) {
doScrollCheck();
}
}
},
 
// See test/unit/core.js for details concerning isFunction.
// Since version 1.3, DOM methods and functions like alert
// aren't supported. They return false on IE (#2968).
isFunction: function( obj ) {
return toString.call(obj) === "[object Function]";
},
 
isArray: function( obj ) {
return toString.call(obj) === "[object Array]";
},
 
isPlainObject: function( obj ) {
// Must be an Object.
// Because of IE, we also have to check the presence of the constructor property.
// Make sure that DOM nodes and window objects don't pass through, as well
if ( !obj || toString.call(obj) !== "[object Object]" || obj.nodeType || obj.setInterval ) {
return false;
}
// Not own constructor property must be Object
if ( obj.constructor
&& !hasOwnProperty.call(obj, "constructor")
&& !hasOwnProperty.call(obj.constructor.prototype, "isPrototypeOf") ) {
return false;
}
// Own properties are enumerated firstly, so to speed up,
// if last one is own, then all properties are own.
var key;
for ( key in obj ) {}
return key === undefined || hasOwnProperty.call( obj, key );
},
 
isEmptyObject: function( obj ) {
for ( var name in obj ) {
return false;
}
return true;
},
error: function( msg ) {
throw msg;
},
parseJSON: function( data ) {
if ( typeof data !== "string" || !data ) {
return null;
}
 
// Make sure leading/trailing whitespace is removed (IE can't handle it)
data = jQuery.trim( data );
// Make sure the incoming data is actual JSON
// Logic borrowed from http://json.org/json2.js
if ( /^[\],:{}\s]*$/.test(data.replace(/\\(?:["\\\/bfnrt]|u[0-9a-fA-F]{4})/g, "@")
.replace(/"[^"\\\n\r]*"|true|false|null|-?\d+(?:\.\d*)?(?:[eE][+\-]?\d+)?/g, "]")
.replace(/(?:^|:|,)(?:\s*\[)+/g, "")) ) {
 
// Try to use the native JSON parser first
return window.JSON && window.JSON.parse ?
window.JSON.parse( data ) :
(new Function("return " + data))();
 
} else {
jQuery.error( "Invalid JSON: " + data );
}
},
 
noop: function() {},
 
// Evalulates a script in a global context
globalEval: function( data ) {
if ( data && rnotwhite.test(data) ) {
// Inspired by code by Andrea Giammarchi
// http://webreflection.blogspot.com/2007/08/global-scope-evaluation-and-dom.html
var head = document.getElementsByTagName("head")[0] || document.documentElement,
script = document.createElement("script");
 
script.type = "text/javascript";
 
if ( jQuery.support.scriptEval ) {
script.appendChild( document.createTextNode( data ) );
} else {
script.text = data;
}
 
// Use insertBefore instead of appendChild to circumvent an IE6 bug.
// This arises when a base node is used (#2709).
head.insertBefore( script, head.firstChild );
head.removeChild( script );
}
},
 
nodeName: function( elem, name ) {
return elem.nodeName && elem.nodeName.toUpperCase() === name.toUpperCase();
},
 
// args is for internal usage only
each: function( object, callback, args ) {
var name, i = 0,
length = object.length,
isObj = length === undefined || jQuery.isFunction(object);
 
if ( args ) {
if ( isObj ) {
for ( name in object ) {
if ( callback.apply( object[ name ], args ) === false ) {
break;
}
}
} else {
for ( ; i < length; ) {
if ( callback.apply( object[ i++ ], args ) === false ) {
break;
}
}
}
 
// A special, fast, case for the most common use of each
} else {
if ( isObj ) {
for ( name in object ) {
if ( callback.call( object[ name ], name, object[ name ] ) === false ) {
break;
}
}
} else {
for ( var value = object[0];
i < length && callback.call( value, i, value ) !== false; value = object[++i] ) {}
}
}
 
return object;
},
 
trim: function( text ) {
return (text || "").replace( rtrim, "" );
},
 
// results is for internal usage only
makeArray: function( array, results ) {
var ret = results || [];
 
if ( array != null ) {
// The window, strings (and functions) also have 'length'
// The extra typeof function check is to prevent crashes
// in Safari 2 (See: #3039)
if ( array.length == null || typeof array === "string" || jQuery.isFunction(array) || (typeof array !== "function" && array.setInterval) ) {
push.call( ret, array );
} else {
jQuery.merge( ret, array );
}
}
 
return ret;
},
 
inArray: function( elem, array ) {
if ( array.indexOf ) {
return array.indexOf( elem );
}
 
for ( var i = 0, length = array.length; i < length; i++ ) {
if ( array[ i ] === elem ) {
return i;
}
}
 
return -1;
},
 
merge: function( first, second ) {
var i = first.length, j = 0;
 
if ( typeof second.length === "number" ) {
for ( var l = second.length; j < l; j++ ) {
first[ i++ ] = second[ j ];
}
} else {
while ( second[j] !== undefined ) {
first[ i++ ] = second[ j++ ];
}
}
 
first.length = i;
 
return first;
},
 
grep: function( elems, callback, inv ) {
var ret = [];
 
// Go through the array, only saving the items
// that pass the validator function
for ( var i = 0, length = elems.length; i < length; i++ ) {
if ( !inv !== !callback( elems[ i ], i ) ) {
ret.push( elems[ i ] );
}
}
 
return ret;
},
 
// arg is for internal usage only
map: function( elems, callback, arg ) {
var ret = [], value;
 
// Go through the array, translating each of the items to their
// new value (or values).
for ( var i = 0, length = elems.length; i < length; i++ ) {
value = callback( elems[ i ], i, arg );
 
if ( value != null ) {
ret[ ret.length ] = value;
}
}
 
return ret.concat.apply( [], ret );
},
 
// A global GUID counter for objects
guid: 1,
 
proxy: function( fn, proxy, thisObject ) {
if ( arguments.length === 2 ) {
if ( typeof proxy === "string" ) {
thisObject = fn;
fn = thisObject[ proxy ];
proxy = undefined;
 
} else if ( proxy && !jQuery.isFunction( proxy ) ) {
thisObject = proxy;
proxy = undefined;
}
}
 
if ( !proxy && fn ) {
proxy = function() {
return fn.apply( thisObject || this, arguments );
};
}
 
// Set the guid of unique handler to the same of original handler, so it can be removed
if ( fn ) {
proxy.guid = fn.guid = fn.guid || proxy.guid || jQuery.guid++;
}
 
// So proxy can be declared as an argument
return proxy;
},
 
// Use of jQuery.browser is frowned upon.
// More details: http://docs.jquery.com/Utilities/jQuery.browser
uaMatch: function( ua ) {
ua = ua.toLowerCase();
 
var match = /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
/(opera)(?:.*version)?[ \/]([\w.]+)/.exec( ua ) ||
/(msie) ([\w.]+)/.exec( ua ) ||
!/compatible/.test( ua ) && /(mozilla)(?:.*? rv:([\w.]+))?/.exec( ua ) ||
[];
 
return { browser: match[1] || "", version: match[2] || "0" };
},
 
browser: {}
});
 
browserMatch = jQuery.uaMatch( userAgent );
if ( browserMatch.browser ) {
jQuery.browser[ browserMatch.browser ] = true;
jQuery.browser.version = browserMatch.version;
}
 
// Deprecated, use jQuery.browser.webkit instead
if ( jQuery.browser.webkit ) {
jQuery.browser.safari = true;
}
 
if ( indexOf ) {
jQuery.inArray = function( elem, array ) {
return indexOf.call( array, elem );
};
}
 
// All jQuery objects should point back to these
rootjQuery = jQuery(document);
 
// Cleanup functions for the document ready method
if ( document.addEventListener ) {
DOMContentLoaded = function() {
document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
jQuery.ready();
};
 
} else if ( document.attachEvent ) {
DOMContentLoaded = function() {
// Make sure body exists, at least, in case IE gets a little overzealous (ticket #5443).
if ( document.readyState === "complete" ) {
document.detachEvent( "onreadystatechange", DOMContentLoaded );
jQuery.ready();
}
};
}
 
// The DOM ready check for Internet Explorer
function doScrollCheck() {
if ( jQuery.isReady ) {
return;
}
 
try {
// If IE is used, use the trick by Diego Perini
// http://javascript.nwbox.com/IEContentLoaded/
document.documentElement.doScroll("left");
} catch( error ) {
setTimeout( doScrollCheck, 1 );
return;
}
 
// and execute any waiting functions
jQuery.ready();
}
 
function evalScript( i, elem ) {
if ( elem.src ) {
jQuery.ajax({
url: elem.src,
async: false,
dataType: "script"
});
} else {
jQuery.globalEval( elem.text || elem.textContent || elem.innerHTML || "" );
}
 
if ( elem.parentNode ) {
elem.parentNode.removeChild( elem );
}
}
 
// Mutifunctional method to get and set values to a collection
// The value/s can be optionally by executed if its a function
function access( elems, key, value, exec, fn, pass ) {
var length = elems.length;
// Setting many attributes
if ( typeof key === "object" ) {
for ( var k in key ) {
access( elems, k, key[k], exec, fn, value );
}
return elems;
}
// Setting one attribute
if ( value !== undefined ) {
// Optionally, function values get executed if exec is true
exec = !pass && exec && jQuery.isFunction(value);
for ( var i = 0; i < length; i++ ) {
fn( elems[i], key, exec ? value.call( elems[i], i, fn( elems[i], key ) ) : value, pass );
}
return elems;
}
// Getting an attribute
return length ? fn( elems[0], key ) : undefined;
}
 
function now() {
return (new Date).getTime();
}
(function() {
 
jQuery.support = {};
 
var root = document.documentElement,
script = document.createElement("script"),
div = document.createElement("div"),
id = "script" + now();
 
div.style.display = "none";
div.innerHTML = " <link/><table></table><a href='/a' style='color:red;float:left;opacity:.55;'>a</a><input type='checkbox'/>";
 
var all = div.getElementsByTagName("*"),
a = div.getElementsByTagName("a")[0];
 
// Can't get basic test support
if ( !all || !all.length || !a ) {
return;
}
 
jQuery.support = {
// IE strips leading whitespace when .innerHTML is used
leadingWhitespace: div.firstChild.nodeType === 3,
 
// Make sure that tbody elements aren't automatically inserted
// IE will insert them into empty tables
tbody: !div.getElementsByTagName("tbody").length,
 
// Make sure that link elements get serialized correctly by innerHTML
// This requires a wrapper element in IE
htmlSerialize: !!div.getElementsByTagName("link").length,
 
// Get the style information from getAttribute
// (IE uses .cssText insted)
style: /red/.test( a.getAttribute("style") ),
 
// Make sure that URLs aren't manipulated
// (IE normalizes it by default)
hrefNormalized: a.getAttribute("href") === "/a",
 
// Make sure that element opacity exists
// (IE uses filter instead)
// Use a regex to work around a WebKit issue. See #5145
opacity: /^0.55$/.test( a.style.opacity ),
 
// Verify style float existence
// (IE uses styleFloat instead of cssFloat)
cssFloat: !!a.style.cssFloat,
 
// Make sure that if no value is specified for a checkbox
// that it defaults to "on".
// (WebKit defaults to "" instead)
checkOn: div.getElementsByTagName("input")[0].value === "on",
 
// Make sure that a selected-by-default option has a working selected property.
// (WebKit defaults to false instead of true, IE too, if it's in an optgroup)
optSelected: document.createElement("select").appendChild( document.createElement("option") ).selected,
 
parentNode: div.removeChild( div.appendChild( document.createElement("div") ) ).parentNode === null,
 
// Will be defined later
deleteExpando: true,
checkClone: false,
scriptEval: false,
noCloneEvent: true,
boxModel: null
};
 
script.type = "text/javascript";
try {
script.appendChild( document.createTextNode( "window." + id + "=1;" ) );
} catch(e) {}
 
root.insertBefore( script, root.firstChild );
 
// Make sure that the execution of code works by injecting a script
// tag with appendChild/createTextNode
// (IE doesn't support this, fails, and uses .text instead)
if ( window[ id ] ) {
jQuery.support.scriptEval = true;
delete window[ id ];
}
 
// Test to see if it's possible to delete an expando from an element
// Fails in Internet Explorer
try {
delete script.test;
} catch(e) {
jQuery.support.deleteExpando = false;
}
 
root.removeChild( script );
 
if ( div.attachEvent && div.fireEvent ) {
div.attachEvent("onclick", function click() {
// Cloning a node shouldn't copy over any
// bound event handlers (IE does this)
jQuery.support.noCloneEvent = false;
div.detachEvent("onclick", click);
});
div.cloneNode(true).fireEvent("onclick");
}
 
div = document.createElement("div");
div.innerHTML = "<input type='radio' name='radiotest' checked='checked'/>";
 
var fragment = document.createDocumentFragment();
fragment.appendChild( div.firstChild );
 
// WebKit doesn't clone checked state correctly in fragments
jQuery.support.checkClone = fragment.cloneNode(true).cloneNode(true).lastChild.checked;
 
// Figure out if the W3C box model works as expected
// document.body must exist before we can do this
jQuery(function() {
var div = document.createElement("div");
div.style.width = div.style.paddingLeft = "1px";
 
document.body.appendChild( div );
jQuery.boxModel = jQuery.support.boxModel = div.offsetWidth === 2;
document.body.removeChild( div ).style.display = 'none';
 
div = null;
});
 
// Technique from Juriy Zaytsev
// http://thinkweb2.com/projects/prototype/detecting-event-support-without-browser-sniffing/
var eventSupported = function( eventName ) {
var el = document.createElement("div");
eventName = "on" + eventName;
 
var isSupported = (eventName in el);
if ( !isSupported ) {
el.setAttribute(eventName, "return;");
isSupported = typeof el[eventName] === "function";
}
el = null;
 
return isSupported;
};
jQuery.support.submitBubbles = eventSupported("submit");
jQuery.support.changeBubbles = eventSupported("change");
 
// release memory in IE
root = script = div = all = a = null;
})();
 
jQuery.props = {
"for": "htmlFor",
"class": "className",
readonly: "readOnly",
maxlength: "maxLength",
cellspacing: "cellSpacing",
rowspan: "rowSpan",
colspan: "colSpan",
tabindex: "tabIndex",
usemap: "useMap",
frameborder: "frameBorder"
};
var expando = "jQuery" + now(), uuid = 0, windowData = {};
 
jQuery.extend({
cache: {},
expando:expando,
 
// The following elements throw uncatchable exceptions if you
// attempt to add expando properties to them.
noData: {
"embed": true,
"object": true,
"applet": true
},
 
data: function( elem, name, data ) {
if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
return;
}
 
elem = elem == window ?
windowData :
elem;
 
var id = elem[ expando ], cache = jQuery.cache, thisCache;
 
if ( !id && typeof name === "string" && data === undefined ) {
return null;
}
 
// Compute a unique ID for the element
if ( !id ) {
id = ++uuid;
}
 
// Avoid generating a new cache unless none exists and we
// want to manipulate it.
if ( typeof name === "object" ) {
elem[ expando ] = id;
thisCache = cache[ id ] = jQuery.extend(true, {}, name);
 
} else if ( !cache[ id ] ) {
elem[ expando ] = id;
cache[ id ] = {};
}
 
thisCache = cache[ id ];
 
// Prevent overriding the named cache with undefined values
if ( data !== undefined ) {
thisCache[ name ] = data;
}
 
return typeof name === "string" ? thisCache[ name ] : thisCache;
},
 
removeData: function( elem, name ) {
if ( elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()] ) {
return;
}
 
elem = elem == window ?
windowData :
elem;
 
var id = elem[ expando ], cache = jQuery.cache, thisCache = cache[ id ];
 
// If we want to remove a specific section of the element's data
if ( name ) {
if ( thisCache ) {
// Remove the section of cache data
delete thisCache[ name ];
 
// If we've removed all the data, remove the element's cache
if ( jQuery.isEmptyObject(thisCache) ) {
jQuery.removeData( elem );
}
}
 
// Otherwise, we want to remove all of the element's data
} else {
if ( jQuery.support.deleteExpando ) {
delete elem[ jQuery.expando ];
 
} else if ( elem.removeAttribute ) {
elem.removeAttribute( jQuery.expando );
}
 
// Completely remove the data cache
delete cache[ id ];
}
}
});
 
jQuery.fn.extend({
data: function( key, value ) {
if ( typeof key === "undefined" && this.length ) {
return jQuery.data( this[0] );
 
} else if ( typeof key === "object" ) {
return this.each(function() {
jQuery.data( this, key );
});
}
 
var parts = key.split(".");
parts[1] = parts[1] ? "." + parts[1] : "";
 
if ( value === undefined ) {
var data = this.triggerHandler("getData" + parts[1] + "!", [parts[0]]);
 
if ( data === undefined && this.length ) {
data = jQuery.data( this[0], key );
}
return data === undefined && parts[1] ?
this.data( parts[0] ) :
data;
} else {
return this.trigger("setData" + parts[1] + "!", [parts[0], value]).each(function() {
jQuery.data( this, key, value );
});
}
},
 
removeData: function( key ) {
return this.each(function() {
jQuery.removeData( this, key );
});
}
});
jQuery.extend({
queue: function( elem, type, data ) {
if ( !elem ) {
return;
}
 
type = (type || "fx") + "queue";
var q = jQuery.data( elem, type );
 
// Speed up dequeue by getting out quickly if this is just a lookup
if ( !data ) {
return q || [];
}
 
if ( !q || jQuery.isArray(data) ) {
q = jQuery.data( elem, type, jQuery.makeArray(data) );
 
} else {
q.push( data );
}
 
return q;
},
 
dequeue: function( elem, type ) {
type = type || "fx";
 
var queue = jQuery.queue( elem, type ), fn = queue.shift();
 
// If the fx queue is dequeued, always remove the progress sentinel
if ( fn === "inprogress" ) {
fn = queue.shift();
}
 
if ( fn ) {
// Add a progress sentinel to prevent the fx queue from being
// automatically dequeued
if ( type === "fx" ) {
queue.unshift("inprogress");
}
 
fn.call(elem, function() {
jQuery.dequeue(elem, type);
});
}
}
});
 
jQuery.fn.extend({
queue: function( type, data ) {
if ( typeof type !== "string" ) {
data = type;
type = "fx";
}
 
if ( data === undefined ) {
return jQuery.queue( this[0], type );
}
return this.each(function( i, elem ) {
var queue = jQuery.queue( this, type, data );
 
if ( type === "fx" && queue[0] !== "inprogress" ) {
jQuery.dequeue( this, type );
}
});
},
dequeue: function( type ) {
return this.each(function() {
jQuery.dequeue( this, type );
});
},
 
// Based off of the plugin by Clint Helfers, with permission.
// http://blindsignals.com/index.php/2009/07/jquery-delay/
delay: function( time, type ) {
time = jQuery.fx ? jQuery.fx.speeds[time] || time : time;
type = type || "fx";
 
return this.queue( type, function() {
var elem = this;
setTimeout(function() {
jQuery.dequeue( elem, type );
}, time );
});
},
 
clearQueue: function( type ) {
return this.queue( type || "fx", [] );
}
});
var rclass = /[\n\t]/g,
rspace = /\s+/,
rreturn = /\r/g,
rspecialurl = /href|src|style/,
rtype = /(button|input)/i,
rfocusable = /(button|input|object|select|textarea)/i,
rclickable = /^(a|area)$/i,
rradiocheck = /radio|checkbox/;
 
jQuery.fn.extend({
attr: function( name, value ) {
return access( this, name, value, true, jQuery.attr );
},
 
removeAttr: function( name, fn ) {
return this.each(function(){
jQuery.attr( this, name, "" );
if ( this.nodeType === 1 ) {
this.removeAttribute( name );
}
});
},
 
addClass: function( value ) {
if ( jQuery.isFunction(value) ) {
return this.each(function(i) {
var self = jQuery(this);
self.addClass( value.call(this, i, self.attr("class")) );
});
}
 
if ( value && typeof value === "string" ) {
var classNames = (value || "").split( rspace );
 
for ( var i = 0, l = this.length; i < l; i++ ) {
var elem = this[i];
 
if ( elem.nodeType === 1 ) {
if ( !elem.className ) {
elem.className = value;
 
} else {
var className = " " + elem.className + " ", setClass = elem.className;
for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
if ( className.indexOf( " " + classNames[c] + " " ) < 0 ) {
setClass += " " + classNames[c];
}
}
elem.className = jQuery.trim( setClass );
}
}
}
}
 
return this;
},
 
removeClass: function( value ) {
if ( jQuery.isFunction(value) ) {
return this.each(function(i) {
var self = jQuery(this);
self.removeClass( value.call(this, i, self.attr("class")) );
});
}
 
if ( (value && typeof value === "string") || value === undefined ) {
var classNames = (value || "").split(rspace);
 
for ( var i = 0, l = this.length; i < l; i++ ) {
var elem = this[i];
 
if ( elem.nodeType === 1 && elem.className ) {
if ( value ) {
var className = (" " + elem.className + " ").replace(rclass, " ");
for ( var c = 0, cl = classNames.length; c < cl; c++ ) {
className = className.replace(" " + classNames[c] + " ", " ");
}
elem.className = jQuery.trim( className );
 
} else {
elem.className = "";
}
}
}
}
 
return this;
},
 
toggleClass: function( value, stateVal ) {
var type = typeof value, isBool = typeof stateVal === "boolean";
 
if ( jQuery.isFunction( value ) ) {
return this.each(function(i) {
var self = jQuery(this);
self.toggleClass( value.call(this, i, self.attr("class"), stateVal), stateVal );
});
}
 
return this.each(function() {
if ( type === "string" ) {
// toggle individual class names
var className, i = 0, self = jQuery(this),
state = stateVal,
classNames = value.split( rspace );
 
while ( (className = classNames[ i++ ]) ) {
// check each className given, space seperated list
state = isBool ? state : !self.hasClass( className );
self[ state ? "addClass" : "removeClass" ]( className );
}
 
} else if ( type === "undefined" || type === "boolean" ) {
if ( this.className ) {
// store className if set
jQuery.data( this, "__className__", this.className );
}
 
// toggle whole className
this.className = this.className || value === false ? "" : jQuery.data( this, "__className__" ) || "";
}
});
},
 
hasClass: function( selector ) {
var className = " " + selector + " ";
for ( var i = 0, l = this.length; i < l; i++ ) {
if ( (" " + this[i].className + " ").replace(rclass, " ").indexOf( className ) > -1 ) {
return true;
}
}
 
return false;
},
 
val: function( value ) {
if ( value === undefined ) {
var elem = this[0];
 
if ( elem ) {
if ( jQuery.nodeName( elem, "option" ) ) {
return (elem.attributes.value || {}).specified ? elem.value : elem.text;
}
 
// We need to handle select boxes special
if ( jQuery.nodeName( elem, "select" ) ) {
var index = elem.selectedIndex,
values = [],
options = elem.options,
one = elem.type === "select-one";
 
// Nothing was selected
if ( index < 0 ) {
return null;
}
 
// Loop through all the selected options
for ( var i = one ? index : 0, max = one ? index + 1 : options.length; i < max; i++ ) {
var option = options[ i ];
 
if ( option.selected ) {
// Get the specifc value for the option
value = jQuery(option).val();
 
// We don't need an array for one selects
if ( one ) {
return value;
}
 
// Multi-Selects return an array
values.push( value );
}
}
 
return values;
}
 
// Handle the case where in Webkit "" is returned instead of "on" if a value isn't specified
if ( rradiocheck.test( elem.type ) && !jQuery.support.checkOn ) {
return elem.getAttribute("value") === null ? "on" : elem.value;
}
 
// Everything else, we just grab the value
return (elem.value || "").replace(rreturn, "");
 
}
 
return undefined;
}
 
var isFunction = jQuery.isFunction(value);
 
return this.each(function(i) {
var self = jQuery(this), val = value;
 
if ( this.nodeType !== 1 ) {
return;
}
 
if ( isFunction ) {
val = value.call(this, i, self.val());
}
 
// Typecast each time if the value is a Function and the appended
// value is therefore different each time.
if ( typeof val === "number" ) {
val += "";
}
 
if ( jQuery.isArray(val) && rradiocheck.test( this.type ) ) {
this.checked = jQuery.inArray( self.val(), val ) >= 0;
 
} else if ( jQuery.nodeName( this, "select" ) ) {
var values = jQuery.makeArray(val);
 
jQuery( "option", this ).each(function() {
this.selected = jQuery.inArray( jQuery(this).val(), values ) >= 0;
});
 
if ( !values.length ) {
this.selectedIndex = -1;
}
 
} else {
this.value = val;
}
});
}
});
 
jQuery.extend({
attrFn: {
val: true,
css: true,
html: true,
text: true,
data: true,
width: true,
height: true,
offset: true
},
attr: function( elem, name, value, pass ) {
// don't set attributes on text and comment nodes
if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
return undefined;
}
 
if ( pass && name in jQuery.attrFn ) {
return jQuery(elem)[name](value);
}
 
var notxml = elem.nodeType !== 1 || !jQuery.isXMLDoc( elem ),
// Whether we are setting (or getting)
set = value !== undefined;
 
// Try to normalize/fix the name
name = notxml && jQuery.props[ name ] || name;
 
// Only do all the following if this is a node (faster for style)
if ( elem.nodeType === 1 ) {
// These attributes require special treatment
var special = rspecialurl.test( name );
 
// Safari mis-reports the default selected property of an option
// Accessing the parent's selectedIndex property fixes it
if ( name === "selected" && !jQuery.support.optSelected ) {
var parent = elem.parentNode;
if ( parent ) {
parent.selectedIndex;
// Make sure that it also works with optgroups, see #5701
if ( parent.parentNode ) {
parent.parentNode.selectedIndex;
}
}
}
 
// If applicable, access the attribute via the DOM 0 way
if ( name in elem && notxml && !special ) {
if ( set ) {
// We can't allow the type property to be changed (since it causes problems in IE)
if ( name === "type" && rtype.test( elem.nodeName ) && elem.parentNode ) {
jQuery.error( "type property can't be changed" );
}
 
elem[ name ] = value;
}
 
// browsers index elements by id/name on forms, give priority to attributes.
if ( jQuery.nodeName( elem, "form" ) && elem.getAttributeNode(name) ) {
return elem.getAttributeNode( name ).nodeValue;
}
 
// elem.tabIndex doesn't always return the correct value when it hasn't been explicitly set
// http://fluidproject.org/blog/2008/01/09/getting-setting-and-removing-tabindex-values-with-javascript/
if ( name === "tabIndex" ) {
var attributeNode = elem.getAttributeNode( "tabIndex" );
 
return attributeNode && attributeNode.specified ?
attributeNode.value :
rfocusable.test( elem.nodeName ) || rclickable.test( elem.nodeName ) && elem.href ?
0 :
undefined;
}
 
return elem[ name ];
}
 
if ( !jQuery.support.style && notxml && name === "style" ) {
if ( set ) {
elem.style.cssText = "" + value;
}
 
return elem.style.cssText;
}
 
if ( set ) {
// convert the value to a string (all browsers do this but IE) see #1070
elem.setAttribute( name, "" + value );
}
 
var attr = !jQuery.support.hrefNormalized && notxml && special ?
// Some attributes require a special call on IE
elem.getAttribute( name, 2 ) :
elem.getAttribute( name );
 
// Non-existent attributes return null, we normalize to undefined
return attr === null ? undefined : attr;
}
 
// elem is actually elem.style ... set the style
// Using attr for specific style information is now deprecated. Use style instead.
return jQuery.style( elem, name, value );
}
});
var rnamespaces = /\.(.*)$/,
fcleanup = function( nm ) {
return nm.replace(/[^\w\s\.\|`]/g, function( ch ) {
return "\\" + ch;
});
};
 
/*
* A number of helper functions used for managing events.
* Many of the ideas behind this code originated from
* Dean Edwards' addEvent library.
*/
jQuery.event = {
 
// Bind an event to an element
// Original by Dean Edwards
add: function( elem, types, handler, data ) {
if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
return;
}
 
// For whatever reason, IE has trouble passing the window object
// around, causing it to be cloned in the process
if ( elem.setInterval && ( elem !== window && !elem.frameElement ) ) {
elem = window;
}
 
var handleObjIn, handleObj;
 
if ( handler.handler ) {
handleObjIn = handler;
handler = handleObjIn.handler;
}
 
// Make sure that the function being executed has a unique ID
if ( !handler.guid ) {
handler.guid = jQuery.guid++;
}
 
// Init the element's event structure
var elemData = jQuery.data( elem );
 
// If no elemData is found then we must be trying to bind to one of the
// banned noData elements
if ( !elemData ) {
return;
}
 
var events = elemData.events = elemData.events || {},
eventHandle = elemData.handle, eventHandle;
 
if ( !eventHandle ) {
elemData.handle = eventHandle = function() {
// Handle the second event of a trigger and when
// an event is called after a page has unloaded
return typeof jQuery !== "undefined" && !jQuery.event.triggered ?
jQuery.event.handle.apply( eventHandle.elem, arguments ) :
undefined;
};
}
 
// Add elem as a property of the handle function
// This is to prevent a memory leak with non-native events in IE.
eventHandle.elem = elem;
 
// Handle multiple events separated by a space
// jQuery(...).bind("mouseover mouseout", fn);
types = types.split(" ");
 
var type, i = 0, namespaces;
 
while ( (type = types[ i++ ]) ) {
handleObj = handleObjIn ?
jQuery.extend({}, handleObjIn) :
{ handler: handler, data: data };
 
// Namespaced event handlers
if ( type.indexOf(".") > -1 ) {
namespaces = type.split(".");
type = namespaces.shift();
handleObj.namespace = namespaces.slice(0).sort().join(".");
 
} else {
namespaces = [];
handleObj.namespace = "";
}
 
handleObj.type = type;
handleObj.guid = handler.guid;
 
// Get the current list of functions bound to this event
var handlers = events[ type ],
special = jQuery.event.special[ type ] || {};
 
// Init the event handler queue
if ( !handlers ) {
handlers = events[ type ] = [];
 
// Check for a special event handler
// Only use addEventListener/attachEvent if the special
// events handler returns false
if ( !special.setup || special.setup.call( elem, data, namespaces, eventHandle ) === false ) {
// Bind the global event handler to the element
if ( elem.addEventListener ) {
elem.addEventListener( type, eventHandle, false );
 
} else if ( elem.attachEvent ) {
elem.attachEvent( "on" + type, eventHandle );
}
}
}
if ( special.add ) {
special.add.call( elem, handleObj );
 
if ( !handleObj.handler.guid ) {
handleObj.handler.guid = handler.guid;
}
}
 
// Add the function to the element's handler list
handlers.push( handleObj );
 
// Keep track of which events have been used, for global triggering
jQuery.event.global[ type ] = true;
}
 
// Nullify elem to prevent memory leaks in IE
elem = null;
},
 
global: {},
 
// Detach an event or set of events from an element
remove: function( elem, types, handler, pos ) {
// don't do events on text and comment nodes
if ( elem.nodeType === 3 || elem.nodeType === 8 ) {
return;
}
 
var ret, type, fn, i = 0, all, namespaces, namespace, special, eventType, handleObj, origType,
elemData = jQuery.data( elem ),
events = elemData && elemData.events;
 
if ( !elemData || !events ) {
return;
}
 
// types is actually an event object here
if ( types && types.type ) {
handler = types.handler;
types = types.type;
}
 
// Unbind all events for the element
if ( !types || typeof types === "string" && types.charAt(0) === "." ) {
types = types || "";
 
for ( type in events ) {
jQuery.event.remove( elem, type + types );
}
 
return;
}
 
// Handle multiple events separated by a space
// jQuery(...).unbind("mouseover mouseout", fn);
types = types.split(" ");
 
while ( (type = types[ i++ ]) ) {
origType = type;
handleObj = null;
all = type.indexOf(".") < 0;
namespaces = [];
 
if ( !all ) {
// Namespaced event handlers
namespaces = type.split(".");
type = namespaces.shift();
 
namespace = new RegExp("(^|\\.)" +
jQuery.map( namespaces.slice(0).sort(), fcleanup ).join("\\.(?:.*\\.)?") + "(\\.|$)")
}
 
eventType = events[ type ];
 
if ( !eventType ) {
continue;
}
 
if ( !handler ) {
for ( var j = 0; j < eventType.length; j++ ) {
handleObj = eventType[ j ];
 
if ( all || namespace.test( handleObj.namespace ) ) {
jQuery.event.remove( elem, origType, handleObj.handler, j );
eventType.splice( j--, 1 );
}
}
 
continue;
}
 
special = jQuery.event.special[ type ] || {};
 
for ( var j = pos || 0; j < eventType.length; j++ ) {
handleObj = eventType[ j ];
 
if ( handler.guid === handleObj.guid ) {
// remove the given handler for the given type
if ( all || namespace.test( handleObj.namespace ) ) {
if ( pos == null ) {
eventType.splice( j--, 1 );
}
 
if ( special.remove ) {
special.remove.call( elem, handleObj );
}
}
 
if ( pos != null ) {
break;
}
}
}
 
// remove generic event handler if no more handlers exist
if ( eventType.length === 0 || pos != null && eventType.length === 1 ) {
if ( !special.teardown || special.teardown.call( elem, namespaces ) === false ) {
removeEvent( elem, type, elemData.handle );
}
 
ret = null;
delete events[ type ];
}
}
 
// Remove the expando if it's no longer used
if ( jQuery.isEmptyObject( events ) ) {
var handle = elemData.handle;
if ( handle ) {
handle.elem = null;
}
 
delete elemData.events;
delete elemData.handle;
 
if ( jQuery.isEmptyObject( elemData ) ) {
jQuery.removeData( elem );
}
}
},
 
// bubbling is internal
trigger: function( event, data, elem /*, bubbling */ ) {
// Event object or event type
var type = event.type || event,
bubbling = arguments[3];
 
if ( !bubbling ) {
event = typeof event === "object" ?
// jQuery.Event object
event[expando] ? event :
// Object literal
jQuery.extend( jQuery.Event(type), event ) :
// Just the event type (string)
jQuery.Event(type);
 
if ( type.indexOf("!") >= 0 ) {
event.type = type = type.slice(0, -1);
event.exclusive = true;
}
 
// Handle a global trigger
if ( !elem ) {
// Don't bubble custom events when global (to avoid too much overhead)
event.stopPropagation();
 
// Only trigger if we've ever bound an event for it
if ( jQuery.event.global[ type ] ) {
jQuery.each( jQuery.cache, function() {
if ( this.events && this.events[type] ) {
jQuery.event.trigger( event, data, this.handle.elem );
}
});
}
}
 
// Handle triggering a single element
 
// don't do events on text and comment nodes
if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
return undefined;
}
 
// Clean up in case it is reused
event.result = undefined;
event.target = elem;
 
// Clone the incoming data, if any
data = jQuery.makeArray( data );
data.unshift( event );
}
 
event.currentTarget = elem;
 
// Trigger the event, it is assumed that "handle" is a function
var handle = jQuery.data( elem, "handle" );
if ( handle ) {
handle.apply( elem, data );
}
 
var parent = elem.parentNode || elem.ownerDocument;
 
// Trigger an inline bound script
try {
if ( !(elem && elem.nodeName && jQuery.noData[elem.nodeName.toLowerCase()]) ) {
if ( elem[ "on" + type ] && elem[ "on" + type ].apply( elem, data ) === false ) {
event.result = false;
}
}
 
// prevent IE from throwing an error for some elements with some event types, see #3533
} catch (e) {}
 
if ( !event.isPropagationStopped() && parent ) {
jQuery.event.trigger( event, data, parent, true );
 
} else if ( !event.isDefaultPrevented() ) {
var target = event.target, old,
isClick = jQuery.nodeName(target, "a") && type === "click",
special = jQuery.event.special[ type ] || {};
 
if ( (!special._default || special._default.call( elem, event ) === false) &&
!isClick && !(target && target.nodeName && jQuery.noData[target.nodeName.toLowerCase()]) ) {
 
try {
if ( target[ type ] ) {
// Make sure that we don't accidentally re-trigger the onFOO events
old = target[ "on" + type ];
 
if ( old ) {
target[ "on" + type ] = null;
}
 
jQuery.event.triggered = true;
target[ type ]();
}
 
// prevent IE from throwing an error for some elements with some event types, see #3533
} catch (e) {}
 
if ( old ) {
target[ "on" + type ] = old;
}
 
jQuery.event.triggered = false;
}
}
},
 
handle: function( event ) {
var all, handlers, namespaces, namespace, events;
 
event = arguments[0] = jQuery.event.fix( event || window.event );
event.currentTarget = this;
 
// Namespaced event handlers
all = event.type.indexOf(".") < 0 && !event.exclusive;
 
if ( !all ) {
namespaces = event.type.split(".");
event.type = namespaces.shift();
namespace = new RegExp("(^|\\.)" + namespaces.slice(0).sort().join("\\.(?:.*\\.)?") + "(\\.|$)");
}
 
var events = jQuery.data(this, "events"), handlers = events[ event.type ];
 
if ( events && handlers ) {
// Clone the handlers to prevent manipulation
handlers = handlers.slice(0);
 
for ( var j = 0, l = handlers.length; j < l; j++ ) {
var handleObj = handlers[ j ];
 
// Filter the functions by class
if ( all || namespace.test( handleObj.namespace ) ) {
// Pass in a reference to the handler function itself
// So that we can later remove it
event.handler = handleObj.handler;
event.data = handleObj.data;
event.handleObj = handleObj;
var ret = handleObj.handler.apply( this, arguments );
 
if ( ret !== undefined ) {
event.result = ret;
if ( ret === false ) {
event.preventDefault();
event.stopPropagation();
}
}
 
if ( event.isImmediatePropagationStopped() ) {
break;
}
}
}
}
 
return event.result;
},
 
props: "altKey attrChange attrName bubbles button cancelable charCode clientX clientY ctrlKey currentTarget data detail eventPhase fromElement handler keyCode layerX layerY metaKey newValue offsetX offsetY originalTarget pageX pageY prevValue relatedNode relatedTarget screenX screenY shiftKey srcElement target toElement view wheelDelta which".split(" "),
 
fix: function( event ) {
if ( event[ expando ] ) {
return event;
}
 
// store a copy of the original event object
// and "clone" to set read-only properties
var originalEvent = event;
event = jQuery.Event( originalEvent );
 
for ( var i = this.props.length, prop; i; ) {
prop = this.props[ --i ];
event[ prop ] = originalEvent[ prop ];
}
 
// Fix target property, if necessary
if ( !event.target ) {
event.target = event.srcElement || document; // Fixes #1925 where srcElement might not be defined either
}
 
// check if target is a textnode (safari)
if ( event.target.nodeType === 3 ) {
event.target = event.target.parentNode;
}
 
// Add relatedTarget, if necessary
if ( !event.relatedTarget && event.fromElement ) {
event.relatedTarget = event.fromElement === event.target ? event.toElement : event.fromElement;
}
 
// Calculate pageX/Y if missing and clientX/Y available
if ( event.pageX == null && event.clientX != null ) {
var doc = document.documentElement, body = document.body;
event.pageX = event.clientX + (doc && doc.scrollLeft || body && body.scrollLeft || 0) - (doc && doc.clientLeft || body && body.clientLeft || 0);
event.pageY = event.clientY + (doc && doc.scrollTop || body && body.scrollTop || 0) - (doc && doc.clientTop || body && body.clientTop || 0);
}
 
// Add which for key events
if ( !event.which && ((event.charCode || event.charCode === 0) ? event.charCode : event.keyCode) ) {
event.which = event.charCode || event.keyCode;
}
 
// Add metaKey to non-Mac browsers (use ctrl for PC's and Meta for Macs)
if ( !event.metaKey && event.ctrlKey ) {
event.metaKey = event.ctrlKey;
}
 
// Add which for click: 1 === left; 2 === middle; 3 === right
// Note: button is not normalized, so don't use it
if ( !event.which && event.button !== undefined ) {
event.which = (event.button & 1 ? 1 : ( event.button & 2 ? 3 : ( event.button & 4 ? 2 : 0 ) ));
}
 
return event;
},
 
// Deprecated, use jQuery.guid instead
guid: 1E8,
 
// Deprecated, use jQuery.proxy instead
proxy: jQuery.proxy,
 
special: {
ready: {
// Make sure the ready event is setup
setup: jQuery.bindReady,
teardown: jQuery.noop
},
 
live: {
add: function( handleObj ) {
jQuery.event.add( this, handleObj.origType, jQuery.extend({}, handleObj, {handler: liveHandler}) );
},
 
remove: function( handleObj ) {
var remove = true,
type = handleObj.origType.replace(rnamespaces, "");
jQuery.each( jQuery.data(this, "events").live || [], function() {
if ( type === this.origType.replace(rnamespaces, "") ) {
remove = false;
return false;
}
});
 
if ( remove ) {
jQuery.event.remove( this, handleObj.origType, liveHandler );
}
}
 
},
 
beforeunload: {
setup: function( data, namespaces, eventHandle ) {
// We only want to do this special case on windows
if ( this.setInterval ) {
this.onbeforeunload = eventHandle;
}
 
return false;
},
teardown: function( namespaces, eventHandle ) {
if ( this.onbeforeunload === eventHandle ) {
this.onbeforeunload = null;
}
}
}
}
};
 
var removeEvent = document.removeEventListener ?
function( elem, type, handle ) {
elem.removeEventListener( type, handle, false );
} :
function( elem, type, handle ) {
elem.detachEvent( "on" + type, handle );
};
 
jQuery.Event = function( src ) {
// Allow instantiation without the 'new' keyword
if ( !this.preventDefault ) {
return new jQuery.Event( src );
}
 
// Event object
if ( src && src.type ) {
this.originalEvent = src;
this.type = src.type;
// Event type
} else {
this.type = src;
}
 
// timeStamp is buggy for some events on Firefox(#3843)
// So we won't rely on the native value
this.timeStamp = now();
 
// Mark it as fixed
this[ expando ] = true;
};
 
function returnFalse() {
return false;
}
function returnTrue() {
return true;
}
 
// jQuery.Event is based on DOM3 Events as specified by the ECMAScript Language Binding
// http://www.w3.org/TR/2003/WD-DOM-Level-3-Events-20030331/ecma-script-binding.html
jQuery.Event.prototype = {
preventDefault: function() {
this.isDefaultPrevented = returnTrue;
 
var e = this.originalEvent;
if ( !e ) {
return;
}
// if preventDefault exists run it on the original event
if ( e.preventDefault ) {
e.preventDefault();
}
// otherwise set the returnValue property of the original event to false (IE)
e.returnValue = false;
},
stopPropagation: function() {
this.isPropagationStopped = returnTrue;
 
var e = this.originalEvent;
if ( !e ) {
return;
}
// if stopPropagation exists run it on the original event
if ( e.stopPropagation ) {
e.stopPropagation();
}
// otherwise set the cancelBubble property of the original event to true (IE)
e.cancelBubble = true;
},
stopImmediatePropagation: function() {
this.isImmediatePropagationStopped = returnTrue;
this.stopPropagation();
},
isDefaultPrevented: returnFalse,
isPropagationStopped: returnFalse,
isImmediatePropagationStopped: returnFalse
};
 
// Checks if an event happened on an element within another element
// Used in jQuery.event.special.mouseenter and mouseleave handlers
var withinElement = function( event ) {
// Check if mouse(over|out) are still within the same parent element
var parent = event.relatedTarget;
 
// Firefox sometimes assigns relatedTarget a XUL element
// which we cannot access the parentNode property of
try {
// Traverse up the tree
while ( parent && parent !== this ) {
parent = parent.parentNode;
}
 
if ( parent !== this ) {
// set the correct event type
event.type = event.data;
 
// handle event if we actually just moused on to a non sub-element
jQuery.event.handle.apply( this, arguments );
}
 
// assuming we've left the element since we most likely mousedover a xul element
} catch(e) { }
},
 
// In case of event delegation, we only need to rename the event.type,
// liveHandler will take care of the rest.
delegate = function( event ) {
event.type = event.data;
jQuery.event.handle.apply( this, arguments );
};
 
// Create mouseenter and mouseleave events
jQuery.each({
mouseenter: "mouseover",
mouseleave: "mouseout"
}, function( orig, fix ) {
jQuery.event.special[ orig ] = {
setup: function( data ) {
jQuery.event.add( this, fix, data && data.selector ? delegate : withinElement, orig );
},
teardown: function( data ) {
jQuery.event.remove( this, fix, data && data.selector ? delegate : withinElement );
}
};
});
 
// submit delegation
if ( !jQuery.support.submitBubbles ) {
 
jQuery.event.special.submit = {
setup: function( data, namespaces ) {
if ( this.nodeName.toLowerCase() !== "form" ) {
jQuery.event.add(this, "click.specialSubmit", function( e ) {
var elem = e.target, type = elem.type;
 
if ( (type === "submit" || type === "image") && jQuery( elem ).closest("form").length ) {
return trigger( "submit", this, arguments );
}
});
jQuery.event.add(this, "keypress.specialSubmit", function( e ) {
var elem = e.target, type = elem.type;
 
if ( (type === "text" || type === "password") && jQuery( elem ).closest("form").length && e.keyCode === 13 ) {
return trigger( "submit", this, arguments );
}
});
 
} else {
return false;
}
},
 
teardown: function( namespaces ) {
jQuery.event.remove( this, ".specialSubmit" );
}
};
 
}
 
// change delegation, happens here so we have bind.
if ( !jQuery.support.changeBubbles ) {
 
var formElems = /textarea|input|select/i,
 
changeFilters,
 
getVal = function( elem ) {
var type = elem.type, val = elem.value;
 
if ( type === "radio" || type === "checkbox" ) {
val = elem.checked;
 
} else if ( type === "select-multiple" ) {
val = elem.selectedIndex > -1 ?
jQuery.map( elem.options, function( elem ) {
return elem.selected;
}).join("-") :
"";
 
} else if ( elem.nodeName.toLowerCase() === "select" ) {
val = elem.selectedIndex;
}
 
return val;
},
 
testChange = function testChange( e ) {
var elem = e.target, data, val;
 
if ( !formElems.test( elem.nodeName ) || elem.readOnly ) {
return;
}
 
data = jQuery.data( elem, "_change_data" );
val = getVal(elem);
 
// the current data will be also retrieved by beforeactivate
if ( e.type !== "focusout" || elem.type !== "radio" ) {
jQuery.data( elem, "_change_data", val );
}
if ( data === undefined || val === data ) {
return;
}
 
if ( data != null || val ) {
e.type = "change";
return jQuery.event.trigger( e, arguments[1], elem );
}
};
 
jQuery.event.special.change = {
filters: {
focusout: testChange,
 
click: function( e ) {
var elem = e.target, type = elem.type;
 
if ( type === "radio" || type === "checkbox" || elem.nodeName.toLowerCase() === "select" ) {
return testChange.call( this, e );
}
},
 
// Change has to be called before submit
// Keydown will be called before keypress, which is used in submit-event delegation
keydown: function( e ) {
var elem = e.target, type = elem.type;
 
if ( (e.keyCode === 13 && elem.nodeName.toLowerCase() !== "textarea") ||
(e.keyCode === 32 && (type === "checkbox" || type === "radio")) ||
type === "select-multiple" ) {
return testChange.call( this, e );
}
},
 
// Beforeactivate happens also before the previous element is blurred
// with this event you can't trigger a change event, but you can store
// information/focus[in] is not needed anymore
beforeactivate: function( e ) {
var elem = e.target;
jQuery.data( elem, "_change_data", getVal(elem) );
}
},
 
setup: function( data, namespaces ) {
if ( this.type === "file" ) {
return false;
}
 
for ( var type in changeFilters ) {
jQuery.event.add( this, type + ".specialChange", changeFilters[type] );
}
 
return formElems.test( this.nodeName );
},
 
teardown: function( namespaces ) {
jQuery.event.remove( this, ".specialChange" );
 
return formElems.test( this.nodeName );
}
};
 
changeFilters = jQuery.event.special.change.filters;
}
 
function trigger( type, elem, args ) {
args[0].type = type;
return jQuery.event.handle.apply( elem, args );
}
 
// Create "bubbling" focus and blur events
if ( document.addEventListener ) {
jQuery.each({ focus: "focusin", blur: "focusout" }, function( orig, fix ) {
jQuery.event.special[ fix ] = {
setup: function() {
this.addEventListener( orig, handler, true );
},
teardown: function() {
this.removeEventListener( orig, handler, true );
}
};
 
function handler( e ) {
e = jQuery.event.fix( e );
e.type = fix;
return jQuery.event.handle.call( this, e );
}
});
}
 
jQuery.each(["bind", "one"], function( i, name ) {
jQuery.fn[ name ] = function( type, data, fn ) {
// Handle object literals
if ( typeof type === "object" ) {
for ( var key in type ) {
this[ name ](key, data, type[key], fn);
}
return this;
}
if ( jQuery.isFunction( data ) ) {
fn = data;
data = undefined;
}
 
var handler = name === "one" ? jQuery.proxy( fn, function( event ) {
jQuery( this ).unbind( event, handler );
return fn.apply( this, arguments );
}) : fn;
 
if ( type === "unload" && name !== "one" ) {
this.one( type, data, fn );
 
} else {
for ( var i = 0, l = this.length; i < l; i++ ) {
jQuery.event.add( this[i], type, handler, data );
}
}
 
return this;
};
});
 
jQuery.fn.extend({
unbind: function( type, fn ) {
// Handle object literals
if ( typeof type === "object" && !type.preventDefault ) {
for ( var key in type ) {
this.unbind(key, type[key]);
}
 
} else {
for ( var i = 0, l = this.length; i < l; i++ ) {
jQuery.event.remove( this[i], type, fn );
}
}
 
return this;
},
delegate: function( selector, types, data, fn ) {
return this.live( types, data, fn, selector );
},
undelegate: function( selector, types, fn ) {
if ( arguments.length === 0 ) {
return this.unbind( "live" );
} else {
return this.die( types, null, fn, selector );
}
},
trigger: function( type, data ) {
return this.each(function() {
jQuery.event.trigger( type, data, this );
});
},
 
triggerHandler: function( type, data ) {
if ( this[0] ) {
var event = jQuery.Event( type );
event.preventDefault();
event.stopPropagation();
jQuery.event.trigger( event, data, this[0] );
return event.result;
}
},
 
toggle: function( fn ) {
// Save reference to arguments for access in closure
var args = arguments, i = 1;
 
// link all the functions, so any of them can unbind this click handler
while ( i < args.length ) {
jQuery.proxy( fn, args[ i++ ] );
}
 
return this.click( jQuery.proxy( fn, function( event ) {
// Figure out which function to execute
var lastToggle = ( jQuery.data( this, "lastToggle" + fn.guid ) || 0 ) % i;
jQuery.data( this, "lastToggle" + fn.guid, lastToggle + 1 );
 
// Make sure that clicks stop
event.preventDefault();
 
// and execute the function
return args[ lastToggle ].apply( this, arguments ) || false;
}));
},
 
hover: function( fnOver, fnOut ) {
return this.mouseenter( fnOver ).mouseleave( fnOut || fnOver );
}
});
 
var liveMap = {
focus: "focusin",
blur: "focusout",
mouseenter: "mouseover",
mouseleave: "mouseout"
};
 
jQuery.each(["live", "die"], function( i, name ) {
jQuery.fn[ name ] = function( types, data, fn, origSelector /* Internal Use Only */ ) {
var type, i = 0, match, namespaces, preType,
selector = origSelector || this.selector,
context = origSelector ? this : jQuery( this.context );
 
if ( jQuery.isFunction( data ) ) {
fn = data;
data = undefined;
}
 
types = (types || "").split(" ");
 
while ( (type = types[ i++ ]) != null ) {
match = rnamespaces.exec( type );
namespaces = "";
 
if ( match ) {
namespaces = match[0];
type = type.replace( rnamespaces, "" );
}
 
if ( type === "hover" ) {
types.push( "mouseenter" + namespaces, "mouseleave" + namespaces );
continue;
}
 
preType = type;
 
if ( type === "focus" || type === "blur" ) {
types.push( liveMap[ type ] + namespaces );
type = type + namespaces;
 
} else {
type = (liveMap[ type ] || type) + namespaces;
}
 
if ( name === "live" ) {
// bind live handler
context.each(function(){
jQuery.event.add( this, liveConvert( type, selector ),
{ data: data, selector: selector, handler: fn, origType: type, origHandler: fn, preType: preType } );
});
 
} else {
// unbind live handler
context.unbind( liveConvert( type, selector ), fn );
}
}
return this;
}
});
 
function liveHandler( event ) {
var stop, elems = [], selectors = [], args = arguments,
related, match, handleObj, elem, j, i, l, data,
events = jQuery.data( this, "events" );
 
// Make sure we avoid non-left-click bubbling in Firefox (#3861)
if ( event.liveFired === this || !events || !events.live || event.button && event.type === "click" ) {
return;
}
 
event.liveFired = this;
 
var live = events.live.slice(0);
 
for ( j = 0; j < live.length; j++ ) {
handleObj = live[j];
 
if ( handleObj.origType.replace( rnamespaces, "" ) === event.type ) {
selectors.push( handleObj.selector );
 
} else {
live.splice( j--, 1 );
}
}
 
match = jQuery( event.target ).closest( selectors, event.currentTarget );
 
for ( i = 0, l = match.length; i < l; i++ ) {
for ( j = 0; j < live.length; j++ ) {
handleObj = live[j];
 
if ( match[i].selector === handleObj.selector ) {
elem = match[i].elem;
related = null;
 
// Those two events require additional checking
if ( handleObj.preType === "mouseenter" || handleObj.preType === "mouseleave" ) {
related = jQuery( event.relatedTarget ).closest( handleObj.selector )[0];
}
 
if ( !related || related !== elem ) {
elems.push({ elem: elem, handleObj: handleObj });
}
}
}
}
 
for ( i = 0, l = elems.length; i < l; i++ ) {
match = elems[i];
event.currentTarget = match.elem;
event.data = match.handleObj.data;
event.handleObj = match.handleObj;
 
if ( match.handleObj.origHandler.apply( match.elem, args ) === false ) {
stop = false;
break;
}
}
 
return stop;
}
 
function liveConvert( type, selector ) {
return "live." + (type && type !== "*" ? type + "." : "") + selector.replace(/\./g, "`").replace(/ /g, "&");
}
 
jQuery.each( ("blur focus focusin focusout load resize scroll unload click dblclick " +
"mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
"change select submit keydown keypress keyup error").split(" "), function( i, name ) {
 
// Handle event binding
jQuery.fn[ name ] = function( fn ) {
return fn ? this.bind( name, fn ) : this.trigger( name );
};
 
if ( jQuery.attrFn ) {
jQuery.attrFn[ name ] = true;
}
});
 
// Prevent memory leaks in IE
// Window isn't included so as not to unbind existing unload events
// More info:
// - http://isaacschlueter.com/2006/10/msie-memory-leaks/
if ( window.attachEvent && !window.addEventListener ) {
window.attachEvent("onunload", function() {
for ( var id in jQuery.cache ) {
if ( jQuery.cache[ id ].handle ) {
// Try/Catch is to handle iframes being unloaded, see #4280
try {
jQuery.event.remove( jQuery.cache[ id ].handle.elem );
} catch(e) {}
}
}
});
}
/*!
* Sizzle CSS Selector Engine - v1.0
* Copyright 2009, The Dojo Foundation
* Released under the MIT, BSD, and GPL Licenses.
* More information: http://sizzlejs.com/
*/
(function(){
 
var chunker = /((?:\((?:\([^()]+\)|[^()]+)+\)|\[(?:\[[^[\]]*\]|['"][^'"]*['"]|[^[\]'"]+)+\]|\\.|[^ >+~,(\[\\]+)+|[>+~])(\s*,\s*)?((?:.|\r|\n)*)/g,
done = 0,
toString = Object.prototype.toString,
hasDuplicate = false,
baseHasDuplicate = true;
 
// Here we check if the JavaScript engine is using some sort of
// optimization where it does not always call our comparision
// function. If that is the case, discard the hasDuplicate value.
// Thus far that includes Google Chrome.
[0, 0].sort(function(){
baseHasDuplicate = false;
return 0;
});
 
var Sizzle = function(selector, context, results, seed) {
results = results || [];
var origContext = context = context || document;
 
if ( context.nodeType !== 1 && context.nodeType !== 9 ) {
return [];
}
if ( !selector || typeof selector !== "string" ) {
return results;
}
 
var parts = [], m, set, checkSet, extra, prune = true, contextXML = isXML(context),
soFar = selector;
// Reset the position of the chunker regexp (start from head)
while ( (chunker.exec(""), m = chunker.exec(soFar)) !== null ) {
soFar = m[3];
parts.push( m[1] );
if ( m[2] ) {
extra = m[3];
break;
}
}
 
if ( parts.length > 1 && origPOS.exec( selector ) ) {
if ( parts.length === 2 && Expr.relative[ parts[0] ] ) {
set = posProcess( parts[0] + parts[1], context );
} else {
set = Expr.relative[ parts[0] ] ?
[ context ] :
Sizzle( parts.shift(), context );
 
while ( parts.length ) {
selector = parts.shift();
 
if ( Expr.relative[ selector ] ) {
selector += parts.shift();
}
set = posProcess( selector, set );
}
}
} else {
// Take a shortcut and set the context if the root selector is an ID
// (but not if it'll be faster if the inner selector is an ID)
if ( !seed && parts.length > 1 && context.nodeType === 9 && !contextXML &&
Expr.match.ID.test(parts[0]) && !Expr.match.ID.test(parts[parts.length - 1]) ) {
var ret = Sizzle.find( parts.shift(), context, contextXML );
context = ret.expr ? Sizzle.filter( ret.expr, ret.set )[0] : ret.set[0];
}
 
if ( context ) {
var ret = seed ?
{ expr: parts.pop(), set: makeArray(seed) } :
Sizzle.find( parts.pop(), parts.length === 1 && (parts[0] === "~" || parts[0] === "+") && context.parentNode ? context.parentNode : context, contextXML );
set = ret.expr ? Sizzle.filter( ret.expr, ret.set ) : ret.set;
 
if ( parts.length > 0 ) {
checkSet = makeArray(set);
} else {
prune = false;
}
 
while ( parts.length ) {
var cur = parts.pop(), pop = cur;
 
if ( !Expr.relative[ cur ] ) {
cur = "";
} else {
pop = parts.pop();
}
 
if ( pop == null ) {
pop = context;
}
 
Expr.relative[ cur ]( checkSet, pop, contextXML );
}
} else {
checkSet = parts = [];
}
}
 
if ( !checkSet ) {
checkSet = set;
}
 
if ( !checkSet ) {
Sizzle.error( cur || selector );
}
 
if ( toString.call(checkSet) === "[object Array]" ) {
if ( !prune ) {
results.push.apply( results, checkSet );
} else if ( context && context.nodeType === 1 ) {
for ( var i = 0; checkSet[i] != null; i++ ) {
if ( checkSet[i] && (checkSet[i] === true || checkSet[i].nodeType === 1 && contains(context, checkSet[i])) ) {
results.push( set[i] );
}
}
} else {
for ( var i = 0; checkSet[i] != null; i++ ) {
if ( checkSet[i] && checkSet[i].nodeType === 1 ) {
results.push( set[i] );
}
}
}
} else {
makeArray( checkSet, results );
}
 
if ( extra ) {
Sizzle( extra, origContext, results, seed );
Sizzle.uniqueSort( results );
}
 
return results;
};
 
Sizzle.uniqueSort = function(results){
if ( sortOrder ) {
hasDuplicate = baseHasDuplicate;
results.sort(sortOrder);
 
if ( hasDuplicate ) {
for ( var i = 1; i < results.length; i++ ) {
if ( results[i] === results[i-1] ) {
results.splice(i--, 1);
}
}
}
}
 
return results;
};
 
Sizzle.matches = function(expr, set){
return Sizzle(expr, null, null, set);
};
 
Sizzle.find = function(expr, context, isXML){
var set, match;
 
if ( !expr ) {
return [];
}
 
for ( var i = 0, l = Expr.order.length; i < l; i++ ) {
var type = Expr.order[i], match;
if ( (match = Expr.leftMatch[ type ].exec( expr )) ) {
var left = match[1];
match.splice(1,1);
 
if ( left.substr( left.length - 1 ) !== "\\" ) {
match[1] = (match[1] || "").replace(/\\/g, "");
set = Expr.find[ type ]( match, context, isXML );
if ( set != null ) {
expr = expr.replace( Expr.match[ type ], "" );
break;
}
}
}
}
 
if ( !set ) {
set = context.getElementsByTagName("*");
}
 
return {set: set, expr: expr};
};
 
Sizzle.filter = function(expr, set, inplace, not){
var old = expr, result = [], curLoop = set, match, anyFound,
isXMLFilter = set && set[0] && isXML(set[0]);
 
while ( expr && set.length ) {
for ( var type in Expr.filter ) {
if ( (match = Expr.leftMatch[ type ].exec( expr )) != null && match[2] ) {
var filter = Expr.filter[ type ], found, item, left = match[1];
anyFound = false;
 
match.splice(1,1);
 
if ( left.substr( left.length - 1 ) === "\\" ) {
continue;
}
 
if ( curLoop === result ) {
result = [];
}
 
if ( Expr.preFilter[ type ] ) {
match = Expr.preFilter[ type ]( match, curLoop, inplace, result, not, isXMLFilter );
 
if ( !match ) {
anyFound = found = true;
} else if ( match === true ) {
continue;
}
}
 
if ( match ) {
for ( var i = 0; (item = curLoop[i]) != null; i++ ) {
if ( item ) {
found = filter( item, match, i, curLoop );
var pass = not ^ !!found;
 
if ( inplace && found != null ) {
if ( pass ) {
anyFound = true;
} else {
curLoop[i] = false;
}
} else if ( pass ) {
result.push( item );
anyFound = true;
}
}
}
}
 
if ( found !== undefined ) {
if ( !inplace ) {
curLoop = result;
}
 
expr = expr.replace( Expr.match[ type ], "" );
 
if ( !anyFound ) {
return [];
}
 
break;
}
}
}
 
// Improper expression
if ( expr === old ) {
if ( anyFound == null ) {
Sizzle.error( expr );
} else {
break;
}
}
 
old = expr;
}
 
return curLoop;
};
 
Sizzle.error = function( msg ) {
throw "Syntax error, unrecognized expression: " + msg;
};
 
var Expr = Sizzle.selectors = {
order: [ "ID", "NAME", "TAG" ],
match: {
ID: /#((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
CLASS: /\.((?:[\w\u00c0-\uFFFF-]|\\.)+)/,
NAME: /\[name=['"]*((?:[\w\u00c0-\uFFFF-]|\\.)+)['"]*\]/,
ATTR: /\[\s*((?:[\w\u00c0-\uFFFF-]|\\.)+)\s*(?:(\S?=)\s*(['"]*)(.*?)\3|)\s*\]/,
TAG: /^((?:[\w\u00c0-\uFFFF\*-]|\\.)+)/,
CHILD: /:(only|nth|last|first)-child(?:\((even|odd|[\dn+-]*)\))?/,
POS: /:(nth|eq|gt|lt|first|last|even|odd)(?:\((\d*)\))?(?=[^-]|$)/,
PSEUDO: /:((?:[\w\u00c0-\uFFFF-]|\\.)+)(?:\((['"]?)((?:\([^\)]+\)|[^\(\)]*)+)\2\))?/
},
leftMatch: {},
attrMap: {
"class": "className",
"for": "htmlFor"
},
attrHandle: {
href: function(elem){
return elem.getAttribute("href");
}
},
relative: {
"+": function(checkSet, part){
var isPartStr = typeof part === "string",
isTag = isPartStr && !/\W/.test(part),
isPartStrNotTag = isPartStr && !isTag;
 
if ( isTag ) {
part = part.toLowerCase();
}
 
for ( var i = 0, l = checkSet.length, elem; i < l; i++ ) {
if ( (elem = checkSet[i]) ) {
while ( (elem = elem.previousSibling) && elem.nodeType !== 1 ) {}
 
checkSet[i] = isPartStrNotTag || elem && elem.nodeName.toLowerCase() === part ?
elem || false :
elem === part;
}
}
 
if ( isPartStrNotTag ) {
Sizzle.filter( part, checkSet, true );
}
},
">": function(checkSet, part){
var isPartStr = typeof part === "string";
 
if ( isPartStr && !/\W/.test(part) ) {
part = part.toLowerCase();
 
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
var elem = checkSet[i];
if ( elem ) {
var parent = elem.parentNode;
checkSet[i] = parent.nodeName.toLowerCase() === part ? parent : false;
}
}
} else {
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
var elem = checkSet[i];
if ( elem ) {
checkSet[i] = isPartStr ?
elem.parentNode :
elem.parentNode === part;
}
}
 
if ( isPartStr ) {
Sizzle.filter( part, checkSet, true );
}
}
},
"": function(checkSet, part, isXML){
var doneName = done++, checkFn = dirCheck;
 
if ( typeof part === "string" && !/\W/.test(part) ) {
var nodeCheck = part = part.toLowerCase();
checkFn = dirNodeCheck;
}
 
checkFn("parentNode", part, doneName, checkSet, nodeCheck, isXML);
},
"~": function(checkSet, part, isXML){
var doneName = done++, checkFn = dirCheck;
 
if ( typeof part === "string" && !/\W/.test(part) ) {
var nodeCheck = part = part.toLowerCase();
checkFn = dirNodeCheck;
}
 
checkFn("previousSibling", part, doneName, checkSet, nodeCheck, isXML);
}
},
find: {
ID: function(match, context, isXML){
if ( typeof context.getElementById !== "undefined" && !isXML ) {
var m = context.getElementById(match[1]);
return m ? [m] : [];
}
},
NAME: function(match, context){
if ( typeof context.getElementsByName !== "undefined" ) {
var ret = [], results = context.getElementsByName(match[1]);
 
for ( var i = 0, l = results.length; i < l; i++ ) {
if ( results[i].getAttribute("name") === match[1] ) {
ret.push( results[i] );
}
}
 
return ret.length === 0 ? null : ret;
}
},
TAG: function(match, context){
return context.getElementsByTagName(match[1]);
}
},
preFilter: {
CLASS: function(match, curLoop, inplace, result, not, isXML){
match = " " + match[1].replace(/\\/g, "") + " ";
 
if ( isXML ) {
return match;
}
 
for ( var i = 0, elem; (elem = curLoop[i]) != null; i++ ) {
if ( elem ) {
if ( not ^ (elem.className && (" " + elem.className + " ").replace(/[\t\n]/g, " ").indexOf(match) >= 0) ) {
if ( !inplace ) {
result.push( elem );
}
} else if ( inplace ) {
curLoop[i] = false;
}
}
}
 
return false;
},
ID: function(match){
return match[1].replace(/\\/g, "");
},
TAG: function(match, curLoop){
return match[1].toLowerCase();
},
CHILD: function(match){
if ( match[1] === "nth" ) {
// parse equations like 'even', 'odd', '5', '2n', '3n+2', '4n-1', '-n+6'
var test = /(-?)(\d*)n((?:\+|-)?\d*)/.exec(
match[2] === "even" && "2n" || match[2] === "odd" && "2n+1" ||
!/\D/.test( match[2] ) && "0n+" + match[2] || match[2]);
 
// calculate the numbers (first)n+(last) including if they are negative
match[2] = (test[1] + (test[2] || 1)) - 0;
match[3] = test[3] - 0;
}
 
// TODO: Move to normal caching system
match[0] = done++;
 
return match;
},
ATTR: function(match, curLoop, inplace, result, not, isXML){
var name = match[1].replace(/\\/g, "");
if ( !isXML && Expr.attrMap[name] ) {
match[1] = Expr.attrMap[name];
}
 
if ( match[2] === "~=" ) {
match[4] = " " + match[4] + " ";
}
 
return match;
},
PSEUDO: function(match, curLoop, inplace, result, not){
if ( match[1] === "not" ) {
// If we're dealing with a complex expression, or a simple one
if ( ( chunker.exec(match[3]) || "" ).length > 1 || /^\w/.test(match[3]) ) {
match[3] = Sizzle(match[3], null, null, curLoop);
} else {
var ret = Sizzle.filter(match[3], curLoop, inplace, true ^ not);
if ( !inplace ) {
result.push.apply( result, ret );
}
return false;
}
} else if ( Expr.match.POS.test( match[0] ) || Expr.match.CHILD.test( match[0] ) ) {
return true;
}
return match;
},
POS: function(match){
match.unshift( true );
return match;
}
},
filters: {
enabled: function(elem){
return elem.disabled === false && elem.type !== "hidden";
},
disabled: function(elem){
return elem.disabled === true;
},
checked: function(elem){
return elem.checked === true;
},
selected: function(elem){
// Accessing this property makes selected-by-default
// options in Safari work properly
elem.parentNode.selectedIndex;
return elem.selected === true;
},
parent: function(elem){
return !!elem.firstChild;
},
empty: function(elem){
return !elem.firstChild;
},
has: function(elem, i, match){
return !!Sizzle( match[3], elem ).length;
},
header: function(elem){
return /h\d/i.test( elem.nodeName );
},
text: function(elem){
return "text" === elem.type;
},
radio: function(elem){
return "radio" === elem.type;
},
checkbox: function(elem){
return "checkbox" === elem.type;
},
file: function(elem){
return "file" === elem.type;
},
password: function(elem){
return "password" === elem.type;
},
submit: function(elem){
return "submit" === elem.type;
},
image: function(elem){
return "image" === elem.type;
},
reset: function(elem){
return "reset" === elem.type;
},
button: function(elem){
return "button" === elem.type || elem.nodeName.toLowerCase() === "button";
},
input: function(elem){
return /input|select|textarea|button/i.test(elem.nodeName);
}
},
setFilters: {
first: function(elem, i){
return i === 0;
},
last: function(elem, i, match, array){
return i === array.length - 1;
},
even: function(elem, i){
return i % 2 === 0;
},
odd: function(elem, i){
return i % 2 === 1;
},
lt: function(elem, i, match){
return i < match[3] - 0;
},
gt: function(elem, i, match){
return i > match[3] - 0;
},
nth: function(elem, i, match){
return match[3] - 0 === i;
},
eq: function(elem, i, match){
return match[3] - 0 === i;
}
},
filter: {
PSEUDO: function(elem, match, i, array){
var name = match[1], filter = Expr.filters[ name ];
 
if ( filter ) {
return filter( elem, i, match, array );
} else if ( name === "contains" ) {
return (elem.textContent || elem.innerText || getText([ elem ]) || "").indexOf(match[3]) >= 0;
} else if ( name === "not" ) {
var not = match[3];
 
for ( var i = 0, l = not.length; i < l; i++ ) {
if ( not[i] === elem ) {
return false;
}
}
 
return true;
} else {
Sizzle.error( "Syntax error, unrecognized expression: " + name );
}
},
CHILD: function(elem, match){
var type = match[1], node = elem;
switch (type) {
case 'only':
case 'first':
while ( (node = node.previousSibling) ) {
if ( node.nodeType === 1 ) {
return false;
}
}
if ( type === "first" ) {
return true;
}
node = elem;
case 'last':
while ( (node = node.nextSibling) ) {
if ( node.nodeType === 1 ) {
return false;
}
}
return true;
case 'nth':
var first = match[2], last = match[3];
 
if ( first === 1 && last === 0 ) {
return true;
}
var doneName = match[0],
parent = elem.parentNode;
if ( parent && (parent.sizcache !== doneName || !elem.nodeIndex) ) {
var count = 0;
for ( node = parent.firstChild; node; node = node.nextSibling ) {
if ( node.nodeType === 1 ) {
node.nodeIndex = ++count;
}
}
parent.sizcache = doneName;
}
var diff = elem.nodeIndex - last;
if ( first === 0 ) {
return diff === 0;
} else {
return ( diff % first === 0 && diff / first >= 0 );
}
}
},
ID: function(elem, match){
return elem.nodeType === 1 && elem.getAttribute("id") === match;
},
TAG: function(elem, match){
return (match === "*" && elem.nodeType === 1) || elem.nodeName.toLowerCase() === match;
},
CLASS: function(elem, match){
return (" " + (elem.className || elem.getAttribute("class")) + " ")
.indexOf( match ) > -1;
},
ATTR: function(elem, match){
var name = match[1],
result = Expr.attrHandle[ name ] ?
Expr.attrHandle[ name ]( elem ) :
elem[ name ] != null ?
elem[ name ] :
elem.getAttribute( name ),
value = result + "",
type = match[2],
check = match[4];
 
return result == null ?
type === "!=" :
type === "=" ?
value === check :
type === "*=" ?
value.indexOf(check) >= 0 :
type === "~=" ?
(" " + value + " ").indexOf(check) >= 0 :
!check ?
value && result !== false :
type === "!=" ?
value !== check :
type === "^=" ?
value.indexOf(check) === 0 :
type === "$=" ?
value.substr(value.length - check.length) === check :
type === "|=" ?
value === check || value.substr(0, check.length + 1) === check + "-" :
false;
},
POS: function(elem, match, i, array){
var name = match[2], filter = Expr.setFilters[ name ];
 
if ( filter ) {
return filter( elem, i, match, array );
}
}
}
};
 
var origPOS = Expr.match.POS;
 
for ( var type in Expr.match ) {
Expr.match[ type ] = new RegExp( Expr.match[ type ].source + /(?![^\[]*\])(?![^\(]*\))/.source );
Expr.leftMatch[ type ] = new RegExp( /(^(?:.|\r|\n)*?)/.source + Expr.match[ type ].source.replace(/\\(\d+)/g, function(all, num){
return "\\" + (num - 0 + 1);
}));
}
 
var makeArray = function(array, results) {
array = Array.prototype.slice.call( array, 0 );
 
if ( results ) {
results.push.apply( results, array );
return results;
}
return array;
};
 
// Perform a simple check to determine if the browser is capable of
// converting a NodeList to an array using builtin methods.
// Also verifies that the returned array holds DOM nodes
// (which is not the case in the Blackberry browser)
try {
Array.prototype.slice.call( document.documentElement.childNodes, 0 )[0].nodeType;
 
// Provide a fallback method if it does not work
} catch(e){
makeArray = function(array, results) {
var ret = results || [];
 
if ( toString.call(array) === "[object Array]" ) {
Array.prototype.push.apply( ret, array );
} else {
if ( typeof array.length === "number" ) {
for ( var i = 0, l = array.length; i < l; i++ ) {
ret.push( array[i] );
}
} else {
for ( var i = 0; array[i]; i++ ) {
ret.push( array[i] );
}
}
}
 
return ret;
};
}
 
var sortOrder;
 
if ( document.documentElement.compareDocumentPosition ) {
sortOrder = function( a, b ) {
if ( !a.compareDocumentPosition || !b.compareDocumentPosition ) {
if ( a == b ) {
hasDuplicate = true;
}
return a.compareDocumentPosition ? -1 : 1;
}
 
var ret = a.compareDocumentPosition(b) & 4 ? -1 : a === b ? 0 : 1;
if ( ret === 0 ) {
hasDuplicate = true;
}
return ret;
};
} else if ( "sourceIndex" in document.documentElement ) {
sortOrder = function( a, b ) {
if ( !a.sourceIndex || !b.sourceIndex ) {
if ( a == b ) {
hasDuplicate = true;
}
return a.sourceIndex ? -1 : 1;
}
 
var ret = a.sourceIndex - b.sourceIndex;
if ( ret === 0 ) {
hasDuplicate = true;
}
return ret;
};
} else if ( document.createRange ) {
sortOrder = function( a, b ) {
if ( !a.ownerDocument || !b.ownerDocument ) {
if ( a == b ) {
hasDuplicate = true;
}
return a.ownerDocument ? -1 : 1;
}
 
var aRange = a.ownerDocument.createRange(), bRange = b.ownerDocument.createRange();
aRange.setStart(a, 0);
aRange.setEnd(a, 0);
bRange.setStart(b, 0);
bRange.setEnd(b, 0);
var ret = aRange.compareBoundaryPoints(Range.START_TO_END, bRange);
if ( ret === 0 ) {
hasDuplicate = true;
}
return ret;
};
}
 
// Utility function for retreiving the text value of an array of DOM nodes
function getText( elems ) {
var ret = "", elem;
 
for ( var i = 0; elems[i]; i++ ) {
elem = elems[i];
 
// Get the text from text nodes and CDATA nodes
if ( elem.nodeType === 3 || elem.nodeType === 4 ) {
ret += elem.nodeValue;
 
// Traverse everything else, except comment nodes
} else if ( elem.nodeType !== 8 ) {
ret += getText( elem.childNodes );
}
}
 
return ret;
}
 
// Check to see if the browser returns elements by name when
// querying by getElementById (and provide a workaround)
(function(){
// We're going to inject a fake input element with a specified name
var form = document.createElement("div"),
id = "script" + (new Date).getTime();
form.innerHTML = "<a name='" + id + "'/>";
 
// Inject it into the root element, check its status, and remove it quickly
var root = document.documentElement;
root.insertBefore( form, root.firstChild );
 
// The workaround has to do additional checks after a getElementById
// Which slows things down for other browsers (hence the branching)
if ( document.getElementById( id ) ) {
Expr.find.ID = function(match, context, isXML){
if ( typeof context.getElementById !== "undefined" && !isXML ) {
var m = context.getElementById(match[1]);
return m ? m.id === match[1] || typeof m.getAttributeNode !== "undefined" && m.getAttributeNode("id").nodeValue === match[1] ? [m] : undefined : [];
}
};
 
Expr.filter.ID = function(elem, match){
var node = typeof elem.getAttributeNode !== "undefined" && elem.getAttributeNode("id");
return elem.nodeType === 1 && node && node.nodeValue === match;
};
}
 
root.removeChild( form );
root = form = null; // release memory in IE
})();
 
(function(){
// Check to see if the browser returns only elements
// when doing getElementsByTagName("*")
 
// Create a fake element
var div = document.createElement("div");
div.appendChild( document.createComment("") );
 
// Make sure no comments are found
if ( div.getElementsByTagName("*").length > 0 ) {
Expr.find.TAG = function(match, context){
var results = context.getElementsByTagName(match[1]);
 
// Filter out possible comments
if ( match[1] === "*" ) {
var tmp = [];
 
for ( var i = 0; results[i]; i++ ) {
if ( results[i].nodeType === 1 ) {
tmp.push( results[i] );
}
}
 
results = tmp;
}
 
return results;
};
}
 
// Check to see if an attribute returns normalized href attributes
div.innerHTML = "<a href='#'></a>";
if ( div.firstChild && typeof div.firstChild.getAttribute !== "undefined" &&
div.firstChild.getAttribute("href") !== "#" ) {
Expr.attrHandle.href = function(elem){
return elem.getAttribute("href", 2);
};
}
 
div = null; // release memory in IE
})();
 
if ( document.querySelectorAll ) {
(function(){
var oldSizzle = Sizzle, div = document.createElement("div");
div.innerHTML = "<p class='TEST'></p>";
 
// Safari can't handle uppercase or unicode characters when
// in quirks mode.
if ( div.querySelectorAll && div.querySelectorAll(".TEST").length === 0 ) {
return;
}
Sizzle = function(query, context, extra, seed){
context = context || document;
 
// Only use querySelectorAll on non-XML documents
// (ID selectors don't work in non-HTML documents)
if ( !seed && context.nodeType === 9 && !isXML(context) ) {
try {
return makeArray( context.querySelectorAll(query), extra );
} catch(e){}
}
return oldSizzle(query, context, extra, seed);
};
 
for ( var prop in oldSizzle ) {
Sizzle[ prop ] = oldSizzle[ prop ];
}
 
div = null; // release memory in IE
})();
}
 
(function(){
var div = document.createElement("div");
 
div.innerHTML = "<div class='test e'></div><div class='test'></div>";
 
// Opera can't find a second classname (in 9.6)
// Also, make sure that getElementsByClassName actually exists
if ( !div.getElementsByClassName || div.getElementsByClassName("e").length === 0 ) {
return;
}
 
// Safari caches class attributes, doesn't catch changes (in 3.2)
div.lastChild.className = "e";
 
if ( div.getElementsByClassName("e").length === 1 ) {
return;
}
Expr.order.splice(1, 0, "CLASS");
Expr.find.CLASS = function(match, context, isXML) {
if ( typeof context.getElementsByClassName !== "undefined" && !isXML ) {
return context.getElementsByClassName(match[1]);
}
};
 
div = null; // release memory in IE
})();
 
function dirNodeCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
var elem = checkSet[i];
if ( elem ) {
elem = elem[dir];
var match = false;
 
while ( elem ) {
if ( elem.sizcache === doneName ) {
match = checkSet[elem.sizset];
break;
}
 
if ( elem.nodeType === 1 && !isXML ){
elem.sizcache = doneName;
elem.sizset = i;
}
 
if ( elem.nodeName.toLowerCase() === cur ) {
match = elem;
break;
}
 
elem = elem[dir];
}
 
checkSet[i] = match;
}
}
}
 
function dirCheck( dir, cur, doneName, checkSet, nodeCheck, isXML ) {
for ( var i = 0, l = checkSet.length; i < l; i++ ) {
var elem = checkSet[i];
if ( elem ) {
elem = elem[dir];
var match = false;
 
while ( elem ) {
if ( elem.sizcache === doneName ) {
match = checkSet[elem.sizset];
break;
}
 
if ( elem.nodeType === 1 ) {
if ( !isXML ) {
elem.sizcache = doneName;
elem.sizset = i;
}
if ( typeof cur !== "string" ) {
if ( elem === cur ) {
match = true;
break;
}
 
} else if ( Sizzle.filter( cur, [elem] ).length > 0 ) {
match = elem;
break;
}
}
 
elem = elem[dir];
}
 
checkSet[i] = match;
}
}
}
 
var contains = document.compareDocumentPosition ? function(a, b){
return !!(a.compareDocumentPosition(b) & 16);
} : function(a, b){
return a !== b && (a.contains ? a.contains(b) : true);
};
 
var isXML = function(elem){
// documentElement is verified for cases where it doesn't yet exist
// (such as loading iframes in IE - #4833)
var documentElement = (elem ? elem.ownerDocument || elem : 0).documentElement;
return documentElement ? documentElement.nodeName !== "HTML" : false;
};
 
var posProcess = function(selector, context){
var tmpSet = [], later = "", match,
root = context.nodeType ? [context] : context;
 
// Position selectors must be done after the filter
// And so must :not(positional) so we move all PSEUDOs to the end
while ( (match = Expr.match.PSEUDO.exec( selector )) ) {
later += match[0];
selector = selector.replace( Expr.match.PSEUDO, "" );
}
 
selector = Expr.relative[selector] ? selector + "*" : selector;
 
for ( var i = 0, l = root.length; i < l; i++ ) {
Sizzle( selector, root[i], tmpSet );
}
 
return Sizzle.filter( later, tmpSet );
};
 
// EXPOSE
jQuery.find = Sizzle;
jQuery.expr = Sizzle.selectors;
jQuery.expr[":"] = jQuery.expr.filters;
jQuery.unique = Sizzle.uniqueSort;
jQuery.text = getText;
jQuery.isXMLDoc = isXML;
jQuery.contains = contains;
 
return;
 
window.Sizzle = Sizzle;
 
})();
var runtil = /Until$/,
rparentsprev = /^(?:parents|prevUntil|prevAll)/,
// Note: This RegExp should be improved, or likely pulled from Sizzle
rmultiselector = /,/,
slice = Array.prototype.slice;
 
// Implement the identical functionality for filter and not
var winnow = function( elements, qualifier, keep ) {
if ( jQuery.isFunction( qualifier ) ) {
return jQuery.grep(elements, function( elem, i ) {
return !!qualifier.call( elem, i, elem ) === keep;
});
 
} else if ( qualifier.nodeType ) {
return jQuery.grep(elements, function( elem, i ) {
return (elem === qualifier) === keep;
});
 
} else if ( typeof qualifier === "string" ) {
var filtered = jQuery.grep(elements, function( elem ) {
return elem.nodeType === 1;
});
 
if ( isSimple.test( qualifier ) ) {
return jQuery.filter(qualifier, filtered, !keep);
} else {
qualifier = jQuery.filter( qualifier, filtered );
}
}
 
return jQuery.grep(elements, function( elem, i ) {
return (jQuery.inArray( elem, qualifier ) >= 0) === keep;
});
};
 
jQuery.fn.extend({
find: function( selector ) {
var ret = this.pushStack( "", "find", selector ), length = 0;
 
for ( var i = 0, l = this.length; i < l; i++ ) {
length = ret.length;
jQuery.find( selector, this[i], ret );
 
if ( i > 0 ) {
// Make sure that the results are unique
for ( var n = length; n < ret.length; n++ ) {
for ( var r = 0; r < length; r++ ) {
if ( ret[r] === ret[n] ) {
ret.splice(n--, 1);
break;
}
}
}
}
}
 
return ret;
},
 
has: function( target ) {
var targets = jQuery( target );
return this.filter(function() {
for ( var i = 0, l = targets.length; i < l; i++ ) {
if ( jQuery.contains( this, targets[i] ) ) {
return true;
}
}
});
},
 
not: function( selector ) {
return this.pushStack( winnow(this, selector, false), "not", selector);
},
 
filter: function( selector ) {
return this.pushStack( winnow(this, selector, true), "filter", selector );
},
is: function( selector ) {
return !!selector && jQuery.filter( selector, this ).length > 0;
},
 
closest: function( selectors, context ) {
if ( jQuery.isArray( selectors ) ) {
var ret = [], cur = this[0], match, matches = {}, selector;
 
if ( cur && selectors.length ) {
for ( var i = 0, l = selectors.length; i < l; i++ ) {
selector = selectors[i];
 
if ( !matches[selector] ) {
matches[selector] = jQuery.expr.match.POS.test( selector ) ?
jQuery( selector, context || this.context ) :
selector;
}
}
 
while ( cur && cur.ownerDocument && cur !== context ) {
for ( selector in matches ) {
match = matches[selector];
 
if ( match.jquery ? match.index(cur) > -1 : jQuery(cur).is(match) ) {
ret.push({ selector: selector, elem: cur });
delete matches[selector];
}
}
cur = cur.parentNode;
}
}
 
return ret;
}
 
var pos = jQuery.expr.match.POS.test( selectors ) ?
jQuery( selectors, context || this.context ) : null;
 
return this.map(function( i, cur ) {
while ( cur && cur.ownerDocument && cur !== context ) {
if ( pos ? pos.index(cur) > -1 : jQuery(cur).is(selectors) ) {
return cur;
}
cur = cur.parentNode;
}
return null;
});
},
// Determine the position of an element within
// the matched set of elements
index: function( elem ) {
if ( !elem || typeof elem === "string" ) {
return jQuery.inArray( this[0],
// If it receives a string, the selector is used
// If it receives nothing, the siblings are used
elem ? jQuery( elem ) : this.parent().children() );
}
// Locate the position of the desired element
return jQuery.inArray(
// If it receives a jQuery object, the first element is used
elem.jquery ? elem[0] : elem, this );
},
 
add: function( selector, context ) {
var set = typeof selector === "string" ?
jQuery( selector, context || this.context ) :
jQuery.makeArray( selector ),
all = jQuery.merge( this.get(), set );
 
return this.pushStack( isDisconnected( set[0] ) || isDisconnected( all[0] ) ?
all :
jQuery.unique( all ) );
},
 
andSelf: function() {
return this.add( this.prevObject );
}
});
 
// A painfully simple check to see if an element is disconnected
// from a document (should be improved, where feasible).
function isDisconnected( node ) {
return !node || !node.parentNode || node.parentNode.nodeType === 11;
}
 
jQuery.each({
parent: function( elem ) {
var parent = elem.parentNode;
return parent && parent.nodeType !== 11 ? parent : null;
},
parents: function( elem ) {
return jQuery.dir( elem, "parentNode" );
},
parentsUntil: function( elem, i, until ) {
return jQuery.dir( elem, "parentNode", until );
},
next: function( elem ) {
return jQuery.nth( elem, 2, "nextSibling" );
},
prev: function( elem ) {
return jQuery.nth( elem, 2, "previousSibling" );
},
nextAll: function( elem ) {
return jQuery.dir( elem, "nextSibling" );
},
prevAll: function( elem ) {
return jQuery.dir( elem, "previousSibling" );
},
nextUntil: function( elem, i, until ) {
return jQuery.dir( elem, "nextSibling", until );
},
prevUntil: function( elem, i, until ) {
return jQuery.dir( elem, "previousSibling", until );
},
siblings: function( elem ) {
return jQuery.sibling( elem.parentNode.firstChild, elem );
},
children: function( elem ) {
return jQuery.sibling( elem.firstChild );
},
contents: function( elem ) {
return jQuery.nodeName( elem, "iframe" ) ?
elem.contentDocument || elem.contentWindow.document :
jQuery.makeArray( elem.childNodes );
}
}, function( name, fn ) {
jQuery.fn[ name ] = function( until, selector ) {
var ret = jQuery.map( this, fn, until );
if ( !runtil.test( name ) ) {
selector = until;
}
 
if ( selector && typeof selector === "string" ) {
ret = jQuery.filter( selector, ret );
}
 
ret = this.length > 1 ? jQuery.unique( ret ) : ret;
 
if ( (this.length > 1 || rmultiselector.test( selector )) && rparentsprev.test( name ) ) {
ret = ret.reverse();
}
 
return this.pushStack( ret, name, slice.call(arguments).join(",") );
};
});
 
jQuery.extend({
filter: function( expr, elems, not ) {
if ( not ) {
expr = ":not(" + expr + ")";
}
 
return jQuery.find.matches(expr, elems);
},
dir: function( elem, dir, until ) {
var matched = [], cur = elem[dir];
while ( cur && cur.nodeType !== 9 && (until === undefined || cur.nodeType !== 1 || !jQuery( cur ).is( until )) ) {
if ( cur.nodeType === 1 ) {
matched.push( cur );
}
cur = cur[dir];
}
return matched;
},
 
nth: function( cur, result, dir, elem ) {
result = result || 1;
var num = 0;
 
for ( ; cur; cur = cur[dir] ) {
if ( cur.nodeType === 1 && ++num === result ) {
break;
}
}
 
return cur;
},
 
sibling: function( n, elem ) {
var r = [];
 
for ( ; n; n = n.nextSibling ) {
if ( n.nodeType === 1 && n !== elem ) {
r.push( n );
}
}
 
return r;
}
});
var rinlinejQuery = / jQuery\d+="(?:\d+|null)"/g,
rleadingWhitespace = /^\s+/,
rxhtmlTag = /(<([\w:]+)[^>]*?)\/>/g,
rselfClosing = /^(?:area|br|col|embed|hr|img|input|link|meta|param)$/i,
rtagName = /<([\w:]+)/,
rtbody = /<tbody/i,
rhtml = /<|&#?\w+;/,
rnocache = /<script|<object|<embed|<option|<style/i,
rchecked = /checked\s*(?:[^=]|=\s*.checked.)/i, // checked="checked" or checked (html5)
fcloseTag = function( all, front, tag ) {
return rselfClosing.test( tag ) ?
all :
front + "></" + tag + ">";
},
wrapMap = {
option: [ 1, "<select multiple='multiple'>", "</select>" ],
legend: [ 1, "<fieldset>", "</fieldset>" ],
thead: [ 1, "<table>", "</table>" ],
tr: [ 2, "<table><tbody>", "</tbody></table>" ],
td: [ 3, "<table><tbody><tr>", "</tr></tbody></table>" ],
col: [ 2, "<table><tbody></tbody><colgroup>", "</colgroup></table>" ],
area: [ 1, "<map>", "</map>" ],
_default: [ 0, "", "" ]
};
 
wrapMap.optgroup = wrapMap.option;
wrapMap.tbody = wrapMap.tfoot = wrapMap.colgroup = wrapMap.caption = wrapMap.thead;
wrapMap.th = wrapMap.td;
 
// IE can't serialize <link> and <script> tags normally
if ( !jQuery.support.htmlSerialize ) {
wrapMap._default = [ 1, "div<div>", "</div>" ];
}
 
jQuery.fn.extend({
text: function( text ) {
if ( jQuery.isFunction(text) ) {
return this.each(function(i) {
var self = jQuery(this);
self.text( text.call(this, i, self.text()) );
});
}
 
if ( typeof text !== "object" && text !== undefined ) {
return this.empty().append( (this[0] && this[0].ownerDocument || document).createTextNode( text ) );
}
 
return jQuery.text( this );
},
 
wrapAll: function( html ) {
if ( jQuery.isFunction( html ) ) {
return this.each(function(i) {
jQuery(this).wrapAll( html.call(this, i) );
});
}
 
if ( this[0] ) {
// The elements to wrap the target around
var wrap = jQuery( html, this[0].ownerDocument ).eq(0).clone(true);
 
if ( this[0].parentNode ) {
wrap.insertBefore( this[0] );
}
 
wrap.map(function() {
var elem = this;
 
while ( elem.firstChild && elem.firstChild.nodeType === 1 ) {
elem = elem.firstChild;
}
 
return elem;
}).append(this);
}
 
return this;
},
 
wrapInner: function( html ) {
if ( jQuery.isFunction( html ) ) {
return this.each(function(i) {
jQuery(this).wrapInner( html.call(this, i) );
});
}
 
return this.each(function() {
var self = jQuery( this ), contents = self.contents();
 
if ( contents.length ) {
contents.wrapAll( html );
 
} else {
self.append( html );
}
});
},
 
wrap: function( html ) {
return this.each(function() {
jQuery( this ).wrapAll( html );
});
},
 
unwrap: function() {
return this.parent().each(function() {
if ( !jQuery.nodeName( this, "body" ) ) {
jQuery( this ).replaceWith( this.childNodes );
}
}).end();
},
 
append: function() {
return this.domManip(arguments, true, function( elem ) {
if ( this.nodeType === 1 ) {
this.appendChild( elem );
}
});
},
 
prepend: function() {
return this.domManip(arguments, true, function( elem ) {
if ( this.nodeType === 1 ) {
this.insertBefore( elem, this.firstChild );
}
});
},
 
before: function() {
if ( this[0] && this[0].parentNode ) {
return this.domManip(arguments, false, function( elem ) {
this.parentNode.insertBefore( elem, this );
});
} else if ( arguments.length ) {
var set = jQuery(arguments[0]);
set.push.apply( set, this.toArray() );
return this.pushStack( set, "before", arguments );
}
},
 
after: function() {
if ( this[0] && this[0].parentNode ) {
return this.domManip(arguments, false, function( elem ) {
this.parentNode.insertBefore( elem, this.nextSibling );
});
} else if ( arguments.length ) {
var set = this.pushStack( this, "after", arguments );
set.push.apply( set, jQuery(arguments[0]).toArray() );
return set;
}
},
// keepData is for internal use only--do not document
remove: function( selector, keepData ) {
for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
if ( !selector || jQuery.filter( selector, [ elem ] ).length ) {
if ( !keepData && elem.nodeType === 1 ) {
jQuery.cleanData( elem.getElementsByTagName("*") );
jQuery.cleanData( [ elem ] );
}
 
if ( elem.parentNode ) {
elem.parentNode.removeChild( elem );
}
}
}
return this;
},
 
empty: function() {
for ( var i = 0, elem; (elem = this[i]) != null; i++ ) {
// Remove element nodes and prevent memory leaks
if ( elem.nodeType === 1 ) {
jQuery.cleanData( elem.getElementsByTagName("*") );
}
 
// Remove any remaining nodes
while ( elem.firstChild ) {
elem.removeChild( elem.firstChild );
}
}
return this;
},
 
clone: function( events ) {
// Do the clone
var ret = this.map(function() {
if ( !jQuery.support.noCloneEvent && !jQuery.isXMLDoc(this) ) {
// IE copies events bound via attachEvent when
// using cloneNode. Calling detachEvent on the
// clone will also remove the events from the orignal
// In order to get around this, we use innerHTML.
// Unfortunately, this means some modifications to
// attributes in IE that are actually only stored
// as properties will not be copied (such as the
// the name attribute on an input).
var html = this.outerHTML, ownerDocument = this.ownerDocument;
if ( !html ) {
var div = ownerDocument.createElement("div");
div.appendChild( this.cloneNode(true) );
html = div.innerHTML;
}
 
return jQuery.clean([html.replace(rinlinejQuery, "")
// Handle the case in IE 8 where action=/test/> self-closes a tag
.replace(/=([^="'>\s]+\/)>/g, '="$1">')
.replace(rleadingWhitespace, "")], ownerDocument)[0];
} else {
return this.cloneNode(true);
}
});
 
// Copy the events from the original to the clone
if ( events === true ) {
cloneCopyEvent( this, ret );
cloneCopyEvent( this.find("*"), ret.find("*") );
}
 
// Return the cloned set
return ret;
},
 
html: function( value ) {
if ( value === undefined ) {
return this[0] && this[0].nodeType === 1 ?
this[0].innerHTML.replace(rinlinejQuery, "") :
null;
 
// See if we can take a shortcut and just use innerHTML
} else if ( typeof value === "string" && !rnocache.test( value ) &&
(jQuery.support.leadingWhitespace || !rleadingWhitespace.test( value )) &&
!wrapMap[ (rtagName.exec( value ) || ["", ""])[1].toLowerCase() ] ) {
 
value = value.replace(rxhtmlTag, fcloseTag);
 
try {
for ( var i = 0, l = this.length; i < l; i++ ) {
// Remove element nodes and prevent memory leaks
if ( this[i].nodeType === 1 ) {
jQuery.cleanData( this[i].getElementsByTagName("*") );
this[i].innerHTML = value;
}
}
 
// If using innerHTML throws an exception, use the fallback method
} catch(e) {
this.empty().append( value );
}
 
} else if ( jQuery.isFunction( value ) ) {
this.each(function(i){
var self = jQuery(this), old = self.html();
self.empty().append(function(){
return value.call( this, i, old );
});
});
 
} else {
this.empty().append( value );
}
 
return this;
},
 
replaceWith: function( value ) {
if ( this[0] && this[0].parentNode ) {
// Make sure that the elements are removed from the DOM before they are inserted
// this can help fix replacing a parent with child elements
if ( jQuery.isFunction( value ) ) {
return this.each(function(i) {
var self = jQuery(this), old = self.html();
self.replaceWith( value.call( this, i, old ) );
});
}
 
if ( typeof value !== "string" ) {
value = jQuery(value).detach();
}
 
return this.each(function() {
var next = this.nextSibling, parent = this.parentNode;
 
jQuery(this).remove();
 
if ( next ) {
jQuery(next).before( value );
} else {
jQuery(parent).append( value );
}
});
} else {
return this.pushStack( jQuery(jQuery.isFunction(value) ? value() : value), "replaceWith", value );
}
},
 
detach: function( selector ) {
return this.remove( selector, true );
},
 
domManip: function( args, table, callback ) {
var results, first, value = args[0], scripts = [], fragment, parent;
 
// We can't cloneNode fragments that contain checked, in WebKit
if ( !jQuery.support.checkClone && arguments.length === 3 && typeof value === "string" && rchecked.test( value ) ) {
return this.each(function() {
jQuery(this).domManip( args, table, callback, true );
});
}
 
if ( jQuery.isFunction(value) ) {
return this.each(function(i) {
var self = jQuery(this);
args[0] = value.call(this, i, table ? self.html() : undefined);
self.domManip( args, table, callback );
});
}
 
if ( this[0] ) {
parent = value && value.parentNode;
 
// If we're in a fragment, just use that instead of building a new one
if ( jQuery.support.parentNode && parent && parent.nodeType === 11 && parent.childNodes.length === this.length ) {
results = { fragment: parent };
 
} else {
results = buildFragment( args, this, scripts );
}
fragment = results.fragment;
if ( fragment.childNodes.length === 1 ) {
first = fragment = fragment.firstChild;
} else {
first = fragment.firstChild;
}
 
if ( first ) {
table = table && jQuery.nodeName( first, "tr" );
 
for ( var i = 0, l = this.length; i < l; i++ ) {
callback.call(
table ?
root(this[i], first) :
this[i],
i > 0 || results.cacheable || this.length > 1 ?
fragment.cloneNode(true) :
fragment
);
}
}
 
if ( scripts.length ) {
jQuery.each( scripts, evalScript );
}
}
 
return this;
 
function root( elem, cur ) {
return jQuery.nodeName(elem, "table") ?
(elem.getElementsByTagName("tbody")[0] ||
elem.appendChild(elem.ownerDocument.createElement("tbody"))) :
elem;
}
}
});
 
function cloneCopyEvent(orig, ret) {
var i = 0;
 
ret.each(function() {
if ( this.nodeName !== (orig[i] && orig[i].nodeName) ) {
return;
}
 
var oldData = jQuery.data( orig[i++] ), curData = jQuery.data( this, oldData ), events = oldData && oldData.events;
 
if ( events ) {
delete curData.handle;
curData.events = {};
 
for ( var type in events ) {
for ( var handler in events[ type ] ) {
jQuery.event.add( this, type, events[ type ][ handler ], events[ type ][ handler ].data );
}
}
}
});
}
 
function buildFragment( args, nodes, scripts ) {
var fragment, cacheable, cacheresults,
doc = (nodes && nodes[0] ? nodes[0].ownerDocument || nodes[0] : document);
 
// Only cache "small" (1/2 KB) strings that are associated with the main document
// Cloning options loses the selected state, so don't cache them
// IE 6 doesn't like it when you put <object> or <embed> elements in a fragment
// Also, WebKit does not clone 'checked' attributes on cloneNode, so don't cache
if ( args.length === 1 && typeof args[0] === "string" && args[0].length < 512 && doc === document &&
!rnocache.test( args[0] ) && (jQuery.support.checkClone || !rchecked.test( args[0] )) ) {
 
cacheable = true;
cacheresults = jQuery.fragments[ args[0] ];
if ( cacheresults ) {
if ( cacheresults !== 1 ) {
fragment = cacheresults;
}
}
}
 
if ( !fragment ) {
fragment = doc.createDocumentFragment();
jQuery.clean( args, doc, fragment, scripts );
}
 
if ( cacheable ) {
jQuery.fragments[ args[0] ] = cacheresults ? fragment : 1;
}
 
return { fragment: fragment, cacheable: cacheable };
}
 
jQuery.fragments = {};
 
jQuery.each({
appendTo: "append",
prependTo: "prepend",
insertBefore: "before",
insertAfter: "after",
replaceAll: "replaceWith"
}, function( name, original ) {
jQuery.fn[ name ] = function( selector ) {
var ret = [], insert = jQuery( selector ),
parent = this.length === 1 && this[0].parentNode;
if ( parent && parent.nodeType === 11 && parent.childNodes.length === 1 && insert.length === 1 ) {
insert[ original ]( this[0] );
return this;
} else {
for ( var i = 0, l = insert.length; i < l; i++ ) {
var elems = (i > 0 ? this.clone(true) : this).get();
jQuery.fn[ original ].apply( jQuery(insert[i]), elems );
ret = ret.concat( elems );
}
return this.pushStack( ret, name, insert.selector );
}
};
});
 
jQuery.extend({
clean: function( elems, context, fragment, scripts ) {
context = context || document;
 
// !context.createElement fails in IE with an error but returns typeof 'object'
if ( typeof context.createElement === "undefined" ) {
context = context.ownerDocument || context[0] && context[0].ownerDocument || document;
}
 
var ret = [];
 
for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
if ( typeof elem === "number" ) {
elem += "";
}
 
if ( !elem ) {
continue;
}
 
// Convert html string into DOM nodes
if ( typeof elem === "string" && !rhtml.test( elem ) ) {
elem = context.createTextNode( elem );
 
} else if ( typeof elem === "string" ) {
// Fix "XHTML"-style tags in all browsers
elem = elem.replace(rxhtmlTag, fcloseTag);
 
// Trim whitespace, otherwise indexOf won't work as expected
var tag = (rtagName.exec( elem ) || ["", ""])[1].toLowerCase(),
wrap = wrapMap[ tag ] || wrapMap._default,
depth = wrap[0],
div = context.createElement("div");
 
// Go to html and back, then peel off extra wrappers
div.innerHTML = wrap[1] + elem + wrap[2];
 
// Move to the right depth
while ( depth-- ) {
div = div.lastChild;
}
 
// Remove IE's autoinserted <tbody> from table fragments
if ( !jQuery.support.tbody ) {
 
// String was a <table>, *may* have spurious <tbody>
var hasBody = rtbody.test(elem),
tbody = tag === "table" && !hasBody ?
div.firstChild && div.firstChild.childNodes :
 
// String was a bare <thead> or <tfoot>
wrap[1] === "<table>" && !hasBody ?
div.childNodes :
[];
 
for ( var j = tbody.length - 1; j >= 0 ; --j ) {
if ( jQuery.nodeName( tbody[ j ], "tbody" ) && !tbody[ j ].childNodes.length ) {
tbody[ j ].parentNode.removeChild( tbody[ j ] );
}
}
 
}
 
// IE completely kills leading whitespace when innerHTML is used
if ( !jQuery.support.leadingWhitespace && rleadingWhitespace.test( elem ) ) {
div.insertBefore( context.createTextNode( rleadingWhitespace.exec(elem)[0] ), div.firstChild );
}
 
elem = div.childNodes;
}
 
if ( elem.nodeType ) {
ret.push( elem );
} else {
ret = jQuery.merge( ret, elem );
}
}
 
if ( fragment ) {
for ( var i = 0; ret[i]; i++ ) {
if ( scripts && jQuery.nodeName( ret[i], "script" ) && (!ret[i].type || ret[i].type.toLowerCase() === "text/javascript") ) {
scripts.push( ret[i].parentNode ? ret[i].parentNode.removeChild( ret[i] ) : ret[i] );
} else {
if ( ret[i].nodeType === 1 ) {
ret.splice.apply( ret, [i + 1, 0].concat(jQuery.makeArray(ret[i].getElementsByTagName("script"))) );
}
fragment.appendChild( ret[i] );
}
}
}
 
return ret;
},
cleanData: function( elems ) {
var data, id, cache = jQuery.cache,
special = jQuery.event.special,
deleteExpando = jQuery.support.deleteExpando;
for ( var i = 0, elem; (elem = elems[i]) != null; i++ ) {
id = elem[ jQuery.expando ];
if ( id ) {
data = cache[ id ];
if ( data.events ) {
for ( var type in data.events ) {
if ( special[ type ] ) {
jQuery.event.remove( elem, type );
 
} else {
removeEvent( elem, type, data.handle );
}
}
}
if ( deleteExpando ) {
delete elem[ jQuery.expando ];
 
} else if ( elem.removeAttribute ) {
elem.removeAttribute( jQuery.expando );
}
delete cache[ id ];
}
}
}
});
// exclude the following css properties to add px
var rexclude = /z-?index|font-?weight|opacity|zoom|line-?height/i,
ralpha = /alpha\([^)]*\)/,
ropacity = /opacity=([^)]*)/,
rfloat = /float/i,
rdashAlpha = /-([a-z])/ig,
rupper = /([A-Z])/g,
rnumpx = /^-?\d+(?:px)?$/i,
rnum = /^-?\d/,
 
cssShow = { position: "absolute", visibility: "hidden", display:"block" },
cssWidth = [ "Left", "Right" ],
cssHeight = [ "Top", "Bottom" ],
 
// cache check for defaultView.getComputedStyle
getComputedStyle = document.defaultView && document.defaultView.getComputedStyle,
// normalize float css property
styleFloat = jQuery.support.cssFloat ? "cssFloat" : "styleFloat",
fcamelCase = function( all, letter ) {
return letter.toUpperCase();
};
 
jQuery.fn.css = function( name, value ) {
return access( this, name, value, true, function( elem, name, value ) {
if ( value === undefined ) {
return jQuery.curCSS( elem, name );
}
if ( typeof value === "number" && !rexclude.test(name) ) {
value += "px";
}
 
jQuery.style( elem, name, value );
});
};
 
jQuery.extend({
style: function( elem, name, value ) {
// don't set styles on text and comment nodes
if ( !elem || elem.nodeType === 3 || elem.nodeType === 8 ) {
return undefined;
}
 
// ignore negative width and height values #1599
if ( (name === "width" || name === "height") && parseFloat(value) < 0 ) {
value = undefined;
}
 
var style = elem.style || elem, set = value !== undefined;
 
// IE uses filters for opacity
if ( !jQuery.support.opacity && name === "opacity" ) {
if ( set ) {
// IE has trouble with opacity if it does not have layout
// Force it by setting the zoom level
style.zoom = 1;
 
// Set the alpha filter to set the opacity
var opacity = parseInt( value, 10 ) + "" === "NaN" ? "" : "alpha(opacity=" + value * 100 + ")";
var filter = style.filter || jQuery.curCSS( elem, "filter" ) || "";
style.filter = ralpha.test(filter) ? filter.replace(ralpha, opacity) : opacity;
}
 
return style.filter && style.filter.indexOf("opacity=") >= 0 ?
(parseFloat( ropacity.exec(style.filter)[1] ) / 100) + "":
"";
}
 
// Make sure we're using the right name for getting the float value
if ( rfloat.test( name ) ) {
name = styleFloat;
}
 
name = name.replace(rdashAlpha, fcamelCase);
 
if ( set ) {
style[ name ] = value;
}
 
return style[ name ];
},
 
css: function( elem, name, force, extra ) {
if ( name === "width" || name === "height" ) {
var val, props = cssShow, which = name === "width" ? cssWidth : cssHeight;
 
function getWH() {
val = name === "width" ? elem.offsetWidth : elem.offsetHeight;
 
if ( extra === "border" ) {
return;
}
 
jQuery.each( which, function() {
if ( !extra ) {
val -= parseFloat(jQuery.curCSS( elem, "padding" + this, true)) || 0;
}
 
if ( extra === "margin" ) {
val += parseFloat(jQuery.curCSS( elem, "margin" + this, true)) || 0;
} else {
val -= parseFloat(jQuery.curCSS( elem, "border" + this + "Width", true)) || 0;
}
});
}
 
if ( elem.offsetWidth !== 0 ) {
getWH();
} else {
jQuery.swap( elem, props, getWH );
}
 
return Math.max(0, Math.round(val));
}
 
return jQuery.curCSS( elem, name, force );
},
 
curCSS: function( elem, name, force ) {
var ret, style = elem.style, filter;
 
// IE uses filters for opacity
if ( !jQuery.support.opacity && name === "opacity" && elem.currentStyle ) {
ret = ropacity.test(elem.currentStyle.filter || "") ?
(parseFloat(RegExp.$1) / 100) + "" :
"";
 
return ret === "" ?
"1" :
ret;
}
 
// Make sure we're using the right name for getting the float value
if ( rfloat.test( name ) ) {
name = styleFloat;
}
 
if ( !force && style && style[ name ] ) {
ret = style[ name ];
 
} else if ( getComputedStyle ) {
 
// Only "float" is needed here
if ( rfloat.test( name ) ) {
name = "float";
}
 
name = name.replace( rupper, "-$1" ).toLowerCase();
 
var defaultView = elem.ownerDocument.defaultView;
 
if ( !defaultView ) {
return null;
}
 
var computedStyle = defaultView.getComputedStyle( elem, null );
 
if ( computedStyle ) {
ret = computedStyle.getPropertyValue( name );
}
 
// We should always get a number back from opacity
if ( name === "opacity" && ret === "" ) {
ret = "1";
}
 
} else if ( elem.currentStyle ) {
var camelCase = name.replace(rdashAlpha, fcamelCase);
 
ret = elem.currentStyle[ name ] || elem.currentStyle[ camelCase ];
 
// From the awesome hack by Dean Edwards
// http://erik.eae.net/archives/2007/07/27/18.54.15/#comment-102291
 
// If we're not dealing with a regular pixel number
// but a number that has a weird ending, we need to convert it to pixels
if ( !rnumpx.test( ret ) && rnum.test( ret ) ) {
// Remember the original values
var left = style.left, rsLeft = elem.runtimeStyle.left;
 
// Put in the new values to get a computed value out
elem.runtimeStyle.left = elem.currentStyle.left;
style.left = camelCase === "fontSize" ? "1em" : (ret || 0);
ret = style.pixelLeft + "px";
 
// Revert the changed values
style.left = left;
elem.runtimeStyle.left = rsLeft;
}
}
 
return ret;
},
 
// A method for quickly swapping in/out CSS properties to get correct calculations
swap: function( elem, options, callback ) {
var old = {};
 
// Remember the old values, and insert the new ones
for ( var name in options ) {
old[ name ] = elem.style[ name ];
elem.style[ name ] = options[ name ];
}
 
callback.call( elem );
 
// Revert the old values
for ( var name in options ) {
elem.style[ name ] = old[ name ];
}
}
});
 
if ( jQuery.expr && jQuery.expr.filters ) {
jQuery.expr.filters.hidden = function( elem ) {
var width = elem.offsetWidth, height = elem.offsetHeight,
skip = elem.nodeName.toLowerCase() === "tr";
 
return width === 0 && height === 0 && !skip ?
true :
width > 0 && height > 0 && !skip ?
false :
jQuery.curCSS(elem, "display") === "none";
};
 
jQuery.expr.filters.visible = function( elem ) {
return !jQuery.expr.filters.hidden( elem );
};
}
var jsc = now(),
rscript = /<script(.|\s)*?\/script>/gi,
rselectTextarea = /select|textarea/i,
rinput = /color|date|datetime|email|hidden|month|number|password|range|search|tel|text|time|url|week/i,
jsre = /=\?(&|$)/,
rquery = /\?/,
rts = /(\?|&)_=.*?(&|$)/,
rurl = /^(\w+:)?\/\/([^\/?#]+)/,
r20 = /%20/g,
 
// Keep a copy of the old load method
_load = jQuery.fn.load;
 
jQuery.fn.extend({
load: function( url, params, callback ) {
if ( typeof url !== "string" ) {
return _load.call( this, url );
 
// Don't do a request if no elements are being requested
} else if ( !this.length ) {
return this;
}
 
var off = url.indexOf(" ");
if ( off >= 0 ) {
var selector = url.slice(off, url.length);
url = url.slice(0, off);
}
 
// Default to a GET request
var type = "GET";
 
// If the second parameter was provided
if ( params ) {
// If it's a function
if ( jQuery.isFunction( params ) ) {
// We assume that it's the callback
callback = params;
params = null;
 
// Otherwise, build a param string
} else if ( typeof params === "object" ) {
params = jQuery.param( params, jQuery.ajaxSettings.traditional );
type = "POST";
}
}
 
var self = this;
 
// Request the remote document
jQuery.ajax({
url: url,
type: type,
dataType: "html",
data: params,
complete: function( res, status ) {
// If successful, inject the HTML into all the matched elements
if ( status === "success" || status === "notmodified" ) {
// See if a selector was specified
self.html( selector ?
// Create a dummy div to hold the results
jQuery("<div />")
// inject the contents of the document in, removing the scripts
// to avoid any 'Permission Denied' errors in IE
.append(res.responseText.replace(rscript, ""))
 
// Locate the specified elements
.find(selector) :
 
// If not, just inject the full result
res.responseText );
}
 
if ( callback ) {
self.each( callback, [res.responseText, status, res] );
}
}
});
 
return this;
},
 
serialize: function() {
return jQuery.param(this.serializeArray());
},
serializeArray: function() {
return this.map(function() {
return this.elements ? jQuery.makeArray(this.elements) : this;
})
.filter(function() {
return this.name && !this.disabled &&
(this.checked || rselectTextarea.test(this.nodeName) ||
rinput.test(this.type));
})
.map(function( i, elem ) {
var val = jQuery(this).val();
 
return val == null ?
null :
jQuery.isArray(val) ?
jQuery.map( val, function( val, i ) {
return { name: elem.name, value: val };
}) :
{ name: elem.name, value: val };
}).get();
}
});
 
// Attach a bunch of functions for handling common AJAX events
jQuery.each( "ajaxStart ajaxStop ajaxComplete ajaxError ajaxSuccess ajaxSend".split(" "), function( i, o ) {
jQuery.fn[o] = function( f ) {
return this.bind(o, f);
};
});
 
jQuery.extend({
 
get: function( url, data, callback, type ) {
// shift arguments if data argument was omited
if ( jQuery.isFunction( data ) ) {
type = type || callback;
callback = data;
data = null;
}
 
return jQuery.ajax({
type: "GET",
url: url,
data: data,
success: callback,
dataType: type
});
},
 
getScript: function( url, callback ) {
return jQuery.get(url, null, callback, "script");
},
 
getJSON: function( url, data, callback ) {
return jQuery.get(url, data, callback, "json");
},
 
post: function( url, data, callback, type ) {
// shift arguments if data argument was omited
if ( jQuery.isFunction( data ) ) {
type = type || callback;
callback = data;
data = {};
}
 
return jQuery.ajax({
type: "POST",
url: url,
data: data,
success: callback,
dataType: type
});
},
 
ajaxSetup: function( settings ) {
jQuery.extend( jQuery.ajaxSettings, settings );
},
 
ajaxSettings: {
url: location.href,
global: true,
type: "GET",
contentType: "application/x-www-form-urlencoded",
processData: true,
async: true,
/*
timeout: 0,
data: null,
username: null,
password: null,
traditional: false,
*/
// Create the request object; Microsoft failed to properly
// implement the XMLHttpRequest in IE7 (can't request local files),
// so we use the ActiveXObject when it is available
// This function can be overriden by calling jQuery.ajaxSetup
xhr: window.XMLHttpRequest && (window.location.protocol !== "file:" || !window.ActiveXObject) ?
function() {
return new window.XMLHttpRequest();
} :
function() {
try {
return new window.ActiveXObject("Microsoft.XMLHTTP");
} catch(e) {}
},
accepts: {
xml: "application/xml, text/xml",
html: "text/html",
script: "text/javascript, application/javascript",
json: "application/json, text/javascript",
text: "text/plain",
_default: "*/*"
}
},
 
// Last-Modified header cache for next request
lastModified: {},
etag: {},
 
ajax: function( origSettings ) {
var s = jQuery.extend(true, {}, jQuery.ajaxSettings, origSettings);
var jsonp, status, data,
callbackContext = origSettings && origSettings.context || s,
type = s.type.toUpperCase();
 
// convert data if not already a string
if ( s.data && s.processData && typeof s.data !== "string" ) {
s.data = jQuery.param( s.data, s.traditional );
}
 
// Handle JSONP Parameter Callbacks
if ( s.dataType === "jsonp" ) {
if ( type === "GET" ) {
if ( !jsre.test( s.url ) ) {
s.url += (rquery.test( s.url ) ? "&" : "?") + (s.jsonp || "callback") + "=?";
}
} else if ( !s.data || !jsre.test(s.data) ) {
s.data = (s.data ? s.data + "&" : "") + (s.jsonp || "callback") + "=?";
}
s.dataType = "json";
}
 
// Build temporary JSONP function
if ( s.dataType === "json" && (s.data && jsre.test(s.data) || jsre.test(s.url)) ) {
jsonp = s.jsonpCallback || ("jsonp" + jsc++);
 
// Replace the =? sequence both in the query string and the data
if ( s.data ) {
s.data = (s.data + "").replace(jsre, "=" + jsonp + "$1");
}
 
s.url = s.url.replace(jsre, "=" + jsonp + "$1");
 
// We need to make sure
// that a JSONP style response is executed properly
s.dataType = "script";
 
// Handle JSONP-style loading
window[ jsonp ] = window[ jsonp ] || function( tmp ) {
data = tmp;
success();
complete();
// Garbage collect
window[ jsonp ] = undefined;
 
try {
delete window[ jsonp ];
} catch(e) {}
 
if ( head ) {
head.removeChild( script );
}
};
}
 
if ( s.dataType === "script" && s.cache === null ) {
s.cache = false;
}
 
if ( s.cache === false && type === "GET" ) {
var ts = now();
 
// try replacing _= if it is there
var ret = s.url.replace(rts, "$1_=" + ts + "$2");
 
// if nothing was replaced, add timestamp to the end
s.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? "&" : "?") + "_=" + ts : "");
}
 
// If data is available, append data to url for get requests
if ( s.data && type === "GET" ) {
s.url += (rquery.test(s.url) ? "&" : "?") + s.data;
}
 
// Watch for a new set of requests
if ( s.global && ! jQuery.active++ ) {
jQuery.event.trigger( "ajaxStart" );
}
 
// Matches an absolute URL, and saves the domain
var parts = rurl.exec( s.url ),
remote = parts && (parts[1] && parts[1] !== location.protocol || parts[2] !== location.host);
 
// If we're requesting a remote document
// and trying to load JSON or Script with a GET
if ( s.dataType === "script" && type === "GET" && remote ) {
var head = document.getElementsByTagName("head")[0] || document.documentElement;
var script = document.createElement("script");
script.src = s.url;
if ( s.scriptCharset ) {
script.charset = s.scriptCharset;
}
 
// Handle Script loading
if ( !jsonp ) {
var done = false;
 
// Attach handlers for all browsers
script.onload = script.onreadystatechange = function() {
if ( !done && (!this.readyState ||
this.readyState === "loaded" || this.readyState === "complete") ) {
done = true;
success();
complete();
 
// Handle memory leak in IE
script.onload = script.onreadystatechange = null;
if ( head && script.parentNode ) {
head.removeChild( script );
}
}
};
}
 
// Use insertBefore instead of appendChild to circumvent an IE6 bug.
// This arises when a base node is used (#2709 and #4378).
head.insertBefore( script, head.firstChild );
 
// We handle everything using the script element injection
return undefined;
}
 
var requestDone = false;
 
// Create the request object
var xhr = s.xhr();
 
if ( !xhr ) {
return;
}
 
// Open the socket
// Passing null username, generates a login popup on Opera (#2865)
if ( s.username ) {
xhr.open(type, s.url, s.async, s.username, s.password);
} else {
xhr.open(type, s.url, s.async);
}
 
// Need an extra try/catch for cross domain requests in Firefox 3
try {
// Set the correct header, if data is being sent
if ( s.data || origSettings && origSettings.contentType ) {
xhr.setRequestHeader("Content-Type", s.contentType);
}
 
// Set the If-Modified-Since and/or If-None-Match header, if in ifModified mode.
if ( s.ifModified ) {
if ( jQuery.lastModified[s.url] ) {
xhr.setRequestHeader("If-Modified-Since", jQuery.lastModified[s.url]);
}
 
if ( jQuery.etag[s.url] ) {
xhr.setRequestHeader("If-None-Match", jQuery.etag[s.url]);
}
}
 
// Set header so the called script knows that it's an XMLHttpRequest
// Only send the header if it's not a remote XHR
if ( !remote ) {
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
}
 
// Set the Accepts header for the server, depending on the dataType
xhr.setRequestHeader("Accept", s.dataType && s.accepts[ s.dataType ] ?
s.accepts[ s.dataType ] + ", */*" :
s.accepts._default );
} catch(e) {}
 
// Allow custom headers/mimetypes and early abort
if ( s.beforeSend && s.beforeSend.call(callbackContext, xhr, s) === false ) {
// Handle the global AJAX counter
if ( s.global && ! --jQuery.active ) {
jQuery.event.trigger( "ajaxStop" );
}
 
// close opended socket
xhr.abort();
return false;
}
 
if ( s.global ) {
trigger("ajaxSend", [xhr, s]);
}
 
// Wait for a response to come back
var onreadystatechange = xhr.onreadystatechange = function( isTimeout ) {
// The request was aborted
if ( !xhr || xhr.readyState === 0 || isTimeout === "abort" ) {
// Opera doesn't call onreadystatechange before this point
// so we simulate the call
if ( !requestDone ) {
complete();
}
 
requestDone = true;
if ( xhr ) {
xhr.onreadystatechange = jQuery.noop;
}
 
// The transfer is complete and the data is available, or the request timed out
} else if ( !requestDone && xhr && (xhr.readyState === 4 || isTimeout === "timeout") ) {
requestDone = true;
xhr.onreadystatechange = jQuery.noop;
 
status = isTimeout === "timeout" ?
"timeout" :
!jQuery.httpSuccess( xhr ) ?
"error" :
s.ifModified && jQuery.httpNotModified( xhr, s.url ) ?
"notmodified" :
"success";
 
var errMsg;
 
if ( status === "success" ) {
// Watch for, and catch, XML document parse errors
try {
// process the data (runs the xml through httpData regardless of callback)
data = jQuery.httpData( xhr, s.dataType, s );
} catch(err) {
status = "parsererror";
errMsg = err;
}
}
 
// Make sure that the request was successful or notmodified
if ( status === "success" || status === "notmodified" ) {
// JSONP handles its own success callback
if ( !jsonp ) {
success();
}
} else {
jQuery.handleError(s, xhr, status, errMsg);
}
 
// Fire the complete handlers
complete();
 
if ( isTimeout === "timeout" ) {
xhr.abort();
}
 
// Stop memory leaks
if ( s.async ) {
xhr = null;
}
}
};
 
// Override the abort handler, if we can (IE doesn't allow it, but that's OK)
// Opera doesn't fire onreadystatechange at all on abort
try {
var oldAbort = xhr.abort;
xhr.abort = function() {
if ( xhr ) {
oldAbort.call( xhr );
}
 
onreadystatechange( "abort" );
};
} catch(e) { }
 
// Timeout checker
if ( s.async && s.timeout > 0 ) {
setTimeout(function() {
// Check to see if the request is still happening
if ( xhr && !requestDone ) {
onreadystatechange( "timeout" );
}
}, s.timeout);
}
 
// Send the data
try {
xhr.send( type === "POST" || type === "PUT" || type === "DELETE" ? s.data : null );
} catch(e) {
jQuery.handleError(s, xhr, null, e);
// Fire the complete handlers
complete();
}
 
// firefox 1.5 doesn't fire statechange for sync requests
if ( !s.async ) {
onreadystatechange();
}
 
function success() {
// If a local callback was specified, fire it and pass it the data
if ( s.success ) {
s.success.call( callbackContext, data, status, xhr );
}
 
// Fire the global callback
if ( s.global ) {
trigger( "ajaxSuccess", [xhr, s] );
}
}
 
function complete() {
// Process result
if ( s.complete ) {
s.complete.call( callbackContext, xhr, status);
}
 
// The request was completed
if ( s.global ) {
trigger( "ajaxComplete", [xhr, s] );
}
 
// Handle the global AJAX counter
if ( s.global && ! --jQuery.active ) {
jQuery.event.trigger( "ajaxStop" );
}
}
function trigger(type, args) {
(s.context ? jQuery(s.context) : jQuery.event).trigger(type, args);
}
 
// return XMLHttpRequest to allow aborting the request etc.
return xhr;
},
 
handleError: function( s, xhr, status, e ) {
// If a local callback was specified, fire it
if ( s.error ) {
s.error.call( s.context || s, xhr, status, e );
}
 
// Fire the global callback
if ( s.global ) {
(s.context ? jQuery(s.context) : jQuery.event).trigger( "ajaxError", [xhr, s, e] );
}
},
 
// Counter for holding the number of active queries
active: 0,
 
// Determines if an XMLHttpRequest was successful or not
httpSuccess: function( xhr ) {
try {
// IE error sometimes returns 1223 when it should be 204 so treat it as success, see #1450
return !xhr.status && location.protocol === "file:" ||
// Opera returns 0 when status is 304
( xhr.status >= 200 && xhr.status < 300 ) ||
xhr.status === 304 || xhr.status === 1223 || xhr.status === 0;
} catch(e) {}
 
return false;
},
 
// Determines if an XMLHttpRequest returns NotModified
httpNotModified: function( xhr, url ) {
var lastModified = xhr.getResponseHeader("Last-Modified"),
etag = xhr.getResponseHeader("Etag");
 
if ( lastModified ) {
jQuery.lastModified[url] = lastModified;
}
 
if ( etag ) {
jQuery.etag[url] = etag;
}
 
// Opera returns 0 when status is 304
return xhr.status === 304 || xhr.status === 0;
},
 
httpData: function( xhr, type, s ) {
var ct = xhr.getResponseHeader("content-type") || "",
xml = type === "xml" || !type && ct.indexOf("xml") >= 0,
data = xml ? xhr.responseXML : xhr.responseText;
 
if ( xml && data.documentElement.nodeName === "parsererror" ) {
jQuery.error( "parsererror" );
}
 
// Allow a pre-filtering function to sanitize the response
// s is checked to keep backwards compatibility
if ( s && s.dataFilter ) {
data = s.dataFilter( data, type );
}
 
// The filter can actually parse the response
if ( typeof data === "string" ) {
// Get the JavaScript object, if JSON is used.
if ( type === "json" || !type && ct.indexOf("json") >= 0 ) {
data = jQuery.parseJSON( data );
 
// If the type is "script", eval it in global context
} else if ( type === "script" || !type && ct.indexOf("javascript") >= 0 ) {
jQuery.globalEval( data );
}
}
 
return data;
},
 
// Serialize an array of form elements or a set of
// key/values into a query string
param: function( a, traditional ) {
var s = [];
// Set traditional to true for jQuery <= 1.3.2 behavior.
if ( traditional === undefined ) {
traditional = jQuery.ajaxSettings.traditional;
}
// If an array was passed in, assume that it is an array of form elements.
if ( jQuery.isArray(a) || a.jquery ) {
// Serialize the form elements
jQuery.each( a, function() {
add( this.name, this.value );
});
} else {
// If traditional, encode the "old" way (the way 1.3.2 or older
// did it), otherwise encode params recursively.
for ( var prefix in a ) {
buildParams( prefix, a[prefix] );
}
}
 
// Return the resulting serialization
return s.join("&").replace(r20, "+");
 
function buildParams( prefix, obj ) {
if ( jQuery.isArray(obj) ) {
// Serialize array item.
jQuery.each( obj, function( i, v ) {
if ( traditional || /\[\]$/.test( prefix ) ) {
// Treat each array item as a scalar.
add( prefix, v );
} else {
// If array item is non-scalar (array or object), encode its
// numeric index to resolve deserialization ambiguity issues.
// Note that rack (as of 1.0.0) can't currently deserialize
// nested arrays properly, and attempting to do so may cause
// a server error. Possible fixes are to modify rack's
// deserialization algorithm or to provide an option or flag
// to force array serialization to be shallow.
buildParams( prefix + "[" + ( typeof v === "object" || jQuery.isArray(v) ? i : "" ) + "]", v );
}
});
} else if ( !traditional && obj != null && typeof obj === "object" ) {
// Serialize object item.
jQuery.each( obj, function( k, v ) {
buildParams( prefix + "[" + k + "]", v );
});
} else {
// Serialize scalar item.
add( prefix, obj );
}
}
 
function add( key, value ) {
// If value is a function, invoke it and return its value
value = jQuery.isFunction(value) ? value() : value;
s[ s.length ] = encodeURIComponent(key) + "=" + encodeURIComponent(value);
}
}
});
var elemdisplay = {},
rfxtypes = /toggle|show|hide/,
rfxnum = /^([+-]=)?([\d+-.]+)(.*)$/,
timerId,
fxAttrs = [
// height animations
[ "height", "marginTop", "marginBottom", "paddingTop", "paddingBottom" ],
// width animations
[ "width", "marginLeft", "marginRight", "paddingLeft", "paddingRight" ],
// opacity animations
[ "opacity" ]
];
 
jQuery.fn.extend({
show: function( speed, callback ) {
if ( speed || speed === 0) {
return this.animate( genFx("show", 3), speed, callback);
 
} else {
for ( var i = 0, l = this.length; i < l; i++ ) {
var old = jQuery.data(this[i], "olddisplay");
 
this[i].style.display = old || "";
 
if ( jQuery.css(this[i], "display") === "none" ) {
var nodeName = this[i].nodeName, display;
 
if ( elemdisplay[ nodeName ] ) {
display = elemdisplay[ nodeName ];
 
} else {
var elem = jQuery("<" + nodeName + " />").appendTo("body");
 
display = elem.css("display");
 
if ( display === "none" ) {
display = "block";
}
 
elem.remove();
 
elemdisplay[ nodeName ] = display;
}
 
jQuery.data(this[i], "olddisplay", display);
}
}
 
// Set the display of the elements in a second loop
// to avoid the constant reflow
for ( var j = 0, k = this.length; j < k; j++ ) {
this[j].style.display = jQuery.data(this[j], "olddisplay") || "";
}
 
return this;
}
},
 
hide: function( speed, callback ) {
if ( speed || speed === 0 ) {
return this.animate( genFx("hide", 3), speed, callback);
 
} else {
for ( var i = 0, l = this.length; i < l; i++ ) {
var old = jQuery.data(this[i], "olddisplay");
if ( !old && old !== "none" ) {
jQuery.data(this[i], "olddisplay", jQuery.css(this[i], "display"));
}
}
 
// Set the display of the elements in a second loop
// to avoid the constant reflow
for ( var j = 0, k = this.length; j < k; j++ ) {
this[j].style.display = "none";
}
 
return this;
}
},
 
// Save the old toggle function
_toggle: jQuery.fn.toggle,
 
toggle: function( fn, fn2 ) {
var bool = typeof fn === "boolean";
 
if ( jQuery.isFunction(fn) && jQuery.isFunction(fn2) ) {
this._toggle.apply( this, arguments );
 
} else if ( fn == null || bool ) {
this.each(function() {
var state = bool ? fn : jQuery(this).is(":hidden");
jQuery(this)[ state ? "show" : "hide" ]();
});
 
} else {
this.animate(genFx("toggle", 3), fn, fn2);
}
 
return this;
},
 
fadeTo: function( speed, to, callback ) {
return this.filter(":hidden").css("opacity", 0).show().end()
.animate({opacity: to}, speed, callback);
},
 
animate: function( prop, speed, easing, callback ) {
var optall = jQuery.speed(speed, easing, callback);
 
if ( jQuery.isEmptyObject( prop ) ) {
return this.each( optall.complete );
}
 
return this[ optall.queue === false ? "each" : "queue" ](function() {
var opt = jQuery.extend({}, optall), p,
hidden = this.nodeType === 1 && jQuery(this).is(":hidden"),
self = this;
 
for ( p in prop ) {
var name = p.replace(rdashAlpha, fcamelCase);
 
if ( p !== name ) {
prop[ name ] = prop[ p ];
delete prop[ p ];
p = name;
}
 
if ( prop[p] === "hide" && hidden || prop[p] === "show" && !hidden ) {
return opt.complete.call(this);
}
 
if ( ( p === "height" || p === "width" ) && this.style ) {
// Store display property
opt.display = jQuery.css(this, "display");
 
// Make sure that nothing sneaks out
opt.overflow = this.style.overflow;
}
 
if ( jQuery.isArray( prop[p] ) ) {
// Create (if needed) and add to specialEasing
(opt.specialEasing = opt.specialEasing || {})[p] = prop[p][1];
prop[p] = prop[p][0];
}
}
 
if ( opt.overflow != null ) {
this.style.overflow = "hidden";
}
 
opt.curAnim = jQuery.extend({}, prop);
 
jQuery.each( prop, function( name, val ) {
var e = new jQuery.fx( self, opt, name );
 
if ( rfxtypes.test(val) ) {
e[ val === "toggle" ? hidden ? "show" : "hide" : val ]( prop );
 
} else {
var parts = rfxnum.exec(val),
start = e.cur(true) || 0;
 
if ( parts ) {
var end = parseFloat( parts[2] ),
unit = parts[3] || "px";
 
// We need to compute starting value
if ( unit !== "px" ) {
self.style[ name ] = (end || 1) + unit;
start = ((end || 1) / e.cur(true)) * start;
self.style[ name ] = start + unit;
}
 
// If a +=/-= token was provided, we're doing a relative animation
if ( parts[1] ) {
end = ((parts[1] === "-=" ? -1 : 1) * end) + start;
}
 
e.custom( start, end, unit );
 
} else {
e.custom( start, val, "" );
}
}
});
 
// For JS strict compliance
return true;
});
},
 
stop: function( clearQueue, gotoEnd ) {
var timers = jQuery.timers;
 
if ( clearQueue ) {
this.queue([]);
}
 
this.each(function() {
// go in reverse order so anything added to the queue during the loop is ignored
for ( var i = timers.length - 1; i >= 0; i-- ) {
if ( timers[i].elem === this ) {
if (gotoEnd) {
// force the next step to be the last
timers[i](true);
}
 
timers.splice(i, 1);
}
}
});
 
// start the next in the queue if the last step wasn't forced
if ( !gotoEnd ) {
this.dequeue();
}
 
return this;
}
 
});
 
// Generate shortcuts for custom animations
jQuery.each({
slideDown: genFx("show", 1),
slideUp: genFx("hide", 1),
slideToggle: genFx("toggle", 1),
fadeIn: { opacity: "show" },
fadeOut: { opacity: "hide" }
}, function( name, props ) {
jQuery.fn[ name ] = function( speed, callback ) {
return this.animate( props, speed, callback );
};
});
 
jQuery.extend({
speed: function( speed, easing, fn ) {
var opt = speed && typeof speed === "object" ? speed : {
complete: fn || !fn && easing ||
jQuery.isFunction( speed ) && speed,
duration: speed,
easing: fn && easing || easing && !jQuery.isFunction(easing) && easing
};
 
opt.duration = jQuery.fx.off ? 0 : typeof opt.duration === "number" ? opt.duration :
jQuery.fx.speeds[opt.duration] || jQuery.fx.speeds._default;
 
// Queueing
opt.old = opt.complete;
opt.complete = function() {
if ( opt.queue !== false ) {
jQuery(this).dequeue();
}
if ( jQuery.isFunction( opt.old ) ) {
opt.old.call( this );
}
};
 
return opt;
},
 
easing: {
linear: function( p, n, firstNum, diff ) {
return firstNum + diff * p;
},
swing: function( p, n, firstNum, diff ) {
return ((-Math.cos(p*Math.PI)/2) + 0.5) * diff + firstNum;
}
},
 
timers: [],
 
fx: function( elem, options, prop ) {
this.options = options;
this.elem = elem;
this.prop = prop;
 
if ( !options.orig ) {
options.orig = {};
}
}
 
});
 
jQuery.fx.prototype = {
// Simple function for setting a style value
update: function() {
if ( this.options.step ) {
this.options.step.call( this.elem, this.now, this );
}
 
(jQuery.fx.step[this.prop] || jQuery.fx.step._default)( this );
 
// Set display property to block for height/width animations
if ( ( this.prop === "height" || this.prop === "width" ) && this.elem.style ) {
this.elem.style.display = "block";
}
},
 
// Get the current size
cur: function( force ) {
if ( this.elem[this.prop] != null && (!this.elem.style || this.elem.style[this.prop] == null) ) {
return this.elem[ this.prop ];
}
 
var r = parseFloat(jQuery.css(this.elem, this.prop, force));
return r && r > -10000 ? r : parseFloat(jQuery.curCSS(this.elem, this.prop)) || 0;
},
 
// Start an animation from one number to another
custom: function( from, to, unit ) {
this.startTime = now();
this.start = from;
this.end = to;
this.unit = unit || this.unit || "px";
this.now = this.start;
this.pos = this.state = 0;
 
var self = this;
function t( gotoEnd ) {
return self.step(gotoEnd);
}
 
t.elem = this.elem;
 
if ( t() && jQuery.timers.push(t) && !timerId ) {
timerId = setInterval(jQuery.fx.tick, 13);
}
},
 
// Simple 'show' function
show: function() {
// Remember where we started, so that we can go back to it later
this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
this.options.show = true;
 
// Begin the animation
// Make sure that we start at a small width/height to avoid any
// flash of content
this.custom(this.prop === "width" || this.prop === "height" ? 1 : 0, this.cur());
 
// Start by showing the element
jQuery( this.elem ).show();
},
 
// Simple 'hide' function
hide: function() {
// Remember where we started, so that we can go back to it later
this.options.orig[this.prop] = jQuery.style( this.elem, this.prop );
this.options.hide = true;
 
// Begin the animation
this.custom(this.cur(), 0);
},
 
// Each step of an animation
step: function( gotoEnd ) {
var t = now(), done = true;
 
if ( gotoEnd || t >= this.options.duration + this.startTime ) {
this.now = this.end;
this.pos = this.state = 1;
this.update();
 
this.options.curAnim[ this.prop ] = true;
 
for ( var i in this.options.curAnim ) {
if ( this.options.curAnim[i] !== true ) {
done = false;
}
}
 
if ( done ) {
if ( this.options.display != null ) {
// Reset the overflow
this.elem.style.overflow = this.options.overflow;
 
// Reset the display
var old = jQuery.data(this.elem, "olddisplay");
this.elem.style.display = old ? old : this.options.display;
 
if ( jQuery.css(this.elem, "display") === "none" ) {
this.elem.style.display = "block";
}
}
 
// Hide the element if the "hide" operation was done
if ( this.options.hide ) {
jQuery(this.elem).hide();
}
 
// Reset the properties, if the item has been hidden or shown
if ( this.options.hide || this.options.show ) {
for ( var p in this.options.curAnim ) {
jQuery.style(this.elem, p, this.options.orig[p]);
}
}
 
// Execute the complete function
this.options.complete.call( this.elem );
}
 
return false;
 
} else {
var n = t - this.startTime;
this.state = n / this.options.duration;
 
// Perform the easing function, defaults to swing
var specialEasing = this.options.specialEasing && this.options.specialEasing[this.prop];
var defaultEasing = this.options.easing || (jQuery.easing.swing ? "swing" : "linear");
this.pos = jQuery.easing[specialEasing || defaultEasing](this.state, n, 0, 1, this.options.duration);
this.now = this.start + ((this.end - this.start) * this.pos);
 
// Perform the next step of the animation
this.update();
}
 
return true;
}
};
 
jQuery.extend( jQuery.fx, {
tick: function() {
var timers = jQuery.timers;
 
for ( var i = 0; i < timers.length; i++ ) {
if ( !timers[i]() ) {
timers.splice(i--, 1);
}
}
 
if ( !timers.length ) {
jQuery.fx.stop();
}
},
stop: function() {
clearInterval( timerId );
timerId = null;
},
speeds: {
slow: 600,
fast: 200,
// Default speed
_default: 400
},
 
step: {
opacity: function( fx ) {
jQuery.style(fx.elem, "opacity", fx.now);
},
 
_default: function( fx ) {
if ( fx.elem.style && fx.elem.style[ fx.prop ] != null ) {
fx.elem.style[ fx.prop ] = (fx.prop === "width" || fx.prop === "height" ? Math.max(0, fx.now) : fx.now) + fx.unit;
} else {
fx.elem[ fx.prop ] = fx.now;
}
}
}
});
 
if ( jQuery.expr && jQuery.expr.filters ) {
jQuery.expr.filters.animated = function( elem ) {
return jQuery.grep(jQuery.timers, function( fn ) {
return elem === fn.elem;
}).length;
};
}
 
function genFx( type, num ) {
var obj = {};
 
jQuery.each( fxAttrs.concat.apply([], fxAttrs.slice(0,num)), function() {
obj[ this ] = type;
});
 
return obj;
}
if ( "getBoundingClientRect" in document.documentElement ) {
jQuery.fn.offset = function( options ) {
var elem = this[0];
 
if ( options ) {
return this.each(function( i ) {
jQuery.offset.setOffset( this, options, i );
});
}
 
if ( !elem || !elem.ownerDocument ) {
return null;
}
 
if ( elem === elem.ownerDocument.body ) {
return jQuery.offset.bodyOffset( elem );
}
 
var box = elem.getBoundingClientRect(), doc = elem.ownerDocument, body = doc.body, docElem = doc.documentElement,
clientTop = docElem.clientTop || body.clientTop || 0, clientLeft = docElem.clientLeft || body.clientLeft || 0,
top = box.top + (self.pageYOffset || jQuery.support.boxModel && docElem.scrollTop || body.scrollTop ) - clientTop,
left = box.left + (self.pageXOffset || jQuery.support.boxModel && docElem.scrollLeft || body.scrollLeft) - clientLeft;
 
return { top: top, left: left };
};
 
} else {
jQuery.fn.offset = function( options ) {
var elem = this[0];
 
if ( options ) {
return this.each(function( i ) {
jQuery.offset.setOffset( this, options, i );
});
}
 
if ( !elem || !elem.ownerDocument ) {
return null;
}
 
if ( elem === elem.ownerDocument.body ) {
return jQuery.offset.bodyOffset( elem );
}
 
jQuery.offset.initialize();
 
var offsetParent = elem.offsetParent, prevOffsetParent = elem,
doc = elem.ownerDocument, computedStyle, docElem = doc.documentElement,
body = doc.body, defaultView = doc.defaultView,
prevComputedStyle = defaultView ? defaultView.getComputedStyle( elem, null ) : elem.currentStyle,
top = elem.offsetTop, left = elem.offsetLeft;
 
while ( (elem = elem.parentNode) && elem !== body && elem !== docElem ) {
if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
break;
}
 
computedStyle = defaultView ? defaultView.getComputedStyle(elem, null) : elem.currentStyle;
top -= elem.scrollTop;
left -= elem.scrollLeft;
 
if ( elem === offsetParent ) {
top += elem.offsetTop;
left += elem.offsetLeft;
 
if ( jQuery.offset.doesNotAddBorder && !(jQuery.offset.doesAddBorderForTableAndCells && /^t(able|d|h)$/i.test(elem.nodeName)) ) {
top += parseFloat( computedStyle.borderTopWidth ) || 0;
left += parseFloat( computedStyle.borderLeftWidth ) || 0;
}
 
prevOffsetParent = offsetParent, offsetParent = elem.offsetParent;
}
 
if ( jQuery.offset.subtractsBorderForOverflowNotVisible && computedStyle.overflow !== "visible" ) {
top += parseFloat( computedStyle.borderTopWidth ) || 0;
left += parseFloat( computedStyle.borderLeftWidth ) || 0;
}
 
prevComputedStyle = computedStyle;
}
 
if ( prevComputedStyle.position === "relative" || prevComputedStyle.position === "static" ) {
top += body.offsetTop;
left += body.offsetLeft;
}
 
if ( jQuery.offset.supportsFixedPosition && prevComputedStyle.position === "fixed" ) {
top += Math.max( docElem.scrollTop, body.scrollTop );
left += Math.max( docElem.scrollLeft, body.scrollLeft );
}
 
return { top: top, left: left };
};
}
 
jQuery.offset = {
initialize: function() {
var body = document.body, container = document.createElement("div"), innerDiv, checkDiv, table, td, bodyMarginTop = parseFloat( jQuery.curCSS(body, "marginTop", true) ) || 0,
html = "<div style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;'><div></div></div><table style='position:absolute;top:0;left:0;margin:0;border:5px solid #000;padding:0;width:1px;height:1px;' cellpadding='0' cellspacing='0'><tr><td></td></tr></table>";
 
jQuery.extend( container.style, { position: "absolute", top: 0, left: 0, margin: 0, border: 0, width: "1px", height: "1px", visibility: "hidden" } );
 
container.innerHTML = html;
body.insertBefore( container, body.firstChild );
innerDiv = container.firstChild;
checkDiv = innerDiv.firstChild;
td = innerDiv.nextSibling.firstChild.firstChild;
 
this.doesNotAddBorder = (checkDiv.offsetTop !== 5);
this.doesAddBorderForTableAndCells = (td.offsetTop === 5);
 
checkDiv.style.position = "fixed", checkDiv.style.top = "20px";
// safari subtracts parent border width here which is 5px
this.supportsFixedPosition = (checkDiv.offsetTop === 20 || checkDiv.offsetTop === 15);
checkDiv.style.position = checkDiv.style.top = "";
 
innerDiv.style.overflow = "hidden", innerDiv.style.position = "relative";
this.subtractsBorderForOverflowNotVisible = (checkDiv.offsetTop === -5);
 
this.doesNotIncludeMarginInBodyOffset = (body.offsetTop !== bodyMarginTop);
 
body.removeChild( container );
body = container = innerDiv = checkDiv = table = td = null;
jQuery.offset.initialize = jQuery.noop;
},
 
bodyOffset: function( body ) {
var top = body.offsetTop, left = body.offsetLeft;
 
jQuery.offset.initialize();
 
if ( jQuery.offset.doesNotIncludeMarginInBodyOffset ) {
top += parseFloat( jQuery.curCSS(body, "marginTop", true) ) || 0;
left += parseFloat( jQuery.curCSS(body, "marginLeft", true) ) || 0;
}
 
return { top: top, left: left };
},
setOffset: function( elem, options, i ) {
// set position first, in-case top/left are set even on static elem
if ( /static/.test( jQuery.curCSS( elem, "position" ) ) ) {
elem.style.position = "relative";
}
var curElem = jQuery( elem ),
curOffset = curElem.offset(),
curTop = parseInt( jQuery.curCSS( elem, "top", true ), 10 ) || 0,
curLeft = parseInt( jQuery.curCSS( elem, "left", true ), 10 ) || 0;
 
if ( jQuery.isFunction( options ) ) {
options = options.call( elem, i, curOffset );
}
 
var props = {
top: (options.top - curOffset.top) + curTop,
left: (options.left - curOffset.left) + curLeft
};
if ( "using" in options ) {
options.using.call( elem, props );
} else {
curElem.css( props );
}
}
};
 
 
jQuery.fn.extend({
position: function() {
if ( !this[0] ) {
return null;
}
 
var elem = this[0],
 
// Get *real* offsetParent
offsetParent = this.offsetParent(),
 
// Get correct offsets
offset = this.offset(),
parentOffset = /^body|html$/i.test(offsetParent[0].nodeName) ? { top: 0, left: 0 } : offsetParent.offset();
 
// Subtract element margins
// note: when an element has margin: auto the offsetLeft and marginLeft
// are the same in Safari causing offset.left to incorrectly be 0
offset.top -= parseFloat( jQuery.curCSS(elem, "marginTop", true) ) || 0;
offset.left -= parseFloat( jQuery.curCSS(elem, "marginLeft", true) ) || 0;
 
// Add offsetParent borders
parentOffset.top += parseFloat( jQuery.curCSS(offsetParent[0], "borderTopWidth", true) ) || 0;
parentOffset.left += parseFloat( jQuery.curCSS(offsetParent[0], "borderLeftWidth", true) ) || 0;
 
// Subtract the two offsets
return {
top: offset.top - parentOffset.top,
left: offset.left - parentOffset.left
};
},
 
offsetParent: function() {
return this.map(function() {
var offsetParent = this.offsetParent || document.body;
while ( offsetParent && (!/^body|html$/i.test(offsetParent.nodeName) && jQuery.css(offsetParent, "position") === "static") ) {
offsetParent = offsetParent.offsetParent;
}
return offsetParent;
});
}
});
 
 
// Create scrollLeft and scrollTop methods
jQuery.each( ["Left", "Top"], function( i, name ) {
var method = "scroll" + name;
 
jQuery.fn[ method ] = function(val) {
var elem = this[0], win;
if ( !elem ) {
return null;
}
 
if ( val !== undefined ) {
// Set the scroll offset
return this.each(function() {
win = getWindow( this );
 
if ( win ) {
win.scrollTo(
!i ? val : jQuery(win).scrollLeft(),
i ? val : jQuery(win).scrollTop()
);
 
} else {
this[ method ] = val;
}
});
} else {
win = getWindow( elem );
 
// Return the scroll offset
return win ? ("pageXOffset" in win) ? win[ i ? "pageYOffset" : "pageXOffset" ] :
jQuery.support.boxModel && win.document.documentElement[ method ] ||
win.document.body[ method ] :
elem[ method ];
}
};
});
 
function getWindow( elem ) {
return ("scrollTo" in elem && elem.document) ?
elem :
elem.nodeType === 9 ?
elem.defaultView || elem.parentWindow :
false;
}
// Create innerHeight, innerWidth, outerHeight and outerWidth methods
jQuery.each([ "Height", "Width" ], function( i, name ) {
 
var type = name.toLowerCase();
 
// innerHeight and innerWidth
jQuery.fn["inner" + name] = function() {
return this[0] ?
jQuery.css( this[0], type, false, "padding" ) :
null;
};
 
// outerHeight and outerWidth
jQuery.fn["outer" + name] = function( margin ) {
return this[0] ?
jQuery.css( this[0], type, false, margin ? "margin" : "border" ) :
null;
};
 
jQuery.fn[ type ] = function( size ) {
// Get window width or height
var elem = this[0];
if ( !elem ) {
return size == null ? null : this;
}
if ( jQuery.isFunction( size ) ) {
return this.each(function( i ) {
var self = jQuery( this );
self[ type ]( size.call( this, i, self[ type ]() ) );
});
}
 
return ("scrollTo" in elem && elem.document) ? // does it walk and quack like a window?
// Everyone else use document.documentElement or document.body depending on Quirks vs Standards mode
elem.document.compatMode === "CSS1Compat" && elem.document.documentElement[ "client" + name ] ||
elem.document.body[ "client" + name ] :
 
// Get document width or height
(elem.nodeType === 9) ? // is it a document
// Either scroll[Width/Height] or offset[Width/Height], whichever is greater
Math.max(
elem.documentElement["client" + name],
elem.body["scroll" + name], elem.documentElement["scroll" + name],
elem.body["offset" + name], elem.documentElement["offset" + name]
) :
 
// Get or set width or height on the element
size === undefined ?
// Get width or height on the element
jQuery.css( elem, type ) :
 
// Set the width or height on the element (default to pixels if value is unitless)
this.css( type, typeof size === "string" ? size : size + "px" );
};
 
});
// Expose jQuery to the global object
window.jQuery = window.$ = jQuery;
 
})(window);
/trunk/applications/saisie/squelettes/navigation/menu.tpl.html
New file
0,0 → 1,31
<!-- ODS_SAISIE - DEBUT MENU NAVIGATION -->
<div id="retour_au_site">
<ul>
<li>
<a href="http://162.38.234.9/obs_saisons/node/13"> Retour au site </a>
</li>
</ul>
</div>
 
<div id="saisie_menu">
<ul class=menu">
<li><a href="<?= aControleur::getUrlFormulaireSaisieStation() ?>"> Nouvelle station </a> </li>
<?php if (isset($stations)) : ?>
<?php foreach ($stations as $station) : ?>
<li <?= ($station['id'] == $id_station_en_cours) ? 'class="station_en_cours"' : '' ?>>
<a href="<?= $station['url'] ?>"><?=$station['nom']?></a>
<?php if (isset($station['especes']) && $station['id'] == $id_station_en_cours) : ?>
<ul>
<li><a href="<?= aControleur::getUrlFormulaireAjoutEspece($station['id']) ?>"> Nouvelle espece </a></li>
<?php foreach (($station['especes']) as $espece) : ?>
<li <?= ($espece['id'] == $id_espece_en_cours) ? 'class="espece_en_cours"' : '' ?>>
<a href="<?= $espece['url'] ?>"><?= $espece['nom_vernaculaire']?></a></li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</li>
<?php endforeach; ?>
<?php endif; ?>
</ul>
</div>
<!-- ODS_SAISIE - FIN MENU NAVIGATION -->
/trunk/applications/saisie/squelettes/navigation/fil_ariane.tpl.html
New file
0,0 → 1,6
<ul class="liste_fil_ariane">
<?php foreach($elements_fil as $element) ?>
<li class="element_fil"><a href=""></a></li>
<?php if($element != array_pop($element)) { ?> > <?php } ?>
<?php endforeach ?>
</ul>
/trunk/applications/saisie/squelettes/fiches/station_fiche.tpl.html
New file
0,0 → 1,34
<!-- ODS_SAISIE - DEBUT FICHE STATION -->
<script type="text/javascript" src="http://162.38.234.9/ODS_redesign/applications/saisie/squelettes/js/fiche.js"></script>
<div id="fiche_station">
<h2 id="titre_station"> <?= $infos_station['nom'] ?></h2>
<div class="element_fiche">
<span class="intitule_fiche">Commune / ville :</span>
<span class="valeur_fiche"> <?= $infos_station['commune'] ?> </span>
</div>
<div class="element_fiche">
<span class="intitule_fiche">Milieu :</span>
<span class="valeur_fiche"> <?= $infos_station['milieu'] ?> </span>
</div>
<p> <?= $infos_station['description']; ?> </p>
<div id="conteneur_liens_lat_lon" class="element_fiche" >
<span class="intitule_fiche">Latitude :</span>
<span id="station_lat" class="valeur_fiche"> <?= $infos_station['latitude'] ?> </span>
<span class="intitule_fiche">Longitude :</span>
<span id="station_lon" class="valeur_fiche"> <?= $infos_station['longitude'] ?> </span>
</div>
<div class="conteneur_carte element_fiche" >
<div id="map_canvas" style="width:100%; height:100%"></div>
</div>
<form class="form_afficher_modification" method="post" action="<?= aControleur::getUrlFormulaireModificationStation($id_station); ?>" id="form_afficher_modification">
<input class="droite gros_bouton_validation" type="submit" value="Modifier" />
</form>
</div>
<!-- ODS_SAISIE - FIN FICHE STATION -->
/trunk/applications/saisie/squelettes/fiches/evenement_fiche.tpl.html
--- trunk/applications/saisie/squelettes/fiches/individu_fiche.tpl.html (revision 0)
+++ trunk/applications/saisie/squelettes/fiches/individu_fiche.tpl.html (revision 31)
@@ -0,0 +1,62 @@
+<!-- ODS_SAISIE - DEBUT FICHE INDIVIDU -->
+<div id="fiche_individu">
+ <h2 class="titre_fiche"> <?= $infos_individu['nom'] ?></h2>
+ <hr class="separation_section" />
+
+ <h3 class="titre_section_fiche"> Espèce </h3>
+ <div class="informations_espece">
+ <img alt="image de l'espece" class="droite" width="100px" src="<?= $infos_espece['url_image'] ?>" />
+ <span class="intitule_fiche"> <?= $infos_espece['nom_vernaculaire'] ?> </span>
+ <span class="valeur_fiche"> (<?= $infos_espece['nom_scientifique'] ?>) </span>
+ </div>
+
+ <p class="intitule_fiche" > <?= $infos_espece['description'] ?> </p>
+
+ <hr class="separation_section" />
+
+ <h3 class="titre_section_fiche" > Observations réalisées </h3>
+ <div class="pliage">
+ <h4> année 2006</h4>
+ <ul>
+ <li>
+ <div class="pliage">
+ <h4>Floraison</h4>
+ <ul>
+ <li>
+ Stade 12 : 12/10/2006
+ </li>
+ <li>
+ Stade 35 : 15/10/2006
+ </li>
+ </ul>
+ </div>
+ </li>
+ <li>
+ <div class="pliage">
+ <h4>Scenescence</h4>
+ <ul>
+ <li>
+ Stade 88 : 11/11/2006
+ </li>
+ </ul>
+ </div>
+ </li>
+ <li>
+ <div class="pliage">
+ <h4>Pourriture</h4>
+ <ul>
+ <li>
+ Stade 12 : 12/12/2006
+ </li>
+ <li>
+ Stade 35 : 25/12/2006
+ </li>
+ </ul>
+ </div>
+ </li>
+ </ul>
+ </div>
+
+ <hr class="separation_section" />
+</div>
+<!-- ODS_SAISIE - FIN FICHE INDIVIDU -->
/trunk/applications/saisie/squelettes/pied.tpl.html
New file
0,0 → 1,22
<!-- SAISIE - DEBUT PIED DE PAGE -->
<div id="saisie_pied">
<script type="text/javascript">
$(document).ready(function() {
$('span.courriel').courriel();
});
</script>
<p>
Pour plus d'informations ou nous faire part d'une erreur dans les informations affichées, veuillez nous contacter à l'adresse <br />
<span class="courriel">contact [arrobase] obs-saisons [point] fr</span>.
</p>
<p>
Merci de signaler
<a href="http://www.tela-botanica.net/suivi/index.php?do=newtask&project=32" class="lien_ext">
les bogues et améliorations pour l'application <?php echo I18n::get('test.truc'); ?>
<span class="saisie-appli-info">
Application de saisie D'ODS
</span>
</a>.
</p>
</div>
<!-- SAISIE - FIN PIED DE PAGE -->
/trunk/applications/saisie/squelettes/tete.tpl.html
New file
0,0 → 1,5
<!-- SAISIE - DEBUT TETE DE PAGE -->
<div id="saisie_tete">
</div>
<!-- SAISIE - FIN TETE DE PAGE -->
/trunk/applications/saisie/squelettes/listes/station_liste.tpl.html
New file
0,0 → 1,13
<!-- ODS_SAISIE - DEBUT LISTE STATIONS -->
<div id="saisie_liste_station">
<?php if (isset($stations)) : ?>
<ul class=saisie_liste">
<li><a href=""> Nouvelle station </a> </li>
<?php foreach ($stations as $station) : ?>
<li><a href=""><?=$station['nom']?></a>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>
<!-- ODS_SAISIE - FIN LISTE STATIONS -->
/trunk/applications/saisie/squelettes/listes/evenement_liste.tpl.html
New file
0,0 → 1,47
<!-- ODS_SAISIE - DEBUT LISTE EVENEMENTS -->
<?php $stades = 0; ?>
<div >
<table id="saisie_liste_evenements">
<tr>
<th></th>
<?php $classe_evenement = 1 ; ?>
<?php foreach($evenements as $evenement) : ?>
<th class="stade<?=$classe_evenement ?>" colspan="<?=count($evenement['stades']) ?>">
<div class="evenement"><?= $evenement['nom'] ?></div>
</th>
<?php $classe_evenement++ ; ?>
<?php endforeach; ?>
<th></th>
</tr>
<tr>
<th> stades </th>
<?php foreach($evenements as $evenement) : ?>
<?php foreach($evenement['stades'] as $stade) : ?>
<th class="stade_evenement">
<?= $stade ?>
</th>
<?php $stades++; ?>
<?php endforeach; ?>
<?php endforeach; ?>
</tr>
 
<?php foreach ($individus as $individu) : ?>
<tr class="observations_individu" id="observations_individu_<?= $individu['id'] ?>">
<td>
<a href="<?= aControleur::getUrlConsultationFicheIndividu($id_station, $individu['id']); ?>"><?= $individu['nom'] ?></a>
</td>
<?php for( $i = 0; $i < $stades; $i++) : ?>
<td>
<?= array_shift($individu['observations']); ?>
</td>
<?php endfor; ?>
<td><a class="lien_modifier" href="<?= aControleur::getUrlFormulaireModificationObservation($individu['id']); ?>"><img alt="modifier" src="http://162.38.234.9/ODS_redesign/applications/saisie/squelettes/images/modification.png" /></a></td>
</tr>
<?php endforeach; ?>
</table>
 
<a href="<?= aControleur::getUrlFormulaireAjoutIndividu($id_station,$id_espece); ?>"> Nouvel individu </a>
</div>
<!-- ODS_SAISIE - FIN LISTE EVENEMENTS -->
/trunk/applications/saisie/squelettes/listes/individu_liste.tpl.html
New file
0,0 → 1,13
<!-- ODS_SAISIE - DEBUT LISTE INDIVIDUS -->
<div id="saisie_liste_individus">
<?php if (isset($individus)) : ?>
<ul class=saisie_liste">
<li><a href=""> Nouvel individu </a> </li>
<?php foreach ($individus as $individu) : ?>
<li><a href="<?= $individu['url']?>"><?= $individu['nom']?></a>
</li>
<?php endforeach; ?>
</ul>
<?php endif; ?>
</div>
<!-- ODS_SAISIE - FIN LISTE INDIVIDUS -->
/trunk/applications/saisie/controleurs/Espece.php
New file
0,0 → 1,59
<?php
// declare(encoding='UTF-8');
/**
* Classe de gestion des epeces.
*
* @package Collection
* @category Php 5.2
* @author Aurélien PERONNET <aurelien@tela-botanica.org>
* @copyright 2010 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version SVN: $Id: Espece.php 152 2010-09-06 16:19:12Z jpm $
*/
class Espece extends aControleur {
private $id_espece_en_cours = null;
public function __construct() {
parent::__construct();
$this->initialiser();
}
public function initialiser() {
$this->id_espece_en_cours = $_GET['$id_espece'];
$this->setNavigation();
}
private function setNavigation() {
$station = new Station();
$this->setSortie(self::RENDU_NAVIGATION, $station->construireMenuNavigation());
}
public function executerActionParDefaut() {
return $this->afficherFormulaireSaisieIndividu();
}
// +---------------------------------------------------------------------------------------------------------------+
// METHODES D'AFFICHAGE DE FORMULAIRE
public function afficherFormulaireSaisieEspece() {
$donnees = array();
$espece_dao = new EspeceDao();
$donnees['types'] = $espece_dao->getListeEspecesParType();
$formulaire = $this->getVue('formulaires/espece_saisie',$donnees);
$this->setSortie(self::RENDU_CORPS, $formulaire);
}
public function getListeEvenementPourEspece($id_espece) {
$evenementDao = new EvenementDao();
$liste_evenements = $evenementDao->getListeEvenementPourEspece($id_espece);
return $liste_evenements;
}
}
/trunk/applications/saisie/controleurs/Evenement.php
New file
0,0 → 1,3
<?php
 
?>
/trunk/applications/saisie/controleurs/Utilisateur.php
New file
0,0 → 1,24
<?php
abstract class Utilisateur extends aControleur {
public function getIdentifiantNumerique() {
return 0;
}
public function getEmail() {
return '';
}
public function getNom() {
return '';
}
public function getPrenom() {
return '';
}
public function estAdmin() {
return false;
}
}
?>
/trunk/applications/saisie/controleurs/Individu.php
New file
0,0 → 1,101
<?php
// declare(encoding='UTF-8');
/**
* Classe de test des Controleurs.
*
* @package Collection
* @category Php 5.2
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @copyright 2010 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version SVN: $Id: Fiche.php 152 2010-09-06 16:19:12Z jpm $
*/
class Individu extends aControleur {
private $id_individu_en_cours = null;
public function __construct() {
parent::__construct();
$this->initialiser();
}
public function initialiser() {
$this->id_individu_en_cours = $_GET['id_individu'];
$this->setNavigation();
}
public function executerActionParDefaut() {
return $this->afficherFormulaireAjoutIndividu();
}
public function afficherFormulaireAjoutIndividu() {
$donnees = array();
$formulaire = $this->getVue('formulaires/individu_saisie',$donnees);
$this->setSortie(self::RENDU_CORPS, $formulaire);
}
// +---------------------------------------------------------------------------------------------------------------+
// METHODES GENERIQUES
public function afficherListeIndividu($id_espece) {
$id_utilisateur = AppControleur::getUtilisateur()->getIdentifiantNumerique();
$espece = new Espece();
$evenements = $espece->getListeEvenementPourEspece($id_espece);
$individus = $this->getListeIndividus($id_utilisateur);
foreach($individus as &$individu) {
$observation = new Observation();
$individu['observations'] = $observation->getListeObservationsPourIndividu($individu['id']);
$individu['url'] = aControleur::getUrlConsultationFicheIndividu($individu['id'],$individu['id']);
}
 
$donnees['evenements'] = $evenements;
$donnees['individus'] = $individus;
$donnees['id_station'] = $_GET['id_station'];
$donnees['id_espece'] = $_GET['id_espece'];
 
$this->setSortie(self::RENDU_CORPS, $this->getVue('listes/evenement_liste', $donnees));
}
public function afficherInformationsIndividu() {
$id_individu = $this->id_individu_en_cours;
$id_espece = $_GET['id_individu'];
$donnees['infos_individu'] = $this->getInformationsIndividu($id_individu);
$espece_dao = new EspeceDao();
$donnees['infos_espece'] = $espece_dao->getInformationsEspece($id_espece);
$this->setSortie(self::RENDU_CORPS, $this->getVue('fiches/individu_fiche', $donnees));
}
// +---------------------------------------------------------------------------------------------------------------+
// METHODES POUR FABRIQUER LE MENU
public function setNavigation() {
$station = new Station();
$this->setSortie(self::RENDU_NAVIGATION, $station->construireMenuNavigation());
}
protected function getListeIndividus($id_utilisateur) {
$individu_dao = new IndividuDao();
return $individu_dao->getListeIndividusPourStation($id_utilisateur);
}
private function getInformationsIndividu($id_individu) {
$individu_dao = new IndividuDao();
$infos_individu = $individu_dao->getInformationsIndividu($id_individu);
return $infos_individu;
}
}
/trunk/applications/saisie/controleurs/Observation.php
New file
0,0 → 1,105
<?php
// declare(encoding='UTF-8');
/**
* Classe de gestion des observations.
*
* @package ODS_saisie
* @category Php 5.2
* @author Aurélien Peronnet <aurelien@tela-botanica.org>
* @copyright 2010 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version SVN: $Id: Fiche.php 152 2010-09-06 16:19:12Z jpm $
*/
class Observation extends aControleur {
private $id_observation_en_cours = null;
public function __construct() {
parent::__construct();
$this->initialiser();
}
public function initialiser() {
$this->id_observation_en_cours = $_GET['id_observation'];
}
public function executerActionParDefaut() {
return $this->afficherFormulaireAjoutIndividu();
}
public function afficherFormulaireAjoutObservation($id_individu) {
$donnees = array();
$formulaire = $this->getVue('formulaires/observation_saisie',$donnees);
$this->setSortie(self::RENDU_CORPS, $formulaire);
}
public function afficherFormulaireModificationObservation() {
$id_individu_a_modifier_observation = $_GET['id_individu'];
$id_espece = $_GET['id_espece'];
$id_station = $_GET['id_station'];
$id_utilisateur = AppControleur::getUtilisateur()->getIdentifiantNumerique();
$espece = new Espece();
$evenements = $espece->getListeEvenementPourEspece($id_espece);
$station = new Station();
$individus = $station->getIndividusStation($id_station);
foreach($individus as &$individu) {
$observation = new Observation();
$individu['observations'] = $observation->getListeObservationsPourIndividu($individu['id']);
$individu['url'] = aControleur::getUrlConsultationFicheIndividu($individu['id'],$individu['id']);
}
echo $id_individu_a_modifier_observation;
 
$donnees['evenements'] = $evenements;
$donnees['individus'] = $individus;
$donnees['id_station'] = $_GET['id_station'];
$donnees['id_espece'] = $_GET['id_espece'];
$donnees['id_individu_a_modifier_observation'] = $id_individu_a_modifier_observation;
 
$this->setSortie(self::RENDU_CORPS, $this->getVue('formulaires/evenement_modification', $donnees));
}
// +---------------------------------------------------------------------------------------------------------------+
// METHODES APPELEES LORS DE LA VALIDATION D'UN FORMULAIRE
public function validerFormulaireAjoutObservation() {
$valeurs_formulaires = $_POST['form_saisie_observation'];
$valeurs_verifiees = array();
$observation_dao = new ObservationDao();
$observation_dao->ajouterObservation($valeurs_verifiees);
}
public function validerFormulaireModificationObservation() {
$valeurs_formulaires = $_POST['form_modif_observation'];
$valeurs_verifiees = array();
$observation_dao = new ObservationDao();
$observation_dao->modifierObservation($valeurs_verifiees);
}
// +---------------------------------------------------------------------------------------------------------------+
// METHODES DE RECHERCHE DE DONNEES
public function getListeObservationsPourIndividu($id_individu, $annee = null) {
$observation_dao = new ObservationDao();
$observations_pour_individu = $observation_dao->getListeObservationsPourIndividu($id_individu, $annee);
return $observations_pour_individu;
}
}
/trunk/applications/saisie/controleurs/AppControleur.php
New file
0,0 → 1,232
<?php
// declare(encoding='UTF-8');
/**
* Classe mère des controleurs de l'application.
* Elle repartie les demandes utilisateurs dans les différents modules, executent les actions et redistribue le code
* html dans les différentes fonctions d'affichage.
* C'est une Singleton.
*
* @category PHP 5.2
* @package saisie
* @author Aurélien Peronnet <aurelien@tela-botanica.org>
* @copyright 2010 Tela-Botanica
* @license GPL-v3 et CECILL-v2
* @version $Id: AppControleur.php 160 2010-09-13 13:10:41Z aurelien $
*/
 
class AppControleur extends Controleur {
/**
* Instance de la classe pointant sur elle même (pour le pattern singleton)
*/
private static $instance = null;
/**
* Instance de l'utilisateur en cours
*/
private static $utilisateur = null;
/**
* Paramètres
*/
private static $parametres = array();
/**
* Constructeur vide
*/
public function __construct() {
$sortie = array('titre' => '', 'description' => '', 'tags' => '',
'corps' => '', 'tete' => '', 'pied' => '', 'navigation' => '');
$url = new Url(Config::get('url_base_index'));
self::$parametres = array( 'module' => 'Station',
'action' => 'executerActionParDefaut',
'sortie' => $sortie,
'url' => $url);
parent::__construct();
}
/**
* Initialisation du controleur principal en fonction des paramètres de l'url.
*/
public static function initialiser() {
self::verifierCreationInstance();
self::gererSession();
self::gererUtilisateur();
if (isset($_GET['module'])) {
self::$parametres['module'] = $_GET['module'];
}
self::$parametres['url']->setVariableRequete('module', self::$parametres['module']);
if (isset($_GET['action'])) {
self::$parametres['action'] = $_GET['action'];
}
self::$parametres['url']->setVariableRequete('action', self::$parametres['action']);
$registre = Registre::getInstance();
$registre->set('parametres', &self::$parametres);
$ClasseModule = self::$parametres['module'];
$action = self::$parametres['action'];
$module = new $ClasseModule();
$module->$action();
self::fusionnerSortie($module->getSortie());
}
private static function gererSession() {
if (Config::get('session_demarrage')) {
// Attribution d'un nom à la session
session_name(Config::get('session_nom'));
// Démarrage de la session
session_start();
}
}
private static function gererUtilisateur() {
$classe_utilisateur = Config::get('objet_utilisateur');
if(!class_exists($classe_utilisateur)) {
trigger_error('Aucune classe n\'a été définie pour gérer les utilisateurs', E_USER_ERROR);
}
self::$utilisateur = new $classe_utilisateur();
}
public static function getUtilisateur() {
if(self::$utilisateur == null) {
self::gererUtilisateur();
}
return self::$utilisateur;
}
public static function getIdUtilisateur() {
return self::getUtilisateur()->getIdentifiantNumerique();
}
/**
* Fusionne un tableau de sortie par défaut avec le tableau renvoyé par l'action du module.
* @param array le tableauàfusionner
*/
private static function fusionnerSortie($sortie) {
self::$parametres['sortie'] = array_merge(self::$parametres['sortie'], $sortie);
}
/**
* Vérifie si l'instance de classe été crée, si non la crée.
*/
private static function verifierCreationInstance() {
if (empty(self::$instance)) {
self::$instance = new AppControleur();
}
}
public static function getInstance() {
self::verifierCreationInstance();
return self::$instance;
}
/**
* Retourne le titre du contenu de l'application.
*/
public static function getMetaTitre() {
$sortie = self::$parametres['sortie']['titre'];
if (Config::get('sortie_encodage') != Config::get('appli_encodage')) {
$sortie = mb_convert_encoding($sortie, Config::get('sortie_encodage'), Config::get('appli_encodage'));
}
return $sortie;
}
/**
* Retourne la description du contenu de l'application.
*/
public static function getMetaDescription() {
$sortie = self::$parametres['sortie']['description'];
if (Config::get('sortie_encodage') != Config::get('appli_encodage')) {
$sortie = mb_convert_encoding($sortie, Config::get('sortie_encodage'), Config::get('appli_encodage'));
}
return $sortie;
}
/**
* Retourne les mots-clés (tags) du contenu de l'application.
*/
public static function getMetaTags() {
$sortie = self::$parametres['sortie']['tags'];
if (Config::get('sortie_encodage') != Config::get('appli_encodage')) {
$sortie = mb_convert_encoding($sortie, Config::get('sortie_encodage'), Config::get('appli_encodage'));
}
return $sortie;
}
/**
* Retourne le contenu du corps de l'application.
*/
public static function getContenuCorps() {
$sortie = self::$parametres['sortie']['corps'];
if (Config::get('sortie_encodage') != Config::get('appli_encodage')) {
$sortie = mb_convert_encoding($sortie, Config::get('sortie_encodage'), Config::get('appli_encodage'));
}
return $sortie;
}
/**
* Retourne le contenu de la tête de l'application.
*/
public static function getContenuTete() {
$sortie = self::$parametres['sortie']['tete'];
if (Config::get('sortie_encodage') != Config::get('appli_encodage')) {
$sortie = mb_convert_encoding($sortie, Config::get('sortie_encodage'), Config::get('appli_encodage'));
}
return $sortie;
}
/**
* Retourne le contenu du pied de l'application.
*/
public static function getContenuPied() {
$sortie = self::$parametres['sortie']['pied'];
if (Config::get('sortie_encodage') != Config::get('appli_encodage')) {
$sortie = mb_convert_encoding($sortie, Config::get('sortie_encodage'), Config::get('appli_encodage'));
}
return $sortie;
}
/**
* Retourne les éléments de navigation de l'application.
*/
public static function getContenuNavigation() {
$sortie = self::$parametres['sortie']['navigation'];
if (Config::get('sortie_encodage') != Config::get('appli_encodage')) {
$sortie = mb_convert_encoding($sortie, Config::get('sortie_encodage'), Config::get('appli_encodage'));
}
return $sortie;
}
/**
* Retourne les chronos pris dans l'appli
*/
public static function getChrono() {
$sortie = '';
if (Config::get('chronometrage')) {
$chrono = Chronometre::afficherChrono();
$sortie = mb_convert_encoding($chrono, Config::get('sortie_encodage'), Config::get('appli_encodage'));
}
return $sortie;
}
/**
* Retourne les messages d'exceptions et d'erreurs.
*/
public static function getExceptions() {
$sortie = (Config::get('fw_debogage')) ? GestionnaireException::getExceptions() : '';
if (Config::get('sortie_encodage') != Config::get('appli_encodage')) {
$sortie = mb_convert_encoding($sortie, Config::get('sortie_encodage'), Config::get('appli_encodage'));
}
return $sortie;
}
}
/trunk/applications/saisie/controleurs/aControleur.php
New file
0,0 → 1,272
<?php
// declare(encoding='UTF-8');
/**
* Classe Controleur générale de l'application saisie.
*
* @category php5.2
* @package saisie
* @author Aurélien Peronnet <aurelien@tela-botanica.org>
* @copyright 2010 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version SVN: $Id: aControleur.php 152 2010-09-06 16:19:12Z aurelien $
*/
abstract class aControleur extends Controleur {
const FMT_DATE = '%d/%m/%Y';// Supporte les formats de dates non valides (1989-00-00)
const FMT_DATE_TXT = '%A %d %B %Y';
const FMT_DATE_HEURE = '%d/%m/%Y %H:%i:%s';// Supporte les formats de dates non valides (1989-00-00 00:00:00)
const FMT_DATE_HEURE_TXT = '%A %d %B %Yà%H:%M';
const META_TITRE = 'titre';
const META_DESCRIPTION = 'description';
const META_TAGS = 'tags';
const RENDU_TETE = 'tete';
const RENDU_CORPS = 'corps';
const RENDU_PIED = 'pied';
const RENDU_NAVIGATION = 'navigation';
const TYPE_AUTRE = 'AUTRE';
const TYPE_TOTAL = 'TOTAL';
const SEPARATEUR_TYPE_VALEUR = '##';
const SEPARATEUR_VALEURS = ';;';
const SEPARATEUR_DONNEES = '||';
const VALEUR_NULL = 'NC';
private $sortie = array();
private $parametres = array();
protected static $hierarchie_appels = array();
// FIXME : voir s'il est plus intéressant d'utiliser une méthode dans les classes filles
protected $url = null;
public function __construct() {
$registre = Registre::getInstance();
$this->parametres = $registre->get('parametres');
$this->url = $this->parametres['url'];
parent::__construct();
}
/**
* Attribue une position de sortie à un contenu.
*/
protected function setSortie($position, $contenu, $fusionner = false) {
if ($this->verifierExistenceTypeSortie($position)) {
if ($fusionner) {
$this->sortie[$position] .= $contenu;
} else {
$this->sortie[$position] = $contenu;
}
}
}
/**
* Vérifie l'existence du type de sortie indiqué pour son utilisation dans le tableau de sortie.
* @param string le type de sortie à tester.
* @return bool true si le type de sortie est valide, sinon false.
*/
private function verifierExistenceTypeSortie($type) {
$existe = true;
if ($type != self::RENDU_TETE &&
$type != self::RENDU_NAVIGATION &&
$type != self::RENDU_CORPS &&
$type != self::RENDU_PIED &&
$type != self::META_TITRE &&
$type != self::META_DESCRIPTION &&
$type != self::META_TAGS) {
trigger_error("Le type de sortie '$type' n'est pas une valeur prédéfinie.", E_USER_WARNING);
$existe = false;
}
return $existe;
}
/**
* Retourne le tableau de sortie à utiliser dans le controleur principal de l'application.
*/
public function getSortie() {
return $this->sortie;
}
 
/**
* Execute l'action d'un module donnée et fusionne le résultat avec le tableau de sortie.
*/
protected function executerAction($ClasseModule, $action) {
$module = new $ClasseModule();
$module->$action();
$this->fusionnerSortie($module->getSortie());
}
/**
* Fusionne un tableau de sortie par défaut avec le tableau passé en paramètre.
* @param array le tableauàfusionner
*/
private function fusionnerSortie($sortie) {
$this->sortie = array_merge($this->sortie, $sortie);
}
protected function formaterParenthese($chaine_a_afficher) {
if ($chaine_a_afficher != '') {
$chaine_a_afficher = '('.$chaine_a_afficher.')';
}
return $chaine_a_afficher;
}
protected function formaterSautDeLigne($chaine_a_formater) {
$txt_a_retourner = preg_replace('/\n/', '<br />', $chaine_a_formater);
return $txt_a_retourner;
}
protected function formaterTableauDeTxt($tableau_de_txt, $majuscule = true, $point_final = true) {
$chaine_a_afficher = '';
$taille_du_tableau = count($tableau_de_txt);
if ($taille_du_tableau > 0) {
$index_avt_dernier = $taille_du_tableau - 1;
for ($i = 0; $i < $taille_du_tableau; $i++) {
$mot = $tableau_de_txt[$i];
if ($i != $index_avt_dernier) {
$chaine_a_afficher .= $mot.', ';
} else {
$chaine_a_afficher .= $this->nettoyerPointFinal($mot);
if ($point_final) {
$chaine_a_afficher .= '.';
}
}
}
}
if ($majuscule) {
$chaine_a_afficher = ucfirst($chaine_a_afficher);
}
return $chaine_a_afficher;
}
protected function formaterOuiNon($chaine_a_formater) {
$txt_a_retourner = '';
if ($chaine_a_formater == '0') {
$txt_a_retourner = 'non';
} else if ($chaine_a_formater == '1') {
$txt_a_retourner = 'oui';
}
return $txt_a_retourner;
}
protected function formaterDate($date, $format = self::FMT_DATE_HEURE) {
if ($date == '' || $date == '0000-00-00' || $date == '0000-00-00 00:00:00') {
$date = 'Inconnue';
} else {
if (preg_match('/^([0-9]{4})-([0-9]{2})-([0-9]{2})(?: ([0-9]{2}):([0-9]{2}):([0-9]{2})|)$/', $date, $match)) {// Date Heure
$annee = $match[1];
$mois = $match[2];
$jour = $match[3];
$heure = (isset($match[4])) ? $match[4] : '00';
$minute = (isset($match[5])) ? $match[5] : '00';
$seconde = (isset($match[6])) ? $match[6] : '00';
if ($format == self::FMT_DATE && $jour == '00' && $mois == '00') {
$date = $annee;
} else if ($format == self::FMT_DATE && $jour == '00') {
$date = strftime('%b', mktime(0, 0, 0, $mois, 1)).' '.$annee;
} else {
$timestamp = strtotime($date);
if ($timestamp !== false) {
$date = strftime($format, $timestamp);
}
}
} else {
$e = "La chaine '$date' n'est pas reconnue.";
trigger_error($e, E_USER_WARNING);
}
}
return $date;
}
protected function formaterCourriels($courriels) {
$fmt_courriels = '';
if (!empty($courriels)) {
$courriels = (is_array($courriels)) ? $courriels : array($courriels);
foreach ($courriels as $cle => $courriel) {
$courriel = preg_replace('/@/', ' [arrobase] ', $courriel);
$courriel = preg_replace('/[.]([^.]+)$/', " [point] $1", $courriel);
$fmt_courriels[] = $this->getVue('courriel', array('courriel' => $courriel));
}
$fmt_courriels = implode(', ', $fmt_courriels);
}
return $fmt_courriels;
}
protected function nettoyerPointFinal($mot) {
$mot = preg_replace('/[.]$/', '', $mot);
return $mot;
}
protected function postraiterDonnees(&$tableau) {
if (count($tableau) > 0) {
foreach ($tableau as $cle => &$valeur) {
if ($valeur == '') {
$valeur = '&nbsp;';
} else if (is_string($valeur)) {
$valeur = $this->remplacerEsperluette($valeur);
} else if (is_array($valeur)) {
$this->postraiterDonnees($valeur);
}
}
}
}
private function remplacerEsperluette($txt) {
$txt = preg_replace('/&(?!([a-z]+|#[0-9]+|#x[0-9a-f]+);)/i', '&amp;', $txt, -1);
return $txt;
}
protected function chargerPiedDePage() {
$donnees['appli'] = Application::getInfo();
$this->setSortie(self::RENDU_PIED, $this->getVue('pied', $donnees));
}
public static function getUrlConsultationFicheStation($id_station) {
// TODO: gérer les urls comme dans l'annuaire
return '?module=Station&action=afficherInformationsStation&id_station='.$id_station;
}
public static function getUrlFormulaireSaisieStation() {
// TODO: gérer les urls comme dans l'annuaire
return '?module=Station&action=afficherFormulaireSaisieStation';
}
public static function getUrlValidationFormulaireSaisieStation() {
// TODO: gérer les urls comme dans l'annuaire
return '?module=Station&action=validerFormulaireSaisieStation';
}
public static function getUrlFormulaireModificationStation($id_station) {
// TODO: gérer les urls comme dans l'annuaire
return '?module=Station&action=afficherFormulaireModificationStation&id_station='.$id_station;
}
public static function getUrlConsultationFicheIndividu($id_station, $id_individu) {
// TODO: gérer les urls comme dans l'annuaire
return '?module=Individu&action=afficherInformationsIndividu&id_station='.$id_station.'&id_individu='.$id_individu;
}
public static function getUrlFormulaireAjoutIndividu($id_station, $id_espece) {
// TODO: gérer les urls comme dans l'annuaire
return '?module=Individu&action=afficherFormulaireAjoutIndividu&id_station='.$id_station.'&id_espece='.$id_espece;
}
public static function getUrlConsultationEspeceStation($id_station, $id_espece) {
// TODO: gérer les urls comme dans l'annuaire
return '?module=Individu&action=afficherListeIndividu&id_station='.$id_station.'&id_espece='.$id_espece;
}
 
public static function getUrlFormulaireAjoutEspece($id_station) {
// TODO: gérer les urls comme dans l'annuaire
return '?module=Espece&action=afficherFormulaireSaisieEspece&id_station='.$id_station;
}
public static function getUrlFormulaireModificationObservation($id_individu) {
// TODO: gérer les urls comme dans l'annuaire
return '?module=Observation&action=afficherFormulaireModificationObservation&id_individu='.$id_individu;
}
}
/trunk/applications/saisie/controleurs/Station.php
New file
0,0 → 1,182
<?php
// declare(encoding='UTF-8');
/**
* Classe de test des Controleurs.
*
* @package Collection
* @category Php 5.2
* @author Jean-Pascal MILCENT <jpm@tela-botanica.org>
* @copyright 2010 Tela-Botanica
* @license http://www.cecill.info/licences/Licence_CeCILL_V2-fr.txt Licence CECILL
* @license http://www.gnu.org/licenses/gpl.html Licence GNU-GPL
* @version SVN: $Id: Fiche.php 152 2010-09-06 16:19:12Z jpm $
*/
class Station extends aControleur {
 
private $id_station_en_cours = null;
public function __construct() {
parent::__construct();
$this->initialiser();
}
public function initialiser() {
$this->id_station_en_cours = $_GET['id_station'];
$this->setNavigation();
}
public function executerActionParDefaut() {
return $this->afficherFormulaireSaisieStation();
}
// +---------------------------------------------------------------------------------------------------------------+
// METHODES D'AFFICHAGE DE FORMULAIRE
public function afficherFormulaireSaisieStation() {
$donnees = array();
$formulaire = $this->getVue('formulaires/station_saisie',$donnees);
$this->setSortie(self::RENDU_CORPS, $formulaire);
}
public function afficherFormulaireModificationStation() {
$donnees = array();
$id_station = $this->id_station_en_cours;
$donnees['infos_station'] = $this->getInformationsStation($id_station);
$formulaire = $this->getVue('formulaires/station_modification',$donnees);
$this->setSortie(self::RENDU_CORPS, $formulaire);
}
// +---------------------------------------------------------------------------------------------------------------+
// METHODES APPELEES LORS DE LA VALIDATION D'UN FORMULAIRE
public function validerFormulaireSaisieStation() {
$valeurs_formulaires = $_POST['form_saisie_station'];
$valeurs_verifiees = $this->collecterValeursFormulaireSaisieStation();
$station_dao = new StationDao();
$station_dao->ajouterStation($valeurs_verifiees);
}
private function collecterValeursFormulaireSaisieStation() {
$valeurs_verifiees['station_commune'] = $_POST['station_commune'];
$valeurs_verifiees['station_milieu'] = $_POST['station_milieu'];
$valeurs_verifiees['station_nom'] = $_POST['station_nom'];
$valeurs_verifiees['station_lat'] = $_POST['station_lat'];
$valeurs_verifiees['station_lon'] = $_POST['station_lon'];
//TODO: verifier valeurs plus complètement
return $valeurs_verifiees;
}
public function validerFormulaireModificationStation() {
$valeurs_formulaires = $_POST['form_modif_station'];
$valeurs_verifiees = array();
$station_dao = new StationDao();
$station_dao->modifierStation($valeurs_verifiees);
}
// +---------------------------------------------------------------------------------------------------------------+
// METHODES D'AFFICHAGES D'INFORMATION
public function afficherlisteStation($param = null) {
$id_utilisateur = AppControleur::getIdUtilisateur();
$donnees['stations'] = $this->getListeStations($id_utilisateur);
$this->setSortie(self::RENDU_CORPS, $this->getVue('listes/station_liste', $donnees));
}
public function afficherInformationsStation() {
$id_station = $this->id_station_en_cours;
$donnees['id_station'] = $id_station;
$donnees['infos_station'] = $this->getInformationsStation($id_station);
$this->setSortie(self::RENDU_CORPS, $this->getVue('fiches/station_fiche', $donnees));
}
// +---------------------------------------------------------------------------------------------------------------+
// METHODES POUR FABRIQUER LE MENU
public function construireMenuNavigation($espece_en_cours = null) {
$id_station_en_cours = $this->id_station_en_cours;
$stations = $this->getListeStations();
foreach($stations as &$station) {
$station['url'] = aControleur::getUrlConsultationFicheStation($station['id']);
}
if($id_station_en_cours != null) {
$especes_station_en_cours = $this->getEspecesStation($id_station_en_cours);
foreach($especes_station_en_cours as &$espece) {
$espece['url'] = aControleur::getUrlConsultationEspeceStation($id_station_en_cours, $espece['id']);
}
$stations[$id_station_en_cours]['especes'] = $especes_station_en_cours;
}
$donnees['stations'] = $stations;
$donnees['id_station_en_cours'] = $id_station_en_cours;
if($id_espece_en_cours != null) {
$donnees['id_espece_en_cours'] = $id_espece;
}
$menu_navigation = $this->getVue('navigation/menu', $donnees);
return $menu_navigation;
}
public function setNavigation() {
$this->setSortie(self::RENDU_NAVIGATION, $this->construireMenuNavigation());
}
// +---------------------------------------------------------------------------------------------------------------+
// METHODE D'APPELS AUX DAOS
protected function getListeStations() {
$station_dao = new StationDao();
return $station_dao->getListeStations();
}
private function getInformationsStation($id_station) {
$station_dao = new StationDao();
$infos_station = $station_dao->getInformationsStation($id_station);
$infos_station['individus'] = $this->getIndividusStation($id_station);
return $infos_station;
}
private function getEspecesStation($id_station) {
$espece_dao = new EspeceDao();
$liste_especes = $espece_dao->getListeEspecesPourStation($id_station);
return $liste_especes;
}
public function getIndividusStation($id_station) {
$individu_dao = new IndividuDao();
$liste_individus = $individu_dao->getListeIndividusPourStation($id_station);
return $liste_individus;
}
}
/trunk/applications/saisie/controleurs/DrupalUtilisateur.php
New file
0,0 → 1,28
<?php
class DrupalUtilisateur extends Utilisateur {
public function getIdentifiantNumerique() {
return $GLOBALS['user']->uid;
}
public function getEmail() {
return $GLOBALS['user']-mail;
}
public function getNom() {
return $GLOBALS['user']->name;
}
public function getPrenom() {
return '';
}
public function estAdmin() {
return in_array('3',array_keys($GLOBALS['user']->roles));
}
public function estIdentifie() {
return in_array('2',array_keys($GLOBALS['user']->roles));
}
}
?>
/trunk/applications/saisie/i18n/fr.ini
New file
0,0 → 1,3
[saisie]
station = Creation d'un nouvelle station
espece = Création d'un nouvel individu
/trunk/applications/saisie/framework.php
New file
0,0 → 1,5
<?php
// Inclusion du Framework
// Indiquer ci-dessous le chemin vers le fichier autoload.inc.php de la bonne version du Framework
require_once '/home/aurelien/web/framework/framework/autoload.inc.php';
?>
/trunk/applications/saisie/saisie.php
New file
0,0 → 1,53
<?php
// declare(encoding='UTF-8');
/**
* Application de saisie d'ODS.
* Fichier principal d'initialisation.
*
* @category PHP5
* @package Saisie
* @author Aur�lien Peronnet <aurelien@tela-botanica.org>
* @copyright 2010 Tela-Botanica
* @license GPL-v3 et CECILL-v2
* @version $Id: collection.php 142 2010-08-30 12:41:00Z aurelien $
*/
// Autoload pour cette application
function __autoload($nom_classe) {
// Tableau des chemins à inclure pour trouver une classe relatif à ce fichier
$chemins = array(
'bibliotheque'.DS.'dao',
'bibliotheque'.DS.'pear',
'bibliotheque'.DS.'utilitaires',
'composants',
'composants'.DS.'cartographie');
foreach ($chemins as $chemin) {
$fichier_a_inclure = dirname(__FILE__).DS.$chemin.DS.$nom_classe.'.php';
if (file_exists($fichier_a_inclure)) {
include_once $fichier_a_inclure;
return null;
}
}
}
 
// Le fichier autoload.inc.php du Framework de Tela Botanica doit être appelée avant tout autre chose dans l'application.
// Sinon, rien ne sera chargé.
// Chemin du fichier chargeant le framework requis
$framework = dirname(__FILE__).'/framework.php';
if (!file_exists($framework)) {
$e = "Veuillez paramétrer l'emplacement et la version du Framework dans le fichier $framework";
trigger_error($e, E_USER_ERROR);
} else {
// Inclusion du Framework
require_once $framework;
// Ajout d'information concernant cette application
Application::setChemin(__FILE__);// Obligatoire
Application::setInfo(Config::get('info'));
// Lancement du débogage si nécessaire
if (Config::get('chronometrage')) {
Chronometre::chrono('Saisie.php - début');
}
// Lancement de l'application
AppControleur::initialiser();
}
?>