commit 8461a4f4a1d9bf0650f8c23590465c0e330f8773
parent 67d4a6c660b2b3848358805e088c529b77db9b4d
Author: Jan Dankert <devnull@localhost>
Date: Sat, 16 Dec 2017 23:21:31 +0100
Eigenes Modul für alle Util-Klassen.
Diffstat:
70 files changed, 20693 insertions(+), 20736 deletions(-)
diff --git a/dispatcher.php b/dispatcher.php
@@ -37,7 +37,7 @@ try
// Jetzt erst die Sitzung starten (nachdem alle Klassen zur Verfügung stehen).
session_start();
- require_once( OR_SERVICECLASSES_DIR."Session.class.".PHP_EXT );
+ require_once( OR_MODULES_DIR."util/Session.class.".PHP_EXT );
// Vorhandene Konfiguration aus der Sitzung lesen.
$conf = Session::getConfig();
@@ -53,7 +53,7 @@ try
// Fest eingebaute Standard-Konfiguration laden.
$conf = array();
- require('./util/config-default.php'); // writes to $conf
+ require(OR_MODULES_DIR.'util/config-default.php'); // writes to $conf
$customConfig = Configuration::load();
$conf = array_replace_recursive($conf, $customConfig);
diff --git a/init.php b/init.php
@@ -121,15 +121,11 @@ register_shutdown_function( "fatal_handler" );
require_once( "functions/request.inc.php" );
// Werkzeugklassen einbinden.
-require_once( OR_SERVICECLASSES_DIR."include.inc.".PHP_EXT );
+require_once( OR_MODULES_DIR."util/require.".PHP_EXT );
require_once( OR_AUTHCLASSES_DIR."include.inc.".PHP_EXT );
-
-
-
require_once( OR_MODULES_DIR."security/require.".PHP_EXT );
require_once( OR_MODULES_DIR."template-engine/require.".PHP_EXT );
-require_once( OR_MODULES_DIR."util/require.".PHP_EXT );
require_once( OR_MODULES_DIR."configuration/require.".PHP_EXT );
diff --git a/modules/util/.htaccess b/modules/util/.htaccess
@@ -0,0 +1,2 @@
+order deny,allow
+deny from all+
\ No newline at end of file
diff --git a/modules/util/AbstractTree.class.php b/modules/util/AbstractTree.class.php
@@ -0,0 +1,171 @@
+<?php
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+/**
+ * Darstellen einer Baumstruktur mit Funktion zum Laden, Oeffnen und Schliessen
+ * von Teilbaeumen
+ * @author $Author$
+ * @version $Revision$
+ * @package openrat.services
+ */
+class AbstractTree
+{
+ /**
+ * Alle Elemente des Baumes
+ */
+ var $elements = array();
+
+ var $tempElements = array();
+ var $userIsAdmin = false;
+
+ var $autoOpen = array(0,1);
+
+ var $opened = array();
+
+ /**
+ * Hoechste Element-Id
+ * @type Integer
+ */
+ var $maxId;
+
+ // Konstruktor
+ function AbstractTree()
+ {
+ // Feststellen, ob der angemeldete Benutzer ein Administrator ist
+ $user = Session::getUser();
+ $this->userIsAdmin = $user->isAdmin;
+
+ // Wurzel-Element laden
+ $this->root();
+ $this->elements[0] = $this->tempElements[0];
+ $this->tempElements = array();
+ $this->maxId = 0;
+
+ foreach( $this->autoOpen as $openId )
+ $this->open($openId);
+ }
+
+ function refresh() {
+
+ $this->elements = array();
+
+ // Wurzel-Element laden
+ $this->root();
+ $this->elements[0] = $this->tempElements[0];
+ $this->tempElements = array();
+ $this->maxId = 0;
+
+ $oids = $this->opened;
+ $this->opened = array();
+ foreach( $oids as $oid)
+ {
+ if ( isset($this->elements[$oid]) )
+ $this->open($oid);
+ }
+ }
+
+
+
+ function all() {
+
+ $this->elements = array();
+ $this->opened = array();
+
+ // Wurzel-Element laden
+ $this->root();
+ $this->elements[0] = $this->tempElements[0];
+ $this->tempElements = array();
+ $this->maxId = 0;
+
+ for( $eid=0;isset($this->elements[$eid]); $eid++)
+ {
+ $this->open($eid);
+ }
+ }
+
+
+ /**
+ * Oeffnen eines Teilbaumes. Es wird der eindeutige Name des zu oeffnenden Teilbaumes als
+ * Parameter uebergeben
+ * @param elementName der Name des Elementes, welches zu oeffnen ist
+ */
+ function open( $elementId )
+ {
+ $k = array_search($elementId,$this->opened);
+ if ( $k !== false )
+ return; // Ist schon offen. Evtl. Reload gedrückt?
+
+ $this->opened[] = $elementId;
+ if ( ! isset($this->elements[$elementId]) )
+ return;
+ $funcName = $this->elements[$elementId]->type;
+ if ( empty($funcName) )
+ return;
+
+ $this->$funcName( $this->elements[$elementId]->internalId );
+
+ // Wenn keine Unterelemente gefunden, dann die ?ffnen-Funktion deaktivieren
+ if ( count( $this->tempElements ) == 0 )
+ $this->elements[$elementId]->type = '';
+
+ foreach( $this->tempElements as $treeElement )
+ {
+ $this->maxId++;
+ $this->elements[$elementId]->subElementIds[] = $this->maxId;
+ $this->elements[$this->maxId] = $treeElement;
+ }
+
+ if ( count($this->tempElements)==1 )
+ {
+ $this->tempElements = array();
+ $this->open($this->maxId);
+ }
+
+ $this->tempElements = array();
+ }
+
+
+ /**
+ * Schliessen eines Teilbaumes
+ * @param elementName der Name des Elementes, welches zu schliessen ist
+ */
+
+ function close( $elementId )
+ {
+ $this->elements[$elementId]->subElementIds = array();
+
+ $k = array_search($elementId,$this->opened);
+ if ( $k !== false )
+ unset($this->opened[$k]);
+ }
+
+
+ /**
+ * Hinzufuegen eines Baum-Elementes
+ * @param TreeElement Hinzuzufuegendes Baumelement
+ */
+ function addTreeElement( $treeElement )
+ {
+ $this->tempElements[] = $treeElement;
+ }
+
+
+}
+
+?>+
\ No newline at end of file
diff --git a/modules/util/AdministrationTree.class.php b/modules/util/AdministrationTree.class.php
@@ -0,0 +1,594 @@
+<?php
+use cms\model\User;
+use cms\model\Project;
+use cms\model\Group;
+use cms\model\Folder;
+
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+/**
+ * Darstellen einer Baumstruktur mit Administrationfunktionen
+ * @author $Author$
+ * @version $Revision$
+ * @package openrat.services
+ */
+class AdministrationTree extends AbstractTree
+{
+ /**
+ * Alle Elemente des Baumes
+ */
+ var $elements;
+ var $confCache = array();
+
+ function root()
+ {
+ if ( !$this->userIsAdmin )
+ Http::notAuthorized('Administration-Tree is only visible for admins.');
+
+// $treeElement = new TreeElement();
+// $treeElement->text = lang('GLOBAL_ADMINISTRATION');
+// $treeElement->description = lang('GLOBAL_ADMINISTRATION');
+// $treeElement->type = 'administration';
+// $treeElement->icon = 'administration';
+
+ $this->administration();
+
+ $this->autoOpen[] = 1;
+ }
+
+
+
+ function administration()
+ {
+ global $conf;
+ $conf_config = $conf['interface']['config'];
+
+ $treeElement = new TreeElement();
+ $treeElement->id = 0;
+ $treeElement->text = lang('GLOBAL_PROJECTS');
+ $treeElement->description = lang('GLOBAL_PROJECTS');
+ $treeElement->url = Html::url('projectlist','show',0,array(REQ_PARAM_TARGET=>'content'));
+ $treeElement->action = 'projectlist';
+ $treeElement->icon = 'projectlist';
+ $treeElement->type = 'projects';
+ $treeElement->target = 'cms_main';
+
+ $this->addTreeElement( $treeElement );
+
+
+ $treeElement = new TreeElement();
+ $treeElement->text = lang('USER_AND_GROUPS');
+ $treeElement->description = lang('USER_AND_GROUPS');
+ $treeElement->icon = 'userlist';
+ $treeElement->type = 'userandgroups';
+
+ $this->addTreeElement( $treeElement );
+// $this->userandgroups(0);;
+
+ if ( $conf_config['enable'] )
+ {
+ $treeElement = new TreeElement();
+ $treeElement->text = lang('PREFERENCES');
+ $treeElement->description = lang('PREFERENCES');
+ $treeElement->icon = 'configuration';
+ //$treeElement->type = 'prefs';
+ $treeElement->action = 'configuration';
+
+ $this->addTreeElement( $treeElement );
+ }
+
+ // Wechseln zu: Projekte...
+ /*
+ foreach( Project::getAll() as $id=>$name )
+ {
+ $treeElement = new TreeElement();
+
+ $treeElement->text = lang('PROJECT').' '.$name;
+ $treeElement->url = Html::url(array('action' =>'tree',
+ 'subaction' =>'reload',
+ 'projectid' =>$id ));
+ $treeElement->icon = 'project';
+ $treeElement->description = '';
+ $treeElement->target = 'cms_tree';
+
+ $this->addTreeElement( $treeElement );
+ }
+ */
+ }
+
+
+
+ function userandgroups( )
+ {
+ $treeElement = new TreeElement();
+ $treeElement->text = lang('GLOBAL_USER');
+ $treeElement->description = lang('GLOBAL_USER');
+ $treeElement->url = Html::url('user','listing',0,array(REQ_PARAM_TARGET=>'content'));
+ $treeElement->action = 'userlist';
+ $treeElement->icon = 'userlist';
+ $treeElement->target = 'cms_main';
+ $treeElement->type = 'users';
+
+ $this->addTreeElement( $treeElement );
+
+ $treeElement = new TreeElement();
+ $treeElement->text = lang('GLOBAL_GROUPS');
+ $treeElement->description = lang('GLOBAL_GROUPS');
+ $treeElement->url = Html::url('group','listing',0,array(REQ_PARAM_TARGET=>'content'));
+ $treeElement->action = 'grouplist';
+ $treeElement->icon = 'userlist';
+ $treeElement->target = 'cms_main';
+ $treeElement->type = 'groups';
+
+ $this->addTreeElement( $treeElement );
+ }
+
+
+ function projects( )
+ {
+ // Schleife ueber alle Projekte
+ foreach(Project::getAllProjects() as $id=> $name )
+ {
+ $treeElement = new TreeElement();
+
+ $treeElement->internalId = $id;
+ $treeElement->id = $id;
+ $treeElement->text = $name;
+ $treeElement->url = Html::url('project','edit',$id,array(REQ_PARAM_TARGET=>'content'));
+ $treeElement->icon = 'project';
+ $treeElement->action = 'project';
+ $treeElement->type = 'project';
+ $treeElement->description = '';
+ $treeElement->target = 'cms_main';
+
+ $this->addTreeElement( $treeElement );
+ }
+ }
+
+
+
+ function project( $projectid )
+ {
+ $project = new Project( $projectid );
+
+ // Hoechster Ordner der Projektstruktur
+ $folder = new Folder( $project->getRootObjectId() );
+ $folder->load();
+
+
+ // Ermitteln, ob der Benutzer Projektadministrator ist
+ // Projektadministratoren haben das Recht, im Root-Ordner die Eigenschaften zu aendern.
+ if ( $folder->hasRight( ACL_PROP ) )
+ $this->userIsProjectAdmin = true;
+
+ if ( $folder->hasRight( ACL_READ ) )
+ {
+ $treeElement = new TreeElement();
+ $treeElement->id = $folder->objectid;
+ // $treeElement->text = $folder->name;
+ $treeElement->text = lang('FOLDER_ROOT');
+ $treeElement->description = lang('FOLDER_ROOT_DESC');
+ $treeElement->icon = 'folder';
+ $treeElement->action = 'folder';
+ $treeElement->url = Html::url( 'folder','',$folder->objectid,array(REQ_PARAM_TARGET=>'content') );
+ $treeElement->target = 'content';
+ $treeElement->type = 'folder';
+ $treeElement->internalId = $folder->objectid;
+ $this->addTreeElement( $treeElement );
+ }
+
+
+ if ( $this->userIsProjectAdmin )
+ {
+ // Templates
+ $treeElement = new TreeElement();
+ $treeElement->id = 0;
+ $treeElement->text = lang('GLOBAL_TEMPLATES');
+ $treeElement->url = Html::url('template','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content'));
+ $treeElement->description= lang('GLOBAL_TEMPLATES_DESC');
+ $treeElement->icon = 'templatelist';
+ $treeElement->action = 'templatelist';
+ $treeElement->target = 'content';
+ $treeElement->type = 'templates';
+ $this->addTreeElement( $treeElement );
+ }
+
+
+ // Sprachen
+ $treeElement = new TreeElement();
+ $treeElement->description= '';
+ $treeElement->id = 0;
+ $treeElement->action = 'languagelist';
+ $treeElement->text = lang('GLOBAL_LANGUAGES');
+ $treeElement->url = Html::url('language','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content'));
+ $treeElement->icon = 'languagelist';
+ $treeElement->description= lang('GLOBAL_LANGUAGES_DESC');
+ $treeElement->target = 'content';
+
+ // Nur fuer Projekt-Administratoren aufklappbar
+ if ( $this->userIsProjectAdmin )
+ $treeElement->type = 'languages';
+
+ $this->addTreeElement( $treeElement );
+
+
+ // Projektmodelle
+ $treeElement = new TreeElement();
+ $treeElement->description= '';
+
+ // Nur fuer Projekt-Administratoren aufklappbar
+ if ( $this->userIsProjectAdmin )
+ $treeElement->type = 'models';
+
+ $treeElement->id = 0;
+ $treeElement->description= lang('GLOBAL_MODELS_DESC');
+ $treeElement->text = lang('GLOBAL_MODELS');
+ $treeElement->url = Html::url('model','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content'));
+ $treeElement->action = 'modellist';
+ $treeElement->icon = 'modellist';
+ $treeElement->target = 'content';
+ $this->addTreeElement( $treeElement );
+
+
+ // Sonstiges
+ // $treeElement = new TreeElement();
+ // $treeElement->text = lang('GLOBAL_OTHER');
+ // $treeElement->description= lang('GLOBAL_OTHER_DESC');
+ // $treeElement->icon = 'other';
+ // $treeElement->type = 'other';
+ // $this->addTreeElement( $treeElement );
+
+ // Suche
+ $treeElement = new TreeElement();
+ $treeElement->id = 0;
+ $treeElement->text = lang('GLOBAL_SEARCH');
+ $treeElement->url = Html::url('search','',0,array(REQ_PARAM_TARGET=>'content'));
+ $treeElement->action = 'search';
+ $treeElement->icon = 'search';
+ $treeElement->description = lang('GLOBAL_SEARCH_DESC');
+ $treeElement->target = 'content';
+ $this->addTreeElement( $treeElement );
+
+ }
+
+
+
+ function prefs_system( )
+ {
+ $system = array( 'time' => date('r'),
+ 'os' => php_uname('s'),
+ 'host' => php_uname('n'),
+ 'release'=> php_uname('r'),
+ 'machine'=> php_uname('m'),
+ 'owner' => get_current_user(),
+ 'pid' => getmypid() );
+
+ foreach( $system as $key=>$value )
+ {
+ $treeElement = new TreeElement();
+ $treeElement->text = $key.'='.$value;
+ $treeElement->icon = 'config_property';
+ $this->addTreeElement( $treeElement );
+ $treeElement->description = lang('SETTING')." '".$key."'".(!empty($value)?': '.$value:'');
+ }
+
+ if ( function_exists('getrusage') ) // Funktion existiert auf WIN32 nicht.
+ {
+ foreach( getrusage() as $name=>$value );
+ {
+ $treeElement = new TreeElement();
+ $treeElement->text = $name.':'.$value;
+ $treeElement->description = lang('SETTING')." '".$name."'".(!empty($value)?': '.$value:'');
+ $treeElement->icon = 'config_property';
+ $this->addTreeElement( $treeElement );
+ }
+ }
+ }
+
+
+
+
+ function prefs_php( )
+ {
+ $php_prefs = array( 'version' => phpversion(),
+ 'SAPI' => php_sapi_name(),
+ 'session-name' => session_name(),
+ 'magic_quotes_gpc' => get_magic_quotes_gpc(),
+ 'magic_quotes_runtime'=> get_magic_quotes_runtime() );
+
+ foreach( array('upload_max_filesize',
+ 'file_uploads',
+ 'memory_limit',
+ 'max_execution_time',
+ 'post_max_size',
+ 'display_errors',
+ 'register_globals'
+ ) as $iniName )
+ $php_prefs[ $iniName ] = ini_get( $iniName );
+
+ foreach( $php_prefs as $key=>$value )
+ {
+ $treeElement = new TreeElement();
+ $treeElement->text = $key.'='.$value;
+ $treeElement->description = lang('SETTING')." '".$key."'".(!empty($value)?': '.$value:'');
+ $treeElement->icon = 'config_property';
+ $this->addTreeElement( $treeElement );
+ }
+ }
+
+
+
+ function prefs_extensions( )
+ {
+ $extensions = get_loaded_extensions();
+ asort( $extensions );
+
+ foreach( $extensions as $id=>$extensionName )
+ {
+ $treeElement = new TreeElement();
+ $treeElement->text = $extensionName;
+ $treeElement->icon = 'config_property';
+ $treeElement->internalId = $id;
+ $this->addTreeElement( $treeElement );
+ }
+ }
+
+
+
+ function prefs_extension( $id )
+ {
+ $extensions = get_loaded_extensions();
+ $functions = get_extension_funcs( $extensions[$id] );
+ asort( $functions );
+
+ foreach( $functions as $functionName )
+ {
+ $treeElement = new TreeElement();
+ $treeElement->text = $functionName;
+ $treeElement->icon = 'config_property';
+ $this->addTreeElement( $treeElement );
+ }
+ }
+
+
+ /**
+ * Anzeigen von Einstellungen.
+ *
+ * @param $id
+ */
+ function prefs( )
+ {
+ global $conf;
+
+ if ( !@$conf['security']['show_system_info'] )
+ return;
+
+ $conf_config = $conf['interface']['config'];
+
+
+ $treeElement = new TreeElement();
+
+ $treeElement->internalId = -1;
+ $treeElement->text = 'OpenRat';
+ $treeElement->icon = 'configuration';
+
+ if ( !empty($conf_config['file_manager_url']) )
+ $treeElement->url = $conf_config['file_manager_url'];
+ $treeElement->target = '_blank';
+ $treeElement->description = '';
+ $treeElement->type = 'prefs_cms';
+ $this->addTreeElement( $treeElement );
+
+
+
+ if ( !empty($conf_config['show_system']) )
+ {
+ $treeElement = new TreeElement();
+
+ $treeElement->internalId = 0;
+ $treeElement->text = lang('GLOBAL_SYSTEM');
+ $treeElement->icon = 'configuration';
+
+ $treeElement->description = '';
+ $treeElement->target = 'cms_main';
+ $treeElement->type = 'prefs_system';
+ $this->addTreeElement( $treeElement );
+ }
+
+
+ if ( !empty($conf_config['show_interpreter']) )
+ {
+ $treeElement = new TreeElement();
+
+ $treeElement->internalId = 0;
+ $treeElement->text = lang('GLOBAL_PHP');
+ $treeElement->icon = 'configuration';
+
+ $treeElement->description = '';
+ $treeElement->target = 'cms_main';
+ $treeElement->type = 'prefs_php';
+ $this->addTreeElement( $treeElement );
+ }
+
+
+ if ( !empty($conf_config['show_extensions']) )
+ {
+ $treeElement = new TreeElement();
+
+ $treeElement->internalId = 0;
+ $treeElement->text = lang('GLOBAL_EXTENSIONS');
+ $treeElement->icon = 'configuration';
+
+ $treeElement->description = '';
+ $treeElement->target = 'cms_main';
+ $treeElement->type = 'prefs_extensions';
+ $this->addTreeElement( $treeElement );
+ }
+ }
+
+
+ function prefs_cms( $id )
+ {
+ global $conf;
+
+ if ( $id < 0 )
+ {
+ $tmpConf = $conf;
+ }
+ else
+ $tmpConf = $this->confCache[$id];
+
+ if ( !is_array($tmpConf) )
+ $tmpConf = array('unknown');
+
+ foreach( $tmpConf as $key=>$value )
+ {
+ if ( is_array($value) )
+ {
+ $this->confCache[crc32($key)] = $value;
+
+ $treeElement = new TreeElement();
+
+ $treeElement->internalId = crc32($key);
+ $treeElement->text = $key;
+// if ( $id == 0 )
+// $treeElement->url = Html::url('main','prefs',0,array('conf'=>$key));
+ $treeElement->icon = 'configuration';
+
+ $treeElement->description = count($value).' '.lang('SETTINGS');
+ $treeElement->target = 'cms_main';
+ $treeElement->type = 'prefs_cms';
+ $this->addTreeElement( $treeElement );
+ }
+ else
+ {
+ // Die PHP-funktion 'parse_ini_file()' liefert alle Einstellungen leider nur als String
+ // Daher weiß man hier nicht, ob '1' nun '1' oder 'true' heißen soll.
+ if ( $value=='' )
+ // Anzeige 'Leer'
+ $value = lang('EMPTY');
+ elseif ( $value=='0' )
+ // Anzeige 'Nein'
+ $value = $value.' ('.lang('IS_NO').')';
+ elseif ( $value=='1' )
+ // Anzeige 'Ja'
+ $value = '+'.$value.' ('.lang('IS_YES').')';
+ elseif ( is_numeric($value) )
+ // Anzeige numerische Werte
+ $value = ($value>0?'+':'').$value;
+ else
+ // Anzeige von Zeichenketten
+ $value = $value;
+
+ $this->confCache[crc32($key)] = $value;
+
+ if ( strpos($key,'pass') !== FALSE )
+ $value = '***'; // Kennwörter nicht anzeigen
+
+ $treeElement = new TreeElement();
+ $treeElement->text = $key.': '.$value;
+ $treeElement->icon = 'config_property';
+ $treeElement->description = lang('SETTING')." '".$key."'".(!empty($value)?': '.$value:'');
+
+ $this->addTreeElement( $treeElement );
+ }
+ }
+ }
+
+
+ function users( )
+ {
+ foreach( User::getAllUsers() as $user )
+ {
+ $treeElement = new TreeElement();
+ $treeElement->id = $user->userid;
+ $treeElement->internalId = $user->userid;
+ $treeElement->text = $user->name;
+ $treeElement->url = Html::url('user','edit',
+ $user->userid,array(REQ_PARAM_TARGET=>'content') );
+ $treeElement->action = 'user';
+ $treeElement->icon = 'user';
+
+ $desc = $user->fullname;
+
+ if ( $user->isAdmin )
+ $desc .= ' ('.lang('USER_ADMIN').') ';
+ if ( $user->desc == "" )
+ $desc .= ' - '.lang('GLOBAL_NO_DESCRIPTION_AVAILABLE');
+ else
+ $desc .= ' - '.$user->desc;
+
+ $treeElement->description = $desc;
+ $treeElement->target = 'cms_main';
+
+ $this->addTreeElement( $treeElement );
+ }
+ }
+
+
+ function groups( )
+ {
+
+ foreach( Group::getAll() as $id=>$name )
+ {
+ $treeElement = new TreeElement();
+
+ $g = new Group( $id );
+ $g->load();
+
+ $treeElement->id = $id;
+ $treeElement->internalId = $id;
+ $treeElement->text = $g->name;
+ $treeElement->url = Html::url('group','edit',$id,
+ array(REQ_PARAM_TARGET=>'content') );
+ $treeElement->icon = 'group';
+ $treeElement->description = lang('GLOBAL_GROUP').' '.$g->name.': '.implode(', ',$g->getUsers());
+ $treeElement->target = 'cms_main';
+ $treeElement->type = 'userofgroup';
+ $treeElement->action = 'group';
+
+ $this->addTreeElement( $treeElement );
+ }
+ }
+
+
+ function userofgroup( $id )
+ {
+ $g = new Group( $id );
+
+ foreach( $g->getUsers() as $id=>$name )
+ {
+ $treeElement = new TreeElement();
+
+ $u = new User( $id );
+ $u->load();
+ $treeElement->id = $u->userid;
+ $treeElement->text = $u->name;
+ $treeElement->url = Html::url('user','edit',$id,array(REQ_PARAM_TARGET=>'content'));
+ $treeElement->icon = 'user';
+ $treeElement->action = 'user';
+ $treeElement->description = $u->fullname;
+ $treeElement->target = 'cms_main';
+
+ $this->addTreeElement( $treeElement );
+ }
+ }
+}
+
+?>+
\ No newline at end of file
diff --git a/modules/util/Api.class.php b/modules/util/Api.class.php
@@ -0,0 +1,101 @@
+<?php
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+use cms\model\Folder;
+
+
+/**
+ * Service-Klasse fuer allgemeine Interface-Methoden. Insbesondere
+ * in Code-Elementen kann und soll auf diese Methoden zurueckgegriffen
+ * werden.
+ * @author $Author$
+ * @version $Revision$
+ * @package openrat.services
+ */
+class Api
+{
+ var $output = '';
+ var $objectid = 0;
+ var $page;
+
+ function db()
+ {
+ return db_connection();
+ }
+
+ function pageid()
+ {
+ echo 'WARNING: pageid() deprecated!<br>';
+ global $SESS;
+ return $SESS['objectid'];
+ }
+
+ function getObjectId()
+ {
+ return $this->objectid;
+ }
+
+ function setObjectId( $objectid )
+ {
+ $this->objectid = $objectid;
+ }
+
+ function getRootObjectId()
+ {
+ return Folder::getRootObjectId();
+ }
+
+ function folderid()
+ {
+ global $SESS;
+ return $SESS['folderid'];
+ }
+
+
+ function execute( $code )
+ {
+ global $conf_tmpdir;
+ $code = "<?php\n".$code."\n?>";
+
+ $tmp = $conf_tmpdir.'/'.md5(microtime()).'.tmp';
+ $f = fopen( $tmp,'w' );
+ fwrite( $f,$code );
+ fclose( $f );
+
+ require( $tmp ); // Ausfuehren des temporaeren PHP-Codes
+
+ unlink( $tmp );
+ $inhalt = Api::getOutput();
+ $this->output( $inhalt );
+ }
+
+ function delOutput()
+ {
+ $this->output = '';
+ }
+
+ function output( $text )
+ {
+ $this->output .= $text;
+ }
+
+
+ function getOutput()
+ {
+ return $this->output;
+ }
+}+
\ No newline at end of file
diff --git a/modules/util/ArchiveTar.class.php b/modules/util/ArchiveTar.class.php
@@ -0,0 +1,397 @@
+<?php
+/*
+=======================================================================
+Name:
+ tar Class
+
+Author:
+ Josh Barger <joshb@npt.com>
+
+Description:
+ This class reads and writes Tape-Archive (TAR) Files and Gzip
+ compressed TAR files, which are mainly used on UNIX systems.
+ This class works on both windows AND unix systems, and does
+ NOT rely on external applications!! Woohoo!
+
+Usage:
+ Copyright (C) 2002 Josh Barger
+
+ 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 at:
+ http://www.gnu.org/copyleft/lesser.html
+
+ If you use this script in your application/website, please
+ send me an e-mail letting me know about it :)
+
+Bugs:
+ Please report any bugs you might find to my e-mail address
+ at joshb@npt.com. If you have already created a fix/patch
+ for the bug, please do send it to me so I can incorporate it into my release.
+
+Version History:
+ 1.0 04/10/2002 - InitialRelease
+
+ 2.0 04/11/2002 - Merged both tarReader and tarWriter
+ classes into one
+ - Added support for gzipped tar files
+ Remember to name for .tar.gz or .tgz
+ if you use gzip compression!
+ :: THIS REQUIRES ZLIB EXTENSION ::
+ - Added additional comments to
+ functions to help users
+ - Added ability to remove files and
+ directories from archive
+ 2.1 04/12/2002 - Fixed serious bug in generating tar
+ - Created another example file
+ - Added check to make sure ZLIB is
+ installed before running GZIP
+ compression on TAR
+ 2.2 05/07/2002 - Added automatic detection of Gzipped
+ tar files (Thanks go to J�rgen Falch
+ for the idea)
+ - Changed "private" functions to have
+ special function names beginning with
+ two underscores
+=======================================================================
+*/
+
+
+class ArchiveTar
+{
+ // Unprocessed Archive Information
+ var $filename;
+ var $isGzipped;
+ var $tar_file;
+
+ // Processed Archive Information
+ var $files;
+ var $directories;
+ var $numFiles;
+ var $numDirectories;
+
+
+ // Class Constructor -- Does nothing...
+ function tar() {
+ return true;
+ }
+
+
+ // Computes the unsigned Checksum of a file's header
+ // to try to ensure valid file
+ // PRIVATE ACCESS FUNCTION
+ function __computeUnsignedChecksum($bytestring)
+ {
+ $unsigned_chksum=0;
+ for($i=0; $i<512; $i++)
+ $unsigned_chksum += ord($bytestring[$i]);
+ for($i=0; $i<8; $i++)
+ $unsigned_chksum -= ord($bytestring[148 + $i]);
+ $unsigned_chksum += ord(" ") * 8;
+
+ return $unsigned_chksum;
+ }
+
+
+ // Converts a NULL padded string to a non-NULL padded string
+ // PRIVATE ACCESS FUNCTION
+ function __parseNullPaddedString($string)
+ {
+ $position = strpos($string,chr(0));
+ return substr($string,0,$position);
+ }
+
+
+ // This function parses the current TAR file
+ // PRIVATE ACCESS FUNCTION
+ function __parseTar()
+ {
+ // Read Files from archive
+ $this->numFiles=0;
+ $tar_length = strlen($this->tar_file);
+ $main_offset = 0;
+ while($main_offset < $tar_length) {
+ // If we read a block of 512 nulls, we are at the end of the archive
+ if(substr($this->tar_file,$main_offset,512) == str_repeat(chr(0),512))
+ break;
+
+ // Parse file name
+ $file_name = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset,100));
+
+ // Parse the file mode
+ $file_mode = substr($this->tar_file,$main_offset + 100,8);
+
+ // Parse the file user ID
+ $file_uid = octdec(substr($this->tar_file,$main_offset + 108,8));
+
+ // Parse the file group ID
+ $file_gid = octdec(substr($this->tar_file,$main_offset + 116,8));
+
+ // Parse the file size
+ $file_size = octdec(substr($this->tar_file,$main_offset + 124,12));
+
+ // Parse the file update time - unix timestamp format
+ $file_time = octdec(substr($this->tar_file,$main_offset + 136,12));
+
+ // Parse Checksum
+ $file_chksum = octdec(substr($this->tar_file,$main_offset + 148,6));
+
+ // Parse user name
+ $file_uname = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset + 265,32));
+
+ // Parse Group name
+ $file_gname = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset + 297,32));
+
+ // Make sure our file is valid
+ if($this->__computeUnsignedChecksum(substr($this->tar_file,$main_offset,512)) != $file_chksum)
+ return false;
+
+ // Parse File Contents
+ $file_contents = substr($this->tar_file,$main_offset + 512,$file_size);
+
+ /* ### Unused Header Information ###
+ $activeFile["typeflag"] = substr($this->tar_file,$main_offset + 156,1);
+ $activeFile["linkname"] = substr($this->tar_file,$main_offset + 157,100);
+ $activeFile["magic"] = substr($this->tar_file,$main_offset + 257,6);
+ $activeFile["version"] = substr($this->tar_file,$main_offset + 263,2);
+ $activeFile["devmajor"] = substr($this->tar_file,$main_offset + 329,8);
+ $activeFile["devminor"] = substr($this->tar_file,$main_offset + 337,8);
+ $activeFile["prefix"] = substr($this->tar_file,$main_offset + 345,155);
+ $activeFile["endheader"] = substr($this->tar_file,$main_offset + 500,12);
+ */
+
+ if($file_size > 0) {
+ // Increment number of files
+ $this->numFiles++;
+
+ // Create us a new file in our array
+ $activeFile = &$this->files[];
+
+ // Asign Values
+ $activeFile["name"] = $file_name;
+ $activeFile["mode"] = $file_mode;
+ $activeFile["size"] = $file_size;
+ $activeFile["time"] = $file_time;
+ $activeFile["user_id"] = $file_uid;
+ $activeFile["group_id"] = $file_gid;
+ $activeFile["user_name"] = $file_uname;
+ $activeFile["group_name"] = $file_gname;
+ $activeFile["checksum"] = $file_chksum;
+ $activeFile["file"] = $file_contents;
+
+ } else {
+ // Increment number of directories
+ $this->numDirectories++;
+
+ // Create a new directory in our array
+ $activeDir = &$this->directories[];
+
+ // Assign values
+ $activeDir["name"] = $file_name;
+ $activeDir["mode"] = $file_mode;
+ $activeDir["time"] = $file_time;
+ $activeDir["user_id"] = $file_uid;
+ $activeDir["group_id"] = $file_gid;
+ $activeDir["user_name"] = $file_uname;
+ $activeDir["group_name"] = $file_gname;
+ $activeDir["checksum"] = $file_chksum;
+ }
+
+ // Move our offset the number of blocks we have processed
+ $main_offset += 512 + (ceil($file_size / 512) * 512);
+ }
+
+ return true;
+ }
+
+
+ // Read a non gzipped tar file in for processing
+ // PRIVATE ACCESS FUNCTION
+ function __readTar($filename='')
+ {
+ // Set the filename to load
+ // Read in the TAR file
+
+ if($this->tar_file[0] == chr(31) && $this->tar_file[1] == chr(139) && $this->tar_file[2] == chr(8)) {
+ if(!function_exists("gzinflate"))
+ return false;
+
+ $this->isGzipped = TRUE;
+
+ $this->tar_file = gzinflate(substr($this->tar_file,10,-4));
+ }
+
+ // Parse the TAR file
+ $this->__parseTar();
+
+ return true;
+ }
+
+
+ // Generates a TAR file from the processed data
+ // PRIVATE ACCESS FUNCTION
+ function __generateTAR()
+ {
+ // Clear any data currently in $this->tar_file
+ unset($this->tar_file);
+
+ // Generate Records for each directory, if we have directories
+ if($this->numDirectories > 0) {
+ foreach($this->directories as $key => $information) {
+ unset($header);
+
+ // Generate tar header for this directory
+ // Filename, Permissions, UID, GID, size, Time, checksum, typeflag, linkname, magic, version, user name, group name, devmajor, devminor, prefix, end
+ $header .= str_pad($information["name"],100,chr(0));
+ $header .= str_pad(decoct($information["mode"]),7,"0",STR_PAD_LEFT) . chr(0);
+ $header .= str_pad(decoct($information["user_id"]),7,"0",STR_PAD_LEFT) . chr(0);
+ $header .= str_pad(decoct($information["group_id"]),7,"0",STR_PAD_LEFT) . chr(0);
+ $header .= str_pad(decoct(0),11,"0",STR_PAD_LEFT) . chr(0);
+ $header .= str_pad(decoct($information["time"]),11,"0",STR_PAD_LEFT) . chr(0);
+ $header .= str_repeat(" ",8);
+ $header .= "5";
+ $header .= str_repeat(chr(0),100);
+ $header .= str_pad("ustar",6,chr(32));
+ $header .= chr(32) . chr(0);
+ $header .= str_pad("",32,chr(0));
+ $header .= str_pad("",32,chr(0));
+ $header .= str_repeat(chr(0),8);
+ $header .= str_repeat(chr(0),8);
+ $header .= str_repeat(chr(0),155);
+ $header .= str_repeat(chr(0),12);
+
+ // Compute header checksum
+ $checksum = str_pad(decoct($this->__computeUnsignedChecksum($header)),6,"0",STR_PAD_LEFT);
+ for($i=0; $i<6; $i++) {
+ $header[(148 + $i)] = substr($checksum,$i,1);
+ }
+ $header[154] = chr(0);
+ $header[155] = chr(32);
+
+ // Add new tar formatted data to tar file contents
+ $this->tar_file .= $header;
+ }
+ }
+
+ // Generate Records for each file, if we have files (We should...)
+ if($this->numFiles > 0)
+ {
+ foreach($this->files as $key => $information)
+ {
+ unset($header);
+ $header = '';
+
+ // Generate the TAR header for this file
+ // Filename, Permissions, UID, GID, size, Time, checksum, typeflag, linkname, magic, version, user name, group name, devmajor, devminor, prefix, end
+ $header .= str_pad($information["name"],100,chr(0));
+ $header .= str_pad(decoct($information["mode"]),7,"0",STR_PAD_LEFT) . chr(0);
+ $header .= str_pad(decoct($information["user_id"]),7,"0",STR_PAD_LEFT) . chr(0);
+ $header .= str_pad(decoct($information["group_id"]),7,"0",STR_PAD_LEFT) . chr(0);
+ $header .= str_pad(decoct($information["size"]),11,"0",STR_PAD_LEFT) . chr(0);
+ $header .= str_pad(decoct($information["time"]),11,"0",STR_PAD_LEFT) . chr(0);
+ $header .= str_repeat(" ",8);
+ $header .= "0";
+ $header .= str_repeat(chr(0),100);
+ $header .= str_pad("ustar",6,chr(32));
+ $header .= chr(32) . chr(0);
+ $header .= str_pad($information["user_name"],32,chr(0)); // How do I get a file's user name from PHP?
+ $header .= str_pad($information["group_name"],32,chr(0)); // How do I get a file's group name from PHP?
+ $header .= str_repeat(chr(0),8);
+ $header .= str_repeat(chr(0),8);
+ $header .= str_repeat(chr(0),155);
+ $header .= str_repeat(chr(0),12);
+
+ // Compute header checksum
+ $checksum = str_pad(decoct($this->__computeUnsignedChecksum($header)),6,"0",STR_PAD_LEFT);
+ for($i=0; $i<6; $i++)
+ {
+ $header[(148 + $i)] = substr($checksum,$i,1);
+ }
+ $header[154] = chr(0);
+ $header[155] = chr(32);
+
+ // Pad file contents to byte count divisible by 512
+ $file_contents = str_pad($information["file"],(ceil($information["size"] / 512) * 512),chr(0));
+
+ // Add new tar formatted data to tar file contents
+ $this->tar_file .= $header . $file_contents;
+ }
+ }
+
+ // Add 512 bytes of NULLs to designate EOF
+ $this->tar_file .= str_repeat(chr(0),512);
+
+ return true;
+ }
+
+
+ // Open a TAR file
+ function openTAR($value)
+ {
+ // Clear any values from previous tar archives
+ unset($this->filename);
+ unset($this->isGzipped);
+ unset($this->tar_file);
+ unset($this->files);
+ unset($this->directories);
+ unset($this->numFiles);
+ unset($this->numDirectories);
+
+ $this->filename = 'none';
+ $this->tar_file = $value;
+ // Parse this file
+ $this->__readTar();
+
+ return true;
+ }
+
+
+ // Write the currently loaded tar archive to disk
+ function saveTar()
+ {
+ if(!$this->filename)
+ return false;
+
+ // Write tar to current file using specified gzip compression
+ $this->toTar($this->filename,$this->isGzipped);
+
+ return true;
+ }
+
+
+ // Saves tar archive to a different file than the current file
+ function toTar($filename,$useGzip)
+ {
+ if(!$filename)
+ return false;
+
+ // Encode processed files into TAR file format
+ $this->__generateTar();
+
+ // GZ Compress the data if we need to
+ if($useGzip) {
+ // Make sure we have gzip support
+ if(!function_exists("gzencode"))
+ return false;
+
+ $file = gzencode($this->tar_file);
+ } else {
+ $file = $this->tar_file;
+ }
+
+ // Write the TAR file
+ $fp = fopen($filename,"wb");
+ fwrite($fp,$file);
+ fclose($fp);
+
+ return true;
+ }
+}
+
+?>+
\ No newline at end of file
diff --git a/modules/util/ArchiveUnzip.class.php b/modules/util/ArchiveUnzip.class.php
@@ -0,0 +1,447 @@
+<?php
+// 28/11/2005 (2.4)
+// - dUnzip2 is now compliant with wrong placed "Data Description", made by some compressors,
+// like the classes ZipLib and ZipLib2 by 'Hasin Hayder'. Thanks to Ricardo Parreno for pointing it.
+// 09/11/2005 (2.3)
+// - Added optional parameter '$stopOnFile' on method 'getList()'.
+// If given, file listing will stop when find given filename. (Useful to open and unzip an exact file)
+// 06/11/2005 (2.21)
+// - Added support to PK00 file format (Packed to Removable Disk) (thanks to Lito [PHPfileNavigator])
+// - Method 'getExtraInfo': If requested file doesn't exist, return FALSE instead of Array()
+// 31/10/2005 (2.2)
+// - Removed redundant 'file_name' on centralDirs declaration (thanks to Lito [PHPfileNavigator])
+// - Fixed redeclaration of file_put_contents when in PHP4 (not returning true)
+
+##############################################################
+# Class dUnzip2 v2.4
+#
+# Author: Alexandre Tedeschi (d)
+# E-Mail: alexandrebr at gmail dot com
+# Londrina - PR / Brazil
+#
+# Objective:
+# This class allows programmer to easily unzip files on the fly.
+#
+# Requirements:
+# This class requires extension ZLib Enabled. It is default
+# for most site hosts around the world, and for the PHP Win32 dist.
+#
+# To do:
+# * Error handling
+# * Write a PHP-Side gzinflate, to completely avoid any external extensions
+# * Write other decompress algorithms
+#
+# If you modify this class, or have any ideas to improve it, please contact me!
+# You are allowed to redistribute this class, if you keep my name and contact e-mail on it.
+##############################################################
+
+class ArchiveUnzip{
+
+ // Public
+ var $files = array();
+ var $value = '';
+ var $fileName;
+ var $compressedList; // You will problably use only this one!
+ var $centralDirList; // Central dir list... It's a kind of 'extra attributes' for a set of files
+ var $endOfCentral; // End of central dir, contains ZIP Comments
+ var $debug;
+
+ // Private
+ var $fh;
+ var $zipSignature = "\x50\x4b\x03\x04"; // local file header signature
+ var $dirSignature = "\x50\x4b\x01\x02"; // central dir header signature
+ var $dirSignatureE= "\x50\x4b\x05\x06"; // end of central dir signature
+
+ // Public
+ Function ArchiveUnzip()
+ {
+ $this->compressedList =
+ $this->centralDirList =
+ $this->endOfCentral = Array();
+ }
+
+ function open( $value )
+ {
+ $this->fileName = tempnam('/tmp','unzip');
+// echo $this->fileName;
+ $fo = fopen( $this->fileName,'w');
+ fwrite($fo,$value);
+ $this->unzipAll();
+ }
+
+
+ Function getList($stopOnFile=false){
+ if(sizeof($this->compressedList)){
+ $this->debugMsg(1, "Returning already loaded file list.");
+ return $this->compressedList;
+ }
+
+ // Open file, and set file handler
+ $fh = fopen($this->fileName, "r");
+ $this->fh = &$fh;
+ if(!$fh){
+ $this->debugMsg(2, "Failed to load file.");
+ return false;
+ }
+
+ // Loop the file, looking for files and folders
+ $ddTry = false;
+ fseek($fh, 0);
+ for(;;){
+ // Check if the signature is valid...
+ $signature = fread($fh, 4);
+ if(feof($fh)){
+# $this->debugMsg(1, "Reached end of file");
+ break;
+ }
+
+ // If signature is a 'Packed to Removable Disk', just ignore it and move to the next.
+ if($signature == 'PK00'){
+ $this->debugMsg(1, "Found PK00: Packed to Removable Disk");
+ continue;
+ }
+
+ // If signature of a 'Local File Header'
+ if($signature == $this->zipSignature){
+ # $this->debugMsg(1, "Zip Signature!");
+
+ // Get information about the zipped file
+ $file['version_needed'] = unpack("v", fread($fh, 2)); // version needed to extract
+ $file['general_bit_flag'] = unpack("v", fread($fh, 2)); // general purpose bit flag
+ $file['compression_method'] = unpack("v", fread($fh, 2)); // compression method
+ $file['lastmod_time'] = unpack("v", fread($fh, 2)); // last mod file time
+ $file['lastmod_date'] = unpack("v", fread($fh, 2)); // last mod file date
+ $file['crc-32'] = fread($fh, 4); // crc-32
+ $file['compressed_size'] = unpack("V", fread($fh, 4)); // compressed size
+ $file['uncompressed_size'] = unpack("V", fread($fh, 4)); // uncompressed size
+ $fileNameLength = unpack("v", fread($fh, 2)); // filename length
+ $extraFieldLength = unpack("v", fread($fh, 2)); // extra field length
+ $file['file_name'] = fread($fh, $fileNameLength[1]); // filename
+ $file['extra_field'] = $extraFieldLength[1]?fread($fh, $extraFieldLength[1]):''; // extra field
+ $file['contents-startOffset']= ftell($fh);
+
+ // Bypass the whole compressed contents, and look for the next file
+ fseek($fh, $file['compressed_size'][1], SEEK_CUR);
+
+ // Convert the date and time, from MS-DOS format to UNIX Timestamp
+ $BINlastmod_date = str_pad(decbin($file['lastmod_date'][1]), 16, '0', STR_PAD_LEFT);
+ $BINlastmod_time = str_pad(decbin($file['lastmod_time'][1]), 16, '0', STR_PAD_LEFT);
+ $lastmod_dateY = bindec(substr($BINlastmod_date, 0, 7))+1980;
+ $lastmod_dateM = bindec(substr($BINlastmod_date, 7, 4));
+ $lastmod_dateD = bindec(substr($BINlastmod_date, 11, 5));
+ $lastmod_timeH = bindec(substr($BINlastmod_time, 0, 5));
+ $lastmod_timeM = bindec(substr($BINlastmod_time, 5, 6));
+ $lastmod_timeS = bindec(substr($BINlastmod_time, 11, 5));
+
+ // Mount file table
+ $this->compressedList[$file['file_name']] = Array(
+ 'file_name' =>$file['file_name'],
+ 'compression_method'=>$file['compression_method'][1],
+ 'version_needed' =>$file['version_needed'][1],
+ 'lastmod_datetime' =>mktime($lastmod_timeH, $lastmod_timeM, $lastmod_timeS, $lastmod_dateM, $lastmod_dateD, $lastmod_dateY),
+ 'crc-32' =>str_pad(dechex(ord($file['crc-32'][3])), 2, '0', STR_PAD_LEFT).
+ str_pad(dechex(ord($file['crc-32'][2])), 2, '0', STR_PAD_LEFT).
+ str_pad(dechex(ord($file['crc-32'][1])), 2, '0', STR_PAD_LEFT).
+ str_pad(dechex(ord($file['crc-32'][0])), 2, '0', STR_PAD_LEFT),
+ 'compressed_size' =>$file['compressed_size'][1],
+ 'uncompressed_size' =>$file['uncompressed_size'][1],
+ 'extra_field' =>$file['extra_field'],
+ 'general_bit_flag' =>str_pad(decbin($file['general_bit_flag'][1]), 8, '0', STR_PAD_LEFT),
+ 'contents-startOffset'=>$file['contents-startOffset']
+ );
+
+ if($stopOnFile) if($file['file_name'] == $stopOnFile){
+ $this->debugMsg(1, "Stopping on file...");
+ break;
+ }
+ }
+
+ // If signature of a 'Central Directory Structure'
+ elseif($signature == $this->dirSignature){
+ # $this->debugMsg(1, "Dir Signature!");
+
+ $dir['version_madeby'] = unpack("v", fread($fh, 2)); // version made by
+ $dir['version_needed'] = unpack("v", fread($fh, 2)); // version needed to extract
+ $dir['general_bit_flag'] = unpack("v", fread($fh, 2)); // general purpose bit flag
+ $dir['compression_method'] = unpack("v", fread($fh, 2)); // compression method
+ $dir['lastmod_time'] = unpack("v", fread($fh, 2)); // last mod file time
+ $dir['lastmod_date'] = unpack("v", fread($fh, 2)); // last mod file date
+ $dir['crc-32'] = fread($fh, 4); // crc-32
+ $dir['compressed_size'] = unpack("V", fread($fh, 4)); // compressed size
+ $dir['uncompressed_size'] = unpack("V", fread($fh, 4)); // uncompressed size
+ $fileNameLength = unpack("v", fread($fh, 2)); // filename length
+ $extraFieldLength = unpack("v", fread($fh, 2)); // extra field length
+ $fileCommentLength = unpack("v", fread($fh, 2)); // file comment length
+ $dir['disk_number_start'] = unpack("v", fread($fh, 2)); // disk number start
+ $dir['internal_attributes'] = unpack("v", fread($fh, 2)); // internal file attributes-byte1
+ $dir['external_attributes1']= unpack("v", fread($fh, 2)); // external file attributes-byte2
+ $dir['external_attributes2']= unpack("v", fread($fh, 2)); // external file attributes
+ $dir['relative_offset'] = unpack("V", fread($fh, 4)); // relative offset of local header
+ $dir['file_name'] = fread($fh, $fileNameLength[1]); // filename
+ $dir['extra_field'] = $extraFieldLength[1] ?fread($fh, $extraFieldLength[1]) :''; // extra field
+ $dir['file_comment'] = $fileCommentLength[1]?fread($fh, $fileCommentLength[1]):''; // file comment
+
+ // Convert the date and time, from MS-DOS format to UNIX Timestamp
+ $BINlastmod_date = str_pad(decbin($file['lastmod_date'][1]), 16, '0', STR_PAD_LEFT);
+ $BINlastmod_time = str_pad(decbin($file['lastmod_time'][1]), 16, '0', STR_PAD_LEFT);
+ $lastmod_dateY = bindec(substr($BINlastmod_date, 0, 7))+1980;
+ $lastmod_dateM = bindec(substr($BINlastmod_date, 7, 4));
+ $lastmod_dateD = bindec(substr($BINlastmod_date, 11, 5));
+ $lastmod_timeH = bindec(substr($BINlastmod_time, 0, 5));
+ $lastmod_timeM = bindec(substr($BINlastmod_time, 5, 6));
+ $lastmod_timeS = bindec(substr($BINlastmod_time, 11, 5));
+
+ $this->centralDirList[$dir['file_name']] = Array(
+ 'version_madeby'=>$dir['version_madeby'][1],
+ 'version_needed'=>$dir['version_needed'][1],
+ 'general_bit_flag'=>str_pad(decbin($file['general_bit_flag'][1]), 8, '0', STR_PAD_LEFT),
+ 'compression_method'=>$dir['compression_method'][1],
+ 'lastmod_datetime' =>mktime($lastmod_timeH, $lastmod_timeM, $lastmod_timeS, $lastmod_dateM, $lastmod_dateD, $lastmod_dateY),
+ 'crc-32' =>str_pad(dechex(ord($file['crc-32'][3])), 2, '0', STR_PAD_LEFT).
+ str_pad(dechex(ord($file['crc-32'][2])), 2, '0', STR_PAD_LEFT).
+ str_pad(dechex(ord($file['crc-32'][1])), 2, '0', STR_PAD_LEFT).
+ str_pad(dechex(ord($file['crc-32'][0])), 2, '0', STR_PAD_LEFT),
+ 'compressed_size'=>$dir['compressed_size'][1],
+ 'uncompressed_size'=>$dir['uncompressed_size'][1],
+ 'disk_number_start'=>$dir['disk_number_start'][1],
+ 'internal_attributes'=>$dir['internal_attributes'][1],
+ 'external_attributes1'=>$dir['external_attributes1'][1],
+ 'external_attributes2'=>$dir['external_attributes2'][1],
+ 'relative_offset'=>$dir['relative_offset'][1],
+ 'file_name'=>$dir['file_name'],
+ 'extra_field'=>$dir['extra_field'],
+ 'file_comment'=>$dir['file_comment'],
+ );
+ }
+
+ elseif($signature == $this->dirSignatureE){
+ # $this->debugMsg(1, "EOF Dir Signature!");
+
+ $eodir['disk_number_this'] = unpack("v", fread($fh, 2)); // number of this disk
+ $eodir['disk_number'] = unpack("v", fread($fh, 2)); // number of the disk with the start of the central directory
+ $eodir['total_entries_this'] = unpack("v", fread($fh, 2)); // total number of entries in the central dir on this disk
+ $eodir['total_entries'] = unpack("v", fread($fh, 2)); // total number of entries in
+ $eodir['size_of_cd'] = unpack("V", fread($fh, 4)); // size of the central directory
+ $eodir['offset_start_cd'] = unpack("V", fread($fh, 4)); // offset of start of central directory with respect to the starting disk number
+ $zipFileCommentLenght = unpack("v", fread($fh, 2)); // zipfile comment length
+ $eodir['zipfile_comment'] = $zipFileCommentLenght[1]?fread($fh, $zipFileCommentLenght[1]):''; // zipfile comment
+ $this->endOfCentral = Array(
+ 'disk_number_this'=>$eodir['disk_number_this'][1],
+ 'disk_number'=>$eodir['disk_number'][1],
+ 'total_entries_this'=>$eodir['total_entries_this'][1],
+ 'total_entries'=>$eodir['total_entries'][1],
+ 'size_of_cd'=>$eodir['size_of_cd'][1],
+ 'offset_start_cd'=>$eodir['offset_start_cd'][1],
+ 'zipfile_comment'=>$eodir['zipfile_comment'],
+ );
+ }
+ else{
+ if(!$ddTry){
+ $this->debugMsg(1, "Unexpected header. Trying to detect wrong placed 'Data Descriptor'...\n");
+ $ddTry = true;
+ fseek($fh, 12-4, SEEK_CUR); // Jump over 'crc-32'(4) 'compressed-size'(4), 'uncompressed-size'(4)
+ continue;
+ }
+ $this->debugMsg(1, "Unexpected header, ending loop at offset ".ftell($fh));
+ break;
+ }
+ $ddTry = false;
+ }
+
+ if($this->debug){
+ #------- Debug compressedList
+ $kkk = 0;
+ echo "<table border='0' style='font: 11px Verdana; border: 1px solid #000'>";
+ foreach($this->compressedList as $fileName=>$item){
+ if(!$kkk && $kkk=1){
+ echo "<tr style='background: #ADA'>";
+ foreach($item as $fieldName=>$value)
+ echo "<td>$fieldName</td>";
+ echo '</tr>';
+ }
+ echo "<tr style='background: #CFC'>";
+ foreach($item as $fieldName=>$value){
+ if($fieldName == 'lastmod_datetime')
+ echo "<td title='$fieldName' nowrap='nowrap'>".date("d/m/Y H:i:s", $value)."</td>";
+ else
+ echo "<td title='$fieldName' nowrap='nowrap'>$value</td>";
+ }
+ echo "</tr>";
+ }
+ echo "</table>";
+
+ #------- Debug centralDirList
+ $kkk = 0;
+ if(sizeof($this->centralDirList)){
+ echo "<table border='0' style='font: 11px Verdana; border: 1px solid #000'>";
+ foreach($this->centralDirList as $fileName=>$item){
+ if(!$kkk && $kkk=1){
+ echo "<tr style='background: #AAD'>";
+ foreach($item as $fieldName=>$value)
+ echo "<td>$fieldName</td>";
+ echo '</tr>';
+ }
+ echo "<tr style='background: #CCF'>";
+ foreach($item as $fieldName=>$value){
+ if($fieldName == 'lastmod_datetime')
+ echo "<td title='$fieldName' nowrap='nowrap'>".date("d/m/Y H:i:s", $value)."</td>";
+ else
+ echo "<td title='$fieldName' nowrap='nowrap'>$value</td>";
+ }
+ echo "</tr>";
+ }
+ echo "</table>";
+ }
+
+ #------- Debug endOfCentral
+ $kkk = 0;
+ if(sizeof($this->endOfCentral)){
+ echo "<table border='0' style='font: 11px Verdana' style='border: 1px solid #000'>";
+ echo "<tr style='background: #DAA'><td colspan='2'>dUnzip - End of file</td></tr>";
+ foreach($this->endOfCentral as $field=>$value){
+ echo "<tr>";
+ echo "<td style='background: #FCC'>$field</td>";
+ echo "<td style='background: #FDD'>$value</td>";
+ echo "</tr>";
+ }
+ echo "</table>";
+ }
+ }
+
+ return $this->compressedList;
+ }
+
+
+ Function getExtraInfo($compressedFileName)
+ {
+ return
+ isset($this->centralDirList[$compressedFileName])?
+ $this->centralDirList[$compressedFileName]:
+ false;
+ }
+
+
+ Function getZipInfo($detail=false)
+ {
+ return $detail?
+ $this->endOfCentral[$detail]:
+ $this->endOfCentral;
+ }
+
+
+ Function unzip($compressedFileName, $targetFileName=false){
+ $fdetails = &$this->compressedList[$compressedFileName];
+
+ if(!sizeof($this->compressedList)){
+ $this->debugMsg(1, "Trying to unzip before loading file list... Loading it!");
+ $this->getList(false, $compressedFileName);
+ }
+ if(!isset($this->compressedList[$compressedFileName])){
+ $this->debugMsg(2, "File '<b>$compressedFileName</b>' is not compressed in the zip.");
+ return false;
+ }
+ if(substr($compressedFileName, -1) == "/"){
+ $this->debugMsg(2, "Trying to unzip a folder name '<b>$compressedFileName</b>'.");
+ return false;
+ }
+ if(!$fdetails['uncompressed_size']){
+ $this->debugMsg(1, "File '<b>$compressedFileName</b>' is empty.");
+ return "";
+ }
+
+ fseek($this->fh, $fdetails['contents-startOffset']);
+ return $this->uncompress(
+ fread($this->fh, $fdetails['compressed_size']),
+ $fdetails['compression_method'],
+ $fdetails['uncompressed_size'] );
+ }
+
+
+ Function unzipAll($targetDir=false, $baseDir="", $maintainStructure=true, $chmod=false){
+ if($targetDir === false)
+ $targetDir = dirname(__FILE__)."/";
+
+ $lista = $this->getList();
+ if(sizeof($lista)) foreach($lista as $fileName=>$trash){
+ $dirname = dirname($fileName);
+ $outDN = "$targetDir/$dirname";
+
+ if(substr($dirname, 0, strlen($baseDir)) != $baseDir)
+ continue;
+
+ if(!is_dir($outDN) && $maintainStructure){
+ $str = "";
+ $folders = explode("/", $dirname);
+ foreach($folders as $folder){
+ $str = $str?"$str/$folder":$folder;
+ if(!is_dir("$targetDir/$str")){
+ $this->debugMsg(1, "Creating folder: $targetDir/$str");
+ mkdir("$targetDir/$str");
+ if($chmod)
+ chmod("$targetDir/$str", $chmod);
+ }
+ }
+ }
+ if(substr($fileName, -1, 1) == "/")
+ continue;
+
+ $maintainStructure?
+ $this->unzip($fileName, "$targetDir/$fileName"):
+ $this->unzip($fileName, "$targetDir/".basename($fileName));
+
+ if($chmod)
+ chmod($maintainStructure?"$targetDir/$fileName":"$targetDir/".basename($fileName), $chmod);
+ }
+ }
+
+ Function close(){ // Free the file resource
+ if($this->fh)
+ fclose($this->fh);
+ }
+
+ // Private (you should NOT call these methods):
+ Function uncompress($content, $mode, $uncompressedSize, $targetFileName=false){
+ switch($mode){
+ case 0:
+ // Not compressed
+ return $content;
+ case 1:
+ $this->debugMsg(2, "Shrunk mode is not supported... yet?");
+ return false;
+ case 2:
+ case 3:
+ case 4:
+ case 5:
+ $this->debugMsg(2, "Compression factor ".($mode-1)." is not supported... yet?");
+ return false;
+ case 6:
+ $this->debugMsg(2, "Implode is not supported... yet?");
+ return false;
+ case 7:
+ $this->debugMsg(2, "Tokenizing compression algorithm is not supported... yet?");
+ return false;
+ case 8:
+ // Deflate
+ return gzinflate($content, $uncompressedSize);
+ case 9:
+ $this->debugMsg(2, "Enhanced Deflating is not supported... yet?");
+ return false;
+ case 10:
+ $this->debugMsg(2, "PKWARE Date Compression Library Impoloding is not supported... yet?");
+ return false;
+ default:
+ $this->debugMsg(2, "Unknown uncompress method: $mode");
+ return false;
+ }
+ }
+
+
+ Function debugMsg($level, $string){
+ if($this->debug)
+ if($level == 1)
+ echo "<b style='color: #777'>dUnzip2:</b> $string<br>";
+ if($level == 2)
+ echo "<b style='color: #F00'>dUnzip2:</b> $string<br>";
+ }
+}
+?>+
\ No newline at end of file
diff --git a/modules/util/ArchiveZip.class.php b/modules/util/ArchiveZip.class.php
@@ -0,0 +1,89 @@
+<?php
+
+
+/**
+ * This source is taken from http://www.zend.com/zend/spotlight/creating-zip-files1.php
+ * Thank you!
+ */
+class ArchiveZip
+{
+ var $datasec = array();
+ var $ctrl_dir = array();
+ var $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00";
+ var $old_offset = 0;
+
+
+ function add_file($data, $name)
+ {
+ $name = str_replace("\\", "/", $name);
+ $unc_len = strlen($data);
+ $crc = crc32($data);
+ $zdata = gzcompress($data);
+ $zdate = substr ($zdata, 2, -4);
+ $c_len = strlen($zdata);
+
+
+
+ $fr = "\x50\x4b\x03\x04";
+ $fr .= "\x14\x00";
+ $fr .= "\x00\x00";
+ $fr .= "\x08\x00";
+ $fr .= "\x00\x00\x00\x00";
+ $fr .= pack("V",$crc);
+ $fr .= pack("V",$c_len);
+ $fr .= pack("V",$unc_len);
+ $fr .= pack("v", strlen($name) );
+ $fr .= pack("v", 0 );
+ $fr .= $name;
+ $fr .= $zdata;
+ $fr .= pack("V",$crc);
+ $fr .= pack("V",$c_len);
+ $fr .= pack("V",$unc_len);
+
+ $this -> datasec[] = $fr;
+
+
+
+ $new_offset = strlen(implode("", $this->datasec));
+
+ $cdrec = "\x50\x4b\x01\x02";
+ $cdrec .="\x00\x00";
+ $cdrec .="\x14\x00";
+ $cdrec .="\x00\x00";
+ $cdrec .="\x08\x00";
+ $cdrec .="\x00\x00\x00\x00";
+ $cdrec .= pack("V",$crc);
+ $cdrec .= pack("V",$c_len);
+ $cdrec .= pack("V",$unc_len);
+ $cdrec .= pack("v", strlen($name) );
+ $cdrec .= pack("v", 0 );
+ $cdrec .= pack("v", 0 );
+ $cdrec .= pack("v", 0 );
+ $cdrec .= pack("v", 0 );
+ $cdrec .= pack("V", 32 );
+ $cdrec .= pack("V", $this -> old_offset );
+
+ $this -> old_offset = $new_offset;
+
+ $cdrec .= $name;
+ $this -> ctrl_dir[] = $cdrec;
+ }
+
+
+ function file() {
+ $data = implode("", $this -> datasec);
+ $ctrldir = implode("", $this -> ctrl_dir);
+
+ return
+ $data.
+ $ctrldir.
+ $this -> eof_ctrl_dir.
+ pack("v", sizeof($this -> ctrl_dir)).
+ pack("v", sizeof($this -> ctrl_dir)).
+ pack("V", strlen($ctrldir)).
+ pack("V", strlen($data)).
+ "\x00\x00";
+ }
+}
+
+?>+
\ No newline at end of file
diff --git a/modules/util/Code.class.php b/modules/util/Code.class.php
@@ -0,0 +1,44 @@
+<?php
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+/**
+ * @author $Author$
+ * @version $Revision$
+ * @package openrat.services
+ */
+class Code extends Macro
+{
+ var $code = '';
+
+ function execute()
+ {
+ if ( substr($this->code,0,5) != '<?php' )
+ $this->code = "<?php\n".$this->code."\n?>";
+
+ $tmp = FileUtils::getTempDir().'/openratMacro';
+ $tmp .= '.code.php.tmp';
+
+ $f = fopen( $tmp,'w' );
+ fwrite( $f,$this->code );
+ fclose( $f );
+
+ require( $tmp ); // Ausfuehren des temporaeren PHP-Codes
+
+ unlink( $tmp );
+ }
+}+
\ No newline at end of file
diff --git a/modules/util/Dynamic.class.php b/modules/util/Dynamic.class.php
@@ -0,0 +1,34 @@
+<?php
+// OpenRat Content Management System
+// Copyright (C) 2002-2013 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+/**
+ * Oberklasse für dynamische Klassen ("Makros").
+ *
+ * Diese Klasse dient nur der Abwärtskompabilität. Neue dynamische Klassen
+ * (ab jetzt "Makros") sollten von der Klasse Macro erben.
+ *
+ * @author Jan Dankert
+ * @package openrat.services
+ */
+class Dynamic extends Macro
+{
+ // Keine weitere Implementierung
+}
+
+?>+
\ No newline at end of file
diff --git a/modules/util/FileUtils.class.php b/modules/util/FileUtils.class.php
@@ -0,0 +1,102 @@
+<?php
+
+/**
+ * Werkzeugklasse f�r Datei-Operationen.
+ *
+ */
+class FileUtils
+{
+ /**
+ * Fuegt einen Slash ("/") an das Ende an, sofern nicht bereits vorhanden.
+ *
+ * @param String $pfad
+ * @return Pfad mit angeh�ngtem Slash.
+ */
+ public static function slashify($pfad)
+ {
+ if ( substr($pfad,-1,1) == '/')
+ return $pfad;
+ else
+ return $pfad.'/';
+ }
+
+
+
+ /**
+ * Liefert einen Verzeichnisnamen fuer temporaere Dateien.
+ */
+ public static function createTempFile()
+ {
+ global $conf;
+ $tmpdir = @$conf['cache']['tmp_dir'];
+ $tmpfile = @tempnam( $tmpdir,'openrat_tmp' );
+
+ // 2. Versuch: Temp-Dir aus "upload_tmp_dir".
+ if ( $tmpfile === FALSE )
+ {
+ $tmpdir = ini_get('upload_tmp_dir');
+ $tmpfile = @tempnam( $tmpdir,'openrat_tmp' );
+ }
+
+ elseif ( $tmpfile === FALSE )
+ {
+ $tmpfile = @tempnam( '','openrat_tmp' );
+ }
+
+ return $tmpfile;
+ }
+
+
+ /**
+ * Liefert einen Verzeichnisnamen fuer temporaere Dateien.
+ */
+ public static function getTempDir()
+ {
+ $tmpfile = FileUtils::createTempFile();
+
+ $tmpdir = dirname($tmpfile);
+ @unlink($tmpfile);
+
+ return FileUtils::slashify( $tmpdir );
+ }
+
+
+
+ /**
+ * Liest die Dateien aus dem angegebenen Ordner in ein Array.
+ *
+ * @param $dir Verzeichnis, welches gelesen werden soll
+ * @return Array Liste der Dateien im Ordner
+ */
+ public static function readDir($dir)
+ {
+ $dir = FileUtils::slashify($dir);
+ $dateien = array();
+
+ if ( !is_dir($dir) )
+ {
+ return false;
+ }
+
+ if ( $dh = opendir($dir) )
+ {
+ while( ($verzEintrag = readdir($dh)) !== false )
+ {
+ if ( substr($verzEintrag,0,1) != '.' )
+ {
+ $dateien[] = $verzEintrag;
+ }
+ }
+ closedir($dh);
+
+ return $dateien;
+ }
+ else
+ {
+ die('unable to open directory: '.$dir);
+ }
+
+ }
+}
+
+?>+
\ No newline at end of file
diff --git a/modules/util/Ftp.class.php b/modules/util/Ftp.class.php
@@ -0,0 +1,246 @@
+<?php
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+/**
+ * Darstellen einer FTP-Verbindung, das beinhaltet
+ * das Login, das Kopieren von Dateien sowie praktische
+ * FTP-Funktionen
+ *
+ * @author $Author$
+ * @version $Revision$
+ * @package openrat.services
+ */
+class Ftp
+{
+ var $verb;
+ var $url;
+ var $log = array();
+
+ var $passive = false;
+
+ var $ok = true;
+
+ private $path;
+
+
+ // Konstruktor
+ function __construct( $url )
+ {
+ $this->connect( $url );
+ }
+
+
+ // Aufbauen der Verbindung
+ function connect( $url )
+ {
+ $this->url = $url;
+
+ global $conf;
+
+ $conf_ftp = $conf['publish']['ftp'];
+ $ftp = parse_url( $this->url );
+
+ // Die projektspezifischen Werte gewinnen bei �berschneidungen mit den Default-Werten
+ $ftp = array_merge($conf_ftp,$ftp);
+
+ // Nur FTP und FTPS (seit PHP 4.3) erlaubt
+ if ( !in_array(@$ftp['scheme'],array('ftp','ftps')) )
+ {
+ $this->log[] = 'Unknown scheme in FTP Url: '.@$ftp['scheme'];
+ $this->log[] = 'Only FTP (and FTPS, if compiled in) are supported';
+ $this->ok = false;
+ return;
+ }
+
+ if ( function_exists('ftp_ssl_connect') && $ftp['scheme'] == 'ftps' )
+ $this->verb = @ftp_ssl_connect( $ftp['host'],$ftp['port'] );
+ else
+ $this->verb = @ftp_connect( $ftp['host'],$ftp['port'] );
+
+ if ( !$this->verb )
+ {
+ $this->log[] = 'Cannot connect to '.$ftp['scheme'].'-server: '.$ftp['host'].':'.$ftp['port'];
+ $this->ok = false;
+
+ Logger::error('Cannot connect to '.$ftp['host'].':'.$ftp['port']);
+ return;
+ }
+
+ $this->log[] = 'Connected to FTP server '.$ftp['host'].':'.$ftp['port'];
+
+ if ( empty($ftp['user']) )
+ {
+ $ftp['user'] = 'anonymous';
+ $ftp['pass'] = 'openrat@openrat.de';
+ }
+
+ if ( ! ftp_login( $this->verb,$ftp['user'],$ftp['pass'] ) )
+ {
+ $this->log[] = 'Unable to login as user '.$ftp['user'];
+ $this->ok = false;
+ return;
+ }
+
+ $this->log[] = 'Logged in as user '.$ftp['user'];
+
+ $pasv = (!empty($ftp['fragment']) && $ftp['fragment'] == 'passive' );
+
+ $this->log[] = 'entering passive mode '.($pasv?'on':'off');
+ if ( ! ftp_pasv($this->verb,true) )
+ {
+ $this->log[] = 'cannot switch PASV mode';
+ $this->ok = false;
+ return;
+ }
+
+ if ( !empty($ftp['query']) )
+ {
+ parse_str( $ftp['query'],$ftp_var );
+
+ if ( isset( $ftp_var['site'] ) )
+ {
+ $site_commands = explode( ',',$ftp_var['site'] );
+ foreach( $site_commands as $cmd )
+ {
+ $this->log .= 'executing SITE command: '.$cmd;
+
+ if ( ! @ftp_site( $this->verb,$cmd ) )
+ {
+ $this->log[] = 'unable to do SITE command: '.$cmd;
+ $this->ok = false;
+ return;
+ }
+ }
+ }
+ }
+
+ $this->path = rtrim( $ftp['path'],'/' );
+
+ $this->log[] = 'Changing directory to '.$this->path;
+
+ if ( ! @ftp_chdir( $this->verb,$this->path ) )
+ {
+ $this->log[] = 'unable CHDIR to directory: '.$this->path;
+ $this->ok = false;
+ return;
+ }
+ }
+
+
+ /**
+ * Kopieren einer Datei vom lokalen System auf den FTP-Server.
+ *
+ * @param String Quelle
+ * @param String Ziel
+ * @param int FTP-Mode (BINARY oder ASCII)
+ */
+ function put( $source,$dest )
+ {
+ if ( ! $this->ok )
+ return;
+
+ $ftp = parse_url( $this->url );
+
+ $dest = $this->path.'/'.$dest;
+
+ $this->log .= "Copying file: $source -> $dest ...\n";
+
+ $mode = FTP_BINARY;
+ $p = strrpos( basename($dest),'.' ); // Letzten Punkt suchen
+
+ if ($p!==false) // Wennn letzten Punkt gefunden, dann dort aufteilen
+ {
+ $extension = substr( basename($dest),$p+1 );
+ $type = config('mime-types',$extension);
+ if ( substr($type,0,5) == 'text/')
+ $mode = FTP_ASCII;
+ }
+
+ Logger::debug("FTP PUT target:$dest mode:".(($mode==FTP_ASCII)?'ascii':'binary'));
+
+ if ( !@ftp_put( $this->verb,$dest,$source,$mode ) )
+ {
+ if ( !$this->mkdirs( dirname($dest) ) )
+ return; // Fehler.
+
+ ftp_chdir( $this->verb,$this->path );
+
+ if ( ! @ftp_put( $this->verb,$dest,$source,$mode ) )
+ {
+ $this->ok = false;
+ $this->log[] = 'FTP PUT failed...';
+ $this->log[] = 'source : '.$source;
+ $this->log[] = 'destination: '.$dest;
+ return;
+ }
+
+ }
+ }
+
+
+
+ /**
+ * Private Methode zum rekursiven Anlegen von Verzeichnissen.
+ *
+ * @param String Pfad
+ * @return boolean true, wenn ok
+ */
+ function mkdirs( $strPath )
+ {
+ if ( @ftp_chdir($this->verb,$strPath) )
+ return true; // Verzeichnis existiert schon :)
+
+ $pStrPath = dirname($strPath);
+
+ if ( !$this->mkdirs($pStrPath) )
+ return false;
+
+ if ( ! @ftp_mkdir($this->verb,$strPath) )
+ {
+ $this->ok = false;
+ $this->log[] = "failed to create remote directory: $strPath";
+ }
+
+ return $this->ok;
+ }
+
+
+
+ /**
+ * Schlie�en der FTP-Verbindung.<br>
+ * Sollte unbedingt aufgerufen werden, damit keine unn�tigen Sockets aufbleiben.
+ */
+ function close()
+ {
+ if ( !$this->ok ) // Noch alles ok?
+ return;
+
+ if ( ! @ftp_quit( $this->verb ) )
+ {
+ // Das Schlie�en der Verbindung hat nicht funktioniert.
+ // Eigentlich k�nnten wir das ignorieren, aber wir sind anst�ndig und melden eine Fehler.
+ $this->log[] = 'failed to close connection';
+ $this->ok = false;
+ return;
+ }
+ }
+}
+
+
+?>+
\ No newline at end of file
diff --git a/modules/util/GlobalFunctions.class.php b/modules/util/GlobalFunctions.class.php
@@ -0,0 +1,71 @@
+<?php
+
+/**
+ * Bereitstellen von globalen Funktionen
+ * @author $Author$
+ * @version $Revision$
+ * @package openrat.services
+ */
+class GlobalFunctions
+{
+ public static function getIsoCodes()
+ {
+ global $conf_php;
+
+ $iso = parse_ini_file( './language/lang.ini.'.$conf_php );
+ asort( $iso );
+ return $iso;
+ }
+
+
+ public static function lang( $text )
+ {
+ global $SESS;
+ $text = strtoupper($text);
+
+ if ( isset( $SESS['lang'][$text] ) )
+ {
+ return $SESS['lang'][$text];
+ }
+ else
+ {
+ return( '?'.$text.'?' );
+ }
+ }
+
+
+ # Spracheinstellungen laden
+
+ public static function language_from_http()
+ {
+ global $SESS,
+ $HTTP_SERVER_VARS,
+ $conf_php,
+ $conf;
+
+ $languages = $HTTP_SERVER_VARS['HTTP_ACCEPT_LANGUAGE'];
+ $languages = explode(',',$languages);
+ foreach( $languages as $l )
+ {
+ $l = substr($l,0,2);
+ if ( file_exists("./language/$l.ini.$conf_php") )
+ return( $l );
+ }
+
+ // Keine passende Sprache im HTTP-Header gefunden
+ return $conf['global']['default_language'];
+ }
+
+
+ public static function language_read( $l='' )
+ {
+ global $SESS,
+ $HTTP_SERVER_VARS,
+ $conf_php;
+
+ $l = language_from_http();
+ $SESS['lang'] = parse_ini_file( "./language/$l.ini.$conf_php" );
+ }
+}
+
+?>+
\ No newline at end of file
diff --git a/modules/util/Html.class.php b/modules/util/Html.class.php
@@ -0,0 +1,178 @@
+<?php
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+/**
+ * Bereitstellen von Methoden fuer die Darstellung von HTML-Elementen
+ *
+ * @author $Author$
+ * @version $Revision$
+ * @package openrat.services
+ */
+class Html
+{
+ public static function error( $field )
+ {
+ global $inputErrors;
+
+ if ( isset($inputErrors[$field]) )
+ return '<span class="error">'.lang($inputErrors[$field]).'</span';
+ }
+
+
+
+ /**
+ * Ausgabe eines Variablenwertes.<br>
+ */
+ public static function debug( $wert, $text='' )
+ {
+ echo "<strong>DEBUG: $text (".gettype($wert).")</strong><br/>";
+ echo "<pre>";
+ print_r($wert);
+ echo "</pre>";
+ }
+
+
+ /**
+ * Erzeugt eine relative Url innerhalb von Openrat
+ *
+ * @param string Aktion, die aufgerufen werden soll
+ * @param string Unteraktion, die innerhalb der Aktion aufgerufen werden soll
+ * @param int Id fuer diesen Aufruf
+ * @param array Weitere beliebige Parameter
+ */
+ public static function url( $action,$subaction='',$id='',$params=array() )
+ {
+ if ( intval($id)==0 )
+ $id='-';
+
+ global $conf;
+
+ if ( is_array($action) )
+ {
+ $params = $action;
+
+ if ( isset($params['callAction']) )
+ {
+ $params['subaction'] = $params['callAction'];
+ unset( $params['callAction'] );
+ unset( $params['callSubaction'] );
+ }
+
+
+ if ( !isset($params['action' ])) $params['action' ] = '';
+ if ( !isset($params['subaction'])) $params['subaction'] = '';
+ if ( !isset($params['id' ])) $params['id' ] = '';
+ $action = $params['action' ];
+ $subaction = $params['subaction'];
+ $id = $params['id' ];
+ unset( $params['action' ] );
+ unset( $params['subaction'] );
+ unset( $params['id' ] );
+ $params['old']='true';
+ }
+
+ // Session-Id ergaenzen
+ if ( $conf['interface']['url']['add_sessionid'] )
+ $params[ session_name() ] = session_id();
+
+ if ( config('security','use_post_token') )
+ $params[ 'token'] = token();
+
+ $fake_urls = $conf['interface']['url']['fake_url' ];
+ $url_format = $conf['interface']['url']['url_format'];
+
+ if ( isset($params['objectid']) && !isset($params['id']) )
+ $params['id'] = $params['objectid'];
+
+ if ( $fake_urls )
+ {
+// if ( $id != '' )
+// $id = '.'.$id;
+ }
+ else
+ {
+ global $view;
+ $params[REQ_PARAM_ACTION ] = $action;
+ $params[REQ_PARAM_SUBACTION] = $subaction;
+ $params[REQ_PARAM_ID ] = $id;
+
+ if ( !isset($params[REQ_PARAM_TARGET]))
+ $params[REQ_PARAM_TARGET ] = $view;
+ }
+
+ if ( count($params) > 0 )
+ {
+ $urlParameterList = array();
+ foreach( $params as $var=>$value )
+ {
+ $urlParameterList[] = urlencode($var).'='.urlencode($value);
+ }
+ $urlParameter = '?'.implode('&',$urlParameterList);
+ }
+ else
+ {
+ $urlParameter = '';
+ }
+
+ if ( @$conf['interface']['url']['index'] )
+ $controller_file_name = '';
+ else
+ $controller_file_name = OR_CONTROLLER_FILE.'.'.PHP_EXT;
+
+ $prefix = './';
+
+ if ( $fake_urls )
+ $src = sprintf( $url_format,$action,$subaction,$id,session_id() ).$urlParameter;
+ else
+ $src = $prefix.$controller_file_name.$urlParameter;
+
+ return $src;
+ }
+
+
+
+ public static function complete_tag($tagname,$attributes)
+ {
+ $text = '<'.$tagname;
+ foreach( $attributes as $attribute_name=>$attribute_value )
+ if ( !empty($attribute_value) )
+ $text .= ' '.$attribute_name.'="'.$attribute_value.'"';
+ $text .= ' />';
+ return $text;
+ }
+
+
+
+ public static function open_tag($tagname,$attributes)
+ {
+ $text = '<'.$tagname;
+ foreach( $attributes as $attribute_name=>$attribute_value )
+ if ( !empty($attribute_value) )
+ $text .= ' '.$attribute_name.'="'.$attribute_value.'"';
+ $text .= '>';
+ return $text;
+ }
+
+
+ public static function close_tag($tagname)
+ {
+ return '</'.$tagname.'>';
+ }
+}
+?>+
\ No newline at end of file
diff --git a/modules/util/Http.class.php b/modules/util/Http.class.php
@@ -0,0 +1,610 @@
+<?php
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+/**
+ * Kapselung einer HTTP-Anfrage.<br>
+ * Unter Beruecksichtigung von RFC 1945.<br>
+ *
+ * @author Jan Dankert
+ * @package openrat.services
+ */
+class Http
+{
+ public $header = array();
+
+ public $url = array();
+ public $responseHeader = array();
+ public $requestParameter = array();
+ public $urlParameter = array();
+
+ /**
+ * HTTP-Request-Typ.<br>
+ * Muss entweder "GET" oder "POST" sein.<br>
+ * Default: "GET".
+ *
+ * @var String Request-Typ
+ */
+ public $method = 'GET';
+ public $error = '';
+ public $status = '';
+ public $body = '';
+
+ public $httpCmd = '';
+
+
+
+ /**
+ * Erzeugt eine HTTP-Anfrage.
+ *
+ * @param String URL
+ * @return Http
+ */
+ public function Http( $url = '' )
+ {
+ $this->setURL( $url );
+ $this->header['User-Agent'] = 'Mozilla/5.0 (OpenRat CMS)';
+ $this->header['Connection'] = 'close';
+ }
+
+
+
+ /**
+ * Setzt die URL.
+ *
+ * @param String URL
+ */
+ function setURL( $url )
+ {
+ $this->url = parse_url($url);
+
+ if ( empty($this->url['host']) && !empty($this->url['path']) )
+ {
+ $this->url['host'] = basename($this->url['path']);
+ $this->url['path'] = '/';
+ }
+
+ if ( empty($this->url['path']) )
+ $this->url['path'] = '/';
+
+ if ( !isset($this->url['port']) )
+ if ( !isset($this->url['scheme']) )
+ {
+ $this->url['scheme'] = 'http'; // Standard-Port.
+ $this->url['port'] = 80; // Standard-Port.
+ }
+ elseif ( $this->url['scheme'] == 'https' )
+ $this->url['port'] = 443; // SSL-Port.
+ else
+ $this->url['port'] = 80; // Standard-Port.
+
+ if ( !empty($this->url['query']) )
+ parse_str( $this->url['query'],$this->urlParameter );
+
+ }
+
+
+
+ /**
+ * Setzt Authentisierungsinformationen in den HTTP-Request.<br>
+ *
+ * @param String Benutzername
+ * @param String Kennwort
+ */
+ public function setBasicAuthentication( $user, $password )
+ {
+ $this->header['Authorization'] = 'Basic '.base64_encode($user.':'.$password);
+ }
+
+
+
+ /**
+ * Erzeugt eine HTTP-Parameterstring mit allen Parametern.
+ *
+ * @param withPraefixQuestionMark Praefix mit Fragezeichen (fuer GET-Anfragen)
+ * @return String URL-Parameter
+ */
+ public function getParameterString( $withPraefixQuestionMark=false )
+ {
+ $parameterString = '';
+ $parameter = $this->urlParameter + $this->requestParameter;
+
+ if ( ! empty($parameter) )
+ {
+ foreach( $this->requestParameter as $paramName => $paramValue )
+ {
+ if ( strlen($parameterString) > 0)
+ $parameterString .= '&';
+ elseif ( $withPraefixQuestionMark )
+ $parameterString .= '?';
+
+ $parameterString .= urlencode($paramName) . '=' .urlencode($paramValue);
+ }
+ }
+
+ return $parameterString;
+ }
+
+
+ /**
+ * Liefert die URL des Requests.
+ *
+ * @return String URL
+ */
+ public function getUrl()
+ {
+ $location = $this->url['scheme'];
+ $location .= '://';
+ $location .= $this->url['host'];
+ if ( $this->url['scheme'] == 'http' && $this->url['port'] != 80 ||
+ $this->url['scheme'] == 'https' && $this->url['port'] != 443 )
+ $location .= ':'.$this->url['port'];
+ $location .= $this->url['path'];
+
+ $location .= $this->getParameterString(true);
+
+ if ( isset($this->url['fragment']) )
+ $location .= '#'.$this->url['fragment'];
+
+ return $location;
+ }
+
+
+ /**
+ * Sendet eine Redirect-Anweisung mit der aktuellen URL an den Browser.
+ */
+ public function sendRedirect()
+ {
+ $location = $this->getUrl();
+
+ header('Location: '.$location);
+ exit;
+ }
+
+
+ /**
+ * Führt einen HTTP-Request durch.
+ *
+ * @return boolean Erfolg der Anfrage.
+ */
+ public function request()
+ {
+ $this->error = '';
+ $this->status = '';
+
+ $errno = 0;
+ $errstr = '';
+
+ if ( empty($this->url['host']) )
+ {
+ $this->error = "No hostname specified";
+ return false;
+ }
+
+ foreach( $this->header as $header_key=>$header_value )
+ {
+ if ( is_numeric( $header_key ) )
+ {
+ $dp = strpos($header_value,':');
+ if ( $dp!==FALSE)
+ $this->header[substr($header_value,0,$dp)] = substr($header_value,$dp+1);
+ unset($this->header[$header_key]);
+ }
+ }
+
+ $parameterString = $this->getParameterString();
+
+ if ( $this->method == 'POST' )
+ {
+ $this->header['Content-Type' ] = 'application/x-www-form-urlencoded';
+ $this->header['Content-Length'] = strlen($parameterString);
+ }
+
+ // Accept-Header setzen, falls noch nicht vorhanden.
+ if ( !array_key_exists('Accept',$this->header) )
+ $this->header['Accept'] = '*/*';
+
+ $this->responseHeader = array();
+
+ // RFC 1945 (Section 9.3) says:
+ // A user agent should never automatically redirect a request
+ // more than 5 times, since such redirections usually indicate an infinite loop.
+ for( $r=1; $r<=5; $r++ )
+ {
+ $this->header['Host'] = $this->url['host'];
+
+ // Die Funktion fsockopen() erwartet eine Protokollangabe (bei TCP optional, bei SSL notwendig).
+ if ( $this->url['scheme'] == 'https' || $this->url['port'] == '443' )
+ $prx_proto = 'ssl://'; // SSL
+ else
+ $prx_proto = 'tcp://'; // Default
+
+ $fp = @fsockopen ($prx_proto.$this->url['host'],$this->url['port'], $errno, $errstr, 30);
+
+ if ( !$fp || !is_resource($fp) )
+ {
+ // Keine Verbindung zum Host moeglich.
+ $this->error = "Connection refused: '".$prx_proto.$this->url['host'].':'.$this->url['port']." - $errstr ($errno)";
+ return false;
+ }
+ else
+ {
+
+ $lb = "\r\n";
+ $http_get = $this->url['path'];
+
+ $request_header = array( $this->method.' '.$http_get.' HTTP/1.0');
+
+ foreach($this->header as $header_key=>$header_value)
+ $request_header[] = $header_key.': '.$header_value;
+
+ $http_request = implode($lb,$request_header).$lb.$lb;
+
+ if ( $this->method == 'GET')
+ if ( !empty($parameterString) )
+ $http_get .= '?'.$parameterString;
+
+ if ( $this->method == 'POST' )
+ $http_request .= $parameterString;
+
+ if (!is_resource($fp)) {
+ $this->error = 'Connection lost after connect: '.$prx_proto.$this->url['host'].':'.$this->url['port'];
+ return false;
+ }
+ fputs($fp, $http_request); // Die HTTP-Anfrage zum Server senden.
+
+ // Jetzt erfolgt das Auslesen der HTTP-Antwort.
+ $isHeader = true;
+
+ // RFC 1945 (Section 6.1) schreibt als Statuszeile folgendes Format vor
+ // "HTTP/" 1*DIGIT "." 1*DIGIT SP 3DIGIT SP
+ if (!is_resource($fp)) {
+ $this->error = 'Connection lost during transfer: '.$this->url['host'].':'.$this->url['port'];
+ return false;
+ }
+ elseif (!feof($fp)) {
+ $line = fgets($fp,1028);
+ $this->status = substr($line,9,3);
+ }
+ else
+ {
+ $this->error = 'Unexpected EOF while reading HTTP-Response';
+ return false;
+ }
+
+ $this->body = '';
+ while (!feof($fp)) {
+ $line = fgets($fp,1028);
+ if ( $isHeader && trim($line)=='' ) // Leerzeile nach Header.
+ {
+ $isHeader = false;
+ }
+ elseif( $isHeader )
+ {
+ list($headerName,$headerValue) = explode(': ',$line) + array(1=>'');
+ $this->responseHeader[$headerName] = trim($headerValue);
+ }
+ else
+ {
+ $this->body .= $line;
+ }
+ }
+ fclose($fp); // Verbindung brav schlie�en.
+
+
+ // RFC 1945 (Section 6.1.1) schreibt
+ // "[...] However, applications must understand the class of any status code, as
+ // indicated by the first digit"
+ // Daher interessiert uns nur die erste Stelle des 3-stelligen HTTP-Status.
+
+ // 301 Moved Permanently
+ // 302 Moved Temporarily
+ if ( $this->status == '301' ||
+ $this->status == '302' )
+ {
+ $location = @$this->responseHeader['Location'];
+ if ( empty($location) )
+ {
+ $this->error = '301/302 Response without Location-header';
+ return false;
+ }
+
+ //Html::debug($this->url,"alte URL");
+ //Html::debug($location,"NEUES REDIRECT AUF");
+ $this->setURL($location);
+ continue; // Naechster Versuch mit umgeleiteter Adresse.
+ }
+
+ // RFC 1945 (Section 6.1.1) schreibt
+ // "2xx: Success - The action was successfully received, understood, and accepted."
+ elseif ( substr($this->status,0,1) == '2' )
+ {
+ return true;
+ }
+ elseif ( substr($this->status,0,1) == '4' )
+ {
+ $this->error = 'Client Error: '.$this->status;
+ return false;
+ }
+ elseif ( substr($this->status,0,1) == '5' )
+ {
+ $this->error = 'Server Error: '.$this->status;
+ return false;
+ }
+ else
+ {
+ $this->error = 'Unexpected HTTP-Status: '.$this->status. '; this is mostly a client error, sorry.';
+ return false;
+ }
+ }
+
+ $this->error = 'Too much redirects, infinite loop assumed. Exiting. Last URL: '.$http_get;
+ return false;
+
+ }
+
+ }
+
+
+ /**
+ * Aus dem HTTP-Header werden die vom Browser angeforderten Sprachen
+ * gelesen.<br>
+ * Es wird eine Liste von Sprachen erzeugt.<br>
+ *
+ * Beispiel:
+ * 'de_DE','de','en_GB','en' ... usw.<br>
+ * Wenn der Browser 'de_DE' anfordert, wird hier zusätzlich
+ * auch 'de' (als Fallback) ermittelt.
+ *
+ * @static
+ * @return Array
+ */
+ public static function getLanguages()
+ {
+ global $HTTP_SERVER_VARS;
+
+ $languages = array();
+ $http_languages = @$HTTP_SERVER_VARS['HTTP_ACCEPT_LANGUAGE'];
+ foreach( explode(',',$http_languages) as $l )
+ {
+ list($part) = explode(';',$l); // Priorit�ten ignorieren.
+ $languages[] = trim($part);
+
+ // Aus "de_DE" das "de" extrahieren.
+ $languages[] = current(explode('_',str_replace('-','_',trim($part))));
+ }
+
+ return array_unique( $languages );
+ }
+
+
+ /**
+ * Ermittelt die aktuelle HTTP-Adresse des Requests (inkl. Pfad, jedoch ohne Datei).
+ *
+ * @return String URL
+ */
+ public static function getServer()
+ {
+ $https = getenv('HTTPS');
+
+ if ( $https )
+ $server = 'https://';
+ else
+ $server = 'http://';
+
+ $server .= getenv('SERVER_NAME').dirname(getenv('REQUEST_URI'));
+
+ return $server;
+ }
+
+
+
+ /**
+ * Server-Fehlermeldung anzeigen.<br>
+ *
+ * Erzeugt einen "HTTP 501 Internal Server Error". Zusaetzlich
+ * wird ein 'rollback' auf der Datenbank ausgefaehrt.
+ *
+ * @param String $message Eigener Hinweistext
+ */
+ public static function serverError($message,$reason='')
+ {
+ if ( class_exists('Session'))
+ {
+ $db = Session::getDatabase();
+ if ( is_object( $db ) )
+ $db->rollback();
+ }
+
+ if ( class_exists('Logger'))
+ Logger::warn($message."\n".$reason);
+
+ Http::sendStatus(501,'Internal Server Error',$message,$reason);
+ }
+
+
+
+ /**
+ * Der Benutzer ist nicht autorisiert, eine Aktion auszufuehren.
+ *
+ * Diese Funktion erzeugt einen "HTTP 403 Not Authorized" und das
+ * Skript wird beendet.
+ *
+ * @param String $text Text
+ * @param String $message Eigener Hinweistext
+ */
+ public static function notAuthorized($message='')
+ {
+ Logger::warn("Security warning: $message");
+ Http::sendStatus(403,'Not authorized',$message);
+ }
+
+
+
+
+
+
+ /**
+ * Nichts gefunden.
+ *
+ * Diese Funktion erzeugt einen "HTTP 404 Not found" und das
+ * Skript wird beendet.
+ *
+ * @param String $text Text
+ * @param String $message Eigener Hinweistext
+ */
+ public static function notFound($text,$message)
+ {
+ Http::sendStatus(404,'Not found',$message);
+ }
+
+
+
+ /**
+ * Kein Inhalt.
+ *
+ * Die HTTP-Antwort stellt gegenüber dem Client klar, dass es keinen Inhalt gibt.
+ */
+ public static function noContent()
+ {
+ header('HTTP/1.0 204 No Content');
+ exit;
+ }
+
+
+
+ /**
+ * Schickt einen HTTP-Status zum Client und beendet das Skript.
+ *
+ * @param Integer $status HTTP-Status (ganzzahlig) (Default: 501)
+ * @param String $text HTTP-Meldung (Default: 'Internal Server Error')
+ * @param String $message Eigener Hinweistext (Default: leer)
+ * @param String $reason Technischer Grund (Default: leer)
+ */
+ public static function sendStatus( $status=501,$text='Internal Server Error',$message='',$reason='' )
+ {
+ if ( headers_sent() )
+ {
+ echo "$status $text\n$message";
+ exit;
+ }
+
+ header('HTTP/1.0 '.intval($status).' '.$text);
+
+
+ $types = Http::getAccept();
+
+ if ( @$_REQUEST['output']=='json' || sizeof($types)==1 && in_array('application/json',$types) )
+ {
+ header('Content-Type: application/json');
+ require_once( OR_SERVICECLASSES_DIR."JSON.class.".PHP_EXT );
+ $json = new JSON();
+ echo $json->encode( array('status'=>$status,'error'=>$text,'description'=>$message,'reason'=>$reason) );
+ }
+ elseif ( @$_REQUEST['output']=='xml' || sizeof($types)==1 && in_array('application/xml',$types) )
+ {
+ header('Content-Type: application/xml');
+ require_once( OR_SERVICECLASSES_DIR."XML.class.".PHP_EXT );
+ $xml = new XML();
+ $xml->root='error';
+ echo $xml->encode( array('status'=>$status,'error'=>$text,'description'=>$message,'reason'=>$reason) );
+ }
+ else
+ {
+ header('Content-Type: text/html');
+ $message = htmlentities($message);
+ $reason = htmlentities($reason );
+ $signature = OR_TITLE.' '.OR_VERSION.' '.getenv('SERVER_SOFTWARE');
+ echo <<<HTML
+<html>
+<head><title>$status $text - OpenRat</title></head>
+<body>
+<h1>$text</h1>
+<p>$message</p>
+<pre>$reason</pre>
+<hr>
+<address>$signature</adddress>
+</body>
+</html>
+HTML;
+ }
+ exit;
+ }
+
+
+ /**
+ * Liefert den Mime-Type, den der Browser (oder besser: HTTP-Client) wünscht.
+ *
+ * @return Array Mime-Typen, welche vom User-Agent akzeptiert werden.
+ */
+ public static function getAccept()
+ {
+ $httpAccept = getenv('HTTP_ACCEPT');
+ return $types = explode(',',$httpAccept);
+ }
+
+
+
+ /**
+ * Liefert die IPv4-Adresse des Clients. Falls der Request durch einen Proxy kam, wird
+ * versucht, die echte IP-Adresse aus dem Anfrageheader zu ermitteln.
+ *
+ * @return Client-IPv4-Adresse
+ */
+ public static function getClientIP()
+ {
+ $ip = '';
+
+ if ( isset($_SERVER["HTTP_X_FORWARDED_FOR"]) )
+ {
+ $ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
+ }
+ elseif ( isset($_SERVER["HTTP_CLIENT_IP"]) )
+ {
+ $ip = $_SERVER["HTTP_CLIENT_IP"];
+ }
+ elseif ( isset($_SERVER["REMOTE_ADDR"]) )
+ {
+ $ip = $_SERVER["REMOTE_ADDR"];
+ }
+
+ return $ip;
+ }
+
+
+
+ /**
+ * Ermittelt den TCP/IP-Port des Clients.
+ * Achtung, bei Proxy-Zugriffen kann dies der Port des Proxys sein.
+ *
+ * @return TCP/IP-Port
+ */
+ public static function getClientPort()
+ {
+ $ip = '';
+
+ if ( isset($_SERVER["REMOTE_PORT"]) )
+ {
+ $ip = $_SERVER["REMOTE_PORT"];
+ }
+
+ return $ip;
+ }
+}
+
+?>+
\ No newline at end of file
diff --git a/modules/util/JSON.class.php b/modules/util/JSON.class.php
@@ -0,0 +1,809 @@
+<?php
+/**
+ * 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>
+ * @author Jan Dankert
+ * @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_INDENT', "\t");
+
+/**
+ * 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 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()
+ {
+ $this->use = SERVICES_JSON_LOOSE_TYPE;
+ }
+
+ /**
+ * 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)
+ {
+ static $indentNr = 0;
+
+ $indent = str_repeat(SERVICES_JSON_INDENT,$indentNr);
+
+ 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))) {
+ $indentNr++;
+ $properties = array_map(array($this, 'name_value'),
+ array_keys($var),
+ array_values($var));
+ $indentNr--;
+
+ foreach($properties as $property) {
+ if(JSON::isError($property)) {
+ return $property;
+ }
+ }
+
+ return "\n$indent".'{' ."\n$indent".SERVICES_JSON_INDENT. join(','."\n$indent".SERVICES_JSON_INDENT, $properties) ."\n$indent".'}'."\n$indent";
+ }
+
+ // treat it like a regular array
+ $indentNr++;
+ $elements = array_map(array($this, 'encode'), $var);
+ $indentNr--;
+
+ foreach($elements as $element) {
+ if(JSON::isError($element)) {
+ return $element;
+ }
+ }
+
+ return "\n$indent".'['."\n$indent".SERVICES_JSON_INDENT. join(','."\n$indent".SERVICES_JSON_INDENT, $elements) . "\n$indent".']'."\n$indent";
+
+ case 'object':
+ $vars = get_object_vars($var);
+
+ $indentNr++;
+ $properties = array_map(array($this, 'name_value'),
+ array_keys($vars),
+ array_values($vars));
+ $indentNr--;
+
+ foreach($properties as $property) {
+ if(JSON::isError($property)) {
+ return $property;
+ }
+ }
+
+ return "\n$indent".'{' ."\n$indent".SERVICES_JSON_INDENT. join(','."\n$indent".SERVICES_JSON_INDENT, $properties) . "\n$indent".'}'."\n$indent";
+
+ default:
+ return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
+ ? 'null'
+ : new 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(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) == 'json_error' ||
+ is_subclass_of($data, 'json_error'))) {
+ return true;
+ }
+
+ return false;
+ }
+}
+
+
+ /**
+ * @todo Ultimately, this class shall be descended from PEAR_Error
+ */
+ class JSON_Error
+ {
+ function JSON_Error($message = 'unknown error', $code = null,
+ $mode = null, $options = null, $userinfo = null)
+ {
+
+ }
+ }
+
+
+?>
diff --git a/modules/util/JSqueeze.class.php b/modules/util/JSqueeze.class.php
@@ -0,0 +1,1064 @@
+<?php
+
+/*
+ * Copyright (C) 2016 Nicolas Grekas - p@tchwork.com
+ *
+ * This library is free software; you can redistribute it and/or modify it
+ * under the terms of the (at your option):
+ * Apache License v2.0 (see provided LICENCE.ASL20 file), or
+ * GNU General Public License v2.0 (see provided LICENCE.GPLv2 file).
+ */
+
+//namespace Patchwork;
+
+/*
+*
+* This class shrinks Javascript code
+* (a process called minification nowadays)
+*
+* Should work with most valid Javascript code,
+* even when semi-colons are missing.
+*
+* Features:
+* - Removes comments and white spaces.
+* - Renames every local vars, typically to a single character.
+* - Renames also global vars, methods and properties, but only if they
+* are marked special by some naming convention. By default, special
+* var names begin with one or more "$", or with a single "_".
+* - Renames also local/global vars found in strings,
+* but only if they are marked special.
+* - Keep Microsoft's conditional comments.
+* - Output is optimized for later HTTP compression.
+*
+* Notes:
+* - Source code must be parse error free before processing.
+* - In order to maximise later HTTP compression (deflate, gzip),
+* new variables names are chosen by considering closures,
+* variables' frequency and characters' frequency.
+* - If you use with/eval then be careful.
+*
+* Bonus:
+* - Replaces false/true by !1/!0
+* - Replaces new Array/Object by []/{}
+* - Merges consecutive "var" declarations with commas
+* - Merges consecutive concatened strings
+* - Fix a bug in Safari's parser (http://forums.asp.net/thread/1585609.aspx)
+* - Can replace optional semi-colons by line feeds,
+* thus facilitating output debugging.
+* - Keep important comments marked with /*!...
+* - Treats three semi-colons ;;; like single-line comments
+* (http://dean.edwards.name/packer/2/usage/#triple-semi-colon).
+* - Fix special catch scope across browsers
+* - Work around buggy-handling of named function expressions in IE<=8
+*
+* TODO?
+* - foo['bar'] => foo.bar
+* - {'foo':'bar'} => {foo:'bar'}
+* - Dead code removal (never used function)
+* - Munge primitives: var WINDOW=window, etc.
+*/
+
+class JSqueeze
+{
+ const
+
+ SPECIAL_VAR_PACKER = '(\$+[a-zA-Z_]|_[a-zA-Z0-9$])[a-zA-Z0-9_$]*';
+
+ public
+
+ $charFreq;
+
+ protected
+
+ $strings,
+ $closures,
+ $str0,
+ $str1,
+ $argFreq,
+ $specialVarRx,
+ $keepImportantComments,
+
+ $varRx = '(?:[a-zA-Z_$])[a-zA-Z0-9_$]*',
+ $reserved = array(
+ // Literals
+ 'true','false','null',
+ // ES6
+ 'break','case','class','catch','const','continue','debugger','default','delete','do','else','export','extends','finally','for','function','if','import','in','instanceof','new','return','super','switch','this','throw','try','typeof','var','void','while','with','yield',
+ // Future
+ 'enum',
+ // Strict mode
+ 'implements','package','protected','static','let','interface','private','public',
+ // Module
+ 'await',
+ // Older standards
+ 'abstract','boolean','byte','char','double','final','float','goto','int','long','native','short','synchronized','throws','transient','volatile',
+ );
+
+ public function __construct()
+ {
+ $this->reserved = array_flip($this->reserved);
+ $this->charFreq = array_fill(0, 256, 0);
+ }
+
+ /**
+ * Squeezes a JavaScript source code.
+ *
+ * Set $singleLine to false if you want optional
+ * semi-colons to be replaced by line feeds.
+ *
+ * Set $keepImportantComments to false if you want /*! comments to be removed.
+ *
+ * $specialVarRx defines the regular expression of special variables names
+ * for global vars, methods, properties and in string substitution.
+ * Set it to false if you don't want any.
+ *
+ * If the analysed javascript source contains a single line comment like
+ * this one, then the directive will overwrite $specialVarRx:
+ *
+ * // jsqueeze.specialVarRx = your_special_var_regexp_here
+ *
+ * Only the first directive is parsed, others are ignored. It is not possible
+ * to redefine $specialVarRx in the middle of the javascript source.
+ *
+ * Example:
+ * $parser = new JSqueeze;
+ * $squeezed_js = $parser->squeeze($fat_js);
+ */
+ public function squeeze($code, $singleLine = true, $keepImportantComments = true, $specialVarRx = false)
+ {
+ $code = trim($code);
+ if ('' === $code) {
+ return '';
+ }
+
+ $this->argFreq = array(-1 => 0);
+ $this->specialVarRx = $specialVarRx;
+ $this->keepImportantComments = !!$keepImportantComments;
+
+ if (preg_match("#//[ \t]*jsqueeze\.specialVarRx[ \t]*=[ \t]*([\"']?)(.*)\\1#i", $code, $key)) {
+ if (!$key[1]) {
+ $key[2] = trim($key[2]);
+ $key[1] = strtolower($key[2]);
+ $key[1] = $key[1] && $key[1] != 'false' && $key[1] != 'none' && $key[1] != 'off';
+ }
+
+ $this->specialVarRx = $key[1] ? $key[2] : false;
+ }
+
+ // Remove capturing parentheses
+ $this->specialVarRx && $this->specialVarRx = preg_replace('/(?<!\\\\)((?:\\\\\\\\)*)\((?!\?)/', '(?:', $this->specialVarRx);
+
+ false !== strpos($code, "\r") && $code = strtr(str_replace("\r\n", "\n", $code), "\r", "\n");
+ false !== strpos($code, "\xC2\x85") && $code = str_replace("\xC2\x85", "\n", $code); // Next Line
+ false !== strpos($code, "\xE2\x80\xA8") && $code = str_replace("\xE2\x80\xA8", "\n", $code); // Line Separator
+ false !== strpos($code, "\xE2\x80\xA9") && $code = str_replace("\xE2\x80\xA9", "\n", $code); // Paragraph Separator
+
+ list($code, $this->strings) = $this->extractStrings($code);
+ list($code, $this->closures) = $this->extractClosures($code);
+
+ $key = "//''\"\"#0'"; // This crap has a wonderful property: it can not happen in any valid javascript, even in strings
+ $this->closures[$key] = &$code;
+
+ $tree = array($key => array('parent' => false));
+ $this->makeVars($code, $tree[$key], $key);
+ $this->renameVars($tree[$key], true);
+
+ $code = substr($tree[$key]['code'], 1);
+ $code = preg_replace("'\breturn !'", 'return!', $code);
+ $code = preg_replace("'\}(?=(else|while)[^\$.a-zA-Z0-9_])'", "}\r", $code);
+ $code = str_replace(array_keys($this->strings), array_values($this->strings), $code);
+
+ if ($singleLine) {
+ $code = strtr($code, "\n", ';');
+ } else {
+ $code = str_replace("\n", ";\n", $code);
+ }
+ false !== strpos($code, "\r") && $code = strtr(trim($code), "\r", "\n");
+
+ // Cleanup memory
+ $this->charFreq = array_fill(0, 256, 0);
+ $this->strings = $this->closures = $this->argFreq = array();
+ $this->str0 = $this->str1 = '';
+
+ return $code;
+ }
+
+ protected function extractStrings($f)
+ {
+ if ($cc_on = false !== strpos($f, '@cc_on')) {
+ // Protect conditional comments from being removed
+ $f = str_replace('#', '##', $f);
+ $f = str_replace('/*@', '1#@', $f);
+ $f = preg_replace("'//@([^\n]+)'", '2#@$1@#3', $f);
+ $f = str_replace('@*/', '@#1', $f);
+ }
+
+ $len = strlen($f);
+ $code = str_repeat(' ', $len);
+ $j = 0;
+
+ $strings = array();
+ $K = 0;
+
+ $instr = false;
+
+ $q = array(
+ "'", '"',
+ "'" => 0,
+ '"' => 0,
+ );
+
+ // Extract strings, removes comments
+ for ($i = 0; $i < $len; ++$i) {
+ if ($instr) {
+ if ('//' == $instr) {
+ if ("\n" == $f[$i]) {
+ $f[$i--] = ' ';
+ $instr = false;
+ }
+ } elseif ($f[$i] == $instr || ('/' == $f[$i] && "/'" == $instr)) {
+ if ('!' == $instr) {
+ } elseif ('*' == $instr) {
+ if ('/' == $f[$i + 1]) {
+ ++$i;
+ $instr = false;
+ }
+ } else {
+ if ("/'" == $instr) {
+ while (isset($f[$i + 1]) && false !== strpos('gmi', $f[$i + 1])) {
+ $s[] = $f[$i++];
+ }
+ $s[] = $f[$i];
+ }
+
+ $instr = false;
+ }
+ } elseif ('*' == $instr) {
+ } elseif ('!' == $instr) {
+ if ('*' == $f[$i] && '/' == $f[$i + 1]) {
+ $s[] = "*/\r";
+ ++$i;
+ $instr = false;
+ } elseif ("\n" == $f[$i]) {
+ $s[] = "\r";
+ } else {
+ $s[] = $f[$i];
+ }
+ } elseif ('\\' == $f[$i]) {
+ ++$i;
+
+ if ("\n" != $f[$i]) {
+ isset($q[$f[$i]]) && ++$q[$f[$i]];
+ $s[] = '\\'.$f[$i];
+ }
+ } elseif ('[' == $f[$i] && "/'" == $instr) {
+ $instr = '/[';
+ $s[] = '[';
+ } elseif (']' == $f[$i] && '/[' == $instr) {
+ $instr = "/'";
+ $s[] = ']';
+ } elseif ("'" == $f[$i] || '"' == $f[$i]) {
+ ++$q[$f[$i]];
+ $s[] = '\\'.$f[$i];
+ } else {
+ $s[] = $f[$i];
+ }
+ } else {
+ switch ($f[$i]) {
+ case ';':
+ // Remove triple semi-colon
+ if ($i > 0 && ';' == $f[$i - 1] && $i + 1 < $len && ';' == $f[$i + 1]) {
+ $f[$i] = $f[$i + 1] = '/';
+ } else {
+ $code[++$j] = ';';
+ break;
+ }
+
+ case '/':
+ if ('*' == $f[$i + 1]) {
+ ++$i;
+ $instr = '*';
+
+ if ($this->keepImportantComments && '!' == $f[$i + 1]) {
+ ++$i;
+ // no break here
+ } else {
+ break;
+ }
+ } elseif ('/' == $f[$i + 1]) {
+ ++$i;
+ $instr = '//';
+ break;
+ } else {
+ $a = $j && (' ' == $code[$j] || "\x7F" == $code[$j]) ? $code[$j - 1] : $code[$j];
+ if (false !== strpos('-!%&;<=>~:^+|,()*?[{} ', $a)
+ || (false !== strpos('oenfd', $a)
+ && preg_match(
+ "'(?<![\$.a-zA-Z0-9_])(do|else|return|typeof|yield[ \x7F]?\*?)[ \x7F]?$'",
+ substr($code, $j - 7, 8)
+ ))
+ ) {
+ if (')' === $a && $j > 1) {
+ $a = 1;
+ $k = $j - (' ' == $code[$j] || "\x7F" == $code[$j]) - 1;
+ while ($k >= 0 && $a) {
+ if ('(' === $code[$k]) {
+ --$a;
+ } elseif (')' === $code[$k]) {
+ ++$a;
+ }
+ --$k;
+ }
+ if (!preg_match("'(?<![\$.a-zA-Z0-9_])(if|for|while)[ \x7F]?$'", substr($code, 0, $k + 1))) {
+ $code[++$j] = '/';
+ break;
+ }
+ }
+
+ $key = "//''\"\"".$K++.$instr = "/'";
+ $a = $j;
+ $code .= $key;
+ while (isset($key[++$j - $a - 1])) {
+ $code[$j] = $key[$j - $a - 1];
+ }
+ --$j;
+ isset($s) && ($s = implode('', $s)) && $cc_on && $this->restoreCc($s);
+ $strings[$key] = array('/');
+ $s = &$strings[$key];
+ } else {
+ $code[++$j] = '/';
+ }
+
+ break;
+ }
+
+ case "'":
+ case '"':
+ $instr = $f[$i];
+ $key = "//''\"\"".$K++.('!' == $instr ? ']' : "'");
+ $a = $j;
+ $code .= $key;
+ while (isset($key[++$j - $a - 1])) {
+ $code[$j] = $key[$j - $a - 1];
+ }
+ --$j;
+ isset($s) && ($s = implode('', $s)) && $cc_on && $this->restoreCc($s);
+ $strings[$key] = array();
+ $s = &$strings[$key];
+ '!' == $instr && $s[] = "\r/*!";
+
+ break;
+
+ case "\n":
+ if ($j > 3) {
+ if (' ' == $code[$j] || "\x7F" == $code[$j]) {
+ --$j;
+ }
+
+ $code[++$j] =
+ false !== strpos('kend+-', $code[$j - 1])
+ && preg_match(
+ "'(?:\+\+|--|(?<![\$.a-zA-Z0-9_])(break|continue|return|yield[ \x7F]?\*?))[ \x7F]?$'",
+ substr($code, $j - 8, 9)
+ )
+ ? ';' : "\x7F";
+
+ break;
+ }
+
+ case "\t": $f[$i] = ' ';
+ case ' ':
+ if (!$j || ' ' == $code[$j] || "\x7F" == $code[$j]) {
+ break;
+ }
+
+ default:
+ $code[++$j] = $f[$i];
+ }
+ }
+ }
+
+ isset($s) && ($s = implode('', $s)) && $cc_on && $this->restoreCc($s);
+ unset($s);
+
+ $code = substr($code, 0, $j + 1);
+ $cc_on && $this->restoreCc($code, false);
+
+ // Protect wanted spaces and remove unwanted ones
+ $code = strtr($code, "\x7F", ' ');
+ $code = str_replace('- -', "-\x7F-", $code);
+ $code = str_replace('+ +', "+\x7F+", $code);
+ $code = preg_replace("'(\d)\s+\.\s*([a-zA-Z\$_[(])'", "$1\x7F.$2", $code);
+ $code = preg_replace("# ([-!%&;<=>~:.^+|,()*?[\]{}/']+)#", '$1', $code);
+ $code = preg_replace("#([-!%&;<=>~:.^+|,()*?[\]{}/]+) #", '$1', $code);
+ $cc_on && $code = preg_replace_callback("'//[^\'].*?@#3'", function ($m) { return strtr($m[0], ' ', "\x7F"); }, $code);
+
+ // Replace new Array/Object by []/{}
+ false !== strpos($code, 'new Array') && $code = preg_replace("'new Array(?:\(\)|([;\])},:]))'", '[]$1', $code);
+ false !== strpos($code, 'new Object') && $code = preg_replace("'new Object(?:\(\)|([;\])},:]))'", '{}$1', $code);
+
+ // Add missing semi-colons after curly braces
+ // This adds more semi-colons than strictly needed,
+ // but it seems that later gzipping is favorable to the repetition of "};"
+ $code = preg_replace("'\}(?![:,;.()\[\]}\|&]|(else|catch|finally|while)[^\$.a-zA-Z0-9_])'", '};', $code);
+
+ // Tag possible empty instruction for easy detection
+ $code = preg_replace("'(?<![\$.a-zA-Z0-9_])if\('", '1#(', $code);
+ $code = preg_replace("'(?<![\$.a-zA-Z0-9_])for\('", '2#(', $code);
+ $code = preg_replace("'(?<![\$.a-zA-Z0-9_])while\('", '3#(', $code);
+
+ $forPool = array();
+ $instrPool = array();
+ $s = 0;
+
+ $f = array();
+ $j = -1;
+
+ // Remove as much semi-colon as possible
+ $len = strlen($code);
+ for ($i = 0; $i < $len; ++$i) {
+ switch ($code[$i]) {
+ case '(':
+ if ($j >= 0 && "\n" == $f[$j]) {
+ $f[$j] = ';';
+ }
+
+ ++$s;
+
+ if ($i && '#' == $code[$i - 1]) {
+ $instrPool[$s - 1] = 1;
+ if ('2' == $code[$i - 2]) {
+ $forPool[$s] = 1;
+ }
+ }
+
+ $f[++$j] = '(';
+ break;
+
+ case ']':
+ case ')':
+ if ($i + 1 < $len && !isset($forPool[$s]) && !isset($instrPool[$s - 1]) && preg_match("'[a-zA-Z0-9_\$]'", $code[$i + 1])) {
+ $f[$j] .= $code[$i];
+ $f[++$j] = "\n";
+ } else {
+ $f[++$j] = $code[$i];
+ }
+
+ if (')' == $code[$i]) {
+ unset($forPool[$s]);
+ --$s;
+ }
+
+ continue 2;
+
+ case '}':
+ if ("\n" == $f[$j]) {
+ $f[$j] = '}';
+ } else {
+ $f[++$j] = '}';
+ }
+ break;
+
+ case ';':
+ if (isset($forPool[$s]) || isset($instrPool[$s])) {
+ $f[++$j] = ';';
+ } elseif ($j >= 0 && "\n" != $f[$j] && ';' != $f[$j]) {
+ $f[++$j] = "\n";
+ }
+
+ break;
+
+ case '#':
+ switch ($f[$j]) {
+ case '1': $f[$j] = 'if'; break 2;
+ case '2': $f[$j] = 'for'; break 2;
+ case '3': $f[$j] = 'while'; break 2;
+ }
+
+ case '[';
+ if ($j >= 0 && "\n" == $f[$j]) {
+ $f[$j] = ';';
+ }
+
+ default: $f[++$j] = $code[$i];
+ }
+
+ unset($instrPool[$s]);
+ }
+
+ $f = implode('', $f);
+ $cc_on && $f = str_replace('@#3', "\r", $f);
+
+ // Fix "else ;" empty instructions
+ $f = preg_replace("'(?<![\$.a-zA-Z0-9_])else\n'", "\n", $f);
+
+ $r1 = array( // keywords with a direct object
+ 'case','delete','do','else','function','in','instanceof','of','break',
+ 'new','return','throw','typeof','var','void','yield','let','if',
+ 'const','get','set',
+ );
+
+ $r2 = array( // keywords with a subject
+ 'in','instanceof','of',
+ );
+
+ // Fix missing semi-colons
+ $f = preg_replace("'(?<!(?<![a-zA-Z0-9_\$])".implode(')(?<!(?<![a-zA-Z0-9_\$])', $r1).') (?!('.implode('|', $r2).")(?![a-zA-Z0-9_\$]))'", "\n", $f);
+ $f = preg_replace("'(?<!(?<![a-zA-Z0-9_\$])do)(?<!(?<![a-zA-Z0-9_\$])else) if\('", "\nif(", $f);
+ $f = preg_replace("'(?<=--|\+\+)(?<![a-zA-Z0-9_\$])(".implode('|', $r1).")(?![a-zA-Z0-9_\$])'", "\n$1", $f);
+ $f = preg_replace("'(?<![a-zA-Z0-9_\$])for\neach\('", 'for each(', $f);
+ $f = preg_replace("'(?<![a-zA-Z0-9_\$])\n(".implode('|', $r2).")(?![a-zA-Z0-9_\$])'", '$1', $f);
+
+ // Merge strings
+ if ($q["'"] > $q['"']) {
+ $q = array($q[1], $q[0]);
+ }
+ $f = preg_replace("#//''\"\"[0-9]+'#", $q[0].'$0'.$q[0], $f);
+ strpos($f, $q[0].'+'.$q[0]) && $f = str_replace($q[0].'+'.$q[0], '', $f);
+ $len = count($strings);
+ foreach ($strings as $r1 => &$r2) {
+ $r2 = "/'" == substr($r1, -2)
+ ? str_replace(array("\\'", '\\"'), array("'", '"'), $r2)
+ : str_replace('\\'.$q[1], $q[1], $r2);
+ }
+
+ // Restore wanted spaces
+ $f = strtr($f, "\x7F", ' ');
+
+ return array($f, $strings);
+ }
+
+ protected function extractClosures($code)
+ {
+ $code = ';'.$code;
+
+ $this->argFreq[-1] += substr_count($code, '}catch(');
+
+ if ($this->argFreq[-1]) {
+ // Special catch scope handling
+
+ // FIXME: this implementation doesn't work with nested catch scopes who need
+ // access to their parent's caught variable (but who needs that?).
+
+ $f = preg_split("@}catch\(({$this->varRx})@", $code, -1, PREG_SPLIT_DELIM_CAPTURE);
+
+ $code = 'catch$scope$var'.mt_rand();
+ $this->specialVarRx = $this->specialVarRx ? '(?:'.$this->specialVarRx.'|'.preg_quote($code).')' : preg_quote($code);
+ $i = count($f) - 1;
+
+ while ($i) {
+ $c = 1;
+ $j = 0;
+ $l = strlen($f[$i]);
+
+ while ($c && $j < $l) {
+ $s = $f[$i][$j++];
+ $c += '(' == $s ? 1 : (')' == $s ? -1 : 0);
+ }
+
+ if (!$c) {
+ do {
+ $s = $f[$i][$j++];
+ $c += '{' == $s ? 1 : ('}' == $s ? -1 : 0);
+ } while ($c && $j < $l);
+ }
+
+ $c = preg_quote($f[$i - 1], '#');
+ $f[$i - 2] .= '}catch('.preg_replace("#([.,{]?)(?<![a-zA-Z0-9_\$@]){$c}\\b#", '$1'.$code, $f[$i - 1].substr($f[$i], 0, $j)).substr($f[$i], $j);
+
+ unset($f[$i--], $f[$i--]);
+ }
+
+ $code = $f[0];
+ }
+
+ $f = preg_split("'(?<![a-zA-Z0-9_\$])((?:function[ (]|get |set ).*?\{)'", $code, -1, PREG_SPLIT_DELIM_CAPTURE);
+ $i = count($f) - 1;
+ $closures = array();
+
+ while ($i) {
+ $c = 1;
+ $j = 0;
+ $l = strlen($f[$i]);
+
+ while ($c && $j < $l) {
+ $s = $f[$i][$j++];
+ $c += '{' == $s ? 1 : ('}' == $s ? -1 : 0);
+ }
+
+ switch (substr($f[$i - 2], -1)) {
+ default:
+ if (false !== $c = strpos($f[$i - 1], ' ', 8)) {
+ break;
+ }
+ case false: case "\n": case ';': case '{': case '}': case ')': case ']':
+ $c = strpos($f[$i - 1], '(', 4);
+ }
+
+ $l = "//''\"\"#$i'";
+ $code = substr($f[$i - 1], $c);
+ $closures[$l] = $code.substr($f[$i], 0, $j);
+ $f[$i - 2] .= substr($f[$i - 1], 0, $c).$l.substr($f[$i], $j);
+
+ if ('(){' !== $code) {
+ $j = substr_count($code, ',');
+ do {
+ isset($this->argFreq[$j]) ? ++$this->argFreq[$j] : $this->argFreq[$j] = 1;
+ } while ($j--);
+ }
+
+ $i -= 2;
+ }
+
+ return array($f[0], $closures);
+ }
+
+ protected function makeVars($closure, &$tree, $key)
+ {
+ $tree['code'] = &$closure;
+ $tree['nfe'] = false;
+ $tree['used'] = array();
+ $tree['local'] = array();
+
+ // Replace multiple "var" declarations by a single one
+ $closure = preg_replace_callback("'(?<=[\n\{\}])var [^\n\{\};]+(?:\nvar [^\n\{\};]+)+'", array($this, 'mergeVarDeclarations'), $closure);
+
+ // Get all local vars (functions, arguments and "var" prefixed)
+
+ $vars = &$tree['local'];
+
+ if (preg_match("'^( [^(]*)?\((.*?)\)\{'", $closure, $v)) {
+ if ($v[1]) {
+ $vars[$tree['nfe'] = substr($v[1], 1)] = -1;
+ $tree['parent']['local'][';'.$key] = &$vars[$tree['nfe']];
+ }
+
+ if ($v[2]) {
+ $i = 0;
+ $v = explode(',', $v[2]);
+ foreach ($v as $w) {
+ $vars[$w] = $this->argFreq[$i++] - 1; // Give a bonus to argument variables
+ }
+ }
+ }
+
+ $v = preg_split("'(?<![\$.a-zA-Z0-9_])var '", $closure);
+ if ($i = count($v) - 1) {
+ $w = array();
+
+ while ($i) {
+ $j = $c = 0;
+ $l = strlen($v[$i]);
+
+ while ($j < $l) {
+ switch ($v[$i][$j]) {
+ case '(': case '[': case '{':
+ ++$c;
+ break;
+
+ case ')': case ']': case '}':
+ if ($c-- <= 0) {
+ break 2;
+ }
+ break;
+
+ case ';': case "\n":
+ if (!$c) {
+ break 2;
+ }
+
+ default:
+ $c || $w[] = $v[$i][$j];
+ }
+
+ ++$j;
+ }
+
+ $w[] = ',';
+ --$i;
+ }
+
+ $v = explode(',', implode('', $w));
+ foreach ($v as $w) {
+ if (preg_match("'^{$this->varRx}'", $w, $v)) {
+ isset($vars[$v[0]]) || $vars[$v[0]] = 0;
+ }
+ }
+ }
+
+ if (preg_match_all("@function ({$this->varRx})//''\"\"#@", $closure, $v)) {
+ foreach ($v[1] as $w) {
+ isset($vars[$w]) || $vars[$w] = 0;
+ }
+ }
+
+ if ($this->argFreq[-1] && preg_match_all("@}catch\(({$this->varRx})@", $closure, $v)) {
+ $v[0] = array();
+ foreach ($v[1] as $w) {
+ isset($v[0][$w]) ? ++$v[0][$w] : $v[0][$w] = 1;
+ }
+ foreach ($v[0] as $w => $v) {
+ $vars[$w] = $this->argFreq[-1] - $v;
+ }
+ }
+
+ // Get all used vars, local and non-local
+
+ $vars = &$tree['used'];
+
+ if (preg_match_all("#([.,{]?(?:[gs]et )?)(?<![a-zA-Z0-9_\$])({$this->varRx})(:?)#", $closure, $w, PREG_SET_ORDER)) {
+ foreach ($w as $k) {
+ if (isset($k[1][0]) && (',' === $k[1][0] || '{' === $k[1][0])) {
+ if (':' === $k[3]) {
+ $k = '.'.$k[2];
+ } elseif ('get ' === substr($k[1], 1, 4) || 'set ' === substr($k[1], 1, 4)) {
+ ++$this->charFreq[ord($k[1][1])]; // "g" or "s"
+ ++$this->charFreq[101]; // "e"
+ ++$this->charFreq[116]; // "t"
+ $k = '.'.$k[2];
+ } else {
+ $k = $k[2];
+ }
+ } else {
+ $k = $k[1].$k[2];
+ }
+
+ isset($vars[$k]) ? ++$vars[$k] : $vars[$k] = 1;
+ }
+ }
+
+ if (preg_match_all("#//''\"\"[0-9]+(?:['!]|/')#", $closure, $w)) {
+ foreach ($w[0] as $a) {
+ $v = "'" === substr($a, -1) && "/'" !== substr($a, -2) && $this->specialVarRx
+ ? preg_split("#([.,{]?(?:[gs]et )?(?<![a-zA-Z0-9_\$@]){$this->specialVarRx}:?)#", $this->strings[$a], -1, PREG_SPLIT_DELIM_CAPTURE)
+ : array($this->strings[$a]);
+ $a = count($v);
+
+ for ($i = 0; $i < $a; ++$i) {
+ $k = $v[$i];
+
+ if (1 === $i % 2) {
+ if (',' === $k[0] || '{' === $k[0]) {
+ if (':' === substr($k, -1)) {
+ $k = '.'.substr($k, 1, -1);
+ } elseif ('get ' === substr($k, 1, 4) || 'set ' === substr($k, 1, 4)) {
+ ++$this->charFreq[ord($k[1])]; // "g" or "s"
+ ++$this->charFreq[101]; // "e"
+ ++$this->charFreq[116]; // "t"
+ $k = '.'.substr($k, 5);
+ } else {
+ $k = substr($k, 1);
+ }
+ } elseif (':' === substr($k, -1)) {
+ $k = substr($k, 0, -1);
+ }
+
+ $w = &$tree;
+
+ while (isset($w['parent']) && !(isset($w['used'][$k]) || isset($w['local'][$k]))) {
+ $w = &$w['parent'];
+ }
+
+ (isset($w['used'][$k]) || isset($w['local'][$k])) && (isset($vars[$k]) ? ++$vars[$k] : $vars[$k] = 1);
+
+ unset($w);
+ }
+
+ if (0 === $i % 2 || !isset($vars[$k])) {
+ foreach (count_chars($v[$i], 1) as $k => $w) {
+ $this->charFreq[$k] += $w;
+ }
+ }
+ }
+ }
+ }
+
+ // Propagate the usage number to parents
+
+ foreach ($vars as $w => $a) {
+ $k = &$tree;
+ $chain = array();
+ do {
+ $vars = &$k['local'];
+ $chain[] = &$k;
+ if (isset($vars[$w])) {
+ unset($k['used'][$w]);
+ if (isset($vars[$w])) {
+ $vars[$w] += $a;
+ } else {
+ $vars[$w] = $a;
+ }
+ $a = false;
+ break;
+ }
+ } while ($k['parent'] && $k = &$k['parent']);
+
+ if ($a && !$k['parent']) {
+ if (isset($vars[$w])) {
+ $vars[$w] += $a;
+ } else {
+ $vars[$w] = $a;
+ }
+ }
+
+ if (isset($tree['used'][$w]) && isset($vars[$w])) {
+ foreach ($chain as &$b) {
+ isset($b['local'][$w]) || $b['used'][$w] = &$vars[$w];
+ }
+ }
+ }
+
+ // Analyse childs
+
+ $tree['childs'] = array();
+ $vars = &$tree['childs'];
+
+ if (preg_match_all("@//''\"\"#[0-9]+'@", $closure, $w)) {
+ foreach ($w[0] as $a) {
+ $vars[$a] = array('parent' => &$tree);
+ $this->makeVars($this->closures[$a], $vars[$a], $a);
+ }
+ }
+ }
+
+ protected function mergeVarDeclarations($m)
+ {
+ return str_replace("\nvar ", ',', $m[0]);
+ }
+
+ protected function renameVars(&$tree, $root)
+ {
+ if ($root) {
+ $tree['local'] += $tree['used'];
+ $tree['used'] = array();
+
+ foreach ($tree['local'] as $k => $v) {
+ if ('.' == $k[0]) {
+ $k = substr($k, 1);
+ }
+
+ if ('true' === $k) {
+ $this->charFreq[48] += $v;
+ } elseif ('false' === $k) {
+ $this->charFreq[49] += $v;
+ } elseif (!$this->specialVarRx || !preg_match("#^{$this->specialVarRx}$#", $k)) {
+ foreach (count_chars($k, 1) as $k => $w) {
+ $this->charFreq[$k] += $w * $v;
+ }
+ } elseif (2 == strlen($k)) {
+ $tree['used'][] = $k[1];
+ }
+ }
+
+ $this->charFreq = $this->rsort($this->charFreq);
+
+ $this->str0 = '';
+ $this->str1 = '';
+
+ foreach ($this->charFreq as $k => $v) {
+ if (!$v) {
+ break;
+ }
+
+ $v = chr($k);
+
+ if ((64 < $k && $k < 91) || (96 < $k && $k < 123)) { // A-Z a-z
+ $this->str0 .= $v;
+ $this->str1 .= $v;
+ } elseif (47 < $k && $k < 58) { // 0-9
+ $this->str1 .= $v;
+ }
+ }
+
+ if ('' === $this->str0) {
+ $this->str0 = 'claspemitdbfrugnjvhowkxqyzCLASPEMITDBFRUGNJVHOWKXQYZ';
+ $this->str1 = $this->str0.'0123456789';
+ }
+
+ foreach ($tree['local'] as $var => $root) {
+ if ('.' != substr($var, 0, 1) && isset($tree['local'][".{$var}"])) {
+ $tree['local'][$var] += $tree['local'][".{$var}"];
+ }
+ }
+
+ foreach ($tree['local'] as $var => $root) {
+ if ('.' == substr($var, 0, 1) && isset($tree['local'][substr($var, 1)])) {
+ $tree['local'][$var] = $tree['local'][substr($var, 1)];
+ }
+ }
+
+ $tree['local'] = $this->rsort($tree['local']);
+
+ foreach ($tree['local'] as $var => $root) {
+ switch (substr($var, 0, 1)) {
+ case '.':
+ if (!isset($tree['local'][substr($var, 1)])) {
+ $tree['local'][$var] = '#'.($this->specialVarRx && 3 < strlen($var) && preg_match("'^\.{$this->specialVarRx}$'", $var) ? $this->getNextName($tree).'$' : substr($var, 1));
+ }
+ break;
+
+ case ';': $tree['local'][$var] = 0 === $root ? '' : $this->getNextName($tree);
+ case '#': break;
+
+ default:
+ $root = $this->specialVarRx && 2 < strlen($var) && preg_match("'^{$this->specialVarRx}$'", $var) ? $this->getNextName($tree).'$' : $var;
+ $tree['local'][$var] = $root;
+ if (isset($tree['local'][".{$var}"])) {
+ $tree['local'][".{$var}"] = '#'.$root;
+ }
+ }
+ }
+
+ foreach ($tree['local'] as $var => $root) {
+ $tree['local'][$var] = preg_replace("'^#'", '.', $tree['local'][$var]);
+ }
+ } else {
+ $tree['local'] = $this->rsort($tree['local']);
+ if (false !== $tree['nfe']) {
+ $tree['used'][] = $tree['local'][$tree['nfe']];
+ }
+
+ foreach ($tree['local'] as $var => $root) {
+ if ($tree['nfe'] !== $var) {
+ $tree['local'][$var] = 0 === $root ? '' : $this->getNextName($tree);
+ }
+ }
+ }
+
+ $this->local_tree = &$tree['local'];
+ $this->used_tree = &$tree['used'];
+
+ $tree['code'] = preg_replace_callback("#[.,{ ]?(?:[gs]et )?(?<![a-zA-Z0-9_\$@]){$this->varRx}:?#", array($this, 'getNewName'), $tree['code']);
+
+ if ($this->specialVarRx && preg_match_all("#//''\"\"[0-9]+'#", $tree['code'], $b)) {
+ foreach ($b[0] as $a) {
+ $this->strings[$a] = preg_replace_callback(
+ "#[.,{]?(?:[gs]et )?(?<![a-zA-Z0-9_\$@]){$this->specialVarRx}:?#",
+ array($this, 'getNewName'),
+ $this->strings[$a]
+ );
+ }
+ }
+
+ foreach ($tree['childs'] as $a => &$b) {
+ $this->renameVars($b, false);
+ $tree['code'] = str_replace($a, $b['code'], $tree['code']);
+ unset($tree['childs'][$a]);
+ }
+ }
+
+ protected function getNewName($m)
+ {
+ $m = $m[0];
+
+ $pre = '.' === $m[0] ? '.' : '';
+ $post = '';
+
+ if (',' === $m[0] || '{' === $m[0] || ' ' === $m[0]) {
+ $pre = $m[0];
+
+ if (':' === substr($m, -1)) {
+ $post = ':';
+ $m = (' ' !== $m[0] ? '.' : '').substr($m, 1, -1);
+ } elseif ('get ' === substr($m, 1, 4) || 'set ' === substr($m, 1, 4)) {
+ $pre .= substr($m, 1, 4);
+ $m = '.'.substr($m, 5);
+ } else {
+ $m = substr($m, 1);
+ }
+ } elseif (':' === substr($m, -1)) {
+ $post = ':';
+ $m = substr($m, 0, -1);
+ }
+
+ $post = (isset($this->reserved[$m])
+ ? ('true' === $m ? '!0' : ('false' === $m ? '!1' : $m))
+ : (
+ isset($this->local_tree[$m])
+ ? $this->local_tree[$m]
+ : (
+ isset($this->used_tree[$m])
+ ? $this->used_tree[$m]
+ : $m
+ )
+ )
+ ).$post;
+
+ return '' === $post ? '' : ($pre.('.' === $post[0] ? substr($post, 1) : $post));
+ }
+
+ protected function getNextName(&$tree = array(), &$counter = false)
+ {
+ if (false === $counter) {
+ $counter = &$tree['counter'];
+ isset($counter) || $counter = -1;
+ $exclude = array_flip($tree['used']);
+ } else {
+ $exclude = $tree;
+ }
+
+ ++$counter;
+
+ $len0 = strlen($this->str0);
+ $len1 = strlen($this->str0);
+
+ $name = $this->str0[$counter % $len0];
+
+ $i = intval($counter / $len0) - 1;
+ while ($i >= 0) {
+ $name .= $this->str1[ $i % $len1 ];
+ $i = intval($i / $len1) - 1;
+ }
+
+ return !(isset($this->reserved[$name]) || isset($exclude[$name])) ? $name : $this->getNextName($exclude, $counter);
+ }
+
+ protected function restoreCc(&$s, $lf = true)
+ {
+ $lf && $s = str_replace('@#3', '', $s);
+
+ $s = str_replace('@#1', '@*/', $s);
+ $s = str_replace('2#@', '//@', $s);
+ $s = str_replace('1#@', '/*@', $s);
+ $s = str_replace('##', '#', $s);
+ }
+
+ private function rsort($array)
+ {
+ if (!$array) {
+ return $array;
+ }
+
+ $i = 0;
+ $tuples = array();
+ foreach ($array as $k => &$v) {
+ $tuples[] = array(++$i, $k, &$v);
+ }
+
+ usort($tuples, function ($a, $b) {
+ if ($b[2] > $a[2]) {
+ return 1;
+ }
+ if ($b[2] < $a[2]) {
+ return -1;
+ }
+ if ($b[0] > $a[0]) {
+ return -1;
+ }
+ if ($b[0] < $a[0]) {
+ return 1;
+ }
+
+ return 0;
+ });
+
+ $array = array();
+
+ foreach ($tuples as $t) {
+ $array[$t[1]] = &$t[2];
+ }
+
+ return $array;
+ }
+}
+?>+
\ No newline at end of file
diff --git a/modules/util/Ldap.class.php b/modules/util/Ldap.class.php
@@ -0,0 +1,182 @@
+<?php
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+/**
+ * Bereitstellen von LDAP-Funktionen.
+ * @author $Author$
+ * @version $Revision$
+ * @package openrat.services
+ */
+class Ldap
+{
+ var $connection;
+ var $timeout;
+ var $aliases;
+
+
+ /**
+ *
+ */
+ function Ldap()
+ {
+ global $conf;
+
+ $this->timeout = intval($conf['ldap']['search']['timeout']);
+
+ if ( $conf['ldap']['search']['aliases'] )
+ $this->aliases = LDAP_DEREF_ALWAYS;
+ else
+ $this->aliases = LDAP_DEREF_NEVER;
+ }
+
+
+
+ /**
+ * Verbindung �ffnen.
+ */
+ function connect()
+ {
+ global $conf;
+
+ $ldapHost = $conf['ldap']['host'];
+ $ldapPort = $conf['ldap']['port'];
+
+ // Verbindung zum LDAP-Server herstellen
+ $this->connection = @ldap_connect( $ldapHost,$ldapPort );
+
+ // siehe http://bugs.php.net/bug.php?id=15637
+ // Unter bestimmten Bedingungen wird trotz nicht erreichbarem LDAP-Server eine PHP-Resource
+ // zurueck gegeben. Dann erscheint zwar keine Fehlermeldung, aber zumindestens misslingt
+ // der nachfolgende Bind-Befehl.
+ if ( !is_resource($this->connection) || $this->connection === false )
+ {
+ Logger::error( "connect to ldap server '$ldapHost:$ldapPort' failed" );
+ // Abbruch, wenn LDAP-Server nicht erreichbar
+ die( "Connection failed to $ldapHost:$ldapPort (".ldap_errno().'/'.ldap_error().'). Please contact your administrator.' );
+ }
+
+ // Protokollversion setzen.
+ $j = ldap_set_option( $this->connection, LDAP_OPT_PROTOCOL_VERSION,intval($conf['ldap']['protocol']) );
+ if ( ! $j )
+ die( 'LDAP error while setting protocol version'.ldap_errno().'/'.ldap_error().')' );
+
+ }
+
+
+
+ /**
+ * Ein Binding auf den LDAP-Server durchf�hren.
+ */
+ function bind( $user,$pw )
+ {
+ return @ldap_bind( $this->connection,$user,$pw);
+ }
+
+
+
+ /**
+ * Ein Binding auf den LDAP-Server durchf�hren.
+ */
+ function bindAnonymous()
+ {
+ return @ldap_bind( $this->connection );
+ }
+
+
+
+ /**
+ * Das Bindung wird entfernt.
+ */
+ function unbind()
+ {
+ ldap_unbind( $this->connection );
+ }
+
+
+
+ /**
+ * Eine Suche auf den LDAP-Server durchf�hren.
+ */
+ function searchUser( $username )
+ {
+ global $conf;
+
+ $techUser = $conf['ldap']['search']['user'];
+ $techPass = $conf['ldap']['search']['password'];
+
+ if ( $conf['ldap']['search']['anonymous'] )
+ $this->bindAnonymous();
+ else
+ $this->bind( $techUser, $techPass );
+
+ $dn = $conf['ldap']['search']['basedn'];
+ $filter = $conf['ldap']['search']['filter'];
+ $filter = str_replace('{user}', $username, $filter);
+
+ $s = @ldap_search( $this->connection,$dn,$filter,array(),0,1,$this->timeout,$this->aliases );
+
+ if ( ! is_resource($s) )
+ return null;
+
+ $dn = @ldap_get_dn($this->connection, ldap_first_entry($this->connection,$s) );
+
+ return $dn;
+ }
+
+
+
+ /**
+ * Ein Binding auf den LDAP-Server durchf�hren.
+ */
+ function searchAttribute( $filter,$attr )
+ {
+ global $conf;
+
+ $timeout = intval($conf['ldap']['search']['timeout']);
+
+ if ( $conf['ldap']['search']['aliases'] )
+ $aliases = LDAP_DEREF_ALWAYS;
+ else
+ $aliases = LDAP_DEREF_NEVER;
+
+
+ $base_dn = $conf['ldap']['search']['basedn'];
+ $s = ldap_search( $this->connection,$base_dn,$filter,array(),0,0,$this->timeout,$this->aliases );
+ $ergebnisse = ldap_get_entries($this->connection,$s);
+
+ $liste = array();
+// Html::debug($ergebnisse);
+ for( $i=0; $i<=$ergebnisse['count']-1; $i++ )
+ $liste[] = $ergebnisse[$i][$attr][0];
+
+ return $liste;
+ }
+
+
+
+ /**
+ * Verbindung schlie�en.
+ */
+ function close()
+ {
+ // Verbindung zum LDAP-Server brav beenden
+ ldap_close( $this->connection );
+ }
+}
+
+?>+
\ No newline at end of file
diff --git a/modules/util/Less.php b/modules/util/Less.php
@@ -0,0 +1,10524 @@
+<?php
+
+/**
+ * Class for parsing and compiling less files into css
+ *
+ * @package Less
+ * @subpackage parser
+ *
+ */
+class Less_Parser{
+
+
+ /**
+ * Default parser options
+ */
+ public static $default_options = array(
+ 'compress' => false, // option - whether to compress
+ 'strictUnits' => false, // whether units need to evaluate correctly
+ 'strictMath' => false, // whether math has to be within parenthesis
+ 'relativeUrls' => true, // option - whether to adjust URL's to be relative
+ 'urlArgs' => '', // whether to add args into url tokens
+ 'numPrecision' => 8,
+
+ 'import_dirs' => array(),
+ 'import_callback' => null,
+ 'cache_dir' => null,
+ 'cache_method' => 'php', // false, 'serialize', 'php', 'var_export', 'callback';
+ 'cache_callback_get' => null,
+ 'cache_callback_set' => null,
+
+ 'sourceMap' => false, // whether to output a source map
+ 'sourceMapBasepath' => null,
+ 'sourceMapWriteTo' => null,
+ 'sourceMapURL' => null,
+
+ 'indentation' => ' ',
+
+ 'plugins' => array(),
+
+ );
+
+ public static $options = array();
+
+
+ private $input; // Less input string
+ private $input_len; // input string length
+ private $pos; // current index in `input`
+ private $saveStack = array(); // holds state for backtracking
+ private $furthest;
+ private $mb_internal_encoding = ''; // for remember exists value of mbstring.internal_encoding
+
+ /**
+ * @var Less_Environment
+ */
+ private $env;
+
+ protected $rules = array();
+
+ private static $imports = array();
+
+ public static $has_extends = false;
+
+ public static $next_id = 0;
+
+ /**
+ * Filename to contents of all parsed the files
+ *
+ * @var array
+ */
+ public static $contentsMap = array();
+
+
+ /**
+ * @param Less_Environment|array|null $env
+ */
+ public function __construct( $env = null ){
+
+ // Top parser on an import tree must be sure there is one "env"
+ // which will then be passed around by reference.
+ if( $env instanceof Less_Environment ){
+ $this->env = $env;
+ }else{
+ $this->SetOptions(Less_Parser::$default_options);
+ $this->Reset( $env );
+ }
+
+ // mbstring.func_overload > 1 bugfix
+ // The encoding value must be set for each source file,
+ // therefore, to conserve resources and improve the speed of this design is taken here
+ if (ini_get('mbstring.func_overload')) {
+ $this->mb_internal_encoding = ini_get('mbstring.internal_encoding');
+ @ini_set('mbstring.internal_encoding', 'ascii');
+ }
+
+ }
+
+
+ /**
+ * Reset the parser state completely
+ *
+ */
+ public function Reset( $options = null ){
+ $this->rules = array();
+ self::$imports = array();
+ self::$has_extends = false;
+ self::$imports = array();
+ self::$contentsMap = array();
+
+ $this->env = new Less_Environment($options);
+ $this->env->Init();
+
+ //set new options
+ if( is_array($options) ){
+ $this->SetOptions(Less_Parser::$default_options);
+ $this->SetOptions($options);
+ }
+ }
+
+ /**
+ * Set one or more compiler options
+ * options: import_dirs, cache_dir, cache_method
+ *
+ */
+ public function SetOptions( $options ){
+ foreach($options as $option => $value){
+ $this->SetOption($option,$value);
+ }
+ }
+
+ /**
+ * Set one compiler option
+ *
+ */
+ public function SetOption($option,$value){
+
+ switch($option){
+
+ case 'import_dirs':
+ $this->SetImportDirs($value);
+ return;
+
+ case 'cache_dir':
+ if( is_string($value) ){
+ Less_Cache::SetCacheDir($value);
+ Less_Cache::CheckCacheDir();
+ }
+ return;
+ }
+
+ Less_Parser::$options[$option] = $value;
+ }
+
+ /**
+ * Registers a new custom function
+ *
+ * @param string $name function name
+ * @param callable $callback callback
+ */
+ public function registerFunction($name, $callback) {
+ $this->env->functions[$name] = $callback;
+ }
+
+ /**
+ * Removed an already registered function
+ *
+ * @param string $name function name
+ */
+ public function unregisterFunction($name) {
+ if( isset($this->env->functions[$name]) )
+ unset($this->env->functions[$name]);
+ }
+
+
+ /**
+ * Get the current css buffer
+ *
+ * @return string
+ */
+ public function getCss(){
+
+ $precision = ini_get('precision');
+ @ini_set('precision',16);
+ $locale = setlocale(LC_NUMERIC, 0);
+ setlocale(LC_NUMERIC, "C");
+
+ try {
+
+ $root = new Less_Tree_Ruleset(array(), $this->rules );
+ $root->root = true;
+ $root->firstRoot = true;
+
+
+ $this->PreVisitors($root);
+
+ self::$has_extends = false;
+ $evaldRoot = $root->compile($this->env);
+
+
+
+ $this->PostVisitors($evaldRoot);
+
+ if( Less_Parser::$options['sourceMap'] ){
+ $generator = new Less_SourceMap_Generator($evaldRoot, Less_Parser::$contentsMap, Less_Parser::$options );
+ // will also save file
+ // FIXME: should happen somewhere else?
+ $css = $generator->generateCSS();
+ }else{
+ $css = $evaldRoot->toCSS();
+ }
+
+ if( Less_Parser::$options['compress'] ){
+ $css = preg_replace('/(^(\s)+)|((\s)+$)/', '', $css);
+ }
+
+ } catch (Exception $exc) {
+ // Intentional fall-through so we can reset environment
+ }
+
+ //reset php settings
+ @ini_set('precision',$precision);
+ setlocale(LC_NUMERIC, $locale);
+
+ // If you previously defined $this->mb_internal_encoding
+ // is required to return the encoding as it was before
+ if ($this->mb_internal_encoding != '') {
+ @ini_set("mbstring.internal_encoding", $this->mb_internal_encoding);
+ $this->mb_internal_encoding = '';
+ }
+
+ // Rethrow exception after we handled resetting the environment
+ if (!empty($exc)) {
+ throw $exc;
+ }
+
+
+
+ return $css;
+ }
+
+ /**
+ * Run pre-compile visitors
+ *
+ */
+ private function PreVisitors($root){
+
+ if( Less_Parser::$options['plugins'] ){
+ foreach(Less_Parser::$options['plugins'] as $plugin){
+ if( !empty($plugin->isPreEvalVisitor) ){
+ $plugin->run($root);
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Run post-compile visitors
+ *
+ */
+ private function PostVisitors($evaldRoot){
+
+ $visitors = array();
+ $visitors[] = new Less_Visitor_joinSelector();
+ if( self::$has_extends ){
+ $visitors[] = new Less_Visitor_processExtends();
+ }
+ $visitors[] = new Less_Visitor_toCSS();
+
+
+ if( Less_Parser::$options['plugins'] ){
+ foreach(Less_Parser::$options['plugins'] as $plugin){
+ if( property_exists($plugin,'isPreEvalVisitor') && $plugin->isPreEvalVisitor ){
+ continue;
+ }
+
+ if( property_exists($plugin,'isPreVisitor') && $plugin->isPreVisitor ){
+ array_unshift( $visitors, $plugin);
+ }else{
+ $visitors[] = $plugin;
+ }
+ }
+ }
+
+
+ for($i = 0; $i < count($visitors); $i++ ){
+ $visitors[$i]->run($evaldRoot);
+ }
+
+ }
+
+
+ /**
+ * Parse a Less string into css
+ *
+ * @param string $str The string to convert
+ * @param string $uri_root The url of the file
+ * @return Less_Tree_Ruleset|Less_Parser
+ */
+ public function parse( $str, $file_uri = null ){
+
+ if( !$file_uri ){
+ $uri_root = '';
+ $filename = 'anonymous-file-'.Less_Parser::$next_id++.'.less';
+ }else{
+ $file_uri = self::WinPath($file_uri);
+ $filename = $file_uri;
+ $uri_root = dirname($file_uri);
+ }
+
+ $previousFileInfo = $this->env->currentFileInfo;
+ $uri_root = self::WinPath($uri_root);
+ $this->SetFileInfo($filename, $uri_root);
+
+ $this->input = $str;
+ $this->_parse();
+
+ if( $previousFileInfo ){
+ $this->env->currentFileInfo = $previousFileInfo;
+ }
+
+ return $this;
+ }
+
+
+ /**
+ * Parse a Less string from a given file
+ *
+ * @throws Less_Exception_Parser
+ * @param string $filename The file to parse
+ * @param string $uri_root The url of the file
+ * @param bool $returnRoot Indicates whether the return value should be a css string a root node
+ * @return Less_Tree_Ruleset|Less_Parser
+ */
+ public function parseFile( $filename, $uri_root = '', $returnRoot = false){
+
+ if( !file_exists($filename) ){
+ $this->Error(sprintf('File `%s` not found.', $filename));
+ }
+
+
+ // fix uri_root?
+ // Instead of The mixture of file path for the first argument and directory path for the second argument has bee
+ if( !$returnRoot && !empty($uri_root) && basename($uri_root) == basename($filename) ){
+ $uri_root = dirname($uri_root);
+ }
+
+
+ $previousFileInfo = $this->env->currentFileInfo;
+
+
+ if( $filename ){
+ $filename = self::WinPath(realpath($filename));
+ }
+ $uri_root = self::WinPath($uri_root);
+
+ $this->SetFileInfo($filename, $uri_root);
+
+ self::AddParsedFile($filename);
+
+ if( $returnRoot ){
+ $rules = $this->GetRules( $filename );
+ $return = new Less_Tree_Ruleset(array(), $rules );
+ }else{
+ $this->_parse( $filename );
+ $return = $this;
+ }
+
+ if( $previousFileInfo ){
+ $this->env->currentFileInfo = $previousFileInfo;
+ }
+
+ return $return;
+ }
+
+
+ /**
+ * Allows a user to set variables values
+ * @param array $vars
+ * @return Less_Parser
+ */
+ public function ModifyVars( $vars ){
+
+ $this->input = Less_Parser::serializeVars( $vars );
+ $this->_parse();
+
+ return $this;
+ }
+
+
+ /**
+ * @param string $filename
+ */
+ public function SetFileInfo( $filename, $uri_root = ''){
+
+ $filename = Less_Environment::normalizePath($filename);
+ $dirname = preg_replace('/[^\/\\\\]*$/','',$filename);
+
+ if( !empty($uri_root) ){
+ $uri_root = rtrim($uri_root,'/').'/';
+ }
+
+ $currentFileInfo = array();
+
+ //entry info
+ if( isset($this->env->currentFileInfo) ){
+ $currentFileInfo['entryPath'] = $this->env->currentFileInfo['entryPath'];
+ $currentFileInfo['entryUri'] = $this->env->currentFileInfo['entryUri'];
+ $currentFileInfo['rootpath'] = $this->env->currentFileInfo['rootpath'];
+
+ }else{
+ $currentFileInfo['entryPath'] = $dirname;
+ $currentFileInfo['entryUri'] = $uri_root;
+ $currentFileInfo['rootpath'] = $dirname;
+ }
+
+ $currentFileInfo['currentDirectory'] = $dirname;
+ $currentFileInfo['currentUri'] = $uri_root.basename($filename);
+ $currentFileInfo['filename'] = $filename;
+ $currentFileInfo['uri_root'] = $uri_root;
+
+
+ //inherit reference
+ if( isset($this->env->currentFileInfo['reference']) && $this->env->currentFileInfo['reference'] ){
+ $currentFileInfo['reference'] = true;
+ }
+
+ $this->env->currentFileInfo = $currentFileInfo;
+ }
+
+
+ /**
+ * @deprecated 1.5.1.2
+ *
+ */
+ public function SetCacheDir( $dir ){
+
+ if( !file_exists($dir) ){
+ if( mkdir($dir) ){
+ return true;
+ }
+ throw new Less_Exception_Parser('Less.php cache directory couldn\'t be created: '.$dir);
+
+ }elseif( !is_dir($dir) ){
+ throw new Less_Exception_Parser('Less.php cache directory doesn\'t exist: '.$dir);
+
+ }elseif( !is_writable($dir) ){
+ throw new Less_Exception_Parser('Less.php cache directory isn\'t writable: '.$dir);
+
+ }else{
+ $dir = self::WinPath($dir);
+ Less_Cache::$cache_dir = rtrim($dir,'/').'/';
+ return true;
+ }
+ }
+
+
+ /**
+ * Set a list of directories or callbacks the parser should use for determining import paths
+ *
+ * @param array $dirs
+ */
+ public function SetImportDirs( $dirs ){
+ Less_Parser::$options['import_dirs'] = array();
+
+ foreach($dirs as $path => $uri_root){
+
+ $path = self::WinPath($path);
+ if( !empty($path) ){
+ $path = rtrim($path,'/').'/';
+ }
+
+ if ( !is_callable($uri_root) ){
+ $uri_root = self::WinPath($uri_root);
+ if( !empty($uri_root) ){
+ $uri_root = rtrim($uri_root,'/').'/';
+ }
+ }
+
+ Less_Parser::$options['import_dirs'][$path] = $uri_root;
+ }
+ }
+
+ /**
+ * @param string $file_path
+ */
+ private function _parse( $file_path = null ){
+ $this->rules = array_merge($this->rules, $this->GetRules( $file_path ));
+ }
+
+
+ /**
+ * Return the results of parsePrimary for $file_path
+ * Use cache and save cached results if possible
+ *
+ * @param string|null $file_path
+ */
+ private function GetRules( $file_path ){
+
+ $this->SetInput($file_path);
+
+ $cache_file = $this->CacheFile( $file_path );
+ if( $cache_file ){
+ if( Less_Parser::$options['cache_method'] == 'callback' ){
+ if( is_callable(Less_Parser::$options['cache_callback_get']) ){
+ $cache = call_user_func_array(
+ Less_Parser::$options['cache_callback_get'],
+ array($this, $file_path, $cache_file)
+ );
+
+ if( $cache ){
+ $this->UnsetInput();
+ return $cache;
+ }
+ }
+
+ }elseif( file_exists($cache_file) ){
+ switch(Less_Parser::$options['cache_method']){
+
+ // Using serialize
+ // Faster but uses more memory
+ case 'serialize':
+ $cache = unserialize(file_get_contents($cache_file));
+ if( $cache ){
+ touch($cache_file);
+ $this->UnsetInput();
+ return $cache;
+ }
+ break;
+
+
+ // Using generated php code
+ case 'var_export':
+ case 'php':
+ $this->UnsetInput();
+ return include($cache_file);
+ }
+ }
+ }
+
+ $rules = $this->parsePrimary();
+
+ if( $this->pos < $this->input_len ){
+ throw new Less_Exception_Chunk($this->input, null, $this->furthest, $this->env->currentFileInfo);
+ }
+
+ $this->UnsetInput();
+
+
+ //save the cache
+ if( $cache_file ){
+ if( Less_Parser::$options['cache_method'] == 'callback' ){
+ if( is_callable(Less_Parser::$options['cache_callback_set']) ){
+ call_user_func_array(
+ Less_Parser::$options['cache_callback_set'],
+ array($this, $file_path, $cache_file, $rules)
+ );
+ }
+
+ }else{
+ //msg('write cache file');
+ switch(Less_Parser::$options['cache_method']){
+ case 'serialize':
+ file_put_contents( $cache_file, serialize($rules) );
+ break;
+ case 'php':
+ file_put_contents( $cache_file, '<?php return '.self::ArgString($rules).'; ?>' );
+ break;
+ case 'var_export':
+ //Requires __set_state()
+ file_put_contents( $cache_file, '<?php return '.var_export($rules,true).'; ?>' );
+ break;
+ }
+
+ Less_Cache::CleanCache();
+ }
+ }
+
+ return $rules;
+ }
+
+
+ /**
+ * Set up the input buffer
+ *
+ */
+ public function SetInput( $file_path ){
+
+ if( $file_path ){
+ $this->input = file_get_contents( $file_path );
+ }
+
+ $this->pos = $this->furthest = 0;
+
+ // Remove potential UTF Byte Order Mark
+ $this->input = preg_replace('/\\G\xEF\xBB\xBF/', '', $this->input);
+ $this->input_len = strlen($this->input);
+
+
+ if( Less_Parser::$options['sourceMap'] && $this->env->currentFileInfo ){
+ $uri = $this->env->currentFileInfo['currentUri'];
+ Less_Parser::$contentsMap[$uri] = $this->input;
+ }
+
+ }
+
+
+ /**
+ * Free up some memory
+ *
+ */
+ public function UnsetInput(){
+ unset($this->input, $this->pos, $this->input_len, $this->furthest);
+ $this->saveStack = array();
+ }
+
+
+ public function CacheFile( $file_path ){
+
+ if( $file_path && $this->CacheEnabled() ){
+
+ $env = get_object_vars($this->env);
+ unset($env['frames']);
+
+ $parts = array();
+ $parts[] = $file_path;
+ $parts[] = filesize( $file_path );
+ $parts[] = filemtime( $file_path );
+ $parts[] = $env;
+ $parts[] = Less_Version::cache_version;
+ $parts[] = Less_Parser::$options['cache_method'];
+ return Less_Cache::$cache_dir . Less_Cache::$prefix . base_convert( sha1(json_encode($parts) ), 16, 36) . '.lesscache';
+ }
+ }
+
+
+ static function AddParsedFile($file){
+ self::$imports[] = $file;
+ }
+
+ static function AllParsedFiles(){
+ return self::$imports;
+ }
+
+ /**
+ * @param string $file
+ */
+ static function FileParsed($file){
+ return in_array($file,self::$imports);
+ }
+
+
+ function save() {
+ $this->saveStack[] = $this->pos;
+ }
+
+ private function restore() {
+ $this->pos = array_pop($this->saveStack);
+ }
+
+ private function forget(){
+ array_pop($this->saveStack);
+ }
+
+
+ private function isWhitespace($offset = 0) {
+ return preg_match('/\s/',$this->input[ $this->pos + $offset]);
+ }
+
+ /**
+ * Parse from a token, regexp or string, and move forward if match
+ *
+ * @param array $toks
+ * @return array
+ */
+ private function match($toks){
+
+ // The match is confirmed, add the match length to `this::pos`,
+ // and consume any extra white-space characters (' ' || '\n')
+ // which come after that. The reason for this is that LeSS's
+ // grammar is mostly white-space insensitive.
+ //
+
+ foreach($toks as $tok){
+
+ $char = $tok[0];
+
+ if( $char === '/' ){
+ $match = $this->MatchReg($tok);
+
+ if( $match ){
+ return count($match) === 1 ? $match[0] : $match;
+ }
+
+ }elseif( $char === '#' ){
+ $match = $this->MatchChar($tok[1]);
+
+ }else{
+ // Non-terminal, match using a function call
+ $match = $this->$tok();
+
+ }
+
+ if( $match ){
+ return $match;
+ }
+ }
+ }
+
+ /**
+ * @param string[] $toks
+ *
+ * @return string
+ */
+ private function MatchFuncs($toks){
+
+ if( $this->pos < $this->input_len ){
+ foreach($toks as $tok){
+ $match = $this->$tok();
+ if( $match ){
+ return $match;
+ }
+ }
+ }
+
+ }
+
+ // Match a single character in the input,
+ private function MatchChar($tok){
+ if( ($this->pos < $this->input_len) && ($this->input[$this->pos] === $tok) ){
+ $this->skipWhitespace(1);
+ return $tok;
+ }
+ }
+
+ // Match a regexp from the current start point
+ private function MatchReg($tok){
+
+ if( preg_match($tok, $this->input, $match, 0, $this->pos) ){
+ $this->skipWhitespace(strlen($match[0]));
+ return $match;
+ }
+ }
+
+
+ /**
+ * Same as match(), but don't change the state of the parser,
+ * just return the match.
+ *
+ * @param string $tok
+ * @return integer
+ */
+ public function PeekReg($tok){
+ return preg_match($tok, $this->input, $match, 0, $this->pos);
+ }
+
+ /**
+ * @param string $tok
+ */
+ public function PeekChar($tok){
+ //return ($this->input[$this->pos] === $tok );
+ return ($this->pos < $this->input_len) && ($this->input[$this->pos] === $tok );
+ }
+
+
+ /**
+ * @param integer $length
+ */
+ public function skipWhitespace($length){
+
+ $this->pos += $length;
+
+ for(; $this->pos < $this->input_len; $this->pos++ ){
+ $c = $this->input[$this->pos];
+
+ if( ($c !== "\n") && ($c !== "\r") && ($c !== "\t") && ($c !== ' ') ){
+ break;
+ }
+ }
+ }
+
+
+ /**
+ * @param string $tok
+ * @param string|null $msg
+ */
+ public function expect($tok, $msg = NULL) {
+ $result = $this->match( array($tok) );
+ if (!$result) {
+ $this->Error( $msg ? "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'" : $msg );
+ } else {
+ return $result;
+ }
+ }
+
+ /**
+ * @param string $tok
+ */
+ public function expectChar($tok, $msg = null ){
+ $result = $this->MatchChar($tok);
+ if( !$result ){
+ $this->Error( $msg ? "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'" : $msg );
+ }else{
+ return $result;
+ }
+ }
+
+ //
+ // Here in, the parsing rules/functions
+ //
+ // The basic structure of the syntax tree generated is as follows:
+ //
+ // Ruleset -> Rule -> Value -> Expression -> Entity
+ //
+ // Here's some LESS code:
+ //
+ // .class {
+ // color: #fff;
+ // border: 1px solid #000;
+ // width: @w + 4px;
+ // > .child {...}
+ // }
+ //
+ // And here's what the parse tree might look like:
+ //
+ // Ruleset (Selector '.class', [
+ // Rule ("color", Value ([Expression [Color #fff]]))
+ // Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]]))
+ // Rule ("width", Value ([Expression [Operation "+" [Variable "@w"][Dimension 4px]]]))
+ // Ruleset (Selector [Element '>', '.child'], [...])
+ // ])
+ //
+ // In general, most rules will try to parse a token with the `$()` function, and if the return
+ // value is truly, will return a new node, of the relevant type. Sometimes, we need to check
+ // first, before parsing, that's when we use `peek()`.
+ //
+
+ //
+ // The `primary` rule is the *entry* and *exit* point of the parser.
+ // The rules here can appear at any level of the parse tree.
+ //
+ // The recursive nature of the grammar is an interplay between the `block`
+ // rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule,
+ // as represented by this simplified grammar:
+ //
+ // primary → (ruleset | rule)+
+ // ruleset → selector+ block
+ // block → '{' primary '}'
+ //
+ // Only at one point is the primary rule not called from the
+ // block rule: at the root level.
+ //
+ private function parsePrimary(){
+ $root = array();
+
+ while( true ){
+
+ if( $this->pos >= $this->input_len ){
+ break;
+ }
+
+ $node = $this->parseExtend(true);
+ if( $node ){
+ $root = array_merge($root,$node);
+ continue;
+ }
+
+ //$node = $this->MatchFuncs( array( 'parseMixinDefinition', 'parseRule', 'parseRuleset', 'parseMixinCall', 'parseComment', 'parseDirective'));
+ $node = $this->MatchFuncs( array( 'parseMixinDefinition', 'parseNameValue', 'parseRule', 'parseRuleset', 'parseMixinCall', 'parseComment', 'parseRulesetCall', 'parseDirective'));
+
+ if( $node ){
+ $root[] = $node;
+ }elseif( !$this->MatchReg('/\\G[\s\n;]+/') ){
+ break;
+ }
+
+ if( $this->PeekChar('}') ){
+ break;
+ }
+ }
+
+ return $root;
+ }
+
+
+
+ // We create a Comment node for CSS comments `/* */`,
+ // but keep the LeSS comments `//` silent, by just skipping
+ // over them.
+ private function parseComment(){
+
+ if( $this->input[$this->pos] !== '/' ){
+ return;
+ }
+
+ if( $this->input[$this->pos+1] === '/' ){
+ $match = $this->MatchReg('/\\G\/\/.*/');
+ return $this->NewObj4('Less_Tree_Comment',array($match[0], true, $this->pos, $this->env->currentFileInfo));
+ }
+
+ //$comment = $this->MatchReg('/\\G\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/');
+ $comment = $this->MatchReg('/\\G\/\*(?s).*?\*+\/\n?/');//not the same as less.js to prevent fatal errors
+ if( $comment ){
+ return $this->NewObj4('Less_Tree_Comment',array($comment[0], false, $this->pos, $this->env->currentFileInfo));
+ }
+ }
+
+ private function parseComments(){
+ $comments = array();
+
+ while( $this->pos < $this->input_len ){
+ $comment = $this->parseComment();
+ if( !$comment ){
+ break;
+ }
+
+ $comments[] = $comment;
+ }
+
+ return $comments;
+ }
+
+
+
+ //
+ // A string, which supports escaping " and '
+ //
+ // "milky way" 'he\'s the one!'
+ //
+ private function parseEntitiesQuoted() {
+ $j = $this->pos;
+ $e = false;
+ $index = $this->pos;
+
+ if( $this->input[$this->pos] === '~' ){
+ $j++;
+ $e = true; // Escaped strings
+ }
+
+ if( $this->input[$j] != '"' && $this->input[$j] !== "'" ){
+ return;
+ }
+
+ if ($e) {
+ $this->MatchChar('~');
+ }
+
+ // Fix for #124: match escaped newlines
+ //$str = $this->MatchReg('/\\G"((?:[^"\\\\\r\n]|\\\\.)*)"|\'((?:[^\'\\\\\r\n]|\\\\.)*)\'/');
+ $str = $this->MatchReg('/\\G"((?:[^"\\\\\r\n]|\\\\.|\\\\\r\n|\\\\[\n\r\f])*)"|\'((?:[^\'\\\\\r\n]|\\\\.|\\\\\r\n|\\\\[\n\r\f])*)\'/');
+
+ if( $str ){
+ $result = $str[0][0] == '"' ? $str[1] : $str[2];
+ return $this->NewObj5('Less_Tree_Quoted',array($str[0], $result, $e, $index, $this->env->currentFileInfo) );
+ }
+ return;
+ }
+
+
+ //
+ // A catch-all word, such as:
+ //
+ // black border-collapse
+ //
+ private function parseEntitiesKeyword(){
+
+ //$k = $this->MatchReg('/\\G[_A-Za-z-][_A-Za-z0-9-]*/');
+ $k = $this->MatchReg('/\\G%|\\G[_A-Za-z-][_A-Za-z0-9-]*/');
+ if( $k ){
+ $k = $k[0];
+ $color = $this->fromKeyword($k);
+ if( $color ){
+ return $color;
+ }
+ return $this->NewObj1('Less_Tree_Keyword',$k);
+ }
+ }
+
+ // duplicate of Less_Tree_Color::FromKeyword
+ private function FromKeyword( $keyword ){
+ $keyword = strtolower($keyword);
+
+ if( Less_Colors::hasOwnProperty($keyword) ){
+ // detect named color
+ return $this->NewObj1('Less_Tree_Color',substr(Less_Colors::color($keyword), 1));
+ }
+
+ if( $keyword === 'transparent' ){
+ return $this->NewObj3('Less_Tree_Color', array( array(0, 0, 0), 0, true));
+ }
+ }
+
+ //
+ // A function call
+ //
+ // rgb(255, 0, 255)
+ //
+ // We also try to catch IE's `alpha()`, but let the `alpha` parser
+ // deal with the details.
+ //
+ // The arguments are parsed with the `entities.arguments` parser.
+ //
+ private function parseEntitiesCall(){
+ $index = $this->pos;
+
+ if( !preg_match('/\\G([\w-]+|%|progid:[\w\.]+)\(/', $this->input, $name,0,$this->pos) ){
+ return;
+ }
+ $name = $name[1];
+ $nameLC = strtolower($name);
+
+ if ($nameLC === 'url') {
+ return null;
+ }
+
+ $this->pos += strlen($name);
+
+ if( $nameLC === 'alpha' ){
+ $alpha_ret = $this->parseAlpha();
+ if( $alpha_ret ){
+ return $alpha_ret;
+ }
+ }
+
+ $this->MatchChar('('); // Parse the '(' and consume whitespace.
+
+ $args = $this->parseEntitiesArguments();
+
+ if( !$this->MatchChar(')') ){
+ return;
+ }
+
+ if ($name) {
+ return $this->NewObj4('Less_Tree_Call',array($name, $args, $index, $this->env->currentFileInfo) );
+ }
+ }
+
+ /**
+ * Parse a list of arguments
+ *
+ * @return array
+ */
+ private function parseEntitiesArguments(){
+
+ $args = array();
+ while( true ){
+ $arg = $this->MatchFuncs( array('parseEntitiesAssignment','parseExpression') );
+ if( !$arg ){
+ break;
+ }
+
+ $args[] = $arg;
+ if( !$this->MatchChar(',') ){
+ break;
+ }
+ }
+ return $args;
+ }
+
+ private function parseEntitiesLiteral(){
+ return $this->MatchFuncs( array('parseEntitiesDimension','parseEntitiesColor','parseEntitiesQuoted','parseUnicodeDescriptor') );
+ }
+
+ // Assignments are argument entities for calls.
+ // They are present in ie filter properties as shown below.
+ //
+ // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* )
+ //
+ private function parseEntitiesAssignment() {
+
+ $key = $this->MatchReg('/\\G\w+(?=\s?=)/');
+ if( !$key ){
+ return;
+ }
+
+ if( !$this->MatchChar('=') ){
+ return;
+ }
+
+ $value = $this->parseEntity();
+ if( $value ){
+ return $this->NewObj2('Less_Tree_Assignment',array($key[0], $value));
+ }
+ }
+
+ //
+ // Parse url() tokens
+ //
+ // We use a specific rule for urls, because they don't really behave like
+ // standard function calls. The difference is that the argument doesn't have
+ // to be enclosed within a string, so it can't be parsed as an Expression.
+ //
+ private function parseEntitiesUrl(){
+
+
+ if( $this->input[$this->pos] !== 'u' || !$this->matchReg('/\\Gurl\(/') ){
+ return;
+ }
+
+ $value = $this->match( array('parseEntitiesQuoted','parseEntitiesVariable','/\\Gdata\:.*?[^\)]+/','/\\G(?:(?:\\\\[\(\)\'"])|[^\(\)\'"])+/') );
+ if( !$value ){
+ $value = '';
+ }
+
+
+ $this->expectChar(')');
+
+
+ if( isset($value->value) || $value instanceof Less_Tree_Variable ){
+ return $this->NewObj2('Less_Tree_Url',array($value, $this->env->currentFileInfo));
+ }
+
+ return $this->NewObj2('Less_Tree_Url', array( $this->NewObj1('Less_Tree_Anonymous',$value), $this->env->currentFileInfo) );
+ }
+
+
+ //
+ // A Variable entity, such as `@fink`, in
+ //
+ // width: @fink + 2px
+ //
+ // We use a different parser for variable definitions,
+ // see `parsers.variable`.
+ //
+ private function parseEntitiesVariable(){
+ $index = $this->pos;
+ if ($this->PeekChar('@') && ($name = $this->MatchReg('/\\G@@?[\w-]+/'))) {
+ return $this->NewObj3('Less_Tree_Variable', array( $name[0], $index, $this->env->currentFileInfo));
+ }
+ }
+
+
+ // A variable entity useing the protective {} e.g. @{var}
+ private function parseEntitiesVariableCurly() {
+ $index = $this->pos;
+
+ if( $this->input_len > ($this->pos+1) && $this->input[$this->pos] === '@' && ($curly = $this->MatchReg('/\\G@\{([\w-]+)\}/')) ){
+ return $this->NewObj3('Less_Tree_Variable',array('@'.$curly[1], $index, $this->env->currentFileInfo));
+ }
+ }
+
+ //
+ // A Hexadecimal color
+ //
+ // #4F3C2F
+ //
+ // `rgb` and `hsl` colors are parsed through the `entities.call` parser.
+ //
+ private function parseEntitiesColor(){
+ if ($this->PeekChar('#') && ($rgb = $this->MatchReg('/\\G#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/'))) {
+ return $this->NewObj1('Less_Tree_Color',$rgb[1]);
+ }
+ }
+
+ //
+ // A Dimension, that is, a number and a unit
+ //
+ // 0.5em 95%
+ //
+ private function parseEntitiesDimension(){
+
+ $c = @ord($this->input[$this->pos]);
+
+ //Is the first char of the dimension 0-9, '.', '+' or '-'
+ if (($c > 57 || $c < 43) || $c === 47 || $c == 44){
+ return;
+ }
+
+ $value = $this->MatchReg('/\\G([+-]?\d*\.?\d+)(%|[a-z]+)?/');
+ if( $value ){
+
+ if( isset($value[2]) ){
+ return $this->NewObj2('Less_Tree_Dimension', array($value[1],$value[2]));
+ }
+ return $this->NewObj1('Less_Tree_Dimension',$value[1]);
+ }
+ }
+
+
+ //
+ // A unicode descriptor, as is used in unicode-range
+ //
+ // U+0?? or U+00A1-00A9
+ //
+ function parseUnicodeDescriptor() {
+ $ud = $this->MatchReg('/\\G(U\+[0-9a-fA-F?]+)(\-[0-9a-fA-F?]+)?/');
+ if( $ud ){
+ return $this->NewObj1('Less_Tree_UnicodeDescriptor', $ud[0]);
+ }
+ }
+
+
+ //
+ // JavaScript code to be evaluated
+ //
+ // `window.location.href`
+ //
+ private function parseEntitiesJavascript(){
+ $e = false;
+ $j = $this->pos;
+ if( $this->input[$j] === '~' ){
+ $j++;
+ $e = true;
+ }
+ if( $this->input[$j] !== '`' ){
+ return;
+ }
+ if( $e ){
+ $this->MatchChar('~');
+ }
+ $str = $this->MatchReg('/\\G`([^`]*)`/');
+ if( $str ){
+ return $this->NewObj3('Less_Tree_Javascript', array($str[1], $this->pos, $e));
+ }
+ }
+
+
+ //
+ // The variable part of a variable definition. Used in the `rule` parser
+ //
+ // @fink:
+ //
+ private function parseVariable(){
+ if ($this->PeekChar('@') && ($name = $this->MatchReg('/\\G(@[\w-]+)\s*:/'))) {
+ return $name[1];
+ }
+ }
+
+
+ //
+ // The variable part of a variable definition. Used in the `rule` parser
+ //
+ // @fink();
+ //
+ private function parseRulesetCall(){
+
+ if( $this->input[$this->pos] === '@' && ($name = $this->MatchReg('/\\G(@[\w-]+)\s*\(\s*\)\s*;/')) ){
+ return $this->NewObj1('Less_Tree_RulesetCall', $name[1] );
+ }
+ }
+
+
+ //
+ // extend syntax - used to extend selectors
+ //
+ function parseExtend($isRule = false){
+
+ $index = $this->pos;
+ $extendList = array();
+
+
+ if( !$this->MatchReg( $isRule ? '/\\G&:extend\(/' : '/\\G:extend\(/' ) ){ return; }
+
+ do{
+ $option = null;
+ $elements = array();
+ while( true ){
+ $option = $this->MatchReg('/\\G(all)(?=\s*(\)|,))/');
+ if( $option ){ break; }
+ $e = $this->parseElement();
+ if( !$e ){ break; }
+ $elements[] = $e;
+ }
+
+ if( $option ){
+ $option = $option[1];
+ }
+
+ $extendList[] = $this->NewObj3('Less_Tree_Extend', array( $this->NewObj1('Less_Tree_Selector',$elements), $option, $index ));
+
+ }while( $this->MatchChar(",") );
+
+ $this->expect('/\\G\)/');
+
+ if( $isRule ){
+ $this->expect('/\\G;/');
+ }
+
+ return $extendList;
+ }
+
+
+ //
+ // A Mixin call, with an optional argument list
+ //
+ // #mixins > .square(#fff);
+ // .rounded(4px, black);
+ // .button;
+ //
+ // The `while` loop is there because mixins can be
+ // namespaced, but we only support the child and descendant
+ // selector for now.
+ //
+ private function parseMixinCall(){
+
+ $char = $this->input[$this->pos];
+ if( $char !== '.' && $char !== '#' ){
+ return;
+ }
+
+ $index = $this->pos;
+ $this->save(); // stop us absorbing part of an invalid selector
+
+ $elements = $this->parseMixinCallElements();
+
+ if( $elements ){
+
+ if( $this->MatchChar('(') ){
+ $returned = $this->parseMixinArgs(true);
+ $args = $returned['args'];
+ $this->expectChar(')');
+ }else{
+ $args = array();
+ }
+
+ $important = $this->parseImportant();
+
+ if( $this->parseEnd() ){
+ $this->forget();
+ return $this->NewObj5('Less_Tree_Mixin_Call', array( $elements, $args, $index, $this->env->currentFileInfo, $important));
+ }
+ }
+
+ $this->restore();
+ }
+
+
+ private function parseMixinCallElements(){
+ $elements = array();
+ $c = null;
+
+ while( true ){
+ $elemIndex = $this->pos;
+ $e = $this->MatchReg('/\\G[#.](?:[\w-]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/');
+ if( !$e ){
+ break;
+ }
+ $elements[] = $this->NewObj4('Less_Tree_Element', array($c, $e[0], $elemIndex, $this->env->currentFileInfo));
+ $c = $this->MatchChar('>');
+ }
+
+ return $elements;
+ }
+
+
+
+ /**
+ * @param boolean $isCall
+ */
+ private function parseMixinArgs( $isCall ){
+ $expressions = array();
+ $argsSemiColon = array();
+ $isSemiColonSeperated = null;
+ $argsComma = array();
+ $expressionContainsNamed = null;
+ $name = null;
+ $returner = array('args'=>array(), 'variadic'=> false);
+
+ $this->save();
+
+ while( true ){
+ if( $isCall ){
+ $arg = $this->MatchFuncs( array( 'parseDetachedRuleset','parseExpression' ) );
+ } else {
+ $this->parseComments();
+ if( $this->input[ $this->pos ] === '.' && $this->MatchReg('/\\G\.{3}/') ){
+ $returner['variadic'] = true;
+ if( $this->MatchChar(";") && !$isSemiColonSeperated ){
+ $isSemiColonSeperated = true;
+ }
+
+ if( $isSemiColonSeperated ){
+ $argsSemiColon[] = array('variadic'=>true);
+ }else{
+ $argsComma[] = array('variadic'=>true);
+ }
+ break;
+ }
+ $arg = $this->MatchFuncs( array('parseEntitiesVariable','parseEntitiesLiteral','parseEntitiesKeyword') );
+ }
+
+ if( !$arg ){
+ break;
+ }
+
+
+ $nameLoop = null;
+ if( $arg instanceof Less_Tree_Expression ){
+ $arg->throwAwayComments();
+ }
+ $value = $arg;
+ $val = null;
+
+ if( $isCall ){
+ // Variable
+ if( property_exists($arg,'value') && count($arg->value) == 1 ){
+ $val = $arg->value[0];
+ }
+ } else {
+ $val = $arg;
+ }
+
+
+ if( $val instanceof Less_Tree_Variable ){
+
+ if( $this->MatchChar(':') ){
+ if( $expressions ){
+ if( $isSemiColonSeperated ){
+ $this->Error('Cannot mix ; and , as delimiter types');
+ }
+ $expressionContainsNamed = true;
+ }
+
+ // we do not support setting a ruleset as a default variable - it doesn't make sense
+ // However if we do want to add it, there is nothing blocking it, just don't error
+ // and remove isCall dependency below
+ $value = null;
+ if( $isCall ){
+ $value = $this->parseDetachedRuleset();
+ }
+ if( !$value ){
+ $value = $this->parseExpression();
+ }
+
+ if( !$value ){
+ if( $isCall ){
+ $this->Error('could not understand value for named argument');
+ } else {
+ $this->restore();
+ $returner['args'] = array();
+ return $returner;
+ }
+ }
+
+ $nameLoop = ($name = $val->name);
+ }elseif( !$isCall && $this->MatchReg('/\\G\.{3}/') ){
+ $returner['variadic'] = true;
+ if( $this->MatchChar(";") && !$isSemiColonSeperated ){
+ $isSemiColonSeperated = true;
+ }
+ if( $isSemiColonSeperated ){
+ $argsSemiColon[] = array('name'=> $arg->name, 'variadic' => true);
+ }else{
+ $argsComma[] = array('name'=> $arg->name, 'variadic' => true);
+ }
+ break;
+ }elseif( !$isCall ){
+ $name = $nameLoop = $val->name;
+ $value = null;
+ }
+ }
+
+ if( $value ){
+ $expressions[] = $value;
+ }
+
+ $argsComma[] = array('name'=>$nameLoop, 'value'=>$value );
+
+ if( $this->MatchChar(',') ){
+ continue;
+ }
+
+ if( $this->MatchChar(';') || $isSemiColonSeperated ){
+
+ if( $expressionContainsNamed ){
+ $this->Error('Cannot mix ; and , as delimiter types');
+ }
+
+ $isSemiColonSeperated = true;
+
+ if( count($expressions) > 1 ){
+ $value = $this->NewObj1('Less_Tree_Value', $expressions);
+ }
+ $argsSemiColon[] = array('name'=>$name, 'value'=>$value );
+
+ $name = null;
+ $expressions = array();
+ $expressionContainsNamed = false;
+ }
+ }
+
+ $this->forget();
+ $returner['args'] = ($isSemiColonSeperated ? $argsSemiColon : $argsComma);
+ return $returner;
+ }
+
+
+
+ //
+ // A Mixin definition, with a list of parameters
+ //
+ // .rounded (@radius: 2px, @color) {
+ // ...
+ // }
+ //
+ // Until we have a finer grained state-machine, we have to
+ // do a look-ahead, to make sure we don't have a mixin call.
+ // See the `rule` function for more information.
+ //
+ // We start by matching `.rounded (`, and then proceed on to
+ // the argument list, which has optional default values.
+ // We store the parameters in `params`, with a `value` key,
+ // if there is a value, such as in the case of `@radius`.
+ //
+ // Once we've got our params list, and a closing `)`, we parse
+ // the `{...}` block.
+ //
+ private function parseMixinDefinition(){
+ $cond = null;
+
+ $char = $this->input[$this->pos];
+ if( ($char !== '.' && $char !== '#') || ($char === '{' && $this->PeekReg('/\\G[^{]*\}/')) ){
+ return;
+ }
+
+ $this->save();
+
+ $match = $this->MatchReg('/\\G([#.](?:[\w-]|\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/');
+ if( $match ){
+ $name = $match[1];
+
+ $argInfo = $this->parseMixinArgs( false );
+ $params = $argInfo['args'];
+ $variadic = $argInfo['variadic'];
+
+
+ // .mixincall("@{a}");
+ // looks a bit like a mixin definition..
+ // also
+ // .mixincall(@a: {rule: set;});
+ // so we have to be nice and restore
+ if( !$this->MatchChar(')') ){
+ $this->furthest = $this->pos;
+ $this->restore();
+ return;
+ }
+
+
+ $this->parseComments();
+
+ if ($this->MatchReg('/\\Gwhen/')) { // Guard
+ $cond = $this->expect('parseConditions', 'Expected conditions');
+ }
+
+ $ruleset = $this->parseBlock();
+
+ if( is_array($ruleset) ){
+ $this->forget();
+ return $this->NewObj5('Less_Tree_Mixin_Definition', array( $name, $params, $ruleset, $cond, $variadic));
+ }
+
+ $this->restore();
+ }else{
+ $this->forget();
+ }
+ }
+
+ //
+ // Entities are the smallest recognized token,
+ // and can be found inside a rule's value.
+ //
+ private function parseEntity(){
+
+ return $this->MatchFuncs( array('parseEntitiesLiteral','parseEntitiesVariable','parseEntitiesUrl','parseEntitiesCall','parseEntitiesKeyword','parseEntitiesJavascript','parseComment') );
+ }
+
+ //
+ // A Rule terminator. Note that we use `peek()` to check for '}',
+ // because the `block` rule will be expecting it, but we still need to make sure
+ // it's there, if ';' was ommitted.
+ //
+ private function parseEnd(){
+ return $this->MatchChar(';') || $this->PeekChar('}');
+ }
+
+ //
+ // IE's alpha function
+ //
+ // alpha(opacity=88)
+ //
+ private function parseAlpha(){
+
+ if ( ! $this->MatchReg('/\\G\(opacity=/i')) {
+ return;
+ }
+
+ $value = $this->MatchReg('/\\G[0-9]+/');
+ if( $value ){
+ $value = $value[0];
+ }else{
+ $value = $this->parseEntitiesVariable();
+ if( !$value ){
+ return;
+ }
+ }
+
+ $this->expectChar(')');
+ return $this->NewObj1('Less_Tree_Alpha',$value);
+ }
+
+
+ //
+ // A Selector Element
+ //
+ // div
+ // + h1
+ // #socks
+ // input[type="text"]
+ //
+ // Elements are the building blocks for Selectors,
+ // they are made out of a `Combinator` (see combinator rule),
+ // and an element name, such as a tag a class, or `*`.
+ //
+ private function parseElement(){
+ $c = $this->parseCombinator();
+ $index = $this->pos;
+
+ $e = $this->match( array('/\\G(?:\d+\.\d+|\d+)%/', '/\\G(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/',
+ '#*', '#&', 'parseAttribute', '/\\G\([^()@]+\)/', '/\\G[\.#](?=@)/', 'parseEntitiesVariableCurly') );
+
+ if( is_null($e) ){
+ $this->save();
+ if( $this->MatchChar('(') ){
+ if( ($v = $this->parseSelector()) && $this->MatchChar(')') ){
+ $e = $this->NewObj1('Less_Tree_Paren',$v);
+ $this->forget();
+ }else{
+ $this->restore();
+ }
+ }else{
+ $this->forget();
+ }
+ }
+
+ if( !is_null($e) ){
+ return $this->NewObj4('Less_Tree_Element',array( $c, $e, $index, $this->env->currentFileInfo));
+ }
+ }
+
+ //
+ // Combinators combine elements together, in a Selector.
+ //
+ // Because our parser isn't white-space sensitive, special care
+ // has to be taken, when parsing the descendant combinator, ` `,
+ // as it's an empty space. We have to check the previous character
+ // in the input, to see if it's a ` ` character.
+ //
+ private function parseCombinator(){
+ if( $this->pos < $this->input_len ){
+ $c = $this->input[$this->pos];
+ if ($c === '>' || $c === '+' || $c === '~' || $c === '|' || $c === '^' ){
+
+ $this->pos++;
+ if( $this->input[$this->pos] === '^' ){
+ $c = '^^';
+ $this->pos++;
+ }
+
+ $this->skipWhitespace(0);
+
+ return $c;
+ }
+
+ if( $this->pos > 0 && $this->isWhitespace(-1) ){
+ return ' ';
+ }
+ }
+ }
+
+ //
+ // A CSS selector (see selector below)
+ // with less extensions e.g. the ability to extend and guard
+ //
+ private function parseLessSelector(){
+ return $this->parseSelector(true);
+ }
+
+ //
+ // A CSS Selector
+ //
+ // .class > div + h1
+ // li a:hover
+ //
+ // Selectors are made out of one or more Elements, see above.
+ //
+ private function parseSelector( $isLess = false ){
+ $elements = array();
+ $extendList = array();
+ $condition = null;
+ $when = false;
+ $extend = false;
+ $e = null;
+ $c = null;
+ $index = $this->pos;
+
+ while( ($isLess && ($extend = $this->parseExtend())) || ($isLess && ($when = $this->MatchReg('/\\Gwhen/') )) || ($e = $this->parseElement()) ){
+ if( $when ){
+ $condition = $this->expect('parseConditions', 'expected condition');
+ }elseif( $condition ){
+ //error("CSS guard can only be used at the end of selector");
+ }elseif( $extend ){
+ $extendList = array_merge($extendList,$extend);
+ }else{
+ //if( count($extendList) ){
+ //error("Extend can only be used at the end of selector");
+ //}
+ if( $this->pos < $this->input_len ){
+ $c = $this->input[ $this->pos ];
+ }
+ $elements[] = $e;
+ $e = null;
+ }
+
+ if( $c === '{' || $c === '}' || $c === ';' || $c === ',' || $c === ')') { break; }
+ }
+
+ if( $elements ){
+ return $this->NewObj5('Less_Tree_Selector',array($elements, $extendList, $condition, $index, $this->env->currentFileInfo));
+ }
+ if( $extendList ) {
+ $this->Error('Extend must be used to extend a selector, it cannot be used on its own');
+ }
+ }
+
+ private function parseTag(){
+ return ( $tag = $this->MatchReg('/\\G[A-Za-z][A-Za-z-]*[0-9]?/') ) ? $tag : $this->MatchChar('*');
+ }
+
+ private function parseAttribute(){
+
+ $val = null;
+
+ if( !$this->MatchChar('[') ){
+ return;
+ }
+
+ $key = $this->parseEntitiesVariableCurly();
+ if( !$key ){
+ $key = $this->expect('/\\G(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\\\.)+/');
+ }
+
+ $op = $this->MatchReg('/\\G[|~*$^]?=/');
+ if( $op ){
+ $val = $this->match( array('parseEntitiesQuoted','/\\G[0-9]+%/','/\\G[\w-]+/','parseEntitiesVariableCurly') );
+ }
+
+ $this->expectChar(']');
+
+ return $this->NewObj3('Less_Tree_Attribute',array( $key, $op[0], $val));
+ }
+
+ //
+ // The `block` rule is used by `ruleset` and `mixin.definition`.
+ // It's a wrapper around the `primary` rule, with added `{}`.
+ //
+ private function parseBlock(){
+ if( $this->MatchChar('{') ){
+ $content = $this->parsePrimary();
+ if( $this->MatchChar('}') ){
+ return $content;
+ }
+ }
+ }
+
+ private function parseBlockRuleset(){
+ $block = $this->parseBlock();
+
+ if( $block ){
+ $block = $this->NewObj2('Less_Tree_Ruleset',array( null, $block));
+ }
+
+ return $block;
+ }
+
+ private function parseDetachedRuleset(){
+ $blockRuleset = $this->parseBlockRuleset();
+ if( $blockRuleset ){
+ return $this->NewObj1('Less_Tree_DetachedRuleset',$blockRuleset);
+ }
+ }
+
+ //
+ // div, .class, body > p {...}
+ //
+ private function parseRuleset(){
+ $selectors = array();
+
+ $this->save();
+
+ while( true ){
+ $s = $this->parseLessSelector();
+ if( !$s ){
+ break;
+ }
+ $selectors[] = $s;
+ $this->parseComments();
+
+ if( $s->condition && count($selectors) > 1 ){
+ $this->Error('Guards are only currently allowed on a single selector.');
+ }
+
+ if( !$this->MatchChar(',') ){
+ break;
+ }
+ if( $s->condition ){
+ $this->Error('Guards are only currently allowed on a single selector.');
+ }
+ $this->parseComments();
+ }
+
+
+ if( $selectors ){
+ $rules = $this->parseBlock();
+ if( is_array($rules) ){
+ $this->forget();
+ return $this->NewObj2('Less_Tree_Ruleset',array( $selectors, $rules)); //Less_Environment::$strictImports
+ }
+ }
+
+ // Backtrack
+ $this->furthest = $this->pos;
+ $this->restore();
+ }
+
+ /**
+ * Custom less.php parse function for finding simple name-value css pairs
+ * ex: width:100px;
+ *
+ */
+ private function parseNameValue(){
+
+ $index = $this->pos;
+ $this->save();
+
+
+ //$match = $this->MatchReg('/\\G([a-zA-Z\-]+)\s*:\s*((?:\'")?[a-zA-Z0-9\-% \.,!]+?(?:\'")?)\s*([;}])/');
+ $match = $this->MatchReg('/\\G([a-zA-Z\-]+)\s*:\s*([\'"]?[#a-zA-Z0-9\-%\.,]+?[\'"]?) *(! *important)?\s*([;}])/');
+ if( $match ){
+
+ if( $match[4] == '}' ){
+ $this->pos = $index + strlen($match[0])-1;
+ }
+
+ if( $match[3] ){
+ $match[2] .= ' !important';
+ }
+
+ return $this->NewObj4('Less_Tree_NameValue',array( $match[1], $match[2], $index, $this->env->currentFileInfo));
+ }
+
+ $this->restore();
+ }
+
+
+ private function parseRule( $tryAnonymous = null ){
+
+ $merge = false;
+ $startOfRule = $this->pos;
+
+ $c = $this->input[$this->pos];
+ if( $c === '.' || $c === '#' || $c === '&' ){
+ return;
+ }
+
+ $this->save();
+ $name = $this->MatchFuncs( array('parseVariable','parseRuleProperty'));
+
+ if( $name ){
+
+ $isVariable = is_string($name);
+
+ $value = null;
+ if( $isVariable ){
+ $value = $this->parseDetachedRuleset();
+ }
+
+ $important = null;
+ if( !$value ){
+
+ // prefer to try to parse first if its a variable or we are compressing
+ // but always fallback on the other one
+ //if( !$tryAnonymous && is_string($name) && $name[0] === '@' ){
+ if( !$tryAnonymous && (Less_Parser::$options['compress'] || $isVariable) ){
+ $value = $this->MatchFuncs( array('parseValue','parseAnonymousValue'));
+ }else{
+ $value = $this->MatchFuncs( array('parseAnonymousValue','parseValue'));
+ }
+
+ $important = $this->parseImportant();
+
+ // a name returned by this.ruleProperty() is always an array of the form:
+ // [string-1, ..., string-n, ""] or [string-1, ..., string-n, "+"]
+ // where each item is a tree.Keyword or tree.Variable
+ if( !$isVariable && is_array($name) ){
+ $nm = array_pop($name);
+ if( $nm->value ){
+ $merge = $nm->value;
+ }
+ }
+ }
+
+
+ if( $value && $this->parseEnd() ){
+ $this->forget();
+ return $this->NewObj6('Less_Tree_Rule',array( $name, $value, $important, $merge, $startOfRule, $this->env->currentFileInfo));
+ }else{
+ $this->furthest = $this->pos;
+ $this->restore();
+ if( $value && !$tryAnonymous ){
+ return $this->parseRule(true);
+ }
+ }
+ }else{
+ $this->forget();
+ }
+ }
+
+ function parseAnonymousValue(){
+
+ if( preg_match('/\\G([^@+\/\'"*`(;{}-]*);/',$this->input, $match, 0, $this->pos) ){
+ $this->pos += strlen($match[1]);
+ return $this->NewObj1('Less_Tree_Anonymous',$match[1]);
+ }
+ }
+
+ //
+ // An @import directive
+ //
+ // @import "lib";
+ //
+ // Depending on our environment, importing is done differently:
+ // In the browser, it's an XHR request, in Node, it would be a
+ // file-system operation. The function used for importing is
+ // stored in `import`, which we pass to the Import constructor.
+ //
+ private function parseImport(){
+
+ $this->save();
+
+ $dir = $this->MatchReg('/\\G@import?\s+/');
+
+ if( $dir ){
+ $options = $this->parseImportOptions();
+ $path = $this->MatchFuncs( array('parseEntitiesQuoted','parseEntitiesUrl'));
+
+ if( $path ){
+ $features = $this->parseMediaFeatures();
+ if( $this->MatchChar(';') ){
+ if( $features ){
+ $features = $this->NewObj1('Less_Tree_Value',$features);
+ }
+
+ $this->forget();
+ return $this->NewObj5('Less_Tree_Import',array( $path, $features, $options, $this->pos, $this->env->currentFileInfo));
+ }
+ }
+ }
+
+ $this->restore();
+ }
+
+ private function parseImportOptions(){
+
+ $options = array();
+
+ // list of options, surrounded by parens
+ if( !$this->MatchChar('(') ){
+ return $options;
+ }
+ do{
+ $optionName = $this->parseImportOption();
+ if( $optionName ){
+ $value = true;
+ switch( $optionName ){
+ case "css":
+ $optionName = "less";
+ $value = false;
+ break;
+ case "once":
+ $optionName = "multiple";
+ $value = false;
+ break;
+ }
+ $options[$optionName] = $value;
+ if( !$this->MatchChar(',') ){ break; }
+ }
+ }while( $optionName );
+ $this->expectChar(')');
+ return $options;
+ }
+
+ private function parseImportOption(){
+ $opt = $this->MatchReg('/\\G(less|css|multiple|once|inline|reference|optional)/');
+ if( $opt ){
+ return $opt[1];
+ }
+ }
+
+ private function parseMediaFeature() {
+ $nodes = array();
+
+ do{
+ $e = $this->MatchFuncs(array('parseEntitiesKeyword','parseEntitiesVariable'));
+ if( $e ){
+ $nodes[] = $e;
+ } elseif ($this->MatchChar('(')) {
+ $p = $this->parseProperty();
+ $e = $this->parseValue();
+ if ($this->MatchChar(')')) {
+ if ($p && $e) {
+ $r = $this->NewObj7('Less_Tree_Rule', array( $p, $e, null, null, $this->pos, $this->env->currentFileInfo, true));
+ $nodes[] = $this->NewObj1('Less_Tree_Paren',$r);
+ } elseif ($e) {
+ $nodes[] = $this->NewObj1('Less_Tree_Paren',$e);
+ } else {
+ return null;
+ }
+ } else
+ return null;
+ }
+ } while ($e);
+
+ if ($nodes) {
+ return $this->NewObj1('Less_Tree_Expression',$nodes);
+ }
+ }
+
+ private function parseMediaFeatures() {
+ $features = array();
+
+ do{
+ $e = $this->parseMediaFeature();
+ if( $e ){
+ $features[] = $e;
+ if (!$this->MatchChar(',')) break;
+ }else{
+ $e = $this->parseEntitiesVariable();
+ if( $e ){
+ $features[] = $e;
+ if (!$this->MatchChar(',')) break;
+ }
+ }
+ } while ($e);
+
+ return $features ? $features : null;
+ }
+
+ private function parseMedia() {
+ if( $this->MatchReg('/\\G@media/') ){
+ $features = $this->parseMediaFeatures();
+ $rules = $this->parseBlock();
+
+ if( is_array($rules) ){
+ return $this->NewObj4('Less_Tree_Media',array( $rules, $features, $this->pos, $this->env->currentFileInfo));
+ }
+ }
+ }
+
+
+ //
+ // A CSS Directive
+ //
+ // @charset "utf-8";
+ //
+ private function parseDirective(){
+
+ if( !$this->PeekChar('@') ){
+ return;
+ }
+
+ $rules = null;
+ $index = $this->pos;
+ $hasBlock = true;
+ $hasIdentifier = false;
+ $hasExpression = false;
+ $hasUnknown = false;
+
+
+ $value = $this->MatchFuncs(array('parseImport','parseMedia'));
+ if( $value ){
+ return $value;
+ }
+
+ $this->save();
+
+ $name = $this->MatchReg('/\\G@[a-z-]+/');
+
+ if( !$name ) return;
+ $name = $name[0];
+
+
+ $nonVendorSpecificName = $name;
+ $pos = strpos($name,'-', 2);
+ if( $name[1] == '-' && $pos > 0 ){
+ $nonVendorSpecificName = "@" . substr($name, $pos + 1);
+ }
+
+
+ switch( $nonVendorSpecificName ){
+ /*
+ case "@font-face":
+ case "@viewport":
+ case "@top-left":
+ case "@top-left-corner":
+ case "@top-center":
+ case "@top-right":
+ case "@top-right-corner":
+ case "@bottom-left":
+ case "@bottom-left-corner":
+ case "@bottom-center":
+ case "@bottom-right":
+ case "@bottom-right-corner":
+ case "@left-top":
+ case "@left-middle":
+ case "@left-bottom":
+ case "@right-top":
+ case "@right-middle":
+ case "@right-bottom":
+ hasBlock = true;
+ break;
+ */
+ case "@charset":
+ $hasIdentifier = true;
+ $hasBlock = false;
+ break;
+ case "@namespace":
+ $hasExpression = true;
+ $hasBlock = false;
+ break;
+ case "@keyframes":
+ $hasIdentifier = true;
+ break;
+ case "@host":
+ case "@page":
+ case "@document":
+ case "@supports":
+ $hasUnknown = true;
+ break;
+ }
+
+ if( $hasIdentifier ){
+ $value = $this->parseEntity();
+ if( !$value ){
+ $this->error("expected " . $name . " identifier");
+ }
+ } else if( $hasExpression ){
+ $value = $this->parseExpression();
+ if( !$value ){
+ $this->error("expected " . $name. " expression");
+ }
+ } else if ($hasUnknown) {
+
+ $value = $this->MatchReg('/\\G[^{;]+/');
+ if( $value ){
+ $value = $this->NewObj1('Less_Tree_Anonymous',trim($value[0]));
+ }
+ }
+
+ if( $hasBlock ){
+ $rules = $this->parseBlockRuleset();
+ }
+
+ if( $rules || (!$hasBlock && $value && $this->MatchChar(';'))) {
+ $this->forget();
+ return $this->NewObj5('Less_Tree_Directive',array($name, $value, $rules, $index, $this->env->currentFileInfo));
+ }
+
+ $this->restore();
+ }
+
+
+ //
+ // A Value is a comma-delimited list of Expressions
+ //
+ // font-family: Baskerville, Georgia, serif;
+ //
+ // In a Rule, a Value represents everything after the `:`,
+ // and before the `;`.
+ //
+ private function parseValue(){
+ $expressions = array();
+
+ do{
+ $e = $this->parseExpression();
+ if( $e ){
+ $expressions[] = $e;
+ if (! $this->MatchChar(',')) {
+ break;
+ }
+ }
+ }while($e);
+
+ if( $expressions ){
+ return $this->NewObj1('Less_Tree_Value',$expressions);
+ }
+ }
+
+ private function parseImportant (){
+ if( $this->PeekChar('!') && $this->MatchReg('/\\G! *important/') ){
+ return ' !important';
+ }
+ }
+
+ private function parseSub (){
+
+ if( $this->MatchChar('(') ){
+ $a = $this->parseAddition();
+ if( $a ){
+ $this->expectChar(')');
+ return $this->NewObj2('Less_Tree_Expression',array( array($a), true) ); //instead of $e->parens = true so the value is cached
+ }
+ }
+ }
+
+
+ /**
+ * Parses multiplication operation
+ *
+ * @return Less_Tree_Operation|null
+ */
+ function parseMultiplication(){
+
+ $return = $m = $this->parseOperand();
+ if( $return ){
+ while( true ){
+
+ $isSpaced = $this->isWhitespace( -1 );
+
+ if( $this->PeekReg('/\\G\/[*\/]/') ){
+ break;
+ }
+
+ $op = $this->MatchChar('/');
+ if( !$op ){
+ $op = $this->MatchChar('*');
+ if( !$op ){
+ break;
+ }
+ }
+
+ $a = $this->parseOperand();
+
+ if(!$a) { break; }
+
+ $m->parensInOp = true;
+ $a->parensInOp = true;
+ $return = $this->NewObj3('Less_Tree_Operation',array( $op, array( $return, $a ), $isSpaced) );
+ }
+ }
+ return $return;
+
+ }
+
+
+ /**
+ * Parses an addition operation
+ *
+ * @return Less_Tree_Operation|null
+ */
+ private function parseAddition (){
+
+ $return = $m = $this->parseMultiplication();
+ if( $return ){
+ while( true ){
+
+ $isSpaced = $this->isWhitespace( -1 );
+
+ $op = $this->MatchReg('/\\G[-+]\s+/');
+ if( $op ){
+ $op = $op[0];
+ }else{
+ if( !$isSpaced ){
+ $op = $this->match(array('#+','#-'));
+ }
+ if( !$op ){
+ break;
+ }
+ }
+
+ $a = $this->parseMultiplication();
+ if( !$a ){
+ break;
+ }
+
+ $m->parensInOp = true;
+ $a->parensInOp = true;
+ $return = $this->NewObj3('Less_Tree_Operation',array($op, array($return, $a), $isSpaced));
+ }
+ }
+
+ return $return;
+ }
+
+
+ /**
+ * Parses the conditions
+ *
+ * @return Less_Tree_Condition|null
+ */
+ private function parseConditions() {
+ $index = $this->pos;
+ $return = $a = $this->parseCondition();
+ if( $a ){
+ while( true ){
+ if( !$this->PeekReg('/\\G,\s*(not\s*)?\(/') || !$this->MatchChar(',') ){
+ break;
+ }
+ $b = $this->parseCondition();
+ if( !$b ){
+ break;
+ }
+
+ $return = $this->NewObj4('Less_Tree_Condition',array('or', $return, $b, $index));
+ }
+ return $return;
+ }
+ }
+
+ private function parseCondition() {
+ $index = $this->pos;
+ $negate = false;
+ $c = null;
+
+ if ($this->MatchReg('/\\Gnot/')) $negate = true;
+ $this->expectChar('(');
+ $a = $this->MatchFuncs(array('parseAddition','parseEntitiesKeyword','parseEntitiesQuoted'));
+
+ if( $a ){
+ $op = $this->MatchReg('/\\G(?:>=|<=|=<|[<=>])/');
+ if( $op ){
+ $b = $this->MatchFuncs(array('parseAddition','parseEntitiesKeyword','parseEntitiesQuoted'));
+ if( $b ){
+ $c = $this->NewObj5('Less_Tree_Condition',array($op[0], $a, $b, $index, $negate));
+ } else {
+ $this->Error('Unexpected expression');
+ }
+ } else {
+ $k = $this->NewObj1('Less_Tree_Keyword','true');
+ $c = $this->NewObj5('Less_Tree_Condition',array('=', $a, $k, $index, $negate));
+ }
+ $this->expectChar(')');
+ return $this->MatchReg('/\\Gand/') ? $this->NewObj3('Less_Tree_Condition',array('and', $c, $this->parseCondition())) : $c;
+ }
+ }
+
+ /**
+ * An operand is anything that can be part of an operation,
+ * such as a Color, or a Variable
+ *
+ */
+ private function parseOperand (){
+
+ $negate = false;
+ $offset = $this->pos+1;
+ if( $offset >= $this->input_len ){
+ return;
+ }
+ $char = $this->input[$offset];
+ if( $char === '@' || $char === '(' ){
+ $negate = $this->MatchChar('-');
+ }
+
+ $o = $this->MatchFuncs(array('parseSub','parseEntitiesDimension','parseEntitiesColor','parseEntitiesVariable','parseEntitiesCall'));
+
+ if( $negate ){
+ $o->parensInOp = true;
+ $o = $this->NewObj1('Less_Tree_Negative',$o);
+ }
+
+ return $o;
+ }
+
+
+ /**
+ * Expressions either represent mathematical operations,
+ * or white-space delimited Entities.
+ *
+ * 1px solid black
+ * @var * 2
+ *
+ * @return Less_Tree_Expression|null
+ */
+ private function parseExpression (){
+ $entities = array();
+
+ do{
+ $e = $this->MatchFuncs(array('parseAddition','parseEntity'));
+ if( $e ){
+ $entities[] = $e;
+ // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here
+ if( !$this->PeekReg('/\\G\/[\/*]/') ){
+ $delim = $this->MatchChar('/');
+ if( $delim ){
+ $entities[] = $this->NewObj1('Less_Tree_Anonymous',$delim);
+ }
+ }
+ }
+ }while($e);
+
+ if( $entities ){
+ return $this->NewObj1('Less_Tree_Expression',$entities);
+ }
+ }
+
+
+ /**
+ * Parse a property
+ * eg: 'min-width', 'orientation', etc
+ *
+ * @return string
+ */
+ private function parseProperty (){
+ $name = $this->MatchReg('/\\G(\*?-?[_a-zA-Z0-9-]+)\s*:/');
+ if( $name ){
+ return $name[1];
+ }
+ }
+
+
+ /**
+ * Parse a rule property
+ * eg: 'color', 'width', 'height', etc
+ *
+ * @return string
+ */
+ private function parseRuleProperty(){
+ $offset = $this->pos;
+ $name = array();
+ $index = array();
+ $length = 0;
+
+
+ $this->rulePropertyMatch('/\\G(\*?)/', $offset, $length, $index, $name );
+ while( $this->rulePropertyMatch('/\\G((?:[\w-]+)|(?:@\{[\w-]+\}))/', $offset, $length, $index, $name )); // !
+
+ if( (count($name) > 1) && $this->rulePropertyMatch('/\\G\s*((?:\+_|\+)?)\s*:/', $offset, $length, $index, $name) ){
+ // at last, we have the complete match now. move forward,
+ // convert name particles to tree objects and return:
+ $this->skipWhitespace($length);
+
+ if( $name[0] === '' ){
+ array_shift($name);
+ array_shift($index);
+ }
+ foreach($name as $k => $s ){
+ if( !$s || $s[0] !== '@' ){
+ $name[$k] = $this->NewObj1('Less_Tree_Keyword',$s);
+ }else{
+ $name[$k] = $this->NewObj3('Less_Tree_Variable',array('@' . substr($s,2,-1), $index[$k], $this->env->currentFileInfo));
+ }
+ }
+ return $name;
+ }
+
+
+ }
+
+ private function rulePropertyMatch( $re, &$offset, &$length, &$index, &$name ){
+ preg_match($re, $this->input, $a, 0, $offset);
+ if( $a ){
+ $index[] = $this->pos + $length;
+ $length += strlen($a[0]);
+ $offset += strlen($a[0]);
+ $name[] = $a[1];
+ return true;
+ }
+ }
+
+ public static function serializeVars( $vars ){
+ $s = '';
+
+ foreach($vars as $name => $value){
+ $s .= (($name[0] === '@') ? '' : '@') . $name .': '. $value . ((substr($value,-1) === ';') ? '' : ';');
+ }
+
+ return $s;
+ }
+
+
+ /**
+ * Some versions of php have trouble with method_exists($a,$b) if $a is not an object
+ *
+ * @param string $b
+ */
+ public static function is_method($a,$b){
+ return is_object($a) && method_exists($a,$b);
+ }
+
+
+ /**
+ * Round numbers similarly to javascript
+ * eg: 1.499999 to 1 instead of 2
+ *
+ */
+ public static function round($i, $precision = 0){
+
+ $precision = pow(10,$precision);
+ $i = $i*$precision;
+
+ $ceil = ceil($i);
+ $floor = floor($i);
+ if( ($ceil - $i) <= ($i - $floor) ){
+ return $ceil/$precision;
+ }else{
+ return $floor/$precision;
+ }
+ }
+
+
+ /**
+ * Create Less_Tree_* objects and optionally generate a cache string
+ *
+ * @return mixed
+ */
+ public function NewObj0($class){
+ $obj = new $class();
+ if( $this->CacheEnabled() ){
+ $obj->cache_string = ' new '.$class.'()';
+ }
+ return $obj;
+ }
+
+ public function NewObj1($class, $arg){
+ $obj = new $class( $arg );
+ if( $this->CacheEnabled() ){
+ $obj->cache_string = ' new '.$class.'('.Less_Parser::ArgString($arg).')';
+ }
+ return $obj;
+ }
+
+ public function NewObj2($class, $args){
+ $obj = new $class( $args[0], $args[1] );
+ if( $this->CacheEnabled() ){
+ $this->ObjCache( $obj, $class, $args);
+ }
+ return $obj;
+ }
+
+ public function NewObj3($class, $args){
+ $obj = new $class( $args[0], $args[1], $args[2] );
+ if( $this->CacheEnabled() ){
+ $this->ObjCache( $obj, $class, $args);
+ }
+ return $obj;
+ }
+
+ public function NewObj4($class, $args){
+ $obj = new $class( $args[0], $args[1], $args[2], $args[3] );
+ if( $this->CacheEnabled() ){
+ $this->ObjCache( $obj, $class, $args);
+ }
+ return $obj;
+ }
+
+ public function NewObj5($class, $args){
+ $obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4] );
+ if( $this->CacheEnabled() ){
+ $this->ObjCache( $obj, $class, $args);
+ }
+ return $obj;
+ }
+
+ public function NewObj6($class, $args){
+ $obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4], $args[5] );
+ if( $this->CacheEnabled() ){
+ $this->ObjCache( $obj, $class, $args);
+ }
+ return $obj;
+ }
+
+ public function NewObj7($class, $args){
+ $obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4], $args[5], $args[6] );
+ if( $this->CacheEnabled() ){
+ $this->ObjCache( $obj, $class, $args);
+ }
+ return $obj;
+ }
+
+ //caching
+ public function ObjCache($obj, $class, $args=array()){
+ $obj->cache_string = ' new '.$class.'('. self::ArgCache($args).')';
+ }
+
+ public function ArgCache($args){
+ return implode(',',array_map( array('Less_Parser','ArgString'),$args));
+ }
+
+
+ /**
+ * Convert an argument to a string for use in the parser cache
+ *
+ * @return string
+ */
+ public static function ArgString($arg){
+
+ $type = gettype($arg);
+
+ if( $type === 'object'){
+ $string = $arg->cache_string;
+ unset($arg->cache_string);
+ return $string;
+
+ }elseif( $type === 'array' ){
+ $string = ' Array(';
+ foreach($arg as $k => $a){
+ $string .= var_export($k,true).' => '.self::ArgString($a).',';
+ }
+ return $string . ')';
+ }
+
+ return var_export($arg,true);
+ }
+
+ public function Error($msg){
+ throw new Less_Exception_Parser($msg, null, $this->furthest, $this->env->currentFileInfo);
+ }
+
+ public static function WinPath($path){
+ return str_replace('\\', '/', $path);
+ }
+
+ public function CacheEnabled(){
+ return false;
+ //return (Less_Parser::$options['cache_method'] && (Less_Cache::$cache_dir || (Less_Parser::$options['cache_method'] == 'callback')));
+ }
+
+}
+
+
+/**
+ * Utility for css colors
+ *
+ * @package Less
+ * @subpackage color
+ */
+class Less_Colors {
+
+ public static $colors = array(
+ 'aliceblue'=>'#f0f8ff',
+ 'antiquewhite'=>'#faebd7',
+ 'aqua'=>'#00ffff',
+ 'aquamarine'=>'#7fffd4',
+ 'azure'=>'#f0ffff',
+ 'beige'=>'#f5f5dc',
+ 'bisque'=>'#ffe4c4',
+ 'black'=>'#000000',
+ 'blanchedalmond'=>'#ffebcd',
+ 'blue'=>'#0000ff',
+ 'blueviolet'=>'#8a2be2',
+ 'brown'=>'#a52a2a',
+ 'burlywood'=>'#deb887',
+ 'cadetblue'=>'#5f9ea0',
+ 'chartreuse'=>'#7fff00',
+ 'chocolate'=>'#d2691e',
+ 'coral'=>'#ff7f50',
+ 'cornflowerblue'=>'#6495ed',
+ 'cornsilk'=>'#fff8dc',
+ 'crimson'=>'#dc143c',
+ 'cyan'=>'#00ffff',
+ 'darkblue'=>'#00008b',
+ 'darkcyan'=>'#008b8b',
+ 'darkgoldenrod'=>'#b8860b',
+ 'darkgray'=>'#a9a9a9',
+ 'darkgrey'=>'#a9a9a9',
+ 'darkgreen'=>'#006400',
+ 'darkkhaki'=>'#bdb76b',
+ 'darkmagenta'=>'#8b008b',
+ 'darkolivegreen'=>'#556b2f',
+ 'darkorange'=>'#ff8c00',
+ 'darkorchid'=>'#9932cc',
+ 'darkred'=>'#8b0000',
+ 'darksalmon'=>'#e9967a',
+ 'darkseagreen'=>'#8fbc8f',
+ 'darkslateblue'=>'#483d8b',
+ 'darkslategray'=>'#2f4f4f',
+ 'darkslategrey'=>'#2f4f4f',
+ 'darkturquoise'=>'#00ced1',
+ 'darkviolet'=>'#9400d3',
+ 'deeppink'=>'#ff1493',
+ 'deepskyblue'=>'#00bfff',
+ 'dimgray'=>'#696969',
+ 'dimgrey'=>'#696969',
+ 'dodgerblue'=>'#1e90ff',
+ 'firebrick'=>'#b22222',
+ 'floralwhite'=>'#fffaf0',
+ 'forestgreen'=>'#228b22',
+ 'fuchsia'=>'#ff00ff',
+ 'gainsboro'=>'#dcdcdc',
+ 'ghostwhite'=>'#f8f8ff',
+ 'gold'=>'#ffd700',
+ 'goldenrod'=>'#daa520',
+ 'gray'=>'#808080',
+ 'grey'=>'#808080',
+ 'green'=>'#008000',
+ 'greenyellow'=>'#adff2f',
+ 'honeydew'=>'#f0fff0',
+ 'hotpink'=>'#ff69b4',
+ 'indianred'=>'#cd5c5c',
+ 'indigo'=>'#4b0082',
+ 'ivory'=>'#fffff0',
+ 'khaki'=>'#f0e68c',
+ 'lavender'=>'#e6e6fa',
+ 'lavenderblush'=>'#fff0f5',
+ 'lawngreen'=>'#7cfc00',
+ 'lemonchiffon'=>'#fffacd',
+ 'lightblue'=>'#add8e6',
+ 'lightcoral'=>'#f08080',
+ 'lightcyan'=>'#e0ffff',
+ 'lightgoldenrodyellow'=>'#fafad2',
+ 'lightgray'=>'#d3d3d3',
+ 'lightgrey'=>'#d3d3d3',
+ 'lightgreen'=>'#90ee90',
+ 'lightpink'=>'#ffb6c1',
+ 'lightsalmon'=>'#ffa07a',
+ 'lightseagreen'=>'#20b2aa',
+ 'lightskyblue'=>'#87cefa',
+ 'lightslategray'=>'#778899',
+ 'lightslategrey'=>'#778899',
+ 'lightsteelblue'=>'#b0c4de',
+ 'lightyellow'=>'#ffffe0',
+ 'lime'=>'#00ff00',
+ 'limegreen'=>'#32cd32',
+ 'linen'=>'#faf0e6',
+ 'magenta'=>'#ff00ff',
+ 'maroon'=>'#800000',
+ 'mediumaquamarine'=>'#66cdaa',
+ 'mediumblue'=>'#0000cd',
+ 'mediumorchid'=>'#ba55d3',
+ 'mediumpurple'=>'#9370d8',
+ 'mediumseagreen'=>'#3cb371',
+ 'mediumslateblue'=>'#7b68ee',
+ 'mediumspringgreen'=>'#00fa9a',
+ 'mediumturquoise'=>'#48d1cc',
+ 'mediumvioletred'=>'#c71585',
+ 'midnightblue'=>'#191970',
+ 'mintcream'=>'#f5fffa',
+ 'mistyrose'=>'#ffe4e1',
+ 'moccasin'=>'#ffe4b5',
+ 'navajowhite'=>'#ffdead',
+ 'navy'=>'#000080',
+ 'oldlace'=>'#fdf5e6',
+ 'olive'=>'#808000',
+ 'olivedrab'=>'#6b8e23',
+ 'orange'=>'#ffa500',
+ 'orangered'=>'#ff4500',
+ 'orchid'=>'#da70d6',
+ 'palegoldenrod'=>'#eee8aa',
+ 'palegreen'=>'#98fb98',
+ 'paleturquoise'=>'#afeeee',
+ 'palevioletred'=>'#d87093',
+ 'papayawhip'=>'#ffefd5',
+ 'peachpuff'=>'#ffdab9',
+ 'peru'=>'#cd853f',
+ 'pink'=>'#ffc0cb',
+ 'plum'=>'#dda0dd',
+ 'powderblue'=>'#b0e0e6',
+ 'purple'=>'#800080',
+ 'red'=>'#ff0000',
+ 'rosybrown'=>'#bc8f8f',
+ 'royalblue'=>'#4169e1',
+ 'saddlebrown'=>'#8b4513',
+ 'salmon'=>'#fa8072',
+ 'sandybrown'=>'#f4a460',
+ 'seagreen'=>'#2e8b57',
+ 'seashell'=>'#fff5ee',
+ 'sienna'=>'#a0522d',
+ 'silver'=>'#c0c0c0',
+ 'skyblue'=>'#87ceeb',
+ 'slateblue'=>'#6a5acd',
+ 'slategray'=>'#708090',
+ 'slategrey'=>'#708090',
+ 'snow'=>'#fffafa',
+ 'springgreen'=>'#00ff7f',
+ 'steelblue'=>'#4682b4',
+ 'tan'=>'#d2b48c',
+ 'teal'=>'#008080',
+ 'thistle'=>'#d8bfd8',
+ 'tomato'=>'#ff6347',
+ 'turquoise'=>'#40e0d0',
+ 'violet'=>'#ee82ee',
+ 'wheat'=>'#f5deb3',
+ 'white'=>'#ffffff',
+ 'whitesmoke'=>'#f5f5f5',
+ 'yellow'=>'#ffff00',
+ 'yellowgreen'=>'#9acd32'
+ );
+
+ public static function hasOwnProperty($color) {
+ return isset(self::$colors[$color]);
+ }
+
+
+ public static function color($color) {
+ return self::$colors[$color];
+ }
+
+}
+
+
+
+/**
+ * Environment
+ *
+ * @package Less
+ * @subpackage environment
+ */
+class Less_Environment{
+
+ //public $paths = array(); // option - unmodified - paths to search for imports on
+ //public static $files = array(); // list of files that have been imported, used for import-once
+ //public $rootpath; // option - rootpath to append to URL's
+ //public static $strictImports = null; // option -
+ //public $insecure; // option - whether to allow imports from insecure ssl hosts
+ //public $processImports; // option - whether to process imports. if false then imports will not be imported
+ //public $javascriptEnabled; // option - whether JavaScript is enabled. if undefined, defaults to true
+ //public $useFileCache; // browser only - whether to use the per file session cache
+ public $currentFileInfo; // information about the current file - for error reporting and importing and making urls relative etc.
+
+ public $importMultiple = false; // whether we are currently importing multiple copies
+
+
+ /**
+ * @var array
+ */
+ public $frames = array();
+
+ /**
+ * @var array
+ */
+ public $mediaBlocks = array();
+
+ /**
+ * @var array
+ */
+ public $mediaPath = array();
+
+ public static $parensStack = 0;
+
+ public static $tabLevel = 0;
+
+ public static $lastRule = false;
+
+ public static $_outputMap;
+
+ public static $mixin_stack = 0;
+
+ /**
+ * @var array
+ */
+ public $functions = array();
+
+
+ public function Init(){
+
+ self::$parensStack = 0;
+ self::$tabLevel = 0;
+ self::$lastRule = false;
+ self::$mixin_stack = 0;
+
+ if( Less_Parser::$options['compress'] ){
+
+ Less_Environment::$_outputMap = array(
+ ',' => ',',
+ ': ' => ':',
+ '' => '',
+ ' ' => ' ',
+ ':' => ' :',
+ '+' => '+',
+ '~' => '~',
+ '>' => '>',
+ '|' => '|',
+ '^' => '^',
+ '^^' => '^^'
+ );
+
+ }else{
+
+ Less_Environment::$_outputMap = array(
+ ',' => ', ',
+ ': ' => ': ',
+ '' => '',
+ ' ' => ' ',
+ ':' => ' :',
+ '+' => ' + ',
+ '~' => ' ~ ',
+ '>' => ' > ',
+ '|' => '|',
+ '^' => ' ^ ',
+ '^^' => ' ^^ '
+ );
+
+ }
+ }
+
+
+ public function copyEvalEnv($frames = array() ){
+ $new_env = new Less_Environment();
+ $new_env->frames = $frames;
+ return $new_env;
+ }
+
+
+ public static function isMathOn(){
+ return !Less_Parser::$options['strictMath'] || Less_Environment::$parensStack;
+ }
+
+ public static function isPathRelative($path){
+ return !preg_match('/^(?:[a-z-]+:|\/)/',$path);
+ }
+
+
+ /**
+ * Canonicalize a path by resolving references to '/./', '/../'
+ * Does not remove leading "../"
+ * @param string path or url
+ * @return string Canonicalized path
+ *
+ */
+ public static function normalizePath($path){
+
+ $segments = explode('/',$path);
+ $segments = array_reverse($segments);
+
+ $path = array();
+ $path_len = 0;
+
+ while( $segments ){
+ $segment = array_pop($segments);
+ switch( $segment ) {
+
+ case '.':
+ break;
+
+ case '..':
+ if( !$path_len || ( $path[$path_len-1] === '..') ){
+ $path[] = $segment;
+ $path_len++;
+ }else{
+ array_pop($path);
+ $path_len--;
+ }
+ break;
+
+ default:
+ $path[] = $segment;
+ $path_len++;
+ break;
+ }
+ }
+
+ return implode('/',$path);
+ }
+
+
+ public function unshiftFrame($frame){
+ array_unshift($this->frames, $frame);
+ }
+
+ public function shiftFrame(){
+ return array_shift($this->frames);
+ }
+
+}
+
+
+/**
+ * Builtin functions
+ *
+ * @package Less
+ * @subpackage function
+ * @see http://lesscss.org/functions/
+ */
+class Less_Functions{
+
+ public $env;
+ public $currentFileInfo;
+
+ function __construct($env, $currentFileInfo = null ){
+ $this->env = $env;
+ $this->currentFileInfo = $currentFileInfo;
+ }
+
+ /**
+ * @param string $op
+ */
+ public static function operate( $op, $a, $b ){
+ switch ($op) {
+ case '+': return $a + $b;
+ case '-': return $a - $b;
+ case '*': return $a * $b;
+ case '/': return $a / $b;
+ }
+ }
+
+ public static function clamp($val, $max = 1){
+ return min( max($val, 0), $max);
+ }
+
+ public static function fround( $value ){
+
+ if( $value === 0 ){
+ return $value;
+ }
+
+ if( Less_Parser::$options['numPrecision'] ){
+ $p = pow(10, Less_Parser::$options['numPrecision']);
+ return round( $value * $p) / $p;
+ }
+ return $value;
+ }
+
+ public static function number($n){
+
+ if ($n instanceof Less_Tree_Dimension) {
+ return floatval( $n->unit->is('%') ? $n->value / 100 : $n->value);
+ } else if (is_numeric($n)) {
+ return $n;
+ } else {
+ throw new Less_Exception_Compiler("color functions take numbers as parameters");
+ }
+ }
+
+ public static function scaled($n, $size = 255 ){
+ if( $n instanceof Less_Tree_Dimension && $n->unit->is('%') ){
+ return (float)$n->value * $size / 100;
+ } else {
+ return Less_Functions::number($n);
+ }
+ }
+
+ public function rgb ($r = null, $g = null, $b = null){
+ if (is_null($r) || is_null($g) || is_null($b)) {
+ throw new Less_Exception_Compiler("rgb expects three parameters");
+ }
+ return $this->rgba($r, $g, $b, 1.0);
+ }
+
+ public function rgba($r = null, $g = null, $b = null, $a = null){
+ $rgb = array($r, $g, $b);
+ $rgb = array_map(array('Less_Functions','scaled'),$rgb);
+
+ $a = self::number($a);
+ return new Less_Tree_Color($rgb, $a);
+ }
+
+ public function hsl($h, $s, $l){
+ return $this->hsla($h, $s, $l, 1.0);
+ }
+
+ public function hsla($h, $s, $l, $a){
+
+ $h = fmod(self::number($h), 360) / 360; // Classic % operator will change float to int
+ $s = self::clamp(self::number($s));
+ $l = self::clamp(self::number($l));
+ $a = self::clamp(self::number($a));
+
+ $m2 = $l <= 0.5 ? $l * ($s + 1) : $l + $s - $l * $s;
+
+ $m1 = $l * 2 - $m2;
+
+ return $this->rgba( self::hsla_hue($h + 1/3, $m1, $m2) * 255,
+ self::hsla_hue($h, $m1, $m2) * 255,
+ self::hsla_hue($h - 1/3, $m1, $m2) * 255,
+ $a);
+ }
+
+ /**
+ * @param double $h
+ */
+ public function hsla_hue($h, $m1, $m2){
+ $h = $h < 0 ? $h + 1 : ($h > 1 ? $h - 1 : $h);
+ if ($h * 6 < 1) return $m1 + ($m2 - $m1) * $h * 6;
+ else if ($h * 2 < 1) return $m2;
+ else if ($h * 3 < 2) return $m1 + ($m2 - $m1) * (2/3 - $h) * 6;
+ else return $m1;
+ }
+
+ public function hsv($h, $s, $v) {
+ return $this->hsva($h, $s, $v, 1.0);
+ }
+
+ /**
+ * @param double $a
+ */
+ public function hsva($h, $s, $v, $a) {
+ $h = ((Less_Functions::number($h) % 360) / 360 ) * 360;
+ $s = Less_Functions::number($s);
+ $v = Less_Functions::number($v);
+ $a = Less_Functions::number($a);
+
+ $i = floor(($h / 60) % 6);
+ $f = ($h / 60) - $i;
+
+ $vs = array( $v,
+ $v * (1 - $s),
+ $v * (1 - $f * $s),
+ $v * (1 - (1 - $f) * $s));
+
+ $perm = array(array(0, 3, 1),
+ array(2, 0, 1),
+ array(1, 0, 3),
+ array(1, 2, 0),
+ array(3, 1, 0),
+ array(0, 1, 2));
+
+ return $this->rgba($vs[$perm[$i][0]] * 255,
+ $vs[$perm[$i][1]] * 255,
+ $vs[$perm[$i][2]] * 255,
+ $a);
+ }
+
+ public function hue($color = null){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to hue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $c = $color->toHSL();
+ return new Less_Tree_Dimension(Less_Parser::round($c['h']));
+ }
+
+ public function saturation($color = null){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to saturation must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $c = $color->toHSL();
+ return new Less_Tree_Dimension(Less_Parser::round($c['s'] * 100), '%');
+ }
+
+ public function lightness($color = null){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to lightness must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $c = $color->toHSL();
+ return new Less_Tree_Dimension(Less_Parser::round($c['l'] * 100), '%');
+ }
+
+ public function hsvhue( $color = null ){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to hsvhue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $hsv = $color->toHSV();
+ return new Less_Tree_Dimension( Less_Parser::round($hsv['h']) );
+ }
+
+
+ public function hsvsaturation( $color = null ){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to hsvsaturation must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $hsv = $color->toHSV();
+ return new Less_Tree_Dimension( Less_Parser::round($hsv['s'] * 100), '%' );
+ }
+
+ public function hsvvalue( $color = null ){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to hsvvalue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $hsv = $color->toHSV();
+ return new Less_Tree_Dimension( Less_Parser::round($hsv['v'] * 100), '%' );
+ }
+
+ public function red($color = null) {
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to red must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return new Less_Tree_Dimension( $color->rgb[0] );
+ }
+
+ public function green($color = null) {
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to green must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return new Less_Tree_Dimension( $color->rgb[1] );
+ }
+
+ public function blue($color = null) {
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to blue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return new Less_Tree_Dimension( $color->rgb[2] );
+ }
+
+ public function alpha($color = null){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to alpha must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $c = $color->toHSL();
+ return new Less_Tree_Dimension($c['a']);
+ }
+
+ public function luma ($color = null) {
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to luma must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return new Less_Tree_Dimension(Less_Parser::round( $color->luma() * $color->alpha * 100), '%');
+ }
+
+ public function luminance( $color = null ){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to luminance must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $luminance =
+ (0.2126 * $color->rgb[0] / 255)
+ + (0.7152 * $color->rgb[1] / 255)
+ + (0.0722 * $color->rgb[2] / 255);
+
+ return new Less_Tree_Dimension(Less_Parser::round( $luminance * $color->alpha * 100), '%');
+ }
+
+ public function saturate($color = null, $amount = null){
+ // filter: saturate(3.2);
+ // should be kept as is, so check for color
+ if ($color instanceof Less_Tree_Dimension) {
+ return null;
+ }
+
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to saturate must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$amount instanceof Less_Tree_Dimension) {
+ throw new Less_Exception_Compiler('The second argument to saturate must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $hsl = $color->toHSL();
+
+ $hsl['s'] += $amount->value / 100;
+ $hsl['s'] = self::clamp($hsl['s']);
+
+ return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
+ }
+
+ /**
+ * @param Less_Tree_Dimension $amount
+ */
+ public function desaturate($color = null, $amount = null){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to desaturate must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$amount instanceof Less_Tree_Dimension) {
+ throw new Less_Exception_Compiler('The second argument to desaturate must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $hsl = $color->toHSL();
+
+ $hsl['s'] -= $amount->value / 100;
+ $hsl['s'] = self::clamp($hsl['s']);
+
+ return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
+ }
+
+
+
+ public function lighten($color = null, $amount=null){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to lighten must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$amount instanceof Less_Tree_Dimension) {
+ throw new Less_Exception_Compiler('The second argument to lighten must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $hsl = $color->toHSL();
+
+ $hsl['l'] += $amount->value / 100;
+ $hsl['l'] = self::clamp($hsl['l']);
+
+ return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
+ }
+
+ public function darken($color = null, $amount = null){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to darken must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$amount instanceof Less_Tree_Dimension) {
+ throw new Less_Exception_Compiler('The second argument to darken must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $hsl = $color->toHSL();
+ $hsl['l'] -= $amount->value / 100;
+ $hsl['l'] = self::clamp($hsl['l']);
+
+ return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
+ }
+
+ public function fadein($color = null, $amount = null){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to fadein must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$amount instanceof Less_Tree_Dimension) {
+ throw new Less_Exception_Compiler('The second argument to fadein must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $hsl = $color->toHSL();
+ $hsl['a'] += $amount->value / 100;
+ $hsl['a'] = self::clamp($hsl['a']);
+ return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
+ }
+
+ public function fadeout($color = null, $amount = null){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to fadeout must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$amount instanceof Less_Tree_Dimension) {
+ throw new Less_Exception_Compiler('The second argument to fadeout must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $hsl = $color->toHSL();
+ $hsl['a'] -= $amount->value / 100;
+ $hsl['a'] = self::clamp($hsl['a']);
+ return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
+ }
+
+ public function fade($color = null, $amount = null){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to fade must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$amount instanceof Less_Tree_Dimension) {
+ throw new Less_Exception_Compiler('The second argument to fade must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $hsl = $color->toHSL();
+
+ $hsl['a'] = $amount->value / 100;
+ $hsl['a'] = self::clamp($hsl['a']);
+ return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
+ }
+
+
+
+ public function spin($color = null, $amount = null){
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to spin must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$amount instanceof Less_Tree_Dimension) {
+ throw new Less_Exception_Compiler('The second argument to spin must be a number' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $hsl = $color->toHSL();
+ $hue = fmod($hsl['h'] + $amount->value, 360);
+
+ $hsl['h'] = $hue < 0 ? 360 + $hue : $hue;
+
+ return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
+ }
+
+ //
+ // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein
+ // http://sass-lang.com
+ //
+
+ /**
+ * @param Less_Tree_Color $color1
+ */
+ public function mix($color1 = null, $color2 = null, $weight = null){
+ if (!$color1 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to mix must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$color2 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The second argument to mix must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$weight) {
+ $weight = new Less_Tree_Dimension('50', '%');
+ }
+ if (!$weight instanceof Less_Tree_Dimension) {
+ throw new Less_Exception_Compiler('The third argument to contrast must be a percentage' . ($weight instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ $p = $weight->value / 100.0;
+ $w = $p * 2 - 1;
+ $hsl1 = $color1->toHSL();
+ $hsl2 = $color2->toHSL();
+ $a = $hsl1['a'] - $hsl2['a'];
+
+ $w1 = (((($w * $a) == -1) ? $w : ($w + $a) / (1 + $w * $a)) + 1) / 2;
+ $w2 = 1 - $w1;
+
+ $rgb = array($color1->rgb[0] * $w1 + $color2->rgb[0] * $w2,
+ $color1->rgb[1] * $w1 + $color2->rgb[1] * $w2,
+ $color1->rgb[2] * $w1 + $color2->rgb[2] * $w2);
+
+ $alpha = $color1->alpha * $p + $color2->alpha * (1 - $p);
+
+ return new Less_Tree_Color($rgb, $alpha);
+ }
+
+ public function greyscale($color){
+ return $this->desaturate($color, new Less_Tree_Dimension(100,'%'));
+ }
+
+
+ public function contrast( $color, $dark = null, $light = null, $threshold = null){
+ // filter: contrast(3.2);
+ // should be kept as is, so check for color
+ if (!$color instanceof Less_Tree_Color) {
+ return null;
+ }
+ if( !$light ){
+ $light = $this->rgba(255, 255, 255, 1.0);
+ }
+ if( !$dark ){
+ $dark = $this->rgba(0, 0, 0, 1.0);
+ }
+
+ if (!$dark instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The second argument to contrast must be a color' . ($dark instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$light instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The third argument to contrast must be a color' . ($light instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ //Figure out which is actually light and dark!
+ if( $dark->luma() > $light->luma() ){
+ $t = $light;
+ $light = $dark;
+ $dark = $t;
+ }
+ if( !$threshold ){
+ $threshold = 0.43;
+ } else {
+ $threshold = Less_Functions::number($threshold);
+ }
+
+ if( $color->luma() < $threshold ){
+ return $light;
+ } else {
+ return $dark;
+ }
+ }
+
+ public function e ($str){
+ if( is_string($str) ){
+ return new Less_Tree_Anonymous($str);
+ }
+ return new Less_Tree_Anonymous($str instanceof Less_Tree_JavaScript ? $str->expression : $str->value);
+ }
+
+ public function escape ($str){
+
+ $revert = array('%21'=>'!', '%2A'=>'*', '%27'=>"'",'%3F'=>'?','%26'=>'&','%2C'=>',','%2F'=>'/','%40'=>'@','%2B'=>'+','%24'=>'$');
+
+ return new Less_Tree_Anonymous(strtr(rawurlencode($str->value), $revert));
+ }
+
+
+ /**
+ * todo: This function will need some additional work to make it work the same as less.js
+ *
+ */
+ public function replace( $string, $pattern, $replacement, $flags = null ){
+ $result = $string->value;
+
+ $expr = '/'.str_replace('/','\\/',$pattern->value).'/';
+ if( $flags && $flags->value){
+ $expr .= self::replace_flags($flags->value);
+ }
+
+ $result = preg_replace($expr,$replacement->value,$result);
+
+
+ if( property_exists($string,'quote') ){
+ return new Less_Tree_Quoted( $string->quote, $result, $string->escaped);
+ }
+ return new Less_Tree_Quoted( '', $result );
+ }
+
+ public static function replace_flags($flags){
+ $flags = str_split($flags,1);
+ $new_flags = '';
+
+ foreach($flags as $flag){
+ switch($flag){
+ case 'e':
+ case 'g':
+ break;
+
+ default:
+ $new_flags .= $flag;
+ break;
+ }
+ }
+
+ return $new_flags;
+ }
+
+ public function _percent(){
+ $string = func_get_arg(0);
+
+ $args = func_get_args();
+ array_shift($args);
+ $result = $string->value;
+
+ foreach($args as $arg){
+ if( preg_match('/%[sda]/i',$result, $token) ){
+ $token = $token[0];
+ $value = stristr($token, 's') ? $arg->value : $arg->toCSS();
+ $value = preg_match('/[A-Z]$/', $token) ? urlencode($value) : $value;
+ $result = preg_replace('/%[sda]/i',$value, $result, 1);
+ }
+ }
+ $result = str_replace('%%', '%', $result);
+
+ return new Less_Tree_Quoted( $string->quote , $result, $string->escaped);
+ }
+
+ public function unit( $val, $unit = null) {
+ if( !($val instanceof Less_Tree_Dimension) ){
+ throw new Less_Exception_Compiler('The first argument to unit must be a number' . ($val instanceof Less_Tree_Operation ? '. Have you forgotten parenthesis?' : '.') );
+ }
+
+ if( $unit ){
+ if( $unit instanceof Less_Tree_Keyword ){
+ $unit = $unit->value;
+ } else {
+ $unit = $unit->toCSS();
+ }
+ } else {
+ $unit = "";
+ }
+ return new Less_Tree_Dimension($val->value, $unit );
+ }
+
+ public function convert($val, $unit){
+ return $val->convertTo($unit->value);
+ }
+
+ public function round($n, $f = false) {
+
+ $fraction = 0;
+ if( $f !== false ){
+ $fraction = $f->value;
+ }
+
+ return $this->_math('Less_Parser::round',null, $n, $fraction);
+ }
+
+ public function pi(){
+ return new Less_Tree_Dimension(M_PI);
+ }
+
+ public function mod($a, $b) {
+ return new Less_Tree_Dimension( $a->value % $b->value, $a->unit);
+ }
+
+
+
+ public function pow($x, $y) {
+ if( is_numeric($x) && is_numeric($y) ){
+ $x = new Less_Tree_Dimension($x);
+ $y = new Less_Tree_Dimension($y);
+ }elseif( !($x instanceof Less_Tree_Dimension) || !($y instanceof Less_Tree_Dimension) ){
+ throw new Less_Exception_Compiler('Arguments must be numbers');
+ }
+
+ return new Less_Tree_Dimension( pow($x->value, $y->value), $x->unit );
+ }
+
+ // var mathFunctions = [{name:"ce ...
+ public function ceil( $n ){ return $this->_math('ceil', null, $n); }
+ public function floor( $n ){ return $this->_math('floor', null, $n); }
+ public function sqrt( $n ){ return $this->_math('sqrt', null, $n); }
+ public function abs( $n ){ return $this->_math('abs', null, $n); }
+
+ public function tan( $n ){ return $this->_math('tan', '', $n); }
+ public function sin( $n ){ return $this->_math('sin', '', $n); }
+ public function cos( $n ){ return $this->_math('cos', '', $n); }
+
+ public function atan( $n ){ return $this->_math('atan', 'rad', $n); }
+ public function asin( $n ){ return $this->_math('asin', 'rad', $n); }
+ public function acos( $n ){ return $this->_math('acos', 'rad', $n); }
+
+ private function _math() {
+ $args = func_get_args();
+ $fn = array_shift($args);
+ $unit = array_shift($args);
+
+ if ($args[0] instanceof Less_Tree_Dimension) {
+
+ if( $unit === null ){
+ $unit = $args[0]->unit;
+ }else{
+ $args[0] = $args[0]->unify();
+ }
+ $args[0] = (float)$args[0]->value;
+ return new Less_Tree_Dimension( call_user_func_array($fn, $args), $unit);
+ } else if (is_numeric($args[0])) {
+ return call_user_func_array($fn,$args);
+ } else {
+ throw new Less_Exception_Compiler("math functions take numbers as parameters");
+ }
+ }
+
+ /**
+ * @param boolean $isMin
+ */
+ private function _minmax( $isMin, $args ){
+
+ $arg_count = count($args);
+
+ if( $arg_count < 1 ){
+ throw new Less_Exception_Compiler( 'one or more arguments required');
+ }
+
+ $j = null;
+ $unitClone = null;
+ $unitStatic = null;
+
+
+ $order = array(); // elems only contains original argument values.
+ $values = array(); // key is the unit.toString() for unified tree.Dimension values,
+ // value is the index into the order array.
+
+
+ for( $i = 0; $i < $arg_count; $i++ ){
+ $current = $args[$i];
+ if( !($current instanceof Less_Tree_Dimension) ){
+ if( is_array($args[$i]->value) ){
+ $args[] = $args[$i]->value;
+ }
+ continue;
+ }
+
+ if( $current->unit->toString() === '' && !$unitClone ){
+ $temp = new Less_Tree_Dimension($current->value, $unitClone);
+ $currentUnified = $temp->unify();
+ }else{
+ $currentUnified = $current->unify();
+ }
+
+ if( $currentUnified->unit->toString() === "" && !$unitStatic ){
+ $unit = $unitStatic;
+ }else{
+ $unit = $currentUnified->unit->toString();
+ }
+
+ if( $unit !== '' && !$unitStatic || $unit !== '' && $order[0]->unify()->unit->toString() === "" ){
+ $unitStatic = $unit;
+ }
+
+ if( $unit != '' && !$unitClone ){
+ $unitClone = $current->unit->toString();
+ }
+
+ if( isset($values['']) && $unit !== '' && $unit === $unitStatic ){
+ $j = $values[''];
+ }elseif( isset($values[$unit]) ){
+ $j = $values[$unit];
+ }else{
+
+ if( $unitStatic && $unit !== $unitStatic ){
+ throw new Less_Exception_Compiler( 'incompatible types');
+ }
+ $values[$unit] = count($order);
+ $order[] = $current;
+ continue;
+ }
+
+
+ if( $order[$j]->unit->toString() === "" && $unitClone ){
+ $temp = new Less_Tree_Dimension( $order[$j]->value, $unitClone);
+ $referenceUnified = $temp->unify();
+ }else{
+ $referenceUnified = $order[$j]->unify();
+ }
+ if( ($isMin && $currentUnified->value < $referenceUnified->value) || (!$isMin && $currentUnified->value > $referenceUnified->value) ){
+ $order[$j] = $current;
+ }
+ }
+
+ if( count($order) == 1 ){
+ return $order[0];
+ }
+ $args = array();
+ foreach($order as $a){
+ $args[] = $a->toCSS($this->env);
+ }
+ return new Less_Tree_Anonymous( ($isMin?'min(':'max(') . implode(Less_Environment::$_outputMap[','],$args).')');
+ }
+
+ public function min(){
+ $args = func_get_args();
+ return $this->_minmax( true, $args );
+ }
+
+ public function max(){
+ $args = func_get_args();
+ return $this->_minmax( false, $args );
+ }
+
+ public function getunit($n){
+ return new Less_Tree_Anonymous($n->unit);
+ }
+
+ public function argb($color) {
+ if (!$color instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to argb must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return new Less_Tree_Anonymous($color->toARGB());
+ }
+
+ public function percentage($n) {
+ return new Less_Tree_Dimension($n->value * 100, '%');
+ }
+
+ public function color($n) {
+
+ if( $n instanceof Less_Tree_Quoted ){
+ $colorCandidate = $n->value;
+ $returnColor = Less_Tree_Color::fromKeyword($colorCandidate);
+ if( $returnColor ){
+ return $returnColor;
+ }
+ if( preg_match('/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/',$colorCandidate) ){
+ return new Less_Tree_Color(substr($colorCandidate, 1));
+ }
+ throw new Less_Exception_Compiler("argument must be a color keyword or 3/6 digit hex e.g. #FFF");
+ } else {
+ throw new Less_Exception_Compiler("argument must be a string");
+ }
+ }
+
+
+ public function iscolor($n) {
+ return $this->_isa($n, 'Less_Tree_Color');
+ }
+
+ public function isnumber($n) {
+ return $this->_isa($n, 'Less_Tree_Dimension');
+ }
+
+ public function isstring($n) {
+ return $this->_isa($n, 'Less_Tree_Quoted');
+ }
+
+ public function iskeyword($n) {
+ return $this->_isa($n, 'Less_Tree_Keyword');
+ }
+
+ public function isurl($n) {
+ return $this->_isa($n, 'Less_Tree_Url');
+ }
+
+ public function ispixel($n) {
+ return $this->isunit($n, 'px');
+ }
+
+ public function ispercentage($n) {
+ return $this->isunit($n, '%');
+ }
+
+ public function isem($n) {
+ return $this->isunit($n, 'em');
+ }
+
+ /**
+ * @param string $unit
+ */
+ public function isunit( $n, $unit ){
+ return ($n instanceof Less_Tree_Dimension) && $n->unit->is( ( property_exists($unit,'value') ? $unit->value : $unit) ) ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false');
+ }
+
+ /**
+ * @param string $type
+ */
+ private function _isa($n, $type) {
+ return is_a($n, $type) ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false');
+ }
+
+ public function tint($color, $amount) {
+ return $this->mix( $this->rgb(255,255,255), $color, $amount);
+ }
+
+ public function shade($color, $amount) {
+ return $this->mix($this->rgb(0, 0, 0), $color, $amount);
+ }
+
+ public function extract($values, $index ){
+ $index = (int)$index->value - 1; // (1-based index)
+ // handle non-array values as an array of length 1
+ // return 'undefined' if index is invalid
+ if( property_exists($values,'value') && is_array($values->value) ){
+ if( isset($values->value[$index]) ){
+ return $values->value[$index];
+ }
+ return null;
+
+ }elseif( (int)$index === 0 ){
+ return $values;
+ }
+
+ return null;
+ }
+
+ public function length($values){
+ $n = (property_exists($values,'value') && is_array($values->value)) ? count($values->value) : 1;
+ return new Less_Tree_Dimension($n);
+ }
+
+ public function datauri($mimetypeNode, $filePathNode = null ) {
+
+ $filePath = ( $filePathNode ? $filePathNode->value : null );
+ $mimetype = $mimetypeNode->value;
+
+ $args = 2;
+ if( !$filePath ){
+ $filePath = $mimetype;
+ $args = 1;
+ }
+
+ $filePath = str_replace('\\','/',$filePath);
+ if( Less_Environment::isPathRelative($filePath) ){
+
+ if( Less_Parser::$options['relativeUrls'] ){
+ $temp = $this->currentFileInfo['currentDirectory'];
+ } else {
+ $temp = $this->currentFileInfo['entryPath'];
+ }
+
+ if( !empty($temp) ){
+ $filePath = Less_Environment::normalizePath(rtrim($temp,'/').'/'.$filePath);
+ }
+
+ }
+
+
+ // detect the mimetype if not given
+ if( $args < 2 ){
+
+ /* incomplete
+ $mime = require('mime');
+ mimetype = mime.lookup(path);
+
+ // use base 64 unless it's an ASCII or UTF-8 format
+ var charset = mime.charsets.lookup(mimetype);
+ useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0;
+ if (useBase64) mimetype += ';base64';
+ */
+
+ $mimetype = Less_Mime::lookup($filePath);
+
+ $charset = Less_Mime::charsets_lookup($mimetype);
+ $useBase64 = !in_array($charset,array('US-ASCII', 'UTF-8'));
+ if( $useBase64 ){ $mimetype .= ';base64'; }
+
+ }else{
+ $useBase64 = preg_match('/;base64$/',$mimetype);
+ }
+
+
+ if( file_exists($filePath) ){
+ $buf = @file_get_contents($filePath);
+ }else{
+ $buf = false;
+ }
+
+
+ // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded
+ // and the --ieCompat flag is enabled, return a normal url() instead.
+ $DATA_URI_MAX_KB = 32;
+ $fileSizeInKB = round( strlen($buf) / 1024 );
+ if( $fileSizeInKB >= $DATA_URI_MAX_KB ){
+ $url = new Less_Tree_Url( ($filePathNode ? $filePathNode : $mimetypeNode), $this->currentFileInfo);
+ return $url->compile($this);
+ }
+
+ if( $buf ){
+ $buf = $useBase64 ? base64_encode($buf) : rawurlencode($buf);
+ $filePath = '"data:' . $mimetype . ',' . $buf . '"';
+ }
+
+ return new Less_Tree_Url( new Less_Tree_Anonymous($filePath) );
+ }
+
+ //svg-gradient
+ public function svggradient( $direction ){
+
+ $throw_message = 'svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]';
+ $arguments = func_get_args();
+
+ if( count($arguments) < 3 ){
+ throw new Less_Exception_Compiler( $throw_message );
+ }
+
+ $stops = array_slice($arguments,1);
+ $gradientType = 'linear';
+ $rectangleDimension = 'x="0" y="0" width="1" height="1"';
+ $useBase64 = true;
+ $directionValue = $direction->toCSS();
+
+
+ switch( $directionValue ){
+ case "to bottom":
+ $gradientDirectionSvg = 'x1="0%" y1="0%" x2="0%" y2="100%"';
+ break;
+ case "to right":
+ $gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="0%"';
+ break;
+ case "to bottom right":
+ $gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="100%"';
+ break;
+ case "to top right":
+ $gradientDirectionSvg = 'x1="0%" y1="100%" x2="100%" y2="0%"';
+ break;
+ case "ellipse":
+ case "ellipse at center":
+ $gradientType = "radial";
+ $gradientDirectionSvg = 'cx="50%" cy="50%" r="75%"';
+ $rectangleDimension = 'x="-50" y="-50" width="101" height="101"';
+ break;
+ default:
+ throw new Less_Exception_Compiler( "svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'" );
+ }
+
+ $returner = '<?xml version="1.0" ?>' .
+ '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">' .
+ '<' . $gradientType . 'Gradient id="gradient" gradientUnits="userSpaceOnUse" ' . $gradientDirectionSvg . '>';
+
+ for( $i = 0; $i < count($stops); $i++ ){
+ if( is_object($stops[$i]) && property_exists($stops[$i],'value') ){
+ $color = $stops[$i]->value[0];
+ $position = $stops[$i]->value[1];
+ }else{
+ $color = $stops[$i];
+ $position = null;
+ }
+
+ if( !($color instanceof Less_Tree_Color) || (!(($i === 0 || $i+1 === count($stops)) && $position === null) && !($position instanceof Less_Tree_Dimension)) ){
+ throw new Less_Exception_Compiler( $throw_message );
+ }
+ if( $position ){
+ $positionValue = $position->toCSS();
+ }elseif( $i === 0 ){
+ $positionValue = '0%';
+ }else{
+ $positionValue = '100%';
+ }
+ $alpha = $color->alpha;
+ $returner .= '<stop offset="' . $positionValue . '" stop-color="' . $color->toRGB() . '"' . ($alpha < 1 ? ' stop-opacity="' . $alpha . '"' : '') . '/>';
+ }
+
+ $returner .= '</' . $gradientType . 'Gradient><rect ' . $rectangleDimension . ' fill="url(#gradient)" /></svg>';
+
+
+ if( $useBase64 ){
+ $returner = "'data:image/svg+xml;base64,".base64_encode($returner)."'";
+ }else{
+ $returner = "'data:image/svg+xml,".$returner."'";
+ }
+
+ return new Less_Tree_URL( new Less_Tree_Anonymous( $returner ) );
+ }
+
+
+ /**
+ * Php version of javascript's `encodeURIComponent` function
+ *
+ * @param string $string The string to encode
+ * @return string The encoded string
+ */
+ public static function encodeURIComponent($string){
+ $revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')');
+ return strtr(rawurlencode($string), $revert);
+ }
+
+
+ // Color Blending
+ // ref: http://www.w3.org/TR/compositing-1
+
+ public function colorBlend( $mode, $color1, $color2 ){
+ $ab = $color1->alpha; // backdrop
+ $as = $color2->alpha; // source
+ $r = array(); // result
+
+ $ar = $as + $ab * (1 - $as);
+ for( $i = 0; $i < 3; $i++ ){
+ $cb = $color1->rgb[$i] / 255;
+ $cs = $color2->rgb[$i] / 255;
+ $cr = call_user_func( $mode, $cb, $cs );
+ if( $ar ){
+ $cr = ($as * $cs + $ab * ($cb - $as * ($cb + $cs - $cr))) / $ar;
+ }
+ $r[$i] = $cr * 255;
+ }
+
+ return new Less_Tree_Color($r, $ar);
+ }
+
+ public function multiply($color1 = null, $color2 = null ){
+ if (!$color1 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to multiply must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$color2 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The second argument to multiply must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return $this->colorBlend( array($this,'colorBlendMultiply'), $color1, $color2 );
+ }
+
+ private function colorBlendMultiply($cb, $cs){
+ return $cb * $cs;
+ }
+
+ public function screen($color1 = null, $color2 = null ){
+ if (!$color1 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to screen must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$color2 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The second argument to screen must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return $this->colorBlend( array($this,'colorBlendScreen'), $color1, $color2 );
+ }
+
+ private function colorBlendScreen( $cb, $cs){
+ return $cb + $cs - $cb * $cs;
+ }
+
+ public function overlay($color1 = null, $color2 = null){
+ if (!$color1 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to overlay must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$color2 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The second argument to overlay must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return $this->colorBlend( array($this,'colorBlendOverlay'), $color1, $color2 );
+ }
+
+ private function colorBlendOverlay($cb, $cs ){
+ $cb *= 2;
+ return ($cb <= 1)
+ ? $this->colorBlendMultiply($cb, $cs)
+ : $this->colorBlendScreen($cb - 1, $cs);
+ }
+
+ public function softlight($color1 = null, $color2 = null){
+ if (!$color1 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to softlight must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$color2 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The second argument to softlight must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return $this->colorBlend( array($this,'colorBlendSoftlight'), $color1, $color2 );
+ }
+
+ private function colorBlendSoftlight($cb, $cs ){
+ $d = 1;
+ $e = $cb;
+ if( $cs > 0.5 ){
+ $e = 1;
+ $d = ($cb > 0.25) ? sqrt($cb)
+ : ((16 * $cb - 12) * $cb + 4) * $cb;
+ }
+ return $cb - (1 - 2 * $cs) * $e * ($d - $cb);
+ }
+
+ public function hardlight($color1 = null, $color2 = null){
+ if (!$color1 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to hardlight must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$color2 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The second argument to hardlight must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return $this->colorBlend( array($this,'colorBlendHardlight'), $color1, $color2 );
+ }
+
+ private function colorBlendHardlight( $cb, $cs ){
+ return $this->colorBlendOverlay($cs, $cb);
+ }
+
+ public function difference($color1 = null, $color2 = null) {
+ if (!$color1 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to difference must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$color2 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The second argument to difference must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return $this->colorBlend( array($this,'colorBlendDifference'), $color1, $color2 );
+ }
+
+ private function colorBlendDifference( $cb, $cs ){
+ return abs($cb - $cs);
+ }
+
+ public function exclusion( $color1 = null, $color2 = null ){
+ if (!$color1 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to exclusion must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$color2 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The second argument to exclusion must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return $this->colorBlend( array($this,'colorBlendExclusion'), $color1, $color2 );
+ }
+
+ private function colorBlendExclusion( $cb, $cs ){
+ return $cb + $cs - 2 * $cb * $cs;
+ }
+
+ public function average($color1 = null, $color2 = null){
+ if (!$color1 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to average must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$color2 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The second argument to average must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return $this->colorBlend( array($this,'colorBlendAverage'), $color1, $color2 );
+ }
+
+ // non-w3c functions:
+ public function colorBlendAverage($cb, $cs ){
+ return ($cb + $cs) / 2;
+ }
+
+ public function negation($color1 = null, $color2 = null ){
+ if (!$color1 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The first argument to negation must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+ if (!$color2 instanceof Less_Tree_Color) {
+ throw new Less_Exception_Compiler('The second argument to negation must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
+ }
+
+ return $this->colorBlend( array($this,'colorBlendNegation'), $color1, $color2 );
+ }
+
+ public function colorBlendNegation($cb, $cs){
+ return 1 - abs($cb + $cs - 1);
+ }
+
+ // ~ End of Color Blending
+
+}
+
+
+/**
+ * Mime lookup
+ *
+ * @package Less
+ * @subpackage node
+ */
+class Less_Mime{
+
+ // this map is intentionally incomplete
+ // if you want more, install 'mime' dep
+ static $_types = array(
+ '.htm' => 'text/html',
+ '.html'=> 'text/html',
+ '.gif' => 'image/gif',
+ '.jpg' => 'image/jpeg',
+ '.jpeg'=> 'image/jpeg',
+ '.png' => 'image/png',
+ '.ttf' => 'application/x-font-ttf',
+ '.otf' => 'application/x-font-otf',
+ '.eot' => 'application/vnd.ms-fontobject',
+ '.woff' => 'application/x-font-woff',
+ '.svg' => 'image/svg+xml',
+ );
+
+ public static function lookup( $filepath ){
+ $parts = explode('.',$filepath);
+ $ext = '.'.strtolower(array_pop($parts));
+
+ if( !isset(self::$_types[$ext]) ){
+ return null;
+ }
+ return self::$_types[$ext];
+ }
+
+ public static function charsets_lookup( $type = null ){
+ // assumes all text types are UTF-8
+ return $type && preg_match('/^text\//',$type) ? 'UTF-8' : '';
+ }
+}
+
+
+/**
+ * Tree
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree{
+
+ public $cache_string;
+
+ public function toCSS(){
+ $output = new Less_Output();
+ $this->genCSS($output);
+ return $output->toString();
+ }
+
+
+ /**
+ * Generate CSS by adding it to the output object
+ *
+ * @param Less_Output $output The output
+ * @return void
+ */
+ public function genCSS($output){}
+
+
+ /**
+ * @param Less_Tree_Ruleset[] $rules
+ */
+ public static function outputRuleset( $output, $rules ){
+
+ $ruleCnt = count($rules);
+ Less_Environment::$tabLevel++;
+
+
+ // Compressed
+ if( Less_Parser::$options['compress'] ){
+ $output->add('{');
+ for( $i = 0; $i < $ruleCnt; $i++ ){
+ $rules[$i]->genCSS( $output );
+ }
+
+ $output->add( '}' );
+ Less_Environment::$tabLevel--;
+ return;
+ }
+
+
+ // Non-compressed
+ $tabSetStr = "\n".str_repeat( Less_Parser::$options['indentation'] , Less_Environment::$tabLevel-1 );
+ $tabRuleStr = $tabSetStr.Less_Parser::$options['indentation'];
+
+ $output->add( " {" );
+ for($i = 0; $i < $ruleCnt; $i++ ){
+ $output->add( $tabRuleStr );
+ $rules[$i]->genCSS( $output );
+ }
+ Less_Environment::$tabLevel--;
+ $output->add( $tabSetStr.'}' );
+
+ }
+
+ public function accept($visitor){}
+
+
+ public static function ReferencedArray($rules){
+ foreach($rules as $rule){
+ if( method_exists($rule, 'markReferenced') ){
+ $rule->markReferenced();
+ }
+ }
+ }
+
+
+ /**
+ * Requires php 5.3+
+ */
+ public static function __set_state($args){
+
+ $class = get_called_class();
+ $obj = new $class(null,null,null,null);
+ foreach($args as $key => $val){
+ $obj->$key = $val;
+ }
+ return $obj;
+ }
+
+}
+
+
+/**
+ * Parser output
+ *
+ * @package Less
+ * @subpackage output
+ */
+class Less_Output{
+
+ /**
+ * Output holder
+ *
+ * @var string
+ */
+ protected $strs = array();
+
+ /**
+ * Adds a chunk to the stack
+ *
+ * @param string $chunk The chunk to output
+ * @param Less_FileInfo $fileInfo The file information
+ * @param integer $index The index
+ * @param mixed $mapLines
+ */
+ public function add($chunk, $fileInfo = null, $index = 0, $mapLines = null){
+ $this->strs[] = $chunk;
+ }
+
+ /**
+ * Is the output empty?
+ *
+ * @return boolean
+ */
+ public function isEmpty(){
+ return count($this->strs) === 0;
+ }
+
+
+ /**
+ * Converts the output to string
+ *
+ * @return string
+ */
+ public function toString(){
+ return implode('',$this->strs);
+ }
+
+}
+
+/**
+ * Visitor
+ *
+ * @package Less
+ * @subpackage visitor
+ */
+class Less_Visitor{
+
+ protected $methods = array();
+ protected $_visitFnCache = array();
+
+ public function __construct(){
+ $this->_visitFnCache = get_class_methods(get_class($this));
+ $this->_visitFnCache = array_flip($this->_visitFnCache);
+ }
+
+ public function visitObj( $node ){
+
+ $funcName = 'visit'.$node->type;
+ if( isset($this->_visitFnCache[$funcName]) ){
+
+ $visitDeeper = true;
+ $this->$funcName( $node, $visitDeeper );
+
+ if( $visitDeeper ){
+ $node->accept($this);
+ }
+
+ $funcName = $funcName . "Out";
+ if( isset($this->_visitFnCache[$funcName]) ){
+ $this->$funcName( $node );
+ }
+
+ }else{
+ $node->accept($this);
+ }
+
+ return $node;
+ }
+
+ public function visitArray( $nodes ){
+
+ array_map( array($this,'visitObj'), $nodes);
+ return $nodes;
+ }
+}
+
+
+
+/**
+ * Replacing Visitor
+ *
+ * @package Less
+ * @subpackage visitor
+ */
+class Less_VisitorReplacing extends Less_Visitor{
+
+ public function visitObj( $node ){
+
+ $funcName = 'visit'.$node->type;
+ if( isset($this->_visitFnCache[$funcName]) ){
+
+ $visitDeeper = true;
+ $node = $this->$funcName( $node, $visitDeeper );
+
+ if( $node ){
+ if( $visitDeeper && is_object($node) ){
+ $node->accept($this);
+ }
+
+ $funcName = $funcName . "Out";
+ if( isset($this->_visitFnCache[$funcName]) ){
+ $this->$funcName( $node );
+ }
+ }
+
+ }else{
+ $node->accept($this);
+ }
+
+ return $node;
+ }
+
+ public function visitArray( $nodes ){
+
+ $newNodes = array();
+ foreach($nodes as $node){
+ $evald = $this->visitObj($node);
+ if( $evald ){
+ if( is_array($evald) ){
+ self::flatten($evald,$newNodes);
+ }else{
+ $newNodes[] = $evald;
+ }
+ }
+ }
+ return $newNodes;
+ }
+
+ public function flatten( $arr, &$out ){
+
+ foreach($arr as $item){
+ if( !is_array($item) ){
+ $out[] = $item;
+ continue;
+ }
+
+ foreach($item as $nestedItem){
+ if( is_array($nestedItem) ){
+ self::flatten( $nestedItem, $out);
+ }else{
+ $out[] = $nestedItem;
+ }
+ }
+ }
+
+ return $out;
+ }
+
+}
+
+
+
+
+/**
+ * Configurable
+ *
+ * @package Less
+ * @subpackage Core
+ */
+abstract class Less_Configurable {
+
+ /**
+ * Array of options
+ *
+ * @var array
+ */
+ protected $options = array();
+
+ /**
+ * Array of default options
+ *
+ * @var array
+ */
+ protected $defaultOptions = array();
+
+
+ /**
+ * Set options
+ *
+ * If $options is an object it will be converted into an array by called
+ * it's toArray method.
+ *
+ * @throws Exception
+ * @param array|object $options
+ *
+ */
+ public function setOptions($options){
+ $options = array_intersect_key($options,$this->defaultOptions);
+ $this->options = array_merge($this->defaultOptions, $this->options, $options);
+ }
+
+
+ /**
+ * Get an option value by name
+ *
+ * If the option is empty or not set a NULL value will be returned.
+ *
+ * @param string $name
+ * @param mixed $default Default value if confiuration of $name is not present
+ * @return mixed
+ */
+ public function getOption($name, $default = null){
+ if(isset($this->options[$name])){
+ return $this->options[$name];
+ }
+ return $default;
+ }
+
+
+ /**
+ * Set an option
+ *
+ * @param string $name
+ * @param mixed $value
+ */
+ public function setOption($name, $value){
+ $this->options[$name] = $value;
+ }
+
+}
+
+/**
+ * Alpha
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Alpha extends Less_Tree{
+ public $value;
+ public $type = 'Alpha';
+
+ public function __construct($val){
+ $this->value = $val;
+ }
+
+ //function accept( $visitor ){
+ // $this->value = $visitor->visit( $this->value );
+ //}
+
+ public function compile($env){
+
+ if( is_object($this->value) ){
+ $this->value = $this->value->compile($env);
+ }
+
+ return $this;
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+
+ $output->add( "alpha(opacity=" );
+
+ if( is_string($this->value) ){
+ $output->add( $this->value );
+ }else{
+ $this->value->genCSS( $output);
+ }
+
+ $output->add( ')' );
+ }
+
+ public function toCSS(){
+ return "alpha(opacity=" . (is_string($this->value) ? $this->value : $this->value->toCSS()) . ")";
+ }
+
+
+}
+
+/**
+ * Anonymous
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Anonymous extends Less_Tree{
+ public $value;
+ public $quote;
+ public $index;
+ public $mapLines;
+ public $currentFileInfo;
+ public $type = 'Anonymous';
+
+ /**
+ * @param integer $index
+ * @param boolean $mapLines
+ */
+ public function __construct($value, $index = null, $currentFileInfo = null, $mapLines = null ){
+ $this->value = $value;
+ $this->index = $index;
+ $this->mapLines = $mapLines;
+ $this->currentFileInfo = $currentFileInfo;
+ }
+
+ public function compile(){
+ return new Less_Tree_Anonymous($this->value, $this->index, $this->currentFileInfo, $this->mapLines);
+ }
+
+ public function compare($x){
+ if( !is_object($x) ){
+ return -1;
+ }
+
+ $left = $this->toCSS();
+ $right = $x->toCSS();
+
+ if( $left === $right ){
+ return 0;
+ }
+
+ return $left < $right ? -1 : 1;
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $output->add( $this->value, $this->currentFileInfo, $this->index, $this->mapLines );
+ }
+
+ public function toCSS(){
+ return $this->value;
+ }
+
+}
+
+
+/**
+ * Assignment
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Assignment extends Less_Tree{
+
+ public $key;
+ public $value;
+ public $type = 'Assignment';
+
+ public function __construct($key, $val) {
+ $this->key = $key;
+ $this->value = $val;
+ }
+
+ public function accept( $visitor ){
+ $this->value = $visitor->visitObj( $this->value );
+ }
+
+ public function compile($env) {
+ return new Less_Tree_Assignment( $this->key, $this->value->compile($env));
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $output->add( $this->key . '=' );
+ $this->value->genCSS( $output );
+ }
+
+ public function toCss(){
+ return $this->key . '=' . $this->value->toCSS();
+ }
+}
+
+
+/**
+ * Attribute
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Attribute extends Less_Tree{
+
+ public $key;
+ public $op;
+ public $value;
+ public $type = 'Attribute';
+
+ public function __construct($key, $op, $value){
+ $this->key = $key;
+ $this->op = $op;
+ $this->value = $value;
+ }
+
+ public function compile($env){
+
+ $key_obj = is_object($this->key);
+ $val_obj = is_object($this->value);
+
+ if( !$key_obj && !$val_obj ){
+ return $this;
+ }
+
+ return new Less_Tree_Attribute(
+ $key_obj ? $this->key->compile($env) : $this->key ,
+ $this->op,
+ $val_obj ? $this->value->compile($env) : $this->value);
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $output->add( $this->toCSS() );
+ }
+
+ public function toCSS(){
+ $value = $this->key;
+
+ if( $this->op ){
+ $value .= $this->op;
+ $value .= (is_object($this->value) ? $this->value->toCSS() : $this->value);
+ }
+
+ return '[' . $value . ']';
+ }
+}
+
+
+/**
+ * Call
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Call extends Less_Tree{
+ public $value;
+
+ protected $name;
+ protected $args;
+ protected $index;
+ protected $currentFileInfo;
+ public $type = 'Call';
+
+ public function __construct($name, $args, $index, $currentFileInfo = null ){
+ $this->name = $name;
+ $this->args = $args;
+ $this->index = $index;
+ $this->currentFileInfo = $currentFileInfo;
+ }
+
+ public function accept( $visitor ){
+ $this->args = $visitor->visitArray( $this->args );
+ }
+
+ //
+ // When evaluating a function call,
+ // we either find the function in `tree.functions` [1],
+ // in which case we call it, passing the evaluated arguments,
+ // or we simply print it out as it appeared originally [2].
+ //
+ // The *functions.js* file contains the built-in functions.
+ //
+ // The reason why we evaluate the arguments, is in the case where
+ // we try to pass a variable to a function, like: `saturate(@color)`.
+ // The function should receive the value, not the variable.
+ //
+ public function compile($env=null){
+ $args = array();
+ foreach($this->args as $a){
+ $args[] = $a->compile($env);
+ }
+
+ $nameLC = strtolower($this->name);
+ switch($nameLC){
+ case '%':
+ $nameLC = '_percent';
+ break;
+
+ case 'get-unit':
+ $nameLC = 'getunit';
+ break;
+
+ case 'data-uri':
+ $nameLC = 'datauri';
+ break;
+
+ case 'svg-gradient':
+ $nameLC = 'svggradient';
+ break;
+ }
+
+ $result = null;
+ if( $nameLC === 'default' ){
+ $result = Less_Tree_DefaultFunc::compile();
+
+ }else{
+
+ if( method_exists('Less_Functions',$nameLC) ){ // 1.
+ try {
+
+ $func = new Less_Functions($env, $this->currentFileInfo);
+ $result = call_user_func_array( array($func,$nameLC),$args);
+
+ } catch (Exception $e) {
+ throw new Less_Exception_Compiler('error evaluating function `' . $this->name . '` '.$e->getMessage().' index: '. $this->index);
+ }
+ } elseif( isset( $env->functions[$nameLC] ) && is_callable( $env->functions[$nameLC] ) ) {
+ try {
+ $result = call_user_func_array( $env->functions[$nameLC], $args );
+ } catch (Exception $e) {
+ throw new Less_Exception_Compiler('error evaluating function `' . $this->name . '` '.$e->getMessage().' index: '. $this->index);
+ }
+ }
+ }
+
+ if( $result !== null ){
+ return $result;
+ }
+
+
+ return new Less_Tree_Call( $this->name, $args, $this->index, $this->currentFileInfo );
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+
+ $output->add( $this->name . '(', $this->currentFileInfo, $this->index );
+ $args_len = count($this->args);
+ for($i = 0; $i < $args_len; $i++ ){
+ $this->args[$i]->genCSS( $output );
+ if( $i + 1 < $args_len ){
+ $output->add( ', ' );
+ }
+ }
+
+ $output->add( ')' );
+ }
+
+
+ //public function toCSS(){
+ // return $this->compile()->toCSS();
+ //}
+
+}
+
+
+/**
+ * Color
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Color extends Less_Tree{
+ public $rgb;
+ public $alpha;
+ public $isTransparentKeyword;
+ public $type = 'Color';
+
+ public function __construct($rgb, $a = 1, $isTransparentKeyword = null ){
+
+ if( $isTransparentKeyword ){
+ $this->rgb = $rgb;
+ $this->alpha = $a;
+ $this->isTransparentKeyword = true;
+ return;
+ }
+
+ $this->rgb = array();
+ if( is_array($rgb) ){
+ $this->rgb = $rgb;
+ }else if( strlen($rgb) == 6 ){
+ foreach(str_split($rgb, 2) as $c){
+ $this->rgb[] = hexdec($c);
+ }
+ }else{
+ foreach(str_split($rgb, 1) as $c){
+ $this->rgb[] = hexdec($c.$c);
+ }
+ }
+ $this->alpha = is_numeric($a) ? $a : 1;
+ }
+
+ public function compile(){
+ return $this;
+ }
+
+ public function luma(){
+ $r = $this->rgb[0] / 255;
+ $g = $this->rgb[1] / 255;
+ $b = $this->rgb[2] / 255;
+
+ $r = ($r <= 0.03928) ? $r / 12.92 : pow((($r + 0.055) / 1.055), 2.4);
+ $g = ($g <= 0.03928) ? $g / 12.92 : pow((($g + 0.055) / 1.055), 2.4);
+ $b = ($b <= 0.03928) ? $b / 12.92 : pow((($b + 0.055) / 1.055), 2.4);
+
+ return 0.2126 * $r + 0.7152 * $g + 0.0722 * $b;
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $output->add( $this->toCSS() );
+ }
+
+ public function toCSS( $doNotCompress = false ){
+ $compress = Less_Parser::$options['compress'] && !$doNotCompress;
+ $alpha = Less_Functions::fround( $this->alpha );
+
+
+ //
+ // If we have some transparency, the only way to represent it
+ // is via `rgba`. Otherwise, we use the hex representation,
+ // which has better compatibility with older browsers.
+ // Values are capped between `0` and `255`, rounded and zero-padded.
+ //
+ if( $alpha < 1 ){
+ if( ( $alpha === 0 || $alpha === 0.0 ) && isset($this->isTransparentKeyword) && $this->isTransparentKeyword ){
+ return 'transparent';
+ }
+
+ $values = array();
+ foreach($this->rgb as $c){
+ $values[] = Less_Functions::clamp( round($c), 255);
+ }
+ $values[] = $alpha;
+
+ $glue = ($compress ? ',' : ', ');
+ return "rgba(" . implode($glue, $values) . ")";
+ }else{
+
+ $color = $this->toRGB();
+
+ if( $compress ){
+
+ // Convert color to short format
+ if( $color[1] === $color[2] && $color[3] === $color[4] && $color[5] === $color[6]) {
+ $color = '#'.$color[1] . $color[3] . $color[5];
+ }
+ }
+
+ return $color;
+ }
+ }
+
+ //
+ // Operations have to be done per-channel, if not,
+ // channels will spill onto each other. Once we have
+ // our result, in the form of an integer triplet,
+ // we create a new Color node to hold the result.
+ //
+
+ /**
+ * @param string $op
+ */
+ public function operate( $op, $other) {
+ $rgb = array();
+ $alpha = $this->alpha * (1 - $other->alpha) + $other->alpha;
+ for ($c = 0; $c < 3; $c++) {
+ $rgb[$c] = Less_Functions::operate( $op, $this->rgb[$c], $other->rgb[$c]);
+ }
+ return new Less_Tree_Color($rgb, $alpha);
+ }
+
+ public function toRGB(){
+ return $this->toHex($this->rgb);
+ }
+
+ public function toHSL(){
+ $r = $this->rgb[0] / 255;
+ $g = $this->rgb[1] / 255;
+ $b = $this->rgb[2] / 255;
+ $a = $this->alpha;
+
+ $max = max($r, $g, $b);
+ $min = min($r, $g, $b);
+ $l = ($max + $min) / 2;
+ $d = $max - $min;
+
+ $h = $s = 0;
+ if( $max !== $min ){
+ $s = $l > 0.5 ? $d / (2 - $max - $min) : $d / ($max + $min);
+
+ switch ($max) {
+ case $r: $h = ($g - $b) / $d + ($g < $b ? 6 : 0); break;
+ case $g: $h = ($b - $r) / $d + 2; break;
+ case $b: $h = ($r - $g) / $d + 4; break;
+ }
+ $h /= 6;
+ }
+ return array('h' => $h * 360, 's' => $s, 'l' => $l, 'a' => $a );
+ }
+
+ //Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
+ public function toHSV() {
+ $r = $this->rgb[0] / 255;
+ $g = $this->rgb[1] / 255;
+ $b = $this->rgb[2] / 255;
+ $a = $this->alpha;
+
+ $max = max($r, $g, $b);
+ $min = min($r, $g, $b);
+
+ $v = $max;
+
+ $d = $max - $min;
+ if ($max === 0) {
+ $s = 0;
+ } else {
+ $s = $d / $max;
+ }
+
+ $h = 0;
+ if( $max !== $min ){
+ switch($max){
+ case $r: $h = ($g - $b) / $d + ($g < $b ? 6 : 0); break;
+ case $g: $h = ($b - $r) / $d + 2; break;
+ case $b: $h = ($r - $g) / $d + 4; break;
+ }
+ $h /= 6;
+ }
+ return array('h'=> $h * 360, 's'=> $s, 'v'=> $v, 'a' => $a );
+ }
+
+ public function toARGB(){
+ $argb = array_merge( (array) Less_Parser::round($this->alpha * 255), $this->rgb);
+ return $this->toHex( $argb );
+ }
+
+ public function compare($x){
+
+ if( !property_exists( $x, 'rgb' ) ){
+ return -1;
+ }
+
+
+ return ($x->rgb[0] === $this->rgb[0] &&
+ $x->rgb[1] === $this->rgb[1] &&
+ $x->rgb[2] === $this->rgb[2] &&
+ $x->alpha === $this->alpha) ? 0 : -1;
+ }
+
+ public function toHex( $v ){
+
+ $ret = '#';
+ foreach($v as $c){
+ $c = Less_Functions::clamp( Less_Parser::round($c), 255);
+ if( $c < 16 ){
+ $ret .= '0';
+ }
+ $ret .= dechex($c);
+ }
+
+ return $ret;
+ }
+
+
+ /**
+ * @param string $keyword
+ */
+ public static function fromKeyword( $keyword ){
+ $keyword = strtolower($keyword);
+
+ if( Less_Colors::hasOwnProperty($keyword) ){
+ // detect named color
+ return new Less_Tree_Color(substr(Less_Colors::color($keyword), 1));
+ }
+
+ if( $keyword === 'transparent' ){
+ return new Less_Tree_Color( array(0, 0, 0), 0, true);
+ }
+ }
+
+}
+
+
+/**
+ * Comment
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Comment extends Less_Tree{
+
+ public $value;
+ public $silent;
+ public $isReferenced;
+ public $currentFileInfo;
+ public $type = 'Comment';
+
+ public function __construct($value, $silent, $index = null, $currentFileInfo = null ){
+ $this->value = $value;
+ $this->silent = !! $silent;
+ $this->currentFileInfo = $currentFileInfo;
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ //if( $this->debugInfo ){
+ //$output->add( tree.debugInfo($env, $this), $this->currentFileInfo, $this->index);
+ //}
+ $output->add( trim($this->value) );//TODO shouldn't need to trim, we shouldn't grab the \n
+ }
+
+ public function toCSS(){
+ return Less_Parser::$options['compress'] ? '' : $this->value;
+ }
+
+ public function isSilent(){
+ $isReference = ($this->currentFileInfo && isset($this->currentFileInfo['reference']) && (!isset($this->isReferenced) || !$this->isReferenced) );
+ $isCompressed = Less_Parser::$options['compress'] && !preg_match('/^\/\*!/', $this->value);
+ return $this->silent || $isReference || $isCompressed;
+ }
+
+ public function compile(){
+ return $this;
+ }
+
+ public function markReferenced(){
+ $this->isReferenced = true;
+ }
+
+}
+
+
+/**
+ * Condition
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Condition extends Less_Tree{
+
+ public $op;
+ public $lvalue;
+ public $rvalue;
+ public $index;
+ public $negate;
+ public $type = 'Condition';
+
+ public function __construct($op, $l, $r, $i = 0, $negate = false) {
+ $this->op = trim($op);
+ $this->lvalue = $l;
+ $this->rvalue = $r;
+ $this->index = $i;
+ $this->negate = $negate;
+ }
+
+ public function accept($visitor){
+ $this->lvalue = $visitor->visitObj( $this->lvalue );
+ $this->rvalue = $visitor->visitObj( $this->rvalue );
+ }
+
+ public function compile($env) {
+ $a = $this->lvalue->compile($env);
+ $b = $this->rvalue->compile($env);
+
+ switch( $this->op ){
+ case 'and':
+ $result = $a && $b;
+ break;
+
+ case 'or':
+ $result = $a || $b;
+ break;
+
+ default:
+ if( Less_Parser::is_method($a, 'compare') ){
+ $result = $a->compare($b);
+ }elseif( Less_Parser::is_method($b, 'compare') ){
+ $result = $b->compare($a);
+ }else{
+ throw new Less_Exception_Compiler('Unable to perform comparison', null, $this->index);
+ }
+
+ switch ($result) {
+ case -1:
+ $result = $this->op === '<' || $this->op === '=<' || $this->op === '<=';
+ break;
+
+ case 0:
+ $result = $this->op === '=' || $this->op === '>=' || $this->op === '=<' || $this->op === '<=';
+ break;
+
+ case 1:
+ $result = $this->op === '>' || $this->op === '>=';
+ break;
+ }
+ break;
+ }
+
+ return $this->negate ? !$result : $result;
+ }
+
+}
+
+
+/**
+ * DefaultFunc
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_DefaultFunc{
+
+ static $error_;
+ static $value_;
+
+ public static function compile(){
+ if( self::$error_ ){
+ throw new Exception(self::$error_);
+ }
+ if( self::$value_ !== null ){
+ return self::$value_ ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false');
+ }
+ }
+
+ public static function value( $v ){
+ self::$value_ = $v;
+ }
+
+ public static function error( $e ){
+ self::$error_ = $e;
+ }
+
+ public static function reset(){
+ self::$value_ = self::$error_ = null;
+ }
+}
+
+/**
+ * DetachedRuleset
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_DetachedRuleset extends Less_Tree{
+
+ public $ruleset;
+ public $frames;
+ public $type = 'DetachedRuleset';
+
+ public function __construct( $ruleset, $frames = null ){
+ $this->ruleset = $ruleset;
+ $this->frames = $frames;
+ }
+
+ public function accept($visitor) {
+ $this->ruleset = $visitor->visitObj($this->ruleset);
+ }
+
+ public function compile($env){
+ if( $this->frames ){
+ $frames = $this->frames;
+ }else{
+ $frames = $env->frames;
+ }
+ return new Less_Tree_DetachedRuleset($this->ruleset, $frames);
+ }
+
+ public function callEval($env) {
+ if( $this->frames ){
+ return $this->ruleset->compile( $env->copyEvalEnv( array_merge($this->frames,$env->frames) ) );
+ }
+ return $this->ruleset->compile( $env );
+ }
+}
+
+
+
+/**
+ * Dimension
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Dimension extends Less_Tree{
+
+ public $value;
+ public $unit;
+ public $type = 'Dimension';
+
+ public function __construct($value, $unit = null){
+ $this->value = floatval($value);
+
+ if( $unit && ($unit instanceof Less_Tree_Unit) ){
+ $this->unit = $unit;
+ }elseif( $unit ){
+ $this->unit = new Less_Tree_Unit( array($unit) );
+ }else{
+ $this->unit = new Less_Tree_Unit( );
+ }
+ }
+
+ public function accept( $visitor ){
+ $this->unit = $visitor->visitObj( $this->unit );
+ }
+
+ public function compile(){
+ return $this;
+ }
+
+ public function toColor() {
+ return new Less_Tree_Color(array($this->value, $this->value, $this->value));
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+
+ if( Less_Parser::$options['strictUnits'] && !$this->unit->isSingular() ){
+ throw new Less_Exception_Compiler("Multiple units in dimension. Correct the units or use the unit function. Bad unit: ".$this->unit->toString());
+ }
+
+ $value = Less_Functions::fround( $this->value );
+ $strValue = (string)$value;
+
+ if( $value !== 0 && $value < 0.000001 && $value > -0.000001 ){
+ // would be output 1e-6 etc.
+ $strValue = number_format($strValue,10);
+ $strValue = preg_replace('/\.?0+$/','', $strValue);
+ }
+
+ if( Less_Parser::$options['compress'] ){
+ // Zero values doesn't need a unit
+ if( $value === 0 && $this->unit->isLength() ){
+ $output->add( $strValue );
+ return $strValue;
+ }
+
+ // Float values doesn't need a leading zero
+ if( $value > 0 && $value < 1 && $strValue[0] === '0' ){
+ $strValue = substr($strValue,1);
+ }
+ }
+
+ $output->add( $strValue );
+ $this->unit->genCSS( $output );
+ }
+
+ public function __toString(){
+ return $this->toCSS();
+ }
+
+ // In an operation between two Dimensions,
+ // we default to the first Dimension's unit,
+ // so `1px + 2em` will yield `3px`.
+
+ /**
+ * @param string $op
+ */
+ public function operate( $op, $other){
+
+ $value = Less_Functions::operate( $op, $this->value, $other->value);
+ $unit = clone $this->unit;
+
+ if( $op === '+' || $op === '-' ){
+
+ if( !$unit->numerator && !$unit->denominator ){
+ $unit->numerator = $other->unit->numerator;
+ $unit->denominator = $other->unit->denominator;
+ }elseif( !$other->unit->numerator && !$other->unit->denominator ){
+ // do nothing
+ }else{
+ $other = $other->convertTo( $this->unit->usedUnits());
+
+ if( Less_Parser::$options['strictUnits'] && $other->unit->toString() !== $unit->toCSS() ){
+ throw new Less_Exception_Compiler("Incompatible units. Change the units or use the unit function. Bad units: '" . $unit->toString() . "' and " . $other->unit->toString() . "'.");
+ }
+
+ $value = Less_Functions::operate( $op, $this->value, $other->value);
+ }
+ }elseif( $op === '*' ){
+ $unit->numerator = array_merge($unit->numerator, $other->unit->numerator);
+ $unit->denominator = array_merge($unit->denominator, $other->unit->denominator);
+ sort($unit->numerator);
+ sort($unit->denominator);
+ $unit->cancel();
+ }elseif( $op === '/' ){
+ $unit->numerator = array_merge($unit->numerator, $other->unit->denominator);
+ $unit->denominator = array_merge($unit->denominator, $other->unit->numerator);
+ sort($unit->numerator);
+ sort($unit->denominator);
+ $unit->cancel();
+ }
+ return new Less_Tree_Dimension( $value, $unit);
+ }
+
+ public function compare($other) {
+ if ($other instanceof Less_Tree_Dimension) {
+
+ if( $this->unit->isEmpty() || $other->unit->isEmpty() ){
+ $a = $this;
+ $b = $other;
+ } else {
+ $a = $this->unify();
+ $b = $other->unify();
+ if( $a->unit->compare($b->unit) !== 0 ){
+ return -1;
+ }
+ }
+ $aValue = $a->value;
+ $bValue = $b->value;
+
+ if ($bValue > $aValue) {
+ return -1;
+ } elseif ($bValue < $aValue) {
+ return 1;
+ } else {
+ return 0;
+ }
+ } else {
+ return -1;
+ }
+ }
+
+ public function unify() {
+ return $this->convertTo(array('length'=> 'px', 'duration'=> 's', 'angle' => 'rad' ));
+ }
+
+ public function convertTo($conversions) {
+ $value = $this->value;
+ $unit = clone $this->unit;
+
+ if( is_string($conversions) ){
+ $derivedConversions = array();
+ foreach( Less_Tree_UnitConversions::$groups as $i ){
+ if( isset(Less_Tree_UnitConversions::${$i}[$conversions]) ){
+ $derivedConversions = array( $i => $conversions);
+ }
+ }
+ $conversions = $derivedConversions;
+ }
+
+
+ foreach($conversions as $groupName => $targetUnit){
+ $group = Less_Tree_UnitConversions::${$groupName};
+
+ //numerator
+ foreach($unit->numerator as $i => $atomicUnit){
+ $atomicUnit = $unit->numerator[$i];
+ if( !isset($group[$atomicUnit]) ){
+ continue;
+ }
+
+ $value = $value * ($group[$atomicUnit] / $group[$targetUnit]);
+
+ $unit->numerator[$i] = $targetUnit;
+ }
+
+ //denominator
+ foreach($unit->denominator as $i => $atomicUnit){
+ $atomicUnit = $unit->denominator[$i];
+ if( !isset($group[$atomicUnit]) ){
+ continue;
+ }
+
+ $value = $value / ($group[$atomicUnit] / $group[$targetUnit]);
+
+ $unit->denominator[$i] = $targetUnit;
+ }
+ }
+
+ $unit->cancel();
+
+ return new Less_Tree_Dimension( $value, $unit);
+ }
+}
+
+
+/**
+ * Directive
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Directive extends Less_Tree{
+
+ public $name;
+ public $value;
+ public $rules;
+ public $index;
+ public $isReferenced;
+ public $currentFileInfo;
+ public $debugInfo;
+ public $type = 'Directive';
+
+ public function __construct($name, $value = null, $rules, $index = null, $currentFileInfo = null, $debugInfo = null ){
+ $this->name = $name;
+ $this->value = $value;
+ if( $rules ){
+ $this->rules = $rules;
+ $this->rules->allowImports = true;
+ }
+
+ $this->index = $index;
+ $this->currentFileInfo = $currentFileInfo;
+ $this->debugInfo = $debugInfo;
+ }
+
+
+ public function accept( $visitor ){
+ if( $this->rules ){
+ $this->rules = $visitor->visitObj( $this->rules );
+ }
+ if( $this->value ){
+ $this->value = $visitor->visitObj( $this->value );
+ }
+ }
+
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $value = $this->value;
+ $rules = $this->rules;
+ $output->add( $this->name, $this->currentFileInfo, $this->index );
+ if( $this->value ){
+ $output->add(' ');
+ $this->value->genCSS($output);
+ }
+ if( $this->rules ){
+ Less_Tree::outputRuleset( $output, array($this->rules));
+ } else {
+ $output->add(';');
+ }
+ }
+
+ public function compile($env){
+
+ $value = $this->value;
+ $rules = $this->rules;
+ if( $value ){
+ $value = $value->compile($env);
+ }
+
+ if( $rules ){
+ $rules = $rules->compile($env);
+ $rules->root = true;
+ }
+
+ return new Less_Tree_Directive( $this->name, $value, $rules, $this->index, $this->currentFileInfo, $this->debugInfo );
+ }
+
+
+ public function variable($name){
+ if( $this->rules ){
+ return $this->rules->variable($name);
+ }
+ }
+
+ public function find($selector){
+ if( $this->rules ){
+ return $this->rules->find($selector, $this);
+ }
+ }
+
+ //rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); },
+
+ public function markReferenced(){
+ $this->isReferenced = true;
+ if( $this->rules ){
+ Less_Tree::ReferencedArray($this->rules->rules);
+ }
+ }
+
+}
+
+
+/**
+ * Element
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Element extends Less_Tree{
+
+ public $combinator = '';
+ public $value = '';
+ public $index;
+ public $currentFileInfo;
+ public $type = 'Element';
+
+ public $value_is_object = false;
+
+ public function __construct($combinator, $value, $index = null, $currentFileInfo = null ){
+
+ $this->value = $value;
+ $this->value_is_object = is_object($value);
+
+ if( $combinator ){
+ $this->combinator = $combinator;
+ }
+
+ $this->index = $index;
+ $this->currentFileInfo = $currentFileInfo;
+ }
+
+ public function accept( $visitor ){
+ if( $this->value_is_object ){ //object or string
+ $this->value = $visitor->visitObj( $this->value );
+ }
+ }
+
+ public function compile($env){
+
+ if( Less_Environment::$mixin_stack ){
+ return new Less_Tree_Element($this->combinator, ($this->value_is_object ? $this->value->compile($env) : $this->value), $this->index, $this->currentFileInfo );
+ }
+
+ if( $this->value_is_object ){
+ $this->value = $this->value->compile($env);
+ }
+
+ return $this;
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $output->add( $this->toCSS(), $this->currentFileInfo, $this->index );
+ }
+
+ public function toCSS(){
+
+ if( $this->value_is_object ){
+ $value = $this->value->toCSS();
+ }else{
+ $value = $this->value;
+ }
+
+
+ if( $value === '' && $this->combinator && $this->combinator === '&' ){
+ return '';
+ }
+
+
+ return Less_Environment::$_outputMap[$this->combinator] . $value;
+ }
+
+}
+
+
+/**
+ * Expression
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Expression extends Less_Tree{
+
+ public $value = array();
+ public $parens = false;
+ public $parensInOp = false;
+ public $type = 'Expression';
+
+ public function __construct( $value, $parens = null ){
+ $this->value = $value;
+ $this->parens = $parens;
+ }
+
+ public function accept( $visitor ){
+ $this->value = $visitor->visitArray( $this->value );
+ }
+
+ public function compile($env) {
+
+ $doubleParen = false;
+
+ if( $this->parens && !$this->parensInOp ){
+ Less_Environment::$parensStack++;
+ }
+
+ $returnValue = null;
+ if( $this->value ){
+
+ $count = count($this->value);
+
+ if( $count > 1 ){
+
+ $ret = array();
+ foreach($this->value as $e){
+ $ret[] = $e->compile($env);
+ }
+ $returnValue = new Less_Tree_Expression($ret);
+
+ }else{
+
+ if( ($this->value[0] instanceof Less_Tree_Expression) && $this->value[0]->parens && !$this->value[0]->parensInOp ){
+ $doubleParen = true;
+ }
+
+ $returnValue = $this->value[0]->compile($env);
+ }
+
+ } else {
+ $returnValue = $this;
+ }
+
+ if( $this->parens ){
+ if( !$this->parensInOp ){
+ Less_Environment::$parensStack--;
+
+ }elseif( !Less_Environment::isMathOn() && !$doubleParen ){
+ $returnValue = new Less_Tree_Paren($returnValue);
+
+ }
+ }
+ return $returnValue;
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $val_len = count($this->value);
+ for( $i = 0; $i < $val_len; $i++ ){
+ $this->value[$i]->genCSS( $output );
+ if( $i + 1 < $val_len ){
+ $output->add( ' ' );
+ }
+ }
+ }
+
+ public function throwAwayComments() {
+
+ if( is_array($this->value) ){
+ $new_value = array();
+ foreach($this->value as $v){
+ if( $v instanceof Less_Tree_Comment ){
+ continue;
+ }
+ $new_value[] = $v;
+ }
+ $this->value = $new_value;
+ }
+ }
+}
+
+
+/**
+ * Extend
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Extend extends Less_Tree{
+
+ public $selector;
+ public $option;
+ public $index;
+ public $selfSelectors = array();
+ public $allowBefore;
+ public $allowAfter;
+ public $firstExtendOnThisSelectorPath;
+ public $type = 'Extend';
+ public $ruleset;
+
+
+ public $object_id;
+ public $parent_ids = array();
+
+ /**
+ * @param integer $index
+ */
+ public function __construct($selector, $option, $index){
+ static $i = 0;
+ $this->selector = $selector;
+ $this->option = $option;
+ $this->index = $index;
+
+ switch($option){
+ case "all":
+ $this->allowBefore = true;
+ $this->allowAfter = true;
+ break;
+ default:
+ $this->allowBefore = false;
+ $this->allowAfter = false;
+ break;
+ }
+
+ $this->object_id = $i++;
+ $this->parent_ids = array($this->object_id);
+ }
+
+ public function accept( $visitor ){
+ $this->selector = $visitor->visitObj( $this->selector );
+ }
+
+ public function compile( $env ){
+ Less_Parser::$has_extends = true;
+ $this->selector = $this->selector->compile($env);
+ return $this;
+ //return new Less_Tree_Extend( $this->selector->compile($env), $this->option, $this->index);
+ }
+
+ public function findSelfSelectors( $selectors ){
+ $selfElements = array();
+
+
+ for( $i = 0, $selectors_len = count($selectors); $i < $selectors_len; $i++ ){
+ $selectorElements = $selectors[$i]->elements;
+ // duplicate the logic in genCSS function inside the selector node.
+ // future TODO - move both logics into the selector joiner visitor
+ if( $i && $selectorElements && $selectorElements[0]->combinator === "") {
+ $selectorElements[0]->combinator = ' ';
+ }
+ $selfElements = array_merge( $selfElements, $selectors[$i]->elements );
+ }
+
+ $this->selfSelectors = array(new Less_Tree_Selector($selfElements));
+ }
+
+}
+
+/**
+ * CSS @import node
+ *
+ * The general strategy here is that we don't want to wait
+ * for the parsing to be completed, before we start importing
+ * the file. That's because in the context of a browser,
+ * most of the time will be spent waiting for the server to respond.
+ *
+ * On creation, we push the import path to our import queue, though
+ * `import,push`, we also pass it a callback, which it'll call once
+ * the file has been fetched, and parsed.
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Import extends Less_Tree{
+
+ public $options;
+ public $index;
+ public $path;
+ public $features;
+ public $currentFileInfo;
+ public $css;
+ public $skip;
+ public $root;
+ public $type = 'Import';
+
+ public function __construct($path, $features, $options, $index, $currentFileInfo = null ){
+ $this->options = $options;
+ $this->index = $index;
+ $this->path = $path;
+ $this->features = $features;
+ $this->currentFileInfo = $currentFileInfo;
+
+ if( is_array($options) ){
+ $this->options += array('inline'=>false);
+
+ if( isset($this->options['less']) || $this->options['inline'] ){
+ $this->css = !isset($this->options['less']) || !$this->options['less'] || $this->options['inline'];
+ } else {
+ $pathValue = $this->getPath();
+ if( $pathValue && preg_match('/css([\?;].*)?$/',$pathValue) ){
+ $this->css = true;
+ }
+ }
+ }
+ }
+
+//
+// The actual import node doesn't return anything, when converted to CSS.
+// The reason is that it's used at the evaluation stage, so that the rules
+// it imports can be treated like any other rules.
+//
+// In `eval`, we make sure all Import nodes get evaluated, recursively, so
+// we end up with a flat structure, which can easily be imported in the parent
+// ruleset.
+//
+
+ public function accept($visitor){
+
+ if( $this->features ){
+ $this->features = $visitor->visitObj($this->features);
+ }
+ $this->path = $visitor->visitObj($this->path);
+
+ if( !$this->options['inline'] && $this->root ){
+ $this->root = $visitor->visit($this->root);
+ }
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ if( $this->css ){
+
+ $output->add( '@import ', $this->currentFileInfo, $this->index );
+
+ $this->path->genCSS( $output );
+ if( $this->features ){
+ $output->add( ' ' );
+ $this->features->genCSS( $output );
+ }
+ $output->add( ';' );
+ }
+ }
+
+ public function toCSS(){
+ $features = $this->features ? ' ' . $this->features->toCSS() : '';
+
+ if ($this->css) {
+ return "@import " . $this->path->toCSS() . $features . ";\n";
+ } else {
+ return "";
+ }
+ }
+
+ /**
+ * @return string
+ */
+ public function getPath(){
+ if ($this->path instanceof Less_Tree_Quoted) {
+ $path = $this->path->value;
+ $path = ( isset($this->css) || preg_match('/(\.[a-z]*$)|([\?;].*)$/',$path)) ? $path : $path . '.less';
+ } else if ($this->path instanceof Less_Tree_URL) {
+ $path = $this->path->value->value;
+ }else{
+ return null;
+ }
+
+ //remove query string and fragment
+ return preg_replace('/[\?#][^\?]*$/','',$path);
+ }
+
+ public function compileForImport( $env ){
+ return new Less_Tree_Import( $this->path->compile($env), $this->features, $this->options, $this->index, $this->currentFileInfo);
+ }
+
+ public function compilePath($env) {
+ $path = $this->path->compile($env);
+ $rootpath = '';
+ if( $this->currentFileInfo && $this->currentFileInfo['rootpath'] ){
+ $rootpath = $this->currentFileInfo['rootpath'];
+ }
+
+
+ if( !($path instanceof Less_Tree_URL) ){
+ if( $rootpath ){
+ $pathValue = $path->value;
+ // Add the base path if the import is relative
+ if( $pathValue && Less_Environment::isPathRelative($pathValue) ){
+ $path->value = $this->currentFileInfo['uri_root'].$pathValue;
+ }
+ }
+ $path->value = Less_Environment::normalizePath($path->value);
+ }
+
+
+
+ return $path;
+ }
+
+ public function compile( $env ){
+
+ $evald = $this->compileForImport($env);
+
+ //get path & uri
+ $path_and_uri = null;
+ if( is_callable(Less_Parser::$options['import_callback']) ){
+ $path_and_uri = call_user_func(Less_Parser::$options['import_callback'],$evald);
+ }
+
+ if( !$path_and_uri ){
+ $path_and_uri = $evald->PathAndUri();
+ }
+
+ if( $path_and_uri ){
+ list($full_path, $uri) = $path_and_uri;
+ }else{
+ $full_path = $uri = $evald->getPath();
+ }
+
+
+ //import once
+ if( $evald->skip( $full_path, $env) ){
+ return array();
+ }
+
+ if( $this->options['inline'] ){
+ //todo needs to reference css file not import
+ //$contents = new Less_Tree_Anonymous($this->root, 0, array('filename'=>$this->importedFilename), true );
+
+ Less_Parser::AddParsedFile($full_path);
+ $contents = new Less_Tree_Anonymous( file_get_contents($full_path), 0, array(), true );
+
+ if( $this->features ){
+ return new Less_Tree_Media( array($contents), $this->features->value );
+ }
+
+ return array( $contents );
+ }
+
+ // optional (need to be before "CSS" to support optional CSS imports. CSS should be checked only if empty($this->currentFileInfo))
+ if( isset($this->options['optional']) && $this->options['optional'] && !file_exists($full_path) && (!$evald->css || !empty($this->currentFileInfo))) {
+ return array();
+ }
+
+
+ // css ?
+ if( $evald->css ){
+ $features = ( $evald->features ? $evald->features->compile($env) : null );
+ return new Less_Tree_Import( $this->compilePath( $env), $features, $this->options, $this->index);
+ }
+
+
+ return $this->ParseImport( $full_path, $uri, $env );
+ }
+
+
+ /**
+ * Using the import directories, get the full absolute path and uri of the import
+ *
+ * @param Less_Tree_Import $evald
+ */
+ public function PathAndUri(){
+
+ $evald_path = $this->getPath();
+
+ if( $evald_path ){
+
+ $import_dirs = array();
+
+ if( Less_Environment::isPathRelative($evald_path) ){
+ //if the path is relative, the file should be in the current directory
+ $import_dirs[ $this->currentFileInfo['currentDirectory'] ] = $this->currentFileInfo['uri_root'];
+
+ }else{
+ //otherwise, the file should be relative to the server root
+ $import_dirs[ $this->currentFileInfo['entryPath'] ] = $this->currentFileInfo['entryUri'];
+
+ //if the user supplied entryPath isn't the actual root
+ $import_dirs[ $_SERVER['DOCUMENT_ROOT'] ] = '';
+
+ }
+
+ // always look in user supplied import directories
+ $import_dirs = array_merge( $import_dirs, Less_Parser::$options['import_dirs'] );
+
+
+ foreach( $import_dirs as $rootpath => $rooturi){
+ if( is_callable($rooturi) ){
+ list($path, $uri) = call_user_func($rooturi, $evald_path);
+ if( is_string($path) ){
+ $full_path = $path;
+ return array( $full_path, $uri );
+ }
+ }elseif( !empty($rootpath) ){
+
+
+ if( $rooturi ){
+ if( strpos($evald_path,$rooturi) === 0 ){
+ $evald_path = substr( $evald_path, strlen($rooturi) );
+ }
+ }
+
+ $path = rtrim($rootpath,'/\\').'/'.ltrim($evald_path,'/\\');
+
+ if( file_exists($path) ){
+ $full_path = Less_Environment::normalizePath($path);
+ $uri = Less_Environment::normalizePath(dirname($rooturi.$evald_path));
+ return array( $full_path, $uri );
+ } elseif( file_exists($path.'.less') ){
+ $full_path = Less_Environment::normalizePath($path.'.less');
+ $uri = Less_Environment::normalizePath(dirname($rooturi.$evald_path.'.less'));
+ return array( $full_path, $uri );
+ }
+ }
+ }
+ }
+ }
+
+
+ /**
+ * Parse the import url and return the rules
+ *
+ * @return Less_Tree_Media|array
+ */
+ public function ParseImport( $full_path, $uri, $env ){
+
+ $import_env = clone $env;
+ if( (isset($this->options['reference']) && $this->options['reference']) || isset($this->currentFileInfo['reference']) ){
+ $import_env->currentFileInfo['reference'] = true;
+ }
+
+ if( (isset($this->options['multiple']) && $this->options['multiple']) ){
+ $import_env->importMultiple = true;
+ }
+
+ $parser = new Less_Parser($import_env);
+ $root = $parser->parseFile($full_path, $uri, true);
+
+
+ $ruleset = new Less_Tree_Ruleset(array(), $root->rules );
+ $ruleset->evalImports($import_env);
+
+ return $this->features ? new Less_Tree_Media($ruleset->rules, $this->features->value) : $ruleset->rules;
+ }
+
+
+ /**
+ * Should the import be skipped?
+ *
+ * @return boolean|null
+ */
+ private function Skip($path, $env){
+
+ $path = Less_Parser::winPath(realpath($path));
+
+ if( $path && Less_Parser::FileParsed($path) ){
+
+ if( isset($this->currentFileInfo['reference']) ){
+ return true;
+ }
+
+ return !isset($this->options['multiple']) && !$env->importMultiple;
+ }
+
+ }
+}
+
+
+/**
+ * Javascript
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Javascript extends Less_Tree{
+
+ public $type = 'Javascript';
+ public $escaped;
+ public $expression;
+ public $index;
+
+ /**
+ * @param boolean $index
+ * @param boolean $escaped
+ */
+ public function __construct($string, $index, $escaped){
+ $this->escaped = $escaped;
+ $this->expression = $string;
+ $this->index = $index;
+ }
+
+ public function compile(){
+ return new Less_Tree_Anonymous('/* Sorry, can not do JavaScript evaluation in PHP... :( */');
+ }
+
+}
+
+
+/**
+ * Keyword
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Keyword extends Less_Tree{
+
+ public $value;
+ public $type = 'Keyword';
+
+ /**
+ * @param string $value
+ */
+ public function __construct($value){
+ $this->value = $value;
+ }
+
+ public function compile(){
+ return $this;
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+
+ if( $this->value === '%') {
+ throw new Less_Exception_Compiler("Invalid % without number");
+ }
+
+ $output->add( $this->value );
+ }
+
+ public function compare($other) {
+ if ($other instanceof Less_Tree_Keyword) {
+ return $other->value === $this->value ? 0 : 1;
+ } else {
+ return -1;
+ }
+ }
+}
+
+
+/**
+ * Media
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Media extends Less_Tree{
+
+ public $features;
+ public $rules;
+ public $index;
+ public $currentFileInfo;
+ public $isReferenced;
+ public $type = 'Media';
+
+ public function __construct($value = array(), $features = array(), $index = null, $currentFileInfo = null ){
+
+ $this->index = $index;
+ $this->currentFileInfo = $currentFileInfo;
+
+ $selectors = $this->emptySelectors();
+
+ $this->features = new Less_Tree_Value($features);
+
+ $this->rules = array(new Less_Tree_Ruleset($selectors, $value));
+ $this->rules[0]->allowImports = true;
+ }
+
+ public function accept( $visitor ){
+ $this->features = $visitor->visitObj($this->features);
+ $this->rules = $visitor->visitArray($this->rules);
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+
+ $output->add( '@media ', $this->currentFileInfo, $this->index );
+ $this->features->genCSS( $output );
+ Less_Tree::outputRuleset( $output, $this->rules);
+
+ }
+
+ public function compile($env) {
+
+ $media = new Less_Tree_Media(array(), array(), $this->index, $this->currentFileInfo );
+
+ $strictMathBypass = false;
+ if( Less_Parser::$options['strictMath'] === false) {
+ $strictMathBypass = true;
+ Less_Parser::$options['strictMath'] = true;
+ }
+
+ $media->features = $this->features->compile($env);
+
+ if( $strictMathBypass ){
+ Less_Parser::$options['strictMath'] = false;
+ }
+
+ $env->mediaPath[] = $media;
+ $env->mediaBlocks[] = $media;
+
+ array_unshift($env->frames, $this->rules[0]);
+ $media->rules = array($this->rules[0]->compile($env));
+ array_shift($env->frames);
+
+ array_pop($env->mediaPath);
+
+ return !$env->mediaPath ? $media->compileTop($env) : $media->compileNested($env);
+ }
+
+ public function variable($name) {
+ return $this->rules[0]->variable($name);
+ }
+
+ public function find($selector) {
+ return $this->rules[0]->find($selector, $this);
+ }
+
+ public function emptySelectors(){
+ $el = new Less_Tree_Element('','&', $this->index, $this->currentFileInfo );
+ $sels = array( new Less_Tree_Selector(array($el), array(), null, $this->index, $this->currentFileInfo) );
+ $sels[0]->mediaEmpty = true;
+ return $sels;
+ }
+
+ public function markReferenced(){
+ $this->rules[0]->markReferenced();
+ $this->isReferenced = true;
+ Less_Tree::ReferencedArray($this->rules[0]->rules);
+ }
+
+ // evaltop
+ public function compileTop($env) {
+ $result = $this;
+
+ if (count($env->mediaBlocks) > 1) {
+ $selectors = $this->emptySelectors();
+ $result = new Less_Tree_Ruleset($selectors, $env->mediaBlocks);
+ $result->multiMedia = true;
+ }
+
+ $env->mediaBlocks = array();
+ $env->mediaPath = array();
+
+ return $result;
+ }
+
+ public function compileNested($env) {
+ $path = array_merge($env->mediaPath, array($this));
+
+ // Extract the media-query conditions separated with `,` (OR).
+ foreach ($path as $key => $p) {
+ $value = $p->features instanceof Less_Tree_Value ? $p->features->value : $p->features;
+ $path[$key] = is_array($value) ? $value : array($value);
+ }
+
+ // Trace all permutations to generate the resulting media-query.
+ //
+ // (a, b and c) with nested (d, e) ->
+ // a and d
+ // a and e
+ // b and c and d
+ // b and c and e
+
+ $permuted = $this->permute($path);
+ $expressions = array();
+ foreach($permuted as $path){
+
+ for( $i=0, $len=count($path); $i < $len; $i++){
+ $path[$i] = Less_Parser::is_method($path[$i], 'toCSS') ? $path[$i] : new Less_Tree_Anonymous($path[$i]);
+ }
+
+ for( $i = count($path) - 1; $i > 0; $i-- ){
+ array_splice($path, $i, 0, array(new Less_Tree_Anonymous('and')));
+ }
+
+ $expressions[] = new Less_Tree_Expression($path);
+ }
+ $this->features = new Less_Tree_Value($expressions);
+
+
+
+ // Fake a tree-node that doesn't output anything.
+ return new Less_Tree_Ruleset(array(), array());
+ }
+
+ public function permute($arr) {
+ if (!$arr)
+ return array();
+
+ if (count($arr) == 1)
+ return $arr[0];
+
+ $result = array();
+ $rest = $this->permute(array_slice($arr, 1));
+ foreach ($rest as $r) {
+ foreach ($arr[0] as $a) {
+ $result[] = array_merge(
+ is_array($a) ? $a : array($a),
+ is_array($r) ? $r : array($r)
+ );
+ }
+ }
+
+ return $result;
+ }
+
+ public function bubbleSelectors($selectors) {
+
+ if( !$selectors) return;
+
+ $this->rules = array(new Less_Tree_Ruleset( $selectors, array($this->rules[0])));
+ }
+
+}
+
+
+/**
+ * A simple css name-value pair
+ * ex: width:100px;
+ *
+ * In bootstrap, there are about 600-1,000 simple name-value pairs (depending on how forgiving the match is) -vs- 6,020 dynamic rules (Less_Tree_Rule)
+ * Using the name-value object can speed up bootstrap compilation slightly, but it breaks color keyword interpretation: color:red -> color:#FF0000;
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_NameValue extends Less_Tree{
+
+ public $name;
+ public $value;
+ public $index;
+ public $currentFileInfo;
+ public $type = 'NameValue';
+ public $important = '';
+
+ public function __construct($name, $value = null, $index = null, $currentFileInfo = null ){
+ $this->name = $name;
+ $this->value = $value;
+ $this->index = $index;
+ $this->currentFileInfo = $currentFileInfo;
+ }
+
+ public function genCSS( $output ){
+
+ $output->add(
+ $this->name
+ . Less_Environment::$_outputMap[': ']
+ . $this->value
+ . $this->important
+ . (((Less_Environment::$lastRule && Less_Parser::$options['compress'])) ? "" : ";")
+ , $this->currentFileInfo, $this->index);
+ }
+
+ public function compile ($env){
+ return $this;
+ }
+
+ public function makeImportant(){
+ $new = new Less_Tree_NameValue($this->name, $this->value, $this->index, $this->currentFileInfo);
+ $new->important = ' !important';
+ return $new;
+ }
+
+
+}
+
+
+/**
+ * Negative
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Negative extends Less_Tree{
+
+ public $value;
+ public $type = 'Negative';
+
+ public function __construct($node){
+ $this->value = $node;
+ }
+
+ //function accept($visitor) {
+ // $this->value = $visitor->visit($this->value);
+ //}
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $output->add( '-' );
+ $this->value->genCSS( $output );
+ }
+
+ public function compile($env) {
+ if( Less_Environment::isMathOn() ){
+ $ret = new Less_Tree_Operation('*', array( new Less_Tree_Dimension(-1), $this->value ) );
+ return $ret->compile($env);
+ }
+ return new Less_Tree_Negative( $this->value->compile($env) );
+ }
+}
+
+/**
+ * Operation
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Operation extends Less_Tree{
+
+ public $op;
+ public $operands;
+ public $isSpaced;
+ public $type = 'Operation';
+
+ /**
+ * @param string $op
+ */
+ public function __construct($op, $operands, $isSpaced = false){
+ $this->op = trim($op);
+ $this->operands = $operands;
+ $this->isSpaced = $isSpaced;
+ }
+
+ public function accept($visitor) {
+ $this->operands = $visitor->visitArray($this->operands);
+ }
+
+ public function compile($env){
+ $a = $this->operands[0]->compile($env);
+ $b = $this->operands[1]->compile($env);
+
+
+ if( Less_Environment::isMathOn() ){
+
+ if( $a instanceof Less_Tree_Dimension && $b instanceof Less_Tree_Color ){
+ $a = $a->toColor();
+
+ }elseif( $b instanceof Less_Tree_Dimension && $a instanceof Less_Tree_Color ){
+ $b = $b->toColor();
+
+ }
+
+ if( !method_exists($a,'operate') ){
+ throw new Less_Exception_Compiler("Operation on an invalid type");
+ }
+
+ return $a->operate( $this->op, $b);
+ }
+
+ return new Less_Tree_Operation($this->op, array($a, $b), $this->isSpaced );
+ }
+
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $this->operands[0]->genCSS( $output );
+ if( $this->isSpaced ){
+ $output->add( " " );
+ }
+ $output->add( $this->op );
+ if( $this->isSpaced ){
+ $output->add( ' ' );
+ }
+ $this->operands[1]->genCSS( $output );
+ }
+
+}
+
+
+/**
+ * Paren
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Paren extends Less_Tree{
+
+ public $value;
+ public $type = 'Paren';
+
+ public function __construct($value) {
+ $this->value = $value;
+ }
+
+ public function accept($visitor){
+ $this->value = $visitor->visitObj($this->value);
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $output->add( '(' );
+ $this->value->genCSS( $output );
+ $output->add( ')' );
+ }
+
+ public function compile($env) {
+ return new Less_Tree_Paren($this->value->compile($env));
+ }
+
+}
+
+
+/**
+ * Quoted
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Quoted extends Less_Tree{
+ public $escaped;
+ public $value;
+ public $quote;
+ public $index;
+ public $currentFileInfo;
+ public $type = 'Quoted';
+
+ /**
+ * @param string $str
+ */
+ public function __construct($str, $content = '', $escaped = false, $index = false, $currentFileInfo = null ){
+ $this->escaped = $escaped;
+ $this->value = $content;
+ if( $str ){
+ $this->quote = $str[0];
+ }
+ $this->index = $index;
+ $this->currentFileInfo = $currentFileInfo;
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ if( !$this->escaped ){
+ $output->add( $this->quote, $this->currentFileInfo, $this->index );
+ }
+ $output->add( $this->value );
+ if( !$this->escaped ){
+ $output->add( $this->quote );
+ }
+ }
+
+ public function compile($env){
+
+ $value = $this->value;
+ if( preg_match_all('/`([^`]+)`/', $this->value, $matches) ){
+ foreach($matches as $i => $match){
+ $js = new Less_Tree_JavaScript($matches[1], $this->index, true);
+ $js = $js->compile()->value;
+ $value = str_replace($matches[0][$i], $js, $value);
+ }
+ }
+
+ if( preg_match_all('/@\{([\w-]+)\}/',$value,$matches) ){
+ foreach($matches[1] as $i => $match){
+ $v = new Less_Tree_Variable('@' . $match, $this->index, $this->currentFileInfo );
+ $v = $v->compile($env);
+ $v = ($v instanceof Less_Tree_Quoted) ? $v->value : $v->toCSS();
+ $value = str_replace($matches[0][$i], $v, $value);
+ }
+ }
+
+ return new Less_Tree_Quoted($this->quote . $value . $this->quote, $value, $this->escaped, $this->index, $this->currentFileInfo);
+ }
+
+ public function compare($x) {
+
+ if( !Less_Parser::is_method($x, 'toCSS') ){
+ return -1;
+ }
+
+ $left = $this->toCSS();
+ $right = $x->toCSS();
+
+ if ($left === $right) {
+ return 0;
+ }
+
+ return $left < $right ? -1 : 1;
+ }
+}
+
+
+/**
+ * Rule
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Rule extends Less_Tree{
+
+ public $name;
+ public $value;
+ public $important;
+ public $merge;
+ public $index;
+ public $inline;
+ public $variable;
+ public $currentFileInfo;
+ public $type = 'Rule';
+
+ /**
+ * @param string $important
+ */
+ public function __construct($name, $value = null, $important = null, $merge = null, $index = null, $currentFileInfo = null, $inline = false){
+ $this->name = $name;
+ $this->value = ($value instanceof Less_Tree_Value || $value instanceof Less_Tree_Ruleset) ? $value : new Less_Tree_Value(array($value));
+ $this->important = $important ? ' ' . trim($important) : '';
+ $this->merge = $merge;
+ $this->index = $index;
+ $this->currentFileInfo = $currentFileInfo;
+ $this->inline = $inline;
+ $this->variable = ( is_string($name) && $name[0] === '@');
+ }
+
+ public function accept($visitor) {
+ $this->value = $visitor->visitObj( $this->value );
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+
+ $output->add( $this->name . Less_Environment::$_outputMap[': '], $this->currentFileInfo, $this->index);
+ try{
+ $this->value->genCSS( $output);
+
+ }catch( Less_Exception_Parser $e ){
+ $e->index = $this->index;
+ $e->currentFile = $this->currentFileInfo;
+ throw $e;
+ }
+ $output->add( $this->important . (($this->inline || (Less_Environment::$lastRule && Less_Parser::$options['compress'])) ? "" : ";"), $this->currentFileInfo, $this->index);
+ }
+
+ public function compile ($env){
+
+ $name = $this->name;
+ if( is_array($name) ){
+ // expand 'primitive' name directly to get
+ // things faster (~10% for benchmark.less):
+ if( count($name) === 1 && $name[0] instanceof Less_Tree_Keyword ){
+ $name = $name[0]->value;
+ }else{
+ $name = $this->CompileName($env,$name);
+ }
+ }
+
+ $strictMathBypass = Less_Parser::$options['strictMath'];
+ if( $name === "font" && !Less_Parser::$options['strictMath'] ){
+ Less_Parser::$options['strictMath'] = true;
+ }
+
+ try {
+ $evaldValue = $this->value->compile($env);
+
+ if( !$this->variable && $evaldValue->type === "DetachedRuleset") {
+ throw new Less_Exception_Compiler("Rulesets cannot be evaluated on a property.", null, $this->index, $this->currentFileInfo);
+ }
+
+ if( Less_Environment::$mixin_stack ){
+ $return = new Less_Tree_Rule($name, $evaldValue, $this->important, $this->merge, $this->index, $this->currentFileInfo, $this->inline);
+ }else{
+ $this->name = $name;
+ $this->value = $evaldValue;
+ $return = $this;
+ }
+
+ }catch( Less_Exception_Parser $e ){
+ if( !is_numeric($e->index) ){
+ $e->index = $this->index;
+ $e->currentFile = $this->currentFileInfo;
+ }
+ throw $e;
+ }
+
+ Less_Parser::$options['strictMath'] = $strictMathBypass;
+
+ return $return;
+ }
+
+
+ public function CompileName( $env, $name ){
+ $output = new Less_Output();
+ foreach($name as $n){
+ $n->compile($env)->genCSS($output);
+ }
+ return $output->toString();
+ }
+
+ public function makeImportant(){
+ return new Less_Tree_Rule($this->name, $this->value, '!important', $this->merge, $this->index, $this->currentFileInfo, $this->inline);
+ }
+
+}
+
+
+/**
+ * Ruleset
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Ruleset extends Less_Tree{
+
+ protected $lookups;
+ public $_variables;
+ public $_rulesets;
+
+ public $strictImports;
+
+ public $selectors;
+ public $rules;
+ public $root;
+ public $allowImports;
+ public $paths;
+ public $firstRoot;
+ public $type = 'Ruleset';
+ public $multiMedia;
+ public $allExtends;
+
+ public $ruleset_id;
+ public $originalRuleset;
+
+ public $first_oelements;
+
+ public function SetRulesetIndex(){
+ $this->ruleset_id = Less_Parser::$next_id++;
+ $this->originalRuleset = $this->ruleset_id;
+
+ if( $this->selectors ){
+ foreach($this->selectors as $sel){
+ if( $sel->_oelements ){
+ $this->first_oelements[$sel->_oelements[0]] = true;
+ }
+ }
+ }
+ }
+
+ public function __construct($selectors, $rules, $strictImports = null){
+ $this->selectors = $selectors;
+ $this->rules = $rules;
+ $this->lookups = array();
+ $this->strictImports = $strictImports;
+ $this->SetRulesetIndex();
+ }
+
+ public function accept( $visitor ){
+ if( $this->paths ){
+ $paths_len = count($this->paths);
+ for($i = 0,$paths_len; $i < $paths_len; $i++ ){
+ $this->paths[$i] = $visitor->visitArray($this->paths[$i]);
+ }
+ }elseif( $this->selectors ){
+ $this->selectors = $visitor->visitArray($this->selectors);
+ }
+
+ if( $this->rules ){
+ $this->rules = $visitor->visitArray($this->rules);
+ }
+ }
+
+ public function compile($env){
+
+ $ruleset = $this->PrepareRuleset($env);
+
+
+ // Store the frames around mixin definitions,
+ // so they can be evaluated like closures when the time comes.
+ $rsRuleCnt = count($ruleset->rules);
+ for( $i = 0; $i < $rsRuleCnt; $i++ ){
+ if( $ruleset->rules[$i] instanceof Less_Tree_Mixin_Definition || $ruleset->rules[$i] instanceof Less_Tree_DetachedRuleset ){
+ $ruleset->rules[$i] = $ruleset->rules[$i]->compile($env);
+ }
+ }
+
+ $mediaBlockCount = 0;
+ if( $env instanceof Less_Environment ){
+ $mediaBlockCount = count($env->mediaBlocks);
+ }
+
+ // Evaluate mixin calls.
+ $this->EvalMixinCalls( $ruleset, $env, $rsRuleCnt );
+
+
+ // Evaluate everything else
+ for( $i=0; $i<$rsRuleCnt; $i++ ){
+ if(! ($ruleset->rules[$i] instanceof Less_Tree_Mixin_Definition || $ruleset->rules[$i] instanceof Less_Tree_DetachedRuleset) ){
+ $ruleset->rules[$i] = $ruleset->rules[$i]->compile($env);
+ }
+ }
+
+ // Evaluate everything else
+ for( $i=0; $i<$rsRuleCnt; $i++ ){
+ $rule = $ruleset->rules[$i];
+
+ // for rulesets, check if it is a css guard and can be removed
+ if( $rule instanceof Less_Tree_Ruleset && $rule->selectors && count($rule->selectors) === 1 ){
+
+ // check if it can be folded in (e.g. & where)
+ if( $rule->selectors[0]->isJustParentSelector() ){
+ array_splice($ruleset->rules,$i--,1);
+ $rsRuleCnt--;
+
+ for($j = 0; $j < count($rule->rules); $j++ ){
+ $subRule = $rule->rules[$j];
+ if( !($subRule instanceof Less_Tree_Rule) || !$subRule->variable ){
+ array_splice($ruleset->rules, ++$i, 0, array($subRule));
+ $rsRuleCnt++;
+ }
+ }
+
+ }
+ }
+ }
+
+
+ // Pop the stack
+ $env->shiftFrame();
+
+ if ($mediaBlockCount) {
+ $len = count($env->mediaBlocks);
+ for($i = $mediaBlockCount; $i < $len; $i++ ){
+ $env->mediaBlocks[$i]->bubbleSelectors($ruleset->selectors);
+ }
+ }
+
+ return $ruleset;
+ }
+
+ /**
+ * Compile Less_Tree_Mixin_Call objects
+ *
+ * @param Less_Tree_Ruleset $ruleset
+ * @param integer $rsRuleCnt
+ */
+ private function EvalMixinCalls( $ruleset, $env, &$rsRuleCnt ){
+ for($i=0; $i < $rsRuleCnt; $i++){
+ $rule = $ruleset->rules[$i];
+
+ if( $rule instanceof Less_Tree_Mixin_Call ){
+ $rule = $rule->compile($env);
+
+ $temp = array();
+ foreach($rule as $r){
+ if( ($r instanceof Less_Tree_Rule) && $r->variable ){
+ // do not pollute the scope if the variable is
+ // already there. consider returning false here
+ // but we need a way to "return" variable from mixins
+ if( !$ruleset->variable($r->name) ){
+ $temp[] = $r;
+ }
+ }else{
+ $temp[] = $r;
+ }
+ }
+ $temp_count = count($temp)-1;
+ array_splice($ruleset->rules, $i, 1, $temp);
+ $rsRuleCnt += $temp_count;
+ $i += $temp_count;
+ $ruleset->resetCache();
+
+ }elseif( $rule instanceof Less_Tree_RulesetCall ){
+
+ $rule = $rule->compile($env);
+ $rules = array();
+ foreach($rule->rules as $r){
+ if( ($r instanceof Less_Tree_Rule) && $r->variable ){
+ continue;
+ }
+ $rules[] = $r;
+ }
+
+ array_splice($ruleset->rules, $i, 1, $rules);
+ $temp_count = count($rules);
+ $rsRuleCnt += $temp_count - 1;
+ $i += $temp_count-1;
+ $ruleset->resetCache();
+ }
+
+ }
+ }
+
+
+ /**
+ * Compile the selectors and create a new ruleset object for the compile() method
+ *
+ */
+ private function PrepareRuleset($env){
+
+ $hasOnePassingSelector = false;
+ $selectors = array();
+ if( $this->selectors ){
+ Less_Tree_DefaultFunc::error("it is currently only allowed in parametric mixin guards,");
+
+ foreach($this->selectors as $s){
+ $selector = $s->compile($env);
+ $selectors[] = $selector;
+ if( $selector->evaldCondition ){
+ $hasOnePassingSelector = true;
+ }
+ }
+
+ Less_Tree_DefaultFunc::reset();
+ } else {
+ $hasOnePassingSelector = true;
+ }
+
+ if( $this->rules && $hasOnePassingSelector ){
+ $rules = $this->rules;
+ }else{
+ $rules = array();
+ }
+
+ $ruleset = new Less_Tree_Ruleset($selectors, $rules, $this->strictImports);
+
+ $ruleset->originalRuleset = $this->ruleset_id;
+
+ $ruleset->root = $this->root;
+ $ruleset->firstRoot = $this->firstRoot;
+ $ruleset->allowImports = $this->allowImports;
+
+
+ // push the current ruleset to the frames stack
+ $env->unshiftFrame($ruleset);
+
+
+ // Evaluate imports
+ if( $ruleset->root || $ruleset->allowImports || !$ruleset->strictImports ){
+ $ruleset->evalImports($env);
+ }
+
+ return $ruleset;
+ }
+
+ function evalImports($env) {
+
+ $rules_len = count($this->rules);
+ for($i=0; $i < $rules_len; $i++){
+ $rule = $this->rules[$i];
+
+ if( $rule instanceof Less_Tree_Import ){
+ $rules = $rule->compile($env);
+ if( is_array($rules) ){
+ array_splice($this->rules, $i, 1, $rules);
+ $temp_count = count($rules)-1;
+ $i += $temp_count;
+ $rules_len += $temp_count;
+ }else{
+ array_splice($this->rules, $i, 1, array($rules));
+ }
+
+ $this->resetCache();
+ }
+ }
+ }
+
+ function makeImportant(){
+
+ $important_rules = array();
+ foreach($this->rules as $rule){
+ if( $rule instanceof Less_Tree_Rule || $rule instanceof Less_Tree_Ruleset || $rule instanceof Less_Tree_NameValue ){
+ $important_rules[] = $rule->makeImportant();
+ }else{
+ $important_rules[] = $rule;
+ }
+ }
+
+ return new Less_Tree_Ruleset($this->selectors, $important_rules, $this->strictImports );
+ }
+
+ public function matchArgs($args){
+ return !$args;
+ }
+
+ // lets you call a css selector with a guard
+ public function matchCondition( $args, $env ){
+ $lastSelector = end($this->selectors);
+
+ if( !$lastSelector->evaldCondition ){
+ return false;
+ }
+ if( $lastSelector->condition && !$lastSelector->condition->compile( $env->copyEvalEnv( $env->frames ) ) ){
+ return false;
+ }
+ return true;
+ }
+
+ function resetCache(){
+ $this->_rulesets = null;
+ $this->_variables = null;
+ $this->lookups = array();
+ }
+
+ public function variables(){
+ $this->_variables = array();
+ foreach( $this->rules as $r){
+ if ($r instanceof Less_Tree_Rule && $r->variable === true) {
+ $this->_variables[$r->name] = $r;
+ }
+ }
+ }
+
+ public function variable($name){
+
+ if( is_null($this->_variables) ){
+ $this->variables();
+ }
+ return isset($this->_variables[$name]) ? $this->_variables[$name] : null;
+ }
+
+ public function find( $selector, $self = null ){
+
+ $key = implode(' ',$selector->_oelements);
+
+ if( !isset($this->lookups[$key]) ){
+
+ if( !$self ){
+ $self = $this->ruleset_id;
+ }
+
+ $this->lookups[$key] = array();
+
+ $first_oelement = $selector->_oelements[0];
+
+ foreach($this->rules as $rule){
+ if( $rule instanceof Less_Tree_Ruleset && $rule->ruleset_id != $self ){
+
+ if( isset($rule->first_oelements[$first_oelement]) ){
+
+ foreach( $rule->selectors as $ruleSelector ){
+ $match = $selector->match($ruleSelector);
+ if( $match ){
+ if( $selector->elements_len > $match ){
+ $this->lookups[$key] = array_merge($this->lookups[$key], $rule->find( new Less_Tree_Selector(array_slice($selector->elements, $match)), $self));
+ } else {
+ $this->lookups[$key][] = $rule;
+ }
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return $this->lookups[$key];
+ }
+
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+
+ if( !$this->root ){
+ Less_Environment::$tabLevel++;
+ }
+
+ $tabRuleStr = $tabSetStr = '';
+ if( !Less_Parser::$options['compress'] ){
+ if( Less_Environment::$tabLevel ){
+ $tabRuleStr = "\n".str_repeat( Less_Parser::$options['indentation'] , Less_Environment::$tabLevel );
+ $tabSetStr = "\n".str_repeat( Less_Parser::$options['indentation'] , Less_Environment::$tabLevel-1 );
+ }else{
+ $tabSetStr = $tabRuleStr = "\n";
+ }
+ }
+
+
+ $ruleNodes = array();
+ $rulesetNodes = array();
+ foreach($this->rules as $rule){
+
+ $class = get_class($rule);
+ if( ($class === 'Less_Tree_Media') || ($class === 'Less_Tree_Directive') || ($this->root && $class === 'Less_Tree_Comment') || ($class === 'Less_Tree_Ruleset' && $rule->rules) ){
+ $rulesetNodes[] = $rule;
+ }else{
+ $ruleNodes[] = $rule;
+ }
+ }
+
+ // If this is the root node, we don't render
+ // a selector, or {}.
+ if( !$this->root ){
+
+ /*
+ debugInfo = tree.debugInfo(env, this, tabSetStr);
+
+ if (debugInfo) {
+ output.add(debugInfo);
+ output.add(tabSetStr);
+ }
+ */
+
+ $paths_len = count($this->paths);
+ for( $i = 0; $i < $paths_len; $i++ ){
+ $path = $this->paths[$i];
+ $firstSelector = true;
+
+ foreach($path as $p){
+ $p->genCSS( $output, $firstSelector );
+ $firstSelector = false;
+ }
+
+ if( $i + 1 < $paths_len ){
+ $output->add( ',' . $tabSetStr );
+ }
+ }
+
+ $output->add( (Less_Parser::$options['compress'] ? '{' : " {") . $tabRuleStr );
+ }
+
+ // Compile rules and rulesets
+ $ruleNodes_len = count($ruleNodes);
+ $rulesetNodes_len = count($rulesetNodes);
+ for( $i = 0; $i < $ruleNodes_len; $i++ ){
+ $rule = $ruleNodes[$i];
+
+ // @page{ directive ends up with root elements inside it, a mix of rules and rulesets
+ // In this instance we do not know whether it is the last property
+ if( $i + 1 === $ruleNodes_len && (!$this->root || $rulesetNodes_len === 0 || $this->firstRoot ) ){
+ Less_Environment::$lastRule = true;
+ }
+
+ $rule->genCSS( $output );
+
+ if( !Less_Environment::$lastRule ){
+ $output->add( $tabRuleStr );
+ }else{
+ Less_Environment::$lastRule = false;
+ }
+ }
+
+ if( !$this->root ){
+ $output->add( $tabSetStr . '}' );
+ Less_Environment::$tabLevel--;
+ }
+
+ $firstRuleset = true;
+ $space = ($this->root ? $tabRuleStr : $tabSetStr);
+ for( $i = 0; $i < $rulesetNodes_len; $i++ ){
+
+ if( $ruleNodes_len && $firstRuleset ){
+ $output->add( $space );
+ }elseif( !$firstRuleset ){
+ $output->add( $space );
+ }
+ $firstRuleset = false;
+ $rulesetNodes[$i]->genCSS( $output);
+ }
+
+ if( !Less_Parser::$options['compress'] && $this->firstRoot ){
+ $output->add( "\n" );
+ }
+
+ }
+
+
+ function markReferenced(){
+ if( !$this->selectors ){
+ return;
+ }
+ foreach($this->selectors as $selector){
+ $selector->markReferenced();
+ }
+ }
+
+ public function joinSelectors( $context, $selectors ){
+ $paths = array();
+ if( is_array($selectors) ){
+ foreach($selectors as $selector) {
+ $this->joinSelector( $paths, $context, $selector);
+ }
+ }
+ return $paths;
+ }
+
+ public function joinSelector( &$paths, $context, $selector){
+
+ $hasParentSelector = false;
+
+ foreach($selector->elements as $el) {
+ if( $el->value === '&') {
+ $hasParentSelector = true;
+ }
+ }
+
+ if( !$hasParentSelector ){
+ if( $context ){
+ foreach($context as $context_el){
+ $paths[] = array_merge($context_el, array($selector) );
+ }
+ }else {
+ $paths[] = array($selector);
+ }
+ return;
+ }
+
+
+ // The paths are [[Selector]]
+ // The first list is a list of comma seperated selectors
+ // The inner list is a list of inheritance seperated selectors
+ // e.g.
+ // .a, .b {
+ // .c {
+ // }
+ // }
+ // == [[.a] [.c]] [[.b] [.c]]
+ //
+
+ // the elements from the current selector so far
+ $currentElements = array();
+ // the current list of new selectors to add to the path.
+ // We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
+ // by the parents
+ $newSelectors = array(array());
+
+
+ foreach( $selector->elements as $el){
+
+ // non parent reference elements just get added
+ if( $el->value !== '&' ){
+ $currentElements[] = $el;
+ } else {
+ // the new list of selectors to add
+ $selectorsMultiplied = array();
+
+ // merge the current list of non parent selector elements
+ // on to the current list of selectors to add
+ if( $currentElements ){
+ $this->mergeElementsOnToSelectors( $currentElements, $newSelectors);
+ }
+
+ // loop through our current selectors
+ foreach($newSelectors as $sel){
+
+ // if we don't have any parent paths, the & might be in a mixin so that it can be used
+ // whether there are parents or not
+ if( !$context ){
+ // the combinator used on el should now be applied to the next element instead so that
+ // it is not lost
+ if( $sel ){
+ $sel[0]->elements = array_slice($sel[0]->elements,0);
+ $sel[0]->elements[] = new Less_Tree_Element($el->combinator, '', $el->index, $el->currentFileInfo );
+ }
+ $selectorsMultiplied[] = $sel;
+ }else {
+
+ // and the parent selectors
+ foreach($context as $parentSel){
+ // We need to put the current selectors
+ // then join the last selector's elements on to the parents selectors
+
+ // our new selector path
+ $newSelectorPath = array();
+ // selectors from the parent after the join
+ $afterParentJoin = array();
+ $newJoinedSelectorEmpty = true;
+
+ //construct the joined selector - if & is the first thing this will be empty,
+ // if not newJoinedSelector will be the last set of elements in the selector
+ if( $sel ){
+ $newSelectorPath = $sel;
+ $lastSelector = array_pop($newSelectorPath);
+ $newJoinedSelector = $selector->createDerived( array_slice($lastSelector->elements,0) );
+ $newJoinedSelectorEmpty = false;
+ }
+ else {
+ $newJoinedSelector = $selector->createDerived(array());
+ }
+
+ //put together the parent selectors after the join
+ if ( count($parentSel) > 1) {
+ $afterParentJoin = array_merge($afterParentJoin, array_slice($parentSel,1) );
+ }
+
+ if ( $parentSel ){
+ $newJoinedSelectorEmpty = false;
+
+ // join the elements so far with the first part of the parent
+ $newJoinedSelector->elements[] = new Less_Tree_Element( $el->combinator, $parentSel[0]->elements[0]->value, $el->index, $el->currentFileInfo);
+
+ $newJoinedSelector->elements = array_merge( $newJoinedSelector->elements, array_slice($parentSel[0]->elements, 1) );
+ }
+
+ if (!$newJoinedSelectorEmpty) {
+ // now add the joined selector
+ $newSelectorPath[] = $newJoinedSelector;
+ }
+
+ // and the rest of the parent
+ $newSelectorPath = array_merge($newSelectorPath, $afterParentJoin);
+
+ // add that to our new set of selectors
+ $selectorsMultiplied[] = $newSelectorPath;
+ }
+ }
+ }
+
+ // our new selectors has been multiplied, so reset the state
+ $newSelectors = $selectorsMultiplied;
+ $currentElements = array();
+ }
+ }
+
+ // if we have any elements left over (e.g. .a& .b == .b)
+ // add them on to all the current selectors
+ if( $currentElements ){
+ $this->mergeElementsOnToSelectors($currentElements, $newSelectors);
+ }
+ foreach( $newSelectors as $new_sel){
+ if( $new_sel ){
+ $paths[] = $new_sel;
+ }
+ }
+ }
+
+ function mergeElementsOnToSelectors( $elements, &$selectors){
+
+ if( !$selectors ){
+ $selectors[] = array( new Less_Tree_Selector($elements) );
+ return;
+ }
+
+
+ foreach( $selectors as &$sel){
+
+ // if the previous thing in sel is a parent this needs to join on to it
+ if( $sel ){
+ $last = count($sel)-1;
+ $sel[$last] = $sel[$last]->createDerived( array_merge($sel[$last]->elements, $elements) );
+ }else{
+ $sel[] = new Less_Tree_Selector( $elements );
+ }
+ }
+ }
+}
+
+
+/**
+ * RulesetCall
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_RulesetCall extends Less_Tree{
+
+ public $variable;
+ public $type = "RulesetCall";
+
+ public function __construct($variable){
+ $this->variable = $variable;
+ }
+
+ public function accept($visitor) {}
+
+ public function compile( $env ){
+ $variable = new Less_Tree_Variable($this->variable);
+ $detachedRuleset = $variable->compile($env);
+ return $detachedRuleset->callEval($env);
+ }
+}
+
+
+
+/**
+ * Selector
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Selector extends Less_Tree{
+
+ public $elements;
+ public $condition;
+ public $extendList = array();
+ public $_css;
+ public $index;
+ public $evaldCondition = false;
+ public $type = 'Selector';
+ public $currentFileInfo = array();
+ public $isReferenced;
+ public $mediaEmpty;
+
+ public $elements_len = 0;
+
+ public $_oelements;
+ public $_oelements_len;
+ public $cacheable = true;
+
+ /**
+ * @param boolean $isReferenced
+ */
+ public function __construct( $elements, $extendList = array() , $condition = null, $index=null, $currentFileInfo=null, $isReferenced=null ){
+
+ $this->elements = $elements;
+ $this->elements_len = count($elements);
+ $this->extendList = $extendList;
+ $this->condition = $condition;
+ if( $currentFileInfo ){
+ $this->currentFileInfo = $currentFileInfo;
+ }
+ $this->isReferenced = $isReferenced;
+ if( !$condition ){
+ $this->evaldCondition = true;
+ }
+
+ $this->CacheElements();
+ }
+
+ public function accept($visitor) {
+ $this->elements = $visitor->visitArray($this->elements);
+ $this->extendList = $visitor->visitArray($this->extendList);
+ if( $this->condition ){
+ $this->condition = $visitor->visitObj($this->condition);
+ }
+
+ if( $visitor instanceof Less_Visitor_extendFinder ){
+ $this->CacheElements();
+ }
+ }
+
+ public function createDerived( $elements, $extendList = null, $evaldCondition = null ){
+ $newSelector = new Less_Tree_Selector( $elements, ($extendList ? $extendList : $this->extendList), null, $this->index, $this->currentFileInfo, $this->isReferenced);
+ $newSelector->evaldCondition = $evaldCondition ? $evaldCondition : $this->evaldCondition;
+ return $newSelector;
+ }
+
+
+ public function match( $other ){
+
+ if( !$other->_oelements || ($this->elements_len < $other->_oelements_len) ){
+ return 0;
+ }
+
+ for( $i = 0; $i < $other->_oelements_len; $i++ ){
+ if( $this->elements[$i]->value !== $other->_oelements[$i]) {
+ return 0;
+ }
+ }
+
+ return $other->_oelements_len; // return number of matched elements
+ }
+
+
+ public function CacheElements(){
+
+ $this->_oelements = array();
+ $css = '';
+
+ foreach($this->elements as $v){
+
+ $css .= $v->combinator;
+ if( !$v->value_is_object ){
+ $css .= $v->value;
+ continue;
+ }
+
+ if( !property_exists($v->value,'value') || !is_string($v->value->value) ){
+ $this->cacheable = false;
+ return;
+ }
+ $css .= $v->value->value;
+ }
+
+ $this->_oelements_len = preg_match_all('/[,&#\.\w-](?:[\w-]|(?:\\\\.))*/', $css, $matches);
+ if( $this->_oelements_len ){
+ $this->_oelements = $matches[0];
+
+ if( $this->_oelements[0] === '&' ){
+ array_shift($this->_oelements);
+ $this->_oelements_len--;
+ }
+ }
+ }
+
+ public function isJustParentSelector(){
+ return !$this->mediaEmpty &&
+ count($this->elements) === 1 &&
+ $this->elements[0]->value === '&' &&
+ ($this->elements[0]->combinator === ' ' || $this->elements[0]->combinator === '');
+ }
+
+ public function compile($env) {
+
+ $elements = array();
+ foreach($this->elements as $el){
+ $elements[] = $el->compile($env);
+ }
+
+ $extendList = array();
+ foreach($this->extendList as $el){
+ $extendList[] = $el->compile($el);
+ }
+
+ $evaldCondition = false;
+ if( $this->condition ){
+ $evaldCondition = $this->condition->compile($env);
+ }
+
+ return $this->createDerived( $elements, $extendList, $evaldCondition );
+ }
+
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output, $firstSelector = true ){
+
+ if( !$firstSelector && $this->elements[0]->combinator === "" ){
+ $output->add(' ', $this->currentFileInfo, $this->index);
+ }
+
+ foreach($this->elements as $element){
+ $element->genCSS( $output );
+ }
+ }
+
+ public function markReferenced(){
+ $this->isReferenced = true;
+ }
+
+ public function getIsReferenced(){
+ return !isset($this->currentFileInfo['reference']) || !$this->currentFileInfo['reference'] || $this->isReferenced;
+ }
+
+ public function getIsOutput(){
+ return $this->evaldCondition;
+ }
+
+}
+
+
+/**
+ * UnicodeDescriptor
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_UnicodeDescriptor extends Less_Tree{
+
+ public $value;
+ public $type = 'UnicodeDescriptor';
+
+ public function __construct($value){
+ $this->value = $value;
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $output->add( $this->value );
+ }
+
+ public function compile(){
+ return $this;
+ }
+}
+
+
+
+/**
+ * Unit
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Unit extends Less_Tree{
+
+ var $numerator = array();
+ var $denominator = array();
+ public $backupUnit;
+ public $type = 'Unit';
+
+ public function __construct($numerator = array(), $denominator = array(), $backupUnit = null ){
+ $this->numerator = $numerator;
+ $this->denominator = $denominator;
+ $this->backupUnit = $backupUnit;
+ }
+
+ public function __clone(){
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+
+ if( $this->numerator ){
+ $output->add( $this->numerator[0] );
+ }elseif( $this->denominator ){
+ $output->add( $this->denominator[0] );
+ }elseif( !Less_Parser::$options['strictUnits'] && $this->backupUnit ){
+ $output->add( $this->backupUnit );
+ return ;
+ }
+ }
+
+ public function toString(){
+ $returnStr = implode('*',$this->numerator);
+ foreach($this->denominator as $d){
+ $returnStr .= '/'.$d;
+ }
+ return $returnStr;
+ }
+
+ public function __toString(){
+ return $this->toString();
+ }
+
+
+ /**
+ * @param Less_Tree_Unit $other
+ */
+ public function compare($other) {
+ return $this->is( $other->toString() ) ? 0 : -1;
+ }
+
+ public function is($unitString){
+ return $this->toString() === $unitString;
+ }
+
+ public function isLength(){
+ $css = $this->toCSS();
+ return !!preg_match('/px|em|%|in|cm|mm|pc|pt|ex/',$css);
+ }
+
+ public function isAngle() {
+ return isset( Less_Tree_UnitConversions::$angle[$this->toCSS()] );
+ }
+
+ public function isEmpty(){
+ return !$this->numerator && !$this->denominator;
+ }
+
+ public function isSingular() {
+ return count($this->numerator) <= 1 && !$this->denominator;
+ }
+
+
+ public function usedUnits(){
+ $result = array();
+
+ foreach(Less_Tree_UnitConversions::$groups as $groupName){
+ $group = Less_Tree_UnitConversions::${$groupName};
+
+ foreach($this->numerator as $atomicUnit){
+ if( isset($group[$atomicUnit]) && !isset($result[$groupName]) ){
+ $result[$groupName] = $atomicUnit;
+ }
+ }
+
+ foreach($this->denominator as $atomicUnit){
+ if( isset($group[$atomicUnit]) && !isset($result[$groupName]) ){
+ $result[$groupName] = $atomicUnit;
+ }
+ }
+ }
+
+ return $result;
+ }
+
+ public function cancel(){
+ $counter = array();
+ $backup = null;
+
+ foreach($this->numerator as $atomicUnit){
+ if( !$backup ){
+ $backup = $atomicUnit;
+ }
+ $counter[$atomicUnit] = ( isset($counter[$atomicUnit]) ? $counter[$atomicUnit] : 0) + 1;
+ }
+
+ foreach($this->denominator as $atomicUnit){
+ if( !$backup ){
+ $backup = $atomicUnit;
+ }
+ $counter[$atomicUnit] = ( isset($counter[$atomicUnit]) ? $counter[$atomicUnit] : 0) - 1;
+ }
+
+ $this->numerator = array();
+ $this->denominator = array();
+
+ foreach($counter as $atomicUnit => $count){
+ if( $count > 0 ){
+ for( $i = 0; $i < $count; $i++ ){
+ $this->numerator[] = $atomicUnit;
+ }
+ }elseif( $count < 0 ){
+ for( $i = 0; $i < -$count; $i++ ){
+ $this->denominator[] = $atomicUnit;
+ }
+ }
+ }
+
+ if( !$this->numerator && !$this->denominator && $backup ){
+ $this->backupUnit = $backup;
+ }
+
+ sort($this->numerator);
+ sort($this->denominator);
+ }
+
+
+}
+
+
+
+/**
+ * UnitConversions
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_UnitConversions{
+
+ public static $groups = array('length','duration','angle');
+
+ public static $length = array(
+ 'm'=> 1,
+ 'cm'=> 0.01,
+ 'mm'=> 0.001,
+ 'in'=> 0.0254,
+ 'px'=> 0.000264583, // 0.0254 / 96,
+ 'pt'=> 0.000352778, // 0.0254 / 72,
+ 'pc'=> 0.004233333, // 0.0254 / 72 * 12
+ );
+
+ public static $duration = array(
+ 's'=> 1,
+ 'ms'=> 0.001
+ );
+
+ public static $angle = array(
+ 'rad' => 0.1591549430919, // 1/(2*M_PI),
+ 'deg' => 0.002777778, // 1/360,
+ 'grad'=> 0.0025, // 1/400,
+ 'turn'=> 1
+ );
+
+}
+
+/**
+ * Url
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Url extends Less_Tree{
+
+ public $attrs;
+ public $value;
+ public $currentFileInfo;
+ public $isEvald;
+ public $type = 'Url';
+
+ public function __construct($value, $currentFileInfo = null, $isEvald = null){
+ $this->value = $value;
+ $this->currentFileInfo = $currentFileInfo;
+ $this->isEvald = $isEvald;
+ }
+
+ public function accept( $visitor ){
+ $this->value = $visitor->visitObj($this->value);
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ public function genCSS( $output ){
+ $output->add( 'url(' );
+ $this->value->genCSS( $output );
+ $output->add( ')' );
+ }
+
+ /**
+ * @param Less_Functions $ctx
+ */
+ public function compile($ctx){
+ $val = $this->value->compile($ctx);
+
+ if( !$this->isEvald ){
+ // Add the base path if the URL is relative
+ if( Less_Parser::$options['relativeUrls']
+ && $this->currentFileInfo
+ && is_string($val->value)
+ && Less_Environment::isPathRelative($val->value)
+ ){
+ $rootpath = $this->currentFileInfo['uri_root'];
+ if ( !$val->quote ){
+ $rootpath = preg_replace('/[\(\)\'"\s]/', '\\$1', $rootpath );
+ }
+ $val->value = $rootpath . $val->value;
+ }
+
+ $val->value = Less_Environment::normalizePath( $val->value);
+ }
+
+ // Add cache buster if enabled
+ if( Less_Parser::$options['urlArgs'] ){
+ if( !preg_match('/^\s*data:/',$val->value) ){
+ $delimiter = strpos($val->value,'?') === false ? '?' : '&';
+ $urlArgs = $delimiter . Less_Parser::$options['urlArgs'];
+ $hash_pos = strpos($val->value,'#');
+ if( $hash_pos !== false ){
+ $val->value = substr_replace($val->value,$urlArgs, $hash_pos, 0);
+ } else {
+ $val->value .= $urlArgs;
+ }
+ }
+ }
+
+ return new Less_Tree_URL($val, $this->currentFileInfo, true);
+ }
+
+}
+
+
+/**
+ * Value
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Value extends Less_Tree{
+
+ public $type = 'Value';
+ public $value;
+
+ public function __construct($value){
+ $this->value = $value;
+ }
+
+ public function accept($visitor) {
+ $this->value = $visitor->visitArray($this->value);
+ }
+
+ public function compile($env){
+
+ $ret = array();
+ $i = 0;
+ foreach($this->value as $i => $v){
+ $ret[] = $v->compile($env);
+ }
+ if( $i > 0 ){
+ return new Less_Tree_Value($ret);
+ }
+ return $ret[0];
+ }
+
+ /**
+ * @see Less_Tree::genCSS
+ */
+ function genCSS( $output ){
+ $len = count($this->value);
+ for($i = 0; $i < $len; $i++ ){
+ $this->value[$i]->genCSS( $output );
+ if( $i+1 < $len ){
+ $output->add( Less_Environment::$_outputMap[','] );
+ }
+ }
+ }
+
+}
+
+
+/**
+ * Variable
+ *
+ * @package Less
+ * @subpackage tree
+ */
+class Less_Tree_Variable extends Less_Tree{
+
+ public $name;
+ public $index;
+ public $currentFileInfo;
+ public $evaluating = false;
+ public $type = 'Variable';
+
+ /**
+ * @param string $name
+ */
+ public function __construct($name, $index = null, $currentFileInfo = null) {
+ $this->name = $name;
+ $this->index = $index;
+ $this->currentFileInfo = $currentFileInfo;
+ }
+
+ public function compile($env) {
+
+ if( $this->name[1] === '@' ){
+ $v = new Less_Tree_Variable(substr($this->name, 1), $this->index + 1, $this->currentFileInfo);
+ $name = '@' . $v->compile($env)->value;
+ }else{
+ $name = $this->name;
+ }
+
+ if ($this->evaluating) {
+ throw new Less_Exception_Compiler("Recursive variable definition for " . $name, null, $this->index, $this->currentFileInfo);
+ }
+
+ $this->evaluating = true;
+
+ foreach($env->frames as $frame){
+ if( $v = $frame->variable($name) ){
+ $r = $v->value->compile($env);
+ $this->evaluating = false;
+ return $r;
+ }
+ }
+
+ throw new Less_Exception_Compiler("variable " . $name . " is undefined in file ".$this->currentFileInfo["filename"], null, $this->index, $this->currentFileInfo);
+ }
+
+}
+
+
+
+class Less_Tree_Mixin_Call extends Less_Tree{
+
+ public $selector;
+ public $arguments;
+ public $index;
+ public $currentFileInfo;
+
+ public $important;
+ public $type = 'MixinCall';
+
+ /**
+ * less.js: tree.mixin.Call
+ *
+ */
+ public function __construct($elements, $args, $index, $currentFileInfo, $important = false){
+ $this->selector = new Less_Tree_Selector($elements);
+ $this->arguments = $args;
+ $this->index = $index;
+ $this->currentFileInfo = $currentFileInfo;
+ $this->important = $important;
+ }
+
+ //function accept($visitor){
+ // $this->selector = $visitor->visit($this->selector);
+ // $this->arguments = $visitor->visit($this->arguments);
+ //}
+
+
+ public function compile($env){
+
+ $rules = array();
+ $match = false;
+ $isOneFound = false;
+ $candidates = array();
+ $defaultUsed = false;
+ $conditionResult = array();
+
+ $args = array();
+ foreach($this->arguments as $a){
+ $args[] = array('name'=> $a['name'], 'value' => $a['value']->compile($env) );
+ }
+
+ foreach($env->frames as $frame){
+
+ $mixins = $frame->find($this->selector);
+
+ if( !$mixins ){
+ continue;
+ }
+
+ $isOneFound = true;
+ $defNone = 0;
+ $defTrue = 1;
+ $defFalse = 2;
+
+ // To make `default()` function independent of definition order we have two "subpasses" here.
+ // At first we evaluate each guard *twice* (with `default() == true` and `default() == false`),
+ // and build candidate list with corresponding flags. Then, when we know all possible matches,
+ // we make a final decision.
+
+ $mixins_len = count($mixins);
+ for( $m = 0; $m < $mixins_len; $m++ ){
+ $mixin = $mixins[$m];
+
+ if( $this->IsRecursive( $env, $mixin ) ){
+ continue;
+ }
+
+ if( $mixin->matchArgs($args, $env) ){
+
+ $candidate = array('mixin' => $mixin, 'group' => $defNone);
+
+ if( $mixin instanceof Less_Tree_Ruleset ){
+
+ for( $f = 0; $f < 2; $f++ ){
+ Less_Tree_DefaultFunc::value($f);
+ $conditionResult[$f] = $mixin->matchCondition( $args, $env);
+ }
+ if( $conditionResult[0] || $conditionResult[1] ){
+ if( $conditionResult[0] != $conditionResult[1] ){
+ $candidate['group'] = $conditionResult[1] ? $defTrue : $defFalse;
+ }
+
+ $candidates[] = $candidate;
+ }
+ }else{
+ $candidates[] = $candidate;
+ }
+
+ $match = true;
+ }
+ }
+
+ Less_Tree_DefaultFunc::reset();
+
+
+ $count = array(0, 0, 0);
+ for( $m = 0; $m < count($candidates); $m++ ){
+ $count[ $candidates[$m]['group'] ]++;
+ }
+
+ if( $count[$defNone] > 0 ){
+ $defaultResult = $defFalse;
+ } else {
+ $defaultResult = $defTrue;
+ if( ($count[$defTrue] + $count[$defFalse]) > 1 ){
+ throw new Exception( 'Ambiguous use of `default()` found when matching for `' . $this->format($args) . '`' );
+ }
+ }
+
+
+ $candidates_length = count($candidates);
+ $length_1 = ($candidates_length == 1);
+
+ for( $m = 0; $m < $candidates_length; $m++){
+ $candidate = $candidates[$m]['group'];
+ if( ($candidate === $defNone) || ($candidate === $defaultResult) ){
+ try{
+ $mixin = $candidates[$m]['mixin'];
+ if( !($mixin instanceof Less_Tree_Mixin_Definition) ){
+ $mixin = new Less_Tree_Mixin_Definition('', array(), $mixin->rules, null, false);
+ $mixin->originalRuleset = $mixins[$m]->originalRuleset;
+ }
+ $rules = array_merge($rules, $mixin->evalCall($env, $args, $this->important)->rules);
+ } catch (Exception $e) {
+ //throw new Less_Exception_Compiler($e->getMessage(), $e->index, null, $this->currentFileInfo['filename']);
+ throw new Less_Exception_Compiler($e->getMessage(), null, null, $this->currentFileInfo);
+ }
+ }
+ }
+
+ if( $match ){
+ if( !$this->currentFileInfo || !isset($this->currentFileInfo['reference']) || !$this->currentFileInfo['reference'] ){
+ Less_Tree::ReferencedArray($rules);
+ }
+
+ return $rules;
+ }
+ }
+
+ if( $isOneFound ){
+ throw new Less_Exception_Compiler('No matching definition was found for `'.$this->Format( $args ).'`', null, $this->index, $this->currentFileInfo);
+
+ }else{
+ throw new Less_Exception_Compiler(trim($this->selector->toCSS()) . " is undefined in ".$this->currentFileInfo['filename'], null, $this->index);
+ }
+
+ }
+
+ /**
+ * Format the args for use in exception messages
+ *
+ */
+ private function Format($args){
+ $message = array();
+ if( $args ){
+ foreach($args as $a){
+ $argValue = '';
+ if( $a['name'] ){
+ $argValue .= $a['name'] . ':';
+ }
+ if( is_object($a['value']) ){
+ $argValue .= $a['value']->toCSS();
+ }else{
+ $argValue .= '???';
+ }
+ $message[] = $argValue;
+ }
+ }
+ return implode(', ',$message);
+ }
+
+
+ /**
+ * Are we in a recursive mixin call?
+ *
+ * @return bool
+ */
+ private function IsRecursive( $env, $mixin ){
+
+ foreach($env->frames as $recur_frame){
+ if( !($mixin instanceof Less_Tree_Mixin_Definition) ){
+
+ if( $mixin === $recur_frame ){
+ return true;
+ }
+
+ if( isset($recur_frame->originalRuleset) && $mixin->ruleset_id === $recur_frame->originalRuleset ){
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+}
+
+
+
+
+class Less_Tree_Mixin_Definition extends Less_Tree_Ruleset{
+ public $name;
+ public $selectors;
+ public $params;
+ public $arity = 0;
+ public $rules;
+ public $lookups = array();
+ public $required = 0;
+ public $frames = array();
+ public $condition;
+ public $variadic;
+ public $type = 'MixinDefinition';
+
+
+ // less.js : /lib/less/tree/mixin.js : tree.mixin.Definition
+ public function __construct($name, $params, $rules, $condition, $variadic = false, $frames = array() ){
+ $this->name = $name;
+ $this->selectors = array(new Less_Tree_Selector(array( new Less_Tree_Element(null, $name))));
+
+ $this->params = $params;
+ $this->condition = $condition;
+ $this->variadic = $variadic;
+ $this->rules = $rules;
+
+ if( $params ){
+ $this->arity = count($params);
+ foreach( $params as $p ){
+ if (! isset($p['name']) || ($p['name'] && !isset($p['value']))) {
+ $this->required++;
+ }
+ }
+ }
+
+ $this->frames = $frames;
+ $this->SetRulesetIndex();
+ }
+
+
+
+ //function accept( $visitor ){
+ // $this->params = $visitor->visit($this->params);
+ // $this->rules = $visitor->visit($this->rules);
+ // $this->condition = $visitor->visit($this->condition);
+ //}
+
+
+ public function toCSS(){
+ return '';
+ }
+
+ // less.js : /lib/less/tree/mixin.js : tree.mixin.Definition.evalParams
+ public function compileParams($env, $mixinFrames, $args = array() , &$evaldArguments = array() ){
+ $frame = new Less_Tree_Ruleset(null, array());
+ $params = $this->params;
+ $mixinEnv = null;
+ $argsLength = 0;
+
+ if( $args ){
+ $argsLength = count($args);
+ for($i = 0; $i < $argsLength; $i++ ){
+ $arg = $args[$i];
+
+ if( $arg && $arg['name'] ){
+ $isNamedFound = false;
+
+ foreach($params as $j => $param){
+ if( !isset($evaldArguments[$j]) && $arg['name'] === $params[$j]['name']) {
+ $evaldArguments[$j] = $arg['value']->compile($env);
+ array_unshift($frame->rules, new Less_Tree_Rule( $arg['name'], $arg['value']->compile($env) ) );
+ $isNamedFound = true;
+ break;
+ }
+ }
+ if ($isNamedFound) {
+ array_splice($args, $i, 1);
+ $i--;
+ $argsLength--;
+ continue;
+ } else {
+ throw new Less_Exception_Compiler("Named argument for " . $this->name .' '.$args[$i]['name'] . ' not found');
+ }
+ }
+ }
+ }
+
+ $argIndex = 0;
+ foreach($params as $i => $param){
+
+ if ( isset($evaldArguments[$i]) ){ continue; }
+
+ $arg = null;
+ if( isset($args[$argIndex]) ){
+ $arg = $args[$argIndex];
+ }
+
+ if (isset($param['name']) && $param['name']) {
+
+ if( isset($param['variadic']) ){
+ $varargs = array();
+ for ($j = $argIndex; $j < $argsLength; $j++) {
+ $varargs[] = $args[$j]['value']->compile($env);
+ }
+ $expression = new Less_Tree_Expression($varargs);
+ array_unshift($frame->rules, new Less_Tree_Rule($param['name'], $expression->compile($env)));
+ }else{
+ $val = ($arg && $arg['value']) ? $arg['value'] : false;
+
+ if ($val) {
+ $val = $val->compile($env);
+ } else if ( isset($param['value']) ) {
+
+ if( !$mixinEnv ){
+ $mixinEnv = new Less_Environment();
+ $mixinEnv->frames = array_merge( array($frame), $mixinFrames);
+ }
+
+ $val = $param['value']->compile($mixinEnv);
+ $frame->resetCache();
+ } else {
+ throw new Less_Exception_Compiler("Wrong number of arguments for " . $this->name . " (" . $argsLength . ' for ' . $this->arity . ")");
+ }
+
+ array_unshift($frame->rules, new Less_Tree_Rule($param['name'], $val));
+ $evaldArguments[$i] = $val;
+ }
+ }
+
+ if ( isset($param['variadic']) && $args) {
+ for ($j = $argIndex; $j < $argsLength; $j++) {
+ $evaldArguments[$j] = $args[$j]['value']->compile($env);
+ }
+ }
+ $argIndex++;
+ }
+
+ ksort($evaldArguments);
+ $evaldArguments = array_values($evaldArguments);
+
+ return $frame;
+ }
+
+ public function compile($env) {
+ if( $this->frames ){
+ return new Less_Tree_Mixin_Definition($this->name, $this->params, $this->rules, $this->condition, $this->variadic, $this->frames );
+ }
+ return new Less_Tree_Mixin_Definition($this->name, $this->params, $this->rules, $this->condition, $this->variadic, $env->frames );
+ }
+
+ public function evalCall($env, $args = NULL, $important = NULL) {
+
+ Less_Environment::$mixin_stack++;
+
+ $_arguments = array();
+
+ if( $this->frames ){
+ $mixinFrames = array_merge($this->frames, $env->frames);
+ }else{
+ $mixinFrames = $env->frames;
+ }
+
+ $frame = $this->compileParams($env, $mixinFrames, $args, $_arguments);
+
+ $ex = new Less_Tree_Expression($_arguments);
+ array_unshift($frame->rules, new Less_Tree_Rule('@arguments', $ex->compile($env)));
+
+
+ $ruleset = new Less_Tree_Ruleset(null, $this->rules);
+ $ruleset->originalRuleset = $this->ruleset_id;
+
+
+ $ruleSetEnv = new Less_Environment();
+ $ruleSetEnv->frames = array_merge( array($this, $frame), $mixinFrames );
+ $ruleset = $ruleset->compile( $ruleSetEnv );
+
+ if( $important ){
+ $ruleset = $ruleset->makeImportant();
+ }
+
+ Less_Environment::$mixin_stack--;
+
+ return $ruleset;
+ }
+
+
+ public function matchCondition($args, $env) {
+
+ if( !$this->condition ){
+ return true;
+ }
+
+ // set array to prevent error on array_merge
+ if(!is_array($this->frames)) {
+ $this->frames = array();
+ }
+
+ $frame = $this->compileParams($env, array_merge($this->frames,$env->frames), $args );
+
+ $compile_env = new Less_Environment();
+ $compile_env->frames = array_merge(
+ array($frame) // the parameter variables
+ , $this->frames // the parent namespace/mixin frames
+ , $env->frames // the current environment frames
+ );
+
+ $compile_env->functions = $env->functions;
+
+ return (bool)$this->condition->compile($compile_env);
+ }
+
+ public function matchArgs($args, $env = NULL){
+ $argsLength = count($args);
+
+ if( !$this->variadic ){
+ if( $argsLength < $this->required ){
+ return false;
+ }
+ if( $argsLength > count($this->params) ){
+ return false;
+ }
+ }else{
+ if( $argsLength < ($this->required - 1)){
+ return false;
+ }
+ }
+
+ $len = min($argsLength, $this->arity);
+
+ for( $i = 0; $i < $len; $i++ ){
+ if( !isset($this->params[$i]['name']) && !isset($this->params[$i]['variadic']) ){
+ if( $args[$i]['value']->compile($env)->toCSS() != $this->params[$i]['value']->compile($env)->toCSS() ){
+ return false;
+ }
+ }
+ }
+
+ return true;
+ }
+
+}
+
+
+/**
+ * Extend Finder Visitor
+ *
+ * @package Less
+ * @subpackage visitor
+ */
+class Less_Visitor_extendFinder extends Less_Visitor{
+
+ public $contexts = array();
+ public $allExtendsStack;
+ public $foundExtends;
+
+ public function __construct(){
+ $this->contexts = array();
+ $this->allExtendsStack = array(array());
+ parent::__construct();
+ }
+
+ /**
+ * @param Less_Tree_Ruleset $root
+ */
+ public function run($root){
+ $root = $this->visitObj($root);
+ $root->allExtends =& $this->allExtendsStack[0];
+ return $root;
+ }
+
+ public function visitRule($ruleNode, &$visitDeeper ){
+ $visitDeeper = false;
+ }
+
+ public function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ){
+ $visitDeeper = false;
+ }
+
+ public function visitRuleset($rulesetNode){
+
+ if( $rulesetNode->root ){
+ return;
+ }
+
+ $allSelectorsExtendList = array();
+
+ // get &:extend(.a); rules which apply to all selectors in this ruleset
+ if( $rulesetNode->rules ){
+ foreach($rulesetNode->rules as $rule){
+ if( $rule instanceof Less_Tree_Extend ){
+ $allSelectorsExtendList[] = $rule;
+ $rulesetNode->extendOnEveryPath = true;
+ }
+ }
+ }
+
+
+ // now find every selector and apply the extends that apply to all extends
+ // and the ones which apply to an individual extend
+ foreach($rulesetNode->paths as $selectorPath){
+ $selector = end($selectorPath); //$selectorPath[ count($selectorPath)-1];
+
+ $j = 0;
+ foreach($selector->extendList as $extend){
+ $this->allExtendsStackPush($rulesetNode, $selectorPath, $extend, $j);
+ }
+ foreach($allSelectorsExtendList as $extend){
+ $this->allExtendsStackPush($rulesetNode, $selectorPath, $extend, $j);
+ }
+ }
+
+ $this->contexts[] = $rulesetNode->selectors;
+ }
+
+ public function allExtendsStackPush($rulesetNode, $selectorPath, $extend, &$j){
+ $this->foundExtends = true;
+ $extend = clone $extend;
+ $extend->findSelfSelectors( $selectorPath );
+ $extend->ruleset = $rulesetNode;
+ if( $j === 0 ){
+ $extend->firstExtendOnThisSelectorPath = true;
+ }
+
+ $end_key = count($this->allExtendsStack)-1;
+ $this->allExtendsStack[$end_key][] = $extend;
+ $j++;
+ }
+
+
+ public function visitRulesetOut( $rulesetNode ){
+ if( !is_object($rulesetNode) || !$rulesetNode->root ){
+ array_pop($this->contexts);
+ }
+ }
+
+ public function visitMedia( $mediaNode ){
+ $mediaNode->allExtends = array();
+ $this->allExtendsStack[] =& $mediaNode->allExtends;
+ }
+
+ public function visitMediaOut(){
+ array_pop($this->allExtendsStack);
+ }
+
+ public function visitDirective( $directiveNode ){
+ $directiveNode->allExtends = array();
+ $this->allExtendsStack[] =& $directiveNode->allExtends;
+ }
+
+ public function visitDirectiveOut(){
+ array_pop($this->allExtendsStack);
+ }
+}
+
+
+
+
+/*
+class Less_Visitor_import extends Less_VisitorReplacing{
+
+ public $_visitor;
+ public $_importer;
+ public $importCount;
+
+ function __construct( $evalEnv ){
+ $this->env = $evalEnv;
+ $this->importCount = 0;
+ parent::__construct();
+ }
+
+
+ function run( $root ){
+ $root = $this->visitObj($root);
+ $this->isFinished = true;
+
+ //if( $this->importCount === 0) {
+ // $this->_finish();
+ //}
+ }
+
+ function visitImport($importNode, &$visitDeeper ){
+ $importVisitor = $this;
+ $inlineCSS = $importNode->options['inline'];
+
+ if( !$importNode->css || $inlineCSS ){
+ $evaldImportNode = $importNode->compileForImport($this->env);
+
+ if( $evaldImportNode && (!$evaldImportNode->css || $inlineCSS) ){
+ $importNode = $evaldImportNode;
+ $this->importCount++;
+ $env = clone $this->env;
+
+ if( (isset($importNode->options['multiple']) && $importNode->options['multiple']) ){
+ $env->importMultiple = true;
+ }
+
+ //get path & uri
+ $path_and_uri = null;
+ if( is_callable(Less_Parser::$options['import_callback']) ){
+ $path_and_uri = call_user_func(Less_Parser::$options['import_callback'],$importNode);
+ }
+
+ if( !$path_and_uri ){
+ $path_and_uri = $importNode->PathAndUri();
+ }
+
+ if( $path_and_uri ){
+ list($full_path, $uri) = $path_and_uri;
+ }else{
+ $full_path = $uri = $importNode->getPath();
+ }
+
+
+ //import once
+ if( $importNode->skip( $full_path, $env) ){
+ return array();
+ }
+
+ if( $importNode->options['inline'] ){
+ //todo needs to reference css file not import
+ //$contents = new Less_Tree_Anonymous($importNode->root, 0, array('filename'=>$importNode->importedFilename), true );
+
+ Less_Parser::AddParsedFile($full_path);
+ $contents = new Less_Tree_Anonymous( file_get_contents($full_path), 0, array(), true );
+
+ if( $importNode->features ){
+ return new Less_Tree_Media( array($contents), $importNode->features->value );
+ }
+
+ return array( $contents );
+ }
+
+
+ // css ?
+ if( $importNode->css ){
+ $features = ( $importNode->features ? $importNode->features->compile($env) : null );
+ return new Less_Tree_Import( $importNode->compilePath( $env), $features, $importNode->options, $this->index);
+ }
+
+ return $importNode->ParseImport( $full_path, $uri, $env );
+ }
+
+ }
+
+ $visitDeeper = false;
+ return $importNode;
+ }
+
+
+ function visitRule( $ruleNode, &$visitDeeper ){
+ $visitDeeper = false;
+ return $ruleNode;
+ }
+
+ function visitDirective($directiveNode, $visitArgs){
+ array_unshift($this->env->frames,$directiveNode);
+ return $directiveNode;
+ }
+
+ function visitDirectiveOut($directiveNode) {
+ array_shift($this->env->frames);
+ }
+
+ function visitMixinDefinition($mixinDefinitionNode, $visitArgs) {
+ array_unshift($this->env->frames,$mixinDefinitionNode);
+ return $mixinDefinitionNode;
+ }
+
+ function visitMixinDefinitionOut($mixinDefinitionNode) {
+ array_shift($this->env->frames);
+ }
+
+ function visitRuleset($rulesetNode, $visitArgs) {
+ array_unshift($this->env->frames,$rulesetNode);
+ return $rulesetNode;
+ }
+
+ function visitRulesetOut($rulesetNode) {
+ array_shift($this->env->frames);
+ }
+
+ function visitMedia($mediaNode, $visitArgs) {
+ array_unshift($this->env->frames, $mediaNode->ruleset);
+ return $mediaNode;
+ }
+
+ function visitMediaOut($mediaNode) {
+ array_shift($this->env->frames);
+ }
+
+}
+*/
+
+
+
+
+/**
+ * Join Selector Visitor
+ *
+ * @package Less
+ * @subpackage visitor
+ */
+class Less_Visitor_joinSelector extends Less_Visitor{
+
+ public $contexts = array( array() );
+
+ /**
+ * @param Less_Tree_Ruleset $root
+ */
+ public function run( $root ){
+ return $this->visitObj($root);
+ }
+
+ public function visitRule( $ruleNode, &$visitDeeper ){
+ $visitDeeper = false;
+ }
+
+ public function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ){
+ $visitDeeper = false;
+ }
+
+ public function visitRuleset( $rulesetNode ){
+
+ $paths = array();
+
+ if( !$rulesetNode->root ){
+ $selectors = array();
+
+ if( $rulesetNode->selectors && $rulesetNode->selectors ){
+ foreach($rulesetNode->selectors as $selector){
+ if( $selector->getIsOutput() ){
+ $selectors[] = $selector;
+ }
+ }
+ }
+
+ if( !$selectors ){
+ $rulesetNode->selectors = null;
+ $rulesetNode->rules = null;
+ }else{
+ $context = end($this->contexts); //$context = $this->contexts[ count($this->contexts) - 1];
+ $paths = $rulesetNode->joinSelectors( $context, $selectors);
+ }
+
+ $rulesetNode->paths = $paths;
+ }
+
+ $this->contexts[] = $paths; //different from less.js. Placed after joinSelectors() so that $this->contexts will get correct $paths
+ }
+
+ public function visitRulesetOut(){
+ array_pop($this->contexts);
+ }
+
+ public function visitMedia($mediaNode) {
+ $context = end($this->contexts); //$context = $this->contexts[ count($this->contexts) - 1];
+
+ if( !count($context) || (is_object($context[0]) && $context[0]->multiMedia) ){
+ $mediaNode->rules[0]->root = true;
+ }
+ }
+
+}
+
+
+
+/**
+ * Process Extends Visitor
+ *
+ * @package Less
+ * @subpackage visitor
+ */
+class Less_Visitor_processExtends extends Less_Visitor{
+
+ public $allExtendsStack;
+
+ /**
+ * @param Less_Tree_Ruleset $root
+ */
+ public function run( $root ){
+ $extendFinder = new Less_Visitor_extendFinder();
+ $extendFinder->run( $root );
+ if( !$extendFinder->foundExtends){
+ return $root;
+ }
+
+ $root->allExtends = $this->doExtendChaining( $root->allExtends, $root->allExtends);
+
+ $this->allExtendsStack = array();
+ $this->allExtendsStack[] = &$root->allExtends;
+
+ return $this->visitObj( $root );
+ }
+
+ private function doExtendChaining( $extendsList, $extendsListTarget, $iterationCount = 0){
+ //
+ // chaining is different from normal extension.. if we extend an extend then we are not just copying, altering and pasting
+ // the selector we would do normally, but we are also adding an extend with the same target selector
+ // this means this new extend can then go and alter other extends
+ //
+ // this method deals with all the chaining work - without it, extend is flat and doesn't work on other extend selectors
+ // this is also the most expensive.. and a match on one selector can cause an extension of a selector we had already processed if
+ // we look at each selector at a time, as is done in visitRuleset
+
+ $extendsToAdd = array();
+
+
+ //loop through comparing every extend with every target extend.
+ // a target extend is the one on the ruleset we are looking at copy/edit/pasting in place
+ // e.g. .a:extend(.b) {} and .b:extend(.c) {} then the first extend extends the second one
+ // and the second is the target.
+ // the seperation into two lists allows us to process a subset of chains with a bigger set, as is the
+ // case when processing media queries
+ for( $extendIndex = 0, $extendsList_len = count($extendsList); $extendIndex < $extendsList_len; $extendIndex++ ){
+ for( $targetExtendIndex = 0; $targetExtendIndex < count($extendsListTarget); $targetExtendIndex++ ){
+
+ $extend = $extendsList[$extendIndex];
+ $targetExtend = $extendsListTarget[$targetExtendIndex];
+
+ // look for circular references
+ if( in_array($targetExtend->object_id, $extend->parent_ids,true) ){
+ continue;
+ }
+
+ // find a match in the target extends self selector (the bit before :extend)
+ $selectorPath = array( $targetExtend->selfSelectors[0] );
+ $matches = $this->findMatch( $extend, $selectorPath);
+
+
+ if( $matches ){
+
+ // we found a match, so for each self selector..
+ foreach($extend->selfSelectors as $selfSelector ){
+
+
+ // process the extend as usual
+ $newSelector = $this->extendSelector( $matches, $selectorPath, $selfSelector);
+
+ // but now we create a new extend from it
+ $newExtend = new Less_Tree_Extend( $targetExtend->selector, $targetExtend->option, 0);
+ $newExtend->selfSelectors = $newSelector;
+
+ // add the extend onto the list of extends for that selector
+ end($newSelector)->extendList = array($newExtend);
+ //$newSelector[ count($newSelector)-1]->extendList = array($newExtend);
+
+ // record that we need to add it.
+ $extendsToAdd[] = $newExtend;
+ $newExtend->ruleset = $targetExtend->ruleset;
+
+ //remember its parents for circular references
+ $newExtend->parent_ids = array_merge($newExtend->parent_ids,$targetExtend->parent_ids,$extend->parent_ids);
+
+ // only process the selector once.. if we have :extend(.a,.b) then multiple
+ // extends will look at the same selector path, so when extending
+ // we know that any others will be duplicates in terms of what is added to the css
+ if( $targetExtend->firstExtendOnThisSelectorPath ){
+ $newExtend->firstExtendOnThisSelectorPath = true;
+ $targetExtend->ruleset->paths[] = $newSelector;
+ }
+ }
+ }
+ }
+ }
+
+ if( $extendsToAdd ){
+ // try to detect circular references to stop a stack overflow.
+ // may no longer be needed. $this->extendChainCount++;
+ if( $iterationCount > 100) {
+
+ try{
+ $selectorOne = $extendsToAdd[0]->selfSelectors[0]->toCSS();
+ $selectorTwo = $extendsToAdd[0]->selector->toCSS();
+ }catch(Exception $e){
+ $selectorOne = "{unable to calculate}";
+ $selectorTwo = "{unable to calculate}";
+ }
+
+ throw new Less_Exception_Parser("extend circular reference detected. One of the circular extends is currently:" . $selectorOne . ":extend(" . $selectorTwo . ")");
+ }
+
+ // now process the new extends on the existing rules so that we can handle a extending b extending c ectending d extending e...
+ $extendsToAdd = $this->doExtendChaining( $extendsToAdd, $extendsListTarget, $iterationCount+1);
+ }
+
+ return array_merge($extendsList, $extendsToAdd);
+ }
+
+
+ protected function visitRule( $ruleNode, &$visitDeeper ){
+ $visitDeeper = false;
+ }
+
+ protected function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ){
+ $visitDeeper = false;
+ }
+
+ protected function visitSelector( $selectorNode, &$visitDeeper ){
+ $visitDeeper = false;
+ }
+
+ protected function visitRuleset($rulesetNode){
+
+
+ if( $rulesetNode->root ){
+ return;
+ }
+
+ $allExtends = end($this->allExtendsStack);
+ $paths_len = count($rulesetNode->paths);
+
+ // look at each selector path in the ruleset, find any extend matches and then copy, find and replace
+ foreach($allExtends as $allExtend){
+ for($pathIndex = 0; $pathIndex < $paths_len; $pathIndex++ ){
+
+ // extending extends happens initially, before the main pass
+ if( isset($rulesetNode->extendOnEveryPath) && $rulesetNode->extendOnEveryPath ){
+ continue;
+ }
+
+ $selectorPath = $rulesetNode->paths[$pathIndex];
+
+ if( end($selectorPath)->extendList ){
+ continue;
+ }
+
+ $this->ExtendMatch( $rulesetNode, $allExtend, $selectorPath);
+
+ }
+ }
+ }
+
+
+ private function ExtendMatch( $rulesetNode, $extend, $selectorPath ){
+ $matches = $this->findMatch($extend, $selectorPath);
+
+ if( $matches ){
+ foreach($extend->selfSelectors as $selfSelector ){
+ $rulesetNode->paths[] = $this->extendSelector($matches, $selectorPath, $selfSelector);
+ }
+ }
+ }
+
+
+
+ private function findMatch($extend, $haystackSelectorPath ){
+
+
+ if( !$this->HasMatches($extend, $haystackSelectorPath) ){
+ return false;
+ }
+
+
+ //
+ // look through the haystack selector path to try and find the needle - extend.selector
+ // returns an array of selector matches that can then be replaced
+ //
+ $needleElements = $extend->selector->elements;
+ $potentialMatches = array();
+ $potentialMatches_len = 0;
+ $potentialMatch = null;
+ $matches = array();
+
+
+
+ // loop through the haystack elements
+ $haystack_path_len = count($haystackSelectorPath);
+ for($haystackSelectorIndex = 0; $haystackSelectorIndex < $haystack_path_len; $haystackSelectorIndex++ ){
+ $hackstackSelector = $haystackSelectorPath[$haystackSelectorIndex];
+
+ $haystack_elements_len = count($hackstackSelector->elements);
+ for($hackstackElementIndex = 0; $hackstackElementIndex < $haystack_elements_len; $hackstackElementIndex++ ){
+
+ $haystackElement = $hackstackSelector->elements[$hackstackElementIndex];
+
+ // if we allow elements before our match we can add a potential match every time. otherwise only at the first element.
+ if( $extend->allowBefore || ($haystackSelectorIndex === 0 && $hackstackElementIndex === 0) ){
+ $potentialMatches[] = array('pathIndex'=> $haystackSelectorIndex, 'index'=> $hackstackElementIndex, 'matched'=> 0, 'initialCombinator'=> $haystackElement->combinator);
+ $potentialMatches_len++;
+ }
+
+ for($i = 0; $i < $potentialMatches_len; $i++ ){
+
+ $potentialMatch = &$potentialMatches[$i];
+ $potentialMatch = $this->PotentialMatch( $potentialMatch, $needleElements, $haystackElement, $hackstackElementIndex );
+
+
+ // if we are still valid and have finished, test whether we have elements after and whether these are allowed
+ if( $potentialMatch && $potentialMatch['matched'] === $extend->selector->elements_len ){
+ $potentialMatch['finished'] = true;
+
+ if( !$extend->allowAfter && ($hackstackElementIndex+1 < $haystack_elements_len || $haystackSelectorIndex+1 < $haystack_path_len) ){
+ $potentialMatch = null;
+ }
+ }
+
+ // if null we remove, if not, we are still valid, so either push as a valid match or continue
+ if( $potentialMatch ){
+ if( $potentialMatch['finished'] ){
+ $potentialMatch['length'] = $extend->selector->elements_len;
+ $potentialMatch['endPathIndex'] = $haystackSelectorIndex;
+ $potentialMatch['endPathElementIndex'] = $hackstackElementIndex + 1; // index after end of match
+ $potentialMatches = array(); // we don't allow matches to overlap, so start matching again
+ $potentialMatches_len = 0;
+ $matches[] = $potentialMatch;
+ }
+ continue;
+ }
+
+ array_splice($potentialMatches, $i, 1);
+ $potentialMatches_len--;
+ $i--;
+ }
+ }
+ }
+
+ return $matches;
+ }
+
+
+ // Before going through all the nested loops, lets check to see if a match is possible
+ // Reduces Bootstrap 3.1 compile time from ~6.5s to ~5.6s
+ private function HasMatches($extend, $haystackSelectorPath){
+
+ if( !$extend->selector->cacheable ){
+ return true;
+ }
+
+ $first_el = $extend->selector->_oelements[0];
+
+ foreach($haystackSelectorPath as $hackstackSelector){
+ if( !$hackstackSelector->cacheable ){
+ return true;
+ }
+
+ if( in_array($first_el, $hackstackSelector->_oelements) ){
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+
+ /**
+ * @param integer $hackstackElementIndex
+ */
+ private function PotentialMatch( $potentialMatch, $needleElements, $haystackElement, $hackstackElementIndex ){
+
+
+ if( $potentialMatch['matched'] > 0 ){
+
+ // selectors add " " onto the first element. When we use & it joins the selectors together, but if we don't
+ // then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to work out
+ // what the resulting combinator will be
+ $targetCombinator = $haystackElement->combinator;
+ if( $targetCombinator === '' && $hackstackElementIndex === 0 ){
+ $targetCombinator = ' ';
+ }
+
+ if( $needleElements[ $potentialMatch['matched'] ]->combinator !== $targetCombinator ){
+ return null;
+ }
+ }
+
+ // if we don't match, null our match to indicate failure
+ if( !$this->isElementValuesEqual( $needleElements[$potentialMatch['matched'] ]->value, $haystackElement->value) ){
+ return null;
+ }
+
+ $potentialMatch['finished'] = false;
+ $potentialMatch['matched']++;
+
+ return $potentialMatch;
+ }
+
+
+ private function isElementValuesEqual( $elementValue1, $elementValue2 ){
+
+ if( $elementValue1 === $elementValue2 ){
+ return true;
+ }
+
+ if( is_string($elementValue1) || is_string($elementValue2) ) {
+ return false;
+ }
+
+ if( $elementValue1 instanceof Less_Tree_Attribute ){
+ return $this->isAttributeValuesEqual( $elementValue1, $elementValue2 );
+ }
+
+ $elementValue1 = $elementValue1->value;
+ if( $elementValue1 instanceof Less_Tree_Selector ){
+ return $this->isSelectorValuesEqual( $elementValue1, $elementValue2 );
+ }
+
+ return false;
+ }
+
+
+ /**
+ * @param Less_Tree_Selector $elementValue1
+ */
+ private function isSelectorValuesEqual( $elementValue1, $elementValue2 ){
+
+ $elementValue2 = $elementValue2->value;
+ if( !($elementValue2 instanceof Less_Tree_Selector) || $elementValue1->elements_len !== $elementValue2->elements_len ){
+ return false;
+ }
+
+ for( $i = 0; $i < $elementValue1->elements_len; $i++ ){
+
+ if( $elementValue1->elements[$i]->combinator !== $elementValue2->elements[$i]->combinator ){
+ if( $i !== 0 || ($elementValue1->elements[$i]->combinator || ' ') !== ($elementValue2->elements[$i]->combinator || ' ') ){
+ return false;
+ }
+ }
+
+ if( !$this->isElementValuesEqual($elementValue1->elements[$i]->value, $elementValue2->elements[$i]->value) ){
+ return false;
+ }
+ }
+
+ return true;
+ }
+
+
+ /**
+ * @param Less_Tree_Attribute $elementValue1
+ */
+ private function isAttributeValuesEqual( $elementValue1, $elementValue2 ){
+
+ if( $elementValue1->op !== $elementValue2->op || $elementValue1->key !== $elementValue2->key ){
+ return false;
+ }
+
+ if( !$elementValue1->value || !$elementValue2->value ){
+ if( $elementValue1->value || $elementValue2->value ) {
+ return false;
+ }
+ return true;
+ }
+
+ $elementValue1 = ($elementValue1->value->value ? $elementValue1->value->value : $elementValue1->value );
+ $elementValue2 = ($elementValue2->value->value ? $elementValue2->value->value : $elementValue2->value );
+
+ return $elementValue1 === $elementValue2;
+ }
+
+
+ private function extendSelector($matches, $selectorPath, $replacementSelector){
+
+ //for a set of matches, replace each match with the replacement selector
+
+ $currentSelectorPathIndex = 0;
+ $currentSelectorPathElementIndex = 0;
+ $path = array();
+ $selectorPath_len = count($selectorPath);
+
+ for($matchIndex = 0, $matches_len = count($matches); $matchIndex < $matches_len; $matchIndex++ ){
+
+
+ $match = $matches[$matchIndex];
+ $selector = $selectorPath[ $match['pathIndex'] ];
+
+ $firstElement = new Less_Tree_Element(
+ $match['initialCombinator'],
+ $replacementSelector->elements[0]->value,
+ $replacementSelector->elements[0]->index,
+ $replacementSelector->elements[0]->currentFileInfo
+ );
+
+ if( $match['pathIndex'] > $currentSelectorPathIndex && $currentSelectorPathElementIndex > 0 ){
+ $last_path = end($path);
+ $last_path->elements = array_merge( $last_path->elements, array_slice( $selectorPath[$currentSelectorPathIndex]->elements, $currentSelectorPathElementIndex));
+ $currentSelectorPathElementIndex = 0;
+ $currentSelectorPathIndex++;
+ }
+
+ $newElements = array_merge(
+ array_slice($selector->elements, $currentSelectorPathElementIndex, ($match['index'] - $currentSelectorPathElementIndex) ) // last parameter of array_slice is different than the last parameter of javascript's slice
+ , array($firstElement)
+ , array_slice($replacementSelector->elements,1)
+ );
+
+ if( $currentSelectorPathIndex === $match['pathIndex'] && $matchIndex > 0 ){
+ $last_key = count($path)-1;
+ $path[$last_key]->elements = array_merge($path[$last_key]->elements,$newElements);
+ }else{
+ $path = array_merge( $path, array_slice( $selectorPath, $currentSelectorPathIndex, $match['pathIndex'] ));
+ $path[] = new Less_Tree_Selector( $newElements );
+ }
+
+ $currentSelectorPathIndex = $match['endPathIndex'];
+ $currentSelectorPathElementIndex = $match['endPathElementIndex'];
+ if( $currentSelectorPathElementIndex >= count($selectorPath[$currentSelectorPathIndex]->elements) ){
+ $currentSelectorPathElementIndex = 0;
+ $currentSelectorPathIndex++;
+ }
+ }
+
+ if( $currentSelectorPathIndex < $selectorPath_len && $currentSelectorPathElementIndex > 0 ){
+ $last_path = end($path);
+ $last_path->elements = array_merge( $last_path->elements, array_slice($selectorPath[$currentSelectorPathIndex]->elements, $currentSelectorPathElementIndex));
+ $currentSelectorPathIndex++;
+ }
+
+ $slice_len = $selectorPath_len - $currentSelectorPathIndex;
+ $path = array_merge($path, array_slice($selectorPath, $currentSelectorPathIndex, $slice_len));
+
+ return $path;
+ }
+
+
+ protected function visitMedia( $mediaNode ){
+ $newAllExtends = array_merge( $mediaNode->allExtends, end($this->allExtendsStack) );
+ $this->allExtendsStack[] = $this->doExtendChaining($newAllExtends, $mediaNode->allExtends);
+ }
+
+ protected function visitMediaOut(){
+ array_pop( $this->allExtendsStack );
+ }
+
+ protected function visitDirective( $directiveNode ){
+ $newAllExtends = array_merge( $directiveNode->allExtends, end($this->allExtendsStack) );
+ $this->allExtendsStack[] = $this->doExtendChaining($newAllExtends, $directiveNode->allExtends);
+ }
+
+ protected function visitDirectiveOut(){
+ array_pop($this->allExtendsStack);
+ }
+
+}
+
+/**
+ * toCSS Visitor
+ *
+ * @package Less
+ * @subpackage visitor
+ */
+class Less_Visitor_toCSS extends Less_VisitorReplacing{
+
+ private $charset;
+
+ public function __construct(){
+ parent::__construct();
+ }
+
+ /**
+ * @param Less_Tree_Ruleset $root
+ */
+ public function run( $root ){
+ return $this->visitObj($root);
+ }
+
+ public function visitRule( $ruleNode ){
+ if( $ruleNode->variable ){
+ return array();
+ }
+ return $ruleNode;
+ }
+
+ public function visitMixinDefinition($mixinNode){
+ // mixin definitions do not get eval'd - this means they keep state
+ // so we have to clear that state here so it isn't used if toCSS is called twice
+ $mixinNode->frames = array();
+ return array();
+ }
+
+ public function visitExtend(){
+ return array();
+ }
+
+ public function visitComment( $commentNode ){
+ if( $commentNode->isSilent() ){
+ return array();
+ }
+ return $commentNode;
+ }
+
+ public function visitMedia( $mediaNode, &$visitDeeper ){
+ $mediaNode->accept($this);
+ $visitDeeper = false;
+
+ if( !$mediaNode->rules ){
+ return array();
+ }
+ return $mediaNode;
+ }
+
+ public function visitDirective( $directiveNode ){
+ if( isset($directiveNode->currentFileInfo['reference']) && (!property_exists($directiveNode,'isReferenced') || !$directiveNode->isReferenced) ){
+ return array();
+ }
+ if( $directiveNode->name === '@charset' ){
+ // Only output the debug info together with subsequent @charset definitions
+ // a comment (or @media statement) before the actual @charset directive would
+ // be considered illegal css as it has to be on the first line
+ if( isset($this->charset) && $this->charset ){
+
+ //if( $directiveNode->debugInfo ){
+ // $comment = new Less_Tree_Comment('/* ' . str_replace("\n",'',$directiveNode->toCSS())." */\n");
+ // $comment->debugInfo = $directiveNode->debugInfo;
+ // return $this->visit($comment);
+ //}
+
+
+ return array();
+ }
+ $this->charset = true;
+ }
+ return $directiveNode;
+ }
+
+ public function checkPropertiesInRoot( $rulesetNode ){
+
+ if( !$rulesetNode->firstRoot ){
+ return;
+ }
+
+ foreach($rulesetNode->rules as $ruleNode){
+ if( $ruleNode instanceof Less_Tree_Rule && !$ruleNode->variable ){
+ $msg = "properties must be inside selector blocks, they cannot be in the root. Index ".$ruleNode->index.($ruleNode->currentFileInfo ? (' Filename: '.$ruleNode->currentFileInfo['filename']) : null);
+ throw new Less_Exception_Compiler($msg);
+ }
+ }
+ }
+
+
+ public function visitRuleset( $rulesetNode, &$visitDeeper ){
+
+ $visitDeeper = false;
+
+ $this->checkPropertiesInRoot( $rulesetNode );
+
+ if( $rulesetNode->root ){
+ return $this->visitRulesetRoot( $rulesetNode );
+ }
+
+ $rulesets = array();
+ $rulesetNode->paths = $this->visitRulesetPaths($rulesetNode);
+
+
+ // Compile rules and rulesets
+ $nodeRuleCnt = count($rulesetNode->rules);
+ for( $i = 0; $i < $nodeRuleCnt; ){
+ $rule = $rulesetNode->rules[$i];
+
+ if( property_exists($rule,'rules') ){
+ // visit because we are moving them out from being a child
+ $rulesets[] = $this->visitObj($rule);
+ array_splice($rulesetNode->rules,$i,1);
+ $nodeRuleCnt--;
+ continue;
+ }
+ $i++;
+ }
+
+
+ // accept the visitor to remove rules and refactor itself
+ // then we can decide now whether we want it or not
+ if( $nodeRuleCnt > 0 ){
+ $rulesetNode->accept($this);
+
+ if( $rulesetNode->rules ){
+
+ if( count($rulesetNode->rules) > 1 ){
+ $this->_mergeRules( $rulesetNode->rules );
+ $this->_removeDuplicateRules( $rulesetNode->rules );
+ }
+
+ // now decide whether we keep the ruleset
+ if( $rulesetNode->paths ){
+ //array_unshift($rulesets, $rulesetNode);
+ array_splice($rulesets,0,0,array($rulesetNode));
+ }
+ }
+
+ }
+
+
+ if( count($rulesets) === 1 ){
+ return $rulesets[0];
+ }
+ return $rulesets;
+ }
+
+
+ /**
+ * Helper function for visitiRuleset
+ *
+ * return array|Less_Tree_Ruleset
+ */
+ private function visitRulesetRoot( $rulesetNode ){
+ $rulesetNode->accept( $this );
+ if( $rulesetNode->firstRoot || $rulesetNode->rules ){
+ return $rulesetNode;
+ }
+ return array();
+ }
+
+
+ /**
+ * Helper function for visitRuleset()
+ *
+ * @return array
+ */
+ private function visitRulesetPaths($rulesetNode){
+
+ $paths = array();
+ foreach($rulesetNode->paths as $p){
+ if( $p[0]->elements[0]->combinator === ' ' ){
+ $p[0]->elements[0]->combinator = '';
+ }
+
+ foreach($p as $pi){
+ if( $pi->getIsReferenced() && $pi->getIsOutput() ){
+ $paths[] = $p;
+ break;
+ }
+ }
+ }
+
+ return $paths;
+ }
+
+ protected function _removeDuplicateRules( &$rules ){
+ // remove duplicates
+ $ruleCache = array();
+ for( $i = count($rules)-1; $i >= 0 ; $i-- ){
+ $rule = $rules[$i];
+ if( $rule instanceof Less_Tree_Rule || $rule instanceof Less_Tree_NameValue ){
+
+ if( !isset($ruleCache[$rule->name]) ){
+ $ruleCache[$rule->name] = $rule;
+ }else{
+ $ruleList =& $ruleCache[$rule->name];
+
+ if( $ruleList instanceof Less_Tree_Rule || $ruleList instanceof Less_Tree_NameValue ){
+ $ruleList = $ruleCache[$rule->name] = array( $ruleCache[$rule->name]->toCSS() );
+ }
+
+ $ruleCSS = $rule->toCSS();
+ if( array_search($ruleCSS,$ruleList) !== false ){
+ array_splice($rules,$i,1);
+ }else{
+ $ruleList[] = $ruleCSS;
+ }
+ }
+ }
+ }
+ }
+
+ protected function _mergeRules( &$rules ){
+ $groups = array();
+
+ //obj($rules);
+
+ $rules_len = count($rules);
+ for( $i = 0; $i < $rules_len; $i++ ){
+ $rule = $rules[$i];
+
+ if( ($rule instanceof Less_Tree_Rule) && $rule->merge ){
+
+ $key = $rule->name;
+ if( $rule->important ){
+ $key .= ',!';
+ }
+
+ if( !isset($groups[$key]) ){
+ $groups[$key] = array();
+ }else{
+ array_splice($rules, $i--, 1);
+ $rules_len--;
+ }
+
+ $groups[$key][] = $rule;
+ }
+ }
+
+
+ foreach($groups as $parts){
+
+ if( count($parts) > 1 ){
+ $rule = $parts[0];
+ $spacedGroups = array();
+ $lastSpacedGroup = array();
+ $parts_mapped = array();
+ foreach($parts as $p){
+ if( $p->merge === '+' ){
+ if( $lastSpacedGroup ){
+ $spacedGroups[] = self::toExpression($lastSpacedGroup);
+ }
+ $lastSpacedGroup = array();
+ }
+ $lastSpacedGroup[] = $p;
+ }
+
+ $spacedGroups[] = self::toExpression($lastSpacedGroup);
+ $rule->value = self::toValue($spacedGroups);
+ }
+ }
+
+ }
+
+ public static function toExpression($values){
+ $mapped = array();
+ foreach($values as $p){
+ $mapped[] = $p->value;
+ }
+ return new Less_Tree_Expression( $mapped );
+ }
+
+ public static function toValue($values){
+ //return new Less_Tree_Value($values); ??
+
+ $mapped = array();
+ foreach($values as $p){
+ $mapped[] = $p;
+ }
+ return new Less_Tree_Value($mapped);
+ }
+}
+
+
+
+/**
+ * Parser Exception
+ *
+ * @package Less
+ * @subpackage exception
+ */
+class Less_Exception_Parser extends Exception{
+
+ /**
+ * The current file
+ *
+ * @var Less_ImportedFile
+ */
+ public $currentFile;
+
+ /**
+ * The current parser index
+ *
+ * @var integer
+ */
+ public $index;
+
+ protected $input;
+
+ protected $details = array();
+
+
+ /**
+ * Constructor
+ *
+ * @param string $message
+ * @param Exception $previous Previous exception
+ * @param integer $index The current parser index
+ * @param Less_FileInfo|string $currentFile The file
+ * @param integer $code The exception code
+ */
+ public function __construct($message = null, Exception $previous = null, $index = null, $currentFile = null, $code = 0){
+
+ if (PHP_VERSION_ID < 50300) {
+ $this->previous = $previous;
+ parent::__construct($message, $code);
+ } else {
+ parent::__construct($message, $code, $previous);
+ }
+
+ $this->currentFile = $currentFile;
+ $this->index = $index;
+
+ $this->genMessage();
+ }
+
+
+ protected function getInput(){
+
+ if( !$this->input && $this->currentFile && $this->currentFile['filename'] && file_exists($this->currentFile['filename']) ){
+ $this->input = file_get_contents( $this->currentFile['filename'] );
+ }
+ }
+
+
+
+ /**
+ * Converts the exception to string
+ *
+ * @return string
+ */
+ public function genMessage(){
+
+ if( $this->currentFile && $this->currentFile['filename'] ){
+ $this->message .= ' in '.basename($this->currentFile['filename']);
+ }
+
+ if( $this->index !== null ){
+ $this->getInput();
+ if( $this->input ){
+ $line = self::getLineNumber();
+ $this->message .= ' on line '.$line.', column '.self::getColumn();
+
+ $lines = explode("\n",$this->input);
+
+ $count = count($lines);
+ $start_line = max(0, $line-3);
+ $last_line = min($count, $start_line+6);
+ $num_len = strlen($last_line);
+ for( $i = $start_line; $i < $last_line; $i++ ){
+ $this->message .= "\n".str_pad($i+1,$num_len,'0',STR_PAD_LEFT).'| '.$lines[$i];
+ }
+ }
+ }
+
+ }
+
+ /**
+ * Returns the line number the error was encountered
+ *
+ * @return integer
+ */
+ public function getLineNumber(){
+ if( $this->index ){
+ // https://bugs.php.net/bug.php?id=49790
+ if (ini_get("mbstring.func_overload")) {
+ return substr_count(substr($this->input, 0, $this->index), "\n") + 1;
+ } else {
+ return substr_count($this->input, "\n", 0, $this->index) + 1;
+ }
+ }
+ return 1;
+ }
+
+
+ /**
+ * Returns the column the error was encountered
+ *
+ * @return integer
+ */
+ public function getColumn(){
+
+ $part = substr($this->input, 0, $this->index);
+ $pos = strrpos($part,"\n");
+ return $this->index - $pos;
+ }
+
+}
+
+
+/**
+ * Chunk Exception
+ *
+ * @package Less
+ * @subpackage exception
+ */
+class Less_Exception_Chunk extends Less_Exception_Parser{
+
+
+ protected $parserCurrentIndex = 0;
+
+ protected $emitFrom = 0;
+
+ protected $input_len;
+
+
+ /**
+ * Constructor
+ *
+ * @param string $input
+ * @param Exception $previous Previous exception
+ * @param integer $index The current parser index
+ * @param Less_FileInfo|string $currentFile The file
+ * @param integer $code The exception code
+ */
+ public function __construct($input, Exception $previous = null, $index = null, $currentFile = null, $code = 0){
+
+ $this->message = 'ParseError: Unexpected input'; //default message
+
+ $this->index = $index;
+
+ $this->currentFile = $currentFile;
+
+ $this->input = $input;
+ $this->input_len = strlen($input);
+
+ $this->Chunks();
+ $this->genMessage();
+ }
+
+
+ /**
+ * See less.js chunks()
+ * We don't actually need the chunks
+ *
+ */
+ protected function Chunks(){
+ $level = 0;
+ $parenLevel = 0;
+ $lastMultiCommentEndBrace = null;
+ $lastOpening = null;
+ $lastMultiComment = null;
+ $lastParen = null;
+
+ for( $this->parserCurrentIndex = 0; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++ ){
+ $cc = $this->CharCode($this->parserCurrentIndex);
+ if ((($cc >= 97) && ($cc <= 122)) || ($cc < 34)) {
+ // a-z or whitespace
+ continue;
+ }
+
+ switch ($cc) {
+
+ // (
+ case 40:
+ $parenLevel++;
+ $lastParen = $this->parserCurrentIndex;
+ continue;
+
+ // )
+ case 41:
+ $parenLevel--;
+ if( $parenLevel < 0 ){
+ return $this->fail("missing opening `(`");
+ }
+ continue;
+
+ // ;
+ case 59:
+ //if (!$parenLevel) { $this->emitChunk(); }
+ continue;
+
+ // {
+ case 123:
+ $level++;
+ $lastOpening = $this->parserCurrentIndex;
+ continue;
+
+ // }
+ case 125:
+ $level--;
+ if( $level < 0 ){
+ return $this->fail("missing opening `{`");
+
+ }
+ //if (!$level && !$parenLevel) { $this->emitChunk(); }
+ continue;
+ // \
+ case 92:
+ if ($this->parserCurrentIndex < $this->input_len - 1) { $this->parserCurrentIndex++; continue; }
+ return $this->fail("unescaped `\\`");
+
+ // ", ' and `
+ case 34:
+ case 39:
+ case 96:
+ $matched = 0;
+ $currentChunkStartIndex = $this->parserCurrentIndex;
+ for ($this->parserCurrentIndex = $this->parserCurrentIndex + 1; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++) {
+ $cc2 = $this->CharCode($this->parserCurrentIndex);
+ if ($cc2 > 96) { continue; }
+ if ($cc2 == $cc) { $matched = 1; break; }
+ if ($cc2 == 92) { // \
+ if ($this->parserCurrentIndex == $this->input_len - 1) {
+ return $this->fail("unescaped `\\`");
+ }
+ $this->parserCurrentIndex++;
+ }
+ }
+ if ($matched) { continue; }
+ return $this->fail("unmatched `" . chr($cc) . "`", $currentChunkStartIndex);
+
+ // /, check for comment
+ case 47:
+ if ($parenLevel || ($this->parserCurrentIndex == $this->input_len - 1)) { continue; }
+ $cc2 = $this->CharCode($this->parserCurrentIndex+1);
+ if ($cc2 == 47) {
+ // //, find lnfeed
+ for ($this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++) {
+ $cc2 = $this->CharCode($this->parserCurrentIndex);
+ if (($cc2 <= 13) && (($cc2 == 10) || ($cc2 == 13))) { break; }
+ }
+ } else if ($cc2 == 42) {
+ // /*, find */
+ $lastMultiComment = $currentChunkStartIndex = $this->parserCurrentIndex;
+ for ($this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len - 1; $this->parserCurrentIndex++) {
+ $cc2 = $this->CharCode($this->parserCurrentIndex);
+ if ($cc2 == 125) { $lastMultiCommentEndBrace = $this->parserCurrentIndex; }
+ if ($cc2 != 42) { continue; }
+ if ($this->CharCode($this->parserCurrentIndex+1) == 47) { break; }
+ }
+ if ($this->parserCurrentIndex == $this->input_len - 1) {
+ return $this->fail("missing closing `*/`", $currentChunkStartIndex);
+ }
+ }
+ continue;
+
+ // *, check for unmatched */
+ case 42:
+ if (($this->parserCurrentIndex < $this->input_len - 1) && ($this->CharCode($this->parserCurrentIndex+1) == 47)) {
+ return $this->fail("unmatched `/*`");
+ }
+ continue;
+ }
+ }
+
+ if( $level !== 0 ){
+ if( ($lastMultiComment > $lastOpening) && ($lastMultiCommentEndBrace > $lastMultiComment) ){
+ return $this->fail("missing closing `}` or `*/`", $lastOpening);
+ } else {
+ return $this->fail("missing closing `}`", $lastOpening);
+ }
+ } else if ( $parenLevel !== 0 ){
+ return $this->fail("missing closing `)`", $lastParen);
+ }
+
+
+ //chunk didn't fail
+
+
+ //$this->emitChunk(true);
+ }
+
+ public function CharCode($pos){
+ return ord($this->input[$pos]);
+ }
+
+
+ public function fail( $msg, $index = null ){
+
+ if( !$index ){
+ $this->index = $this->parserCurrentIndex;
+ }else{
+ $this->index = $index;
+ }
+ $this->message = 'ParseError: '.$msg;
+ }
+
+
+ /*
+ function emitChunk( $force = false ){
+ $len = $this->parserCurrentIndex - $this->emitFrom;
+ if ((($len < 512) && !$force) || !$len) {
+ return;
+ }
+ $chunks[] = substr($this->input, $this->emitFrom, $this->parserCurrentIndex + 1 - $this->emitFrom );
+ $this->emitFrom = $this->parserCurrentIndex + 1;
+ }
+ */
+
+}
+
+
+/**
+ * Compiler Exception
+ *
+ * @package Less
+ * @subpackage exception
+ */
+class Less_Exception_Compiler extends Less_Exception_Parser{
+
+}
+
+/**
+ * Parser output with source map
+ *
+ * @package Less
+ * @subpackage Output
+ */
+class Less_Output_Mapped extends Less_Output {
+
+ /**
+ * The source map generator
+ *
+ * @var Less_SourceMap_Generator
+ */
+ protected $generator;
+
+ /**
+ * Current line
+ *
+ * @var integer
+ */
+ protected $lineNumber = 0;
+
+ /**
+ * Current column
+ *
+ * @var integer
+ */
+ protected $column = 0;
+
+ /**
+ * Array of contents map (file and its content)
+ *
+ * @var array
+ */
+ protected $contentsMap = array();
+
+ /**
+ * Constructor
+ *
+ * @param array $contentsMap Array of filename to contents map
+ * @param Less_SourceMap_Generator $generator
+ */
+ public function __construct(array $contentsMap, $generator){
+ $this->contentsMap = $contentsMap;
+ $this->generator = $generator;
+ }
+
+ /**
+ * Adds a chunk to the stack
+ * The $index for less.php may be different from less.js since less.php does not chunkify inputs
+ *
+ * @param string $chunk
+ * @param string $fileInfo
+ * @param integer $index
+ * @param mixed $mapLines
+ */
+ public function add($chunk, $fileInfo = null, $index = 0, $mapLines = null){
+
+ //ignore adding empty strings
+ if( $chunk === '' ){
+ return;
+ }
+
+
+ $sourceLines = array();
+ $sourceColumns = ' ';
+
+
+ if( $fileInfo ){
+
+ $url = $fileInfo['currentUri'];
+
+ if( isset($this->contentsMap[$url]) ){
+ $inputSource = substr($this->contentsMap[$url], 0, $index);
+ $sourceLines = explode("\n", $inputSource);
+ $sourceColumns = end($sourceLines);
+ }else{
+ throw new Exception('Filename '.$url.' not in contentsMap');
+ }
+
+ }
+
+ $lines = explode("\n", $chunk);
+ $columns = end($lines);
+
+ if($fileInfo){
+
+ if(!$mapLines){
+ $this->generator->addMapping(
+ $this->lineNumber + 1, // generated_line
+ $this->column, // generated_column
+ count($sourceLines), // original_line
+ strlen($sourceColumns), // original_column
+ $fileInfo
+ );
+ }else{
+ for($i = 0, $count = count($lines); $i < $count; $i++){
+ $this->generator->addMapping(
+ $this->lineNumber + $i + 1, // generated_line
+ $i === 0 ? $this->column : 0, // generated_column
+ count($sourceLines) + $i, // original_line
+ $i === 0 ? strlen($sourceColumns) : 0, // original_column
+ $fileInfo
+ );
+ }
+ }
+ }
+
+ if(count($lines) === 1){
+ $this->column += strlen($columns);
+ }else{
+ $this->lineNumber += count($lines) - 1;
+ $this->column = strlen($columns);
+ }
+
+ // add only chunk
+ parent::add($chunk);
+ }
+
+}
+
+/**
+ * Encode / Decode Base64 VLQ.
+ *
+ * @package Less
+ * @subpackage SourceMap
+ */
+class Less_SourceMap_Base64VLQ {
+
+ /**
+ * Shift
+ *
+ * @var integer
+ */
+ private $shift = 5;
+
+ /**
+ * Mask
+ *
+ * @var integer
+ */
+ private $mask = 0x1F; // == (1 << shift) == 0b00011111
+
+ /**
+ * Continuation bit
+ *
+ * @var integer
+ */
+ private $continuationBit = 0x20; // == (mask - 1 ) == 0b00100000
+
+ /**
+ * Char to integer map
+ *
+ * @var array
+ */
+ private $charToIntMap = array(
+ 'A' => 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6,
+ 'H' => 7,'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13,
+ 'O' => 14, 'P' => 15, 'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20,
+ 'V' => 21, 'W' => 22, 'X' => 23, 'Y' => 24, 'Z' => 25, 'a' => 26, 'b' => 27,
+ 'c' => 28, 'd' => 29, 'e' => 30, 'f' => 31, 'g' => 32, 'h' => 33, 'i' => 34,
+ 'j' => 35, 'k' => 36, 'l' => 37, 'm' => 38, 'n' => 39, 'o' => 40, 'p' => 41,
+ 'q' => 42, 'r' => 43, 's' => 44, 't' => 45, 'u' => 46, 'v' => 47, 'w' => 48,
+ 'x' => 49, 'y' => 50, 'z' => 51, 0 => 52, 1 => 53, 2 => 54, 3 => 55, 4 => 56,
+ 5 => 57, 6 => 58, 7 => 59, 8 => 60, 9 => 61, '+' => 62, '/' => 63,
+ );
+
+ /**
+ * Integer to char map
+ *
+ * @var array
+ */
+ private $intToCharMap = array(
+ 0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G',
+ 7 => 'H', 8 => 'I', 9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N',
+ 14 => 'O', 15 => 'P', 16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U',
+ 21 => 'V', 22 => 'W', 23 => 'X', 24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b',
+ 28 => 'c', 29 => 'd', 30 => 'e', 31 => 'f', 32 => 'g', 33 => 'h', 34 => 'i',
+ 35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n', 40 => 'o', 41 => 'p',
+ 42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v', 48 => 'w',
+ 49 => 'x', 50 => 'y', 51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3',
+ 56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8', 61 => '9', 62 => '+',
+ 63 => '/',
+ );
+
+ /**
+ * Constructor
+ */
+ public function __construct(){
+ // I leave it here for future reference
+ // foreach(str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') as $i => $char)
+ // {
+ // $this->charToIntMap[$char] = $i;
+ // $this->intToCharMap[$i] = $char;
+ // }
+ }
+
+ /**
+ * Convert from a two-complement value to a value where the sign bit is
+ * is placed in the least significant bit. For example, as decimals:
+ * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
+ * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
+ * We generate the value for 32 bit machines, hence -2147483648 becomes 1, not 4294967297,
+ * even on a 64 bit machine.
+ * @param string $aValue
+ */
+ public function toVLQSigned($aValue){
+ return 0xffffffff & ($aValue < 0 ? ((-$aValue) << 1) + 1 : ($aValue << 1) + 0);
+ }
+
+ /**
+ * Convert to a two-complement value from a value where the sign bit is
+ * is placed in the least significant bit. For example, as decimals:
+ * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1
+ * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2
+ * We assume that the value was generated with a 32 bit machine in mind.
+ * Hence
+ * 1 becomes -2147483648
+ * even on a 64 bit machine.
+ * @param integer $aValue
+ */
+ public function fromVLQSigned($aValue){
+ return $aValue & 1 ? $this->zeroFill(~$aValue + 2, 1) | (-1 - 0x7fffffff) : $this->zeroFill($aValue, 1);
+ }
+
+ /**
+ * Return the base 64 VLQ encoded value.
+ *
+ * @param string $aValue The value to encode
+ * @return string The encoded value
+ */
+ public function encode($aValue){
+ $encoded = '';
+ $vlq = $this->toVLQSigned($aValue);
+ do
+ {
+ $digit = $vlq & $this->mask;
+ $vlq = $this->zeroFill($vlq, $this->shift);
+ if($vlq > 0){
+ $digit |= $this->continuationBit;
+ }
+ $encoded .= $this->base64Encode($digit);
+ } while($vlq > 0);
+
+ return $encoded;
+ }
+
+ /**
+ * Return the value decoded from base 64 VLQ.
+ *
+ * @param string $encoded The encoded value to decode
+ * @return integer The decoded value
+ */
+ public function decode($encoded){
+ $vlq = 0;
+ $i = 0;
+ do
+ {
+ $digit = $this->base64Decode($encoded[$i]);
+ $vlq |= ($digit & $this->mask) << ($i * $this->shift);
+ $i++;
+ } while($digit & $this->continuationBit);
+
+ return $this->fromVLQSigned($vlq);
+ }
+
+ /**
+ * Right shift with zero fill.
+ *
+ * @param integer $a number to shift
+ * @param integer $b number of bits to shift
+ * @return integer
+ */
+ public function zeroFill($a, $b){
+ return ($a >= 0) ? ($a >> $b) : ($a >> $b) & (PHP_INT_MAX >> ($b - 1));
+ }
+
+ /**
+ * Encode single 6-bit digit as base64.
+ *
+ * @param integer $number
+ * @return string
+ * @throws Exception If the number is invalid
+ */
+ public function base64Encode($number){
+ if($number < 0 || $number > 63){
+ throw new Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number));
+ }
+ return $this->intToCharMap[$number];
+ }
+
+ /**
+ * Decode single 6-bit digit from base64
+ *
+ * @param string $char
+ * @return number
+ * @throws Exception If the number is invalid
+ */
+ public function base64Decode($char){
+ if(!array_key_exists($char, $this->charToIntMap)){
+ throw new Exception(sprintf('Invalid base 64 digit "%s" given.', $char));
+ }
+ return $this->charToIntMap[$char];
+ }
+
+}
+
+
+/**
+ * Source map generator
+ *
+ * @package Less
+ * @subpackage Output
+ */
+class Less_SourceMap_Generator extends Less_Configurable {
+
+ /**
+ * What version of source map does the generator generate?
+ */
+ const VERSION = 3;
+
+ /**
+ * Array of default options
+ *
+ * @var array
+ */
+ protected $defaultOptions = array(
+ // an optional source root, useful for relocating source files
+ // on a server or removing repeated values in the 'sources' entry.
+ // This value is prepended to the individual entries in the 'source' field.
+ 'sourceRoot' => '',
+
+ // an optional name of the generated code that this source map is associated with.
+ 'sourceMapFilename' => null,
+
+ // url of the map
+ 'sourceMapURL' => null,
+
+ // absolute path to a file to write the map to
+ 'sourceMapWriteTo' => null,
+
+ // output source contents?
+ 'outputSourceFiles' => false,
+
+ // base path for filename normalization
+ 'sourceMapRootpath' => '',
+
+ // base path for filename normalization
+ 'sourceMapBasepath' => ''
+ );
+
+ /**
+ * The base64 VLQ encoder
+ *
+ * @var Less_SourceMap_Base64VLQ
+ */
+ protected $encoder;
+
+ /**
+ * Array of mappings
+ *
+ * @var array
+ */
+ protected $mappings = array();
+
+ /**
+ * The root node
+ *
+ * @var Less_Tree_Ruleset
+ */
+ protected $root;
+
+ /**
+ * Array of contents map
+ *
+ * @var array
+ */
+ protected $contentsMap = array();
+
+ /**
+ * File to content map
+ *
+ * @var array
+ */
+ protected $sources = array();
+ protected $source_keys = array();
+
+ /**
+ * Constructor
+ *
+ * @param Less_Tree_Ruleset $root The root node
+ * @param array $options Array of options
+ */
+ public function __construct(Less_Tree_Ruleset $root, $contentsMap, $options = array()){
+ $this->root = $root;
+ $this->contentsMap = $contentsMap;
+ $this->encoder = new Less_SourceMap_Base64VLQ();
+
+ $this->SetOptions($options);
+
+ $this->options['sourceMapRootpath'] = $this->fixWindowsPath($this->options['sourceMapRootpath'], true);
+ $this->options['sourceMapBasepath'] = $this->fixWindowsPath($this->options['sourceMapBasepath'], true);
+ }
+
+ /**
+ * Generates the CSS
+ *
+ * @return string
+ */
+ public function generateCSS(){
+ $output = new Less_Output_Mapped($this->contentsMap, $this);
+
+ // catch the output
+ $this->root->genCSS($output);
+
+
+ $sourceMapUrl = $this->getOption('sourceMapURL');
+ $sourceMapFilename = $this->getOption('sourceMapFilename');
+ $sourceMapContent = $this->generateJson();
+ $sourceMapWriteTo = $this->getOption('sourceMapWriteTo');
+
+ if( !$sourceMapUrl && $sourceMapFilename ){
+ $sourceMapUrl = $this->normalizeFilename($sourceMapFilename);
+ }
+
+ // write map to a file
+ if( $sourceMapWriteTo ){
+ $this->saveMap($sourceMapWriteTo, $sourceMapContent);
+ }
+
+ // inline the map
+ if( !$sourceMapUrl ){
+ $sourceMapUrl = sprintf('data:application/json,%s', Less_Functions::encodeURIComponent($sourceMapContent));
+ }
+
+ if( $sourceMapUrl ){
+ $output->add( sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl) );
+ }
+
+ return $output->toString();
+ }
+
+ /**
+ * Saves the source map to a file
+ *
+ * @param string $file The absolute path to a file
+ * @param string $content The content to write
+ * @throws Exception If the file could not be saved
+ */
+ protected function saveMap($file, $content){
+ $dir = dirname($file);
+ // directory does not exist
+ if( !is_dir($dir) ){
+ // FIXME: create the dir automatically?
+ throw new Exception(sprintf('The directory "%s" does not exist. Cannot save the source map.', $dir));
+ }
+ // FIXME: proper saving, with dir write check!
+ if(file_put_contents($file, $content) === false){
+ throw new Exception(sprintf('Cannot save the source map to "%s"', $file));
+ }
+ return true;
+ }
+
+ /**
+ * Normalizes the filename
+ *
+ * @param string $filename
+ * @return string
+ */
+ protected function normalizeFilename($filename){
+
+ $filename = $this->fixWindowsPath($filename);
+
+ $rootpath = $this->getOption('sourceMapRootpath');
+ $basePath = $this->getOption('sourceMapBasepath');
+
+ // "Trim" the 'sourceMapBasepath' from the output filename.
+ if (strpos($filename, $basePath) === 0) {
+ $filename = substr($filename, strlen($basePath));
+ }
+
+ // Remove extra leading path separators.
+ if(strpos($filename, '\\') === 0 || strpos($filename, '/') === 0){
+ $filename = substr($filename, 1);
+ }
+
+ return $rootpath . $filename;
+ }
+
+ /**
+ * Adds a mapping
+ *
+ * @param integer $generatedLine The line number in generated file
+ * @param integer $generatedColumn The column number in generated file
+ * @param integer $originalLine The line number in original file
+ * @param integer $originalColumn The column number in original file
+ * @param string $sourceFile The original source file
+ */
+ public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $fileInfo ){
+
+ $this->mappings[] = array(
+ 'generated_line' => $generatedLine,
+ 'generated_column' => $generatedColumn,
+ 'original_line' => $originalLine,
+ 'original_column' => $originalColumn,
+ 'source_file' => $fileInfo['currentUri']
+ );
+
+ $this->sources[$fileInfo['currentUri']] = $fileInfo['filename'];
+ }
+
+
+ /**
+ * Generates the JSON source map
+ *
+ * @return string
+ * @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#
+ */
+ protected function generateJson(){
+
+ $sourceMap = array();
+ $mappings = $this->generateMappings();
+
+ // File version (always the first entry in the object) and must be a positive integer.
+ $sourceMap['version'] = self::VERSION;
+
+
+ // An optional name of the generated code that this source map is associated with.
+ $file = $this->getOption('sourceMapFilename');
+ if( $file ){
+ $sourceMap['file'] = $file;
+ }
+
+
+ // An optional source root, useful for relocating source files on a server or removing repeated values in the 'sources' entry. This value is prepended to the individual entries in the 'source' field.
+ $root = $this->getOption('sourceRoot');
+ if( $root ){
+ $sourceMap['sourceRoot'] = $root;
+ }
+
+
+ // A list of original sources used by the 'mappings' entry.
+ $sourceMap['sources'] = array();
+ foreach($this->sources as $source_uri => $source_filename){
+ $sourceMap['sources'][] = $this->normalizeFilename($source_filename);
+ }
+
+
+ // A list of symbol names used by the 'mappings' entry.
+ $sourceMap['names'] = array();
+
+ // A string with the encoded mapping data.
+ $sourceMap['mappings'] = $mappings;
+
+ if( $this->getOption('outputSourceFiles') ){
+ // An optional list of source content, useful when the 'source' can't be hosted.
+ // The contents are listed in the same order as the sources above.
+ // 'null' may be used if some original sources should be retrieved by name.
+ $sourceMap['sourcesContent'] = $this->getSourcesContent();
+ }
+
+ // less.js compat fixes
+ if( count($sourceMap['sources']) && empty($sourceMap['sourceRoot']) ){
+ unset($sourceMap['sourceRoot']);
+ }
+
+ return json_encode($sourceMap);
+ }
+
+ /**
+ * Returns the sources contents
+ *
+ * @return array|null
+ */
+ protected function getSourcesContent(){
+ if(empty($this->sources)){
+ return;
+ }
+ $content = array();
+ foreach($this->sources as $sourceFile){
+ $content[] = file_get_contents($sourceFile);
+ }
+ return $content;
+ }
+
+ /**
+ * Generates the mappings string
+ *
+ * @return string
+ */
+ public function generateMappings(){
+
+ if( !count($this->mappings) ){
+ return '';
+ }
+
+ $this->source_keys = array_flip(array_keys($this->sources));
+
+
+ // group mappings by generated line number.
+ $groupedMap = $groupedMapEncoded = array();
+ foreach($this->mappings as $m){
+ $groupedMap[$m['generated_line']][] = $m;
+ }
+ ksort($groupedMap);
+
+ $lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine = $lastOriginalColumn = 0;
+
+ foreach($groupedMap as $lineNumber => $line_map){
+ while(++$lastGeneratedLine < $lineNumber){
+ $groupedMapEncoded[] = ';';
+ }
+
+ $lineMapEncoded = array();
+ $lastGeneratedColumn = 0;
+
+ foreach($line_map as $m){
+ $mapEncoded = $this->encoder->encode($m['generated_column'] - $lastGeneratedColumn);
+ $lastGeneratedColumn = $m['generated_column'];
+
+ // find the index
+ if( $m['source_file'] ){
+ $index = $this->findFileIndex($m['source_file']);
+ if( $index !== false ){
+ $mapEncoded .= $this->encoder->encode($index - $lastOriginalIndex);
+ $lastOriginalIndex = $index;
+
+ // lines are stored 0-based in SourceMap spec version 3
+ $mapEncoded .= $this->encoder->encode($m['original_line'] - 1 - $lastOriginalLine);
+ $lastOriginalLine = $m['original_line'] - 1;
+
+ $mapEncoded .= $this->encoder->encode($m['original_column'] - $lastOriginalColumn);
+ $lastOriginalColumn = $m['original_column'];
+ }
+ }
+
+ $lineMapEncoded[] = $mapEncoded;
+ }
+
+ $groupedMapEncoded[] = implode(',', $lineMapEncoded) . ';';
+ }
+
+ return rtrim(implode($groupedMapEncoded), ';');
+ }
+
+ /**
+ * Finds the index for the filename
+ *
+ * @param string $filename
+ * @return integer|false
+ */
+ protected function findFileIndex($filename){
+ return $this->source_keys[$filename];
+ }
+
+ /**
+ * fix windows paths
+ * @param string $path
+ * @return string
+ */
+ public function fixWindowsPath($path, $addEndSlash = false){
+ $slash = ($addEndSlash) ? '/' : '';
+ if( !empty($path) ){
+ $path = str_replace('\\', '/', $path);
+ $path = rtrim($path,'/') . $slash;
+ }
+
+ return $path;
+ }
+
+} +
\ No newline at end of file
diff --git a/modules/util/Line.class.php b/modules/util/Line.class.php
@@ -0,0 +1,152 @@
+<?php
+
+/**
+ * Darstellung einer Zeile in einem Freitext.<br>
+ * <br>
+ * Im Konstruktor wird die Zeile analysiert und es wird festgestellt, was
+ * die Zeile f�r einen Inhalt hat (z.B. ein Listenelement, eine �berschrift, usw.)<br>
+ *
+ * @author Jan Dankert
+ * @version $Revision$
+ * @package openrat.services
+ */
+class Line
+{
+ var $source; // Der urspr�ngliche Inhalt
+ var $value; // Der textuelle Inhalt (sofern vorhanden)
+
+ var $isDefinition = false; // Definitionseintrag
+ var $isList = false; // nicht-numerierte Liste
+ var $isNumberedList = false; // numerierte Liste
+ var $indent = 0; // Einschubtiefe der Liste
+
+ var $isHeadline = false; // �berschrift
+ var $isHeadlineUnderline = false; // unterstrichene �berschrift
+ var $headlineLevel = 0; // Grad der �berschrift (1,2,3...)
+
+
+ var $isTableOfContent = false; // Inhaltsverzeichnis
+ var $isTable = false; // Tabelle
+ var $isCode = false; // Code
+ var $isQuote = false; // Zitat
+ var $isQuotePraefix = false; // Zitatbeginn/-ende
+
+ var $isUnparsed = false;
+
+ var $isEmpty = false; // Zeile ist leer
+ var $isNormal = false; // Zeile ist ohne besonderen Inhalt, d.h. keine Tabelle, kein Zitat, usw.
+
+
+
+ /**
+ * Erzeugt einen Zeilenobjekt, der Text im Parameter wird dabei geparst.
+ */
+ function __construct( $s )
+ {
+ global $conf;
+ $text_markup = $conf['editor']['text-markup'];
+// Html::debug($text_markup);
+
+ $list_numbered = $text_markup['list-numbered' ];
+ $list_unnumbered = $text_markup['list-unnumbered'];
+ $headline = $text_markup['headline' ];
+
+ $this->source = $s;
+ $this->value = $s;
+
+ $this->isList = $this->isAnErsterStelle(ltrim($s),$list_unnumbered);
+ $this->isNumberedList = $this->isAnErsterStelle(ltrim($s),$list_numbered );
+ $this->indent = strlen($s) - strlen(ltrim($s)) + 1;
+
+ if ( $this->isList || $this->isNumberedList )
+ $this->value = ltrim(substr($s,$this->indent));
+
+ $this->level = strspn( $s,$headline );
+ $this->isHeadline = $this->level >= 1;
+
+ if ( $this->isHeadline )
+ $this->value = ltrim(substr($s,$this->level));
+
+
+ $hl = array( 1 => $text_markup['headline_level1_underline'],
+ 2 => $text_markup['headline_level2_underline'],
+ 3 => $text_markup['headline_level3_underline'] );
+
+ foreach($hl as $lev=>$char )
+ {
+ if ( substr($s,0,3*strlen($char))==str_repeat($char,3*strlen($char)) )
+ {
+ $this->isHeadlineUnderline = true;
+ $this->isNormal = true;
+ $this->level = intval($lev);
+ }
+ }
+
+ if ( $this->isAnErsterStelle($s,$text_markup['table-of-content']) )
+ {
+ $this->isTableOfContent = true;
+ $this->isNumberedList = false;
+ $this->isList = false;
+ $this->value = '';
+ }
+ elseif
+ ( $this->isHeadline ||
+ $this->isHeadlineUnderline ||
+ $this->isList ||
+ $this->isNumberedList )
+ {
+ ;
+ }
+ elseif ( $this->isAnErsterStelle($s,$text_markup['table-cell-sep']) )
+ {
+ $this->isTable = true;
+ $this->value = '';
+ }
+ elseif ( $this->isAnErsterStelle($s,$text_markup['pre-begin']) && !$this->isHeadlineUnderline )
+ {
+ $this->isCode = true;
+ $this->value = substr($s,strlen($text_markup['pre-begin']));
+ }
+ elseif ( strpos($s, $text_markup['definition-sep'])!== false )
+ {
+ $this->isDefinition = true;
+ }
+ elseif ( trim($s)==$text_markup['quote-line-begin'] )
+ {
+ $this->isQuote = true;
+ }
+ elseif ( $this->isAnErsterStelle($s,$text_markup['quote']) && strlen(trim($s))>1 )
+ {
+ $this->isQuotePraefix = true;
+ $this->value = ltrim(substr($s,strlen($text_markup['quote'])));
+ $this->level = strspn( str_replace(' ','',$s),$text_markup['quote'] );
+ }
+ elseif ( $this->isAnErsterStelle($s,'`') )
+ {
+ $this->isUnparsed = true;
+ $this->value = substr($this->value,1);
+ }
+ elseif ( $s == '' )
+ {
+ $this->isEmpty = true;
+ }
+ else
+ {
+ $this->isNormal = true;
+ }
+ }
+
+
+ /**
+ * Stellt fest, ob $wort am Anfang von $text steht.
+ */
+ function isAnErsterStelle( $text,$wort )
+ {
+// Html::debug($text,"Text");
+// Html::debug($wort,"Wort");
+// Html::debug(substr($text,0,strlen($wort))==$wort,'Ergebnis');
+ return substr($text,0,strlen($wort))==$wort;
+ }
+}
+
+?>+
\ No newline at end of file
diff --git a/modules/util/Macro.class.php b/modules/util/Macro.class.php
@@ -0,0 +1,194 @@
+<?php
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+use cms\model\Page;
+
+
+/**
+ * Service-Klasse fuer allgemeine Interface-Methoden. Insbesondere
+ * in Code-Elementen kann und soll auf diese Methoden zurueckgegriffen
+ * werden.
+ * @author $Author$
+ * @version $Revision$
+ * @package openrat.services
+ */
+class Macro
+{
+ var $project;
+ var $output = '';
+ var $objectid = 0;
+
+ /**
+ * @var Page
+ */
+ var $page;
+ var $parameters = array();
+ var $description = '';
+
+
+ /**
+ * Ausführen des Makros. Diese Methode sollte durch die Unterklasse überschrieben werden.
+ */
+ public function execute()
+ {
+ // overwrite this in subclasses
+ }
+
+
+ /**
+ * Holt die aktuellen Datenbankverbindung.
+ */
+ public function db()
+ {
+ return db_connection();
+ }
+
+
+ /**
+ * Holt die aktuelle Objekt-Id.
+ * @return number
+ */
+ public function getObjectId()
+ {
+ return $this->objectid;
+ }
+
+
+ /**
+ * Holt die aktuelle Seite.
+ * @return \cms\model\Page
+ */
+ public function getPage()
+ {
+ return new Page( $this->objectid );
+ }
+
+
+ /**
+ * Holt das aktuelle Objekt.
+ * @return Object
+ */
+ public function &getObject()
+ {
+ return Session::getObject();
+ }
+
+
+ /**
+ * Setzt eine Objekt-Id.
+ * @param int $objectid
+ */
+ public function setObjectId( $objectid )
+ {
+ $this->objectid = $objectid;
+ }
+
+
+ /**
+ * Ermittelt die Id des Wurzelordners im aktuellen Projekt.
+ */
+ public function getRootObjectId()
+ {
+ $project = Session::getProject();
+ return $project->getRootObjectId();
+ }
+
+
+ /**
+ * DO NOT USE.
+ */
+ public function folderid()
+ {
+ global $SESS;
+ return $SESS['folderid'];
+ }
+
+
+ /**
+ * Löscht die bisher erzeugte Ausgabe.
+ */
+ public function delOutput()
+ {
+ $this->output = '';
+ }
+
+ /**
+ * Ergänzt die Standardausgabe um den gewünschten Wert.
+ */
+ public function output( $text )
+ {
+ $this->output .= $text;
+ }
+
+
+ /**
+ * Ergänzt die Standardausgabe um den gewünschten Wert. Es wird ein Zeilenendezeichen ergänzt.
+ */
+ public function outputLn( $text )
+ {
+ $this->output .= $text."\n";
+ }
+
+
+ /**
+ * Ermittelt die bisher erstellte Ausgabe.
+ * @return string
+ */
+ public function getOutput()
+ {
+ return $this->output;
+ }
+
+
+ /**
+ * Setzt eine Sitzungsvariable.
+ *
+ * @param String $var
+ * @param Object $value
+ */
+ public function setSessionVar( $var,$value )
+ {
+ Session::set( $var,$value );
+ }
+
+
+ /**
+ * Ermittelt eine Sitzungsvariable.
+ *
+ * @param String $var
+ * @return string
+ */
+ public function getSessionVar( $var )
+ {
+ return Session::get( $var );
+ }
+
+
+ /**
+ * Ermittelt den Pfad auf ein Objekt.
+ * @param Object
+ * @return string
+ */
+ public function pathToObject( $obj )
+ {
+ if ( is_object($obj) )
+ return $this->page->path_to_object($obj->objectid);
+ else
+ return $this->page->path_to_object($obj);
+ }
+
+}+
\ No newline at end of file
diff --git a/modules/util/Mail.class.php b/modules/util/Mail.class.php
@@ -0,0 +1,571 @@
+<?php
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+/**
+ * Erzeugen und Versender einer E-Mail gemaess RFC 822.<br>
+ * <br>
+ * Die E-Mail kann entweder �ber
+ * - die interne PHP-Funktion "mail()" versendet werden oder
+ * - direkt per SMTP-Protokoll an einen SMTP-Server.<br>
+ * Welcher Weg gew�hlt wird, kann konfiguriert werden.<br>
+ * <br>
+ * Prinzipiell spricht nichts gegen die interne PHP-Funktion mail(), wenn diese
+ * aber nicht zu Verf�gung steht oder PHP ungeeignet konfiguriert ist, so kann
+ * SMTP direkt verwendet werden. Hierbei sollte wenn m�glich ein Relay-Host
+ * eingesetzt werden. Die Mail kann zwar auch direkt an Mail-Exchanger (MX) des
+ * Empf�ngers geschickt werden, falls dieser aber Greylisting einsetzt ist eine
+ * Zustellung nicht m�glich.<br>
+ * <br>
+ *
+ * @author Jan Dankert
+ * @version $Id$
+ * @package serviceClasses
+ */
+class Mail
+{
+ var $from = '';
+ var $to = '';
+ var $bcc = '';
+ var $cc = '';
+ var $subject = '';
+ var $text = '';
+ var $header = array();
+ var $nl = '';
+
+ /**
+ * Falls beim Versendern der E-Mail etwas schiefgeht, steht hier drin
+ * die technische Fehlermeldung.
+ *
+ * @var String Fehler
+ */
+ var $error = array();
+
+ /**
+ * Set to true for debugging.
+ * If true, All SMTP-Commands are written to error log.
+ *
+ * @var unknown_type
+ */
+ var $debug = true;
+
+
+ /**
+ * Konstruktor.
+ * Es werden folgende Parameter erwartet
+ * @param String $to Empf�nger
+ * @param String der Textschl�ssel
+ * @param String unbenutzt.
+ * @return Mail
+ */
+ function Mail( $to,$text,$xy='' )
+ {
+ global $conf;
+
+ // Zeilenumbruch CR/LF gem. RFC 822.
+ $this->nl = chr(13).chr(10);
+
+ if ( !empty($conf['mail']['from']) )
+ $this->from = $this->header_encode($conf['mail']['from']);
+
+ // Priorit�t definieren (sofern konfiguriert)
+ if ( !empty($conf['mail']['priority']) )
+ $this->header[] = 'X-Priority: '.$conf['mail']['priority'];
+
+ $this->header[] = 'X-Mailer: '.$this->header_encode(OR_TITLE.' '.OR_VERSION);
+ $this->header[] = 'Content-Type: text/plain; charset='.lang( 'CHARSET' );
+ $this->subject = $this->header_encode(lang( 'mail_subject_'.$text ));
+ $this->to = $this->header_encode($to);
+
+ $this->text = $this->nl.wordwrap(str_replace(';',$this->nl,lang('mail_text_'.$text)),70,$this->nl).$this->nl;
+
+ // Signatur anhaengen (sofern konfiguriert)
+ if ( !empty($conf['mail']['signature']) )
+ {
+ $this->text .= $this->nl.'-- '.$this->nl;
+ $this->text .= str_replace(';',$this->nl,$conf['mail']['signature']);
+ $this->text .= $this->nl;
+ }
+
+ // Kopie-Empf�nger
+ if ( !empty($conf['mail']['cc']) )
+ $this->cc = $this->header_encode($conf['mail']['cc']);
+
+ // Blindkopie-Empf�nger
+ if ( !empty($conf['mail']['bcc']) )
+ $this->bcc = $this->header_encode($conf['mail']['bcc']);
+ }
+
+
+
+ /**
+ * Kodiert einen Text in das Format "Quoted-printable".<br>
+ * See RFC 2045.
+ * @param String $text Eingabe
+ * @return Text im quoted-printable-Format
+ */
+ function quoted_printable_encode( $text )
+ {
+ $text = str_replace(' ','=20',$text);
+
+ for( $i=128; $i<=255; $i++ )
+ {
+ $text = str_replace( chr($i),'='.dechex($i),$text );
+ }
+
+ return $text;
+ }
+
+
+
+ /**
+ * Setzen einer Variablen in den Mail-Inhalt.
+ */
+ function setVar( $varName,$varInhalt)
+ {
+ $this->text = str_replace( '{'.$varName.'}', $varInhalt, $this->text );
+ }
+
+
+ /**
+ * Mail absenden.
+ * Die E-Mail wird versendet.
+ *
+ * @return boolean Erfolg
+ */
+ function send()
+ {
+ global $conf;
+
+ $to_domain = array_pop( explode('@',$this->to) );
+
+ // Prüfen gegen die Whitelist
+ $white = @$conf['mail']['whitelist'];
+
+ if ( !empty($white) )
+ {
+ if ( ! $this->containsDomain($to_domain,$white) )
+ {
+ // Wenn Domain nicht in Whitelist gefunden, dann Mail nicht verschicken.
+ $this->error[] = 'Mail-Domain is not whitelisted';
+ return false;
+ }
+ }
+
+ // Prüfen gegen die Blacklist
+ $black = @$conf['mail']['blacklist'];
+
+ if ( !empty($black) )
+ {
+ if ( $this->containsDomain($to_domain,$black) )
+ {
+ // Wenn Domain in Blacklist gefunden, dann Mail nicht verschicken.
+ $this->error[] = 'Mail-Domain is blacklisted';
+ return false;
+ }
+ }
+
+ // Header um Adressangaben erg�nzen.
+ if ( !empty($this->from ) )
+ $this->header[] = 'From: '.$this->from;
+
+ if ( !empty($this->cc ) )
+ $this->header[] = 'Cc: '.$this->cc;
+
+ if ( !empty($this->bcc ) )
+ $this->header[] = 'Bcc: '.$this->bcc;
+
+ // Mail versenden
+ if ( strtolower(@$conf['mail']['client']) == 'php' )
+ {
+ // PHP-interne Mailfunktion verwenden.
+ $result = @mail( $this->to, // Empf�nger
+ $this->subject, // Betreff
+ $this->text, // Inhalt
+ // Lt. RFC822 müssen Header mit CRLF enden.
+ // ABER: Der Parameter "additional headers" verlangt offenbar \n am Zeilenende.
+ implode("\n",$this->header) );
+ if ( !$result )
+ // Die E-Mail wurde nicht akzeptiert.
+ // Genauer geht es leider nicht, da mail() nur einen boolean-Wert
+ // zur�ck liefert.
+ $this->error[] = 'Mail was NOT accepted by mail()';
+
+ return $result;
+ }
+ else
+ {
+ // eigenen SMTP-Dialog verwenden.
+ $smtpConf = $conf['mail']['smtp'];
+
+ if ( !empty($smtpConf['host']))
+ {
+ // Eigenen Relay-Host verwenden.
+ $mxHost = $smtpConf['host'];
+ $mxPort = intval($smtpConf['port']);
+ }
+ else
+ {
+ // Mail direkt zustellen.
+ $mxHost = $this->getMxHost($this->to);
+
+ if ( empty($mxHost) )
+ {
+ $this->error[] = "No MX-Entry found. Mail could not be sent.";
+ return false;
+ }
+
+ if ($smtpConf['ssl'])
+ $mxPort = 465;
+ else
+ $mxPort = 25;
+ }
+
+
+ if ( !empty($smtpConf['localhost']))
+ {
+ $myHost = $smtpConf['localhost'];
+ }
+ else
+ {
+ $myHost = gethostbyaddr(getenv('REMOTE_ADDR'));
+ }
+
+ if ( $smtpConf['ssl'])
+ $proto = 'ssl';
+ else
+ $proto = 'tcp';
+
+ //connect to the host and port
+ $smtpSocket = fsockopen($proto.'://'.$mxHost,$mxPort, $errno, $errstr, intval($smtpConf['timeout']));
+
+ if ( !is_resource($smtpSocket) )
+ {
+ $this->error[] = 'Connection failed to: '.$proto.'://'.$mxHost.':'.$mxPort.' ('.$errstr.'/'.$errno.')';
+ return false;
+ }
+
+ $smtpResponse = fgets($smtpSocket, 4096);
+ if ( $this->debug)
+ $this->error[] = trim($smtpResponse);
+
+ if ( substr($smtpResponse,0,3) != '220' )
+ {
+ $this->error[] = 'No 220: '.trim($smtpResponse);
+ return false;
+ }
+
+ if ( !is_resource($smtpSocket) )
+ {
+ $this->error[] = 'Connection failed to: '.$smtpConf['host'].':'.$smtpConf['port'].' ('.$smtpResponse.')';
+ return false;
+ }
+
+ //you have to say HELO again after TLS is started
+ $smtpResponse = $this->sendSmtpCommand($smtpSocket,'HELO '.$myHost);
+
+ if ( substr($smtpResponse,0,3) != '250' )
+ {
+ $this->error[] = "No 2xx after HELO, server says: ".$smtpResponse;
+ $this->sendSmtpQuit($smtpSocket);
+ return false;
+ }
+
+ if ( $smtpConf['tls'] )
+ {
+ $smtpResponse = $this->sendSmtpCommand($smtpSocket,'STARTTLS');
+ if ( substr($smtpResponse,0,3) == '220' )
+ {
+ // STARTTLS ist gelungen.
+ //you have to say HELO again after TLS is started
+ $smtpResponse = $this->sendSmtpCommand($smtpSocket,'HELO '.$myHost);
+
+ if ( substr($smtpResponse,0,3) != '250' )
+ {
+ $this->error[] = "No 2xx after HELO, server says: ".$smtpResponse;
+ $this->sendSmtpQuit($smtpSocket);
+ return false;
+ }
+ }
+ else
+ {
+ // STARTTLS ging in die Hose. Einfach weitermachen.
+ }
+ }
+
+ // request for auth login
+ if ( isset($smtpConf['auth_username']) && !empty($smtpConf['host']) && !empty($smtpConf['auth_username']))
+ {
+ $smtpResponse = $this->sendSmtpCommand($smtpSocket,"AUTH LOGIN");
+ if ( substr($smtpResponse,0,3) != '334' )
+ {
+ $this->error[] = "No 334 after AUTH_LOGIN, server says: ".$smtpResponse;
+ $this->sendSmtpQuit($smtpSocket);
+ return false;
+ }
+
+ if ( $this->debug)
+ $this->error[] = 'Login for '.$smtpConf['auth_username'];
+
+ //send the username
+ $smtpResponse = $this->sendSmtpCommand($smtpSocket, base64_encode($smtpConf['auth_username']));
+ if ( substr($smtpResponse,0,3) != '334' )
+ {
+ $this->error[] = "No 3xx after setting username, server says: ".$smtpResponse;
+ $this->sendSmtpQuit($smtpSocket);
+ return false;
+ }
+
+ //send the password
+ $smtpResponse = $this->sendSmtpCommand($smtpSocket, base64_encode($smtpConf['auth_password']));
+ if ( substr($smtpResponse,0,3) != '235' )
+ {
+ $this->error[] = "No 235 after sending password, server says: ".$smtpResponse;
+ $this->sendSmtpQuit($smtpSocket);
+ return false;
+ }
+ }
+
+ //email from
+ $smtpResponse = $this->sendSmtpCommand($smtpSocket, 'MAIL FROM: <'.$conf['mail']['from'].'>');
+ if ( substr($smtpResponse,0,3) != '250' )
+ {
+ $this->error[] = "No 2xx after MAIL_FROM, server says: ".$smtpResponse;
+ $this->sendSmtpQuit($smtpSocket);
+ return false;
+ }
+
+ //email to
+ $smtpResponse = $this->sendSmtpCommand($smtpSocket, 'RCPT TO: <'.$this->to.'>');
+ if ( substr($smtpResponse,0,3) != '250' )
+ {
+ $this->error[] = "No 2xx after RCPT_TO, server says: ".$smtpResponse;
+ $this->sendSmtpQuit($smtpSocket);
+ return false;
+ }
+
+ //the email
+ $smtpResponse = $this->sendSmtpCommand($smtpSocket, "DATA");
+ if ( substr($smtpResponse,0,3) != '354' )
+ {
+ $this->error[] = "No 354 after DATA, server says: ".$smtpResponse;
+ $this->sendSmtpQuit($smtpSocket);
+ return false;
+ }
+
+ $this->header[] = 'To: '.$this->to;
+ $this->header[] = 'Subject: '.$this->subject;
+ $this->header[] = 'Date: '.date('r');
+ $this->header[] = 'Message-Id: '.'<'.getenv('REMOTE_ADDR').'.'.time().'.openrat@'.getenv('SERVER_NAME').'.'.getenv('HOSTNAME').'>';
+
+ //observe the . after the newline, it signals the end of message
+ $smtpResponse = $this->sendSmtpCommand($smtpSocket, implode($this->nl,$this->header).$this->nl.$this->nl.$this->text.$this->nl.'.');
+ if ( substr($smtpResponse,0,3) != '250' )
+ {
+ $this->error[] = "No 2xx after putting DATA, server says: ".$smtpResponse;
+ $this->sendSmtpQuit($smtpSocket);
+ return false;
+ }
+
+ // say goodbye
+ $this->sendSmtpQuit($smtpSocket);
+ return true;
+ }
+ }
+
+
+ /**
+ * Sendet ein SMTP-Kommando zum SMTP-Server.
+ *
+ * @access private
+ * @param Resource $socket TCP/IP-Socket zum SMTP-Server
+ * @param unknown_type $cmd SMTP-Kommando
+ * @return Server-Antwort
+ */
+ function sendSmtpCommand( $socket,$cmd )
+ {
+ if ( $this->debug )
+ $this->error[] = 'CLIENT: >>> '.trim($cmd);
+ if ( !is_resource($socket) )
+ {
+ // Die Verbindung ist geschlossen. Dies kann bei dieser
+ // Implementierung eigentlich nur dann passieren, wenn
+ // der Server die Verbindung schlie�t.
+ // Dieser Client trennt die Verbindung nur nach einem "QUIT".
+ $this->error[] = "Connection lost";
+ return;
+ }
+
+ fputs($socket,$cmd.$this->nl);
+ $response = trim(fgets($socket, 4096));
+ if ( $this->debug )
+ $this->error[] = 'SERVER: <<< '.$response;
+ return $response;
+ }
+
+
+
+ /**
+ * Sendet ein QUIT zum SMTP-Server, wartet die Antwort ab und
+ * schlie�t danach die Verbindung.
+ *
+ * @param Resource Socket
+ */
+ function sendSmtpQuit( $socket )
+ {
+
+ if ( $this->debug )
+ $this->error[] = "CLIENT: >>> QUIT";
+ if ( !is_resource($socket) )
+ return;
+ // Wenn die Verbindung nicht mehr da ist, brauchen wir
+ // auch kein QUIT mehr :)
+
+
+ fputs($socket,'QUIT'.$this->nl);
+ $response = trim(fgets($socket, 4096));
+ if ( $this->debug )
+ $this->error[] = 'SERVER: <<< '.$response;
+
+ if ( substr($response,0,3) != '221' )
+ $this->error[] = 'QUIT FAILED: '.$response;
+
+ fclose($socket);
+ }
+
+
+
+ /**
+ * Umwandlung von 8-bit-Zeichen in MIME-Header gemaess RFC 2047.<br>
+ * Header d�rfen nur 7-bit-Zeichen enthalten. 8-bit-Zeichen m�ssen kodiert werden.
+ *
+ * @param String $text
+ * @return String
+ */
+ function header_encode( $text )
+ {
+ global $conf;
+
+ if ( empty($conf['mail']['header_encoding']) )
+ return $text;
+
+ $woerter = explode(' ',$text);
+ $neu = array();
+
+
+ foreach( $woerter as $wort )
+ {
+ $type = strtolower(substr($conf['mail']['header_encoding'],0,1));
+ $neu_wort = '';
+
+ if ( $type == 'b' )
+ $neu_wort = base64_encode($wort);
+ elseif ( $type == 'q' )
+ $neu_wort = $this->quoted_printable_encode($wort);
+ else
+ Logger::error( 'Mail-Configuratin broken: UNKNOWN Header-Encoding type: '.$type);
+
+ if ( strlen($wort)==strlen($neu_wort) )
+ $neu[] = $wort;
+ else
+ $neu[] = '=?'.lang('CHARSET').'?'.$type.'?'.$neu_wort.'?=';
+ }
+
+ return implode(' ',$neu);
+ }
+
+
+ /**
+ * Ermittelt den MX-Eintrag zu einer E-Mail-Adresse.<br>
+ * Es wird der Eintrag mit der h�chsten Priorit�t ermittelt.
+ *
+ * @param String E-Mail-Adresse des Empf�ngers.
+ * @return MX-Eintrag
+ */
+ function getMxHost( $to )
+ {
+ list($user,$host) = explode('@',$to.'@');
+
+ if ( empty($host) )
+ {
+ $this->error[] = 'Illegal mail address - No hostname found.';
+ return "";
+ }
+
+ list($host) = explode('>',$host);
+
+ $mxHostsName = array();
+ $mxHostsPrio = array();
+ getmxrr($host,$mxHostsName,$mxHostsPrio);
+
+ $mxList = array();
+ foreach( $mxHostsName as $id=>$mxHostName )
+ {
+ $mxList[$mxHostName] = $mxHostsPrio[$id];
+ }
+ asort($mxList);
+ return key($mxList);
+ }
+
+
+
+ /**
+ * Stellt fest, ob die E-Mail-Adresse eine gueltige Syntax besitzt.
+ *
+ * Es wird nur die Syntax geprüft. Ob die Adresse wirklich existiert, steht dadurch noch lange
+ * nicht fest. Dazu müsste man die MX-Records auflösen und einen Zustellversuch unternehmen.
+ *
+ * @param $email_address Adresse
+ * @return true, falls Adresse OK, sonst false
+ */
+ function checkAddress( $email_address )
+ {
+ // Source: de.php.net/ereg
+ return ereg("^[-A-Za-z0-9_]+[-A-Za-z0-9_.]*[@]{1}[-A-Za-z0-9_]+[-A-Za-z0-9_.]*[.]{1}[A-Za-z]{2,5}$", $email_address);
+ }
+
+
+
+ /**
+ * Prüft, ob eine Domain in einer List von Domains enthalten ist.
+ *
+ * @param $checkDomain zu prüfende Domain
+ * @param $domain_list Liste von Domains als kommaseparierte Liste
+ * @return true, falls vorhanden, sonst false
+ */
+ function containsDomain($checkDomain, $domain_list)
+ {
+ $domains = explode(',',$domain_list);
+
+ foreach( $domains as $domain )
+ {
+ $domain = trim($domain);
+
+ if (empty($domain))
+ continue;
+
+ if ($domain == substr($checkDomain,-strlen($domain)))
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+}
+
+
+?>
diff --git a/modules/util/ProjectTree.class.php b/modules/util/ProjectTree.class.php
@@ -0,0 +1,515 @@
+<?php
+use cms\model\Value;
+use cms\model\Element;
+use cms\model\Template;
+use cms\model\Page;
+use cms\model\Folder;
+use cms\model\Object;
+use cms\model\File;
+use cms\model\Link;
+
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+/**
+ * Darstellen der Projektstruktur
+ * @author $Author$
+ * @version $Revision$
+ * @package openrat.services
+ */
+class ProjectTree extends AbstractTree
+{
+ var $projectId;
+ var $userIsProjectAdmin = false;
+
+ function root()
+ {
+ $treeElement = new TreeElement();
+ $treeElement->text = lang('GLOBAL_PROJECT');
+ $treeElement->description = lang('GLOBAL_PROJECT');
+ $treeElement->type = 'project';
+ $treeElement->icon = 'project';
+
+ $this->addTreeElement( $treeElement );
+ }
+
+
+
+ function page( $id )
+ {
+ $page = new Page( $id );
+ $page->load();
+
+ $template = new Template( $page->templateid );
+
+ foreach( $template->getElementIds() as $elementid )
+ {
+ $element = new Element( $elementid );
+ $element->load();
+
+ if ( $element->isWritable() )
+ {
+ $treeElement = new TreeElement();
+ $treeElement->id = $id.'_'.$elementid;
+ $treeElement->extraId['elementid'] = $elementid;
+ $treeElement->text = $element->name;
+ $treeElement->url = Html::url('pageelement','edit',
+ $id.'_'.$elementid,
+ array('elementid'=>$elementid,
+ REQ_PARAM_TARGETSUBACTION=>'edit',REQ_PARAM_TARGET=>'content'));
+ $treeElement->action = 'pageelement';
+ $treeElement->icon = 'el_'.$element->type;
+
+ $treeElement->description = lang('EL_'.$element->type);
+ if ( $element->desc != '' )
+ $treeElement->description .= ' - '.Text::maxLaenge( 25,$element->desc );
+ else
+ $treeElement->description .= ' - '.lang('GLOBAL_NO_DESCRIPTION_AVAILABLE');
+ $treeElement->target = 'content';
+
+ if ( in_array($element->type,array('link','list','include') ) )
+ {
+ $treeElement->type = 'value';
+ $value = new Value();
+ $value->pageid = $page->pageid;
+ $value->element = $element;
+ $value->load();
+ $treeElement->internalId = $value->valueid;
+ }
+
+ $this->addTreeElement( $treeElement );
+ }
+ }
+ }
+
+
+ function value( $id )
+ {
+ //echo "id: $id";
+ if ( $id != 0 )
+ {
+ $value = new Value();
+ $value->loadWithId( $id );
+
+ $objectid = intval($value->linkToObjectId);
+ if ( $objectid != 0 )
+ {
+ $object = new Object( $objectid );
+ $object->load();
+
+ $treeElement = new TreeElement();
+ $treeElement->id = $id;
+ $treeElement->text = $object->name;
+ if ( in_array($object->getType(),array('page','folder')))
+ {
+ $treeElement->type = $object->getType();
+ $treeElement->internalId = $object->objectid;
+ }
+ $treeElement->url = Html::url($object->getType(),'',$objectid,array(REQ_PARAM_TARGET=>'content'));
+ $treeElement->action = $object->getType();
+ $treeElement->icon = $object->getType();
+
+ $treeElement->description = lang('GLOBAL_'.$object->getType());
+ if ( $object->desc != '' )
+ $treeElement->description .= ' - '.Text::maxLaenge( 25,$object->desc );
+ else
+ $treeElement->description .= ' - '.lang('GLOBAL_NO_DESCRIPTION_AVAILABLE');
+ $treeElement->target = 'content';
+
+ $this->addTreeElement( $treeElement );
+ }
+ }
+ }
+
+
+ function link( $id )
+ {
+ $link = new Link( $id );
+ $link->load();
+
+ if ( $link->isLinkToObject )
+ {
+ $o = new Object( $link->linkedObjectId );
+ $o->load();
+
+ $treeElement = new TreeElement();
+ $treeElement->id = $o->objectid;
+ $treeElement->internalId = $o->objectid;
+ $treeElement->target = 'content';
+ $treeElement->text = $o->name;
+ $treeElement->description= lang( 'GLOBAL_'.$o->getType() ).' '.$id;
+
+ if ( $o->desc != '' )
+ $treeElement->description .= ': '.$o->desc;
+ else
+ $treeElement->description .= ' - '.lang('GLOBAL_NO_DESCRIPTION_AVAILABLE');
+
+ $treeElement->url = Html::url($o->getType(),'',$o->objectid,array(REQ_PARAM_TARGET=>'content') );
+ $treeElement->action = $o->getType();
+ $treeElement->icon = $o->getType();
+
+ // Besonderheiten fuer bestimmte Objekttypen
+
+ if ( $o->isPage )
+ {
+ // Nur wenn die Seite beschreibbar ist, werden die
+ // Elemente im Baum angezeigt
+ if ( $o->hasRight( ACL_WRITE ) )
+ $treeElement->type='pageelements';
+ }
+ $this->addTreeElement( $treeElement );
+ }
+ }
+
+
+ /**
+ * Laedt Elemente zu einem Ordner
+ * @return Array
+ */
+ function folder( $id )
+ {
+ global
+ $SESS,
+ $projectid;
+
+ $f = new Folder( $id );
+ $t = time();
+
+ foreach( $f->getObjects() as $o )
+ {
+ // Wenn keine Leseberechtigung
+ if ( !$o->hasRight( ACL_READ ) )
+ continue;
+
+ $treeElement = new TreeElement();
+ $treeElement->id = $o->objectid;
+ $treeElement->internalId = $o->objectid;
+ $treeElement->target = 'content';
+ $treeElement->text = $o->name;
+ $treeElement->description= lang( 'GLOBAL_'.$o->getType() ).' '.$o->objectid;
+
+ if ( $o->desc != '' )
+ $treeElement->description .= ': '.$o->desc;
+ else
+ $treeElement->description .= ' - '.lang('GLOBAL_NO_DESCRIPTION_AVAILABLE');
+
+ $treeElement->url = Html::url( $o->getType(),'',$o->objectid,array('readit'=>'__OID__'.$o->objectid.'__',REQ_PARAM_TARGET=>'content') );
+ $treeElement->action = $o->getType();
+ $treeElement->icon = $o->getType();
+
+ // Besonderheiten fuer bestimmte Objekttypen
+
+ if ( $o->isLink )
+ {
+ $treeElement->type='link';
+ }
+
+ if ( $o->isPage )
+ {
+ // Nur wenn die Seite beschreibbar ist, werden die
+ // Elemente im Baum angezeigt
+ if ( $o->hasRight( ACL_WRITE ) )
+ $treeElement->type='page';
+ }
+
+ if ( $o->isFile )
+ {
+ $file = new File( $o->objectid );
+ $file->load();
+
+ if ( substr($file->mimeType(),0,6) == 'image/' )
+ $treeElement->icon = 'image';
+ else $treeElement->icon = 'file';
+ }
+
+ if ( $o->isFolder )
+ {
+ $treeElement->type = 'folder';
+ }
+
+
+ $this->addTreeElement( $treeElement );
+ }
+ }
+
+
+ function project()
+ {
+ $language = Session::getProjectLanguage();
+ $model = Session::getProjectModel();
+ $user = Session::getUser();
+
+ $project = Session::getProject();
+ $this->projectid = $project->projectid;
+
+ // Hoechster Ordner der Projektstruktur
+ $folder = new Folder( $project->getRootObjectId() );
+ $folder->load();
+
+
+ // Ermitteln, ob der Benutzer Projektadministrator ist
+ // Projektadministratoren haben das Recht, im Root-Ordner die Eigenschaften zu aendern.
+ if ( $folder->hasRight( ACL_PROP ) )
+ $this->userIsProjectAdmin = true;
+
+ if ( $folder->hasRight( ACL_READ ) )
+ {
+ $treeElement = new TreeElement();
+ $treeElement->id = $folder->objectid;
+ // $treeElement->text = $folder->name;
+ $treeElement->text = lang('FOLDER_ROOT');
+ $treeElement->description = lang('FOLDER_ROOT_DESC');
+ $treeElement->icon = 'folder';
+ $treeElement->action = 'folder';
+ $treeElement->url = Html::url( 'folder','',$folder->objectid,array(REQ_PARAM_TARGET=>'content') );
+ $treeElement->target = 'content';
+ $treeElement->type = 'folder';
+ $treeElement->internalId = $folder->objectid;
+ $this->addTreeElement( $treeElement );
+ }
+
+
+ if ( $this->userIsProjectAdmin )
+ {
+ // Templates
+ $treeElement = new TreeElement();
+ $treeElement->id = 0;
+ $treeElement->text = lang('GLOBAL_TEMPLATES');
+ $treeElement->url = Html::url('template','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content'));
+ $treeElement->description= lang('GLOBAL_TEMPLATES_DESC');
+ $treeElement->icon = 'templatelist';
+ $treeElement->action = 'templatelist';
+ $treeElement->target = 'content';
+ $treeElement->type = 'templates';
+ $this->addTreeElement( $treeElement );
+ }
+
+
+ // Sprachen
+ $treeElement = new TreeElement();
+ $treeElement->description= '';
+ $treeElement->id = 0;
+ $treeElement->action = 'languagelist';
+ $treeElement->text = lang('GLOBAL_LANGUAGES');
+ $treeElement->url = Html::url('language','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content'));
+ $treeElement->icon = 'languagelist';
+ $treeElement->description= lang('GLOBAL_LANGUAGES_DESC');
+ $treeElement->target = 'content';
+
+ // Nur fuer Projekt-Administratoren aufklappbar
+ if ( $this->userIsProjectAdmin )
+ $treeElement->type = 'languages';
+
+ $this->addTreeElement( $treeElement );
+
+
+ // Projektmodelle
+ $treeElement = new TreeElement();
+ $treeElement->description= '';
+
+ // Nur fuer Projekt-Administratoren aufklappbar
+ if ( $this->userIsProjectAdmin )
+ $treeElement->type = 'models';
+
+ $treeElement->id = 0;
+ $treeElement->description= lang('GLOBAL_MODELS_DESC');
+ $treeElement->text = lang('GLOBAL_MODELS');
+ $treeElement->url = Html::url('model','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content'));
+ $treeElement->action = 'modellist';
+ $treeElement->icon = 'modellist';
+ $treeElement->target = 'content';
+ $this->addTreeElement( $treeElement );
+
+
+ // Sonstiges
+// $treeElement = new TreeElement();
+// $treeElement->text = lang('GLOBAL_OTHER');
+// $treeElement->description= lang('GLOBAL_OTHER_DESC');
+// $treeElement->icon = 'other';
+// $treeElement->type = 'other';
+// $this->addTreeElement( $treeElement );
+
+ // Suche
+ $treeElement = new TreeElement();
+ $treeElement->id = 0;
+ $treeElement->text = lang('GLOBAL_SEARCH');
+ $treeElement->url = Html::url('search','',0,array(REQ_PARAM_TARGET=>'content'));
+ $treeElement->action = 'search';
+ $treeElement->icon = 'search';
+ $treeElement->description = lang('GLOBAL_SEARCH_DESC');
+ $treeElement->target = 'content';
+ $this->addTreeElement( $treeElement );
+
+ }
+
+
+ function templates()
+ {
+ foreach( Template::getAll() as $id=>$name )
+ {
+ $treeElement = new TreeElement();
+
+ $t = new Template( $id );
+ $t->load();
+ $treeElement->text = $t->name;
+ $treeElement->id = $id;
+ $treeElement->url = Html::url('template','src',$id,array(REQ_PARAM_TARGETSUBACTION=>'src',REQ_PARAM_TARGET=>'content'));
+ $treeElement->icon = 'template';
+ $treeElement->action = 'template';
+ $treeElement->target = 'content';
+ $treeElement->internalId = $id;
+ $treeElement->type = 'template';
+ $treeElement->description = $t->name.' ('.lang('GLOBAL_TEMPLATE').' '.$id.'): '.htmlentities(Text::maxLaenge( 40,$t->src ));
+ $this->addTreeElement( $treeElement );
+ }
+ }
+
+
+ function template( $id )
+ {
+
+ $t = new Template( $id );
+ $t->load();
+
+ // Anzeigen der Template-Elemente
+ //
+ foreach( $t->getElementIds() as $elementid )
+ {
+ $e = new Element( $elementid );
+ $e->load();
+
+ // "Code"-Element nur fuer Administratoren
+ if ( $e->type == 'code' && !$this->userIsAdmin )
+ continue;
+
+ $treeElement = new TreeElement();
+ $treeElement->id = $elementid;
+ $treeElement->text = $e->name;
+ $treeElement->url = Html::url('element','',$elementid,array(REQ_PARAM_TARGET=>'content') );
+ $treeElement->icon = 'el_'.$e->type;
+ $treeElement->action = 'element';
+
+ if ( $e->desc == '' )
+ $desc = lang('GLOBAL_NO_DESCRIPTION_AVAILABLE');
+ else
+ $desc = $e->desc;
+ $treeElement->description = $e->name.' ('.lang('EL_'.$e->type).'): '.Text::maxLaenge( 40,$desc );
+ $treeElement->target = 'content';
+ $this->addTreeElement( $treeElement );
+ }
+ }
+
+
+ /**
+ * Sprachen
+ */
+ function languages()
+ {
+ // Sprachvarianten
+ //
+ $l = Session::getProjectLanguage();
+ $languages = $l->getAll();
+
+ foreach( $languages as $languageid=>$name )
+ {
+ $treeElement = new TreeElement();
+ $treeElement->id = $languageid;
+ $treeElement->text = $name;
+ $treeElement->url = Html::url('language','edit',$languageid,
+ array(REQ_PARAM_TARGETSUBACTION=>'edit',REQ_PARAM_TARGET=>'content') );
+ $treeElement->icon = 'language';
+ $treeElement->action = 'language';
+ $treeElement->description = '';
+ $treeElement->target = 'content';
+ $this->addTreeElement( $treeElement );
+ }
+ }
+
+
+ // Projektvarianten
+ //
+ function models()
+ {
+ $m = Session::getProjectModel();
+ $models = $m->getAll();
+
+ foreach( $models as $id=>$name )
+ {
+ $treeElement = new TreeElement();
+ $treeElement->id = $id;
+ $treeElement->text = $name;
+ $treeElement->url = Html::url('model','edit',$id,
+ array(REQ_PARAM_TARGETSUBACTION=>'edit',REQ_PARAM_TARGET=>'content'));
+ $treeElement->action = 'model';
+ $treeElement->icon = 'model';
+ $treeElement->description = '';
+ $treeElement->target = 'content';
+ $this->addTreeElement( $treeElement );
+ }
+ }
+
+
+ function other()
+ {
+// Deaktiviert, da
+// - Dateien auf den Server laden unverst�ndlich/undurchsichtig erscheint
+// - M�glichkeit zum Entpacken von ZIP/TAR online besteht.
+// if ( $this->userIsProjectAdmin )
+// {
+// $treeElement = new TreeElement();
+// $treeElement->text = lang('GLOBAL_FILE_TRANSFER');
+// $treeElement->description = lang('GLOBAL_FILE_TRANSFER_DESC');
+// $treeElement->url = Html::url('main','transfer');
+// $treeElement->icon = 'transfer';
+// $treeElement->target = 'content';
+// $this->addTreeElement( $treeElement );
+// }
+
+ $treeElement = new TreeElement();
+ $treeElement->id = 0;
+ $treeElement->text = lang('GLOBAL_SEARCH');
+ $treeElement->url = Html::url('search');
+ $treeElement->icon = 'search';
+ $treeElement->action = 'search';
+ $treeElement->description = lang('GLOBAL_SEARCH_DESC');
+ $treeElement->target = 'content';
+ $this->addTreeElement( $treeElement );
+
+
+ $treeElement = new TreeElement();
+ $treeElement->id = 0;
+ $treeElement->text = lang('USER_YOURPROFILE');
+ $treeElement->url = Html::url('profile','edit',0,array(REQ_PARAM_TARGET=>'content'));
+ $treeElement->icon = 'user';
+ $treeElement->action = 'profile';
+ $treeElement->description = lang('USER_PROFILE_DESC');
+ $treeElement->target = 'content';
+ $this->addTreeElement( $treeElement );
+
+
+ $treeElement = new TreeElement();
+ $treeElement->id = 0;
+ $treeElement->text = lang('GLOBAL_PROJECTS');
+ $treeElement->url = Html::url('index','projectmenu',0,array(REQ_PARAM_TARGET=>'content'));
+ $treeElement->icon = 'project';
+ $treeElement->description = lang('GLOBAL_PROJECTS');
+ $treeElement->target = 'content';
+ $this->addTreeElement( $treeElement );
+ }
+}
+
+?>+
\ No newline at end of file
diff --git a/modules/util/Publish.class.php b/modules/util/Publish.class.php
@@ -0,0 +1,393 @@
+<?php
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+/**
+ * Diese Klasse kapselt das Veroeffentlichen von Dateien.<br>
+ * <br>
+ * Hier werden<br>
+ * - Dateien in das Zielverzeichnis kopiert<br>
+ * - Dateien per FTP veroeffentlicht<br>
+ * - Zielverzeichnisse aufgeraeumt<br>
+ * - Systembefehle ausgefuehrt.
+ *
+ * @author Jan Dankert
+ * @package openrat.services
+ */
+class Publish
+{
+ /**
+ * Enthaelt bei Bedarf das FTP-Objekt. N�mlich dann, wenn
+ * zu einem FTP-Server veroeffentlicht werden soll.
+ * @var Object
+ */
+ var $ftp;
+
+ /**
+ * Flag, ob in das lokale Dateisystem veroeffentlicht werden soll.
+ * @var boolean
+ */
+ var $with_local = false;
+
+ /**
+ * Flag, ob zu einem FTP-Server ver�ffentlicht werden soll.
+ * @var boolean
+ */
+ var $with_ftp = false;
+
+ var $local_destdir = '';
+
+ /**
+ * Enthaelt die gleichnamige Einstellung aus dem Projekt.
+ * @var boolean
+ */
+ var $content_negotiation = false;
+
+ /**
+ * Enthaelt die gleichnamige Einstellung aus dem Projekt.
+ * @var boolean
+ */
+ var $cut_index = false;
+
+ /**
+ * Enthaelt die gleichnamige Einstellung aus dem Projekt.
+ * @var String
+ */
+ var $cmd_after_publish = '';
+
+ /**
+ * Enthaelt am Ende der Ver�ffentlichung ein Array mit den ver�ffentlichten Objekten.
+ * @var Array
+ */
+ var $publishedObjects = array();
+
+ /**
+ * Enthaelt im Fehlerfall (wenn 'ok' auf 'false' steht) eine
+ * Fehlermeldung.
+ *
+ * @var String
+ */
+ var $log = array();
+
+ /**
+ * Stellt nach der Ver�ffentlichung fest, ob der Vorgang erfolgreich ist.
+ * Falls nicht, enth�lt die Variable 'log' eine Fehlermeldung.
+ * @var boolean
+ */
+ var $ok = true;
+
+ /**
+ * Konstruktor.<br>
+ * <br>
+ * Oeffnet ggf. Verbindungen.
+ *
+ * @return Publish
+ */
+ function __construct()
+ {
+ global $conf;
+ $confPublish = $conf['publish'];
+
+ if ( $conf['security']['nopublish'] )
+ {
+ $this->ok = false;
+ $this->log[] = 'publishing is disabled.';
+ return;
+ }
+
+ $project = Session::getProject();
+
+ // Feststellen, ob FTP benutzt wird.
+ // Dazu muss FTP aktiviert sein (enable=true) und eine URL vorhanden sein.
+ $ftpUrl = '';
+ if ( $conf['publish']['ftp']['enable'] )
+ {
+ if ( $conf['publish']['ftp']['per_project'] && !empty($project->ftp_url) )
+ $ftpUrl = $project->ftp_url;
+ elseif ( !empty($conf['publish']['ftp']['host']) )
+ $ftpUrl = $project->ftp_url;
+ }
+
+ if ( ! empty($ftpUrl) )
+ {
+ $this->with_ftp = true;
+ $this->ftp = new Ftp( $project->ftp_url ); // Aufbauen einer FTP-Verbindung
+
+ if ( ! $this->ftp->ok ) // FTP-Verbindung ok?
+ {
+ $this->ok = false;
+ $this->log = $this->ftp->log;
+ return; // Ende. Ohne FTP brauchen wir nicht weitermachen.
+ }
+
+ $this->ftp->passive = ( $project->ftp_passive == '1' );
+ }
+
+ $localDir = rtrim( $project->target_dir,'/' );
+
+ if ( $confPublish['filesystem']['per_project'] && (!empty($localDir)) )
+ {
+ $this->local_destdir = $localDir; // Projekteinstellung verwenden.
+ }
+ else
+ {
+ if ( empty( $localDir))
+ $localDir = $project->name;
+ // Konfiguriertes Verzeichnis verwenden.
+ $this->local_destdir = $confPublish['filesystem']['directory'].$localDir;
+ }
+
+
+ // Sofort pruefen, ob das Zielverzeichnis ueberhaupt beschreibbar ist.
+ if ( $this->local_destdir != '' )
+ {
+ if ( !is_writeable( $this->local_destdir ) )
+ {
+ $this->ok = false;
+ $this->log[] = 'directory not writable: '.$this->local_destdir;
+ $this->log[] = 'please correct the file permissions.';
+ return;
+ }
+
+ $this->with_local = true;
+ }
+
+ $this->content_negotiation = ( $project->content_negotiation == '1' );
+ $this->cut_index = ( $project->cut_index == '1' );
+
+ if ( $confPublish['command']['enable'] )
+ {
+ if ( $confPublish['command']['per_project'] && !empty($project->cmd_after_publish) )
+ $this->cmd_after_publish = $project->cmd_after_publish;
+ else
+ $this->cmd_after_publish = @$confPublish['command']['command'];
+ }
+
+ // Im Systemkommando Variablen ersetzen
+ $this->cmd_after_publish = str_replace('{name}' ,$project->name ,$this->cmd_after_publish);
+ $this->cmd_after_publish = str_replace('{dir}' ,$this->local_destdir ,$this->cmd_after_publish);
+ $this->cmd_after_publish = str_replace('{dirbase}',basename($this->local_destdir),$this->cmd_after_publish);
+ }
+
+
+
+ /**
+ * Kopieren einer Datei aus dem tempor�ren Verzeichnis in das Zielverzeichnis.<br>
+ * Falls notwenig, wird ein Hochladen per FTP ausgef�hrt.
+ *
+ * @param String $tmp_filename
+ * @param String $dest_filename
+ */
+ function copy( $tmp_filename,$dest_filename,$lastChangeDate=null )
+ {
+ if ( !$this->ok)
+ return;
+
+ global $conf;
+ $source = $tmp_filename;
+
+ if ( $this->with_local )
+ {
+ $dest = $this->local_destdir.'/'.$dest_filename;
+
+ if (!@copy( $source,$dest ));
+ {
+ if ( ! $this->mkdirs( dirname($dest) ) )
+ return; // Fehler bei Verzeichniserstellung, also abbrechen.
+
+ if (!@copy( $source,$dest ))
+ {
+ $this->ok = false;
+ $this->log[] = 'failed copying local file:';
+ $this->log[] = 'source : '.$source;
+ $this->log[] = 'destination: '.$dest;
+ return; // Fehler beim Kopieren, also abbrechen.
+ }
+ if ( ! is_null($lastChangeDate) )
+ @touch( $dest,$lastChangeDate );
+
+ Logger::debug("published: $dest");
+ }
+
+ if (!empty($conf['security']['chmod']))
+ {
+ // CHMOD auf der Datei ausfuehren.
+ if ( ! @chmod($dest,octdec($conf['security']['chmod'])) )
+ {
+ $this->ok = false;
+ $this->log[] = 'Unable to CHMOD file '.$dest;
+ return;
+ }
+ }
+ }
+
+ if ( $this->with_ftp ) // Falls FTP aktiviert
+ {
+ $dest = $dest_filename;
+ $this->ftp->put( $source,$dest );
+
+ if ( ! $this->ftp->ok )
+ {
+ $this->ok = false;
+ $this->log[] = $this->ftp->log;
+ }
+ }
+ }
+
+
+
+ /**
+ * Rekursives Anlagen von Verzeichnisse
+ * Nett gemacht.
+ * Quelle: http://de3.php.net/manual/de/function.mkdir.php
+ * Thx to acroyear at io dot com
+ *
+ * @param String Verzeichnis
+ * @return boolean
+ */
+ function mkdirs( $strPath )
+ {
+ global $conf;
+
+ if ( is_dir($strPath) )
+ return true;
+
+ $pStrPath = dirname($strPath);
+ if ( !$this->mkdirs($pStrPath) )
+ return false;
+
+ if ( ! @mkdir($strPath,0777) )
+ {
+ $this->ok = false;
+ $this->log[] = 'Cannot create directory: '.$strPath;
+ return false;
+ }
+
+ // CHMOD auf dem Verzeichnis ausgef�hren.
+ if (!empty($conf['security']['chmod_dir']))
+ {
+ if ( ! @chmod($strPath,octdec($conf['security']['chmod_dir'])) )
+ {
+ $this->ok = false;
+ $this->log[] = 'Unable to CHMOD directory: '.$strPath;
+ return false;
+ }
+ }
+
+
+ return $this->ok;
+ }
+
+
+
+ /**
+ * Beenden des Ver�ffentlichungs-Vorganges.<br>
+ * Eine vorhandene FTP-Verbindung wird geschlossen.<br>
+ * Falls entsprechend konfiguriert, wird ein Systemkommando ausgef�hrt.
+ */
+ public function close()
+ {
+ if ( $this->with_ftp )
+ {
+ Logger::debug('Closing FTP connection' );
+ $this->ftp->close();
+ }
+
+ // Ausfuehren des Systemkommandos.
+ if ( !empty($this->cmd_after_publish) && $this->ok )
+ {
+ $ausgabe = array();
+ $rc = false;
+ Logger::debug('Executing system command: '.$this->cmd_after_publish );
+ $user = Session::getUser();
+ putenv("CMS_USER_NAME=".$user->name );
+ putenv("CMS_USER_ID=" .$user->userid);
+ putenv("CMS_USER_MAIL=".$user->mail );
+ exec( $this->cmd_after_publish,$ausgabe,$rc );
+
+ if ( $rc != 0 ) // Wenn Returncode ungleich 0, dann Ausgabe ins Log schreiben und Fehler melden.
+ {
+ $this->log = $ausgabe;
+ $this->log[] = 'OpenRat: System command failed - returncode is '.$rc;
+ $this->ok = false;
+
+ Logger::warn('System command '.$this->cmd_after_publish.' failed with status '.$rc );
+
+ }
+ else
+ {
+ Logger::debug('System command successful' );
+ }
+
+ }
+ }
+
+
+
+ /**
+ * Aufraeumen des Zielverzeichnisses.<br><br>
+ * Es wird der komplette Zielordner samt Unterverzeichnissen durchsucht. Jede
+ * Datei, die laenger existiert als der aktuelle Request alt ist, wird geloescht.<br>
+ * Natuerlich darf diese Funktion nur nach einem Gesamt-Veroeffentlichen ausgefuehrt werden.
+ */
+ function clean()
+ {
+ if ( $this->ok )
+ return;
+
+ if ( !empty($this->local_destdir) )
+ $this->cleanFolder($this->local_destdir);
+ }
+
+
+
+ /**
+ * Aufr�umen eines Verzeichnisses.<br><br>
+ * Dateien, die l�nger existieren als der aktuelle Request alt ist, werden gel�scht.<br>
+ *
+ * @param String Verzeichnis
+ */
+ function cleanFolder( $folderName )
+ {
+ $dh = opendir( $folderName );
+
+ while( $file = readdir($dh) )
+ {
+ if ( $file != '.' && $file != '..')
+ {
+ $fullpath = $folderName.'/'.$file;
+
+ // Wenn eine Datei beschreibbar und entsprechend alt
+ // ist, dann entfernen
+ if ( is_file($fullpath) &&
+ is_writable($fullpath) &&
+ filemtime($fullpath) < START_TIME )
+ unlink($fullpath);
+
+ // Bei Ordnern rekursiv absteigen
+ if ( is_dir( $fullpath) )
+ {
+ $this->cleanFolder($fullpath);
+ @rmdir($fullpath);
+ }
+ }
+ }
+ }
+
+}
+
+?>+
\ No newline at end of file
diff --git a/modules/util/Session.class.php b/modules/util/Session.class.php
@@ -0,0 +1,242 @@
+<?php
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+// PHP-Versionsunabhaengiges Array fuer die Session-Variablen ermitteln
+use cms\model\User;
+
+if (isset($_SESSION))
+ $SESS = &$_SESSION;
+else $SESS = &$HTTP_SESSION_VARS;
+
+if ( isset($_FILES) )
+ $FILES = &$_FILES;
+else $FILES = &$HTTP_POST_FILES;
+
+
+/**
+ * Session-Funktionen zum Lesen/Schreiben in/von HTTP-Session
+ * In der Session werden folgende Daten abgelegt
+ * - Ausgewaehltes Projekt
+ * - Ausgewaehlte Projectsprache
+ * - Ausgewaehlte Projektvariante
+ * - Angemeldeter Benutzer
+ * - Auswahlbaum
+ * - Geladene Sprachelemente
+ * - Ausgewaehlter Ordner
+ * - Ausgewaehltes Objekt
+ * - Datenbankobjekt
+ * Die Methoden dieser Klassen koennen statisch aufgerufen werden
+ *
+ * @author $Author$
+ * @version $Revision$
+ * @package openrat.service
+ */
+
+class Session
+{
+ public static function get( $var )
+ {
+ global $SESS;
+ if ( isset($SESS['ors_'.$var]) )
+ return $SESS['ors_'.$var];
+ else
+ return '';
+ }
+
+ public static function set( $var,$value )
+ {
+ global $SESS;
+ $SESS[ 'ors_'.$var ] = $value;
+ }
+
+
+ /**
+ * @return array
+ */
+ public static function getConfig()
+ {
+ return Session::get('config');
+ }
+
+ public static function setConfig( $var )
+ {
+ Session::set('config',$var);
+ }
+
+
+ /**
+ * @return \cms\model\Model
+ */
+ public static function getProjectModel()
+ {
+ return Session::get('project_model');
+ }
+
+ public static function setProjectModel( $var )
+ {
+ Session::set('project_model',$var);
+ }
+
+
+ /**
+ * @return \cms\model\Language
+ */
+ public static function getProjectLanguage()
+ {
+ return Session::get('project_language');
+ }
+
+ public static function setProjectLanguage( $var )
+ {
+ Session::set('project_language',$var);
+ }
+
+
+
+ public static function getObject()
+ {
+ return Session::get('object');
+ }
+
+ public static function setObject( $var )
+ {
+ Session::set('object',$var);
+ }
+
+
+ /**
+ * @return \cms\model\Folder
+ */
+ public static function getFolder()
+ {
+ return Session::get('folder');
+ }
+
+ public static function setFolder( $var )
+ {
+ Session::set('folder',$var);
+ }
+
+
+
+ public static function getTree()
+ {
+ return Session::get('tree');
+ }
+
+ public static function setTree( $var )
+ {
+ Session::set('tree',$var);
+ }
+
+
+
+ public static function getElement()
+ {
+ return Session::get('element');
+ }
+
+ public static function setElement( $var )
+ {
+ Session::set('element',$var);
+ }
+
+
+ /**
+ * @return \cms\model\Project
+ */
+ public static function getProject()
+ {
+ return Session::get('project');
+ }
+
+ public static function setProject( $var )
+ {
+ Session::set('project',$var);
+ }
+
+
+ /**
+ * @return User
+ */
+ public static function getUser()
+ {
+ return Session::get('userObject');
+ }
+
+ public static function setUser( $var )
+ {
+ Session::set('userObject',$var);
+ }
+
+
+ /**
+ * @return \database\Database
+ */
+ public static function getDatabase()
+ {
+ return Session::get('database');
+ }
+
+ public static function setDatabase( $var )
+ {
+ Session::set('database',$var);
+ }
+
+
+ /**
+ * @return string
+ */
+ public static function getSubaction()
+ {
+ return Session::get('subaction');
+ }
+
+ public static function setSubaction( $var )
+ {
+ Session::set('subaction',$var);
+ }
+
+
+ public static function getClipboard()
+ {
+ return Session::get('clipboard');
+ }
+
+ public static function setClipboard( $var )
+ {
+ Session::set('clipboard',$var);
+ }
+
+
+ /**
+ * Schliesst die aktuelle Session
+ *
+ * Diese Funktion sollte so schnell wie moeglich aufgerufen werden, da vorher
+ * keine andere Seite (im Frameset!) geladen werden kann
+ * Nach Aufruf dieser Methode sind keine Session-Zugriffe ueber diese Klasse mehr
+ * moeglich.
+ */
+ public static function close()
+ {
+ session_write_close();
+ }
+}
+
+?>+
\ No newline at end of file
diff --git a/modules/util/Spyc.class.php b/modules/util/Spyc.class.php
@@ -0,0 +1,1161 @@
+<?php
+/**
+ * Spyc -- A Simple PHP YAML Class
+ * @version 0.6.2
+ * @author Vlad Andersen <vlad.andersen@gmail.com>
+ * @author Chris Wanstrath <chris@ozmm.org>
+ * @link https://github.com/mustangostang/spyc/
+ * @copyright Copyright 2005-2006 Chris Wanstrath, 2006-2011 Vlad Andersen
+ * @license http://www.opensource.org/licenses/mit-license.php MIT License
+ * @package Spyc
+ */
+
+if (!function_exists('spyc_load')) {
+ /**
+ * Parses YAML to array.
+ * @param string $string YAML string.
+ * @return array
+ */
+ function spyc_load ($string) {
+ return Spyc::YAMLLoadString($string);
+ }
+}
+
+if (!function_exists('spyc_load_file')) {
+ /**
+ * Parses YAML to array.
+ * @param string $file Path to YAML file.
+ * @return array
+ */
+ function spyc_load_file ($file) {
+ return Spyc::YAMLLoad($file);
+ }
+}
+
+if (!function_exists('spyc_dump')) {
+ /**
+ * Dumps array to YAML.
+ * @param array $data Array.
+ * @return string
+ */
+ function spyc_dump ($data) {
+ return Spyc::YAMLDump($data, false, false, true);
+ }
+}
+
+if (!class_exists('Spyc')) {
+
+/**
+ * The Simple PHP YAML Class.
+ *
+ * This class can be used to read a YAML file and convert its contents
+ * into a PHP array. It currently supports a very limited subsection of
+ * the YAML spec.
+ *
+ * Usage:
+ * <code>
+ * $Spyc = new Spyc;
+ * $array = $Spyc->load($file);
+ * </code>
+ * or:
+ * <code>
+ * $array = Spyc::YAMLLoad($file);
+ * </code>
+ * or:
+ * <code>
+ * $array = spyc_load_file($file);
+ * </code>
+ * @package Spyc
+ */
+class Spyc {
+
+ // SETTINGS
+
+ const REMPTY = "\0\0\0\0\0";
+
+ /**
+ * Setting this to true will force YAMLDump to enclose any string value in
+ * quotes. False by default.
+ *
+ * @var bool
+ */
+ public $setting_dump_force_quotes = false;
+
+ /**
+ * Setting this to true will forse YAMLLoad to use syck_load function when
+ * possible. False by default.
+ * @var bool
+ */
+ public $setting_use_syck_is_possible = false;
+
+
+
+ /**#@+
+ * @access private
+ * @var mixed
+ */
+ private $_dumpIndent;
+ private $_dumpWordWrap;
+ private $_containsGroupAnchor = false;
+ private $_containsGroupAlias = false;
+ private $path;
+ private $result;
+ private $LiteralPlaceHolder = '___YAML_Literal_Block___';
+ private $SavedGroups = array();
+ private $indent;
+ /**
+ * Path modifier that should be applied after adding current element.
+ * @var array
+ */
+ private $delayedPath = array();
+
+ /**#@+
+ * @access public
+ * @var mixed
+ */
+ public $_nodeId;
+
+/**
+ * Load a valid YAML string to Spyc.
+ * @param string $input
+ * @return array
+ */
+ public function load ($input) {
+ return $this->_loadString($input);
+ }
+
+ /**
+ * Load a valid YAML file to Spyc.
+ * @param string $file
+ * @return array
+ */
+ public function loadFile ($file) {
+ return $this->_load($file);
+ }
+
+ /**
+ * Load YAML into a PHP array statically
+ *
+ * The load method, when supplied with a YAML stream (string or file),
+ * will do its best to convert YAML in a file into a PHP array. Pretty
+ * simple.
+ * Usage:
+ * <code>
+ * $array = Spyc::YAMLLoad('lucky.yaml');
+ * print_r($array);
+ * </code>
+ * @access public
+ * @return array
+ * @param string $input Path of YAML file or string containing YAML
+ */
+ public static function YAMLLoad($input) {
+ $Spyc = new Spyc;
+ return $Spyc->_load($input);
+ }
+
+ /**
+ * Load a string of YAML into a PHP array statically
+ *
+ * The load method, when supplied with a YAML string, will do its best
+ * to convert YAML in a string into a PHP array. Pretty simple.
+ *
+ * Note: use this function if you don't want files from the file system
+ * loaded and processed as YAML. This is of interest to people concerned
+ * about security whose input is from a string.
+ *
+ * Usage:
+ * <code>
+ * $array = Spyc::YAMLLoadString("---\n0: hello world\n");
+ * print_r($array);
+ * </code>
+ * @access public
+ * @return array
+ * @param string $input String containing YAML
+ */
+ public static function YAMLLoadString($input) {
+ $Spyc = new Spyc;
+ return $Spyc->_loadString($input);
+ }
+
+ /**
+ * Dump YAML from PHP array statically
+ *
+ * The dump method, when supplied with an array, will do its best
+ * to convert the array into friendly YAML. Pretty simple. Feel free to
+ * save the returned string as nothing.yaml and pass it around.
+ *
+ * Oh, and you can decide how big the indent is and what the wordwrap
+ * for folding is. Pretty cool -- just pass in 'false' for either if
+ * you want to use the default.
+ *
+ * Indent's default is 2 spaces, wordwrap's default is 40 characters. And
+ * you can turn off wordwrap by passing in 0.
+ *
+ * @access public
+ * @return string
+ * @param array|\stdClass $array PHP array
+ * @param int $indent Pass in false to use the default, which is 2
+ * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
+ * @param bool $no_opening_dashes Do not start YAML file with "---\n"
+ */
+ public static function YAMLDump($array, $indent = false, $wordwrap = false, $no_opening_dashes = false) {
+ $spyc = new Spyc;
+ return $spyc->dump($array, $indent, $wordwrap, $no_opening_dashes);
+ }
+
+
+ /**
+ * Dump PHP array to YAML
+ *
+ * The dump method, when supplied with an array, will do its best
+ * to convert the array into friendly YAML. Pretty simple. Feel free to
+ * save the returned string as tasteful.yaml and pass it around.
+ *
+ * Oh, and you can decide how big the indent is and what the wordwrap
+ * for folding is. Pretty cool -- just pass in 'false' for either if
+ * you want to use the default.
+ *
+ * Indent's default is 2 spaces, wordwrap's default is 40 characters. And
+ * you can turn off wordwrap by passing in 0.
+ *
+ * @access public
+ * @return string
+ * @param array $array PHP array
+ * @param int $indent Pass in false to use the default, which is 2
+ * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
+ */
+ public function dump($array,$indent = false,$wordwrap = false, $no_opening_dashes = false) {
+ // Dumps to some very clean YAML. We'll have to add some more features
+ // and options soon. And better support for folding.
+
+ // New features and options.
+ if ($indent === false or !is_numeric($indent)) {
+ $this->_dumpIndent = 2;
+ } else {
+ $this->_dumpIndent = $indent;
+ }
+
+ if ($wordwrap === false or !is_numeric($wordwrap)) {
+ $this->_dumpWordWrap = 40;
+ } else {
+ $this->_dumpWordWrap = $wordwrap;
+ }
+
+ // New YAML document
+ $string = "";
+ if (!$no_opening_dashes) $string = "---\n";
+
+ // Start at the base of the array and move through it.
+ if ($array) {
+ $array = (array)$array;
+ $previous_key = -1;
+ foreach ($array as $key => $value) {
+ if (!isset($first_key)) $first_key = $key;
+ $string .= $this->_yamlize($key,$value,0,$previous_key, $first_key, $array);
+ $previous_key = $key;
+ }
+ }
+ return $string;
+ }
+
+ /**
+ * Attempts to convert a key / value array item to YAML
+ * @access private
+ * @return string
+ * @param $key The name of the key
+ * @param $value The value of the item
+ * @param $indent The indent of the current node
+ */
+ private function _yamlize($key,$value,$indent, $previous_key = -1, $first_key = 0, $source_array = null) {
+ if(is_object($value)) $value = (array)$value;
+ if (is_array($value)) {
+ if (empty ($value))
+ return $this->_dumpNode($key, array(), $indent, $previous_key, $first_key, $source_array);
+ // It has children. What to do?
+ // Make it the right kind of item
+ $string = $this->_dumpNode($key, self::REMPTY, $indent, $previous_key, $first_key, $source_array);
+ // Add the indent
+ $indent += $this->_dumpIndent;
+ // Yamlize the array
+ $string .= $this->_yamlizeArray($value,$indent);
+ } elseif (!is_array($value)) {
+ // It doesn't have children. Yip.
+ $string = $this->_dumpNode($key, $value, $indent, $previous_key, $first_key, $source_array);
+ }
+ return $string;
+ }
+
+ /**
+ * Attempts to convert an array to YAML
+ * @access private
+ * @return string
+ * @param $array The array you want to convert
+ * @param $indent The indent of the current level
+ */
+ private function _yamlizeArray($array,$indent) {
+ if (is_array($array)) {
+ $string = '';
+ $previous_key = -1;
+ foreach ($array as $key => $value) {
+ if (!isset($first_key)) $first_key = $key;
+ $string .= $this->_yamlize($key, $value, $indent, $previous_key, $first_key, $array);
+ $previous_key = $key;
+ }
+ return $string;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Returns YAML from a key and a value
+ * @access private
+ * @return string
+ * @param $key The name of the key
+ * @param $value The value of the item
+ * @param $indent The indent of the current node
+ */
+ private function _dumpNode($key, $value, $indent, $previous_key = -1, $first_key = 0, $source_array = null) {
+ // do some folding here, for blocks
+ if (is_string ($value) && ((strpos($value,"\n") !== false || strpos($value,": ") !== false || strpos($value,"- ") !== false ||
+ strpos($value,"*") !== false || strpos($value,"#") !== false || strpos($value,"<") !== false || strpos($value,">") !== false || strpos ($value, '%') !== false || strpos ($value, ' ') !== false ||
+ strpos($value,"[") !== false || strpos($value,"]") !== false || strpos($value,"{") !== false || strpos($value,"}") !== false) || strpos($value,"&") !== false || strpos($value, "'") !== false || strpos($value, "!") === 0 ||
+ substr ($value, -1, 1) == ':')
+ ) {
+ $value = $this->_doLiteralBlock($value,$indent);
+ } else {
+ $value = $this->_doFolding($value,$indent);
+ }
+
+ if ($value === array()) $value = '[ ]';
+ if ($value === "") $value = '""';
+ if (self::isTranslationWord($value)) {
+ $value = $this->_doLiteralBlock($value, $indent);
+ }
+ if (trim ($value) != $value)
+ $value = $this->_doLiteralBlock($value,$indent);
+
+ if (is_bool($value)) {
+ $value = $value ? "true" : "false";
+ }
+
+ if ($value === null) $value = 'null';
+ if ($value === "'" . self::REMPTY . "'") $value = null;
+
+ $spaces = str_repeat(' ',$indent);
+
+ //if (is_int($key) && $key - 1 == $previous_key && $first_key===0) {
+ if (is_array ($source_array) && array_keys($source_array) === range(0, count($source_array) - 1)) {
+ // It's a sequence
+ $string = $spaces.'- '.$value."\n";
+ } else {
+ // if ($first_key===0) throw new Exception('Keys are all screwy. The first one was zero, now it\'s "'. $key .'"');
+ // It's mapped
+ if (strpos($key, ":") !== false || strpos($key, "#") !== false) { $key = '"' . $key . '"'; }
+ $string = rtrim ($spaces.$key.': '.$value)."\n";
+ }
+ return $string;
+ }
+
+ /**
+ * Creates a literal block for dumping
+ * @access private
+ * @return string
+ * @param $value
+ * @param $indent int The value of the indent
+ */
+ private function _doLiteralBlock($value,$indent) {
+ if ($value === "\n") return '\n';
+ if (strpos($value, "\n") === false && strpos($value, "'") === false) {
+ return sprintf ("'%s'", $value);
+ }
+ if (strpos($value, "\n") === false && strpos($value, '"') === false) {
+ return sprintf ('"%s"', $value);
+ }
+ $exploded = explode("\n",$value);
+ $newValue = '|';
+ if (isset($exploded[0]) && ($exploded[0] == "|" || $exploded[0] == "|-" || $exploded[0] == ">")) {
+ $newValue = $exploded[0];
+ unset($exploded[0]);
+ }
+ $indent += $this->_dumpIndent;
+ $spaces = str_repeat(' ',$indent);
+ foreach ($exploded as $line) {
+ $line = trim($line);
+ if (strpos($line, '"') === 0 && strrpos($line, '"') == (strlen($line)-1) || strpos($line, "'") === 0 && strrpos($line, "'") == (strlen($line)-1)) {
+ $line = substr($line, 1, -1);
+ }
+ $newValue .= "\n" . $spaces . ($line);
+ }
+ return $newValue;
+ }
+
+ /**
+ * Folds a string of text, if necessary
+ * @access private
+ * @return string
+ * @param $value The string you wish to fold
+ */
+ private function _doFolding($value,$indent) {
+ // Don't do anything if wordwrap is set to 0
+
+ if ($this->_dumpWordWrap !== 0 && is_string ($value) && strlen($value) > $this->_dumpWordWrap) {
+ $indent += $this->_dumpIndent;
+ $indent = str_repeat(' ',$indent);
+ $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent");
+ $value = ">\n".$indent.$wrapped;
+ } else {
+ if ($this->setting_dump_force_quotes && is_string ($value) && $value !== self::REMPTY)
+ $value = '"' . $value . '"';
+ if (is_numeric($value) && is_string($value))
+ $value = '"' . $value . '"';
+ }
+
+
+ return $value;
+ }
+
+ private function isTrueWord($value) {
+ $words = self::getTranslations(array('true', 'on', 'yes', 'y'));
+ return in_array($value, $words, true);
+ }
+
+ private function isFalseWord($value) {
+ $words = self::getTranslations(array('false', 'off', 'no', 'n'));
+ return in_array($value, $words, true);
+ }
+
+ private function isNullWord($value) {
+ $words = self::getTranslations(array('null', '~'));
+ return in_array($value, $words, true);
+ }
+
+ private function isTranslationWord($value) {
+ return (
+ self::isTrueWord($value) ||
+ self::isFalseWord($value) ||
+ self::isNullWord($value)
+ );
+ }
+
+ /**
+ * Coerce a string into a native type
+ * Reference: http://yaml.org/type/bool.html
+ * TODO: Use only words from the YAML spec.
+ * @access private
+ * @param $value The value to coerce
+ */
+ private function coerceValue(&$value) {
+ if (self::isTrueWord($value)) {
+ $value = true;
+ } else if (self::isFalseWord($value)) {
+ $value = false;
+ } else if (self::isNullWord($value)) {
+ $value = null;
+ }
+ }
+
+ /**
+ * Given a set of words, perform the appropriate translations on them to
+ * match the YAML 1.1 specification for type coercing.
+ * @param $words The words to translate
+ * @access private
+ */
+ private static function getTranslations(array $words) {
+ $result = array();
+ foreach ($words as $i) {
+ $result = array_merge($result, array(ucfirst($i), strtoupper($i), strtolower($i)));
+ }
+ return $result;
+ }
+
+// LOADING FUNCTIONS
+
+ private function _load($input) {
+ $Source = $this->loadFromSource($input);
+ return $this->loadWithSource($Source);
+ }
+
+ private function _loadString($input) {
+ $Source = $this->loadFromString($input);
+ return $this->loadWithSource($Source);
+ }
+
+ private function loadWithSource($Source) {
+ if (empty ($Source)) return array();
+ if ($this->setting_use_syck_is_possible && function_exists ('syck_load')) {
+ $array = syck_load (implode ("\n", $Source));
+ return is_array($array) ? $array : array();
+ }
+
+ $this->path = array();
+ $this->result = array();
+
+ $cnt = count($Source);
+ for ($i = 0; $i < $cnt; $i++) {
+ $line = $Source[$i];
+
+ $this->indent = strlen($line) - strlen(ltrim($line));
+ $tempPath = $this->getParentPathByIndent($this->indent);
+ $line = self::stripIndent($line, $this->indent);
+ if (self::isComment($line)) continue;
+ if (self::isEmpty($line)) continue;
+ $this->path = $tempPath;
+
+ $literalBlockStyle = self::startsLiteralBlock($line);
+ if ($literalBlockStyle) {
+ $line = rtrim ($line, $literalBlockStyle . " \n");
+ $literalBlock = '';
+ $line .= ' '.$this->LiteralPlaceHolder;
+ $literal_block_indent = strlen($Source[$i+1]) - strlen(ltrim($Source[$i+1]));
+ while (++$i < $cnt && $this->literalBlockContinues($Source[$i], $this->indent)) {
+ $literalBlock = $this->addLiteralLine($literalBlock, $Source[$i], $literalBlockStyle, $literal_block_indent);
+ }
+ $i--;
+ }
+
+ // Strip out comments
+ if (strpos ($line, '#')) {
+ $line = preg_replace('/\s*#([^"\']+)$/','',$line);
+ }
+
+ while (++$i < $cnt && self::greedilyNeedNextLine($line)) {
+ $line = rtrim ($line, " \n\t\r") . ' ' . ltrim ($Source[$i], " \t");
+ }
+ $i--;
+
+ $lineArray = $this->_parseLine($line);
+
+ if ($literalBlockStyle)
+ $lineArray = $this->revertLiteralPlaceHolder ($lineArray, $literalBlock);
+
+ $this->addArray($lineArray, $this->indent);
+
+ foreach ($this->delayedPath as $indent => $delayedPath)
+ $this->path[$indent] = $delayedPath;
+
+ $this->delayedPath = array();
+
+ }
+ return $this->result;
+ }
+
+ private function loadFromSource ($input) {
+ if (!empty($input) && strpos($input, "\n") === false && file_exists($input))
+ $input = file_get_contents($input);
+
+ return $this->loadFromString($input);
+ }
+
+ private function loadFromString ($input) {
+ $lines = explode("\n",$input);
+ foreach ($lines as $k => $_) {
+ $lines[$k] = rtrim ($_, "\r");
+ }
+ return $lines;
+ }
+
+ /**
+ * Parses YAML code and returns an array for a node
+ * @access private
+ * @return array
+ * @param string $line A line from the YAML file
+ */
+ private function _parseLine($line) {
+ if (!$line) return array();
+ $line = trim($line);
+ if (!$line) return array();
+
+ $array = array();
+
+ $group = $this->nodeContainsGroup($line);
+ if ($group) {
+ $this->addGroup($line, $group);
+ $line = $this->stripGroup ($line, $group);
+ }
+
+ if ($this->startsMappedSequence($line))
+ return $this->returnMappedSequence($line);
+
+ if ($this->startsMappedValue($line))
+ return $this->returnMappedValue($line);
+
+ if ($this->isArrayElement($line))
+ return $this->returnArrayElement($line);
+
+ if ($this->isPlainArray($line))
+ return $this->returnPlainArray($line);
+
+
+ return $this->returnKeyValuePair($line);
+
+ }
+
+ /**
+ * Finds the type of the passed value, returns the value as the new type.
+ * @access private
+ * @param string $value
+ * @return mixed
+ */
+ private function _toType($value) {
+ if ($value === '') return "";
+ $first_character = $value[0];
+ $last_character = substr($value, -1, 1);
+
+ $is_quoted = false;
+ do {
+ if (!$value) break;
+ if ($first_character != '"' && $first_character != "'") break;
+ if ($last_character != '"' && $last_character != "'") break;
+ $is_quoted = true;
+ } while (0);
+
+ if ($is_quoted) {
+ $value = str_replace('\n', "\n", $value);
+ if ($first_character == "'")
+ return strtr(substr ($value, 1, -1), array ('\'\'' => '\'', '\\\''=> '\''));
+ return strtr(substr ($value, 1, -1), array ('\\"' => '"', '\\\''=> '\''));
+ }
+
+ if (strpos($value, ' #') !== false && !$is_quoted)
+ $value = preg_replace('/\s+#(.+)$/','',$value);
+
+ if ($first_character == '[' && $last_character == ']') {
+ // Take out strings sequences and mappings
+ $innerValue = trim(substr ($value, 1, -1));
+ if ($innerValue === '') return array();
+ $explode = $this->_inlineEscape($innerValue);
+ // Propagate value array
+ $value = array();
+ foreach ($explode as $v) {
+ $value[] = $this->_toType($v);
+ }
+ return $value;
+ }
+
+ if (strpos($value,': ')!==false && $first_character != '{') {
+ $array = explode(': ',$value);
+ $key = trim($array[0]);
+ array_shift($array);
+ $value = trim(implode(': ',$array));
+ $value = $this->_toType($value);
+ return array($key => $value);
+ }
+
+ if ($first_character == '{' && $last_character == '}') {
+ $innerValue = trim(substr ($value, 1, -1));
+ if ($innerValue === '') return array();
+ // Inline Mapping
+ // Take out strings sequences and mappings
+ $explode = $this->_inlineEscape($innerValue);
+ // Propagate value array
+ $array = array();
+ foreach ($explode as $v) {
+ $SubArr = $this->_toType($v);
+ if (empty($SubArr)) continue;
+ if (is_array ($SubArr)) {
+ $array[key($SubArr)] = $SubArr[key($SubArr)]; continue;
+ }
+ $array[] = $SubArr;
+ }
+ return $array;
+ }
+
+ if ($value == 'null' || $value == 'NULL' || $value == 'Null' || $value == '' || $value == '~') {
+ return null;
+ }
+
+ if ( is_numeric($value) && preg_match ('/^(-|)[1-9]+[0-9]*$/', $value) ){
+ $intvalue = (int)$value;
+ if ($intvalue != PHP_INT_MAX && $intvalue != ~PHP_INT_MAX)
+ $value = $intvalue;
+ return $value;
+ }
+
+ if ( is_string($value) && preg_match('/^0[xX][0-9a-fA-F]+$/', $value)) {
+ // Hexadecimal value.
+ return hexdec($value);
+ }
+
+ $this->coerceValue($value);
+
+ if (is_numeric($value)) {
+ if ($value === '0') return 0;
+ if (rtrim ($value, 0) === $value)
+ $value = (float)$value;
+ return $value;
+ }
+
+ return $value;
+ }
+
+ /**
+ * Used in inlines to check for more inlines or quoted strings
+ * @access private
+ * @return array
+ */
+ private function _inlineEscape($inline) {
+ // There's gotta be a cleaner way to do this...
+ // While pure sequences seem to be nesting just fine,
+ // pure mappings and mappings with sequences inside can't go very
+ // deep. This needs to be fixed.
+
+ $seqs = array();
+ $maps = array();
+ $saved_strings = array();
+ $saved_empties = array();
+
+ // Check for empty strings
+ $regex = '/("")|(\'\')/';
+ if (preg_match_all($regex,$inline,$strings)) {
+ $saved_empties = $strings[0];
+ $inline = preg_replace($regex,'YAMLEmpty',$inline);
+ }
+ unset($regex);
+
+ // Check for strings
+ $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/';
+ if (preg_match_all($regex,$inline,$strings)) {
+ $saved_strings = $strings[0];
+ $inline = preg_replace($regex,'YAMLString',$inline);
+ }
+ unset($regex);
+
+ // echo $inline;
+
+ $i = 0;
+ do {
+
+ // Check for sequences
+ while (preg_match('/\[([^{}\[\]]+)\]/U',$inline,$matchseqs)) {
+ $seqs[] = $matchseqs[0];
+ $inline = preg_replace('/\[([^{}\[\]]+)\]/U', ('YAMLSeq' . (count($seqs) - 1) . 's'), $inline, 1);
+ }
+
+ // Check for mappings
+ while (preg_match('/{([^\[\]{}]+)}/U',$inline,$matchmaps)) {
+ $maps[] = $matchmaps[0];
+ $inline = preg_replace('/{([^\[\]{}]+)}/U', ('YAMLMap' . (count($maps) - 1) . 's'), $inline, 1);
+ }
+
+ if ($i++ >= 10) break;
+
+ } while (strpos ($inline, '[') !== false || strpos ($inline, '{') !== false);
+
+ $explode = explode(',',$inline);
+ $explode = array_map('trim', $explode);
+ $stringi = 0; $i = 0;
+
+ while (1) {
+
+ // Re-add the sequences
+ if (!empty($seqs)) {
+ foreach ($explode as $key => $value) {
+ if (strpos($value,'YAMLSeq') !== false) {
+ foreach ($seqs as $seqk => $seq) {
+ $explode[$key] = str_replace(('YAMLSeq'.$seqk.'s'),$seq,$value);
+ $value = $explode[$key];
+ }
+ }
+ }
+ }
+
+ // Re-add the mappings
+ if (!empty($maps)) {
+ foreach ($explode as $key => $value) {
+ if (strpos($value,'YAMLMap') !== false) {
+ foreach ($maps as $mapk => $map) {
+ $explode[$key] = str_replace(('YAMLMap'.$mapk.'s'), $map, $value);
+ $value = $explode[$key];
+ }
+ }
+ }
+ }
+
+
+ // Re-add the strings
+ if (!empty($saved_strings)) {
+ foreach ($explode as $key => $value) {
+ while (strpos($value,'YAMLString') !== false) {
+ $explode[$key] = preg_replace('/YAMLString/',$saved_strings[$stringi],$value, 1);
+ unset($saved_strings[$stringi]);
+ ++$stringi;
+ $value = $explode[$key];
+ }
+ }
+ }
+
+
+ // Re-add the empties
+ if (!empty($saved_empties)) {
+ foreach ($explode as $key => $value) {
+ while (strpos($value,'YAMLEmpty') !== false) {
+ $explode[$key] = preg_replace('/YAMLEmpty/', '', $value, 1);
+ $value = $explode[$key];
+ }
+ }
+ }
+
+ $finished = true;
+ foreach ($explode as $key => $value) {
+ if (strpos($value,'YAMLSeq') !== false) {
+ $finished = false; break;
+ }
+ if (strpos($value,'YAMLMap') !== false) {
+ $finished = false; break;
+ }
+ if (strpos($value,'YAMLString') !== false) {
+ $finished = false; break;
+ }
+ if (strpos($value,'YAMLEmpty') !== false) {
+ $finished = false; break;
+ }
+ }
+ if ($finished) break;
+
+ $i++;
+ if ($i > 10)
+ break; // Prevent infinite loops.
+ }
+
+
+ return $explode;
+ }
+
+ private function literalBlockContinues ($line, $lineIndent) {
+ if (!trim($line)) return true;
+ if (strlen($line) - strlen(ltrim($line)) > $lineIndent) return true;
+ return false;
+ }
+
+ private function referenceContentsByAlias ($alias) {
+ do {
+ if (!isset($this->SavedGroups[$alias])) { echo "Bad group name: $alias."; break; }
+ $groupPath = $this->SavedGroups[$alias];
+ $value = $this->result;
+ foreach ($groupPath as $k) {
+ $value = $value[$k];
+ }
+ } while (false);
+ return $value;
+ }
+
+ private function addArrayInline ($array, $indent) {
+ $CommonGroupPath = $this->path;
+ if (empty ($array)) return false;
+
+ foreach ($array as $k => $_) {
+ $this->addArray(array($k => $_), $indent);
+ $this->path = $CommonGroupPath;
+ }
+ return true;
+ }
+
+ private function addArray ($incoming_data, $incoming_indent) {
+
+ // print_r ($incoming_data);
+
+ if (count ($incoming_data) > 1)
+ return $this->addArrayInline ($incoming_data, $incoming_indent);
+
+ $key = key ($incoming_data);
+ $value = isset($incoming_data[$key]) ? $incoming_data[$key] : null;
+ if ($key === '__!YAMLZero') $key = '0';
+
+ if ($incoming_indent == 0 && !$this->_containsGroupAlias && !$this->_containsGroupAnchor) { // Shortcut for root-level values.
+ if ($key || $key === '' || $key === '0') {
+ $this->result[$key] = $value;
+ } else {
+ $this->result[] = $value; end ($this->result); $key = key ($this->result);
+ }
+ $this->path[$incoming_indent] = $key;
+ return;
+ }
+
+
+
+ $history = array();
+ // Unfolding inner array tree.
+ $history[] = $_arr = $this->result;
+ foreach ($this->path as $k) {
+ $history[] = $_arr = $_arr[$k];
+ }
+
+ if ($this->_containsGroupAlias) {
+ $value = $this->referenceContentsByAlias($this->_containsGroupAlias);
+ $this->_containsGroupAlias = false;
+ }
+
+
+ // Adding string or numeric key to the innermost level or $this->arr.
+ if (is_string($key) && $key == '<<') {
+ if (!is_array ($_arr)) { $_arr = array (); }
+
+ $_arr = array_merge ($_arr, $value);
+ } else if ($key || $key === '' || $key === '0') {
+ if (!is_array ($_arr))
+ $_arr = array ($key=>$value);
+ else
+ $_arr[$key] = $value;
+ } else {
+ if (!is_array ($_arr)) { $_arr = array ($value); $key = 0; }
+ else { $_arr[] = $value; end ($_arr); $key = key ($_arr); }
+ }
+
+ $reverse_path = array_reverse($this->path);
+ $reverse_history = array_reverse ($history);
+ $reverse_history[0] = $_arr;
+ $cnt = count($reverse_history) - 1;
+ for ($i = 0; $i < $cnt; $i++) {
+ $reverse_history[$i+1][$reverse_path[$i]] = $reverse_history[$i];
+ }
+ $this->result = $reverse_history[$cnt];
+
+ $this->path[$incoming_indent] = $key;
+
+ if ($this->_containsGroupAnchor) {
+ $this->SavedGroups[$this->_containsGroupAnchor] = $this->path;
+ if (is_array ($value)) {
+ $k = key ($value);
+ if (!is_int ($k)) {
+ $this->SavedGroups[$this->_containsGroupAnchor][$incoming_indent + 2] = $k;
+ }
+ }
+ $this->_containsGroupAnchor = false;
+ }
+
+ }
+
+ private static function startsLiteralBlock ($line) {
+ $lastChar = substr (trim($line), -1);
+ if ($lastChar != '>' && $lastChar != '|') return false;
+ if ($lastChar == '|') return $lastChar;
+ // HTML tags should not be counted as literal blocks.
+ if (preg_match ('#<.*?>$#', $line)) return false;
+ return $lastChar;
+ }
+
+ private static function greedilyNeedNextLine($line) {
+ $line = trim ($line);
+ if (!strlen($line)) return false;
+ if (substr ($line, -1, 1) == ']') return false;
+ if ($line[0] == '[') return true;
+ if (preg_match ('#^[^:]+?:\s*\[#', $line)) return true;
+ return false;
+ }
+
+ private function addLiteralLine ($literalBlock, $line, $literalBlockStyle, $indent = -1) {
+ $line = self::stripIndent($line, $indent);
+ if ($literalBlockStyle !== '|') {
+ $line = self::stripIndent($line);
+ }
+ $line = rtrim ($line, "\r\n\t ") . "\n";
+ if ($literalBlockStyle == '|') {
+ return $literalBlock . $line;
+ }
+ if (strlen($line) == 0)
+ return rtrim($literalBlock, ' ') . "\n";
+ if ($line == "\n" && $literalBlockStyle == '>') {
+ return rtrim ($literalBlock, " \t") . "\n";
+ }
+ if ($line != "\n")
+ $line = trim ($line, "\r\n ") . " ";
+ return $literalBlock . $line;
+ }
+
+ function revertLiteralPlaceHolder ($lineArray, $literalBlock) {
+ foreach ($lineArray as $k => $_) {
+ if (is_array($_))
+ $lineArray[$k] = $this->revertLiteralPlaceHolder ($_, $literalBlock);
+ else if (substr($_, -1 * strlen ($this->LiteralPlaceHolder)) == $this->LiteralPlaceHolder)
+ $lineArray[$k] = rtrim ($literalBlock, " \r\n");
+ }
+ return $lineArray;
+ }
+
+ private static function stripIndent ($line, $indent = -1) {
+ if ($indent == -1) $indent = strlen($line) - strlen(ltrim($line));
+ return substr ($line, $indent);
+ }
+
+ private function getParentPathByIndent ($indent) {
+ if ($indent == 0) return array();
+ $linePath = $this->path;
+ do {
+ end($linePath); $lastIndentInParentPath = key($linePath);
+ if ($indent <= $lastIndentInParentPath) array_pop ($linePath);
+ } while ($indent <= $lastIndentInParentPath);
+ return $linePath;
+ }
+
+
+ private function clearBiggerPathValues ($indent) {
+
+
+ if ($indent == 0) $this->path = array();
+ if (empty ($this->path)) return true;
+
+ foreach ($this->path as $k => $_) {
+ if ($k > $indent) unset ($this->path[$k]);
+ }
+
+ return true;
+ }
+
+
+ private static function isComment ($line) {
+ if (!$line) return false;
+ if ($line[0] == '#') return true;
+ if (trim($line, " \r\n\t") == '---') return true;
+ return false;
+ }
+
+ private static function isEmpty ($line) {
+ return (trim ($line) === '');
+ }
+
+
+ private function isArrayElement ($line) {
+ if (!$line || !is_scalar($line)) return false;
+ if (substr($line, 0, 2) != '- ') return false;
+ if (strlen ($line) > 3)
+ if (substr($line,0,3) == '---') return false;
+
+ return true;
+ }
+
+ private function isHashElement ($line) {
+ return strpos($line, ':');
+ }
+
+ private function isLiteral ($line) {
+ if ($this->isArrayElement($line)) return false;
+ if ($this->isHashElement($line)) return false;
+ return true;
+ }
+
+
+ private static function unquote ($value) {
+ if (!$value) return $value;
+ if (!is_string($value)) return $value;
+ if ($value[0] == '\'') return trim ($value, '\'');
+ if ($value[0] == '"') return trim ($value, '"');
+ return $value;
+ }
+
+ private function startsMappedSequence ($line) {
+ return (substr($line, 0, 2) == '- ' && substr ($line, -1, 1) == ':');
+ }
+
+ private function returnMappedSequence ($line) {
+ $array = array();
+ $key = self::unquote(trim(substr($line,1,-1)));
+ $array[$key] = array();
+ $this->delayedPath = array(strpos ($line, $key) + $this->indent => $key);
+ return array($array);
+ }
+
+ private function checkKeysInValue($value) {
+ if (strchr('[{"\'', $value[0]) === false) {
+ if (strchr($value, ': ') !== false) {
+ throw new Exception('Too many keys: '.$value);
+ }
+ }
+ }
+
+ private function returnMappedValue ($line) {
+ $this->checkKeysInValue($line);
+ $array = array();
+ $key = self::unquote (trim(substr($line,0,-1)));
+ $array[$key] = '';
+ return $array;
+ }
+
+ private function startsMappedValue ($line) {
+ return (substr ($line, -1, 1) == ':');
+ }
+
+ private function isPlainArray ($line) {
+ return ($line[0] == '[' && substr ($line, -1, 1) == ']');
+ }
+
+ private function returnPlainArray ($line) {
+ return $this->_toType($line);
+ }
+
+ private function returnKeyValuePair ($line) {
+ $array = array();
+ $key = '';
+ if (strpos ($line, ': ')) {
+ // It's a key/value pair most likely
+ // If the key is in double quotes pull it out
+ if (($line[0] == '"' || $line[0] == "'") && preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) {
+ $value = trim(str_replace($matches[1],'',$line));
+ $key = $matches[2];
+ } else {
+ // Do some guesswork as to the key and the value
+ $explode = explode(': ', $line);
+ $key = trim(array_shift($explode));
+ $value = trim(implode(': ', $explode));
+ $this->checkKeysInValue($value);
+ }
+ // Set the type of the value. Int, string, etc
+ $value = $this->_toType($value);
+ if ($key === '0') $key = '__!YAMLZero';
+ $array[$key] = $value;
+ } else {
+ $array = array ($line);
+ }
+ return $array;
+
+ }
+
+
+ private function returnArrayElement ($line) {
+ if (strlen($line) <= 1) return array(array()); // Weird %)
+ $array = array();
+ $value = trim(substr($line,1));
+ $value = $this->_toType($value);
+ if ($this->isArrayElement($value)) {
+ $value = $this->returnArrayElement($value);
+ }
+ $array[] = $value;
+ return $array;
+ }
+
+
+ private function nodeContainsGroup ($line) {
+ $symbolsForReference = 'A-z0-9_\-';
+ if (strpos($line, '&') === false && strpos($line, '*') === false) return false; // Please die fast ;-)
+ if ($line[0] == '&' && preg_match('/^(&['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1];
+ if ($line[0] == '*' && preg_match('/^(\*['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1];
+ if (preg_match('/(&['.$symbolsForReference.']+)$/', $line, $matches)) return $matches[1];
+ if (preg_match('/(\*['.$symbolsForReference.']+$)/', $line, $matches)) return $matches[1];
+ if (preg_match ('#^\s*<<\s*:\s*(\*[^\s]+).*$#', $line, $matches)) return $matches[1];
+ return false;
+
+ }
+
+ private function addGroup ($line, $group) {
+ if ($group[0] == '&') $this->_containsGroupAnchor = substr ($group, 1);
+ if ($group[0] == '*') $this->_containsGroupAlias = substr ($group, 1);
+ //print_r ($this->path);
+ }
+
+ private function stripGroup ($line, $group) {
+ $line = trim(str_replace($group, '', $line));
+ return $line;
+ }
+}
+}
+
+// Enable use of Spyc from command line
+// The syntax is the following: php Spyc.php spyc.yaml
+
+do {
+ if (PHP_SAPI != 'cli') break;
+ if (empty ($_SERVER['argc']) || $_SERVER['argc'] < 2) break;
+ if (empty ($_SERVER['PHP_SELF']) || FALSE === strpos ($_SERVER['PHP_SELF'], 'Spyc.php') ) break;
+ $file = $argv[1];
+ echo json_encode (spyc_load_file ($file));
+} while (0);
diff --git a/modules/util/Text.class.php b/modules/util/Text.class.php
@@ -0,0 +1,393 @@
+<?php
+// OpenRat Content Management System
+// Copyright (C) 2002 Jan Dankert, jandankert@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+
+/**
+ * Nuetzliche Funktionen fuer das Bearbeiten von Texten/Zeichenketten
+ * @author $Author$
+ * @version $Revision$
+ * @package openrat.services
+ */
+class Text
+{
+ /**
+ *
+ * @param unknown $key
+ * @param unknown $text
+ * @return string|unknown
+ */
+ public static function accessKey( $key, $text )
+ {
+ $pos = strpos(strtolower($text),strtolower($key));
+
+ if ( $pos !== false )
+ return substr($text,0,max($pos,0)).'<span class="accesskey">'.substr($text,$pos,1).'</span>'.substr($text,$pos+1);
+ else
+ return $text;
+ }
+
+ /**
+ * Alias fuer Methode maxLength()
+ *
+ * @deprecated use maxlength() !
+ */
+ public static function maxLaenge( $laenge,$text )
+ {
+ return Text::maxLength($text,$laenge);
+ }
+
+
+ /**
+ * Einen Text auf eine bestimmte Laenge begrenzen.
+ *
+ * Ist der Text zu lang, so wird abgeschnitten und
+ * eine Zeichenkette angehaengt.
+ *
+ * @param String Text, der zu begrenzen ist
+ * @param Integer maximale Laenge des Textes (optional)
+ * @param Text, der an gekuerzten Text angehangen wird (optional)
+ */
+ public static function maxLength( $text,$laenge=20,$append='...',$where=STR_PAD_RIGHT )
+ {
+ if ( strlen($text) > $laenge )
+ {
+ if ( $where == STR_PAD_RIGHT )
+ $text = substr($text,0,$laenge).$append;
+ elseif ( $where == STR_PAD_BOTH )
+ $text = substr($text,0,$laenge/2).$append.substr($text,strlen($text)-($laenge/2));
+ }
+
+ return $text;
+ }
+
+
+ /**
+ * Umwandeln von BB-Code in Wiki-Textauszeichnungen
+ *
+ * @param text zu bearbeitender Text
+ *
+ * @return String Ausgabe
+ */
+ public static function bbCode2Wiki( $inhalt )
+ {
+ $inhalt = preg_replace('/\[b\]([^\[]*)\[\/b\]/i' , '*\\1*' ,$inhalt);
+ $inhalt = preg_replace('/\[i\]([^\[]*)\[\/i\]/i' , '_\\1_' ,$inhalt);
+ $inhalt = preg_replace('/\[code\]([^\[]*)\[\/code\]/i' , '=\\1=' ,$inhalt);
+
+ $inhalt = preg_replace('/\[url\]([^\[]*)\[\/url\]/i' ,'"\\1"->"\\1"' ,$inhalt);
+ $inhalt = preg_replace('/\[url=([^\[]*)\]([^\[]*)\[\/url\]/i' ,'"\\2"->"\\1"' ,$inhalt);
+
+ return $inhalt;
+ }
+
+
+ /**
+ * Umwandeln von einfachen HTML-Befehlen in Wiki-Textauszeichnungen
+ *
+ * @param text zu bearbeitender Text
+ *
+ * @return String Ausgabe
+ */
+ public static function Html2Wiki( $inhalt )
+ {
+ $inhalt = preg_replace('/<b(.*)>(.*)<\/b>/i','*\\2*' ,$inhalt);
+ $inhalt = preg_replace('/<i(.*)>(.*)<\/i>/i','_\\2_' ,$inhalt);
+ $inhalt = preg_replace('/<a(.*)href="(.*)">(.*)<\/a>/i','"\\3"->"\\2"' ,$inhalt);
+
+ return $inhalt;
+ }
+
+
+ /**
+ * HTML-Entitaeten fuer HTML-Tags verwenden
+ *
+ * @param String Text, in dem HTML-Tags umgewandelt werden sollen
+ * @return String Ausgabe
+ */
+ public static function encodeHtml( $inhalt )
+ {
+ //$inhalt = str_replace('&','&',$inhalt);
+ $inhalt = str_replace('"','"',$inhalt);
+ $inhalt = str_replace('<','<' ,$inhalt);
+ $inhalt = str_replace('>','>' ,$inhalt);
+
+ return $inhalt;
+ }
+
+
+
+ /**
+ * Ersetzt Sonderzeichen durch HTML-�quivalente.<br>
+ * Z.B. Ersetzt "(c)" durch "©".
+ */
+ public static function replaceHtmlChars( $text )
+ {
+ global $conf;
+
+ foreach( explode(' ',$conf['editor']['html']['replace']) as $repl )
+ {
+ list( $ersetze, $mit ) = explode(':',$repl.':');
+ $text = str_replace($ersetze, $mit, $text);
+ }
+
+ return $text;
+ }
+
+
+
+ /**
+ * HTML-Entitaeten fuer HTML-Tags verwenden
+ *
+ * @param String Text, in dem HTML-Tags umgewandelt werden sollen
+ * @return String Ausgabe
+ */
+ public static function encodeHtmlSpecialChars( $inhalt )
+ {
+ return Text::replaceHtmlChars( $inhalt );
+ }
+
+
+
+ /**
+ * Vergleicht 2 Text-Arrays und ermittelt eine Darstellung der Unterschiede
+ *
+ */
+ public static function diff( $from_text,$to_text )
+ {
+ // Zaehler pro Textarray
+ $pos_from = -1;
+ $pos_to = -1;
+
+ // Ergebnis-Arrays
+ $from_out = array();
+ $to_out = array();
+
+ while( true )
+ {
+ $pos_from++;
+ $pos_to ++;
+
+ if ( !isset($from_text[$pos_from]) &&
+ !isset($to_text [$pos_to ]) )
+ {
+ // Text in ist 'neu' und 'alt' zuende. Ende der Schleife.
+ break;
+ }
+ elseif
+ ( isset($from_text[$pos_from]) &&
+ !isset($to_text [$pos_to]) )
+ {
+ // Text in 'neu' ist zuende, die Restzeilen von 'alt' werden ausgegeben
+ while( isset($from_text[$pos_from]) )
+ {
+ $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>'old');
+ $to_out [] = array();
+ $pos_from++;
+ }
+ break;
+ }
+ elseif
+ ( !isset($from_text[$pos_from]) &&
+ isset($to_text [$pos_to]) )
+ {
+ // Umgekehrter Fall: Text in 'alt' ist zuende, Restzeilen aus 'neu' werden ausgegeben
+ while( isset($to_text[$pos_to]) )
+ {
+ $from_out[] = array();
+ $to_out [] = array('text'=>$to_text[$pos_to],'line'=>$pos_to+1,'type'=>'new');
+ $pos_to++;
+ }
+ break;
+ }
+ elseif
+ ( rtrim($from_text[$pos_from]) != rtrim($to_text[$pos_to]) )
+ {
+ // Zeilen sind vorhanden, aber ungleich
+ // Wir suchen jetzt die naechsten beiden Zeilen, die gleich sind.
+ $max_entf = min(count($from_text)-$pos_from-1,count($to_text)-$pos_to-1);
+
+ #echo "suche start, max_entf=$max_entf, pos_from=$pos_from, pos_to=$pos_to<br/>";
+
+ for ( $a=0; $a<=$max_entf; $a++ )
+ {
+ #echo "a ist $a<br/>";
+ for ( $b=0; $b<=$max_entf; $b++ )
+ {
+ #echo "b ist $b<br/>";
+ if ( trim($from_text[$pos_from+$b]) != '' &&
+ $from_text[$pos_from+$b] == $to_text[$pos_to+$a] )
+ {
+ $pos_gef_from = $pos_from+$b;
+ $pos_gef_to = $pos_to +$a;
+ break;
+ }
+
+ if ( trim($from_text[$pos_from+$a]) != '' &&
+ $from_text[$pos_from+$a] == $to_text[$pos_to+$b] )
+ {
+ $pos_gef_from = $pos_from+$a;
+ $pos_gef_to = $pos_to +$b;
+ break;
+ }
+ }
+
+ if ( $b <=$max_entf)
+ {
+ break;
+ }
+ }
+
+ if ( $a<=$max_entf )
+ {
+ // Gleiche Zeile gefunden
+ #echo "gefunden, pos_gef_from=$pos_gef_from, pos_gef_to=$pos_gef_to<br/>";
+
+ if ( $pos_gef_from - $pos_from == 0 )
+ $type = 'new';
+ elseif
+ ( $pos_gef_to - $pos_to == 0 )
+ $type = 'old';
+ else
+ $type = 'notequal';
+
+ while( $pos_gef_from - $pos_from > 0 &&
+ $pos_gef_to - $pos_to > 0 )
+ {
+ $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>$type);
+ $to_out [] = array('text'=>$to_text [$pos_to ],'line'=>$pos_to+1 ,'type'=>$type);
+
+ $pos_from++;
+ $pos_to++;
+ }
+
+ while( $pos_gef_from - $pos_from > 0 )
+ {
+ $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>$type);
+ $to_out [] = array();
+ $pos_from++;
+ }
+
+ while( $pos_gef_to - $pos_to > 0 )
+ {
+ $from_out[] = array();
+ $to_out [] = array('text'=>$to_text [$pos_to ],'line'=>$pos_to+1 ,'type'=>$type);
+ $pos_to++;
+ }
+ $pos_from--;
+ $pos_to--;
+ }
+ else
+ {
+ // Keine gleichen Zeilen gefunden
+ #echo "nicht gefunden, i=$i, j=$j, pos_from war $pos_from, pos_to war $pos_to<br/>";
+
+ while( true )
+ {
+ if ( !isset($from_text[$pos_from]) &&
+ !isset($to_text [$pos_to ]) )
+ {
+ break;
+ }
+ elseif
+ ( isset($from_text[$pos_from]) &&
+ !isset($to_text [$pos_to ]) )
+ {
+ $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>'notequal');
+ $to_out [] = array();
+ }
+ elseif
+ ( !isset($from_text[$pos_from]) &&
+ isset($to_text [$pos_to ]) )
+ {
+ $from_out[] = array();
+ $to_out [] = array('text'=>$to_text [$pos_to ],'line'=>$pos_to+1 ,'type'=>'notequal');
+ }
+ else
+ {
+ $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>'notequal');
+ $to_out [] = array('text'=>$to_text [$pos_to ],'line'=>$pos_to+1 ,'type'=>'notequal');
+ }
+ $pos_from++;
+ $pos_to++;
+ }
+ }
+ }
+ else
+ {
+ // Zeilen sind gleich
+ $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>'equal');
+ $to_out [] = array('text'=>$to_text [$pos_to ],'line'=>$pos_to+1 ,'type'=>'equal');
+ }
+ }
+
+ return( array($from_out,$to_out) );
+ }
+
+
+ /**
+ * Entfernt einen Text-Bereich aus einer Zeichenkette.<br>
+ * Es wird angegeben, von wo bis wo entfernt werden soll.
+ *
+ * @param $text Text, aus dem entfernt wird
+ * @param $von der Text, AB dem entfernt wird
+ * @param $bis der Text, BIS ZU DEM entfernt wird
+ * @return String Text
+ */
+ public static function entferneVonBis($text,$von,$bis)
+ {
+ $beg = strpos($text,$von);
+ $end = strpos($text,$bis);
+ if ( $beg!==false && $end !==false )
+ $text = substr($text,0,$beg).substr($text,$end+strlen($bis));
+ return $text;
+ }
+
+
+ /**
+ * Saeubert eine Zeichenkette.
+ *
+ * Es werden ungueltige Zeichen aus einer Zeichenkette entfernt. Es wird mit einer Whitelist
+ * gearbeitet, d.h. die erlaubten Zeichen werden angegeben.
+ *
+ * @param $eingabe Die Eingabe-Zeichenkette, aus der ungueltige Zeichen entfernt werden sollen.
+ * @param $erlaubt Die erlaubten Zeichen (eine "White-List")
+ * @return String die aufgeräumte Zeichenkette
+ */
+ public static function clean( $eingabe, $erlaubt )
+ {
+ $first = strtr( $eingabe, $erlaubt, str_repeat("\x01", strlen($erlaubt)) );
+ $second = strtr( $eingabe, $first , str_repeat("\x00", strlen($first )) );
+ return str_replace("\x00",'',$second);
+ }
+
+
+
+ public static function parseOID( $text )
+ {
+ $oids = array();
+ $treffer = array();
+
+ preg_match_all('/\"([^\"]*)__OID__([0-9]+)__([^\"]*)\"/', $text, $treffer,PREG_SET_ORDER);
+
+ foreach( $treffer as $t )
+ $oids[$t[2]] = $t[0];
+
+ return $oids;
+ }
+}
+
diff --git a/modules/util/Transformer.class.php b/modules/util/Transformer.class.php
@@ -0,0 +1,102 @@
+<?php
+
+/**
+ * Transformieren eines Textes.<br>
+ * Ein Text wird geparst und neu gerendert.
+ *
+ * @author $Author$
+ * @version $Revision$
+ * @package openrat.services
+ */
+class Transformer
+{
+ var $text = '';
+ var $doc;
+ var $page;
+ var $element;
+
+ function transform()
+ {
+ $this->parseDocument();
+ $this->renderDocument();
+
+ $this->text = $this->renderedText;
+ }
+
+
+
+ /**
+ * Parsen eines Textes.<br>
+ * Der Text muss in der Eigenschaft 'text' bereits zur Verf�gung stehen.<br>
+ * Der Text wird geparst und als DOM (Document object model) intern gespeichert.
+ */
+
+ function parseDocument()
+ {
+ // Den Text zeilenweise aufteilen.
+ $zeilen = explode("\n",$this->text);
+
+ // Dokument erzeugen und den Text parsen.
+ $parser = new WikiParser();
+ $this->doc = new DocumentElement();
+ $this->doc->element = $this->element;
+ $this->doc->parse( $zeilen );
+ $this->doc->page = $this->page;
+ }
+
+
+
+ /**
+ * Das interne Dokumente wird gerendet.<br>
+ * Die fertige Ausgabe steht anschliessend in der Eigenschaft "renderedText" zur Verf�gung.
+ */
+ function renderDocument()
+ {
+ $this->doc->encodeHtml = !$this->element->html;
+
+ $text = $this->doc->render( $this->page->mimeType() );
+
+ // Liste der verlinkten Objekt-Ids.
+ // Die Objekt-Ids werden absteigend sortiert, damit z.B. '33' vor '3' ersetzt wird.
+ $linkedObjectIds = $this->doc->linkedObjectIds;
+ rsort( $linkedObjectIds,SORT_NUMERIC );
+
+ // Links object:nnn ersetzen
+ //
+ // Das Dokument-Objekt hat keine Information ueber die aktuelle Seite,
+ // daher werden die Links auf Objekte hier gesetzt.
+ foreach( $linkedObjectIds as $objectId )
+ {
+ $targetPath = $this->page->path_to_object( $objectId );
+
+ // Hack: Sonderzeichen muessen in URLs maskiert werden, aber nur bei URLs die aus Link-Objekten kommen, bei allem
+ // anderen (insbesondere Preview-Links zu andereen Seiten) darf die Umsetzung nicht erfolgen.
+ // Der Renderer kann dies nicht tun, denn der erzeugt nur "object://..."-URLs.
+ // Beispiel: "...?a=1&b=2" wird zu "...?a=1&b=2"
+ $o = new Object($objectId);
+ try
+ {
+ $o->load();
+ if ( $o->isLink )
+ {
+ $l = new Link($objectId);
+ $l->load();
+ if ( $l->isLinkToUrl && $this->page->mimeType() == 'text/html' )
+ $targetPath = htmlspecialchars($targetPath);
+ }
+ }
+ catch( ObjectNotFoundException $e)
+ {
+ $targetPath = 'javascript:alert("object '.$objectId.' not found");';
+ }
+
+
+ $text = str_replace( 'object:' .$objectId, $targetPath, $text );
+ $text = str_replace( 'object://'.$objectId, $targetPath, $text );
+ }
+
+ $this->renderedText = $text;
+ }
+}
+
+?>+
\ No newline at end of file
diff --git a/modules/util/TreeElement.class.php b/modules/util/TreeElement.class.php
@@ -0,0 +1,75 @@
+<?php
+
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+/**
+ * Darstellen eines Elementes in einer Baumstruktur
+ * @author $Author$
+ * @version $Revision$
+ * @package openrat.services
+ */
+class TreeElement
+{
+ /**
+ * @type Integer
+ */
+ var $id;
+
+ var $extraId = array();
+
+ var $internalId = 0;
+
+ /**
+ * Text des Baumelementes
+ * @type String
+ */
+ var $text = "";
+
+ /**
+ * Beschreibung
+ * @type String
+ */
+ var $description = "";
+ var $url = "";
+ var $icon = "";
+ var $target = "";
+ var $action = "";
+
+ /**
+ * Unterelemente
+ * Ein Array von Ids
+ * @type Array
+ */
+ var $subElementIds = array();
+
+ /**
+ * Typ des Elementes
+ * In der Tree-Klasse muss es eine Methode mit diesem Namen geben, die das
+ * Element laedt.
+ * @type String
+ */
+ var $type = "";
+
+
+ // Konstruktor
+ function __construct()
+ {
+ }
+}
+
+?>+
\ No newline at end of file
diff --git a/modules/util/Upload.class.php b/modules/util/Upload.class.php
@@ -0,0 +1,85 @@
+<?php
+// OpenRat Content Management System
+// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
+//
+// This program is free software; you can redistribute it and/or
+// modify it under the terms of the GNU General Public License
+// as published by the Free Software Foundation; either version 2
+// of the License, or (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+
+/**
+ * Methoden fuer den Upload einer Datei
+ *
+ * @author $Author$
+ * @version $Revision$
+ * @package openrat.services
+ */
+class Upload
+{
+ var $filename;
+ var $extension;
+ var $value;
+ var $size;
+ var $error = '';
+
+
+ /**
+ * Stellt fest, ob der Upload geklappt hat.
+ *
+ * @return boolean
+ */
+ function isValid()
+ {
+ return empty($this->error);
+ }
+
+
+
+ /**
+ * Bearbeitet den Upload einer Datei.<br>
+ * Bei der Objekterzeugung wird die Datei bereits geladen.<br>
+ *
+ * @return Upload
+ */
+ function __construct( $name='file' ) // Konstruktor
+ {
+ global $FILES;
+
+ if ( !isset($FILES[$name]) ||
+ !isset($FILES[$name]['tmp_name']) ||
+ !is_file($FILES[$name]['tmp_name']) )
+ {
+ $this->error = 'No file received.';
+ return;
+ }
+
+ $this->size = filesize($FILES[$name]['tmp_name']);
+
+ $fh = fopen( $FILES[$name]['tmp_name'],'r' );
+
+ $this->value = fread($fh,$this->size);
+ fclose( $fh );
+
+ $this->filename = $FILES[$name]['name'];
+ $this->extension = '';
+
+ $p = strrpos( $this->filename,'.' ); // Letzten Punkt suchen
+
+ if ($p!==false) // Wennn letzten Punkt gefunden, dann dort aufteilen
+ {
+ $this->extension = substr( $this->filename,$p+1 );
+ $this->filename = substr( $this->filename,0,$p );
+ }
+ }
+}
+
+?>+
\ No newline at end of file
diff --git a/modules/util/XML.class.php b/modules/util/XML.class.php
@@ -0,0 +1,169 @@
+<?php
+/**
+ * Multidimensional Array-to-XML.
+ *
+ * Example:
+ * $xml = new XML();
+ * header('Content-Type: application/xml');
+ * echo $xml->encode( $yourBigArray );
+ * exit;
+ *
+ * Author: Honor� Vasconcelos, Jan Dankert
+ *
+ * Original from:
+ * Clean XML To Array: http://www.phpclasses.org/browse/package/3598.html
+ *
+ * License of this class: BSD-Licence.
+ *
+ * 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.
+ *
+ * Neither the name of the Author(s) nor the names of its contributors may be used
+ * to endorse or promote products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "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 THE COPYRIGHT OWNER OR
+ * 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.
+ */
+class XML
+{
+ /**
+ * Parse multidimentional array to XML.
+ *
+ * @param array $array
+ * @return String
+ */
+ var $xmlText = '';
+
+
+ /**
+ * Name of the root element.
+ *
+ * @var String
+ */
+ var $root = 'xml';
+
+ /*
+ * Char to indent with.
+ *
+ * @var String
+ */
+ var $indentChar = "\t";
+
+
+ /**
+ * Newline-Char
+ * @var String
+ */
+ var $nl = "\n";
+
+ /**
+ * Encode a array to XML.
+ *
+ * @param Array $array
+ * @return String (serialized XML)
+ */
+ function encode($array)
+ {
+ //star and end the XML document
+ $this->xmlText = '<?xml version="1.0" encoding="utf-8"?>'.$this->nl;
+ $this->xmlText .= '<'.$this->root.'>'.$this->nl;
+ $this->array_transform($array,1);
+ $this->xmlText .='</'.$this->root.'>';
+
+ return $this->xmlText;
+ }
+
+
+ /**
+ * @access private
+ */
+ function array_transform($array,$depth){
+
+ foreach($array as $key => $value)
+ {
+ $attr = array();
+ if ( is_numeric($key) )
+ {
+ // Array-Einträge mit numerischen Index können nicht direkt in ein XML-Element
+ // umgewandelt werden, da nur-numerische Element-Namen nicht erlaubt sind.
+ // Daher verwenden wir dann 'entry' als Elementnamen.
+ $attr['id'] = $key;
+ $key = 'entry';
+ }
+
+ $indent = str_repeat($this->indentChar,$depth);
+
+ if ( empty($value) )
+ {
+ $this->xmlText .= $indent.$this->shortTag($key,$attr).$this->nl;
+ }
+ elseif ( is_object($value) )
+ {
+ // Der Inhalt ist ein Array, daher rekursiv verzweigen.
+ $this->xmlText .= $indent.$this->openTag($key,$attr).$this->nl;
+ $prop = get_object_vars($value);
+ $this->array_transform($prop,$depth+1); // Rekursiver Aufruf
+ $this->xmlText .= $indent.$this->closeTag($key).$this->nl;
+ }
+ elseif ( is_array($value) )
+ {
+ // Der Inhalt ist ein Array, daher rekursiv verzweigen.
+ $this->xmlText .= $indent.$this->openTag($key,$attr).$this->nl;
+ $this->array_transform($value,$depth+1); // Rekursiver Aufruf
+ $this->xmlText .= $indent.$this->closeTag($key).$this->nl;
+ }
+ else
+ {
+ // Der Inhalt ist ein einfacher Inhalt (kein Array).
+ $this->xmlText .= $indent.$this->openTag($key,$attr);
+ $this->xmlText .= $value;
+ $this->xmlText .= $this->closeTag($key).$this->nl;
+ }
+ }
+ }
+
+
+ function openTag($key,$attr)
+ {
+ $tag = '<'.$key;
+ foreach( $attr as $attr_name=>$attr_value )
+ $tag .= ' '.$attr_name.'="'.$attr_value.'"';
+ $tag .= '>';
+ return $tag;
+ }
+
+
+
+ function shortTag($key,$attr)
+ {
+ $tag = '<'.$key;
+ foreach( $attr as $attr_name=>$attr_value )
+ $tag .= ' '.$attr_name.'="'.$attr_value.'"';
+ $tag .= ' />';
+ return $tag;
+ }
+
+
+
+ function closeTag($key)
+ {
+ return '</'.$key.'>';
+ }
+}
+
+?>+
\ No newline at end of file
diff --git a/modules/util/config-default.php b/modules/util/config-default.php
@@ -0,0 +1,880 @@
+<?php
+// DO NOT MAKE ANY CHANGES IN THIS FILE, please edit the file 'config.yml' or 'config-<host>.yml' instead.
+// This file should only be changed by developers.
+$conf = array();
+$conf['applications'] = array();
+$conf['applications']['']=0;
+$conf['applications']['phpmyadmin'] = array();
+$conf['applications']['phpmyadmin']['name']='PHPYourAdmin';
+$conf['applications']['phpmyadmin']['url']="https://example.com/anotherapplication/index.cgi";
+$conf['applications']['phpmyadmin']['param']="ticketidforopenrat";
+$conf['applications']['phpmyadmin']['group']='0';
+$conf['applications']['phpmyadmin']['description']="Your database administration";
+$conf['cache'] = array();
+$conf['cache']['conditional_get']=true;
+$conf['cache']['enable_cache']=false;
+$conf['cache']['tmp_dir']="";
+$conf['config'] = array();
+$conf['config']['auto_reload']= true;
+$conf['config']['session_destroy_on_config_reload']= true;
+$conf['content'] = array();
+$conf['content']['file'] = array();
+$conf['content']['file']['max_file_size']='1500';
+$conf['content']['revision-limit'] = array();
+$conf['content']['revision-limit']['enabled']= false;
+$conf['content']['revision-limit']['max-age']= 120;
+$conf['content']['revision-limit']['min-age']= 1;
+$conf['content']['revision-limit']['max-revisions']= 100;
+$conf['content']['revision-limit']['min-revisions']= 3;
+$conf['content']['language'] = array();
+$conf['content']['language']['use_default_language']= true;
+$conf['countries'] = array();
+$conf['countries']['']='0';
+$conf['countries']['AA']='Afar';
+$conf['countries']['AB']='Abkhazian';
+$conf['countries']['AF']='Afrikaans';
+$conf['countries']['AM']='Amharic';
+$conf['countries']['AR']='Arabic';
+$conf['countries']['AS']='Assamese';
+$conf['countries']['AY']='Aymara';
+$conf['countries']['AZ']='Azerbaijani';
+$conf['countries']['BA']='Bashkir';
+$conf['countries']['BE']='Byelorussian';
+$conf['countries']['BG']='Bulgarian';
+$conf['countries']['BH']='Bihari';
+$conf['countries']['BI']='Bislama';
+$conf['countries']['BN']='Bengali';
+$conf['countries']['BO']='Tibetan';
+$conf['countries']['BR']='Breton';
+$conf['countries']['CA']='Catalan';
+$conf['countries']['CO']='Corsican';
+$conf['countries']['CS']='Czech';
+$conf['countries']['CY']='Welsh';
+$conf['countries']['DA']='Danish';
+$conf['countries']['DE']='German';
+$conf['countries']['DZ']='Bhutani';
+$conf['countries']['EL']='Greek';
+$conf['countries']['EN']='English';
+$conf['countries']['EO']='Esperanto';
+$conf['countries']['ES']='Spanish';
+$conf['countries']['ET']='Estonian';
+$conf['countries']['EU']='Basque';
+$conf['countries']['FA']='Persian';
+$conf['countries']['FI']='Finnish';
+$conf['countries']['FJ']='Fiji';
+$conf['countries']['FO']='Faeroese';
+$conf['countries']['FR']='French';
+$conf['countries']['FY']='Frisian';
+$conf['countries']['GA']='Irish';
+$conf['countries']['GD']='Gaelic';
+$conf['countries']['GL']='Galician';
+$conf['countries']['GN']='Guarani';
+$conf['countries']['GU']='Gujarati';
+$conf['countries']['HA']='Hausa';
+$conf['countries']['HI']='Hindi';
+$conf['countries']['HR']='Croatian';
+$conf['countries']['HU']='Hungarian';
+$conf['countries']['HY']='Armenian';
+$conf['countries']['IA']='Interlingua';
+$conf['countries']['IE']='Interlingue';
+$conf['countries']['IK']='Inupiak';
+$conf['countries']['IN']='Indonesian';
+$conf['countries']['IS']='Icelandic';
+$conf['countries']['IT']='Italian';
+$conf['countries']['IW']='Hebrew';
+$conf['countries']['JA']='Japanese';
+$conf['countries']['JI']='Yiddish';
+$conf['countries']['JW']='Javanese';
+$conf['countries']['KA']='Georgian';
+$conf['countries']['KK']='Kazakh';
+$conf['countries']['KL']='Greenlandic';
+$conf['countries']['KM']='Cambodian';
+$conf['countries']['KN']='Kannada';
+$conf['countries']['KO']='Korean';
+$conf['countries']['KS']='Kashmiri';
+$conf['countries']['KU']='Kurdish';
+$conf['countries']['KY']='Kirghiz';
+$conf['countries']['LA']='Latin';
+$conf['countries']['LN']='Lingala';
+$conf['countries']['LO']='Laothian';
+$conf['countries']['LT']='Lithuanian';
+$conf['countries']['LV']='Latvian';
+$conf['countries']['MG']='Malagasy';
+$conf['countries']['MI']='Maori';
+$conf['countries']['MK']='Macedonian';
+$conf['countries']['ML']='Malayalam';
+$conf['countries']['MN']='Mongolian';
+$conf['countries']['MO']='Moldavian';
+$conf['countries']['MR']='Marathi';
+$conf['countries']['MS']='Malay';
+$conf['countries']['MT']='Maltese';
+$conf['countries']['MY']='Burmese';
+$conf['countries']['NA']='Nauru';
+$conf['countries']['NE']='Nepali';
+$conf['countries']['NL']='Dutch';
+$conf['countries']['_NO']='Norwegian';
+$conf['countries']['OC']='Occitan';
+$conf['countries']['OM']='Oromo';
+$conf['countries']['OR']='Oriya';
+$conf['countries']['PA']='Punjabi';
+$conf['countries']['PL']='Polish';
+$conf['countries']['PS']='Pashto';
+$conf['countries']['PT']='Portuguese';
+$conf['countries']['QU']='Quechua';
+$conf['countries']['RM']='Rhaeto-Romance';
+$conf['countries']['RN']='Kirundi';
+$conf['countries']['RO']='Romanian';
+$conf['countries']['RU']='Russian';
+$conf['countries']['RW']='Kinyarwanda';
+$conf['countries']['SA']='Sanskrit';
+$conf['countries']['SD']='Sindhi';
+$conf['countries']['SG']='Sangro';
+$conf['countries']['SH']='Serbo-Croatian';
+$conf['countries']['SI']='Singhalese';
+$conf['countries']['SK']='Slovak';
+$conf['countries']['SL']='Slovenian';
+$conf['countries']['SM']='Samoan';
+$conf['countries']['SN']='Shona';
+$conf['countries']['SO']='Somali';
+$conf['countries']['SQ']='Albanian';
+$conf['countries']['SR']='Serbian';
+$conf['countries']['SS']='Siswati';
+$conf['countries']['ST']='Sesotho';
+$conf['countries']['SU']='Sudanese';
+$conf['countries']['SV']='Swedish';
+$conf['countries']['SW']='Swahili';
+$conf['countries']['TA']='Tamil';
+$conf['countries']['TE']='Tegulu';
+$conf['countries']['TG']='Tajik';
+$conf['countries']['TH']='Thai';
+$conf['countries']['TI']='Tigrinya';
+$conf['countries']['TK']='Turkmen';
+$conf['countries']['TL']='Tagalog';
+$conf['countries']['TN']='Setswana';
+$conf['countries']['TO']='Tonga';
+$conf['countries']['TR']='Turkish';
+$conf['countries']['TS']='Tsonga';
+$conf['countries']['TT']='Tatar';
+$conf['countries']['TW']='Twi';
+$conf['countries']['UK']='Ukrainian';
+$conf['countries']['UR']='Urdu';
+$conf['countries']['UZ']='Uzbek';
+$conf['countries']['VI']='Vietnamese';
+$conf['countries']['VO']='Volapuk';
+$conf['countries']['WO']='Wolof';
+$conf['countries']['XH']='Xhosa';
+$conf['countries']['YO']='Yoruba';
+$conf['countries']['ZH']='Chinese';
+
+$conf['database'] = array();
+
+$conf['database-default']=array();
+$conf['database-default']['defaults']=array();
+$conf['database-default']['defaults']['prefix' ] = '';
+$conf['database-default']['defaults']['suffix' ] = '';
+$conf['database-default']['defaults']['enabled' ] = true;
+$conf['database-default']['defaults']['name' ] = '';
+$conf['database-default']['defaults']['description'] = '';
+$conf['database-default']['defaults']['type' ] = 'pdo';
+$conf['database-default']['defaults']['dsn' ] = 'mysql:localhost';
+$conf['database-default']['defaults']['user' ] = '';
+$conf['database-default']['defaults']['password' ] = '';
+$conf['database-default']['defaults']['host' ] = '';
+$conf['database-default']['defaults']['database' ] = '';
+$conf['database-default']['defaults']['base64' ] = false;
+$conf['database-default']['defaults']['persistent' ] = true;
+$conf['database-default']['defaults']['charset' ] = 'UTF-8';
+$conf['database-default']['defaults']['connection_sql'] = '';
+$conf['database-default']['defaults']['cmd' ] = '';
+$conf['database-default']['defaults']['prepare' ] = true;
+$conf['database-default']['defaults']['transaction'] = true;
+$conf['database-default']['defaults']['update' ] = array();
+$conf['database-default']['defaults']['auto_update'] = true;
+$conf['date'] = array();
+$conf['date']['format'] = array();
+$conf['date']['format']['SHORT']= "";
+$conf['date']['format']['ISO8601SHORT']= "Ymd";
+$conf['date']['format']['ISO8601']= "Y-m-d";
+$conf['date']['format']['ISO8601BAS']= "YmdTHis";
+$conf['date']['format']['ISO8601EXT']= "Y-m-dTH:i:s";
+$conf['date']['format']['ISO8601FULL']= "Y-m-dTH:i:sO";
+$conf['date']['format']['ISO8601WEEK']= "YWW";
+$conf['date']['format']['GER1']= "d.m.Y";
+$conf['date']['format']['GER2']= "d.m.Y, H:i";
+$conf['date']['format']['GER3']= "d.m.Y, H:i:s";
+$conf['date']['format']['GER4']= "d. F Y, H:i:s";
+$conf['date']['format']['ENGLONG']= "l dS of F Y h:i:s A";
+$conf['date']['format']['GMDATE']= "D, d M Y H:i:s GMT";
+$conf['date']['format']['RFC822']= "r";
+$conf['date']['format']['UNIX']= "U";
+$conf['date']['format']['LONG']= "F j, Y, g:i a";
+$conf['date']['timezone'] = array();
+$conf['date']['timezone']['-6']="New York";
+$conf['date']['timezone']['0']="UTC (GMT)";
+$conf['date']['timezone']['60']="MET (Middle European Time)";
+$conf['date']['timezone']['120']="MEST (Middle European Summertime)";
+$conf['editor'] = array();
+$conf['editor']['text-markup'] = array();
+$conf['editor']['text-markup']['strong-begin']= "*";
+$conf['editor']['text-markup']['strong-end']= "*";
+$conf['editor']['text-markup']['emphatic-begin']= "_";
+$conf['editor']['text-markup']['emphatic-end']= "_";
+$conf['editor']['text-markup']['image-begin']= "{";
+$conf['editor']['text-markup']['image-end']= "}";
+$conf['editor']['text-markup']['speech-begin']='QUOTE';
+$conf['editor']['text-markup']['speech-end']='QUOTE';
+$conf['editor']['text-markup']['code-begin']= "=";
+$conf['editor']['text-markup']['code-end']= "=";
+$conf['editor']['text-markup']['footnote-begin']= "[";
+$conf['editor']['text-markup']['footnote-end']= "]";
+$conf['editor']['text-markup']['pre-begin']= "=";
+$conf['editor']['text-markup']['pre-end']= "=";
+$conf['editor']['text-markup']['insert-begin']= "++";
+$conf['editor']['text-markup']['insert-end']= "++";
+$conf['editor']['text-markup']['remove-begin']= "--";
+$conf['editor']['text-markup']['remove-end']= "--";
+$conf['editor']['text-markup']['definition-sep']= "::";
+$conf['editor']['text-markup']['headline']= "+";
+$conf['editor']['text-markup']['headline_level1_underline']= "=";
+$conf['editor']['text-markup']['headline_level2_underline']= "-";
+$conf['editor']['text-markup']['headline_level3_underline']= ".";
+$conf['editor']['text-markup']['list-unnumbered']= "-";
+$conf['editor']['text-markup']['list-numbered']= "#";
+$conf['editor']['text-markup']['table-of-content']= "##TOC##";
+$conf['editor']['text-markup']['linkto']= "->";
+$conf['editor']['text-markup']['table-cell-sep']= "|";
+$conf['editor']['text-markup']['style-begin']= "'";
+$conf['editor']['text-markup']['style-end']= "'";
+$conf['editor']['text-markup']['quote']= ">";
+$conf['editor']['text-markup']['quote-line-begin']= ">";
+$conf['editor']['text-markup']['quote-line-end']= ">";
+$conf['editor']['text-markup']['macro-begin']= "<<";
+$conf['editor']['text-markup']['macro-end']= ">>";
+$conf['editor']['text-markup']['macro-attribute-quote']= "'";
+$conf['editor']['text-markup']['macro-attribute-value-seperator']= "=";
+$conf['editor']['html'] = array();
+$conf['editor']['html']['tag_strong']= "strong";
+$conf['editor']['html']['tag_emphatic']= "em";
+$conf['editor']['html']['tag_teletype']= "tt";
+$conf['editor']['html']['tag_speech']= "cite";
+$conf['editor']['html']['override_speech']=false;
+$conf['editor']['html']['override_speech_open']= "«";
+$conf['editor']['html']['override_speech_close']= "»";
+$conf['editor']['html']['rendermode']="sgml";
+$conf['editor']['html']['rendermode']="xml";
+$conf['editor']['html']['replace']= "EUR:€";
+$conf['editor']['wiki'] = array();
+$conf['editor']['wiki']['convert_html']=true;
+$conf['editor']['wiki']['convert_bbcode']=true;
+$conf['editor']['text'] = array();
+$conf['editor']['text']['linelength']='70';
+$conf['editor']['calendar'] = array();
+$conf['editor']['calendar']['weekday_offset']='1';
+$conf['editor']['text'] = array();
+$conf['editor']['text']['linelength']='70';
+$conf['editor']['macro'] = array();
+$conf['editor']['macro']['show_errors']=false;
+$conf['filename'] = array();
+$conf['filename']['edit']=true;
+$conf['filename']['default']='index';
+$conf['filename']['style']='short';
+$conf['filename']['url']='relative';
+$conf['ftp'] = array();
+$conf['ftp']['ascii']= "html,htm,php";
+$conf['help'] = array();
+$conf['help']['enabled']=true;
+$conf['help']['url']="http://help.openrat.de/";
+$conf['help']['suffix']=".html";
+$conf['html'] = array();
+$conf['html']['tag_teletype']='tt';
+$conf['html']['tag_emphatic']='em';
+$conf['html']['tag_strong']='strong';
+$conf['html']['tag_speech']='cite';
+$conf['html']['speech_open']= "&bdquo";
+$conf['html']['speech_close']= "&rdquo";
+$conf['i18n'] = array();
+$conf['i18n']['use_http']=true;
+$conf['i18n']['default']='de';
+$conf['i18n']['available']='de,en,es,fr,it,ru,cn';
+$conf['i18n']['locale'] = array();
+$conf['i18n']['locale']['de']="de_DE.utf8";
+$conf['i18n']['locale']['en']="en_US.utf8";
+$conf['image'] = array();
+$conf['image']['truecolor']=true;
+$conf['interface'] = array();
+$conf['interface']['tree_width']= "25%";
+$conf['interface']['file_separator']= " »";
+$conf['interface']['nice_urls']=false;
+$conf['interface']['url_sessionid']=false;
+$conf['interface']['theme']= 'default';
+$conf['interface']['timeout']='0';
+$conf['interface']['override_title']='';
+$conf['interface']['style'] = array();
+$conf['interface']['style']['default']='modern';
+$conf['interface']['config'] = array();
+$conf['interface']['config']['file_manager_url']="";
+$conf['interface']['config']['enable']=true;
+$conf['interface']['config']['show_system']=true;
+$conf['interface']['config']['show_interpreter']=true;
+$conf['interface']['config']['show_extensions']=true;
+$conf['interface']['frames'] = array();
+$conf['interface']['frames']['top']='_top';
+$conf['interface']['url'] = array();
+$conf['interface']['url']['fake_url']=false;
+$conf['interface']['url']['index']=false;
+$conf['interface']['url']['url_format']= "%s,%s.%i";
+$conf['interface']['url']['url_format']= "%s,%s,%d.do";
+$conf['interface']['url']['add_sessionid']=false;
+$conf['interface']['gravatar'] = array();
+$conf['interface']['gravatar']['enable']=true;
+$conf['interface']['gravatar']['size']='80';
+$conf['interface']['gravatar']['default']='404';
+$conf['interface']['gravatar']['rating']='g';
+$conf['interface']['session'] = array();
+$conf['interface']['session']['auto_extend']=true;
+$conf['ldap'] = array();
+$conf['ldap']['host']="localhost";
+$conf['ldap']['port']="389";
+$conf['ldap']['protocol']="2";
+$conf['ldap']['dn']= "uid={user},ou=users,dc=example,dc=com";
+$conf['ldap']['dn']= "";
+$conf['ldap']['search'] = array();
+$conf['ldap']['search']['anonymous']=true;
+$conf['ldap']['search']['user']= "uid=openrat,ou=users,dc=example,dc=com";
+$conf['ldap']['search']['password']= "verysecret";
+$conf['ldap']['search']['basedn']= "dc=example,dc=com";
+$conf['ldap']['search']['filter']= "(uid={user})";
+$conf['ldap']['search']['aliases']=true;
+$conf['ldap']['search']['timeout']= 30;
+$conf['ldap']['search']['add']=true;
+$conf['ldap']['authorize'] = array();
+$conf['ldap']['authorize']['group_filter']="(memberUid={dn})";
+$conf['ldap']['authorize']['group_name']="cn";
+$conf['ldap']['authorize']['auto_add']=true;
+$conf['login'] = array();
+$conf['login']['motd']='';
+$conf['login']['nologin']=false;
+$conf['login']['register']=false;
+$conf['login']['send_password']=false;
+$conf['login']['gpl'] = array();
+$conf['login']['gpl']['url']="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html";
+$conf['login']['logo'] = array();
+$conf['login']['logo']['enabled']=false;
+$conf['login']['logo']['image']="./themes/default/images/logo.jpg" ;
+$conf['login']['logo']['url']="http://www.openrat.de" ;
+$conf['login']['start'] = array();
+$conf['login']['start']['start_lastchanged_object']=true;
+$conf['login']['start']['start_single_project']=true;
+$conf['login']['default-database']='db';
+$conf['log'] = array();
+$conf['log']['file']= null;
+$conf['log']['level']= "warn";
+$conf['log']['date_format']= "M j H:i:s";
+$conf['log']['ns_lookup']=false;
+$conf['log']['format']= "%time %level %host %user %action %text";
+$conf['mail'] = array();
+$conf['mail']['enabled']=true;
+$conf['mail']['from']="OpenRat <user@example.com>";
+$conf['mail']['signature']="http://www.openrat.de";
+$conf['mail']['cc']='0';
+$conf['mail']['bcc']='0';
+$conf['mail']['priority']='3';
+$conf['mail']['header_encoding']="Quoted-printable";
+$conf['mail']['client']='smtp';
+$conf['mail']['client']='php';
+$conf['mail']['whitelist']= "";
+$conf['mail']['blacklist']= "";
+$conf['mail']['smtp'] = array();
+$conf['mail']['smtp']['host']="mail.yourdomain.example";
+$conf['mail']['smtp']['host']="locahost";
+$conf['mail']['smtp']['port']="25";
+$conf['mail']['smtp']['auth_username']="your.user@something.example";
+$conf['mail']['smtp']['auth_password']="notsecret";
+$conf['mail']['smtp']['timeout']="45";
+$conf['mail']['smtp']['localhost']='0';
+$conf['mail']['smtp']['localhost']="your.fully.qualified.hostname.example";
+$conf['mail']['smtp']['tls']=false;
+$conf['mail']['smtp']['ssl']=false;
+
+$conf['mime-types'] = array();
+$conf['mime-types']['ez'] = 'application/andrew-inset';
+$conf['mime-types']['csm'] = 'application/cu-seeme';
+$conf['mime-types']['cu'] = 'application/cu-seeme';
+$conf['mime-types']['tsp'] = 'application/dsptype';
+$conf['mime-types']['spl'] = 'application/futuresplash';
+$conf['mime-types']['cpt'] = 'application/mac-compactpro';
+$conf['mime-types']['hqx'] = 'application/mac-binhex40';
+$conf['mime-types']['nb'] = 'application/mathematica';
+$conf['mime-types']['mdb'] = 'application/msaccess';
+$conf['mime-types']['doc'] = 'application/msword';
+$conf['mime-types']['dot'] = 'application/msword';
+$conf['mime-types']['bin'] = 'application/octet-stream';
+$conf['mime-types']['oda'] = 'application/oda';
+$conf['mime-types']['pdf'] = 'application/pdf';
+$conf['mime-types']['pgp'] = 'application/pgp-signature';
+$conf['mime-types']['ps'] = 'application/postscript';
+$conf['mime-types']['ai'] = 'application/postscript';
+$conf['mime-types']['eps'] = 'application/postscript';
+$conf['mime-types']['rtf'] = 'application/rtf';
+$conf['mime-types']['smi'] = 'application/smil';
+$conf['mime-types']['smil'] = 'application/smil';
+$conf['mime-types']['xls'] = 'application/vnd.ms-excel';
+$conf['mime-types']['xlb'] = 'application/vnd.ms-excel';
+$conf['mime-types']['ppt'] = 'application/vnd.ms-powerpoint';
+$conf['mime-types']['pps'] = 'application/vnd.ms-powerpoint';
+$conf['mime-types']['pot'] = 'application/vnd.ms-powerpoint';
+$conf['mime-types']['sdw'] = 'application/vnd.stardivision.writer';
+$conf['mime-types']['sgl'] = 'application/vnd.stardivision.writer-global';
+$conf['mime-types']['vor'] = 'application/vnd.stardivision.writer';
+$conf['mime-types']['sdc'] = 'application/vnd.stardivision.calc';
+$conf['mime-types']['sda'] = 'application/vnd.stardivision.draw';
+$conf['mime-types']['sdd'] = 'application/vnd.stardivision.impress';
+$conf['mime-types']['sdp'] = 'application/vnd.stardivision.impress-packed';
+$conf['mime-types']['smf'] = 'application/vnd.stardivision.math';
+$conf['mime-types']['sds'] = 'application/vnd.stardivision.chart';
+$conf['mime-types']['smd'] = 'application/vnd.stardivision.mail';
+$conf['mime-types']['wbxml'] = 'application/vnd.wap.wbxml ';
+$conf['mime-types']['wmlc'] = 'application/vnd.wap.wmlc';
+$conf['mime-types']['wmlsc'] = 'application/vnd.wap.wmlscriptc';
+$conf['mime-types']['wp5'] = 'application/wordperfect5.1';
+$conf['mime-types']['zip'] = 'application/zip';
+$conf['mime-types']['wk'] = 'application/x-123';
+$conf['mime-types']['bcpio'] = 'application/x-bcpio';
+$conf['mime-types']['vcd'] = 'application/x-cdlink ';
+$conf['mime-types']['pgn'] = 'application/x-chess-pgn';
+$conf['mime-types']['cpio'] = 'application/x-cpio';
+$conf['mime-types']['csh'] = 'application/x-csh';
+$conf['mime-types']['deb'] = 'application/x-debian-package';
+$conf['mime-types']['dcr'] = 'application/x-director';
+$conf['mime-types']['dir'] = 'application/x-director';
+$conf['mime-types']['dxr'] = 'application/x-director';
+$conf['mime-types']['wad'] = 'application/x-doom';
+$conf['mime-types']['dms'] = 'application/x-dms';
+$conf['mime-types']['dvi'] = 'application/x-dvi';
+$conf['mime-types']['pfa'] = 'application/x-font';
+$conf['mime-types']['pfb'] = 'application/x-font';
+$conf['mime-types']['gsf'] = 'application/x-font';
+$conf['mime-types']['pcf'] = 'application/x-font';
+$conf['mime-types']['spl'] = 'application/x-futuresplash ';
+$conf['mime-types']['gnumeric'] = 'application/x-gnumeric';
+$conf['mime-types']['gtar'] = 'application/x-gtar';
+$conf['mime-types']['tgz'] = 'application/x-gtar';
+$conf['mime-types']['taz'] = 'application/x-gtar';
+$conf['mime-types']['hdf'] = 'application/x-hdf';
+$conf['mime-types']['phtml'] = 'text/html';
+$conf['mime-types']['pht'] = 'text/html';
+$conf['mime-types']['php'] = 'text/html';
+$conf['mime-types']['phps'] = 'text/html';
+$conf['mime-types']['php3'] = 'text/html';
+$conf['mime-types']['php3p'] = 'text/html ';
+$conf['mime-types']['php4'] = 'text/html';
+$conf['mime-types']['docbook'] = 'application/docbook+xml';
+$conf['mime-types']['ica'] = 'application/x-ica';
+$conf['mime-types']['jar'] = 'application/x-java-archive';
+$conf['mime-types']['jnlp'] = 'application/x-java-jnlp-file';
+$conf['mime-types']['ser'] = 'application/x-java-serialized-object';
+$conf['mime-types']['class'] = 'application/x-java-vm';
+$conf['mime-types']['js'] = 'application/x-javascript';
+$conf['mime-types']['chrt'] = 'application/x-kchart';
+$conf['mime-types']['kil'] = 'application/x-killustrator';
+$conf['mime-types']['kpr'] = 'application/x-kpresenter';
+$conf['mime-types']['kpt'] = 'application/x-kpresenter';
+$conf['mime-types']['skp'] = 'application/x-koan ';
+$conf['mime-types']['skd'] = 'application/x-koan ';
+$conf['mime-types']['skt'] = 'application/x-koan ';
+$conf['mime-types']['skm'] = 'application/x-koan ';
+$conf['mime-types']['ksp'] = 'application/x-kspread';
+$conf['mime-types']['kwd'] = 'application/x-kword';
+$conf['mime-types'][' kwt'] = 'application/x-kword';
+$conf['mime-types']['latex'] = 'application/x-latex';
+$conf['mime-types']['lha'] = 'application/x-lha';
+$conf['mime-types']['lzh'] = 'application/x-lzh';
+$conf['mime-types']['lzx'] = 'application/x-lzx';
+$conf['mime-types']['frm'] = 'fbdocapplication/x-maker';
+$conf['mime-types']['maker'] = 'fbdocapplication/x-maker';
+$conf['mime-types']['frame'] = 'fbdocapplication/x-maker';
+$conf['mime-types']['fm'] = 'fbdocapplication/x-maker';
+$conf['mime-types']['fb'] = 'fbdocapplication/x-maker';
+$conf['mime-types']['book'] = 'fbdocapplication/x-maker';
+$conf['mime-types']['mif'] = 'application/x-mif';
+$conf['mime-types']['com'] = 'application/x-msdos-program';
+$conf['mime-types']['exe'] = 'application/x-msdos-program';
+$conf['mime-types']['bat'] = 'application/x-msdos-program';
+$conf['mime-types']['dll'] = 'application/x-msdos-program';
+$conf['mime-types']['msi'] = 'application/x-msi';
+$conf['mime-types']['nc'] = 'application/x-netcdf';
+$conf['mime-types']['cdf'] = 'application/x-netcdf';
+$conf['mime-types']['pac'] = 'application/x-ns-proxy-autoconfig';
+$conf['mime-types']['o'] = 'application/x-object';
+$conf['mime-types']['ogg'] = 'application/x-ogg';
+$conf['mime-types']['oza'] = 'application/x-oz-application';
+$conf['mime-types']['pl'] = 'application/x-perl';
+$conf['mime-types']['pm'] = 'application/x-perl';
+$conf['mime-types']['crl'] = 'application/x-pkcs7-crl';
+$conf['mime-types']['rpm'] = 'application/x-redhat-package-manager';
+$conf['mime-types']['shar'] = 'application/x-shar';
+$conf['mime-types']['swf'] = 'application/x-shockwave-flash';
+$conf['mime-types']['swfl'] = 'application/x-shockwave-flash';
+$conf['mime-types']['sh'] = 'application/x-sh ';
+$conf['mime-types']['sit'] = 'application/x-stuffit';
+$conf['mime-types']['sv4cpio'] = 'application/x-sv4cpio';
+$conf['mime-types']['sv4crc'] = 'application/x-sv4crc';
+$conf['mime-types']['tar'] = 'application/x-tar';
+$conf['mime-types']['tcl'] = 'application/x-tcl';
+$conf['mime-types']['tex'] = 'application/x-tex';
+$conf['mime-types']['gf'] = 'application/x-tex-gf';
+$conf['mime-types']['pk'] = 'application/x-tex-pk';
+$conf['mime-types']['texinfo'] = 'application/x-texinfo';
+$conf['mime-types']['texi'] = 'application/x-texinfo';
+$conf['mime-types']['; "~"'] = 'application/x-trash';
+$conf['mime-types'][';"%"'] = 'application/x-trash';
+$conf['mime-types']['bak'] = 'application/x-trash';
+$conf['mime-types']['old'] = 'application/x-trash';
+$conf['mime-types']['sik'] = 'application/x-trash';
+$conf['mime-types']['t'] = 'application/x-troff';
+$conf['mime-types']['tr'] = 'application/x-troff';
+$conf['mime-types']['roff'] = 'application/x-troff';
+$conf['mime-types']['man'] = 'application/x-troff-man';
+$conf['mime-types']['me'] = 'application/x-troff-me';
+$conf['mime-types']['ms'] = 'application/x-troff-ms';
+$conf['mime-types']['ustar'] = 'application/x-ustar';
+$conf['mime-types']['src'] = 'application/x-wais-source';
+$conf['mime-types']['wz'] = 'application/x-wingz';
+$conf['mime-types']['crt'] = 'application/x-x509-ca-cert';
+$conf['mime-types']['fig'] = 'application/x-xfig';
+$conf['mime-types']['au'] = 'audio/basic';
+$conf['mime-types']['snd'] = 'audio/basic';
+$conf['mime-types']['mid'] = 'audio/midi';
+$conf['mime-types']['midi'] = 'audio/midi';
+$conf['mime-types']['kar'] = 'audio/midi';
+$conf['mime-types']['mpga'] = 'audio/mpeg';
+$conf['mime-types']['mpega'] = 'audio/mpeg';
+$conf['mime-types']['mp2'] = 'audio/mpeg';
+$conf['mime-types']['mp3'] = 'audio/mpeg';
+$conf['mime-types']['m3u'] = 'audio/mpegurl';
+$conf['mime-types']['sid'] = 'audio/prs.sid';
+$conf['mime-types']['aif'] = 'audio/x-aiff';
+$conf['mime-types']['aiff'] = 'audio/x-aiff';
+$conf['mime-types']['aifc'] = 'audio/x-aiff';
+$conf['mime-types']['gsm'] = 'audio/x-gsm';
+$conf['mime-types']['m3u'] = 'audio/x-mpegurl';
+$conf['mime-types']['rpm'] = 'audio/x-pn-realaudio-plugin ';
+$conf['mime-types']['ra'] = 'audio/x-pn-realaudio';
+$conf['mime-types']['rm'] = 'audio/x-pn-realaudio';
+$conf['mime-types']['ram'] = 'audio/x-pn-realaudio';
+$conf['mime-types']['ra'] = 'audio/x-realaudio ';
+$conf['mime-types']['pls'] = 'audio/x-scpls';
+$conf['mime-types']['wav'] = 'audio/x-wav';
+$conf['mime-types']['pdb'] = 'chemical/x-pdb';
+$conf['mime-types']['xyz'] = 'chemical/x-xyz ';
+$conf['mime-types']['bmp'] = 'image/bmp';
+$conf['mime-types']['gif'] = 'image/gif';
+$conf['mime-types']['ief'] = 'image/ief';
+$conf['mime-types']['jpeg'] = 'image/jpeg';
+$conf['mime-types']['jpg'] = 'image/jpeg';
+$conf['mime-types']['jpe'] = 'image/jpeg';
+$conf['mime-types']['pcx'] = 'image/pcx';
+$conf['mime-types']['png'] = 'image/png';
+$conf['mime-types']['svg'] = 'image/svg+xml';
+$conf['mime-types']['svgz'] = 'image/svg+xml';
+$conf['mime-types']['tiff'] = 'image/tiff';
+$conf['mime-types']['tif'] = 'image/tiff';
+$conf['mime-types']['wbmp'] = 'image/vnd.wap.wbmp';
+$conf['mime-types']['ras'] = 'image/x-cmu-raster';
+$conf['mime-types']['cdr'] = 'image/x-coreldraw';
+$conf['mime-types']['pat'] = 'image/x-coreldrawpattern';
+$conf['mime-types']['cdt'] = 'image/x-coreldrawtemplate';
+$conf['mime-types']['cpt'] = 'image/x-corelphotopaint';
+$conf['mime-types']['djvu'] = 'image/x-djvu';
+$conf['mime-types']['djv'] = 'image/x-djvu';
+$conf['mime-types']['jng'] = 'image/x-jng';
+$conf['mime-types']['bmp'] = 'image/x-ms-bmp';
+$conf['mime-types']['pnm'] = 'image/x-portable-anymap';
+$conf['mime-types']['pbm'] = 'image/x-portable-bitmap';
+$conf['mime-types']['pgm'] = 'image/x-portable-graymap';
+$conf['mime-types']['ppm'] = 'image/x-portable-pixmap';
+$conf['mime-types']['rgb'] = 'image/x-rgb';
+$conf['mime-types']['xbm'] = 'image/x-xbitmap';
+$conf['mime-types']['xpm'] = 'image/x-xpixmap';
+$conf['mime-types']['xwd'] = 'image/x-xwindowdump';
+$conf['mime-types']['igs'] = 'model/iges';
+$conf['mime-types']['iges'] = 'model/iges';
+$conf['mime-types']['msh'] = 'model/mesh';
+$conf['mime-types']['mesh'] = 'model/mesh';
+$conf['mime-types']['silo'] = 'model/mesh';
+$conf['mime-types']['wrl'] = 'model/vrml';
+$conf['mime-types']['vrml'] = 'model/vrml';
+$conf['mime-types']['csv'] = 'text/comma-separated-values';
+$conf['mime-types']['css'] = 'text/css';
+$conf['mime-types']['htm'] = 'text/html';
+$conf['mime-types']['html'] = 'text/html';
+$conf['mime-types']['xhtml'] = 'text/html';
+$conf['mime-types']['mml'] = 'text/mathml';
+$conf['mime-types']['asc'] = 'text/plain';
+$conf['mime-types']['txt'] = 'text/plain';
+$conf['mime-types']['text'] = 'text/plain';
+$conf['mime-types']['diff'] = 'text/plain';
+$conf['mime-types']['rtx'] = 'text/richtext';
+$conf['mime-types']['rtf'] = 'text/rtf';
+$conf['mime-types']['tsv'] = 'text/tab-separated-values';
+$conf['mime-types']['wml'] = 'text/vnd.wap.wml';
+$conf['mime-types']['wmls'] = 'text/vnd.wap.wmlscript';
+$conf['mime-types']['xml'] = 'text/xml';
+$conf['mime-types']['xsl'] = 'text/xml';
+$conf['mime-types']['hpp'] = 'text/x-c++hdr';
+$conf['mime-types']['hxx'] = 'text/x-c++hdr';
+$conf['mime-types']['hh'] = 'text/x-c++hdr';
+$conf['mime-types']['cpp'] = 'text/x-c++src';
+$conf['mime-types']['cxx'] = 'text/x-c++src';
+$conf['mime-types']['cc'] = 'text/x-c++src';
+$conf['mime-types']['h'] = 'text/x-chdr';
+$conf['mime-types']['csh'] = 'text/x-csh';
+$conf['mime-types']['c'] = 'text/x-csrc';
+$conf['mime-types']['java'] = 'text/x-java';
+$conf['mime-types']['moc'] = 'text/x-moc';
+$conf['mime-types']['p'] = 'text/x-pascal';
+$conf['mime-types']['pas'] = 'text/x-pascal';
+$conf['mime-types']['etx'] = 'text/x-setext';
+$conf['mime-types']['sh'] = 'text/x-sh';
+$conf['mime-types']['tcl'] = 'text/x-tcl';
+$conf['mime-types']['tk'] = 'text/x-tcl';
+$conf['mime-types']['tex'] = 'text/x-tex';
+$conf['mime-types']['ltx'] = 'text/x-tex';
+$conf['mime-types']['sty'] = 'text/x-tex';
+$conf['mime-types']['cls'] = 'text/x-tex';
+$conf['mime-types']['vcs'] = 'text/x-vcalendar';
+$conf['mime-types']['vcf'] = 'text/x-vcard';
+$conf['mime-types']['dl'] = 'video/dl';
+$conf['mime-types']['fli'] = 'video/fli';
+$conf['mime-types']['gl'] = 'video/gl';
+$conf['mime-types']['mpeg'] = 'video/mpeg';
+$conf['mime-types']['mpg'] = 'video/mpeg';
+$conf['mime-types']['mpe'] = 'video/mpeg';
+$conf['mime-types']['qt'] = 'video/quicktime';
+$conf['mime-types']['mov'] = 'video/quicktime';
+$conf['mime-types']['mxu'] = 'video/vnd.mpegurl';
+$conf['mime-types']['mng'] = 'video/x-mng';
+$conf['mime-types']['asf'] = 'video/x-ms-asf';
+$conf['mime-types']['asx'] = 'video/x-ms-asf';
+$conf['mime-types']['avi'] = 'video/x-msvideo';
+$conf['mime-types']['movie'] = 'video/x-sgi-movie';
+$conf['mime-types']['ice'] = 'x-conference/x-cooltalk';
+$conf['mime-types']['vrm'] = 'x-world/x-vrml';
+$conf['mime-types']['vrml'] = 'x-world/x-vrml';
+$conf['mime-types']['wrl'] = 'x-world/x-vrml';
+
+$conf['publish'] = array();
+$conf['publish']['edit']=true;
+$conf['publish']['default']='index';
+$conf['publish']['format']= "{filename}{language_sep}{language}{type_sep}{type}";
+$conf['publish']['language_sep']= ".";
+$conf['publish']['type_sep']= ".";
+$conf['publish']['filename_language']='auto';
+$conf['publish']['filename_type']='always';
+$conf['publish']['style']="id";
+$conf['publish']['url']='relative';
+$conf['publish']['url']='absolute';
+$conf['publish']['enable_php_in_page_content']=false;
+$conf['publish']['enable_php_in_file_content']=false;
+$conf['publish']['escape_8bit_characters']=false;
+$conf['publish']['encode_utf8_in_html']=true;
+$conf['publish']['negotiation'] = array();
+$conf['publish']['negotiation']['page_negotiate_type']=true;
+$conf['publish']['negotiation']['page_negotiate_language']=true;
+$conf['publish']['negotiation']['file_negotiate_type']=true;
+$conf['publish']['filesystem'] = array();
+$conf['publish']['filesystem']['per_project']=true;
+$conf['publish']['filesystem']['directory']='/var/www/';
+$conf['publish']['command'] = array();
+$conf['publish']['command']['per_project']=true;
+$conf['publish']['command']['enable']=false;
+$conf['publish']['command']['command']='';
+$conf['publish']['ftp'] = array();
+$conf['publish']['ftp']['enable']=true;
+$conf['publish']['ftp']['per_project']=true;
+$conf['publish']['ftp']['port']='21';
+$conf['publish']['ftp']['host']='';
+$conf['publish']['ftp']['path']='';
+$conf['publish']['ftp']['user']='anonymous';
+$conf['publish']['ftp']['pass']='mail@example.com';
+$conf['replace'] = array();
+$conf['replace']['']='0';
+$conf['replace']['']='0';
+$conf['replace']['euro']= "EUR,€";
+$conf['replace']['copy']= "(c),©";
+$conf['search'] = array();
+$conf['search']['']='0';
+$conf['search']['quicksearch'] = array();
+$conf['search']['quicksearch']['flag'] = array();
+$conf['search']['quicksearch']['flag']['id']=true;
+$conf['search']['quicksearch']['flag']['name']=true;
+$conf['search']['quicksearch']['flag']['filename']=true;
+$conf['search']['quicksearch']['flag']['description']=true;
+$conf['search']['quicksearch']['flag']['content']=false;
+$conf['security'] = array();
+$conf['security']['readonly']=false;
+$conf['security']['nopublish']=false;
+$conf['security']['umask']='0';
+$conf['security']['chmod']='0';
+$conf['security']['chmod_dir']='0';
+$conf['security']['']='0';
+$conf['security']['disable_dynamic_code']=true;
+$conf['security']['show_system_info']=true;
+$conf['security']['use_post_token']=true;
+$conf['security']['renew_session_login']=false;
+$conf['security']['renew_session_logout']=false;
+$conf['security']['default'] = array();
+$conf['security']['default']['username']='';
+$conf['security']['default']['password']='';
+$conf['security']['guest'] = array();
+$conf['security']['guest']['enable']=false;
+$conf['security']['guest']['user']='guest';
+$conf['security']['login'] = array();
+$conf['security']['login']['type']='form';
+$conf['security']['auth'] = array();
+$conf['security']['auth']['type']='database';
+$conf['security']['auth']['userdn']=false;
+$conf['security']['authorize'] = array();
+$conf['security']['authorize']['type']='database';
+$conf['security']['authorize']['type']='ldap';
+
+$conf['security']['modules'] = array();
+$conf['security']['modules']['autologin']='Remember,Guest,SingleSignon';
+$conf['security']['modules']['preselect']='Ident,SSL,Cookie';
+$conf['security']['modules']['authenticate']='LdapUserDN,Database,Internal';
+
+$conf['security']['newuser'] = array();
+$conf['security']['newuser']['autoadd'] = true;
+$conf['security']['newuser']['autogroups'] = "";
+
+$conf['security']['password'] = array();
+$conf['security']['password']['random_length']=10;
+$conf['security']['password']['min_length']=6;
+$conf['security']['password']['pepper']= '';
+$conf['security']['password']['deny_after_expiration_duration'] = 72;
+$conf['security']['password']['force_change_if_cleartext']= true;
+$conf['security']['http'] = array();
+$conf['security']['http']['url']= "http://example.net/restricted-area";
+$conf['security']['authdb'] = array();
+$conf['security']['authdb']['enable'] = false;
+$conf['security']['authdb']['type']='postgresql';
+$conf['security']['authdb']['user']='dbuser';
+$conf['security']['authdb']['password']='dbpassword';
+$conf['security']['authdb']['host']= '127.0.0.1';
+$conf['security']['authdb']['database']='dbname';
+$conf['security']['authdb']['persistent']=false;
+$conf['security']['authdb']['prepare']=false;
+$conf['security']['authdb']['sql']= "select 1 from table where user={username} and password={password}";
+$conf['security']['authdb']['hash_algo']='md5';
+$conf['security']['authdb']['add']=true;
+$conf['security']['ssl'] = array();
+$conf['security']['ssl']['trust']=false;
+$conf['security']['ssl']['client_cert_dn_env'] = 'SSL_CLIENT_S_DN_CN';
+$conf['security']['openid'] = array();
+$conf['security']['openid']['enable']=false;
+$conf['security']['openid']['add']=false;
+$conf['security']['openid']['logo_url']='0';
+$conf['security']['openid']['logo_url']="http://openid.net/login-bg.gif";
+$conf['security']['openid']['trust_root']='http://your.server.example/openrat/';
+$conf['security']['openid']['trust_root']='0';
+$conf['security']['openid']['trusted_server']='openid1.example.com,openid2.example.com';
+$conf['security']['openid']['trusted_server']='0';
+$conf['security']['openid']['update_user']=true;
+$conf['security']['openid']['user_identity']=true;
+$conf['security']['openid']['provider']['name']='google';
+$conf['security']['openid']['provider']['google']['xrds_uri']="http://google.com/accounts/o8/id";
+$conf['security']['openid']['provider']['google']['map_attribute']="email";
+$conf['security']['openid']['provider']['google']['name']="Google";
+$conf['security']['openid']['provider']['google']['map_internal']="mail";
+$conf['security']['openid']['provider']['yahoo']['xrds_uri']="http://??????";
+$conf['security']['openid']['provider']['yahoo']['map_attribute']="usename";
+$conf['security']['openid']['provider']['yahoo']['map_internal']="mail";
+$conf['security']['sso'] = array();
+$conf['security']['sso']['enable']=false;
+$conf['security']['sso']['url']="http://localhost/check.php?phpsessid={id}&check=true";
+$conf['security']['sso']['url']="https://www.example.com/phpmyadmin/main.php?server=1";
+$conf['security']['sso']['auth_param_name']='authid';
+$conf['security']['sso']['auth_param_serialized']=true;
+$conf['security']['sso']['cookie']=true;
+$conf['security']['sso']['cookie_name']='0';
+$conf['security']['sso']['force']=true;
+$conf['security']['sso']['expect']='0';
+$conf['security']['sso']['expect_regexp']="/running on/";
+$conf['security']['sso']['username_regexp']="/running on localhost as ([a-z]+)@localhost/";
+$conf['security']['logout'] = array();
+$conf['security']['logout']['redirect_url']="http://your.intranet.example/";
+$conf['security']['logout']['redirect_url']='0';
+$conf['security']['user'] = array();
+$conf['security']['user']['show_admin_mail']=true;
+$conf['security']['user']['show_mail']=true;
+$conf['security']['user']['send_message']=true;
+$conf['security']['content-security-policy']=true;
+
+$conf['style-default'] = array();
+$conf['style-default']['name']='Unnamed';
+$conf['style-default']['title_background_color']='grey';
+$conf['style-default']['title_text_color']='white';
+$conf['style-default']['text_color' ]= 'black';
+$conf['style-default']['background_color' ]= '#d9d9d9';
+$conf['style-default']['inactive_background_color' ]= 'silver';
+
+
+$conf['style'] = array();
+$conf['style']['earlgrey']=array();
+$conf['style']['earlgrey']['name']='Earl grey';
+$conf['style']['earlgrey']['title_background_color']='grey';
+$conf['style']['earlgrey']['title_text_color']='white';
+$conf['style']['earlgrey']['text_color'] ='black';
+$conf['style']['earlgrey']['background_color'] = '#e9e9e9';
+$conf['style']['earlgrey']['inactive_background_color'] = 'silver';
+
+// $conf['style']['system']=array();
+// $conf['style']['system']['name']='System colors';
+// $conf['style']['system']['title_background_color']='Menu';
+// $conf['style']['system']['title_text_color']='MenuText';
+// $conf['style']['system']['text_color'] ='WindowText';
+// $conf['style']['system']['background_color'] = 'Background';
+// $conf['style']['system']['inactive_background_color'] = 'WindowFrame';
+
+$conf['style']['modern']=array();
+$conf['style']['modern']['name']='Blue sky';
+$conf['style']['modern']['title_background_color']='#3F6194';
+$conf['style']['modern']['title_text_color']='white';
+$conf['style']['modern']['text_color'] ='black';
+$conf['style']['modern']['background_color'] = '#F3F3F3';
+$conf['style']['modern']['inactive_background_color'] = '#CCCCCC';
+
+$conf['style']['moorweide']=array();
+$conf['style']['moorweide']['name']='Moorweide';
+$conf['style']['moorweide']['title_background_color']='#006633';
+$conf['style']['moorweide']['title_text_color']='white';
+$conf['style']['moorweide']['text_color'] ='black';
+$conf['style']['moorweide']['background_color'] = '#F5FFFA';
+$conf['style']['moorweide']['inactive_background_color'] = '#CEE6DA';
+
+$conf['style']['dark']=array();
+$conf['style']['dark']['name']='Dark';
+$conf['style']['dark']['title_background_color']='#868685';
+$conf['style']['dark']['title_text_color']='#DCDCDC';
+$conf['style']['dark']['text_color'] ='#FFFFFF';
+$conf['style']['dark']['background_color'] = '#201F1D';
+$conf['style']['dark']['inactive_background_color'] = '#868685';
+
+$conf['theme'] = array();
+$conf['theme']['compiler'] = array();
+$conf['theme']['compiler']['enable']=true;
+$conf['theme']['compiler']['cache']=true;
+$conf['theme']['compiler']['chmod']='';
+$conf['theme']['compiler']['compile_at_logout']=false;
+$conf['wiki'] = array();
+$conf['wiki']['convert_html']=true;
+$conf['wiki']['convert_bbcode']=true;
+$conf['wiki']['tag_strong']= "*";
+$conf['wiki']['tag_emphatic']= "_";
+
+$conf['application']['name' ] = OR_TITLE;
+$conf['application']['version' ] = OR_VERSION;
+$conf['application']['operator'] = OR_TITLE;
+$conf['production']= true;
+
+?>+
\ No newline at end of file
diff --git a/modules/util/exception/OpenRatException.class.php b/modules/util/exception/OpenRatException.class.php
@@ -0,0 +1,25 @@
+<?php
+
+class OpenRatException extends Exception
+{
+ public $key;
+
+ // Die Exception neu definieren, damit die Mitteilung nicht optional ist
+ public function __construct($key, $message, $code = 0, Exception $previous = null) {
+
+ $this->key = $key;
+
+ // sicherstellen, dass alles korrekt zugewiesen wird
+ parent::__construct($message, $code, $previous);
+ }
+
+ // maßgeschneiderte Stringdarstellung des Objektes
+ public function __toString() {
+ return __CLASS__ . ": ".$this->key." [{$this->code}]: '{$this->message}' in {$this->file}({$this->line})\n"
+ . "{$this->getTraceAsString()}\n";
+ }
+
+}
+
+
+?>+
\ No newline at end of file
diff --git a/modules/util/exception/SecurityException.class.php b/modules/util/exception/SecurityException.class.php
@@ -0,0 +1,9 @@
+<?php
+
+class SecurityException extends RuntimeException
+{
+
+}
+
+
+?>+
\ No newline at end of file
diff --git a/modules/util/require.php b/modules/util/require.php
@@ -1,5 +1,33 @@
<?php
-//include( dirname(__FILE__) . '/.class.php');
+require_once( __DIR__.'/'."GlobalFunctions.class.".PHP_EXT );
+require_once( __DIR__.'/'."Http.class.".PHP_EXT );
+require_once( __DIR__.'/'."Html.class.".PHP_EXT );
+require_once( __DIR__.'/'."Text.class.".PHP_EXT );
+require_once( __DIR__.'/'."Mail.class.".PHP_EXT );
+require_once( __DIR__.'/'."Ldap.class.".PHP_EXT );
+require_once( __DIR__.'/'."FileUtils.class.".PHP_EXT );
+require_once( __DIR__.'/'."JSON.class.".PHP_EXT );
+require_once( __DIR__.'/'."Less.".PHP_EXT );
+require_once( __DIR__.'/'."JSqueeze.class.".PHP_EXT );
+require_once( __DIR__.'/'."Spyc.class.".PHP_EXT );
+require_once( __DIR__.'/'."exception/OpenRatException.class.".PHP_EXT );
+require_once( __DIR__.'/'."exception/SecurityException.class.".PHP_EXT );
+require_once( __DIR__.'/'."TreeElement.class.".PHP_EXT );
+require_once( __DIR__.'/'."AbstractTree.class.".PHP_EXT );
+require_once( __DIR__.'/'."AdministrationTree.class.".PHP_EXT );
+require_once( __DIR__.'/'."ProjectTree.class.".PHP_EXT );
+require_once( __DIR__.'/'."Publish.class.".PHP_EXT );
+require_once( __DIR__.'/'."Ftp.class.".PHP_EXT );
+require_once( __DIR__.'/'."Macro.class.".PHP_EXT );
+require_once( __DIR__.'/'."Dynamic.class.".PHP_EXT );
+require_once( __DIR__.'/'."Api.class.".PHP_EXT );
+require_once( __DIR__.'/'."Code.class.".PHP_EXT );
+require_once( __DIR__.'/'."Transformer.class.".PHP_EXT );
+require_once( __DIR__.'/'."Line.class.".PHP_EXT );
+require_once( __DIR__.'/'."Upload.class.".PHP_EXT );
+require_once( __DIR__.'/'."Upload.class.".PHP_EXT );
+require_once( __DIR__.'/'."ArchiveTar.class.".PHP_EXT );
+require_once( __DIR__.'/'."ArchiveUnzip.class.".PHP_EXT );
+require_once( __DIR__.'/'."ArchiveZip.class.".PHP_EXT );
-?>-
\ No newline at end of file
diff --git a/util/.htaccess b/util/.htaccess
@@ -1,2 +0,0 @@
-order deny,allow
-deny from all-
\ No newline at end of file
diff --git a/util/AbstractTree.class.php b/util/AbstractTree.class.php
@@ -1,171 +0,0 @@
-<?php
-// OpenRat Content Management System
-// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-
-/**
- * Darstellen einer Baumstruktur mit Funktion zum Laden, Oeffnen und Schliessen
- * von Teilbaeumen
- * @author $Author$
- * @version $Revision$
- * @package openrat.services
- */
-class AbstractTree
-{
- /**
- * Alle Elemente des Baumes
- */
- var $elements = array();
-
- var $tempElements = array();
- var $userIsAdmin = false;
-
- var $autoOpen = array(0,1);
-
- var $opened = array();
-
- /**
- * Hoechste Element-Id
- * @type Integer
- */
- var $maxId;
-
- // Konstruktor
- function AbstractTree()
- {
- // Feststellen, ob der angemeldete Benutzer ein Administrator ist
- $user = Session::getUser();
- $this->userIsAdmin = $user->isAdmin;
-
- // Wurzel-Element laden
- $this->root();
- $this->elements[0] = $this->tempElements[0];
- $this->tempElements = array();
- $this->maxId = 0;
-
- foreach( $this->autoOpen as $openId )
- $this->open($openId);
- }
-
- function refresh() {
-
- $this->elements = array();
-
- // Wurzel-Element laden
- $this->root();
- $this->elements[0] = $this->tempElements[0];
- $this->tempElements = array();
- $this->maxId = 0;
-
- $oids = $this->opened;
- $this->opened = array();
- foreach( $oids as $oid)
- {
- if ( isset($this->elements[$oid]) )
- $this->open($oid);
- }
- }
-
-
-
- function all() {
-
- $this->elements = array();
- $this->opened = array();
-
- // Wurzel-Element laden
- $this->root();
- $this->elements[0] = $this->tempElements[0];
- $this->tempElements = array();
- $this->maxId = 0;
-
- for( $eid=0;isset($this->elements[$eid]); $eid++)
- {
- $this->open($eid);
- }
- }
-
-
- /**
- * Oeffnen eines Teilbaumes. Es wird der eindeutige Name des zu oeffnenden Teilbaumes als
- * Parameter uebergeben
- * @param elementName der Name des Elementes, welches zu oeffnen ist
- */
- function open( $elementId )
- {
- $k = array_search($elementId,$this->opened);
- if ( $k !== false )
- return; // Ist schon offen. Evtl. Reload gedrückt?
-
- $this->opened[] = $elementId;
- if ( ! isset($this->elements[$elementId]) )
- return;
- $funcName = $this->elements[$elementId]->type;
- if ( empty($funcName) )
- return;
-
- $this->$funcName( $this->elements[$elementId]->internalId );
-
- // Wenn keine Unterelemente gefunden, dann die ?ffnen-Funktion deaktivieren
- if ( count( $this->tempElements ) == 0 )
- $this->elements[$elementId]->type = '';
-
- foreach( $this->tempElements as $treeElement )
- {
- $this->maxId++;
- $this->elements[$elementId]->subElementIds[] = $this->maxId;
- $this->elements[$this->maxId] = $treeElement;
- }
-
- if ( count($this->tempElements)==1 )
- {
- $this->tempElements = array();
- $this->open($this->maxId);
- }
-
- $this->tempElements = array();
- }
-
-
- /**
- * Schliessen eines Teilbaumes
- * @param elementName der Name des Elementes, welches zu schliessen ist
- */
-
- function close( $elementId )
- {
- $this->elements[$elementId]->subElementIds = array();
-
- $k = array_search($elementId,$this->opened);
- if ( $k !== false )
- unset($this->opened[$k]);
- }
-
-
- /**
- * Hinzufuegen eines Baum-Elementes
- * @param TreeElement Hinzuzufuegendes Baumelement
- */
- function addTreeElement( $treeElement )
- {
- $this->tempElements[] = $treeElement;
- }
-
-
-}
-
-?>-
\ No newline at end of file
diff --git a/util/AdministrationTree.class.php b/util/AdministrationTree.class.php
@@ -1,594 +0,0 @@
-<?php
-use cms\model\User;
-use cms\model\Project;
-use cms\model\Group;
-use cms\model\Folder;
-
-// OpenRat Content Management System
-// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-
-/**
- * Darstellen einer Baumstruktur mit Administrationfunktionen
- * @author $Author$
- * @version $Revision$
- * @package openrat.services
- */
-class AdministrationTree extends AbstractTree
-{
- /**
- * Alle Elemente des Baumes
- */
- var $elements;
- var $confCache = array();
-
- function root()
- {
- if ( !$this->userIsAdmin )
- Http::notAuthorized('Administration-Tree is only visible for admins.');
-
-// $treeElement = new TreeElement();
-// $treeElement->text = lang('GLOBAL_ADMINISTRATION');
-// $treeElement->description = lang('GLOBAL_ADMINISTRATION');
-// $treeElement->type = 'administration';
-// $treeElement->icon = 'administration';
-
- $this->administration();
-
- $this->autoOpen[] = 1;
- }
-
-
-
- function administration()
- {
- global $conf;
- $conf_config = $conf['interface']['config'];
-
- $treeElement = new TreeElement();
- $treeElement->id = 0;
- $treeElement->text = lang('GLOBAL_PROJECTS');
- $treeElement->description = lang('GLOBAL_PROJECTS');
- $treeElement->url = Html::url('projectlist','show',0,array(REQ_PARAM_TARGET=>'content'));
- $treeElement->action = 'projectlist';
- $treeElement->icon = 'projectlist';
- $treeElement->type = 'projects';
- $treeElement->target = 'cms_main';
-
- $this->addTreeElement( $treeElement );
-
-
- $treeElement = new TreeElement();
- $treeElement->text = lang('USER_AND_GROUPS');
- $treeElement->description = lang('USER_AND_GROUPS');
- $treeElement->icon = 'userlist';
- $treeElement->type = 'userandgroups';
-
- $this->addTreeElement( $treeElement );
-// $this->userandgroups(0);;
-
- if ( $conf_config['enable'] )
- {
- $treeElement = new TreeElement();
- $treeElement->text = lang('PREFERENCES');
- $treeElement->description = lang('PREFERENCES');
- $treeElement->icon = 'configuration';
- //$treeElement->type = 'prefs';
- $treeElement->action = 'configuration';
-
- $this->addTreeElement( $treeElement );
- }
-
- // Wechseln zu: Projekte...
- /*
- foreach( Project::getAll() as $id=>$name )
- {
- $treeElement = new TreeElement();
-
- $treeElement->text = lang('PROJECT').' '.$name;
- $treeElement->url = Html::url(array('action' =>'tree',
- 'subaction' =>'reload',
- 'projectid' =>$id ));
- $treeElement->icon = 'project';
- $treeElement->description = '';
- $treeElement->target = 'cms_tree';
-
- $this->addTreeElement( $treeElement );
- }
- */
- }
-
-
-
- function userandgroups( )
- {
- $treeElement = new TreeElement();
- $treeElement->text = lang('GLOBAL_USER');
- $treeElement->description = lang('GLOBAL_USER');
- $treeElement->url = Html::url('user','listing',0,array(REQ_PARAM_TARGET=>'content'));
- $treeElement->action = 'userlist';
- $treeElement->icon = 'userlist';
- $treeElement->target = 'cms_main';
- $treeElement->type = 'users';
-
- $this->addTreeElement( $treeElement );
-
- $treeElement = new TreeElement();
- $treeElement->text = lang('GLOBAL_GROUPS');
- $treeElement->description = lang('GLOBAL_GROUPS');
- $treeElement->url = Html::url('group','listing',0,array(REQ_PARAM_TARGET=>'content'));
- $treeElement->action = 'grouplist';
- $treeElement->icon = 'userlist';
- $treeElement->target = 'cms_main';
- $treeElement->type = 'groups';
-
- $this->addTreeElement( $treeElement );
- }
-
-
- function projects( )
- {
- // Schleife ueber alle Projekte
- foreach(Project::getAllProjects() as $id=> $name )
- {
- $treeElement = new TreeElement();
-
- $treeElement->internalId = $id;
- $treeElement->id = $id;
- $treeElement->text = $name;
- $treeElement->url = Html::url('project','edit',$id,array(REQ_PARAM_TARGET=>'content'));
- $treeElement->icon = 'project';
- $treeElement->action = 'project';
- $treeElement->type = 'project';
- $treeElement->description = '';
- $treeElement->target = 'cms_main';
-
- $this->addTreeElement( $treeElement );
- }
- }
-
-
-
- function project( $projectid )
- {
- $project = new Project( $projectid );
-
- // Hoechster Ordner der Projektstruktur
- $folder = new Folder( $project->getRootObjectId() );
- $folder->load();
-
-
- // Ermitteln, ob der Benutzer Projektadministrator ist
- // Projektadministratoren haben das Recht, im Root-Ordner die Eigenschaften zu aendern.
- if ( $folder->hasRight( ACL_PROP ) )
- $this->userIsProjectAdmin = true;
-
- if ( $folder->hasRight( ACL_READ ) )
- {
- $treeElement = new TreeElement();
- $treeElement->id = $folder->objectid;
- // $treeElement->text = $folder->name;
- $treeElement->text = lang('FOLDER_ROOT');
- $treeElement->description = lang('FOLDER_ROOT_DESC');
- $treeElement->icon = 'folder';
- $treeElement->action = 'folder';
- $treeElement->url = Html::url( 'folder','',$folder->objectid,array(REQ_PARAM_TARGET=>'content') );
- $treeElement->target = 'content';
- $treeElement->type = 'folder';
- $treeElement->internalId = $folder->objectid;
- $this->addTreeElement( $treeElement );
- }
-
-
- if ( $this->userIsProjectAdmin )
- {
- // Templates
- $treeElement = new TreeElement();
- $treeElement->id = 0;
- $treeElement->text = lang('GLOBAL_TEMPLATES');
- $treeElement->url = Html::url('template','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content'));
- $treeElement->description= lang('GLOBAL_TEMPLATES_DESC');
- $treeElement->icon = 'templatelist';
- $treeElement->action = 'templatelist';
- $treeElement->target = 'content';
- $treeElement->type = 'templates';
- $this->addTreeElement( $treeElement );
- }
-
-
- // Sprachen
- $treeElement = new TreeElement();
- $treeElement->description= '';
- $treeElement->id = 0;
- $treeElement->action = 'languagelist';
- $treeElement->text = lang('GLOBAL_LANGUAGES');
- $treeElement->url = Html::url('language','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content'));
- $treeElement->icon = 'languagelist';
- $treeElement->description= lang('GLOBAL_LANGUAGES_DESC');
- $treeElement->target = 'content';
-
- // Nur fuer Projekt-Administratoren aufklappbar
- if ( $this->userIsProjectAdmin )
- $treeElement->type = 'languages';
-
- $this->addTreeElement( $treeElement );
-
-
- // Projektmodelle
- $treeElement = new TreeElement();
- $treeElement->description= '';
-
- // Nur fuer Projekt-Administratoren aufklappbar
- if ( $this->userIsProjectAdmin )
- $treeElement->type = 'models';
-
- $treeElement->id = 0;
- $treeElement->description= lang('GLOBAL_MODELS_DESC');
- $treeElement->text = lang('GLOBAL_MODELS');
- $treeElement->url = Html::url('model','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content'));
- $treeElement->action = 'modellist';
- $treeElement->icon = 'modellist';
- $treeElement->target = 'content';
- $this->addTreeElement( $treeElement );
-
-
- // Sonstiges
- // $treeElement = new TreeElement();
- // $treeElement->text = lang('GLOBAL_OTHER');
- // $treeElement->description= lang('GLOBAL_OTHER_DESC');
- // $treeElement->icon = 'other';
- // $treeElement->type = 'other';
- // $this->addTreeElement( $treeElement );
-
- // Suche
- $treeElement = new TreeElement();
- $treeElement->id = 0;
- $treeElement->text = lang('GLOBAL_SEARCH');
- $treeElement->url = Html::url('search','',0,array(REQ_PARAM_TARGET=>'content'));
- $treeElement->action = 'search';
- $treeElement->icon = 'search';
- $treeElement->description = lang('GLOBAL_SEARCH_DESC');
- $treeElement->target = 'content';
- $this->addTreeElement( $treeElement );
-
- }
-
-
-
- function prefs_system( )
- {
- $system = array( 'time' => date('r'),
- 'os' => php_uname('s'),
- 'host' => php_uname('n'),
- 'release'=> php_uname('r'),
- 'machine'=> php_uname('m'),
- 'owner' => get_current_user(),
- 'pid' => getmypid() );
-
- foreach( $system as $key=>$value )
- {
- $treeElement = new TreeElement();
- $treeElement->text = $key.'='.$value;
- $treeElement->icon = 'config_property';
- $this->addTreeElement( $treeElement );
- $treeElement->description = lang('SETTING')." '".$key."'".(!empty($value)?': '.$value:'');
- }
-
- if ( function_exists('getrusage') ) // Funktion existiert auf WIN32 nicht.
- {
- foreach( getrusage() as $name=>$value );
- {
- $treeElement = new TreeElement();
- $treeElement->text = $name.':'.$value;
- $treeElement->description = lang('SETTING')." '".$name."'".(!empty($value)?': '.$value:'');
- $treeElement->icon = 'config_property';
- $this->addTreeElement( $treeElement );
- }
- }
- }
-
-
-
-
- function prefs_php( )
- {
- $php_prefs = array( 'version' => phpversion(),
- 'SAPI' => php_sapi_name(),
- 'session-name' => session_name(),
- 'magic_quotes_gpc' => get_magic_quotes_gpc(),
- 'magic_quotes_runtime'=> get_magic_quotes_runtime() );
-
- foreach( array('upload_max_filesize',
- 'file_uploads',
- 'memory_limit',
- 'max_execution_time',
- 'post_max_size',
- 'display_errors',
- 'register_globals'
- ) as $iniName )
- $php_prefs[ $iniName ] = ini_get( $iniName );
-
- foreach( $php_prefs as $key=>$value )
- {
- $treeElement = new TreeElement();
- $treeElement->text = $key.'='.$value;
- $treeElement->description = lang('SETTING')." '".$key."'".(!empty($value)?': '.$value:'');
- $treeElement->icon = 'config_property';
- $this->addTreeElement( $treeElement );
- }
- }
-
-
-
- function prefs_extensions( )
- {
- $extensions = get_loaded_extensions();
- asort( $extensions );
-
- foreach( $extensions as $id=>$extensionName )
- {
- $treeElement = new TreeElement();
- $treeElement->text = $extensionName;
- $treeElement->icon = 'config_property';
- $treeElement->internalId = $id;
- $this->addTreeElement( $treeElement );
- }
- }
-
-
-
- function prefs_extension( $id )
- {
- $extensions = get_loaded_extensions();
- $functions = get_extension_funcs( $extensions[$id] );
- asort( $functions );
-
- foreach( $functions as $functionName )
- {
- $treeElement = new TreeElement();
- $treeElement->text = $functionName;
- $treeElement->icon = 'config_property';
- $this->addTreeElement( $treeElement );
- }
- }
-
-
- /**
- * Anzeigen von Einstellungen.
- *
- * @param $id
- */
- function prefs( )
- {
- global $conf;
-
- if ( !@$conf['security']['show_system_info'] )
- return;
-
- $conf_config = $conf['interface']['config'];
-
-
- $treeElement = new TreeElement();
-
- $treeElement->internalId = -1;
- $treeElement->text = 'OpenRat';
- $treeElement->icon = 'configuration';
-
- if ( !empty($conf_config['file_manager_url']) )
- $treeElement->url = $conf_config['file_manager_url'];
- $treeElement->target = '_blank';
- $treeElement->description = '';
- $treeElement->type = 'prefs_cms';
- $this->addTreeElement( $treeElement );
-
-
-
- if ( !empty($conf_config['show_system']) )
- {
- $treeElement = new TreeElement();
-
- $treeElement->internalId = 0;
- $treeElement->text = lang('GLOBAL_SYSTEM');
- $treeElement->icon = 'configuration';
-
- $treeElement->description = '';
- $treeElement->target = 'cms_main';
- $treeElement->type = 'prefs_system';
- $this->addTreeElement( $treeElement );
- }
-
-
- if ( !empty($conf_config['show_interpreter']) )
- {
- $treeElement = new TreeElement();
-
- $treeElement->internalId = 0;
- $treeElement->text = lang('GLOBAL_PHP');
- $treeElement->icon = 'configuration';
-
- $treeElement->description = '';
- $treeElement->target = 'cms_main';
- $treeElement->type = 'prefs_php';
- $this->addTreeElement( $treeElement );
- }
-
-
- if ( !empty($conf_config['show_extensions']) )
- {
- $treeElement = new TreeElement();
-
- $treeElement->internalId = 0;
- $treeElement->text = lang('GLOBAL_EXTENSIONS');
- $treeElement->icon = 'configuration';
-
- $treeElement->description = '';
- $treeElement->target = 'cms_main';
- $treeElement->type = 'prefs_extensions';
- $this->addTreeElement( $treeElement );
- }
- }
-
-
- function prefs_cms( $id )
- {
- global $conf;
-
- if ( $id < 0 )
- {
- $tmpConf = $conf;
- }
- else
- $tmpConf = $this->confCache[$id];
-
- if ( !is_array($tmpConf) )
- $tmpConf = array('unknown');
-
- foreach( $tmpConf as $key=>$value )
- {
- if ( is_array($value) )
- {
- $this->confCache[crc32($key)] = $value;
-
- $treeElement = new TreeElement();
-
- $treeElement->internalId = crc32($key);
- $treeElement->text = $key;
-// if ( $id == 0 )
-// $treeElement->url = Html::url('main','prefs',0,array('conf'=>$key));
- $treeElement->icon = 'configuration';
-
- $treeElement->description = count($value).' '.lang('SETTINGS');
- $treeElement->target = 'cms_main';
- $treeElement->type = 'prefs_cms';
- $this->addTreeElement( $treeElement );
- }
- else
- {
- // Die PHP-funktion 'parse_ini_file()' liefert alle Einstellungen leider nur als String
- // Daher weiß man hier nicht, ob '1' nun '1' oder 'true' heißen soll.
- if ( $value=='' )
- // Anzeige 'Leer'
- $value = lang('EMPTY');
- elseif ( $value=='0' )
- // Anzeige 'Nein'
- $value = $value.' ('.lang('IS_NO').')';
- elseif ( $value=='1' )
- // Anzeige 'Ja'
- $value = '+'.$value.' ('.lang('IS_YES').')';
- elseif ( is_numeric($value) )
- // Anzeige numerische Werte
- $value = ($value>0?'+':'').$value;
- else
- // Anzeige von Zeichenketten
- $value = $value;
-
- $this->confCache[crc32($key)] = $value;
-
- if ( strpos($key,'pass') !== FALSE )
- $value = '***'; // Kennwörter nicht anzeigen
-
- $treeElement = new TreeElement();
- $treeElement->text = $key.': '.$value;
- $treeElement->icon = 'config_property';
- $treeElement->description = lang('SETTING')." '".$key."'".(!empty($value)?': '.$value:'');
-
- $this->addTreeElement( $treeElement );
- }
- }
- }
-
-
- function users( )
- {
- foreach( User::getAllUsers() as $user )
- {
- $treeElement = new TreeElement();
- $treeElement->id = $user->userid;
- $treeElement->internalId = $user->userid;
- $treeElement->text = $user->name;
- $treeElement->url = Html::url('user','edit',
- $user->userid,array(REQ_PARAM_TARGET=>'content') );
- $treeElement->action = 'user';
- $treeElement->icon = 'user';
-
- $desc = $user->fullname;
-
- if ( $user->isAdmin )
- $desc .= ' ('.lang('USER_ADMIN').') ';
- if ( $user->desc == "" )
- $desc .= ' - '.lang('GLOBAL_NO_DESCRIPTION_AVAILABLE');
- else
- $desc .= ' - '.$user->desc;
-
- $treeElement->description = $desc;
- $treeElement->target = 'cms_main';
-
- $this->addTreeElement( $treeElement );
- }
- }
-
-
- function groups( )
- {
-
- foreach( Group::getAll() as $id=>$name )
- {
- $treeElement = new TreeElement();
-
- $g = new Group( $id );
- $g->load();
-
- $treeElement->id = $id;
- $treeElement->internalId = $id;
- $treeElement->text = $g->name;
- $treeElement->url = Html::url('group','edit',$id,
- array(REQ_PARAM_TARGET=>'content') );
- $treeElement->icon = 'group';
- $treeElement->description = lang('GLOBAL_GROUP').' '.$g->name.': '.implode(', ',$g->getUsers());
- $treeElement->target = 'cms_main';
- $treeElement->type = 'userofgroup';
- $treeElement->action = 'group';
-
- $this->addTreeElement( $treeElement );
- }
- }
-
-
- function userofgroup( $id )
- {
- $g = new Group( $id );
-
- foreach( $g->getUsers() as $id=>$name )
- {
- $treeElement = new TreeElement();
-
- $u = new User( $id );
- $u->load();
- $treeElement->id = $u->userid;
- $treeElement->text = $u->name;
- $treeElement->url = Html::url('user','edit',$id,array(REQ_PARAM_TARGET=>'content'));
- $treeElement->icon = 'user';
- $treeElement->action = 'user';
- $treeElement->description = $u->fullname;
- $treeElement->target = 'cms_main';
-
- $this->addTreeElement( $treeElement );
- }
- }
-}
-
-?>-
\ No newline at end of file
diff --git a/util/Api.class.php b/util/Api.class.php
@@ -1,101 +0,0 @@
-<?php
-// OpenRat Content Management System
-// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-use cms\model\Folder;
-
-
-/**
- * Service-Klasse fuer allgemeine Interface-Methoden. Insbesondere
- * in Code-Elementen kann und soll auf diese Methoden zurueckgegriffen
- * werden.
- * @author $Author$
- * @version $Revision$
- * @package openrat.services
- */
-class Api
-{
- var $output = '';
- var $objectid = 0;
- var $page;
-
- function db()
- {
- return db_connection();
- }
-
- function pageid()
- {
- echo 'WARNING: pageid() deprecated!<br>';
- global $SESS;
- return $SESS['objectid'];
- }
-
- function getObjectId()
- {
- return $this->objectid;
- }
-
- function setObjectId( $objectid )
- {
- $this->objectid = $objectid;
- }
-
- function getRootObjectId()
- {
- return Folder::getRootObjectId();
- }
-
- function folderid()
- {
- global $SESS;
- return $SESS['folderid'];
- }
-
-
- function execute( $code )
- {
- global $conf_tmpdir;
- $code = "<?php\n".$code."\n?>";
-
- $tmp = $conf_tmpdir.'/'.md5(microtime()).'.tmp';
- $f = fopen( $tmp,'w' );
- fwrite( $f,$code );
- fclose( $f );
-
- require( $tmp ); // Ausfuehren des temporaeren PHP-Codes
-
- unlink( $tmp );
- $inhalt = Api::getOutput();
- $this->output( $inhalt );
- }
-
- function delOutput()
- {
- $this->output = '';
- }
-
- function output( $text )
- {
- $this->output .= $text;
- }
-
-
- function getOutput()
- {
- return $this->output;
- }
-}-
\ No newline at end of file
diff --git a/util/ArchiveTar.class.php b/util/ArchiveTar.class.php
@@ -1,397 +0,0 @@
-<?php
-/*
-=======================================================================
-Name:
- tar Class
-
-Author:
- Josh Barger <joshb@npt.com>
-
-Description:
- This class reads and writes Tape-Archive (TAR) Files and Gzip
- compressed TAR files, which are mainly used on UNIX systems.
- This class works on both windows AND unix systems, and does
- NOT rely on external applications!! Woohoo!
-
-Usage:
- Copyright (C) 2002 Josh Barger
-
- 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 at:
- http://www.gnu.org/copyleft/lesser.html
-
- If you use this script in your application/website, please
- send me an e-mail letting me know about it :)
-
-Bugs:
- Please report any bugs you might find to my e-mail address
- at joshb@npt.com. If you have already created a fix/patch
- for the bug, please do send it to me so I can incorporate it into my release.
-
-Version History:
- 1.0 04/10/2002 - InitialRelease
-
- 2.0 04/11/2002 - Merged both tarReader and tarWriter
- classes into one
- - Added support for gzipped tar files
- Remember to name for .tar.gz or .tgz
- if you use gzip compression!
- :: THIS REQUIRES ZLIB EXTENSION ::
- - Added additional comments to
- functions to help users
- - Added ability to remove files and
- directories from archive
- 2.1 04/12/2002 - Fixed serious bug in generating tar
- - Created another example file
- - Added check to make sure ZLIB is
- installed before running GZIP
- compression on TAR
- 2.2 05/07/2002 - Added automatic detection of Gzipped
- tar files (Thanks go to J�rgen Falch
- for the idea)
- - Changed "private" functions to have
- special function names beginning with
- two underscores
-=======================================================================
-*/
-
-
-class ArchiveTar
-{
- // Unprocessed Archive Information
- var $filename;
- var $isGzipped;
- var $tar_file;
-
- // Processed Archive Information
- var $files;
- var $directories;
- var $numFiles;
- var $numDirectories;
-
-
- // Class Constructor -- Does nothing...
- function tar() {
- return true;
- }
-
-
- // Computes the unsigned Checksum of a file's header
- // to try to ensure valid file
- // PRIVATE ACCESS FUNCTION
- function __computeUnsignedChecksum($bytestring)
- {
- $unsigned_chksum=0;
- for($i=0; $i<512; $i++)
- $unsigned_chksum += ord($bytestring[$i]);
- for($i=0; $i<8; $i++)
- $unsigned_chksum -= ord($bytestring[148 + $i]);
- $unsigned_chksum += ord(" ") * 8;
-
- return $unsigned_chksum;
- }
-
-
- // Converts a NULL padded string to a non-NULL padded string
- // PRIVATE ACCESS FUNCTION
- function __parseNullPaddedString($string)
- {
- $position = strpos($string,chr(0));
- return substr($string,0,$position);
- }
-
-
- // This function parses the current TAR file
- // PRIVATE ACCESS FUNCTION
- function __parseTar()
- {
- // Read Files from archive
- $this->numFiles=0;
- $tar_length = strlen($this->tar_file);
- $main_offset = 0;
- while($main_offset < $tar_length) {
- // If we read a block of 512 nulls, we are at the end of the archive
- if(substr($this->tar_file,$main_offset,512) == str_repeat(chr(0),512))
- break;
-
- // Parse file name
- $file_name = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset,100));
-
- // Parse the file mode
- $file_mode = substr($this->tar_file,$main_offset + 100,8);
-
- // Parse the file user ID
- $file_uid = octdec(substr($this->tar_file,$main_offset + 108,8));
-
- // Parse the file group ID
- $file_gid = octdec(substr($this->tar_file,$main_offset + 116,8));
-
- // Parse the file size
- $file_size = octdec(substr($this->tar_file,$main_offset + 124,12));
-
- // Parse the file update time - unix timestamp format
- $file_time = octdec(substr($this->tar_file,$main_offset + 136,12));
-
- // Parse Checksum
- $file_chksum = octdec(substr($this->tar_file,$main_offset + 148,6));
-
- // Parse user name
- $file_uname = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset + 265,32));
-
- // Parse Group name
- $file_gname = $this->__parseNullPaddedString(substr($this->tar_file,$main_offset + 297,32));
-
- // Make sure our file is valid
- if($this->__computeUnsignedChecksum(substr($this->tar_file,$main_offset,512)) != $file_chksum)
- return false;
-
- // Parse File Contents
- $file_contents = substr($this->tar_file,$main_offset + 512,$file_size);
-
- /* ### Unused Header Information ###
- $activeFile["typeflag"] = substr($this->tar_file,$main_offset + 156,1);
- $activeFile["linkname"] = substr($this->tar_file,$main_offset + 157,100);
- $activeFile["magic"] = substr($this->tar_file,$main_offset + 257,6);
- $activeFile["version"] = substr($this->tar_file,$main_offset + 263,2);
- $activeFile["devmajor"] = substr($this->tar_file,$main_offset + 329,8);
- $activeFile["devminor"] = substr($this->tar_file,$main_offset + 337,8);
- $activeFile["prefix"] = substr($this->tar_file,$main_offset + 345,155);
- $activeFile["endheader"] = substr($this->tar_file,$main_offset + 500,12);
- */
-
- if($file_size > 0) {
- // Increment number of files
- $this->numFiles++;
-
- // Create us a new file in our array
- $activeFile = &$this->files[];
-
- // Asign Values
- $activeFile["name"] = $file_name;
- $activeFile["mode"] = $file_mode;
- $activeFile["size"] = $file_size;
- $activeFile["time"] = $file_time;
- $activeFile["user_id"] = $file_uid;
- $activeFile["group_id"] = $file_gid;
- $activeFile["user_name"] = $file_uname;
- $activeFile["group_name"] = $file_gname;
- $activeFile["checksum"] = $file_chksum;
- $activeFile["file"] = $file_contents;
-
- } else {
- // Increment number of directories
- $this->numDirectories++;
-
- // Create a new directory in our array
- $activeDir = &$this->directories[];
-
- // Assign values
- $activeDir["name"] = $file_name;
- $activeDir["mode"] = $file_mode;
- $activeDir["time"] = $file_time;
- $activeDir["user_id"] = $file_uid;
- $activeDir["group_id"] = $file_gid;
- $activeDir["user_name"] = $file_uname;
- $activeDir["group_name"] = $file_gname;
- $activeDir["checksum"] = $file_chksum;
- }
-
- // Move our offset the number of blocks we have processed
- $main_offset += 512 + (ceil($file_size / 512) * 512);
- }
-
- return true;
- }
-
-
- // Read a non gzipped tar file in for processing
- // PRIVATE ACCESS FUNCTION
- function __readTar($filename='')
- {
- // Set the filename to load
- // Read in the TAR file
-
- if($this->tar_file[0] == chr(31) && $this->tar_file[1] == chr(139) && $this->tar_file[2] == chr(8)) {
- if(!function_exists("gzinflate"))
- return false;
-
- $this->isGzipped = TRUE;
-
- $this->tar_file = gzinflate(substr($this->tar_file,10,-4));
- }
-
- // Parse the TAR file
- $this->__parseTar();
-
- return true;
- }
-
-
- // Generates a TAR file from the processed data
- // PRIVATE ACCESS FUNCTION
- function __generateTAR()
- {
- // Clear any data currently in $this->tar_file
- unset($this->tar_file);
-
- // Generate Records for each directory, if we have directories
- if($this->numDirectories > 0) {
- foreach($this->directories as $key => $information) {
- unset($header);
-
- // Generate tar header for this directory
- // Filename, Permissions, UID, GID, size, Time, checksum, typeflag, linkname, magic, version, user name, group name, devmajor, devminor, prefix, end
- $header .= str_pad($information["name"],100,chr(0));
- $header .= str_pad(decoct($information["mode"]),7,"0",STR_PAD_LEFT) . chr(0);
- $header .= str_pad(decoct($information["user_id"]),7,"0",STR_PAD_LEFT) . chr(0);
- $header .= str_pad(decoct($information["group_id"]),7,"0",STR_PAD_LEFT) . chr(0);
- $header .= str_pad(decoct(0),11,"0",STR_PAD_LEFT) . chr(0);
- $header .= str_pad(decoct($information["time"]),11,"0",STR_PAD_LEFT) . chr(0);
- $header .= str_repeat(" ",8);
- $header .= "5";
- $header .= str_repeat(chr(0),100);
- $header .= str_pad("ustar",6,chr(32));
- $header .= chr(32) . chr(0);
- $header .= str_pad("",32,chr(0));
- $header .= str_pad("",32,chr(0));
- $header .= str_repeat(chr(0),8);
- $header .= str_repeat(chr(0),8);
- $header .= str_repeat(chr(0),155);
- $header .= str_repeat(chr(0),12);
-
- // Compute header checksum
- $checksum = str_pad(decoct($this->__computeUnsignedChecksum($header)),6,"0",STR_PAD_LEFT);
- for($i=0; $i<6; $i++) {
- $header[(148 + $i)] = substr($checksum,$i,1);
- }
- $header[154] = chr(0);
- $header[155] = chr(32);
-
- // Add new tar formatted data to tar file contents
- $this->tar_file .= $header;
- }
- }
-
- // Generate Records for each file, if we have files (We should...)
- if($this->numFiles > 0)
- {
- foreach($this->files as $key => $information)
- {
- unset($header);
- $header = '';
-
- // Generate the TAR header for this file
- // Filename, Permissions, UID, GID, size, Time, checksum, typeflag, linkname, magic, version, user name, group name, devmajor, devminor, prefix, end
- $header .= str_pad($information["name"],100,chr(0));
- $header .= str_pad(decoct($information["mode"]),7,"0",STR_PAD_LEFT) . chr(0);
- $header .= str_pad(decoct($information["user_id"]),7,"0",STR_PAD_LEFT) . chr(0);
- $header .= str_pad(decoct($information["group_id"]),7,"0",STR_PAD_LEFT) . chr(0);
- $header .= str_pad(decoct($information["size"]),11,"0",STR_PAD_LEFT) . chr(0);
- $header .= str_pad(decoct($information["time"]),11,"0",STR_PAD_LEFT) . chr(0);
- $header .= str_repeat(" ",8);
- $header .= "0";
- $header .= str_repeat(chr(0),100);
- $header .= str_pad("ustar",6,chr(32));
- $header .= chr(32) . chr(0);
- $header .= str_pad($information["user_name"],32,chr(0)); // How do I get a file's user name from PHP?
- $header .= str_pad($information["group_name"],32,chr(0)); // How do I get a file's group name from PHP?
- $header .= str_repeat(chr(0),8);
- $header .= str_repeat(chr(0),8);
- $header .= str_repeat(chr(0),155);
- $header .= str_repeat(chr(0),12);
-
- // Compute header checksum
- $checksum = str_pad(decoct($this->__computeUnsignedChecksum($header)),6,"0",STR_PAD_LEFT);
- for($i=0; $i<6; $i++)
- {
- $header[(148 + $i)] = substr($checksum,$i,1);
- }
- $header[154] = chr(0);
- $header[155] = chr(32);
-
- // Pad file contents to byte count divisible by 512
- $file_contents = str_pad($information["file"],(ceil($information["size"] / 512) * 512),chr(0));
-
- // Add new tar formatted data to tar file contents
- $this->tar_file .= $header . $file_contents;
- }
- }
-
- // Add 512 bytes of NULLs to designate EOF
- $this->tar_file .= str_repeat(chr(0),512);
-
- return true;
- }
-
-
- // Open a TAR file
- function openTAR($value)
- {
- // Clear any values from previous tar archives
- unset($this->filename);
- unset($this->isGzipped);
- unset($this->tar_file);
- unset($this->files);
- unset($this->directories);
- unset($this->numFiles);
- unset($this->numDirectories);
-
- $this->filename = 'none';
- $this->tar_file = $value;
- // Parse this file
- $this->__readTar();
-
- return true;
- }
-
-
- // Write the currently loaded tar archive to disk
- function saveTar()
- {
- if(!$this->filename)
- return false;
-
- // Write tar to current file using specified gzip compression
- $this->toTar($this->filename,$this->isGzipped);
-
- return true;
- }
-
-
- // Saves tar archive to a different file than the current file
- function toTar($filename,$useGzip)
- {
- if(!$filename)
- return false;
-
- // Encode processed files into TAR file format
- $this->__generateTar();
-
- // GZ Compress the data if we need to
- if($useGzip) {
- // Make sure we have gzip support
- if(!function_exists("gzencode"))
- return false;
-
- $file = gzencode($this->tar_file);
- } else {
- $file = $this->tar_file;
- }
-
- // Write the TAR file
- $fp = fopen($filename,"wb");
- fwrite($fp,$file);
- fclose($fp);
-
- return true;
- }
-}
-
-?>-
\ No newline at end of file
diff --git a/util/ArchiveUnzip.class.php b/util/ArchiveUnzip.class.php
@@ -1,447 +0,0 @@
-<?php
-// 28/11/2005 (2.4)
-// - dUnzip2 is now compliant with wrong placed "Data Description", made by some compressors,
-// like the classes ZipLib and ZipLib2 by 'Hasin Hayder'. Thanks to Ricardo Parreno for pointing it.
-// 09/11/2005 (2.3)
-// - Added optional parameter '$stopOnFile' on method 'getList()'.
-// If given, file listing will stop when find given filename. (Useful to open and unzip an exact file)
-// 06/11/2005 (2.21)
-// - Added support to PK00 file format (Packed to Removable Disk) (thanks to Lito [PHPfileNavigator])
-// - Method 'getExtraInfo': If requested file doesn't exist, return FALSE instead of Array()
-// 31/10/2005 (2.2)
-// - Removed redundant 'file_name' on centralDirs declaration (thanks to Lito [PHPfileNavigator])
-// - Fixed redeclaration of file_put_contents when in PHP4 (not returning true)
-
-##############################################################
-# Class dUnzip2 v2.4
-#
-# Author: Alexandre Tedeschi (d)
-# E-Mail: alexandrebr at gmail dot com
-# Londrina - PR / Brazil
-#
-# Objective:
-# This class allows programmer to easily unzip files on the fly.
-#
-# Requirements:
-# This class requires extension ZLib Enabled. It is default
-# for most site hosts around the world, and for the PHP Win32 dist.
-#
-# To do:
-# * Error handling
-# * Write a PHP-Side gzinflate, to completely avoid any external extensions
-# * Write other decompress algorithms
-#
-# If you modify this class, or have any ideas to improve it, please contact me!
-# You are allowed to redistribute this class, if you keep my name and contact e-mail on it.
-##############################################################
-
-class ArchiveUnzip{
-
- // Public
- var $files = array();
- var $value = '';
- var $fileName;
- var $compressedList; // You will problably use only this one!
- var $centralDirList; // Central dir list... It's a kind of 'extra attributes' for a set of files
- var $endOfCentral; // End of central dir, contains ZIP Comments
- var $debug;
-
- // Private
- var $fh;
- var $zipSignature = "\x50\x4b\x03\x04"; // local file header signature
- var $dirSignature = "\x50\x4b\x01\x02"; // central dir header signature
- var $dirSignatureE= "\x50\x4b\x05\x06"; // end of central dir signature
-
- // Public
- Function ArchiveUnzip()
- {
- $this->compressedList =
- $this->centralDirList =
- $this->endOfCentral = Array();
- }
-
- function open( $value )
- {
- $this->fileName = tempnam('/tmp','unzip');
-// echo $this->fileName;
- $fo = fopen( $this->fileName,'w');
- fwrite($fo,$value);
- $this->unzipAll();
- }
-
-
- Function getList($stopOnFile=false){
- if(sizeof($this->compressedList)){
- $this->debugMsg(1, "Returning already loaded file list.");
- return $this->compressedList;
- }
-
- // Open file, and set file handler
- $fh = fopen($this->fileName, "r");
- $this->fh = &$fh;
- if(!$fh){
- $this->debugMsg(2, "Failed to load file.");
- return false;
- }
-
- // Loop the file, looking for files and folders
- $ddTry = false;
- fseek($fh, 0);
- for(;;){
- // Check if the signature is valid...
- $signature = fread($fh, 4);
- if(feof($fh)){
-# $this->debugMsg(1, "Reached end of file");
- break;
- }
-
- // If signature is a 'Packed to Removable Disk', just ignore it and move to the next.
- if($signature == 'PK00'){
- $this->debugMsg(1, "Found PK00: Packed to Removable Disk");
- continue;
- }
-
- // If signature of a 'Local File Header'
- if($signature == $this->zipSignature){
- # $this->debugMsg(1, "Zip Signature!");
-
- // Get information about the zipped file
- $file['version_needed'] = unpack("v", fread($fh, 2)); // version needed to extract
- $file['general_bit_flag'] = unpack("v", fread($fh, 2)); // general purpose bit flag
- $file['compression_method'] = unpack("v", fread($fh, 2)); // compression method
- $file['lastmod_time'] = unpack("v", fread($fh, 2)); // last mod file time
- $file['lastmod_date'] = unpack("v", fread($fh, 2)); // last mod file date
- $file['crc-32'] = fread($fh, 4); // crc-32
- $file['compressed_size'] = unpack("V", fread($fh, 4)); // compressed size
- $file['uncompressed_size'] = unpack("V", fread($fh, 4)); // uncompressed size
- $fileNameLength = unpack("v", fread($fh, 2)); // filename length
- $extraFieldLength = unpack("v", fread($fh, 2)); // extra field length
- $file['file_name'] = fread($fh, $fileNameLength[1]); // filename
- $file['extra_field'] = $extraFieldLength[1]?fread($fh, $extraFieldLength[1]):''; // extra field
- $file['contents-startOffset']= ftell($fh);
-
- // Bypass the whole compressed contents, and look for the next file
- fseek($fh, $file['compressed_size'][1], SEEK_CUR);
-
- // Convert the date and time, from MS-DOS format to UNIX Timestamp
- $BINlastmod_date = str_pad(decbin($file['lastmod_date'][1]), 16, '0', STR_PAD_LEFT);
- $BINlastmod_time = str_pad(decbin($file['lastmod_time'][1]), 16, '0', STR_PAD_LEFT);
- $lastmod_dateY = bindec(substr($BINlastmod_date, 0, 7))+1980;
- $lastmod_dateM = bindec(substr($BINlastmod_date, 7, 4));
- $lastmod_dateD = bindec(substr($BINlastmod_date, 11, 5));
- $lastmod_timeH = bindec(substr($BINlastmod_time, 0, 5));
- $lastmod_timeM = bindec(substr($BINlastmod_time, 5, 6));
- $lastmod_timeS = bindec(substr($BINlastmod_time, 11, 5));
-
- // Mount file table
- $this->compressedList[$file['file_name']] = Array(
- 'file_name' =>$file['file_name'],
- 'compression_method'=>$file['compression_method'][1],
- 'version_needed' =>$file['version_needed'][1],
- 'lastmod_datetime' =>mktime($lastmod_timeH, $lastmod_timeM, $lastmod_timeS, $lastmod_dateM, $lastmod_dateD, $lastmod_dateY),
- 'crc-32' =>str_pad(dechex(ord($file['crc-32'][3])), 2, '0', STR_PAD_LEFT).
- str_pad(dechex(ord($file['crc-32'][2])), 2, '0', STR_PAD_LEFT).
- str_pad(dechex(ord($file['crc-32'][1])), 2, '0', STR_PAD_LEFT).
- str_pad(dechex(ord($file['crc-32'][0])), 2, '0', STR_PAD_LEFT),
- 'compressed_size' =>$file['compressed_size'][1],
- 'uncompressed_size' =>$file['uncompressed_size'][1],
- 'extra_field' =>$file['extra_field'],
- 'general_bit_flag' =>str_pad(decbin($file['general_bit_flag'][1]), 8, '0', STR_PAD_LEFT),
- 'contents-startOffset'=>$file['contents-startOffset']
- );
-
- if($stopOnFile) if($file['file_name'] == $stopOnFile){
- $this->debugMsg(1, "Stopping on file...");
- break;
- }
- }
-
- // If signature of a 'Central Directory Structure'
- elseif($signature == $this->dirSignature){
- # $this->debugMsg(1, "Dir Signature!");
-
- $dir['version_madeby'] = unpack("v", fread($fh, 2)); // version made by
- $dir['version_needed'] = unpack("v", fread($fh, 2)); // version needed to extract
- $dir['general_bit_flag'] = unpack("v", fread($fh, 2)); // general purpose bit flag
- $dir['compression_method'] = unpack("v", fread($fh, 2)); // compression method
- $dir['lastmod_time'] = unpack("v", fread($fh, 2)); // last mod file time
- $dir['lastmod_date'] = unpack("v", fread($fh, 2)); // last mod file date
- $dir['crc-32'] = fread($fh, 4); // crc-32
- $dir['compressed_size'] = unpack("V", fread($fh, 4)); // compressed size
- $dir['uncompressed_size'] = unpack("V", fread($fh, 4)); // uncompressed size
- $fileNameLength = unpack("v", fread($fh, 2)); // filename length
- $extraFieldLength = unpack("v", fread($fh, 2)); // extra field length
- $fileCommentLength = unpack("v", fread($fh, 2)); // file comment length
- $dir['disk_number_start'] = unpack("v", fread($fh, 2)); // disk number start
- $dir['internal_attributes'] = unpack("v", fread($fh, 2)); // internal file attributes-byte1
- $dir['external_attributes1']= unpack("v", fread($fh, 2)); // external file attributes-byte2
- $dir['external_attributes2']= unpack("v", fread($fh, 2)); // external file attributes
- $dir['relative_offset'] = unpack("V", fread($fh, 4)); // relative offset of local header
- $dir['file_name'] = fread($fh, $fileNameLength[1]); // filename
- $dir['extra_field'] = $extraFieldLength[1] ?fread($fh, $extraFieldLength[1]) :''; // extra field
- $dir['file_comment'] = $fileCommentLength[1]?fread($fh, $fileCommentLength[1]):''; // file comment
-
- // Convert the date and time, from MS-DOS format to UNIX Timestamp
- $BINlastmod_date = str_pad(decbin($file['lastmod_date'][1]), 16, '0', STR_PAD_LEFT);
- $BINlastmod_time = str_pad(decbin($file['lastmod_time'][1]), 16, '0', STR_PAD_LEFT);
- $lastmod_dateY = bindec(substr($BINlastmod_date, 0, 7))+1980;
- $lastmod_dateM = bindec(substr($BINlastmod_date, 7, 4));
- $lastmod_dateD = bindec(substr($BINlastmod_date, 11, 5));
- $lastmod_timeH = bindec(substr($BINlastmod_time, 0, 5));
- $lastmod_timeM = bindec(substr($BINlastmod_time, 5, 6));
- $lastmod_timeS = bindec(substr($BINlastmod_time, 11, 5));
-
- $this->centralDirList[$dir['file_name']] = Array(
- 'version_madeby'=>$dir['version_madeby'][1],
- 'version_needed'=>$dir['version_needed'][1],
- 'general_bit_flag'=>str_pad(decbin($file['general_bit_flag'][1]), 8, '0', STR_PAD_LEFT),
- 'compression_method'=>$dir['compression_method'][1],
- 'lastmod_datetime' =>mktime($lastmod_timeH, $lastmod_timeM, $lastmod_timeS, $lastmod_dateM, $lastmod_dateD, $lastmod_dateY),
- 'crc-32' =>str_pad(dechex(ord($file['crc-32'][3])), 2, '0', STR_PAD_LEFT).
- str_pad(dechex(ord($file['crc-32'][2])), 2, '0', STR_PAD_LEFT).
- str_pad(dechex(ord($file['crc-32'][1])), 2, '0', STR_PAD_LEFT).
- str_pad(dechex(ord($file['crc-32'][0])), 2, '0', STR_PAD_LEFT),
- 'compressed_size'=>$dir['compressed_size'][1],
- 'uncompressed_size'=>$dir['uncompressed_size'][1],
- 'disk_number_start'=>$dir['disk_number_start'][1],
- 'internal_attributes'=>$dir['internal_attributes'][1],
- 'external_attributes1'=>$dir['external_attributes1'][1],
- 'external_attributes2'=>$dir['external_attributes2'][1],
- 'relative_offset'=>$dir['relative_offset'][1],
- 'file_name'=>$dir['file_name'],
- 'extra_field'=>$dir['extra_field'],
- 'file_comment'=>$dir['file_comment'],
- );
- }
-
- elseif($signature == $this->dirSignatureE){
- # $this->debugMsg(1, "EOF Dir Signature!");
-
- $eodir['disk_number_this'] = unpack("v", fread($fh, 2)); // number of this disk
- $eodir['disk_number'] = unpack("v", fread($fh, 2)); // number of the disk with the start of the central directory
- $eodir['total_entries_this'] = unpack("v", fread($fh, 2)); // total number of entries in the central dir on this disk
- $eodir['total_entries'] = unpack("v", fread($fh, 2)); // total number of entries in
- $eodir['size_of_cd'] = unpack("V", fread($fh, 4)); // size of the central directory
- $eodir['offset_start_cd'] = unpack("V", fread($fh, 4)); // offset of start of central directory with respect to the starting disk number
- $zipFileCommentLenght = unpack("v", fread($fh, 2)); // zipfile comment length
- $eodir['zipfile_comment'] = $zipFileCommentLenght[1]?fread($fh, $zipFileCommentLenght[1]):''; // zipfile comment
- $this->endOfCentral = Array(
- 'disk_number_this'=>$eodir['disk_number_this'][1],
- 'disk_number'=>$eodir['disk_number'][1],
- 'total_entries_this'=>$eodir['total_entries_this'][1],
- 'total_entries'=>$eodir['total_entries'][1],
- 'size_of_cd'=>$eodir['size_of_cd'][1],
- 'offset_start_cd'=>$eodir['offset_start_cd'][1],
- 'zipfile_comment'=>$eodir['zipfile_comment'],
- );
- }
- else{
- if(!$ddTry){
- $this->debugMsg(1, "Unexpected header. Trying to detect wrong placed 'Data Descriptor'...\n");
- $ddTry = true;
- fseek($fh, 12-4, SEEK_CUR); // Jump over 'crc-32'(4) 'compressed-size'(4), 'uncompressed-size'(4)
- continue;
- }
- $this->debugMsg(1, "Unexpected header, ending loop at offset ".ftell($fh));
- break;
- }
- $ddTry = false;
- }
-
- if($this->debug){
- #------- Debug compressedList
- $kkk = 0;
- echo "<table border='0' style='font: 11px Verdana; border: 1px solid #000'>";
- foreach($this->compressedList as $fileName=>$item){
- if(!$kkk && $kkk=1){
- echo "<tr style='background: #ADA'>";
- foreach($item as $fieldName=>$value)
- echo "<td>$fieldName</td>";
- echo '</tr>';
- }
- echo "<tr style='background: #CFC'>";
- foreach($item as $fieldName=>$value){
- if($fieldName == 'lastmod_datetime')
- echo "<td title='$fieldName' nowrap='nowrap'>".date("d/m/Y H:i:s", $value)."</td>";
- else
- echo "<td title='$fieldName' nowrap='nowrap'>$value</td>";
- }
- echo "</tr>";
- }
- echo "</table>";
-
- #------- Debug centralDirList
- $kkk = 0;
- if(sizeof($this->centralDirList)){
- echo "<table border='0' style='font: 11px Verdana; border: 1px solid #000'>";
- foreach($this->centralDirList as $fileName=>$item){
- if(!$kkk && $kkk=1){
- echo "<tr style='background: #AAD'>";
- foreach($item as $fieldName=>$value)
- echo "<td>$fieldName</td>";
- echo '</tr>';
- }
- echo "<tr style='background: #CCF'>";
- foreach($item as $fieldName=>$value){
- if($fieldName == 'lastmod_datetime')
- echo "<td title='$fieldName' nowrap='nowrap'>".date("d/m/Y H:i:s", $value)."</td>";
- else
- echo "<td title='$fieldName' nowrap='nowrap'>$value</td>";
- }
- echo "</tr>";
- }
- echo "</table>";
- }
-
- #------- Debug endOfCentral
- $kkk = 0;
- if(sizeof($this->endOfCentral)){
- echo "<table border='0' style='font: 11px Verdana' style='border: 1px solid #000'>";
- echo "<tr style='background: #DAA'><td colspan='2'>dUnzip - End of file</td></tr>";
- foreach($this->endOfCentral as $field=>$value){
- echo "<tr>";
- echo "<td style='background: #FCC'>$field</td>";
- echo "<td style='background: #FDD'>$value</td>";
- echo "</tr>";
- }
- echo "</table>";
- }
- }
-
- return $this->compressedList;
- }
-
-
- Function getExtraInfo($compressedFileName)
- {
- return
- isset($this->centralDirList[$compressedFileName])?
- $this->centralDirList[$compressedFileName]:
- false;
- }
-
-
- Function getZipInfo($detail=false)
- {
- return $detail?
- $this->endOfCentral[$detail]:
- $this->endOfCentral;
- }
-
-
- Function unzip($compressedFileName, $targetFileName=false){
- $fdetails = &$this->compressedList[$compressedFileName];
-
- if(!sizeof($this->compressedList)){
- $this->debugMsg(1, "Trying to unzip before loading file list... Loading it!");
- $this->getList(false, $compressedFileName);
- }
- if(!isset($this->compressedList[$compressedFileName])){
- $this->debugMsg(2, "File '<b>$compressedFileName</b>' is not compressed in the zip.");
- return false;
- }
- if(substr($compressedFileName, -1) == "/"){
- $this->debugMsg(2, "Trying to unzip a folder name '<b>$compressedFileName</b>'.");
- return false;
- }
- if(!$fdetails['uncompressed_size']){
- $this->debugMsg(1, "File '<b>$compressedFileName</b>' is empty.");
- return "";
- }
-
- fseek($this->fh, $fdetails['contents-startOffset']);
- return $this->uncompress(
- fread($this->fh, $fdetails['compressed_size']),
- $fdetails['compression_method'],
- $fdetails['uncompressed_size'] );
- }
-
-
- Function unzipAll($targetDir=false, $baseDir="", $maintainStructure=true, $chmod=false){
- if($targetDir === false)
- $targetDir = dirname(__FILE__)."/";
-
- $lista = $this->getList();
- if(sizeof($lista)) foreach($lista as $fileName=>$trash){
- $dirname = dirname($fileName);
- $outDN = "$targetDir/$dirname";
-
- if(substr($dirname, 0, strlen($baseDir)) != $baseDir)
- continue;
-
- if(!is_dir($outDN) && $maintainStructure){
- $str = "";
- $folders = explode("/", $dirname);
- foreach($folders as $folder){
- $str = $str?"$str/$folder":$folder;
- if(!is_dir("$targetDir/$str")){
- $this->debugMsg(1, "Creating folder: $targetDir/$str");
- mkdir("$targetDir/$str");
- if($chmod)
- chmod("$targetDir/$str", $chmod);
- }
- }
- }
- if(substr($fileName, -1, 1) == "/")
- continue;
-
- $maintainStructure?
- $this->unzip($fileName, "$targetDir/$fileName"):
- $this->unzip($fileName, "$targetDir/".basename($fileName));
-
- if($chmod)
- chmod($maintainStructure?"$targetDir/$fileName":"$targetDir/".basename($fileName), $chmod);
- }
- }
-
- Function close(){ // Free the file resource
- if($this->fh)
- fclose($this->fh);
- }
-
- // Private (you should NOT call these methods):
- Function uncompress($content, $mode, $uncompressedSize, $targetFileName=false){
- switch($mode){
- case 0:
- // Not compressed
- return $content;
- case 1:
- $this->debugMsg(2, "Shrunk mode is not supported... yet?");
- return false;
- case 2:
- case 3:
- case 4:
- case 5:
- $this->debugMsg(2, "Compression factor ".($mode-1)." is not supported... yet?");
- return false;
- case 6:
- $this->debugMsg(2, "Implode is not supported... yet?");
- return false;
- case 7:
- $this->debugMsg(2, "Tokenizing compression algorithm is not supported... yet?");
- return false;
- case 8:
- // Deflate
- return gzinflate($content, $uncompressedSize);
- case 9:
- $this->debugMsg(2, "Enhanced Deflating is not supported... yet?");
- return false;
- case 10:
- $this->debugMsg(2, "PKWARE Date Compression Library Impoloding is not supported... yet?");
- return false;
- default:
- $this->debugMsg(2, "Unknown uncompress method: $mode");
- return false;
- }
- }
-
-
- Function debugMsg($level, $string){
- if($this->debug)
- if($level == 1)
- echo "<b style='color: #777'>dUnzip2:</b> $string<br>";
- if($level == 2)
- echo "<b style='color: #F00'>dUnzip2:</b> $string<br>";
- }
-}
-?>-
\ No newline at end of file
diff --git a/util/ArchiveZip.class.php b/util/ArchiveZip.class.php
@@ -1,89 +0,0 @@
-<?php
-
-
-/**
- * This source is taken from http://www.zend.com/zend/spotlight/creating-zip-files1.php
- * Thank you!
- */
-class ArchiveZip
-{
- var $datasec = array();
- var $ctrl_dir = array();
- var $eof_ctrl_dir = "\x50\x4b\x05\x06\x00\x00\x00\x00";
- var $old_offset = 0;
-
-
- function add_file($data, $name)
- {
- $name = str_replace("\\", "/", $name);
- $unc_len = strlen($data);
- $crc = crc32($data);
- $zdata = gzcompress($data);
- $zdate = substr ($zdata, 2, -4);
- $c_len = strlen($zdata);
-
-
-
- $fr = "\x50\x4b\x03\x04";
- $fr .= "\x14\x00";
- $fr .= "\x00\x00";
- $fr .= "\x08\x00";
- $fr .= "\x00\x00\x00\x00";
- $fr .= pack("V",$crc);
- $fr .= pack("V",$c_len);
- $fr .= pack("V",$unc_len);
- $fr .= pack("v", strlen($name) );
- $fr .= pack("v", 0 );
- $fr .= $name;
- $fr .= $zdata;
- $fr .= pack("V",$crc);
- $fr .= pack("V",$c_len);
- $fr .= pack("V",$unc_len);
-
- $this -> datasec[] = $fr;
-
-
-
- $new_offset = strlen(implode("", $this->datasec));
-
- $cdrec = "\x50\x4b\x01\x02";
- $cdrec .="\x00\x00";
- $cdrec .="\x14\x00";
- $cdrec .="\x00\x00";
- $cdrec .="\x08\x00";
- $cdrec .="\x00\x00\x00\x00";
- $cdrec .= pack("V",$crc);
- $cdrec .= pack("V",$c_len);
- $cdrec .= pack("V",$unc_len);
- $cdrec .= pack("v", strlen($name) );
- $cdrec .= pack("v", 0 );
- $cdrec .= pack("v", 0 );
- $cdrec .= pack("v", 0 );
- $cdrec .= pack("v", 0 );
- $cdrec .= pack("V", 32 );
- $cdrec .= pack("V", $this -> old_offset );
-
- $this -> old_offset = $new_offset;
-
- $cdrec .= $name;
- $this -> ctrl_dir[] = $cdrec;
- }
-
-
- function file() {
- $data = implode("", $this -> datasec);
- $ctrldir = implode("", $this -> ctrl_dir);
-
- return
- $data.
- $ctrldir.
- $this -> eof_ctrl_dir.
- pack("v", sizeof($this -> ctrl_dir)).
- pack("v", sizeof($this -> ctrl_dir)).
- pack("V", strlen($ctrldir)).
- pack("V", strlen($data)).
- "\x00\x00";
- }
-}
-
-?>-
\ No newline at end of file
diff --git a/util/Code.class.php b/util/Code.class.php
@@ -1,44 +0,0 @@
-<?php
-// OpenRat Content Management System
-// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-/**
- * @author $Author$
- * @version $Revision$
- * @package openrat.services
- */
-class Code extends Macro
-{
- var $code = '';
-
- function execute()
- {
- if ( substr($this->code,0,5) != '<?php' )
- $this->code = "<?php\n".$this->code."\n?>";
-
- $tmp = FileUtils::getTempDir().'/openratMacro';
- $tmp .= '.code.php.tmp';
-
- $f = fopen( $tmp,'w' );
- fwrite( $f,$this->code );
- fclose( $f );
-
- require( $tmp ); // Ausfuehren des temporaeren PHP-Codes
-
- unlink( $tmp );
- }
-}-
\ No newline at end of file
diff --git a/util/Dynamic.class.php b/util/Dynamic.class.php
@@ -1,34 +0,0 @@
-<?php
-// OpenRat Content Management System
-// Copyright (C) 2002-2013 Jan Dankert, cms@jandankert.de
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-
-/**
- * Oberklasse für dynamische Klassen ("Makros").
- *
- * Diese Klasse dient nur der Abwärtskompabilität. Neue dynamische Klassen
- * (ab jetzt "Makros") sollten von der Klasse Macro erben.
- *
- * @author Jan Dankert
- * @package openrat.services
- */
-class Dynamic extends Macro
-{
- // Keine weitere Implementierung
-}
-
-?>-
\ No newline at end of file
diff --git a/util/FileUtils.class.php b/util/FileUtils.class.php
@@ -1,102 +0,0 @@
-<?php
-
-/**
- * Werkzeugklasse f�r Datei-Operationen.
- *
- */
-class FileUtils
-{
- /**
- * Fuegt einen Slash ("/") an das Ende an, sofern nicht bereits vorhanden.
- *
- * @param String $pfad
- * @return Pfad mit angeh�ngtem Slash.
- */
- public static function slashify($pfad)
- {
- if ( substr($pfad,-1,1) == '/')
- return $pfad;
- else
- return $pfad.'/';
- }
-
-
-
- /**
- * Liefert einen Verzeichnisnamen fuer temporaere Dateien.
- */
- public static function createTempFile()
- {
- global $conf;
- $tmpdir = @$conf['cache']['tmp_dir'];
- $tmpfile = @tempnam( $tmpdir,'openrat_tmp' );
-
- // 2. Versuch: Temp-Dir aus "upload_tmp_dir".
- if ( $tmpfile === FALSE )
- {
- $tmpdir = ini_get('upload_tmp_dir');
- $tmpfile = @tempnam( $tmpdir,'openrat_tmp' );
- }
-
- elseif ( $tmpfile === FALSE )
- {
- $tmpfile = @tempnam( '','openrat_tmp' );
- }
-
- return $tmpfile;
- }
-
-
- /**
- * Liefert einen Verzeichnisnamen fuer temporaere Dateien.
- */
- public static function getTempDir()
- {
- $tmpfile = FileUtils::createTempFile();
-
- $tmpdir = dirname($tmpfile);
- @unlink($tmpfile);
-
- return FileUtils::slashify( $tmpdir );
- }
-
-
-
- /**
- * Liest die Dateien aus dem angegebenen Ordner in ein Array.
- *
- * @param $dir Verzeichnis, welches gelesen werden soll
- * @return Array Liste der Dateien im Ordner
- */
- public static function readDir($dir)
- {
- $dir = FileUtils::slashify($dir);
- $dateien = array();
-
- if ( !is_dir($dir) )
- {
- return false;
- }
-
- if ( $dh = opendir($dir) )
- {
- while( ($verzEintrag = readdir($dh)) !== false )
- {
- if ( substr($verzEintrag,0,1) != '.' )
- {
- $dateien[] = $verzEintrag;
- }
- }
- closedir($dh);
-
- return $dateien;
- }
- else
- {
- die('unable to open directory: '.$dir);
- }
-
- }
-}
-
-?>-
\ No newline at end of file
diff --git a/util/Ftp.class.php b/util/Ftp.class.php
@@ -1,246 +0,0 @@
-<?php
-// OpenRat Content Management System
-// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-
-/**
- * Darstellen einer FTP-Verbindung, das beinhaltet
- * das Login, das Kopieren von Dateien sowie praktische
- * FTP-Funktionen
- *
- * @author $Author$
- * @version $Revision$
- * @package openrat.services
- */
-class Ftp
-{
- var $verb;
- var $url;
- var $log = array();
-
- var $passive = false;
-
- var $ok = true;
-
- private $path;
-
-
- // Konstruktor
- function __construct( $url )
- {
- $this->connect( $url );
- }
-
-
- // Aufbauen der Verbindung
- function connect( $url )
- {
- $this->url = $url;
-
- global $conf;
-
- $conf_ftp = $conf['publish']['ftp'];
- $ftp = parse_url( $this->url );
-
- // Die projektspezifischen Werte gewinnen bei �berschneidungen mit den Default-Werten
- $ftp = array_merge($conf_ftp,$ftp);
-
- // Nur FTP und FTPS (seit PHP 4.3) erlaubt
- if ( !in_array(@$ftp['scheme'],array('ftp','ftps')) )
- {
- $this->log[] = 'Unknown scheme in FTP Url: '.@$ftp['scheme'];
- $this->log[] = 'Only FTP (and FTPS, if compiled in) are supported';
- $this->ok = false;
- return;
- }
-
- if ( function_exists('ftp_ssl_connect') && $ftp['scheme'] == 'ftps' )
- $this->verb = @ftp_ssl_connect( $ftp['host'],$ftp['port'] );
- else
- $this->verb = @ftp_connect( $ftp['host'],$ftp['port'] );
-
- if ( !$this->verb )
- {
- $this->log[] = 'Cannot connect to '.$ftp['scheme'].'-server: '.$ftp['host'].':'.$ftp['port'];
- $this->ok = false;
-
- Logger::error('Cannot connect to '.$ftp['host'].':'.$ftp['port']);
- return;
- }
-
- $this->log[] = 'Connected to FTP server '.$ftp['host'].':'.$ftp['port'];
-
- if ( empty($ftp['user']) )
- {
- $ftp['user'] = 'anonymous';
- $ftp['pass'] = 'openrat@openrat.de';
- }
-
- if ( ! ftp_login( $this->verb,$ftp['user'],$ftp['pass'] ) )
- {
- $this->log[] = 'Unable to login as user '.$ftp['user'];
- $this->ok = false;
- return;
- }
-
- $this->log[] = 'Logged in as user '.$ftp['user'];
-
- $pasv = (!empty($ftp['fragment']) && $ftp['fragment'] == 'passive' );
-
- $this->log[] = 'entering passive mode '.($pasv?'on':'off');
- if ( ! ftp_pasv($this->verb,true) )
- {
- $this->log[] = 'cannot switch PASV mode';
- $this->ok = false;
- return;
- }
-
- if ( !empty($ftp['query']) )
- {
- parse_str( $ftp['query'],$ftp_var );
-
- if ( isset( $ftp_var['site'] ) )
- {
- $site_commands = explode( ',',$ftp_var['site'] );
- foreach( $site_commands as $cmd )
- {
- $this->log .= 'executing SITE command: '.$cmd;
-
- if ( ! @ftp_site( $this->verb,$cmd ) )
- {
- $this->log[] = 'unable to do SITE command: '.$cmd;
- $this->ok = false;
- return;
- }
- }
- }
- }
-
- $this->path = rtrim( $ftp['path'],'/' );
-
- $this->log[] = 'Changing directory to '.$this->path;
-
- if ( ! @ftp_chdir( $this->verb,$this->path ) )
- {
- $this->log[] = 'unable CHDIR to directory: '.$this->path;
- $this->ok = false;
- return;
- }
- }
-
-
- /**
- * Kopieren einer Datei vom lokalen System auf den FTP-Server.
- *
- * @param String Quelle
- * @param String Ziel
- * @param int FTP-Mode (BINARY oder ASCII)
- */
- function put( $source,$dest )
- {
- if ( ! $this->ok )
- return;
-
- $ftp = parse_url( $this->url );
-
- $dest = $this->path.'/'.$dest;
-
- $this->log .= "Copying file: $source -> $dest ...\n";
-
- $mode = FTP_BINARY;
- $p = strrpos( basename($dest),'.' ); // Letzten Punkt suchen
-
- if ($p!==false) // Wennn letzten Punkt gefunden, dann dort aufteilen
- {
- $extension = substr( basename($dest),$p+1 );
- $type = config('mime-types',$extension);
- if ( substr($type,0,5) == 'text/')
- $mode = FTP_ASCII;
- }
-
- Logger::debug("FTP PUT target:$dest mode:".(($mode==FTP_ASCII)?'ascii':'binary'));
-
- if ( !@ftp_put( $this->verb,$dest,$source,$mode ) )
- {
- if ( !$this->mkdirs( dirname($dest) ) )
- return; // Fehler.
-
- ftp_chdir( $this->verb,$this->path );
-
- if ( ! @ftp_put( $this->verb,$dest,$source,$mode ) )
- {
- $this->ok = false;
- $this->log[] = 'FTP PUT failed...';
- $this->log[] = 'source : '.$source;
- $this->log[] = 'destination: '.$dest;
- return;
- }
-
- }
- }
-
-
-
- /**
- * Private Methode zum rekursiven Anlegen von Verzeichnissen.
- *
- * @param String Pfad
- * @return boolean true, wenn ok
- */
- function mkdirs( $strPath )
- {
- if ( @ftp_chdir($this->verb,$strPath) )
- return true; // Verzeichnis existiert schon :)
-
- $pStrPath = dirname($strPath);
-
- if ( !$this->mkdirs($pStrPath) )
- return false;
-
- if ( ! @ftp_mkdir($this->verb,$strPath) )
- {
- $this->ok = false;
- $this->log[] = "failed to create remote directory: $strPath";
- }
-
- return $this->ok;
- }
-
-
-
- /**
- * Schlie�en der FTP-Verbindung.<br>
- * Sollte unbedingt aufgerufen werden, damit keine unn�tigen Sockets aufbleiben.
- */
- function close()
- {
- if ( !$this->ok ) // Noch alles ok?
- return;
-
- if ( ! @ftp_quit( $this->verb ) )
- {
- // Das Schlie�en der Verbindung hat nicht funktioniert.
- // Eigentlich k�nnten wir das ignorieren, aber wir sind anst�ndig und melden eine Fehler.
- $this->log[] = 'failed to close connection';
- $this->ok = false;
- return;
- }
- }
-}
-
-
-?>-
\ No newline at end of file
diff --git a/util/GlobalFunctions.class.php b/util/GlobalFunctions.class.php
@@ -1,71 +0,0 @@
-<?php
-
-/**
- * Bereitstellen von globalen Funktionen
- * @author $Author$
- * @version $Revision$
- * @package openrat.services
- */
-class GlobalFunctions
-{
- public static function getIsoCodes()
- {
- global $conf_php;
-
- $iso = parse_ini_file( './language/lang.ini.'.$conf_php );
- asort( $iso );
- return $iso;
- }
-
-
- public static function lang( $text )
- {
- global $SESS;
- $text = strtoupper($text);
-
- if ( isset( $SESS['lang'][$text] ) )
- {
- return $SESS['lang'][$text];
- }
- else
- {
- return( '?'.$text.'?' );
- }
- }
-
-
- # Spracheinstellungen laden
-
- public static function language_from_http()
- {
- global $SESS,
- $HTTP_SERVER_VARS,
- $conf_php,
- $conf;
-
- $languages = $HTTP_SERVER_VARS['HTTP_ACCEPT_LANGUAGE'];
- $languages = explode(',',$languages);
- foreach( $languages as $l )
- {
- $l = substr($l,0,2);
- if ( file_exists("./language/$l.ini.$conf_php") )
- return( $l );
- }
-
- // Keine passende Sprache im HTTP-Header gefunden
- return $conf['global']['default_language'];
- }
-
-
- public static function language_read( $l='' )
- {
- global $SESS,
- $HTTP_SERVER_VARS,
- $conf_php;
-
- $l = language_from_http();
- $SESS['lang'] = parse_ini_file( "./language/$l.ini.$conf_php" );
- }
-}
-
-?>-
\ No newline at end of file
diff --git a/util/Html.class.php b/util/Html.class.php
@@ -1,178 +0,0 @@
-<?php
-// OpenRat Content Management System
-// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-
-/**
- * Bereitstellen von Methoden fuer die Darstellung von HTML-Elementen
- *
- * @author $Author$
- * @version $Revision$
- * @package openrat.services
- */
-class Html
-{
- public static function error( $field )
- {
- global $inputErrors;
-
- if ( isset($inputErrors[$field]) )
- return '<span class="error">'.lang($inputErrors[$field]).'</span';
- }
-
-
-
- /**
- * Ausgabe eines Variablenwertes.<br>
- */
- public static function debug( $wert, $text='' )
- {
- echo "<strong>DEBUG: $text (".gettype($wert).")</strong><br/>";
- echo "<pre>";
- print_r($wert);
- echo "</pre>";
- }
-
-
- /**
- * Erzeugt eine relative Url innerhalb von Openrat
- *
- * @param string Aktion, die aufgerufen werden soll
- * @param string Unteraktion, die innerhalb der Aktion aufgerufen werden soll
- * @param int Id fuer diesen Aufruf
- * @param array Weitere beliebige Parameter
- */
- public static function url( $action,$subaction='',$id='',$params=array() )
- {
- if ( intval($id)==0 )
- $id='-';
-
- global $conf;
-
- if ( is_array($action) )
- {
- $params = $action;
-
- if ( isset($params['callAction']) )
- {
- $params['subaction'] = $params['callAction'];
- unset( $params['callAction'] );
- unset( $params['callSubaction'] );
- }
-
-
- if ( !isset($params['action' ])) $params['action' ] = '';
- if ( !isset($params['subaction'])) $params['subaction'] = '';
- if ( !isset($params['id' ])) $params['id' ] = '';
- $action = $params['action' ];
- $subaction = $params['subaction'];
- $id = $params['id' ];
- unset( $params['action' ] );
- unset( $params['subaction'] );
- unset( $params['id' ] );
- $params['old']='true';
- }
-
- // Session-Id ergaenzen
- if ( $conf['interface']['url']['add_sessionid'] )
- $params[ session_name() ] = session_id();
-
- if ( config('security','use_post_token') )
- $params[ 'token'] = token();
-
- $fake_urls = $conf['interface']['url']['fake_url' ];
- $url_format = $conf['interface']['url']['url_format'];
-
- if ( isset($params['objectid']) && !isset($params['id']) )
- $params['id'] = $params['objectid'];
-
- if ( $fake_urls )
- {
-// if ( $id != '' )
-// $id = '.'.$id;
- }
- else
- {
- global $view;
- $params[REQ_PARAM_ACTION ] = $action;
- $params[REQ_PARAM_SUBACTION] = $subaction;
- $params[REQ_PARAM_ID ] = $id;
-
- if ( !isset($params[REQ_PARAM_TARGET]))
- $params[REQ_PARAM_TARGET ] = $view;
- }
-
- if ( count($params) > 0 )
- {
- $urlParameterList = array();
- foreach( $params as $var=>$value )
- {
- $urlParameterList[] = urlencode($var).'='.urlencode($value);
- }
- $urlParameter = '?'.implode('&',$urlParameterList);
- }
- else
- {
- $urlParameter = '';
- }
-
- if ( @$conf['interface']['url']['index'] )
- $controller_file_name = '';
- else
- $controller_file_name = OR_CONTROLLER_FILE.'.'.PHP_EXT;
-
- $prefix = './';
-
- if ( $fake_urls )
- $src = sprintf( $url_format,$action,$subaction,$id,session_id() ).$urlParameter;
- else
- $src = $prefix.$controller_file_name.$urlParameter;
-
- return $src;
- }
-
-
-
- public static function complete_tag($tagname,$attributes)
- {
- $text = '<'.$tagname;
- foreach( $attributes as $attribute_name=>$attribute_value )
- if ( !empty($attribute_value) )
- $text .= ' '.$attribute_name.'="'.$attribute_value.'"';
- $text .= ' />';
- return $text;
- }
-
-
-
- public static function open_tag($tagname,$attributes)
- {
- $text = '<'.$tagname;
- foreach( $attributes as $attribute_name=>$attribute_value )
- if ( !empty($attribute_value) )
- $text .= ' '.$attribute_name.'="'.$attribute_value.'"';
- $text .= '>';
- return $text;
- }
-
-
- public static function close_tag($tagname)
- {
- return '</'.$tagname.'>';
- }
-}
-?>-
\ No newline at end of file
diff --git a/util/Http.class.php b/util/Http.class.php
@@ -1,610 +0,0 @@
-<?php
-// OpenRat Content Management System
-// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-
-/**
- * Kapselung einer HTTP-Anfrage.<br>
- * Unter Beruecksichtigung von RFC 1945.<br>
- *
- * @author Jan Dankert
- * @package openrat.services
- */
-class Http
-{
- public $header = array();
-
- public $url = array();
- public $responseHeader = array();
- public $requestParameter = array();
- public $urlParameter = array();
-
- /**
- * HTTP-Request-Typ.<br>
- * Muss entweder "GET" oder "POST" sein.<br>
- * Default: "GET".
- *
- * @var String Request-Typ
- */
- public $method = 'GET';
- public $error = '';
- public $status = '';
- public $body = '';
-
- public $httpCmd = '';
-
-
-
- /**
- * Erzeugt eine HTTP-Anfrage.
- *
- * @param String URL
- * @return Http
- */
- public function Http( $url = '' )
- {
- $this->setURL( $url );
- $this->header['User-Agent'] = 'Mozilla/5.0 (OpenRat CMS)';
- $this->header['Connection'] = 'close';
- }
-
-
-
- /**
- * Setzt die URL.
- *
- * @param String URL
- */
- function setURL( $url )
- {
- $this->url = parse_url($url);
-
- if ( empty($this->url['host']) && !empty($this->url['path']) )
- {
- $this->url['host'] = basename($this->url['path']);
- $this->url['path'] = '/';
- }
-
- if ( empty($this->url['path']) )
- $this->url['path'] = '/';
-
- if ( !isset($this->url['port']) )
- if ( !isset($this->url['scheme']) )
- {
- $this->url['scheme'] = 'http'; // Standard-Port.
- $this->url['port'] = 80; // Standard-Port.
- }
- elseif ( $this->url['scheme'] == 'https' )
- $this->url['port'] = 443; // SSL-Port.
- else
- $this->url['port'] = 80; // Standard-Port.
-
- if ( !empty($this->url['query']) )
- parse_str( $this->url['query'],$this->urlParameter );
-
- }
-
-
-
- /**
- * Setzt Authentisierungsinformationen in den HTTP-Request.<br>
- *
- * @param String Benutzername
- * @param String Kennwort
- */
- public function setBasicAuthentication( $user, $password )
- {
- $this->header['Authorization'] = 'Basic '.base64_encode($user.':'.$password);
- }
-
-
-
- /**
- * Erzeugt eine HTTP-Parameterstring mit allen Parametern.
- *
- * @param withPraefixQuestionMark Praefix mit Fragezeichen (fuer GET-Anfragen)
- * @return String URL-Parameter
- */
- public function getParameterString( $withPraefixQuestionMark=false )
- {
- $parameterString = '';
- $parameter = $this->urlParameter + $this->requestParameter;
-
- if ( ! empty($parameter) )
- {
- foreach( $this->requestParameter as $paramName => $paramValue )
- {
- if ( strlen($parameterString) > 0)
- $parameterString .= '&';
- elseif ( $withPraefixQuestionMark )
- $parameterString .= '?';
-
- $parameterString .= urlencode($paramName) . '=' .urlencode($paramValue);
- }
- }
-
- return $parameterString;
- }
-
-
- /**
- * Liefert die URL des Requests.
- *
- * @return String URL
- */
- public function getUrl()
- {
- $location = $this->url['scheme'];
- $location .= '://';
- $location .= $this->url['host'];
- if ( $this->url['scheme'] == 'http' && $this->url['port'] != 80 ||
- $this->url['scheme'] == 'https' && $this->url['port'] != 443 )
- $location .= ':'.$this->url['port'];
- $location .= $this->url['path'];
-
- $location .= $this->getParameterString(true);
-
- if ( isset($this->url['fragment']) )
- $location .= '#'.$this->url['fragment'];
-
- return $location;
- }
-
-
- /**
- * Sendet eine Redirect-Anweisung mit der aktuellen URL an den Browser.
- */
- public function sendRedirect()
- {
- $location = $this->getUrl();
-
- header('Location: '.$location);
- exit;
- }
-
-
- /**
- * Führt einen HTTP-Request durch.
- *
- * @return boolean Erfolg der Anfrage.
- */
- public function request()
- {
- $this->error = '';
- $this->status = '';
-
- $errno = 0;
- $errstr = '';
-
- if ( empty($this->url['host']) )
- {
- $this->error = "No hostname specified";
- return false;
- }
-
- foreach( $this->header as $header_key=>$header_value )
- {
- if ( is_numeric( $header_key ) )
- {
- $dp = strpos($header_value,':');
- if ( $dp!==FALSE)
- $this->header[substr($header_value,0,$dp)] = substr($header_value,$dp+1);
- unset($this->header[$header_key]);
- }
- }
-
- $parameterString = $this->getParameterString();
-
- if ( $this->method == 'POST' )
- {
- $this->header['Content-Type' ] = 'application/x-www-form-urlencoded';
- $this->header['Content-Length'] = strlen($parameterString);
- }
-
- // Accept-Header setzen, falls noch nicht vorhanden.
- if ( !array_key_exists('Accept',$this->header) )
- $this->header['Accept'] = '*/*';
-
- $this->responseHeader = array();
-
- // RFC 1945 (Section 9.3) says:
- // A user agent should never automatically redirect a request
- // more than 5 times, since such redirections usually indicate an infinite loop.
- for( $r=1; $r<=5; $r++ )
- {
- $this->header['Host'] = $this->url['host'];
-
- // Die Funktion fsockopen() erwartet eine Protokollangabe (bei TCP optional, bei SSL notwendig).
- if ( $this->url['scheme'] == 'https' || $this->url['port'] == '443' )
- $prx_proto = 'ssl://'; // SSL
- else
- $prx_proto = 'tcp://'; // Default
-
- $fp = @fsockopen ($prx_proto.$this->url['host'],$this->url['port'], $errno, $errstr, 30);
-
- if ( !$fp || !is_resource($fp) )
- {
- // Keine Verbindung zum Host moeglich.
- $this->error = "Connection refused: '".$prx_proto.$this->url['host'].':'.$this->url['port']." - $errstr ($errno)";
- return false;
- }
- else
- {
-
- $lb = "\r\n";
- $http_get = $this->url['path'];
-
- $request_header = array( $this->method.' '.$http_get.' HTTP/1.0');
-
- foreach($this->header as $header_key=>$header_value)
- $request_header[] = $header_key.': '.$header_value;
-
- $http_request = implode($lb,$request_header).$lb.$lb;
-
- if ( $this->method == 'GET')
- if ( !empty($parameterString) )
- $http_get .= '?'.$parameterString;
-
- if ( $this->method == 'POST' )
- $http_request .= $parameterString;
-
- if (!is_resource($fp)) {
- $this->error = 'Connection lost after connect: '.$prx_proto.$this->url['host'].':'.$this->url['port'];
- return false;
- }
- fputs($fp, $http_request); // Die HTTP-Anfrage zum Server senden.
-
- // Jetzt erfolgt das Auslesen der HTTP-Antwort.
- $isHeader = true;
-
- // RFC 1945 (Section 6.1) schreibt als Statuszeile folgendes Format vor
- // "HTTP/" 1*DIGIT "." 1*DIGIT SP 3DIGIT SP
- if (!is_resource($fp)) {
- $this->error = 'Connection lost during transfer: '.$this->url['host'].':'.$this->url['port'];
- return false;
- }
- elseif (!feof($fp)) {
- $line = fgets($fp,1028);
- $this->status = substr($line,9,3);
- }
- else
- {
- $this->error = 'Unexpected EOF while reading HTTP-Response';
- return false;
- }
-
- $this->body = '';
- while (!feof($fp)) {
- $line = fgets($fp,1028);
- if ( $isHeader && trim($line)=='' ) // Leerzeile nach Header.
- {
- $isHeader = false;
- }
- elseif( $isHeader )
- {
- list($headerName,$headerValue) = explode(': ',$line) + array(1=>'');
- $this->responseHeader[$headerName] = trim($headerValue);
- }
- else
- {
- $this->body .= $line;
- }
- }
- fclose($fp); // Verbindung brav schlie�en.
-
-
- // RFC 1945 (Section 6.1.1) schreibt
- // "[...] However, applications must understand the class of any status code, as
- // indicated by the first digit"
- // Daher interessiert uns nur die erste Stelle des 3-stelligen HTTP-Status.
-
- // 301 Moved Permanently
- // 302 Moved Temporarily
- if ( $this->status == '301' ||
- $this->status == '302' )
- {
- $location = @$this->responseHeader['Location'];
- if ( empty($location) )
- {
- $this->error = '301/302 Response without Location-header';
- return false;
- }
-
- //Html::debug($this->url,"alte URL");
- //Html::debug($location,"NEUES REDIRECT AUF");
- $this->setURL($location);
- continue; // Naechster Versuch mit umgeleiteter Adresse.
- }
-
- // RFC 1945 (Section 6.1.1) schreibt
- // "2xx: Success - The action was successfully received, understood, and accepted."
- elseif ( substr($this->status,0,1) == '2' )
- {
- return true;
- }
- elseif ( substr($this->status,0,1) == '4' )
- {
- $this->error = 'Client Error: '.$this->status;
- return false;
- }
- elseif ( substr($this->status,0,1) == '5' )
- {
- $this->error = 'Server Error: '.$this->status;
- return false;
- }
- else
- {
- $this->error = 'Unexpected HTTP-Status: '.$this->status. '; this is mostly a client error, sorry.';
- return false;
- }
- }
-
- $this->error = 'Too much redirects, infinite loop assumed. Exiting. Last URL: '.$http_get;
- return false;
-
- }
-
- }
-
-
- /**
- * Aus dem HTTP-Header werden die vom Browser angeforderten Sprachen
- * gelesen.<br>
- * Es wird eine Liste von Sprachen erzeugt.<br>
- *
- * Beispiel:
- * 'de_DE','de','en_GB','en' ... usw.<br>
- * Wenn der Browser 'de_DE' anfordert, wird hier zusätzlich
- * auch 'de' (als Fallback) ermittelt.
- *
- * @static
- * @return Array
- */
- public static function getLanguages()
- {
- global $HTTP_SERVER_VARS;
-
- $languages = array();
- $http_languages = @$HTTP_SERVER_VARS['HTTP_ACCEPT_LANGUAGE'];
- foreach( explode(',',$http_languages) as $l )
- {
- list($part) = explode(';',$l); // Priorit�ten ignorieren.
- $languages[] = trim($part);
-
- // Aus "de_DE" das "de" extrahieren.
- $languages[] = current(explode('_',str_replace('-','_',trim($part))));
- }
-
- return array_unique( $languages );
- }
-
-
- /**
- * Ermittelt die aktuelle HTTP-Adresse des Requests (inkl. Pfad, jedoch ohne Datei).
- *
- * @return String URL
- */
- public static function getServer()
- {
- $https = getenv('HTTPS');
-
- if ( $https )
- $server = 'https://';
- else
- $server = 'http://';
-
- $server .= getenv('SERVER_NAME').dirname(getenv('REQUEST_URI'));
-
- return $server;
- }
-
-
-
- /**
- * Server-Fehlermeldung anzeigen.<br>
- *
- * Erzeugt einen "HTTP 501 Internal Server Error". Zusaetzlich
- * wird ein 'rollback' auf der Datenbank ausgefaehrt.
- *
- * @param String $message Eigener Hinweistext
- */
- public static function serverError($message,$reason='')
- {
- if ( class_exists('Session'))
- {
- $db = Session::getDatabase();
- if ( is_object( $db ) )
- $db->rollback();
- }
-
- if ( class_exists('Logger'))
- Logger::warn($message."\n".$reason);
-
- Http::sendStatus(501,'Internal Server Error',$message,$reason);
- }
-
-
-
- /**
- * Der Benutzer ist nicht autorisiert, eine Aktion auszufuehren.
- *
- * Diese Funktion erzeugt einen "HTTP 403 Not Authorized" und das
- * Skript wird beendet.
- *
- * @param String $text Text
- * @param String $message Eigener Hinweistext
- */
- public static function notAuthorized($message='')
- {
- Logger::warn("Security warning: $message");
- Http::sendStatus(403,'Not authorized',$message);
- }
-
-
-
-
-
-
- /**
- * Nichts gefunden.
- *
- * Diese Funktion erzeugt einen "HTTP 404 Not found" und das
- * Skript wird beendet.
- *
- * @param String $text Text
- * @param String $message Eigener Hinweistext
- */
- public static function notFound($text,$message)
- {
- Http::sendStatus(404,'Not found',$message);
- }
-
-
-
- /**
- * Kein Inhalt.
- *
- * Die HTTP-Antwort stellt gegenüber dem Client klar, dass es keinen Inhalt gibt.
- */
- public static function noContent()
- {
- header('HTTP/1.0 204 No Content');
- exit;
- }
-
-
-
- /**
- * Schickt einen HTTP-Status zum Client und beendet das Skript.
- *
- * @param Integer $status HTTP-Status (ganzzahlig) (Default: 501)
- * @param String $text HTTP-Meldung (Default: 'Internal Server Error')
- * @param String $message Eigener Hinweistext (Default: leer)
- * @param String $reason Technischer Grund (Default: leer)
- */
- public static function sendStatus( $status=501,$text='Internal Server Error',$message='',$reason='' )
- {
- if ( headers_sent() )
- {
- echo "$status $text\n$message";
- exit;
- }
-
- header('HTTP/1.0 '.intval($status).' '.$text);
-
-
- $types = Http::getAccept();
-
- if ( @$_REQUEST['output']=='json' || sizeof($types)==1 && in_array('application/json',$types) )
- {
- header('Content-Type: application/json');
- require_once( OR_SERVICECLASSES_DIR."JSON.class.".PHP_EXT );
- $json = new JSON();
- echo $json->encode( array('status'=>$status,'error'=>$text,'description'=>$message,'reason'=>$reason) );
- }
- elseif ( @$_REQUEST['output']=='xml' || sizeof($types)==1 && in_array('application/xml',$types) )
- {
- header('Content-Type: application/xml');
- require_once( OR_SERVICECLASSES_DIR."XML.class.".PHP_EXT );
- $xml = new XML();
- $xml->root='error';
- echo $xml->encode( array('status'=>$status,'error'=>$text,'description'=>$message,'reason'=>$reason) );
- }
- else
- {
- header('Content-Type: text/html');
- $message = htmlentities($message);
- $reason = htmlentities($reason );
- $signature = OR_TITLE.' '.OR_VERSION.' '.getenv('SERVER_SOFTWARE');
- echo <<<HTML
-<html>
-<head><title>$status $text - OpenRat</title></head>
-<body>
-<h1>$text</h1>
-<p>$message</p>
-<pre>$reason</pre>
-<hr>
-<address>$signature</adddress>
-</body>
-</html>
-HTML;
- }
- exit;
- }
-
-
- /**
- * Liefert den Mime-Type, den der Browser (oder besser: HTTP-Client) wünscht.
- *
- * @return Array Mime-Typen, welche vom User-Agent akzeptiert werden.
- */
- public static function getAccept()
- {
- $httpAccept = getenv('HTTP_ACCEPT');
- return $types = explode(',',$httpAccept);
- }
-
-
-
- /**
- * Liefert die IPv4-Adresse des Clients. Falls der Request durch einen Proxy kam, wird
- * versucht, die echte IP-Adresse aus dem Anfrageheader zu ermitteln.
- *
- * @return Client-IPv4-Adresse
- */
- public static function getClientIP()
- {
- $ip = '';
-
- if ( isset($_SERVER["HTTP_X_FORWARDED_FOR"]) )
- {
- $ip = $_SERVER["HTTP_X_FORWARDED_FOR"];
- }
- elseif ( isset($_SERVER["HTTP_CLIENT_IP"]) )
- {
- $ip = $_SERVER["HTTP_CLIENT_IP"];
- }
- elseif ( isset($_SERVER["REMOTE_ADDR"]) )
- {
- $ip = $_SERVER["REMOTE_ADDR"];
- }
-
- return $ip;
- }
-
-
-
- /**
- * Ermittelt den TCP/IP-Port des Clients.
- * Achtung, bei Proxy-Zugriffen kann dies der Port des Proxys sein.
- *
- * @return TCP/IP-Port
- */
- public static function getClientPort()
- {
- $ip = '';
-
- if ( isset($_SERVER["REMOTE_PORT"]) )
- {
- $ip = $_SERVER["REMOTE_PORT"];
- }
-
- return $ip;
- }
-}
-
-?>-
\ No newline at end of file
diff --git a/util/JSON.class.php b/util/JSON.class.php
@@ -1,809 +0,0 @@
-<?php
-/**
- * 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>
- * @author Jan Dankert
- * @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_INDENT', "\t");
-
-/**
- * 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 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()
- {
- $this->use = SERVICES_JSON_LOOSE_TYPE;
- }
-
- /**
- * 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)
- {
- static $indentNr = 0;
-
- $indent = str_repeat(SERVICES_JSON_INDENT,$indentNr);
-
- 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))) {
- $indentNr++;
- $properties = array_map(array($this, 'name_value'),
- array_keys($var),
- array_values($var));
- $indentNr--;
-
- foreach($properties as $property) {
- if(JSON::isError($property)) {
- return $property;
- }
- }
-
- return "\n$indent".'{' ."\n$indent".SERVICES_JSON_INDENT. join(','."\n$indent".SERVICES_JSON_INDENT, $properties) ."\n$indent".'}'."\n$indent";
- }
-
- // treat it like a regular array
- $indentNr++;
- $elements = array_map(array($this, 'encode'), $var);
- $indentNr--;
-
- foreach($elements as $element) {
- if(JSON::isError($element)) {
- return $element;
- }
- }
-
- return "\n$indent".'['."\n$indent".SERVICES_JSON_INDENT. join(','."\n$indent".SERVICES_JSON_INDENT, $elements) . "\n$indent".']'."\n$indent";
-
- case 'object':
- $vars = get_object_vars($var);
-
- $indentNr++;
- $properties = array_map(array($this, 'name_value'),
- array_keys($vars),
- array_values($vars));
- $indentNr--;
-
- foreach($properties as $property) {
- if(JSON::isError($property)) {
- return $property;
- }
- }
-
- return "\n$indent".'{' ."\n$indent".SERVICES_JSON_INDENT. join(','."\n$indent".SERVICES_JSON_INDENT, $properties) . "\n$indent".'}'."\n$indent";
-
- default:
- return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS)
- ? 'null'
- : new 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(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) == 'json_error' ||
- is_subclass_of($data, 'json_error'))) {
- return true;
- }
-
- return false;
- }
-}
-
-
- /**
- * @todo Ultimately, this class shall be descended from PEAR_Error
- */
- class JSON_Error
- {
- function JSON_Error($message = 'unknown error', $code = null,
- $mode = null, $options = null, $userinfo = null)
- {
-
- }
- }
-
-
-?>
diff --git a/util/JSqueeze.class.php b/util/JSqueeze.class.php
@@ -1,1064 +0,0 @@
-<?php
-
-/*
- * Copyright (C) 2016 Nicolas Grekas - p@tchwork.com
- *
- * This library is free software; you can redistribute it and/or modify it
- * under the terms of the (at your option):
- * Apache License v2.0 (see provided LICENCE.ASL20 file), or
- * GNU General Public License v2.0 (see provided LICENCE.GPLv2 file).
- */
-
-//namespace Patchwork;
-
-/*
-*
-* This class shrinks Javascript code
-* (a process called minification nowadays)
-*
-* Should work with most valid Javascript code,
-* even when semi-colons are missing.
-*
-* Features:
-* - Removes comments and white spaces.
-* - Renames every local vars, typically to a single character.
-* - Renames also global vars, methods and properties, but only if they
-* are marked special by some naming convention. By default, special
-* var names begin with one or more "$", or with a single "_".
-* - Renames also local/global vars found in strings,
-* but only if they are marked special.
-* - Keep Microsoft's conditional comments.
-* - Output is optimized for later HTTP compression.
-*
-* Notes:
-* - Source code must be parse error free before processing.
-* - In order to maximise later HTTP compression (deflate, gzip),
-* new variables names are chosen by considering closures,
-* variables' frequency and characters' frequency.
-* - If you use with/eval then be careful.
-*
-* Bonus:
-* - Replaces false/true by !1/!0
-* - Replaces new Array/Object by []/{}
-* - Merges consecutive "var" declarations with commas
-* - Merges consecutive concatened strings
-* - Fix a bug in Safari's parser (http://forums.asp.net/thread/1585609.aspx)
-* - Can replace optional semi-colons by line feeds,
-* thus facilitating output debugging.
-* - Keep important comments marked with /*!...
-* - Treats three semi-colons ;;; like single-line comments
-* (http://dean.edwards.name/packer/2/usage/#triple-semi-colon).
-* - Fix special catch scope across browsers
-* - Work around buggy-handling of named function expressions in IE<=8
-*
-* TODO?
-* - foo['bar'] => foo.bar
-* - {'foo':'bar'} => {foo:'bar'}
-* - Dead code removal (never used function)
-* - Munge primitives: var WINDOW=window, etc.
-*/
-
-class JSqueeze
-{
- const
-
- SPECIAL_VAR_PACKER = '(\$+[a-zA-Z_]|_[a-zA-Z0-9$])[a-zA-Z0-9_$]*';
-
- public
-
- $charFreq;
-
- protected
-
- $strings,
- $closures,
- $str0,
- $str1,
- $argFreq,
- $specialVarRx,
- $keepImportantComments,
-
- $varRx = '(?:[a-zA-Z_$])[a-zA-Z0-9_$]*',
- $reserved = array(
- // Literals
- 'true','false','null',
- // ES6
- 'break','case','class','catch','const','continue','debugger','default','delete','do','else','export','extends','finally','for','function','if','import','in','instanceof','new','return','super','switch','this','throw','try','typeof','var','void','while','with','yield',
- // Future
- 'enum',
- // Strict mode
- 'implements','package','protected','static','let','interface','private','public',
- // Module
- 'await',
- // Older standards
- 'abstract','boolean','byte','char','double','final','float','goto','int','long','native','short','synchronized','throws','transient','volatile',
- );
-
- public function __construct()
- {
- $this->reserved = array_flip($this->reserved);
- $this->charFreq = array_fill(0, 256, 0);
- }
-
- /**
- * Squeezes a JavaScript source code.
- *
- * Set $singleLine to false if you want optional
- * semi-colons to be replaced by line feeds.
- *
- * Set $keepImportantComments to false if you want /*! comments to be removed.
- *
- * $specialVarRx defines the regular expression of special variables names
- * for global vars, methods, properties and in string substitution.
- * Set it to false if you don't want any.
- *
- * If the analysed javascript source contains a single line comment like
- * this one, then the directive will overwrite $specialVarRx:
- *
- * // jsqueeze.specialVarRx = your_special_var_regexp_here
- *
- * Only the first directive is parsed, others are ignored. It is not possible
- * to redefine $specialVarRx in the middle of the javascript source.
- *
- * Example:
- * $parser = new JSqueeze;
- * $squeezed_js = $parser->squeeze($fat_js);
- */
- public function squeeze($code, $singleLine = true, $keepImportantComments = true, $specialVarRx = false)
- {
- $code = trim($code);
- if ('' === $code) {
- return '';
- }
-
- $this->argFreq = array(-1 => 0);
- $this->specialVarRx = $specialVarRx;
- $this->keepImportantComments = !!$keepImportantComments;
-
- if (preg_match("#//[ \t]*jsqueeze\.specialVarRx[ \t]*=[ \t]*([\"']?)(.*)\\1#i", $code, $key)) {
- if (!$key[1]) {
- $key[2] = trim($key[2]);
- $key[1] = strtolower($key[2]);
- $key[1] = $key[1] && $key[1] != 'false' && $key[1] != 'none' && $key[1] != 'off';
- }
-
- $this->specialVarRx = $key[1] ? $key[2] : false;
- }
-
- // Remove capturing parentheses
- $this->specialVarRx && $this->specialVarRx = preg_replace('/(?<!\\\\)((?:\\\\\\\\)*)\((?!\?)/', '(?:', $this->specialVarRx);
-
- false !== strpos($code, "\r") && $code = strtr(str_replace("\r\n", "\n", $code), "\r", "\n");
- false !== strpos($code, "\xC2\x85") && $code = str_replace("\xC2\x85", "\n", $code); // Next Line
- false !== strpos($code, "\xE2\x80\xA8") && $code = str_replace("\xE2\x80\xA8", "\n", $code); // Line Separator
- false !== strpos($code, "\xE2\x80\xA9") && $code = str_replace("\xE2\x80\xA9", "\n", $code); // Paragraph Separator
-
- list($code, $this->strings) = $this->extractStrings($code);
- list($code, $this->closures) = $this->extractClosures($code);
-
- $key = "//''\"\"#0'"; // This crap has a wonderful property: it can not happen in any valid javascript, even in strings
- $this->closures[$key] = &$code;
-
- $tree = array($key => array('parent' => false));
- $this->makeVars($code, $tree[$key], $key);
- $this->renameVars($tree[$key], true);
-
- $code = substr($tree[$key]['code'], 1);
- $code = preg_replace("'\breturn !'", 'return!', $code);
- $code = preg_replace("'\}(?=(else|while)[^\$.a-zA-Z0-9_])'", "}\r", $code);
- $code = str_replace(array_keys($this->strings), array_values($this->strings), $code);
-
- if ($singleLine) {
- $code = strtr($code, "\n", ';');
- } else {
- $code = str_replace("\n", ";\n", $code);
- }
- false !== strpos($code, "\r") && $code = strtr(trim($code), "\r", "\n");
-
- // Cleanup memory
- $this->charFreq = array_fill(0, 256, 0);
- $this->strings = $this->closures = $this->argFreq = array();
- $this->str0 = $this->str1 = '';
-
- return $code;
- }
-
- protected function extractStrings($f)
- {
- if ($cc_on = false !== strpos($f, '@cc_on')) {
- // Protect conditional comments from being removed
- $f = str_replace('#', '##', $f);
- $f = str_replace('/*@', '1#@', $f);
- $f = preg_replace("'//@([^\n]+)'", '2#@$1@#3', $f);
- $f = str_replace('@*/', '@#1', $f);
- }
-
- $len = strlen($f);
- $code = str_repeat(' ', $len);
- $j = 0;
-
- $strings = array();
- $K = 0;
-
- $instr = false;
-
- $q = array(
- "'", '"',
- "'" => 0,
- '"' => 0,
- );
-
- // Extract strings, removes comments
- for ($i = 0; $i < $len; ++$i) {
- if ($instr) {
- if ('//' == $instr) {
- if ("\n" == $f[$i]) {
- $f[$i--] = ' ';
- $instr = false;
- }
- } elseif ($f[$i] == $instr || ('/' == $f[$i] && "/'" == $instr)) {
- if ('!' == $instr) {
- } elseif ('*' == $instr) {
- if ('/' == $f[$i + 1]) {
- ++$i;
- $instr = false;
- }
- } else {
- if ("/'" == $instr) {
- while (isset($f[$i + 1]) && false !== strpos('gmi', $f[$i + 1])) {
- $s[] = $f[$i++];
- }
- $s[] = $f[$i];
- }
-
- $instr = false;
- }
- } elseif ('*' == $instr) {
- } elseif ('!' == $instr) {
- if ('*' == $f[$i] && '/' == $f[$i + 1]) {
- $s[] = "*/\r";
- ++$i;
- $instr = false;
- } elseif ("\n" == $f[$i]) {
- $s[] = "\r";
- } else {
- $s[] = $f[$i];
- }
- } elseif ('\\' == $f[$i]) {
- ++$i;
-
- if ("\n" != $f[$i]) {
- isset($q[$f[$i]]) && ++$q[$f[$i]];
- $s[] = '\\'.$f[$i];
- }
- } elseif ('[' == $f[$i] && "/'" == $instr) {
- $instr = '/[';
- $s[] = '[';
- } elseif (']' == $f[$i] && '/[' == $instr) {
- $instr = "/'";
- $s[] = ']';
- } elseif ("'" == $f[$i] || '"' == $f[$i]) {
- ++$q[$f[$i]];
- $s[] = '\\'.$f[$i];
- } else {
- $s[] = $f[$i];
- }
- } else {
- switch ($f[$i]) {
- case ';':
- // Remove triple semi-colon
- if ($i > 0 && ';' == $f[$i - 1] && $i + 1 < $len && ';' == $f[$i + 1]) {
- $f[$i] = $f[$i + 1] = '/';
- } else {
- $code[++$j] = ';';
- break;
- }
-
- case '/':
- if ('*' == $f[$i + 1]) {
- ++$i;
- $instr = '*';
-
- if ($this->keepImportantComments && '!' == $f[$i + 1]) {
- ++$i;
- // no break here
- } else {
- break;
- }
- } elseif ('/' == $f[$i + 1]) {
- ++$i;
- $instr = '//';
- break;
- } else {
- $a = $j && (' ' == $code[$j] || "\x7F" == $code[$j]) ? $code[$j - 1] : $code[$j];
- if (false !== strpos('-!%&;<=>~:^+|,()*?[{} ', $a)
- || (false !== strpos('oenfd', $a)
- && preg_match(
- "'(?<![\$.a-zA-Z0-9_])(do|else|return|typeof|yield[ \x7F]?\*?)[ \x7F]?$'",
- substr($code, $j - 7, 8)
- ))
- ) {
- if (')' === $a && $j > 1) {
- $a = 1;
- $k = $j - (' ' == $code[$j] || "\x7F" == $code[$j]) - 1;
- while ($k >= 0 && $a) {
- if ('(' === $code[$k]) {
- --$a;
- } elseif (')' === $code[$k]) {
- ++$a;
- }
- --$k;
- }
- if (!preg_match("'(?<![\$.a-zA-Z0-9_])(if|for|while)[ \x7F]?$'", substr($code, 0, $k + 1))) {
- $code[++$j] = '/';
- break;
- }
- }
-
- $key = "//''\"\"".$K++.$instr = "/'";
- $a = $j;
- $code .= $key;
- while (isset($key[++$j - $a - 1])) {
- $code[$j] = $key[$j - $a - 1];
- }
- --$j;
- isset($s) && ($s = implode('', $s)) && $cc_on && $this->restoreCc($s);
- $strings[$key] = array('/');
- $s = &$strings[$key];
- } else {
- $code[++$j] = '/';
- }
-
- break;
- }
-
- case "'":
- case '"':
- $instr = $f[$i];
- $key = "//''\"\"".$K++.('!' == $instr ? ']' : "'");
- $a = $j;
- $code .= $key;
- while (isset($key[++$j - $a - 1])) {
- $code[$j] = $key[$j - $a - 1];
- }
- --$j;
- isset($s) && ($s = implode('', $s)) && $cc_on && $this->restoreCc($s);
- $strings[$key] = array();
- $s = &$strings[$key];
- '!' == $instr && $s[] = "\r/*!";
-
- break;
-
- case "\n":
- if ($j > 3) {
- if (' ' == $code[$j] || "\x7F" == $code[$j]) {
- --$j;
- }
-
- $code[++$j] =
- false !== strpos('kend+-', $code[$j - 1])
- && preg_match(
- "'(?:\+\+|--|(?<![\$.a-zA-Z0-9_])(break|continue|return|yield[ \x7F]?\*?))[ \x7F]?$'",
- substr($code, $j - 8, 9)
- )
- ? ';' : "\x7F";
-
- break;
- }
-
- case "\t": $f[$i] = ' ';
- case ' ':
- if (!$j || ' ' == $code[$j] || "\x7F" == $code[$j]) {
- break;
- }
-
- default:
- $code[++$j] = $f[$i];
- }
- }
- }
-
- isset($s) && ($s = implode('', $s)) && $cc_on && $this->restoreCc($s);
- unset($s);
-
- $code = substr($code, 0, $j + 1);
- $cc_on && $this->restoreCc($code, false);
-
- // Protect wanted spaces and remove unwanted ones
- $code = strtr($code, "\x7F", ' ');
- $code = str_replace('- -', "-\x7F-", $code);
- $code = str_replace('+ +', "+\x7F+", $code);
- $code = preg_replace("'(\d)\s+\.\s*([a-zA-Z\$_[(])'", "$1\x7F.$2", $code);
- $code = preg_replace("# ([-!%&;<=>~:.^+|,()*?[\]{}/']+)#", '$1', $code);
- $code = preg_replace("#([-!%&;<=>~:.^+|,()*?[\]{}/]+) #", '$1', $code);
- $cc_on && $code = preg_replace_callback("'//[^\'].*?@#3'", function ($m) { return strtr($m[0], ' ', "\x7F"); }, $code);
-
- // Replace new Array/Object by []/{}
- false !== strpos($code, 'new Array') && $code = preg_replace("'new Array(?:\(\)|([;\])},:]))'", '[]$1', $code);
- false !== strpos($code, 'new Object') && $code = preg_replace("'new Object(?:\(\)|([;\])},:]))'", '{}$1', $code);
-
- // Add missing semi-colons after curly braces
- // This adds more semi-colons than strictly needed,
- // but it seems that later gzipping is favorable to the repetition of "};"
- $code = preg_replace("'\}(?![:,;.()\[\]}\|&]|(else|catch|finally|while)[^\$.a-zA-Z0-9_])'", '};', $code);
-
- // Tag possible empty instruction for easy detection
- $code = preg_replace("'(?<![\$.a-zA-Z0-9_])if\('", '1#(', $code);
- $code = preg_replace("'(?<![\$.a-zA-Z0-9_])for\('", '2#(', $code);
- $code = preg_replace("'(?<![\$.a-zA-Z0-9_])while\('", '3#(', $code);
-
- $forPool = array();
- $instrPool = array();
- $s = 0;
-
- $f = array();
- $j = -1;
-
- // Remove as much semi-colon as possible
- $len = strlen($code);
- for ($i = 0; $i < $len; ++$i) {
- switch ($code[$i]) {
- case '(':
- if ($j >= 0 && "\n" == $f[$j]) {
- $f[$j] = ';';
- }
-
- ++$s;
-
- if ($i && '#' == $code[$i - 1]) {
- $instrPool[$s - 1] = 1;
- if ('2' == $code[$i - 2]) {
- $forPool[$s] = 1;
- }
- }
-
- $f[++$j] = '(';
- break;
-
- case ']':
- case ')':
- if ($i + 1 < $len && !isset($forPool[$s]) && !isset($instrPool[$s - 1]) && preg_match("'[a-zA-Z0-9_\$]'", $code[$i + 1])) {
- $f[$j] .= $code[$i];
- $f[++$j] = "\n";
- } else {
- $f[++$j] = $code[$i];
- }
-
- if (')' == $code[$i]) {
- unset($forPool[$s]);
- --$s;
- }
-
- continue 2;
-
- case '}':
- if ("\n" == $f[$j]) {
- $f[$j] = '}';
- } else {
- $f[++$j] = '}';
- }
- break;
-
- case ';':
- if (isset($forPool[$s]) || isset($instrPool[$s])) {
- $f[++$j] = ';';
- } elseif ($j >= 0 && "\n" != $f[$j] && ';' != $f[$j]) {
- $f[++$j] = "\n";
- }
-
- break;
-
- case '#':
- switch ($f[$j]) {
- case '1': $f[$j] = 'if'; break 2;
- case '2': $f[$j] = 'for'; break 2;
- case '3': $f[$j] = 'while'; break 2;
- }
-
- case '[';
- if ($j >= 0 && "\n" == $f[$j]) {
- $f[$j] = ';';
- }
-
- default: $f[++$j] = $code[$i];
- }
-
- unset($instrPool[$s]);
- }
-
- $f = implode('', $f);
- $cc_on && $f = str_replace('@#3', "\r", $f);
-
- // Fix "else ;" empty instructions
- $f = preg_replace("'(?<![\$.a-zA-Z0-9_])else\n'", "\n", $f);
-
- $r1 = array( // keywords with a direct object
- 'case','delete','do','else','function','in','instanceof','of','break',
- 'new','return','throw','typeof','var','void','yield','let','if',
- 'const','get','set',
- );
-
- $r2 = array( // keywords with a subject
- 'in','instanceof','of',
- );
-
- // Fix missing semi-colons
- $f = preg_replace("'(?<!(?<![a-zA-Z0-9_\$])".implode(')(?<!(?<![a-zA-Z0-9_\$])', $r1).') (?!('.implode('|', $r2).")(?![a-zA-Z0-9_\$]))'", "\n", $f);
- $f = preg_replace("'(?<!(?<![a-zA-Z0-9_\$])do)(?<!(?<![a-zA-Z0-9_\$])else) if\('", "\nif(", $f);
- $f = preg_replace("'(?<=--|\+\+)(?<![a-zA-Z0-9_\$])(".implode('|', $r1).")(?![a-zA-Z0-9_\$])'", "\n$1", $f);
- $f = preg_replace("'(?<![a-zA-Z0-9_\$])for\neach\('", 'for each(', $f);
- $f = preg_replace("'(?<![a-zA-Z0-9_\$])\n(".implode('|', $r2).")(?![a-zA-Z0-9_\$])'", '$1', $f);
-
- // Merge strings
- if ($q["'"] > $q['"']) {
- $q = array($q[1], $q[0]);
- }
- $f = preg_replace("#//''\"\"[0-9]+'#", $q[0].'$0'.$q[0], $f);
- strpos($f, $q[0].'+'.$q[0]) && $f = str_replace($q[0].'+'.$q[0], '', $f);
- $len = count($strings);
- foreach ($strings as $r1 => &$r2) {
- $r2 = "/'" == substr($r1, -2)
- ? str_replace(array("\\'", '\\"'), array("'", '"'), $r2)
- : str_replace('\\'.$q[1], $q[1], $r2);
- }
-
- // Restore wanted spaces
- $f = strtr($f, "\x7F", ' ');
-
- return array($f, $strings);
- }
-
- protected function extractClosures($code)
- {
- $code = ';'.$code;
-
- $this->argFreq[-1] += substr_count($code, '}catch(');
-
- if ($this->argFreq[-1]) {
- // Special catch scope handling
-
- // FIXME: this implementation doesn't work with nested catch scopes who need
- // access to their parent's caught variable (but who needs that?).
-
- $f = preg_split("@}catch\(({$this->varRx})@", $code, -1, PREG_SPLIT_DELIM_CAPTURE);
-
- $code = 'catch$scope$var'.mt_rand();
- $this->specialVarRx = $this->specialVarRx ? '(?:'.$this->specialVarRx.'|'.preg_quote($code).')' : preg_quote($code);
- $i = count($f) - 1;
-
- while ($i) {
- $c = 1;
- $j = 0;
- $l = strlen($f[$i]);
-
- while ($c && $j < $l) {
- $s = $f[$i][$j++];
- $c += '(' == $s ? 1 : (')' == $s ? -1 : 0);
- }
-
- if (!$c) {
- do {
- $s = $f[$i][$j++];
- $c += '{' == $s ? 1 : ('}' == $s ? -1 : 0);
- } while ($c && $j < $l);
- }
-
- $c = preg_quote($f[$i - 1], '#');
- $f[$i - 2] .= '}catch('.preg_replace("#([.,{]?)(?<![a-zA-Z0-9_\$@]){$c}\\b#", '$1'.$code, $f[$i - 1].substr($f[$i], 0, $j)).substr($f[$i], $j);
-
- unset($f[$i--], $f[$i--]);
- }
-
- $code = $f[0];
- }
-
- $f = preg_split("'(?<![a-zA-Z0-9_\$])((?:function[ (]|get |set ).*?\{)'", $code, -1, PREG_SPLIT_DELIM_CAPTURE);
- $i = count($f) - 1;
- $closures = array();
-
- while ($i) {
- $c = 1;
- $j = 0;
- $l = strlen($f[$i]);
-
- while ($c && $j < $l) {
- $s = $f[$i][$j++];
- $c += '{' == $s ? 1 : ('}' == $s ? -1 : 0);
- }
-
- switch (substr($f[$i - 2], -1)) {
- default:
- if (false !== $c = strpos($f[$i - 1], ' ', 8)) {
- break;
- }
- case false: case "\n": case ';': case '{': case '}': case ')': case ']':
- $c = strpos($f[$i - 1], '(', 4);
- }
-
- $l = "//''\"\"#$i'";
- $code = substr($f[$i - 1], $c);
- $closures[$l] = $code.substr($f[$i], 0, $j);
- $f[$i - 2] .= substr($f[$i - 1], 0, $c).$l.substr($f[$i], $j);
-
- if ('(){' !== $code) {
- $j = substr_count($code, ',');
- do {
- isset($this->argFreq[$j]) ? ++$this->argFreq[$j] : $this->argFreq[$j] = 1;
- } while ($j--);
- }
-
- $i -= 2;
- }
-
- return array($f[0], $closures);
- }
-
- protected function makeVars($closure, &$tree, $key)
- {
- $tree['code'] = &$closure;
- $tree['nfe'] = false;
- $tree['used'] = array();
- $tree['local'] = array();
-
- // Replace multiple "var" declarations by a single one
- $closure = preg_replace_callback("'(?<=[\n\{\}])var [^\n\{\};]+(?:\nvar [^\n\{\};]+)+'", array($this, 'mergeVarDeclarations'), $closure);
-
- // Get all local vars (functions, arguments and "var" prefixed)
-
- $vars = &$tree['local'];
-
- if (preg_match("'^( [^(]*)?\((.*?)\)\{'", $closure, $v)) {
- if ($v[1]) {
- $vars[$tree['nfe'] = substr($v[1], 1)] = -1;
- $tree['parent']['local'][';'.$key] = &$vars[$tree['nfe']];
- }
-
- if ($v[2]) {
- $i = 0;
- $v = explode(',', $v[2]);
- foreach ($v as $w) {
- $vars[$w] = $this->argFreq[$i++] - 1; // Give a bonus to argument variables
- }
- }
- }
-
- $v = preg_split("'(?<![\$.a-zA-Z0-9_])var '", $closure);
- if ($i = count($v) - 1) {
- $w = array();
-
- while ($i) {
- $j = $c = 0;
- $l = strlen($v[$i]);
-
- while ($j < $l) {
- switch ($v[$i][$j]) {
- case '(': case '[': case '{':
- ++$c;
- break;
-
- case ')': case ']': case '}':
- if ($c-- <= 0) {
- break 2;
- }
- break;
-
- case ';': case "\n":
- if (!$c) {
- break 2;
- }
-
- default:
- $c || $w[] = $v[$i][$j];
- }
-
- ++$j;
- }
-
- $w[] = ',';
- --$i;
- }
-
- $v = explode(',', implode('', $w));
- foreach ($v as $w) {
- if (preg_match("'^{$this->varRx}'", $w, $v)) {
- isset($vars[$v[0]]) || $vars[$v[0]] = 0;
- }
- }
- }
-
- if (preg_match_all("@function ({$this->varRx})//''\"\"#@", $closure, $v)) {
- foreach ($v[1] as $w) {
- isset($vars[$w]) || $vars[$w] = 0;
- }
- }
-
- if ($this->argFreq[-1] && preg_match_all("@}catch\(({$this->varRx})@", $closure, $v)) {
- $v[0] = array();
- foreach ($v[1] as $w) {
- isset($v[0][$w]) ? ++$v[0][$w] : $v[0][$w] = 1;
- }
- foreach ($v[0] as $w => $v) {
- $vars[$w] = $this->argFreq[-1] - $v;
- }
- }
-
- // Get all used vars, local and non-local
-
- $vars = &$tree['used'];
-
- if (preg_match_all("#([.,{]?(?:[gs]et )?)(?<![a-zA-Z0-9_\$])({$this->varRx})(:?)#", $closure, $w, PREG_SET_ORDER)) {
- foreach ($w as $k) {
- if (isset($k[1][0]) && (',' === $k[1][0] || '{' === $k[1][0])) {
- if (':' === $k[3]) {
- $k = '.'.$k[2];
- } elseif ('get ' === substr($k[1], 1, 4) || 'set ' === substr($k[1], 1, 4)) {
- ++$this->charFreq[ord($k[1][1])]; // "g" or "s"
- ++$this->charFreq[101]; // "e"
- ++$this->charFreq[116]; // "t"
- $k = '.'.$k[2];
- } else {
- $k = $k[2];
- }
- } else {
- $k = $k[1].$k[2];
- }
-
- isset($vars[$k]) ? ++$vars[$k] : $vars[$k] = 1;
- }
- }
-
- if (preg_match_all("#//''\"\"[0-9]+(?:['!]|/')#", $closure, $w)) {
- foreach ($w[0] as $a) {
- $v = "'" === substr($a, -1) && "/'" !== substr($a, -2) && $this->specialVarRx
- ? preg_split("#([.,{]?(?:[gs]et )?(?<![a-zA-Z0-9_\$@]){$this->specialVarRx}:?)#", $this->strings[$a], -1, PREG_SPLIT_DELIM_CAPTURE)
- : array($this->strings[$a]);
- $a = count($v);
-
- for ($i = 0; $i < $a; ++$i) {
- $k = $v[$i];
-
- if (1 === $i % 2) {
- if (',' === $k[0] || '{' === $k[0]) {
- if (':' === substr($k, -1)) {
- $k = '.'.substr($k, 1, -1);
- } elseif ('get ' === substr($k, 1, 4) || 'set ' === substr($k, 1, 4)) {
- ++$this->charFreq[ord($k[1])]; // "g" or "s"
- ++$this->charFreq[101]; // "e"
- ++$this->charFreq[116]; // "t"
- $k = '.'.substr($k, 5);
- } else {
- $k = substr($k, 1);
- }
- } elseif (':' === substr($k, -1)) {
- $k = substr($k, 0, -1);
- }
-
- $w = &$tree;
-
- while (isset($w['parent']) && !(isset($w['used'][$k]) || isset($w['local'][$k]))) {
- $w = &$w['parent'];
- }
-
- (isset($w['used'][$k]) || isset($w['local'][$k])) && (isset($vars[$k]) ? ++$vars[$k] : $vars[$k] = 1);
-
- unset($w);
- }
-
- if (0 === $i % 2 || !isset($vars[$k])) {
- foreach (count_chars($v[$i], 1) as $k => $w) {
- $this->charFreq[$k] += $w;
- }
- }
- }
- }
- }
-
- // Propagate the usage number to parents
-
- foreach ($vars as $w => $a) {
- $k = &$tree;
- $chain = array();
- do {
- $vars = &$k['local'];
- $chain[] = &$k;
- if (isset($vars[$w])) {
- unset($k['used'][$w]);
- if (isset($vars[$w])) {
- $vars[$w] += $a;
- } else {
- $vars[$w] = $a;
- }
- $a = false;
- break;
- }
- } while ($k['parent'] && $k = &$k['parent']);
-
- if ($a && !$k['parent']) {
- if (isset($vars[$w])) {
- $vars[$w] += $a;
- } else {
- $vars[$w] = $a;
- }
- }
-
- if (isset($tree['used'][$w]) && isset($vars[$w])) {
- foreach ($chain as &$b) {
- isset($b['local'][$w]) || $b['used'][$w] = &$vars[$w];
- }
- }
- }
-
- // Analyse childs
-
- $tree['childs'] = array();
- $vars = &$tree['childs'];
-
- if (preg_match_all("@//''\"\"#[0-9]+'@", $closure, $w)) {
- foreach ($w[0] as $a) {
- $vars[$a] = array('parent' => &$tree);
- $this->makeVars($this->closures[$a], $vars[$a], $a);
- }
- }
- }
-
- protected function mergeVarDeclarations($m)
- {
- return str_replace("\nvar ", ',', $m[0]);
- }
-
- protected function renameVars(&$tree, $root)
- {
- if ($root) {
- $tree['local'] += $tree['used'];
- $tree['used'] = array();
-
- foreach ($tree['local'] as $k => $v) {
- if ('.' == $k[0]) {
- $k = substr($k, 1);
- }
-
- if ('true' === $k) {
- $this->charFreq[48] += $v;
- } elseif ('false' === $k) {
- $this->charFreq[49] += $v;
- } elseif (!$this->specialVarRx || !preg_match("#^{$this->specialVarRx}$#", $k)) {
- foreach (count_chars($k, 1) as $k => $w) {
- $this->charFreq[$k] += $w * $v;
- }
- } elseif (2 == strlen($k)) {
- $tree['used'][] = $k[1];
- }
- }
-
- $this->charFreq = $this->rsort($this->charFreq);
-
- $this->str0 = '';
- $this->str1 = '';
-
- foreach ($this->charFreq as $k => $v) {
- if (!$v) {
- break;
- }
-
- $v = chr($k);
-
- if ((64 < $k && $k < 91) || (96 < $k && $k < 123)) { // A-Z a-z
- $this->str0 .= $v;
- $this->str1 .= $v;
- } elseif (47 < $k && $k < 58) { // 0-9
- $this->str1 .= $v;
- }
- }
-
- if ('' === $this->str0) {
- $this->str0 = 'claspemitdbfrugnjvhowkxqyzCLASPEMITDBFRUGNJVHOWKXQYZ';
- $this->str1 = $this->str0.'0123456789';
- }
-
- foreach ($tree['local'] as $var => $root) {
- if ('.' != substr($var, 0, 1) && isset($tree['local'][".{$var}"])) {
- $tree['local'][$var] += $tree['local'][".{$var}"];
- }
- }
-
- foreach ($tree['local'] as $var => $root) {
- if ('.' == substr($var, 0, 1) && isset($tree['local'][substr($var, 1)])) {
- $tree['local'][$var] = $tree['local'][substr($var, 1)];
- }
- }
-
- $tree['local'] = $this->rsort($tree['local']);
-
- foreach ($tree['local'] as $var => $root) {
- switch (substr($var, 0, 1)) {
- case '.':
- if (!isset($tree['local'][substr($var, 1)])) {
- $tree['local'][$var] = '#'.($this->specialVarRx && 3 < strlen($var) && preg_match("'^\.{$this->specialVarRx}$'", $var) ? $this->getNextName($tree).'$' : substr($var, 1));
- }
- break;
-
- case ';': $tree['local'][$var] = 0 === $root ? '' : $this->getNextName($tree);
- case '#': break;
-
- default:
- $root = $this->specialVarRx && 2 < strlen($var) && preg_match("'^{$this->specialVarRx}$'", $var) ? $this->getNextName($tree).'$' : $var;
- $tree['local'][$var] = $root;
- if (isset($tree['local'][".{$var}"])) {
- $tree['local'][".{$var}"] = '#'.$root;
- }
- }
- }
-
- foreach ($tree['local'] as $var => $root) {
- $tree['local'][$var] = preg_replace("'^#'", '.', $tree['local'][$var]);
- }
- } else {
- $tree['local'] = $this->rsort($tree['local']);
- if (false !== $tree['nfe']) {
- $tree['used'][] = $tree['local'][$tree['nfe']];
- }
-
- foreach ($tree['local'] as $var => $root) {
- if ($tree['nfe'] !== $var) {
- $tree['local'][$var] = 0 === $root ? '' : $this->getNextName($tree);
- }
- }
- }
-
- $this->local_tree = &$tree['local'];
- $this->used_tree = &$tree['used'];
-
- $tree['code'] = preg_replace_callback("#[.,{ ]?(?:[gs]et )?(?<![a-zA-Z0-9_\$@]){$this->varRx}:?#", array($this, 'getNewName'), $tree['code']);
-
- if ($this->specialVarRx && preg_match_all("#//''\"\"[0-9]+'#", $tree['code'], $b)) {
- foreach ($b[0] as $a) {
- $this->strings[$a] = preg_replace_callback(
- "#[.,{]?(?:[gs]et )?(?<![a-zA-Z0-9_\$@]){$this->specialVarRx}:?#",
- array($this, 'getNewName'),
- $this->strings[$a]
- );
- }
- }
-
- foreach ($tree['childs'] as $a => &$b) {
- $this->renameVars($b, false);
- $tree['code'] = str_replace($a, $b['code'], $tree['code']);
- unset($tree['childs'][$a]);
- }
- }
-
- protected function getNewName($m)
- {
- $m = $m[0];
-
- $pre = '.' === $m[0] ? '.' : '';
- $post = '';
-
- if (',' === $m[0] || '{' === $m[0] || ' ' === $m[0]) {
- $pre = $m[0];
-
- if (':' === substr($m, -1)) {
- $post = ':';
- $m = (' ' !== $m[0] ? '.' : '').substr($m, 1, -1);
- } elseif ('get ' === substr($m, 1, 4) || 'set ' === substr($m, 1, 4)) {
- $pre .= substr($m, 1, 4);
- $m = '.'.substr($m, 5);
- } else {
- $m = substr($m, 1);
- }
- } elseif (':' === substr($m, -1)) {
- $post = ':';
- $m = substr($m, 0, -1);
- }
-
- $post = (isset($this->reserved[$m])
- ? ('true' === $m ? '!0' : ('false' === $m ? '!1' : $m))
- : (
- isset($this->local_tree[$m])
- ? $this->local_tree[$m]
- : (
- isset($this->used_tree[$m])
- ? $this->used_tree[$m]
- : $m
- )
- )
- ).$post;
-
- return '' === $post ? '' : ($pre.('.' === $post[0] ? substr($post, 1) : $post));
- }
-
- protected function getNextName(&$tree = array(), &$counter = false)
- {
- if (false === $counter) {
- $counter = &$tree['counter'];
- isset($counter) || $counter = -1;
- $exclude = array_flip($tree['used']);
- } else {
- $exclude = $tree;
- }
-
- ++$counter;
-
- $len0 = strlen($this->str0);
- $len1 = strlen($this->str0);
-
- $name = $this->str0[$counter % $len0];
-
- $i = intval($counter / $len0) - 1;
- while ($i >= 0) {
- $name .= $this->str1[ $i % $len1 ];
- $i = intval($i / $len1) - 1;
- }
-
- return !(isset($this->reserved[$name]) || isset($exclude[$name])) ? $name : $this->getNextName($exclude, $counter);
- }
-
- protected function restoreCc(&$s, $lf = true)
- {
- $lf && $s = str_replace('@#3', '', $s);
-
- $s = str_replace('@#1', '@*/', $s);
- $s = str_replace('2#@', '//@', $s);
- $s = str_replace('1#@', '/*@', $s);
- $s = str_replace('##', '#', $s);
- }
-
- private function rsort($array)
- {
- if (!$array) {
- return $array;
- }
-
- $i = 0;
- $tuples = array();
- foreach ($array as $k => &$v) {
- $tuples[] = array(++$i, $k, &$v);
- }
-
- usort($tuples, function ($a, $b) {
- if ($b[2] > $a[2]) {
- return 1;
- }
- if ($b[2] < $a[2]) {
- return -1;
- }
- if ($b[0] > $a[0]) {
- return -1;
- }
- if ($b[0] < $a[0]) {
- return 1;
- }
-
- return 0;
- });
-
- $array = array();
-
- foreach ($tuples as $t) {
- $array[$t[1]] = &$t[2];
- }
-
- return $array;
- }
-}
-?>-
\ No newline at end of file
diff --git a/util/Ldap.class.php b/util/Ldap.class.php
@@ -1,182 +0,0 @@
-<?php
-// OpenRat Content Management System
-// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-/**
- * Bereitstellen von LDAP-Funktionen.
- * @author $Author$
- * @version $Revision$
- * @package openrat.services
- */
-class Ldap
-{
- var $connection;
- var $timeout;
- var $aliases;
-
-
- /**
- *
- */
- function Ldap()
- {
- global $conf;
-
- $this->timeout = intval($conf['ldap']['search']['timeout']);
-
- if ( $conf['ldap']['search']['aliases'] )
- $this->aliases = LDAP_DEREF_ALWAYS;
- else
- $this->aliases = LDAP_DEREF_NEVER;
- }
-
-
-
- /**
- * Verbindung �ffnen.
- */
- function connect()
- {
- global $conf;
-
- $ldapHost = $conf['ldap']['host'];
- $ldapPort = $conf['ldap']['port'];
-
- // Verbindung zum LDAP-Server herstellen
- $this->connection = @ldap_connect( $ldapHost,$ldapPort );
-
- // siehe http://bugs.php.net/bug.php?id=15637
- // Unter bestimmten Bedingungen wird trotz nicht erreichbarem LDAP-Server eine PHP-Resource
- // zurueck gegeben. Dann erscheint zwar keine Fehlermeldung, aber zumindestens misslingt
- // der nachfolgende Bind-Befehl.
- if ( !is_resource($this->connection) || $this->connection === false )
- {
- Logger::error( "connect to ldap server '$ldapHost:$ldapPort' failed" );
- // Abbruch, wenn LDAP-Server nicht erreichbar
- die( "Connection failed to $ldapHost:$ldapPort (".ldap_errno().'/'.ldap_error().'). Please contact your administrator.' );
- }
-
- // Protokollversion setzen.
- $j = ldap_set_option( $this->connection, LDAP_OPT_PROTOCOL_VERSION,intval($conf['ldap']['protocol']) );
- if ( ! $j )
- die( 'LDAP error while setting protocol version'.ldap_errno().'/'.ldap_error().')' );
-
- }
-
-
-
- /**
- * Ein Binding auf den LDAP-Server durchf�hren.
- */
- function bind( $user,$pw )
- {
- return @ldap_bind( $this->connection,$user,$pw);
- }
-
-
-
- /**
- * Ein Binding auf den LDAP-Server durchf�hren.
- */
- function bindAnonymous()
- {
- return @ldap_bind( $this->connection );
- }
-
-
-
- /**
- * Das Bindung wird entfernt.
- */
- function unbind()
- {
- ldap_unbind( $this->connection );
- }
-
-
-
- /**
- * Eine Suche auf den LDAP-Server durchf�hren.
- */
- function searchUser( $username )
- {
- global $conf;
-
- $techUser = $conf['ldap']['search']['user'];
- $techPass = $conf['ldap']['search']['password'];
-
- if ( $conf['ldap']['search']['anonymous'] )
- $this->bindAnonymous();
- else
- $this->bind( $techUser, $techPass );
-
- $dn = $conf['ldap']['search']['basedn'];
- $filter = $conf['ldap']['search']['filter'];
- $filter = str_replace('{user}', $username, $filter);
-
- $s = @ldap_search( $this->connection,$dn,$filter,array(),0,1,$this->timeout,$this->aliases );
-
- if ( ! is_resource($s) )
- return null;
-
- $dn = @ldap_get_dn($this->connection, ldap_first_entry($this->connection,$s) );
-
- return $dn;
- }
-
-
-
- /**
- * Ein Binding auf den LDAP-Server durchf�hren.
- */
- function searchAttribute( $filter,$attr )
- {
- global $conf;
-
- $timeout = intval($conf['ldap']['search']['timeout']);
-
- if ( $conf['ldap']['search']['aliases'] )
- $aliases = LDAP_DEREF_ALWAYS;
- else
- $aliases = LDAP_DEREF_NEVER;
-
-
- $base_dn = $conf['ldap']['search']['basedn'];
- $s = ldap_search( $this->connection,$base_dn,$filter,array(),0,0,$this->timeout,$this->aliases );
- $ergebnisse = ldap_get_entries($this->connection,$s);
-
- $liste = array();
-// Html::debug($ergebnisse);
- for( $i=0; $i<=$ergebnisse['count']-1; $i++ )
- $liste[] = $ergebnisse[$i][$attr][0];
-
- return $liste;
- }
-
-
-
- /**
- * Verbindung schlie�en.
- */
- function close()
- {
- // Verbindung zum LDAP-Server brav beenden
- ldap_close( $this->connection );
- }
-}
-
-?>-
\ No newline at end of file
diff --git a/util/Less.php b/util/Less.php
@@ -1,10524 +0,0 @@
-<?php
-
-/**
- * Class for parsing and compiling less files into css
- *
- * @package Less
- * @subpackage parser
- *
- */
-class Less_Parser{
-
-
- /**
- * Default parser options
- */
- public static $default_options = array(
- 'compress' => false, // option - whether to compress
- 'strictUnits' => false, // whether units need to evaluate correctly
- 'strictMath' => false, // whether math has to be within parenthesis
- 'relativeUrls' => true, // option - whether to adjust URL's to be relative
- 'urlArgs' => '', // whether to add args into url tokens
- 'numPrecision' => 8,
-
- 'import_dirs' => array(),
- 'import_callback' => null,
- 'cache_dir' => null,
- 'cache_method' => 'php', // false, 'serialize', 'php', 'var_export', 'callback';
- 'cache_callback_get' => null,
- 'cache_callback_set' => null,
-
- 'sourceMap' => false, // whether to output a source map
- 'sourceMapBasepath' => null,
- 'sourceMapWriteTo' => null,
- 'sourceMapURL' => null,
-
- 'indentation' => ' ',
-
- 'plugins' => array(),
-
- );
-
- public static $options = array();
-
-
- private $input; // Less input string
- private $input_len; // input string length
- private $pos; // current index in `input`
- private $saveStack = array(); // holds state for backtracking
- private $furthest;
- private $mb_internal_encoding = ''; // for remember exists value of mbstring.internal_encoding
-
- /**
- * @var Less_Environment
- */
- private $env;
-
- protected $rules = array();
-
- private static $imports = array();
-
- public static $has_extends = false;
-
- public static $next_id = 0;
-
- /**
- * Filename to contents of all parsed the files
- *
- * @var array
- */
- public static $contentsMap = array();
-
-
- /**
- * @param Less_Environment|array|null $env
- */
- public function __construct( $env = null ){
-
- // Top parser on an import tree must be sure there is one "env"
- // which will then be passed around by reference.
- if( $env instanceof Less_Environment ){
- $this->env = $env;
- }else{
- $this->SetOptions(Less_Parser::$default_options);
- $this->Reset( $env );
- }
-
- // mbstring.func_overload > 1 bugfix
- // The encoding value must be set for each source file,
- // therefore, to conserve resources and improve the speed of this design is taken here
- if (ini_get('mbstring.func_overload')) {
- $this->mb_internal_encoding = ini_get('mbstring.internal_encoding');
- @ini_set('mbstring.internal_encoding', 'ascii');
- }
-
- }
-
-
- /**
- * Reset the parser state completely
- *
- */
- public function Reset( $options = null ){
- $this->rules = array();
- self::$imports = array();
- self::$has_extends = false;
- self::$imports = array();
- self::$contentsMap = array();
-
- $this->env = new Less_Environment($options);
- $this->env->Init();
-
- //set new options
- if( is_array($options) ){
- $this->SetOptions(Less_Parser::$default_options);
- $this->SetOptions($options);
- }
- }
-
- /**
- * Set one or more compiler options
- * options: import_dirs, cache_dir, cache_method
- *
- */
- public function SetOptions( $options ){
- foreach($options as $option => $value){
- $this->SetOption($option,$value);
- }
- }
-
- /**
- * Set one compiler option
- *
- */
- public function SetOption($option,$value){
-
- switch($option){
-
- case 'import_dirs':
- $this->SetImportDirs($value);
- return;
-
- case 'cache_dir':
- if( is_string($value) ){
- Less_Cache::SetCacheDir($value);
- Less_Cache::CheckCacheDir();
- }
- return;
- }
-
- Less_Parser::$options[$option] = $value;
- }
-
- /**
- * Registers a new custom function
- *
- * @param string $name function name
- * @param callable $callback callback
- */
- public function registerFunction($name, $callback) {
- $this->env->functions[$name] = $callback;
- }
-
- /**
- * Removed an already registered function
- *
- * @param string $name function name
- */
- public function unregisterFunction($name) {
- if( isset($this->env->functions[$name]) )
- unset($this->env->functions[$name]);
- }
-
-
- /**
- * Get the current css buffer
- *
- * @return string
- */
- public function getCss(){
-
- $precision = ini_get('precision');
- @ini_set('precision',16);
- $locale = setlocale(LC_NUMERIC, 0);
- setlocale(LC_NUMERIC, "C");
-
- try {
-
- $root = new Less_Tree_Ruleset(array(), $this->rules );
- $root->root = true;
- $root->firstRoot = true;
-
-
- $this->PreVisitors($root);
-
- self::$has_extends = false;
- $evaldRoot = $root->compile($this->env);
-
-
-
- $this->PostVisitors($evaldRoot);
-
- if( Less_Parser::$options['sourceMap'] ){
- $generator = new Less_SourceMap_Generator($evaldRoot, Less_Parser::$contentsMap, Less_Parser::$options );
- // will also save file
- // FIXME: should happen somewhere else?
- $css = $generator->generateCSS();
- }else{
- $css = $evaldRoot->toCSS();
- }
-
- if( Less_Parser::$options['compress'] ){
- $css = preg_replace('/(^(\s)+)|((\s)+$)/', '', $css);
- }
-
- } catch (Exception $exc) {
- // Intentional fall-through so we can reset environment
- }
-
- //reset php settings
- @ini_set('precision',$precision);
- setlocale(LC_NUMERIC, $locale);
-
- // If you previously defined $this->mb_internal_encoding
- // is required to return the encoding as it was before
- if ($this->mb_internal_encoding != '') {
- @ini_set("mbstring.internal_encoding", $this->mb_internal_encoding);
- $this->mb_internal_encoding = '';
- }
-
- // Rethrow exception after we handled resetting the environment
- if (!empty($exc)) {
- throw $exc;
- }
-
-
-
- return $css;
- }
-
- /**
- * Run pre-compile visitors
- *
- */
- private function PreVisitors($root){
-
- if( Less_Parser::$options['plugins'] ){
- foreach(Less_Parser::$options['plugins'] as $plugin){
- if( !empty($plugin->isPreEvalVisitor) ){
- $plugin->run($root);
- }
- }
- }
- }
-
-
- /**
- * Run post-compile visitors
- *
- */
- private function PostVisitors($evaldRoot){
-
- $visitors = array();
- $visitors[] = new Less_Visitor_joinSelector();
- if( self::$has_extends ){
- $visitors[] = new Less_Visitor_processExtends();
- }
- $visitors[] = new Less_Visitor_toCSS();
-
-
- if( Less_Parser::$options['plugins'] ){
- foreach(Less_Parser::$options['plugins'] as $plugin){
- if( property_exists($plugin,'isPreEvalVisitor') && $plugin->isPreEvalVisitor ){
- continue;
- }
-
- if( property_exists($plugin,'isPreVisitor') && $plugin->isPreVisitor ){
- array_unshift( $visitors, $plugin);
- }else{
- $visitors[] = $plugin;
- }
- }
- }
-
-
- for($i = 0; $i < count($visitors); $i++ ){
- $visitors[$i]->run($evaldRoot);
- }
-
- }
-
-
- /**
- * Parse a Less string into css
- *
- * @param string $str The string to convert
- * @param string $uri_root The url of the file
- * @return Less_Tree_Ruleset|Less_Parser
- */
- public function parse( $str, $file_uri = null ){
-
- if( !$file_uri ){
- $uri_root = '';
- $filename = 'anonymous-file-'.Less_Parser::$next_id++.'.less';
- }else{
- $file_uri = self::WinPath($file_uri);
- $filename = $file_uri;
- $uri_root = dirname($file_uri);
- }
-
- $previousFileInfo = $this->env->currentFileInfo;
- $uri_root = self::WinPath($uri_root);
- $this->SetFileInfo($filename, $uri_root);
-
- $this->input = $str;
- $this->_parse();
-
- if( $previousFileInfo ){
- $this->env->currentFileInfo = $previousFileInfo;
- }
-
- return $this;
- }
-
-
- /**
- * Parse a Less string from a given file
- *
- * @throws Less_Exception_Parser
- * @param string $filename The file to parse
- * @param string $uri_root The url of the file
- * @param bool $returnRoot Indicates whether the return value should be a css string a root node
- * @return Less_Tree_Ruleset|Less_Parser
- */
- public function parseFile( $filename, $uri_root = '', $returnRoot = false){
-
- if( !file_exists($filename) ){
- $this->Error(sprintf('File `%s` not found.', $filename));
- }
-
-
- // fix uri_root?
- // Instead of The mixture of file path for the first argument and directory path for the second argument has bee
- if( !$returnRoot && !empty($uri_root) && basename($uri_root) == basename($filename) ){
- $uri_root = dirname($uri_root);
- }
-
-
- $previousFileInfo = $this->env->currentFileInfo;
-
-
- if( $filename ){
- $filename = self::WinPath(realpath($filename));
- }
- $uri_root = self::WinPath($uri_root);
-
- $this->SetFileInfo($filename, $uri_root);
-
- self::AddParsedFile($filename);
-
- if( $returnRoot ){
- $rules = $this->GetRules( $filename );
- $return = new Less_Tree_Ruleset(array(), $rules );
- }else{
- $this->_parse( $filename );
- $return = $this;
- }
-
- if( $previousFileInfo ){
- $this->env->currentFileInfo = $previousFileInfo;
- }
-
- return $return;
- }
-
-
- /**
- * Allows a user to set variables values
- * @param array $vars
- * @return Less_Parser
- */
- public function ModifyVars( $vars ){
-
- $this->input = Less_Parser::serializeVars( $vars );
- $this->_parse();
-
- return $this;
- }
-
-
- /**
- * @param string $filename
- */
- public function SetFileInfo( $filename, $uri_root = ''){
-
- $filename = Less_Environment::normalizePath($filename);
- $dirname = preg_replace('/[^\/\\\\]*$/','',$filename);
-
- if( !empty($uri_root) ){
- $uri_root = rtrim($uri_root,'/').'/';
- }
-
- $currentFileInfo = array();
-
- //entry info
- if( isset($this->env->currentFileInfo) ){
- $currentFileInfo['entryPath'] = $this->env->currentFileInfo['entryPath'];
- $currentFileInfo['entryUri'] = $this->env->currentFileInfo['entryUri'];
- $currentFileInfo['rootpath'] = $this->env->currentFileInfo['rootpath'];
-
- }else{
- $currentFileInfo['entryPath'] = $dirname;
- $currentFileInfo['entryUri'] = $uri_root;
- $currentFileInfo['rootpath'] = $dirname;
- }
-
- $currentFileInfo['currentDirectory'] = $dirname;
- $currentFileInfo['currentUri'] = $uri_root.basename($filename);
- $currentFileInfo['filename'] = $filename;
- $currentFileInfo['uri_root'] = $uri_root;
-
-
- //inherit reference
- if( isset($this->env->currentFileInfo['reference']) && $this->env->currentFileInfo['reference'] ){
- $currentFileInfo['reference'] = true;
- }
-
- $this->env->currentFileInfo = $currentFileInfo;
- }
-
-
- /**
- * @deprecated 1.5.1.2
- *
- */
- public function SetCacheDir( $dir ){
-
- if( !file_exists($dir) ){
- if( mkdir($dir) ){
- return true;
- }
- throw new Less_Exception_Parser('Less.php cache directory couldn\'t be created: '.$dir);
-
- }elseif( !is_dir($dir) ){
- throw new Less_Exception_Parser('Less.php cache directory doesn\'t exist: '.$dir);
-
- }elseif( !is_writable($dir) ){
- throw new Less_Exception_Parser('Less.php cache directory isn\'t writable: '.$dir);
-
- }else{
- $dir = self::WinPath($dir);
- Less_Cache::$cache_dir = rtrim($dir,'/').'/';
- return true;
- }
- }
-
-
- /**
- * Set a list of directories or callbacks the parser should use for determining import paths
- *
- * @param array $dirs
- */
- public function SetImportDirs( $dirs ){
- Less_Parser::$options['import_dirs'] = array();
-
- foreach($dirs as $path => $uri_root){
-
- $path = self::WinPath($path);
- if( !empty($path) ){
- $path = rtrim($path,'/').'/';
- }
-
- if ( !is_callable($uri_root) ){
- $uri_root = self::WinPath($uri_root);
- if( !empty($uri_root) ){
- $uri_root = rtrim($uri_root,'/').'/';
- }
- }
-
- Less_Parser::$options['import_dirs'][$path] = $uri_root;
- }
- }
-
- /**
- * @param string $file_path
- */
- private function _parse( $file_path = null ){
- $this->rules = array_merge($this->rules, $this->GetRules( $file_path ));
- }
-
-
- /**
- * Return the results of parsePrimary for $file_path
- * Use cache and save cached results if possible
- *
- * @param string|null $file_path
- */
- private function GetRules( $file_path ){
-
- $this->SetInput($file_path);
-
- $cache_file = $this->CacheFile( $file_path );
- if( $cache_file ){
- if( Less_Parser::$options['cache_method'] == 'callback' ){
- if( is_callable(Less_Parser::$options['cache_callback_get']) ){
- $cache = call_user_func_array(
- Less_Parser::$options['cache_callback_get'],
- array($this, $file_path, $cache_file)
- );
-
- if( $cache ){
- $this->UnsetInput();
- return $cache;
- }
- }
-
- }elseif( file_exists($cache_file) ){
- switch(Less_Parser::$options['cache_method']){
-
- // Using serialize
- // Faster but uses more memory
- case 'serialize':
- $cache = unserialize(file_get_contents($cache_file));
- if( $cache ){
- touch($cache_file);
- $this->UnsetInput();
- return $cache;
- }
- break;
-
-
- // Using generated php code
- case 'var_export':
- case 'php':
- $this->UnsetInput();
- return include($cache_file);
- }
- }
- }
-
- $rules = $this->parsePrimary();
-
- if( $this->pos < $this->input_len ){
- throw new Less_Exception_Chunk($this->input, null, $this->furthest, $this->env->currentFileInfo);
- }
-
- $this->UnsetInput();
-
-
- //save the cache
- if( $cache_file ){
- if( Less_Parser::$options['cache_method'] == 'callback' ){
- if( is_callable(Less_Parser::$options['cache_callback_set']) ){
- call_user_func_array(
- Less_Parser::$options['cache_callback_set'],
- array($this, $file_path, $cache_file, $rules)
- );
- }
-
- }else{
- //msg('write cache file');
- switch(Less_Parser::$options['cache_method']){
- case 'serialize':
- file_put_contents( $cache_file, serialize($rules) );
- break;
- case 'php':
- file_put_contents( $cache_file, '<?php return '.self::ArgString($rules).'; ?>' );
- break;
- case 'var_export':
- //Requires __set_state()
- file_put_contents( $cache_file, '<?php return '.var_export($rules,true).'; ?>' );
- break;
- }
-
- Less_Cache::CleanCache();
- }
- }
-
- return $rules;
- }
-
-
- /**
- * Set up the input buffer
- *
- */
- public function SetInput( $file_path ){
-
- if( $file_path ){
- $this->input = file_get_contents( $file_path );
- }
-
- $this->pos = $this->furthest = 0;
-
- // Remove potential UTF Byte Order Mark
- $this->input = preg_replace('/\\G\xEF\xBB\xBF/', '', $this->input);
- $this->input_len = strlen($this->input);
-
-
- if( Less_Parser::$options['sourceMap'] && $this->env->currentFileInfo ){
- $uri = $this->env->currentFileInfo['currentUri'];
- Less_Parser::$contentsMap[$uri] = $this->input;
- }
-
- }
-
-
- /**
- * Free up some memory
- *
- */
- public function UnsetInput(){
- unset($this->input, $this->pos, $this->input_len, $this->furthest);
- $this->saveStack = array();
- }
-
-
- public function CacheFile( $file_path ){
-
- if( $file_path && $this->CacheEnabled() ){
-
- $env = get_object_vars($this->env);
- unset($env['frames']);
-
- $parts = array();
- $parts[] = $file_path;
- $parts[] = filesize( $file_path );
- $parts[] = filemtime( $file_path );
- $parts[] = $env;
- $parts[] = Less_Version::cache_version;
- $parts[] = Less_Parser::$options['cache_method'];
- return Less_Cache::$cache_dir . Less_Cache::$prefix . base_convert( sha1(json_encode($parts) ), 16, 36) . '.lesscache';
- }
- }
-
-
- static function AddParsedFile($file){
- self::$imports[] = $file;
- }
-
- static function AllParsedFiles(){
- return self::$imports;
- }
-
- /**
- * @param string $file
- */
- static function FileParsed($file){
- return in_array($file,self::$imports);
- }
-
-
- function save() {
- $this->saveStack[] = $this->pos;
- }
-
- private function restore() {
- $this->pos = array_pop($this->saveStack);
- }
-
- private function forget(){
- array_pop($this->saveStack);
- }
-
-
- private function isWhitespace($offset = 0) {
- return preg_match('/\s/',$this->input[ $this->pos + $offset]);
- }
-
- /**
- * Parse from a token, regexp or string, and move forward if match
- *
- * @param array $toks
- * @return array
- */
- private function match($toks){
-
- // The match is confirmed, add the match length to `this::pos`,
- // and consume any extra white-space characters (' ' || '\n')
- // which come after that. The reason for this is that LeSS's
- // grammar is mostly white-space insensitive.
- //
-
- foreach($toks as $tok){
-
- $char = $tok[0];
-
- if( $char === '/' ){
- $match = $this->MatchReg($tok);
-
- if( $match ){
- return count($match) === 1 ? $match[0] : $match;
- }
-
- }elseif( $char === '#' ){
- $match = $this->MatchChar($tok[1]);
-
- }else{
- // Non-terminal, match using a function call
- $match = $this->$tok();
-
- }
-
- if( $match ){
- return $match;
- }
- }
- }
-
- /**
- * @param string[] $toks
- *
- * @return string
- */
- private function MatchFuncs($toks){
-
- if( $this->pos < $this->input_len ){
- foreach($toks as $tok){
- $match = $this->$tok();
- if( $match ){
- return $match;
- }
- }
- }
-
- }
-
- // Match a single character in the input,
- private function MatchChar($tok){
- if( ($this->pos < $this->input_len) && ($this->input[$this->pos] === $tok) ){
- $this->skipWhitespace(1);
- return $tok;
- }
- }
-
- // Match a regexp from the current start point
- private function MatchReg($tok){
-
- if( preg_match($tok, $this->input, $match, 0, $this->pos) ){
- $this->skipWhitespace(strlen($match[0]));
- return $match;
- }
- }
-
-
- /**
- * Same as match(), but don't change the state of the parser,
- * just return the match.
- *
- * @param string $tok
- * @return integer
- */
- public function PeekReg($tok){
- return preg_match($tok, $this->input, $match, 0, $this->pos);
- }
-
- /**
- * @param string $tok
- */
- public function PeekChar($tok){
- //return ($this->input[$this->pos] === $tok );
- return ($this->pos < $this->input_len) && ($this->input[$this->pos] === $tok );
- }
-
-
- /**
- * @param integer $length
- */
- public function skipWhitespace($length){
-
- $this->pos += $length;
-
- for(; $this->pos < $this->input_len; $this->pos++ ){
- $c = $this->input[$this->pos];
-
- if( ($c !== "\n") && ($c !== "\r") && ($c !== "\t") && ($c !== ' ') ){
- break;
- }
- }
- }
-
-
- /**
- * @param string $tok
- * @param string|null $msg
- */
- public function expect($tok, $msg = NULL) {
- $result = $this->match( array($tok) );
- if (!$result) {
- $this->Error( $msg ? "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'" : $msg );
- } else {
- return $result;
- }
- }
-
- /**
- * @param string $tok
- */
- public function expectChar($tok, $msg = null ){
- $result = $this->MatchChar($tok);
- if( !$result ){
- $this->Error( $msg ? "Expected '" . $tok . "' got '" . $this->input[$this->pos] . "'" : $msg );
- }else{
- return $result;
- }
- }
-
- //
- // Here in, the parsing rules/functions
- //
- // The basic structure of the syntax tree generated is as follows:
- //
- // Ruleset -> Rule -> Value -> Expression -> Entity
- //
- // Here's some LESS code:
- //
- // .class {
- // color: #fff;
- // border: 1px solid #000;
- // width: @w + 4px;
- // > .child {...}
- // }
- //
- // And here's what the parse tree might look like:
- //
- // Ruleset (Selector '.class', [
- // Rule ("color", Value ([Expression [Color #fff]]))
- // Rule ("border", Value ([Expression [Dimension 1px][Keyword "solid"][Color #000]]))
- // Rule ("width", Value ([Expression [Operation "+" [Variable "@w"][Dimension 4px]]]))
- // Ruleset (Selector [Element '>', '.child'], [...])
- // ])
- //
- // In general, most rules will try to parse a token with the `$()` function, and if the return
- // value is truly, will return a new node, of the relevant type. Sometimes, we need to check
- // first, before parsing, that's when we use `peek()`.
- //
-
- //
- // The `primary` rule is the *entry* and *exit* point of the parser.
- // The rules here can appear at any level of the parse tree.
- //
- // The recursive nature of the grammar is an interplay between the `block`
- // rule, which represents `{ ... }`, the `ruleset` rule, and this `primary` rule,
- // as represented by this simplified grammar:
- //
- // primary → (ruleset | rule)+
- // ruleset → selector+ block
- // block → '{' primary '}'
- //
- // Only at one point is the primary rule not called from the
- // block rule: at the root level.
- //
- private function parsePrimary(){
- $root = array();
-
- while( true ){
-
- if( $this->pos >= $this->input_len ){
- break;
- }
-
- $node = $this->parseExtend(true);
- if( $node ){
- $root = array_merge($root,$node);
- continue;
- }
-
- //$node = $this->MatchFuncs( array( 'parseMixinDefinition', 'parseRule', 'parseRuleset', 'parseMixinCall', 'parseComment', 'parseDirective'));
- $node = $this->MatchFuncs( array( 'parseMixinDefinition', 'parseNameValue', 'parseRule', 'parseRuleset', 'parseMixinCall', 'parseComment', 'parseRulesetCall', 'parseDirective'));
-
- if( $node ){
- $root[] = $node;
- }elseif( !$this->MatchReg('/\\G[\s\n;]+/') ){
- break;
- }
-
- if( $this->PeekChar('}') ){
- break;
- }
- }
-
- return $root;
- }
-
-
-
- // We create a Comment node for CSS comments `/* */`,
- // but keep the LeSS comments `//` silent, by just skipping
- // over them.
- private function parseComment(){
-
- if( $this->input[$this->pos] !== '/' ){
- return;
- }
-
- if( $this->input[$this->pos+1] === '/' ){
- $match = $this->MatchReg('/\\G\/\/.*/');
- return $this->NewObj4('Less_Tree_Comment',array($match[0], true, $this->pos, $this->env->currentFileInfo));
- }
-
- //$comment = $this->MatchReg('/\\G\/\*(?:[^*]|\*+[^\/*])*\*+\/\n?/');
- $comment = $this->MatchReg('/\\G\/\*(?s).*?\*+\/\n?/');//not the same as less.js to prevent fatal errors
- if( $comment ){
- return $this->NewObj4('Less_Tree_Comment',array($comment[0], false, $this->pos, $this->env->currentFileInfo));
- }
- }
-
- private function parseComments(){
- $comments = array();
-
- while( $this->pos < $this->input_len ){
- $comment = $this->parseComment();
- if( !$comment ){
- break;
- }
-
- $comments[] = $comment;
- }
-
- return $comments;
- }
-
-
-
- //
- // A string, which supports escaping " and '
- //
- // "milky way" 'he\'s the one!'
- //
- private function parseEntitiesQuoted() {
- $j = $this->pos;
- $e = false;
- $index = $this->pos;
-
- if( $this->input[$this->pos] === '~' ){
- $j++;
- $e = true; // Escaped strings
- }
-
- if( $this->input[$j] != '"' && $this->input[$j] !== "'" ){
- return;
- }
-
- if ($e) {
- $this->MatchChar('~');
- }
-
- // Fix for #124: match escaped newlines
- //$str = $this->MatchReg('/\\G"((?:[^"\\\\\r\n]|\\\\.)*)"|\'((?:[^\'\\\\\r\n]|\\\\.)*)\'/');
- $str = $this->MatchReg('/\\G"((?:[^"\\\\\r\n]|\\\\.|\\\\\r\n|\\\\[\n\r\f])*)"|\'((?:[^\'\\\\\r\n]|\\\\.|\\\\\r\n|\\\\[\n\r\f])*)\'/');
-
- if( $str ){
- $result = $str[0][0] == '"' ? $str[1] : $str[2];
- return $this->NewObj5('Less_Tree_Quoted',array($str[0], $result, $e, $index, $this->env->currentFileInfo) );
- }
- return;
- }
-
-
- //
- // A catch-all word, such as:
- //
- // black border-collapse
- //
- private function parseEntitiesKeyword(){
-
- //$k = $this->MatchReg('/\\G[_A-Za-z-][_A-Za-z0-9-]*/');
- $k = $this->MatchReg('/\\G%|\\G[_A-Za-z-][_A-Za-z0-9-]*/');
- if( $k ){
- $k = $k[0];
- $color = $this->fromKeyword($k);
- if( $color ){
- return $color;
- }
- return $this->NewObj1('Less_Tree_Keyword',$k);
- }
- }
-
- // duplicate of Less_Tree_Color::FromKeyword
- private function FromKeyword( $keyword ){
- $keyword = strtolower($keyword);
-
- if( Less_Colors::hasOwnProperty($keyword) ){
- // detect named color
- return $this->NewObj1('Less_Tree_Color',substr(Less_Colors::color($keyword), 1));
- }
-
- if( $keyword === 'transparent' ){
- return $this->NewObj3('Less_Tree_Color', array( array(0, 0, 0), 0, true));
- }
- }
-
- //
- // A function call
- //
- // rgb(255, 0, 255)
- //
- // We also try to catch IE's `alpha()`, but let the `alpha` parser
- // deal with the details.
- //
- // The arguments are parsed with the `entities.arguments` parser.
- //
- private function parseEntitiesCall(){
- $index = $this->pos;
-
- if( !preg_match('/\\G([\w-]+|%|progid:[\w\.]+)\(/', $this->input, $name,0,$this->pos) ){
- return;
- }
- $name = $name[1];
- $nameLC = strtolower($name);
-
- if ($nameLC === 'url') {
- return null;
- }
-
- $this->pos += strlen($name);
-
- if( $nameLC === 'alpha' ){
- $alpha_ret = $this->parseAlpha();
- if( $alpha_ret ){
- return $alpha_ret;
- }
- }
-
- $this->MatchChar('('); // Parse the '(' and consume whitespace.
-
- $args = $this->parseEntitiesArguments();
-
- if( !$this->MatchChar(')') ){
- return;
- }
-
- if ($name) {
- return $this->NewObj4('Less_Tree_Call',array($name, $args, $index, $this->env->currentFileInfo) );
- }
- }
-
- /**
- * Parse a list of arguments
- *
- * @return array
- */
- private function parseEntitiesArguments(){
-
- $args = array();
- while( true ){
- $arg = $this->MatchFuncs( array('parseEntitiesAssignment','parseExpression') );
- if( !$arg ){
- break;
- }
-
- $args[] = $arg;
- if( !$this->MatchChar(',') ){
- break;
- }
- }
- return $args;
- }
-
- private function parseEntitiesLiteral(){
- return $this->MatchFuncs( array('parseEntitiesDimension','parseEntitiesColor','parseEntitiesQuoted','parseUnicodeDescriptor') );
- }
-
- // Assignments are argument entities for calls.
- // They are present in ie filter properties as shown below.
- //
- // filter: progid:DXImageTransform.Microsoft.Alpha( *opacity=50* )
- //
- private function parseEntitiesAssignment() {
-
- $key = $this->MatchReg('/\\G\w+(?=\s?=)/');
- if( !$key ){
- return;
- }
-
- if( !$this->MatchChar('=') ){
- return;
- }
-
- $value = $this->parseEntity();
- if( $value ){
- return $this->NewObj2('Less_Tree_Assignment',array($key[0], $value));
- }
- }
-
- //
- // Parse url() tokens
- //
- // We use a specific rule for urls, because they don't really behave like
- // standard function calls. The difference is that the argument doesn't have
- // to be enclosed within a string, so it can't be parsed as an Expression.
- //
- private function parseEntitiesUrl(){
-
-
- if( $this->input[$this->pos] !== 'u' || !$this->matchReg('/\\Gurl\(/') ){
- return;
- }
-
- $value = $this->match( array('parseEntitiesQuoted','parseEntitiesVariable','/\\Gdata\:.*?[^\)]+/','/\\G(?:(?:\\\\[\(\)\'"])|[^\(\)\'"])+/') );
- if( !$value ){
- $value = '';
- }
-
-
- $this->expectChar(')');
-
-
- if( isset($value->value) || $value instanceof Less_Tree_Variable ){
- return $this->NewObj2('Less_Tree_Url',array($value, $this->env->currentFileInfo));
- }
-
- return $this->NewObj2('Less_Tree_Url', array( $this->NewObj1('Less_Tree_Anonymous',$value), $this->env->currentFileInfo) );
- }
-
-
- //
- // A Variable entity, such as `@fink`, in
- //
- // width: @fink + 2px
- //
- // We use a different parser for variable definitions,
- // see `parsers.variable`.
- //
- private function parseEntitiesVariable(){
- $index = $this->pos;
- if ($this->PeekChar('@') && ($name = $this->MatchReg('/\\G@@?[\w-]+/'))) {
- return $this->NewObj3('Less_Tree_Variable', array( $name[0], $index, $this->env->currentFileInfo));
- }
- }
-
-
- // A variable entity useing the protective {} e.g. @{var}
- private function parseEntitiesVariableCurly() {
- $index = $this->pos;
-
- if( $this->input_len > ($this->pos+1) && $this->input[$this->pos] === '@' && ($curly = $this->MatchReg('/\\G@\{([\w-]+)\}/')) ){
- return $this->NewObj3('Less_Tree_Variable',array('@'.$curly[1], $index, $this->env->currentFileInfo));
- }
- }
-
- //
- // A Hexadecimal color
- //
- // #4F3C2F
- //
- // `rgb` and `hsl` colors are parsed through the `entities.call` parser.
- //
- private function parseEntitiesColor(){
- if ($this->PeekChar('#') && ($rgb = $this->MatchReg('/\\G#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/'))) {
- return $this->NewObj1('Less_Tree_Color',$rgb[1]);
- }
- }
-
- //
- // A Dimension, that is, a number and a unit
- //
- // 0.5em 95%
- //
- private function parseEntitiesDimension(){
-
- $c = @ord($this->input[$this->pos]);
-
- //Is the first char of the dimension 0-9, '.', '+' or '-'
- if (($c > 57 || $c < 43) || $c === 47 || $c == 44){
- return;
- }
-
- $value = $this->MatchReg('/\\G([+-]?\d*\.?\d+)(%|[a-z]+)?/');
- if( $value ){
-
- if( isset($value[2]) ){
- return $this->NewObj2('Less_Tree_Dimension', array($value[1],$value[2]));
- }
- return $this->NewObj1('Less_Tree_Dimension',$value[1]);
- }
- }
-
-
- //
- // A unicode descriptor, as is used in unicode-range
- //
- // U+0?? or U+00A1-00A9
- //
- function parseUnicodeDescriptor() {
- $ud = $this->MatchReg('/\\G(U\+[0-9a-fA-F?]+)(\-[0-9a-fA-F?]+)?/');
- if( $ud ){
- return $this->NewObj1('Less_Tree_UnicodeDescriptor', $ud[0]);
- }
- }
-
-
- //
- // JavaScript code to be evaluated
- //
- // `window.location.href`
- //
- private function parseEntitiesJavascript(){
- $e = false;
- $j = $this->pos;
- if( $this->input[$j] === '~' ){
- $j++;
- $e = true;
- }
- if( $this->input[$j] !== '`' ){
- return;
- }
- if( $e ){
- $this->MatchChar('~');
- }
- $str = $this->MatchReg('/\\G`([^`]*)`/');
- if( $str ){
- return $this->NewObj3('Less_Tree_Javascript', array($str[1], $this->pos, $e));
- }
- }
-
-
- //
- // The variable part of a variable definition. Used in the `rule` parser
- //
- // @fink:
- //
- private function parseVariable(){
- if ($this->PeekChar('@') && ($name = $this->MatchReg('/\\G(@[\w-]+)\s*:/'))) {
- return $name[1];
- }
- }
-
-
- //
- // The variable part of a variable definition. Used in the `rule` parser
- //
- // @fink();
- //
- private function parseRulesetCall(){
-
- if( $this->input[$this->pos] === '@' && ($name = $this->MatchReg('/\\G(@[\w-]+)\s*\(\s*\)\s*;/')) ){
- return $this->NewObj1('Less_Tree_RulesetCall', $name[1] );
- }
- }
-
-
- //
- // extend syntax - used to extend selectors
- //
- function parseExtend($isRule = false){
-
- $index = $this->pos;
- $extendList = array();
-
-
- if( !$this->MatchReg( $isRule ? '/\\G&:extend\(/' : '/\\G:extend\(/' ) ){ return; }
-
- do{
- $option = null;
- $elements = array();
- while( true ){
- $option = $this->MatchReg('/\\G(all)(?=\s*(\)|,))/');
- if( $option ){ break; }
- $e = $this->parseElement();
- if( !$e ){ break; }
- $elements[] = $e;
- }
-
- if( $option ){
- $option = $option[1];
- }
-
- $extendList[] = $this->NewObj3('Less_Tree_Extend', array( $this->NewObj1('Less_Tree_Selector',$elements), $option, $index ));
-
- }while( $this->MatchChar(",") );
-
- $this->expect('/\\G\)/');
-
- if( $isRule ){
- $this->expect('/\\G;/');
- }
-
- return $extendList;
- }
-
-
- //
- // A Mixin call, with an optional argument list
- //
- // #mixins > .square(#fff);
- // .rounded(4px, black);
- // .button;
- //
- // The `while` loop is there because mixins can be
- // namespaced, but we only support the child and descendant
- // selector for now.
- //
- private function parseMixinCall(){
-
- $char = $this->input[$this->pos];
- if( $char !== '.' && $char !== '#' ){
- return;
- }
-
- $index = $this->pos;
- $this->save(); // stop us absorbing part of an invalid selector
-
- $elements = $this->parseMixinCallElements();
-
- if( $elements ){
-
- if( $this->MatchChar('(') ){
- $returned = $this->parseMixinArgs(true);
- $args = $returned['args'];
- $this->expectChar(')');
- }else{
- $args = array();
- }
-
- $important = $this->parseImportant();
-
- if( $this->parseEnd() ){
- $this->forget();
- return $this->NewObj5('Less_Tree_Mixin_Call', array( $elements, $args, $index, $this->env->currentFileInfo, $important));
- }
- }
-
- $this->restore();
- }
-
-
- private function parseMixinCallElements(){
- $elements = array();
- $c = null;
-
- while( true ){
- $elemIndex = $this->pos;
- $e = $this->MatchReg('/\\G[#.](?:[\w-]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/');
- if( !$e ){
- break;
- }
- $elements[] = $this->NewObj4('Less_Tree_Element', array($c, $e[0], $elemIndex, $this->env->currentFileInfo));
- $c = $this->MatchChar('>');
- }
-
- return $elements;
- }
-
-
-
- /**
- * @param boolean $isCall
- */
- private function parseMixinArgs( $isCall ){
- $expressions = array();
- $argsSemiColon = array();
- $isSemiColonSeperated = null;
- $argsComma = array();
- $expressionContainsNamed = null;
- $name = null;
- $returner = array('args'=>array(), 'variadic'=> false);
-
- $this->save();
-
- while( true ){
- if( $isCall ){
- $arg = $this->MatchFuncs( array( 'parseDetachedRuleset','parseExpression' ) );
- } else {
- $this->parseComments();
- if( $this->input[ $this->pos ] === '.' && $this->MatchReg('/\\G\.{3}/') ){
- $returner['variadic'] = true;
- if( $this->MatchChar(";") && !$isSemiColonSeperated ){
- $isSemiColonSeperated = true;
- }
-
- if( $isSemiColonSeperated ){
- $argsSemiColon[] = array('variadic'=>true);
- }else{
- $argsComma[] = array('variadic'=>true);
- }
- break;
- }
- $arg = $this->MatchFuncs( array('parseEntitiesVariable','parseEntitiesLiteral','parseEntitiesKeyword') );
- }
-
- if( !$arg ){
- break;
- }
-
-
- $nameLoop = null;
- if( $arg instanceof Less_Tree_Expression ){
- $arg->throwAwayComments();
- }
- $value = $arg;
- $val = null;
-
- if( $isCall ){
- // Variable
- if( property_exists($arg,'value') && count($arg->value) == 1 ){
- $val = $arg->value[0];
- }
- } else {
- $val = $arg;
- }
-
-
- if( $val instanceof Less_Tree_Variable ){
-
- if( $this->MatchChar(':') ){
- if( $expressions ){
- if( $isSemiColonSeperated ){
- $this->Error('Cannot mix ; and , as delimiter types');
- }
- $expressionContainsNamed = true;
- }
-
- // we do not support setting a ruleset as a default variable - it doesn't make sense
- // However if we do want to add it, there is nothing blocking it, just don't error
- // and remove isCall dependency below
- $value = null;
- if( $isCall ){
- $value = $this->parseDetachedRuleset();
- }
- if( !$value ){
- $value = $this->parseExpression();
- }
-
- if( !$value ){
- if( $isCall ){
- $this->Error('could not understand value for named argument');
- } else {
- $this->restore();
- $returner['args'] = array();
- return $returner;
- }
- }
-
- $nameLoop = ($name = $val->name);
- }elseif( !$isCall && $this->MatchReg('/\\G\.{3}/') ){
- $returner['variadic'] = true;
- if( $this->MatchChar(";") && !$isSemiColonSeperated ){
- $isSemiColonSeperated = true;
- }
- if( $isSemiColonSeperated ){
- $argsSemiColon[] = array('name'=> $arg->name, 'variadic' => true);
- }else{
- $argsComma[] = array('name'=> $arg->name, 'variadic' => true);
- }
- break;
- }elseif( !$isCall ){
- $name = $nameLoop = $val->name;
- $value = null;
- }
- }
-
- if( $value ){
- $expressions[] = $value;
- }
-
- $argsComma[] = array('name'=>$nameLoop, 'value'=>$value );
-
- if( $this->MatchChar(',') ){
- continue;
- }
-
- if( $this->MatchChar(';') || $isSemiColonSeperated ){
-
- if( $expressionContainsNamed ){
- $this->Error('Cannot mix ; and , as delimiter types');
- }
-
- $isSemiColonSeperated = true;
-
- if( count($expressions) > 1 ){
- $value = $this->NewObj1('Less_Tree_Value', $expressions);
- }
- $argsSemiColon[] = array('name'=>$name, 'value'=>$value );
-
- $name = null;
- $expressions = array();
- $expressionContainsNamed = false;
- }
- }
-
- $this->forget();
- $returner['args'] = ($isSemiColonSeperated ? $argsSemiColon : $argsComma);
- return $returner;
- }
-
-
-
- //
- // A Mixin definition, with a list of parameters
- //
- // .rounded (@radius: 2px, @color) {
- // ...
- // }
- //
- // Until we have a finer grained state-machine, we have to
- // do a look-ahead, to make sure we don't have a mixin call.
- // See the `rule` function for more information.
- //
- // We start by matching `.rounded (`, and then proceed on to
- // the argument list, which has optional default values.
- // We store the parameters in `params`, with a `value` key,
- // if there is a value, such as in the case of `@radius`.
- //
- // Once we've got our params list, and a closing `)`, we parse
- // the `{...}` block.
- //
- private function parseMixinDefinition(){
- $cond = null;
-
- $char = $this->input[$this->pos];
- if( ($char !== '.' && $char !== '#') || ($char === '{' && $this->PeekReg('/\\G[^{]*\}/')) ){
- return;
- }
-
- $this->save();
-
- $match = $this->MatchReg('/\\G([#.](?:[\w-]|\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+)\s*\(/');
- if( $match ){
- $name = $match[1];
-
- $argInfo = $this->parseMixinArgs( false );
- $params = $argInfo['args'];
- $variadic = $argInfo['variadic'];
-
-
- // .mixincall("@{a}");
- // looks a bit like a mixin definition..
- // also
- // .mixincall(@a: {rule: set;});
- // so we have to be nice and restore
- if( !$this->MatchChar(')') ){
- $this->furthest = $this->pos;
- $this->restore();
- return;
- }
-
-
- $this->parseComments();
-
- if ($this->MatchReg('/\\Gwhen/')) { // Guard
- $cond = $this->expect('parseConditions', 'Expected conditions');
- }
-
- $ruleset = $this->parseBlock();
-
- if( is_array($ruleset) ){
- $this->forget();
- return $this->NewObj5('Less_Tree_Mixin_Definition', array( $name, $params, $ruleset, $cond, $variadic));
- }
-
- $this->restore();
- }else{
- $this->forget();
- }
- }
-
- //
- // Entities are the smallest recognized token,
- // and can be found inside a rule's value.
- //
- private function parseEntity(){
-
- return $this->MatchFuncs( array('parseEntitiesLiteral','parseEntitiesVariable','parseEntitiesUrl','parseEntitiesCall','parseEntitiesKeyword','parseEntitiesJavascript','parseComment') );
- }
-
- //
- // A Rule terminator. Note that we use `peek()` to check for '}',
- // because the `block` rule will be expecting it, but we still need to make sure
- // it's there, if ';' was ommitted.
- //
- private function parseEnd(){
- return $this->MatchChar(';') || $this->PeekChar('}');
- }
-
- //
- // IE's alpha function
- //
- // alpha(opacity=88)
- //
- private function parseAlpha(){
-
- if ( ! $this->MatchReg('/\\G\(opacity=/i')) {
- return;
- }
-
- $value = $this->MatchReg('/\\G[0-9]+/');
- if( $value ){
- $value = $value[0];
- }else{
- $value = $this->parseEntitiesVariable();
- if( !$value ){
- return;
- }
- }
-
- $this->expectChar(')');
- return $this->NewObj1('Less_Tree_Alpha',$value);
- }
-
-
- //
- // A Selector Element
- //
- // div
- // + h1
- // #socks
- // input[type="text"]
- //
- // Elements are the building blocks for Selectors,
- // they are made out of a `Combinator` (see combinator rule),
- // and an element name, such as a tag a class, or `*`.
- //
- private function parseElement(){
- $c = $this->parseCombinator();
- $index = $this->pos;
-
- $e = $this->match( array('/\\G(?:\d+\.\d+|\d+)%/', '/\\G(?:[.#]?|:*)(?:[\w-]|[^\x00-\x9f]|\\\\(?:[A-Fa-f0-9]{1,6} ?|[^A-Fa-f0-9]))+/',
- '#*', '#&', 'parseAttribute', '/\\G\([^()@]+\)/', '/\\G[\.#](?=@)/', 'parseEntitiesVariableCurly') );
-
- if( is_null($e) ){
- $this->save();
- if( $this->MatchChar('(') ){
- if( ($v = $this->parseSelector()) && $this->MatchChar(')') ){
- $e = $this->NewObj1('Less_Tree_Paren',$v);
- $this->forget();
- }else{
- $this->restore();
- }
- }else{
- $this->forget();
- }
- }
-
- if( !is_null($e) ){
- return $this->NewObj4('Less_Tree_Element',array( $c, $e, $index, $this->env->currentFileInfo));
- }
- }
-
- //
- // Combinators combine elements together, in a Selector.
- //
- // Because our parser isn't white-space sensitive, special care
- // has to be taken, when parsing the descendant combinator, ` `,
- // as it's an empty space. We have to check the previous character
- // in the input, to see if it's a ` ` character.
- //
- private function parseCombinator(){
- if( $this->pos < $this->input_len ){
- $c = $this->input[$this->pos];
- if ($c === '>' || $c === '+' || $c === '~' || $c === '|' || $c === '^' ){
-
- $this->pos++;
- if( $this->input[$this->pos] === '^' ){
- $c = '^^';
- $this->pos++;
- }
-
- $this->skipWhitespace(0);
-
- return $c;
- }
-
- if( $this->pos > 0 && $this->isWhitespace(-1) ){
- return ' ';
- }
- }
- }
-
- //
- // A CSS selector (see selector below)
- // with less extensions e.g. the ability to extend and guard
- //
- private function parseLessSelector(){
- return $this->parseSelector(true);
- }
-
- //
- // A CSS Selector
- //
- // .class > div + h1
- // li a:hover
- //
- // Selectors are made out of one or more Elements, see above.
- //
- private function parseSelector( $isLess = false ){
- $elements = array();
- $extendList = array();
- $condition = null;
- $when = false;
- $extend = false;
- $e = null;
- $c = null;
- $index = $this->pos;
-
- while( ($isLess && ($extend = $this->parseExtend())) || ($isLess && ($when = $this->MatchReg('/\\Gwhen/') )) || ($e = $this->parseElement()) ){
- if( $when ){
- $condition = $this->expect('parseConditions', 'expected condition');
- }elseif( $condition ){
- //error("CSS guard can only be used at the end of selector");
- }elseif( $extend ){
- $extendList = array_merge($extendList,$extend);
- }else{
- //if( count($extendList) ){
- //error("Extend can only be used at the end of selector");
- //}
- if( $this->pos < $this->input_len ){
- $c = $this->input[ $this->pos ];
- }
- $elements[] = $e;
- $e = null;
- }
-
- if( $c === '{' || $c === '}' || $c === ';' || $c === ',' || $c === ')') { break; }
- }
-
- if( $elements ){
- return $this->NewObj5('Less_Tree_Selector',array($elements, $extendList, $condition, $index, $this->env->currentFileInfo));
- }
- if( $extendList ) {
- $this->Error('Extend must be used to extend a selector, it cannot be used on its own');
- }
- }
-
- private function parseTag(){
- return ( $tag = $this->MatchReg('/\\G[A-Za-z][A-Za-z-]*[0-9]?/') ) ? $tag : $this->MatchChar('*');
- }
-
- private function parseAttribute(){
-
- $val = null;
-
- if( !$this->MatchChar('[') ){
- return;
- }
-
- $key = $this->parseEntitiesVariableCurly();
- if( !$key ){
- $key = $this->expect('/\\G(?:[_A-Za-z0-9-\*]*\|)?(?:[_A-Za-z0-9-]|\\\\.)+/');
- }
-
- $op = $this->MatchReg('/\\G[|~*$^]?=/');
- if( $op ){
- $val = $this->match( array('parseEntitiesQuoted','/\\G[0-9]+%/','/\\G[\w-]+/','parseEntitiesVariableCurly') );
- }
-
- $this->expectChar(']');
-
- return $this->NewObj3('Less_Tree_Attribute',array( $key, $op[0], $val));
- }
-
- //
- // The `block` rule is used by `ruleset` and `mixin.definition`.
- // It's a wrapper around the `primary` rule, with added `{}`.
- //
- private function parseBlock(){
- if( $this->MatchChar('{') ){
- $content = $this->parsePrimary();
- if( $this->MatchChar('}') ){
- return $content;
- }
- }
- }
-
- private function parseBlockRuleset(){
- $block = $this->parseBlock();
-
- if( $block ){
- $block = $this->NewObj2('Less_Tree_Ruleset',array( null, $block));
- }
-
- return $block;
- }
-
- private function parseDetachedRuleset(){
- $blockRuleset = $this->parseBlockRuleset();
- if( $blockRuleset ){
- return $this->NewObj1('Less_Tree_DetachedRuleset',$blockRuleset);
- }
- }
-
- //
- // div, .class, body > p {...}
- //
- private function parseRuleset(){
- $selectors = array();
-
- $this->save();
-
- while( true ){
- $s = $this->parseLessSelector();
- if( !$s ){
- break;
- }
- $selectors[] = $s;
- $this->parseComments();
-
- if( $s->condition && count($selectors) > 1 ){
- $this->Error('Guards are only currently allowed on a single selector.');
- }
-
- if( !$this->MatchChar(',') ){
- break;
- }
- if( $s->condition ){
- $this->Error('Guards are only currently allowed on a single selector.');
- }
- $this->parseComments();
- }
-
-
- if( $selectors ){
- $rules = $this->parseBlock();
- if( is_array($rules) ){
- $this->forget();
- return $this->NewObj2('Less_Tree_Ruleset',array( $selectors, $rules)); //Less_Environment::$strictImports
- }
- }
-
- // Backtrack
- $this->furthest = $this->pos;
- $this->restore();
- }
-
- /**
- * Custom less.php parse function for finding simple name-value css pairs
- * ex: width:100px;
- *
- */
- private function parseNameValue(){
-
- $index = $this->pos;
- $this->save();
-
-
- //$match = $this->MatchReg('/\\G([a-zA-Z\-]+)\s*:\s*((?:\'")?[a-zA-Z0-9\-% \.,!]+?(?:\'")?)\s*([;}])/');
- $match = $this->MatchReg('/\\G([a-zA-Z\-]+)\s*:\s*([\'"]?[#a-zA-Z0-9\-%\.,]+?[\'"]?) *(! *important)?\s*([;}])/');
- if( $match ){
-
- if( $match[4] == '}' ){
- $this->pos = $index + strlen($match[0])-1;
- }
-
- if( $match[3] ){
- $match[2] .= ' !important';
- }
-
- return $this->NewObj4('Less_Tree_NameValue',array( $match[1], $match[2], $index, $this->env->currentFileInfo));
- }
-
- $this->restore();
- }
-
-
- private function parseRule( $tryAnonymous = null ){
-
- $merge = false;
- $startOfRule = $this->pos;
-
- $c = $this->input[$this->pos];
- if( $c === '.' || $c === '#' || $c === '&' ){
- return;
- }
-
- $this->save();
- $name = $this->MatchFuncs( array('parseVariable','parseRuleProperty'));
-
- if( $name ){
-
- $isVariable = is_string($name);
-
- $value = null;
- if( $isVariable ){
- $value = $this->parseDetachedRuleset();
- }
-
- $important = null;
- if( !$value ){
-
- // prefer to try to parse first if its a variable or we are compressing
- // but always fallback on the other one
- //if( !$tryAnonymous && is_string($name) && $name[0] === '@' ){
- if( !$tryAnonymous && (Less_Parser::$options['compress'] || $isVariable) ){
- $value = $this->MatchFuncs( array('parseValue','parseAnonymousValue'));
- }else{
- $value = $this->MatchFuncs( array('parseAnonymousValue','parseValue'));
- }
-
- $important = $this->parseImportant();
-
- // a name returned by this.ruleProperty() is always an array of the form:
- // [string-1, ..., string-n, ""] or [string-1, ..., string-n, "+"]
- // where each item is a tree.Keyword or tree.Variable
- if( !$isVariable && is_array($name) ){
- $nm = array_pop($name);
- if( $nm->value ){
- $merge = $nm->value;
- }
- }
- }
-
-
- if( $value && $this->parseEnd() ){
- $this->forget();
- return $this->NewObj6('Less_Tree_Rule',array( $name, $value, $important, $merge, $startOfRule, $this->env->currentFileInfo));
- }else{
- $this->furthest = $this->pos;
- $this->restore();
- if( $value && !$tryAnonymous ){
- return $this->parseRule(true);
- }
- }
- }else{
- $this->forget();
- }
- }
-
- function parseAnonymousValue(){
-
- if( preg_match('/\\G([^@+\/\'"*`(;{}-]*);/',$this->input, $match, 0, $this->pos) ){
- $this->pos += strlen($match[1]);
- return $this->NewObj1('Less_Tree_Anonymous',$match[1]);
- }
- }
-
- //
- // An @import directive
- //
- // @import "lib";
- //
- // Depending on our environment, importing is done differently:
- // In the browser, it's an XHR request, in Node, it would be a
- // file-system operation. The function used for importing is
- // stored in `import`, which we pass to the Import constructor.
- //
- private function parseImport(){
-
- $this->save();
-
- $dir = $this->MatchReg('/\\G@import?\s+/');
-
- if( $dir ){
- $options = $this->parseImportOptions();
- $path = $this->MatchFuncs( array('parseEntitiesQuoted','parseEntitiesUrl'));
-
- if( $path ){
- $features = $this->parseMediaFeatures();
- if( $this->MatchChar(';') ){
- if( $features ){
- $features = $this->NewObj1('Less_Tree_Value',$features);
- }
-
- $this->forget();
- return $this->NewObj5('Less_Tree_Import',array( $path, $features, $options, $this->pos, $this->env->currentFileInfo));
- }
- }
- }
-
- $this->restore();
- }
-
- private function parseImportOptions(){
-
- $options = array();
-
- // list of options, surrounded by parens
- if( !$this->MatchChar('(') ){
- return $options;
- }
- do{
- $optionName = $this->parseImportOption();
- if( $optionName ){
- $value = true;
- switch( $optionName ){
- case "css":
- $optionName = "less";
- $value = false;
- break;
- case "once":
- $optionName = "multiple";
- $value = false;
- break;
- }
- $options[$optionName] = $value;
- if( !$this->MatchChar(',') ){ break; }
- }
- }while( $optionName );
- $this->expectChar(')');
- return $options;
- }
-
- private function parseImportOption(){
- $opt = $this->MatchReg('/\\G(less|css|multiple|once|inline|reference|optional)/');
- if( $opt ){
- return $opt[1];
- }
- }
-
- private function parseMediaFeature() {
- $nodes = array();
-
- do{
- $e = $this->MatchFuncs(array('parseEntitiesKeyword','parseEntitiesVariable'));
- if( $e ){
- $nodes[] = $e;
- } elseif ($this->MatchChar('(')) {
- $p = $this->parseProperty();
- $e = $this->parseValue();
- if ($this->MatchChar(')')) {
- if ($p && $e) {
- $r = $this->NewObj7('Less_Tree_Rule', array( $p, $e, null, null, $this->pos, $this->env->currentFileInfo, true));
- $nodes[] = $this->NewObj1('Less_Tree_Paren',$r);
- } elseif ($e) {
- $nodes[] = $this->NewObj1('Less_Tree_Paren',$e);
- } else {
- return null;
- }
- } else
- return null;
- }
- } while ($e);
-
- if ($nodes) {
- return $this->NewObj1('Less_Tree_Expression',$nodes);
- }
- }
-
- private function parseMediaFeatures() {
- $features = array();
-
- do{
- $e = $this->parseMediaFeature();
- if( $e ){
- $features[] = $e;
- if (!$this->MatchChar(',')) break;
- }else{
- $e = $this->parseEntitiesVariable();
- if( $e ){
- $features[] = $e;
- if (!$this->MatchChar(',')) break;
- }
- }
- } while ($e);
-
- return $features ? $features : null;
- }
-
- private function parseMedia() {
- if( $this->MatchReg('/\\G@media/') ){
- $features = $this->parseMediaFeatures();
- $rules = $this->parseBlock();
-
- if( is_array($rules) ){
- return $this->NewObj4('Less_Tree_Media',array( $rules, $features, $this->pos, $this->env->currentFileInfo));
- }
- }
- }
-
-
- //
- // A CSS Directive
- //
- // @charset "utf-8";
- //
- private function parseDirective(){
-
- if( !$this->PeekChar('@') ){
- return;
- }
-
- $rules = null;
- $index = $this->pos;
- $hasBlock = true;
- $hasIdentifier = false;
- $hasExpression = false;
- $hasUnknown = false;
-
-
- $value = $this->MatchFuncs(array('parseImport','parseMedia'));
- if( $value ){
- return $value;
- }
-
- $this->save();
-
- $name = $this->MatchReg('/\\G@[a-z-]+/');
-
- if( !$name ) return;
- $name = $name[0];
-
-
- $nonVendorSpecificName = $name;
- $pos = strpos($name,'-', 2);
- if( $name[1] == '-' && $pos > 0 ){
- $nonVendorSpecificName = "@" . substr($name, $pos + 1);
- }
-
-
- switch( $nonVendorSpecificName ){
- /*
- case "@font-face":
- case "@viewport":
- case "@top-left":
- case "@top-left-corner":
- case "@top-center":
- case "@top-right":
- case "@top-right-corner":
- case "@bottom-left":
- case "@bottom-left-corner":
- case "@bottom-center":
- case "@bottom-right":
- case "@bottom-right-corner":
- case "@left-top":
- case "@left-middle":
- case "@left-bottom":
- case "@right-top":
- case "@right-middle":
- case "@right-bottom":
- hasBlock = true;
- break;
- */
- case "@charset":
- $hasIdentifier = true;
- $hasBlock = false;
- break;
- case "@namespace":
- $hasExpression = true;
- $hasBlock = false;
- break;
- case "@keyframes":
- $hasIdentifier = true;
- break;
- case "@host":
- case "@page":
- case "@document":
- case "@supports":
- $hasUnknown = true;
- break;
- }
-
- if( $hasIdentifier ){
- $value = $this->parseEntity();
- if( !$value ){
- $this->error("expected " . $name . " identifier");
- }
- } else if( $hasExpression ){
- $value = $this->parseExpression();
- if( !$value ){
- $this->error("expected " . $name. " expression");
- }
- } else if ($hasUnknown) {
-
- $value = $this->MatchReg('/\\G[^{;]+/');
- if( $value ){
- $value = $this->NewObj1('Less_Tree_Anonymous',trim($value[0]));
- }
- }
-
- if( $hasBlock ){
- $rules = $this->parseBlockRuleset();
- }
-
- if( $rules || (!$hasBlock && $value && $this->MatchChar(';'))) {
- $this->forget();
- return $this->NewObj5('Less_Tree_Directive',array($name, $value, $rules, $index, $this->env->currentFileInfo));
- }
-
- $this->restore();
- }
-
-
- //
- // A Value is a comma-delimited list of Expressions
- //
- // font-family: Baskerville, Georgia, serif;
- //
- // In a Rule, a Value represents everything after the `:`,
- // and before the `;`.
- //
- private function parseValue(){
- $expressions = array();
-
- do{
- $e = $this->parseExpression();
- if( $e ){
- $expressions[] = $e;
- if (! $this->MatchChar(',')) {
- break;
- }
- }
- }while($e);
-
- if( $expressions ){
- return $this->NewObj1('Less_Tree_Value',$expressions);
- }
- }
-
- private function parseImportant (){
- if( $this->PeekChar('!') && $this->MatchReg('/\\G! *important/') ){
- return ' !important';
- }
- }
-
- private function parseSub (){
-
- if( $this->MatchChar('(') ){
- $a = $this->parseAddition();
- if( $a ){
- $this->expectChar(')');
- return $this->NewObj2('Less_Tree_Expression',array( array($a), true) ); //instead of $e->parens = true so the value is cached
- }
- }
- }
-
-
- /**
- * Parses multiplication operation
- *
- * @return Less_Tree_Operation|null
- */
- function parseMultiplication(){
-
- $return = $m = $this->parseOperand();
- if( $return ){
- while( true ){
-
- $isSpaced = $this->isWhitespace( -1 );
-
- if( $this->PeekReg('/\\G\/[*\/]/') ){
- break;
- }
-
- $op = $this->MatchChar('/');
- if( !$op ){
- $op = $this->MatchChar('*');
- if( !$op ){
- break;
- }
- }
-
- $a = $this->parseOperand();
-
- if(!$a) { break; }
-
- $m->parensInOp = true;
- $a->parensInOp = true;
- $return = $this->NewObj3('Less_Tree_Operation',array( $op, array( $return, $a ), $isSpaced) );
- }
- }
- return $return;
-
- }
-
-
- /**
- * Parses an addition operation
- *
- * @return Less_Tree_Operation|null
- */
- private function parseAddition (){
-
- $return = $m = $this->parseMultiplication();
- if( $return ){
- while( true ){
-
- $isSpaced = $this->isWhitespace( -1 );
-
- $op = $this->MatchReg('/\\G[-+]\s+/');
- if( $op ){
- $op = $op[0];
- }else{
- if( !$isSpaced ){
- $op = $this->match(array('#+','#-'));
- }
- if( !$op ){
- break;
- }
- }
-
- $a = $this->parseMultiplication();
- if( !$a ){
- break;
- }
-
- $m->parensInOp = true;
- $a->parensInOp = true;
- $return = $this->NewObj3('Less_Tree_Operation',array($op, array($return, $a), $isSpaced));
- }
- }
-
- return $return;
- }
-
-
- /**
- * Parses the conditions
- *
- * @return Less_Tree_Condition|null
- */
- private function parseConditions() {
- $index = $this->pos;
- $return = $a = $this->parseCondition();
- if( $a ){
- while( true ){
- if( !$this->PeekReg('/\\G,\s*(not\s*)?\(/') || !$this->MatchChar(',') ){
- break;
- }
- $b = $this->parseCondition();
- if( !$b ){
- break;
- }
-
- $return = $this->NewObj4('Less_Tree_Condition',array('or', $return, $b, $index));
- }
- return $return;
- }
- }
-
- private function parseCondition() {
- $index = $this->pos;
- $negate = false;
- $c = null;
-
- if ($this->MatchReg('/\\Gnot/')) $negate = true;
- $this->expectChar('(');
- $a = $this->MatchFuncs(array('parseAddition','parseEntitiesKeyword','parseEntitiesQuoted'));
-
- if( $a ){
- $op = $this->MatchReg('/\\G(?:>=|<=|=<|[<=>])/');
- if( $op ){
- $b = $this->MatchFuncs(array('parseAddition','parseEntitiesKeyword','parseEntitiesQuoted'));
- if( $b ){
- $c = $this->NewObj5('Less_Tree_Condition',array($op[0], $a, $b, $index, $negate));
- } else {
- $this->Error('Unexpected expression');
- }
- } else {
- $k = $this->NewObj1('Less_Tree_Keyword','true');
- $c = $this->NewObj5('Less_Tree_Condition',array('=', $a, $k, $index, $negate));
- }
- $this->expectChar(')');
- return $this->MatchReg('/\\Gand/') ? $this->NewObj3('Less_Tree_Condition',array('and', $c, $this->parseCondition())) : $c;
- }
- }
-
- /**
- * An operand is anything that can be part of an operation,
- * such as a Color, or a Variable
- *
- */
- private function parseOperand (){
-
- $negate = false;
- $offset = $this->pos+1;
- if( $offset >= $this->input_len ){
- return;
- }
- $char = $this->input[$offset];
- if( $char === '@' || $char === '(' ){
- $negate = $this->MatchChar('-');
- }
-
- $o = $this->MatchFuncs(array('parseSub','parseEntitiesDimension','parseEntitiesColor','parseEntitiesVariable','parseEntitiesCall'));
-
- if( $negate ){
- $o->parensInOp = true;
- $o = $this->NewObj1('Less_Tree_Negative',$o);
- }
-
- return $o;
- }
-
-
- /**
- * Expressions either represent mathematical operations,
- * or white-space delimited Entities.
- *
- * 1px solid black
- * @var * 2
- *
- * @return Less_Tree_Expression|null
- */
- private function parseExpression (){
- $entities = array();
-
- do{
- $e = $this->MatchFuncs(array('parseAddition','parseEntity'));
- if( $e ){
- $entities[] = $e;
- // operations do not allow keyword "/" dimension (e.g. small/20px) so we support that here
- if( !$this->PeekReg('/\\G\/[\/*]/') ){
- $delim = $this->MatchChar('/');
- if( $delim ){
- $entities[] = $this->NewObj1('Less_Tree_Anonymous',$delim);
- }
- }
- }
- }while($e);
-
- if( $entities ){
- return $this->NewObj1('Less_Tree_Expression',$entities);
- }
- }
-
-
- /**
- * Parse a property
- * eg: 'min-width', 'orientation', etc
- *
- * @return string
- */
- private function parseProperty (){
- $name = $this->MatchReg('/\\G(\*?-?[_a-zA-Z0-9-]+)\s*:/');
- if( $name ){
- return $name[1];
- }
- }
-
-
- /**
- * Parse a rule property
- * eg: 'color', 'width', 'height', etc
- *
- * @return string
- */
- private function parseRuleProperty(){
- $offset = $this->pos;
- $name = array();
- $index = array();
- $length = 0;
-
-
- $this->rulePropertyMatch('/\\G(\*?)/', $offset, $length, $index, $name );
- while( $this->rulePropertyMatch('/\\G((?:[\w-]+)|(?:@\{[\w-]+\}))/', $offset, $length, $index, $name )); // !
-
- if( (count($name) > 1) && $this->rulePropertyMatch('/\\G\s*((?:\+_|\+)?)\s*:/', $offset, $length, $index, $name) ){
- // at last, we have the complete match now. move forward,
- // convert name particles to tree objects and return:
- $this->skipWhitespace($length);
-
- if( $name[0] === '' ){
- array_shift($name);
- array_shift($index);
- }
- foreach($name as $k => $s ){
- if( !$s || $s[0] !== '@' ){
- $name[$k] = $this->NewObj1('Less_Tree_Keyword',$s);
- }else{
- $name[$k] = $this->NewObj3('Less_Tree_Variable',array('@' . substr($s,2,-1), $index[$k], $this->env->currentFileInfo));
- }
- }
- return $name;
- }
-
-
- }
-
- private function rulePropertyMatch( $re, &$offset, &$length, &$index, &$name ){
- preg_match($re, $this->input, $a, 0, $offset);
- if( $a ){
- $index[] = $this->pos + $length;
- $length += strlen($a[0]);
- $offset += strlen($a[0]);
- $name[] = $a[1];
- return true;
- }
- }
-
- public static function serializeVars( $vars ){
- $s = '';
-
- foreach($vars as $name => $value){
- $s .= (($name[0] === '@') ? '' : '@') . $name .': '. $value . ((substr($value,-1) === ';') ? '' : ';');
- }
-
- return $s;
- }
-
-
- /**
- * Some versions of php have trouble with method_exists($a,$b) if $a is not an object
- *
- * @param string $b
- */
- public static function is_method($a,$b){
- return is_object($a) && method_exists($a,$b);
- }
-
-
- /**
- * Round numbers similarly to javascript
- * eg: 1.499999 to 1 instead of 2
- *
- */
- public static function round($i, $precision = 0){
-
- $precision = pow(10,$precision);
- $i = $i*$precision;
-
- $ceil = ceil($i);
- $floor = floor($i);
- if( ($ceil - $i) <= ($i - $floor) ){
- return $ceil/$precision;
- }else{
- return $floor/$precision;
- }
- }
-
-
- /**
- * Create Less_Tree_* objects and optionally generate a cache string
- *
- * @return mixed
- */
- public function NewObj0($class){
- $obj = new $class();
- if( $this->CacheEnabled() ){
- $obj->cache_string = ' new '.$class.'()';
- }
- return $obj;
- }
-
- public function NewObj1($class, $arg){
- $obj = new $class( $arg );
- if( $this->CacheEnabled() ){
- $obj->cache_string = ' new '.$class.'('.Less_Parser::ArgString($arg).')';
- }
- return $obj;
- }
-
- public function NewObj2($class, $args){
- $obj = new $class( $args[0], $args[1] );
- if( $this->CacheEnabled() ){
- $this->ObjCache( $obj, $class, $args);
- }
- return $obj;
- }
-
- public function NewObj3($class, $args){
- $obj = new $class( $args[0], $args[1], $args[2] );
- if( $this->CacheEnabled() ){
- $this->ObjCache( $obj, $class, $args);
- }
- return $obj;
- }
-
- public function NewObj4($class, $args){
- $obj = new $class( $args[0], $args[1], $args[2], $args[3] );
- if( $this->CacheEnabled() ){
- $this->ObjCache( $obj, $class, $args);
- }
- return $obj;
- }
-
- public function NewObj5($class, $args){
- $obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4] );
- if( $this->CacheEnabled() ){
- $this->ObjCache( $obj, $class, $args);
- }
- return $obj;
- }
-
- public function NewObj6($class, $args){
- $obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4], $args[5] );
- if( $this->CacheEnabled() ){
- $this->ObjCache( $obj, $class, $args);
- }
- return $obj;
- }
-
- public function NewObj7($class, $args){
- $obj = new $class( $args[0], $args[1], $args[2], $args[3], $args[4], $args[5], $args[6] );
- if( $this->CacheEnabled() ){
- $this->ObjCache( $obj, $class, $args);
- }
- return $obj;
- }
-
- //caching
- public function ObjCache($obj, $class, $args=array()){
- $obj->cache_string = ' new '.$class.'('. self::ArgCache($args).')';
- }
-
- public function ArgCache($args){
- return implode(',',array_map( array('Less_Parser','ArgString'),$args));
- }
-
-
- /**
- * Convert an argument to a string for use in the parser cache
- *
- * @return string
- */
- public static function ArgString($arg){
-
- $type = gettype($arg);
-
- if( $type === 'object'){
- $string = $arg->cache_string;
- unset($arg->cache_string);
- return $string;
-
- }elseif( $type === 'array' ){
- $string = ' Array(';
- foreach($arg as $k => $a){
- $string .= var_export($k,true).' => '.self::ArgString($a).',';
- }
- return $string . ')';
- }
-
- return var_export($arg,true);
- }
-
- public function Error($msg){
- throw new Less_Exception_Parser($msg, null, $this->furthest, $this->env->currentFileInfo);
- }
-
- public static function WinPath($path){
- return str_replace('\\', '/', $path);
- }
-
- public function CacheEnabled(){
- return false;
- //return (Less_Parser::$options['cache_method'] && (Less_Cache::$cache_dir || (Less_Parser::$options['cache_method'] == 'callback')));
- }
-
-}
-
-
-/**
- * Utility for css colors
- *
- * @package Less
- * @subpackage color
- */
-class Less_Colors {
-
- public static $colors = array(
- 'aliceblue'=>'#f0f8ff',
- 'antiquewhite'=>'#faebd7',
- 'aqua'=>'#00ffff',
- 'aquamarine'=>'#7fffd4',
- 'azure'=>'#f0ffff',
- 'beige'=>'#f5f5dc',
- 'bisque'=>'#ffe4c4',
- 'black'=>'#000000',
- 'blanchedalmond'=>'#ffebcd',
- 'blue'=>'#0000ff',
- 'blueviolet'=>'#8a2be2',
- 'brown'=>'#a52a2a',
- 'burlywood'=>'#deb887',
- 'cadetblue'=>'#5f9ea0',
- 'chartreuse'=>'#7fff00',
- 'chocolate'=>'#d2691e',
- 'coral'=>'#ff7f50',
- 'cornflowerblue'=>'#6495ed',
- 'cornsilk'=>'#fff8dc',
- 'crimson'=>'#dc143c',
- 'cyan'=>'#00ffff',
- 'darkblue'=>'#00008b',
- 'darkcyan'=>'#008b8b',
- 'darkgoldenrod'=>'#b8860b',
- 'darkgray'=>'#a9a9a9',
- 'darkgrey'=>'#a9a9a9',
- 'darkgreen'=>'#006400',
- 'darkkhaki'=>'#bdb76b',
- 'darkmagenta'=>'#8b008b',
- 'darkolivegreen'=>'#556b2f',
- 'darkorange'=>'#ff8c00',
- 'darkorchid'=>'#9932cc',
- 'darkred'=>'#8b0000',
- 'darksalmon'=>'#e9967a',
- 'darkseagreen'=>'#8fbc8f',
- 'darkslateblue'=>'#483d8b',
- 'darkslategray'=>'#2f4f4f',
- 'darkslategrey'=>'#2f4f4f',
- 'darkturquoise'=>'#00ced1',
- 'darkviolet'=>'#9400d3',
- 'deeppink'=>'#ff1493',
- 'deepskyblue'=>'#00bfff',
- 'dimgray'=>'#696969',
- 'dimgrey'=>'#696969',
- 'dodgerblue'=>'#1e90ff',
- 'firebrick'=>'#b22222',
- 'floralwhite'=>'#fffaf0',
- 'forestgreen'=>'#228b22',
- 'fuchsia'=>'#ff00ff',
- 'gainsboro'=>'#dcdcdc',
- 'ghostwhite'=>'#f8f8ff',
- 'gold'=>'#ffd700',
- 'goldenrod'=>'#daa520',
- 'gray'=>'#808080',
- 'grey'=>'#808080',
- 'green'=>'#008000',
- 'greenyellow'=>'#adff2f',
- 'honeydew'=>'#f0fff0',
- 'hotpink'=>'#ff69b4',
- 'indianred'=>'#cd5c5c',
- 'indigo'=>'#4b0082',
- 'ivory'=>'#fffff0',
- 'khaki'=>'#f0e68c',
- 'lavender'=>'#e6e6fa',
- 'lavenderblush'=>'#fff0f5',
- 'lawngreen'=>'#7cfc00',
- 'lemonchiffon'=>'#fffacd',
- 'lightblue'=>'#add8e6',
- 'lightcoral'=>'#f08080',
- 'lightcyan'=>'#e0ffff',
- 'lightgoldenrodyellow'=>'#fafad2',
- 'lightgray'=>'#d3d3d3',
- 'lightgrey'=>'#d3d3d3',
- 'lightgreen'=>'#90ee90',
- 'lightpink'=>'#ffb6c1',
- 'lightsalmon'=>'#ffa07a',
- 'lightseagreen'=>'#20b2aa',
- 'lightskyblue'=>'#87cefa',
- 'lightslategray'=>'#778899',
- 'lightslategrey'=>'#778899',
- 'lightsteelblue'=>'#b0c4de',
- 'lightyellow'=>'#ffffe0',
- 'lime'=>'#00ff00',
- 'limegreen'=>'#32cd32',
- 'linen'=>'#faf0e6',
- 'magenta'=>'#ff00ff',
- 'maroon'=>'#800000',
- 'mediumaquamarine'=>'#66cdaa',
- 'mediumblue'=>'#0000cd',
- 'mediumorchid'=>'#ba55d3',
- 'mediumpurple'=>'#9370d8',
- 'mediumseagreen'=>'#3cb371',
- 'mediumslateblue'=>'#7b68ee',
- 'mediumspringgreen'=>'#00fa9a',
- 'mediumturquoise'=>'#48d1cc',
- 'mediumvioletred'=>'#c71585',
- 'midnightblue'=>'#191970',
- 'mintcream'=>'#f5fffa',
- 'mistyrose'=>'#ffe4e1',
- 'moccasin'=>'#ffe4b5',
- 'navajowhite'=>'#ffdead',
- 'navy'=>'#000080',
- 'oldlace'=>'#fdf5e6',
- 'olive'=>'#808000',
- 'olivedrab'=>'#6b8e23',
- 'orange'=>'#ffa500',
- 'orangered'=>'#ff4500',
- 'orchid'=>'#da70d6',
- 'palegoldenrod'=>'#eee8aa',
- 'palegreen'=>'#98fb98',
- 'paleturquoise'=>'#afeeee',
- 'palevioletred'=>'#d87093',
- 'papayawhip'=>'#ffefd5',
- 'peachpuff'=>'#ffdab9',
- 'peru'=>'#cd853f',
- 'pink'=>'#ffc0cb',
- 'plum'=>'#dda0dd',
- 'powderblue'=>'#b0e0e6',
- 'purple'=>'#800080',
- 'red'=>'#ff0000',
- 'rosybrown'=>'#bc8f8f',
- 'royalblue'=>'#4169e1',
- 'saddlebrown'=>'#8b4513',
- 'salmon'=>'#fa8072',
- 'sandybrown'=>'#f4a460',
- 'seagreen'=>'#2e8b57',
- 'seashell'=>'#fff5ee',
- 'sienna'=>'#a0522d',
- 'silver'=>'#c0c0c0',
- 'skyblue'=>'#87ceeb',
- 'slateblue'=>'#6a5acd',
- 'slategray'=>'#708090',
- 'slategrey'=>'#708090',
- 'snow'=>'#fffafa',
- 'springgreen'=>'#00ff7f',
- 'steelblue'=>'#4682b4',
- 'tan'=>'#d2b48c',
- 'teal'=>'#008080',
- 'thistle'=>'#d8bfd8',
- 'tomato'=>'#ff6347',
- 'turquoise'=>'#40e0d0',
- 'violet'=>'#ee82ee',
- 'wheat'=>'#f5deb3',
- 'white'=>'#ffffff',
- 'whitesmoke'=>'#f5f5f5',
- 'yellow'=>'#ffff00',
- 'yellowgreen'=>'#9acd32'
- );
-
- public static function hasOwnProperty($color) {
- return isset(self::$colors[$color]);
- }
-
-
- public static function color($color) {
- return self::$colors[$color];
- }
-
-}
-
-
-
-/**
- * Environment
- *
- * @package Less
- * @subpackage environment
- */
-class Less_Environment{
-
- //public $paths = array(); // option - unmodified - paths to search for imports on
- //public static $files = array(); // list of files that have been imported, used for import-once
- //public $rootpath; // option - rootpath to append to URL's
- //public static $strictImports = null; // option -
- //public $insecure; // option - whether to allow imports from insecure ssl hosts
- //public $processImports; // option - whether to process imports. if false then imports will not be imported
- //public $javascriptEnabled; // option - whether JavaScript is enabled. if undefined, defaults to true
- //public $useFileCache; // browser only - whether to use the per file session cache
- public $currentFileInfo; // information about the current file - for error reporting and importing and making urls relative etc.
-
- public $importMultiple = false; // whether we are currently importing multiple copies
-
-
- /**
- * @var array
- */
- public $frames = array();
-
- /**
- * @var array
- */
- public $mediaBlocks = array();
-
- /**
- * @var array
- */
- public $mediaPath = array();
-
- public static $parensStack = 0;
-
- public static $tabLevel = 0;
-
- public static $lastRule = false;
-
- public static $_outputMap;
-
- public static $mixin_stack = 0;
-
- /**
- * @var array
- */
- public $functions = array();
-
-
- public function Init(){
-
- self::$parensStack = 0;
- self::$tabLevel = 0;
- self::$lastRule = false;
- self::$mixin_stack = 0;
-
- if( Less_Parser::$options['compress'] ){
-
- Less_Environment::$_outputMap = array(
- ',' => ',',
- ': ' => ':',
- '' => '',
- ' ' => ' ',
- ':' => ' :',
- '+' => '+',
- '~' => '~',
- '>' => '>',
- '|' => '|',
- '^' => '^',
- '^^' => '^^'
- );
-
- }else{
-
- Less_Environment::$_outputMap = array(
- ',' => ', ',
- ': ' => ': ',
- '' => '',
- ' ' => ' ',
- ':' => ' :',
- '+' => ' + ',
- '~' => ' ~ ',
- '>' => ' > ',
- '|' => '|',
- '^' => ' ^ ',
- '^^' => ' ^^ '
- );
-
- }
- }
-
-
- public function copyEvalEnv($frames = array() ){
- $new_env = new Less_Environment();
- $new_env->frames = $frames;
- return $new_env;
- }
-
-
- public static function isMathOn(){
- return !Less_Parser::$options['strictMath'] || Less_Environment::$parensStack;
- }
-
- public static function isPathRelative($path){
- return !preg_match('/^(?:[a-z-]+:|\/)/',$path);
- }
-
-
- /**
- * Canonicalize a path by resolving references to '/./', '/../'
- * Does not remove leading "../"
- * @param string path or url
- * @return string Canonicalized path
- *
- */
- public static function normalizePath($path){
-
- $segments = explode('/',$path);
- $segments = array_reverse($segments);
-
- $path = array();
- $path_len = 0;
-
- while( $segments ){
- $segment = array_pop($segments);
- switch( $segment ) {
-
- case '.':
- break;
-
- case '..':
- if( !$path_len || ( $path[$path_len-1] === '..') ){
- $path[] = $segment;
- $path_len++;
- }else{
- array_pop($path);
- $path_len--;
- }
- break;
-
- default:
- $path[] = $segment;
- $path_len++;
- break;
- }
- }
-
- return implode('/',$path);
- }
-
-
- public function unshiftFrame($frame){
- array_unshift($this->frames, $frame);
- }
-
- public function shiftFrame(){
- return array_shift($this->frames);
- }
-
-}
-
-
-/**
- * Builtin functions
- *
- * @package Less
- * @subpackage function
- * @see http://lesscss.org/functions/
- */
-class Less_Functions{
-
- public $env;
- public $currentFileInfo;
-
- function __construct($env, $currentFileInfo = null ){
- $this->env = $env;
- $this->currentFileInfo = $currentFileInfo;
- }
-
- /**
- * @param string $op
- */
- public static function operate( $op, $a, $b ){
- switch ($op) {
- case '+': return $a + $b;
- case '-': return $a - $b;
- case '*': return $a * $b;
- case '/': return $a / $b;
- }
- }
-
- public static function clamp($val, $max = 1){
- return min( max($val, 0), $max);
- }
-
- public static function fround( $value ){
-
- if( $value === 0 ){
- return $value;
- }
-
- if( Less_Parser::$options['numPrecision'] ){
- $p = pow(10, Less_Parser::$options['numPrecision']);
- return round( $value * $p) / $p;
- }
- return $value;
- }
-
- public static function number($n){
-
- if ($n instanceof Less_Tree_Dimension) {
- return floatval( $n->unit->is('%') ? $n->value / 100 : $n->value);
- } else if (is_numeric($n)) {
- return $n;
- } else {
- throw new Less_Exception_Compiler("color functions take numbers as parameters");
- }
- }
-
- public static function scaled($n, $size = 255 ){
- if( $n instanceof Less_Tree_Dimension && $n->unit->is('%') ){
- return (float)$n->value * $size / 100;
- } else {
- return Less_Functions::number($n);
- }
- }
-
- public function rgb ($r = null, $g = null, $b = null){
- if (is_null($r) || is_null($g) || is_null($b)) {
- throw new Less_Exception_Compiler("rgb expects three parameters");
- }
- return $this->rgba($r, $g, $b, 1.0);
- }
-
- public function rgba($r = null, $g = null, $b = null, $a = null){
- $rgb = array($r, $g, $b);
- $rgb = array_map(array('Less_Functions','scaled'),$rgb);
-
- $a = self::number($a);
- return new Less_Tree_Color($rgb, $a);
- }
-
- public function hsl($h, $s, $l){
- return $this->hsla($h, $s, $l, 1.0);
- }
-
- public function hsla($h, $s, $l, $a){
-
- $h = fmod(self::number($h), 360) / 360; // Classic % operator will change float to int
- $s = self::clamp(self::number($s));
- $l = self::clamp(self::number($l));
- $a = self::clamp(self::number($a));
-
- $m2 = $l <= 0.5 ? $l * ($s + 1) : $l + $s - $l * $s;
-
- $m1 = $l * 2 - $m2;
-
- return $this->rgba( self::hsla_hue($h + 1/3, $m1, $m2) * 255,
- self::hsla_hue($h, $m1, $m2) * 255,
- self::hsla_hue($h - 1/3, $m1, $m2) * 255,
- $a);
- }
-
- /**
- * @param double $h
- */
- public function hsla_hue($h, $m1, $m2){
- $h = $h < 0 ? $h + 1 : ($h > 1 ? $h - 1 : $h);
- if ($h * 6 < 1) return $m1 + ($m2 - $m1) * $h * 6;
- else if ($h * 2 < 1) return $m2;
- else if ($h * 3 < 2) return $m1 + ($m2 - $m1) * (2/3 - $h) * 6;
- else return $m1;
- }
-
- public function hsv($h, $s, $v) {
- return $this->hsva($h, $s, $v, 1.0);
- }
-
- /**
- * @param double $a
- */
- public function hsva($h, $s, $v, $a) {
- $h = ((Less_Functions::number($h) % 360) / 360 ) * 360;
- $s = Less_Functions::number($s);
- $v = Less_Functions::number($v);
- $a = Less_Functions::number($a);
-
- $i = floor(($h / 60) % 6);
- $f = ($h / 60) - $i;
-
- $vs = array( $v,
- $v * (1 - $s),
- $v * (1 - $f * $s),
- $v * (1 - (1 - $f) * $s));
-
- $perm = array(array(0, 3, 1),
- array(2, 0, 1),
- array(1, 0, 3),
- array(1, 2, 0),
- array(3, 1, 0),
- array(0, 1, 2));
-
- return $this->rgba($vs[$perm[$i][0]] * 255,
- $vs[$perm[$i][1]] * 255,
- $vs[$perm[$i][2]] * 255,
- $a);
- }
-
- public function hue($color = null){
- if (!$color instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to hue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- $c = $color->toHSL();
- return new Less_Tree_Dimension(Less_Parser::round($c['h']));
- }
-
- public function saturation($color = null){
- if (!$color instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to saturation must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- $c = $color->toHSL();
- return new Less_Tree_Dimension(Less_Parser::round($c['s'] * 100), '%');
- }
-
- public function lightness($color = null){
- if (!$color instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to lightness must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- $c = $color->toHSL();
- return new Less_Tree_Dimension(Less_Parser::round($c['l'] * 100), '%');
- }
-
- public function hsvhue( $color = null ){
- if (!$color instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to hsvhue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- $hsv = $color->toHSV();
- return new Less_Tree_Dimension( Less_Parser::round($hsv['h']) );
- }
-
-
- public function hsvsaturation( $color = null ){
- if (!$color instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to hsvsaturation must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- $hsv = $color->toHSV();
- return new Less_Tree_Dimension( Less_Parser::round($hsv['s'] * 100), '%' );
- }
-
- public function hsvvalue( $color = null ){
- if (!$color instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to hsvvalue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- $hsv = $color->toHSV();
- return new Less_Tree_Dimension( Less_Parser::round($hsv['v'] * 100), '%' );
- }
-
- public function red($color = null) {
- if (!$color instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to red must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- return new Less_Tree_Dimension( $color->rgb[0] );
- }
-
- public function green($color = null) {
- if (!$color instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to green must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- return new Less_Tree_Dimension( $color->rgb[1] );
- }
-
- public function blue($color = null) {
- if (!$color instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to blue must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- return new Less_Tree_Dimension( $color->rgb[2] );
- }
-
- public function alpha($color = null){
- if (!$color instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to alpha must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- $c = $color->toHSL();
- return new Less_Tree_Dimension($c['a']);
- }
-
- public function luma ($color = null) {
- if (!$color instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to luma must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- return new Less_Tree_Dimension(Less_Parser::round( $color->luma() * $color->alpha * 100), '%');
- }
-
- public function luminance( $color = null ){
- if (!$color instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to luminance must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- $luminance =
- (0.2126 * $color->rgb[0] / 255)
- + (0.7152 * $color->rgb[1] / 255)
- + (0.0722 * $color->rgb[2] / 255);
-
- return new Less_Tree_Dimension(Less_Parser::round( $luminance * $color->alpha * 100), '%');
- }
-
- public function saturate($color = null, $amount = null){
- // filter: saturate(3.2);
- // should be kept as is, so check for color
- if ($color instanceof Less_Tree_Dimension) {
- return null;
- }
-
- if (!$color instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to saturate must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
- if (!$amount instanceof Less_Tree_Dimension) {
- throw new Less_Exception_Compiler('The second argument to saturate must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- $hsl = $color->toHSL();
-
- $hsl['s'] += $amount->value / 100;
- $hsl['s'] = self::clamp($hsl['s']);
-
- return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
- }
-
- /**
- * @param Less_Tree_Dimension $amount
- */
- public function desaturate($color = null, $amount = null){
- if (!$color instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to desaturate must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
- if (!$amount instanceof Less_Tree_Dimension) {
- throw new Less_Exception_Compiler('The second argument to desaturate must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- $hsl = $color->toHSL();
-
- $hsl['s'] -= $amount->value / 100;
- $hsl['s'] = self::clamp($hsl['s']);
-
- return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
- }
-
-
-
- public function lighten($color = null, $amount=null){
- if (!$color instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to lighten must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
- if (!$amount instanceof Less_Tree_Dimension) {
- throw new Less_Exception_Compiler('The second argument to lighten must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- $hsl = $color->toHSL();
-
- $hsl['l'] += $amount->value / 100;
- $hsl['l'] = self::clamp($hsl['l']);
-
- return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
- }
-
- public function darken($color = null, $amount = null){
- if (!$color instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to darken must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
- if (!$amount instanceof Less_Tree_Dimension) {
- throw new Less_Exception_Compiler('The second argument to darken must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- $hsl = $color->toHSL();
- $hsl['l'] -= $amount->value / 100;
- $hsl['l'] = self::clamp($hsl['l']);
-
- return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
- }
-
- public function fadein($color = null, $amount = null){
- if (!$color instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to fadein must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
- if (!$amount instanceof Less_Tree_Dimension) {
- throw new Less_Exception_Compiler('The second argument to fadein must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- $hsl = $color->toHSL();
- $hsl['a'] += $amount->value / 100;
- $hsl['a'] = self::clamp($hsl['a']);
- return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
- }
-
- public function fadeout($color = null, $amount = null){
- if (!$color instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to fadeout must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
- if (!$amount instanceof Less_Tree_Dimension) {
- throw new Less_Exception_Compiler('The second argument to fadeout must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- $hsl = $color->toHSL();
- $hsl['a'] -= $amount->value / 100;
- $hsl['a'] = self::clamp($hsl['a']);
- return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
- }
-
- public function fade($color = null, $amount = null){
- if (!$color instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to fade must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
- if (!$amount instanceof Less_Tree_Dimension) {
- throw new Less_Exception_Compiler('The second argument to fade must be a percentage' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- $hsl = $color->toHSL();
-
- $hsl['a'] = $amount->value / 100;
- $hsl['a'] = self::clamp($hsl['a']);
- return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
- }
-
-
-
- public function spin($color = null, $amount = null){
- if (!$color instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to spin must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
- if (!$amount instanceof Less_Tree_Dimension) {
- throw new Less_Exception_Compiler('The second argument to spin must be a number' . ($amount instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- $hsl = $color->toHSL();
- $hue = fmod($hsl['h'] + $amount->value, 360);
-
- $hsl['h'] = $hue < 0 ? 360 + $hue : $hue;
-
- return $this->hsla($hsl['h'], $hsl['s'], $hsl['l'], $hsl['a']);
- }
-
- //
- // Copyright (c) 2006-2009 Hampton Catlin, Nathan Weizenbaum, and Chris Eppstein
- // http://sass-lang.com
- //
-
- /**
- * @param Less_Tree_Color $color1
- */
- public function mix($color1 = null, $color2 = null, $weight = null){
- if (!$color1 instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to mix must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
- if (!$color2 instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The second argument to mix must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
- if (!$weight) {
- $weight = new Less_Tree_Dimension('50', '%');
- }
- if (!$weight instanceof Less_Tree_Dimension) {
- throw new Less_Exception_Compiler('The third argument to contrast must be a percentage' . ($weight instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- $p = $weight->value / 100.0;
- $w = $p * 2 - 1;
- $hsl1 = $color1->toHSL();
- $hsl2 = $color2->toHSL();
- $a = $hsl1['a'] - $hsl2['a'];
-
- $w1 = (((($w * $a) == -1) ? $w : ($w + $a) / (1 + $w * $a)) + 1) / 2;
- $w2 = 1 - $w1;
-
- $rgb = array($color1->rgb[0] * $w1 + $color2->rgb[0] * $w2,
- $color1->rgb[1] * $w1 + $color2->rgb[1] * $w2,
- $color1->rgb[2] * $w1 + $color2->rgb[2] * $w2);
-
- $alpha = $color1->alpha * $p + $color2->alpha * (1 - $p);
-
- return new Less_Tree_Color($rgb, $alpha);
- }
-
- public function greyscale($color){
- return $this->desaturate($color, new Less_Tree_Dimension(100,'%'));
- }
-
-
- public function contrast( $color, $dark = null, $light = null, $threshold = null){
- // filter: contrast(3.2);
- // should be kept as is, so check for color
- if (!$color instanceof Less_Tree_Color) {
- return null;
- }
- if( !$light ){
- $light = $this->rgba(255, 255, 255, 1.0);
- }
- if( !$dark ){
- $dark = $this->rgba(0, 0, 0, 1.0);
- }
-
- if (!$dark instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The second argument to contrast must be a color' . ($dark instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
- if (!$light instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The third argument to contrast must be a color' . ($light instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- //Figure out which is actually light and dark!
- if( $dark->luma() > $light->luma() ){
- $t = $light;
- $light = $dark;
- $dark = $t;
- }
- if( !$threshold ){
- $threshold = 0.43;
- } else {
- $threshold = Less_Functions::number($threshold);
- }
-
- if( $color->luma() < $threshold ){
- return $light;
- } else {
- return $dark;
- }
- }
-
- public function e ($str){
- if( is_string($str) ){
- return new Less_Tree_Anonymous($str);
- }
- return new Less_Tree_Anonymous($str instanceof Less_Tree_JavaScript ? $str->expression : $str->value);
- }
-
- public function escape ($str){
-
- $revert = array('%21'=>'!', '%2A'=>'*', '%27'=>"'",'%3F'=>'?','%26'=>'&','%2C'=>',','%2F'=>'/','%40'=>'@','%2B'=>'+','%24'=>'$');
-
- return new Less_Tree_Anonymous(strtr(rawurlencode($str->value), $revert));
- }
-
-
- /**
- * todo: This function will need some additional work to make it work the same as less.js
- *
- */
- public function replace( $string, $pattern, $replacement, $flags = null ){
- $result = $string->value;
-
- $expr = '/'.str_replace('/','\\/',$pattern->value).'/';
- if( $flags && $flags->value){
- $expr .= self::replace_flags($flags->value);
- }
-
- $result = preg_replace($expr,$replacement->value,$result);
-
-
- if( property_exists($string,'quote') ){
- return new Less_Tree_Quoted( $string->quote, $result, $string->escaped);
- }
- return new Less_Tree_Quoted( '', $result );
- }
-
- public static function replace_flags($flags){
- $flags = str_split($flags,1);
- $new_flags = '';
-
- foreach($flags as $flag){
- switch($flag){
- case 'e':
- case 'g':
- break;
-
- default:
- $new_flags .= $flag;
- break;
- }
- }
-
- return $new_flags;
- }
-
- public function _percent(){
- $string = func_get_arg(0);
-
- $args = func_get_args();
- array_shift($args);
- $result = $string->value;
-
- foreach($args as $arg){
- if( preg_match('/%[sda]/i',$result, $token) ){
- $token = $token[0];
- $value = stristr($token, 's') ? $arg->value : $arg->toCSS();
- $value = preg_match('/[A-Z]$/', $token) ? urlencode($value) : $value;
- $result = preg_replace('/%[sda]/i',$value, $result, 1);
- }
- }
- $result = str_replace('%%', '%', $result);
-
- return new Less_Tree_Quoted( $string->quote , $result, $string->escaped);
- }
-
- public function unit( $val, $unit = null) {
- if( !($val instanceof Less_Tree_Dimension) ){
- throw new Less_Exception_Compiler('The first argument to unit must be a number' . ($val instanceof Less_Tree_Operation ? '. Have you forgotten parenthesis?' : '.') );
- }
-
- if( $unit ){
- if( $unit instanceof Less_Tree_Keyword ){
- $unit = $unit->value;
- } else {
- $unit = $unit->toCSS();
- }
- } else {
- $unit = "";
- }
- return new Less_Tree_Dimension($val->value, $unit );
- }
-
- public function convert($val, $unit){
- return $val->convertTo($unit->value);
- }
-
- public function round($n, $f = false) {
-
- $fraction = 0;
- if( $f !== false ){
- $fraction = $f->value;
- }
-
- return $this->_math('Less_Parser::round',null, $n, $fraction);
- }
-
- public function pi(){
- return new Less_Tree_Dimension(M_PI);
- }
-
- public function mod($a, $b) {
- return new Less_Tree_Dimension( $a->value % $b->value, $a->unit);
- }
-
-
-
- public function pow($x, $y) {
- if( is_numeric($x) && is_numeric($y) ){
- $x = new Less_Tree_Dimension($x);
- $y = new Less_Tree_Dimension($y);
- }elseif( !($x instanceof Less_Tree_Dimension) || !($y instanceof Less_Tree_Dimension) ){
- throw new Less_Exception_Compiler('Arguments must be numbers');
- }
-
- return new Less_Tree_Dimension( pow($x->value, $y->value), $x->unit );
- }
-
- // var mathFunctions = [{name:"ce ...
- public function ceil( $n ){ return $this->_math('ceil', null, $n); }
- public function floor( $n ){ return $this->_math('floor', null, $n); }
- public function sqrt( $n ){ return $this->_math('sqrt', null, $n); }
- public function abs( $n ){ return $this->_math('abs', null, $n); }
-
- public function tan( $n ){ return $this->_math('tan', '', $n); }
- public function sin( $n ){ return $this->_math('sin', '', $n); }
- public function cos( $n ){ return $this->_math('cos', '', $n); }
-
- public function atan( $n ){ return $this->_math('atan', 'rad', $n); }
- public function asin( $n ){ return $this->_math('asin', 'rad', $n); }
- public function acos( $n ){ return $this->_math('acos', 'rad', $n); }
-
- private function _math() {
- $args = func_get_args();
- $fn = array_shift($args);
- $unit = array_shift($args);
-
- if ($args[0] instanceof Less_Tree_Dimension) {
-
- if( $unit === null ){
- $unit = $args[0]->unit;
- }else{
- $args[0] = $args[0]->unify();
- }
- $args[0] = (float)$args[0]->value;
- return new Less_Tree_Dimension( call_user_func_array($fn, $args), $unit);
- } else if (is_numeric($args[0])) {
- return call_user_func_array($fn,$args);
- } else {
- throw new Less_Exception_Compiler("math functions take numbers as parameters");
- }
- }
-
- /**
- * @param boolean $isMin
- */
- private function _minmax( $isMin, $args ){
-
- $arg_count = count($args);
-
- if( $arg_count < 1 ){
- throw new Less_Exception_Compiler( 'one or more arguments required');
- }
-
- $j = null;
- $unitClone = null;
- $unitStatic = null;
-
-
- $order = array(); // elems only contains original argument values.
- $values = array(); // key is the unit.toString() for unified tree.Dimension values,
- // value is the index into the order array.
-
-
- for( $i = 0; $i < $arg_count; $i++ ){
- $current = $args[$i];
- if( !($current instanceof Less_Tree_Dimension) ){
- if( is_array($args[$i]->value) ){
- $args[] = $args[$i]->value;
- }
- continue;
- }
-
- if( $current->unit->toString() === '' && !$unitClone ){
- $temp = new Less_Tree_Dimension($current->value, $unitClone);
- $currentUnified = $temp->unify();
- }else{
- $currentUnified = $current->unify();
- }
-
- if( $currentUnified->unit->toString() === "" && !$unitStatic ){
- $unit = $unitStatic;
- }else{
- $unit = $currentUnified->unit->toString();
- }
-
- if( $unit !== '' && !$unitStatic || $unit !== '' && $order[0]->unify()->unit->toString() === "" ){
- $unitStatic = $unit;
- }
-
- if( $unit != '' && !$unitClone ){
- $unitClone = $current->unit->toString();
- }
-
- if( isset($values['']) && $unit !== '' && $unit === $unitStatic ){
- $j = $values[''];
- }elseif( isset($values[$unit]) ){
- $j = $values[$unit];
- }else{
-
- if( $unitStatic && $unit !== $unitStatic ){
- throw new Less_Exception_Compiler( 'incompatible types');
- }
- $values[$unit] = count($order);
- $order[] = $current;
- continue;
- }
-
-
- if( $order[$j]->unit->toString() === "" && $unitClone ){
- $temp = new Less_Tree_Dimension( $order[$j]->value, $unitClone);
- $referenceUnified = $temp->unify();
- }else{
- $referenceUnified = $order[$j]->unify();
- }
- if( ($isMin && $currentUnified->value < $referenceUnified->value) || (!$isMin && $currentUnified->value > $referenceUnified->value) ){
- $order[$j] = $current;
- }
- }
-
- if( count($order) == 1 ){
- return $order[0];
- }
- $args = array();
- foreach($order as $a){
- $args[] = $a->toCSS($this->env);
- }
- return new Less_Tree_Anonymous( ($isMin?'min(':'max(') . implode(Less_Environment::$_outputMap[','],$args).')');
- }
-
- public function min(){
- $args = func_get_args();
- return $this->_minmax( true, $args );
- }
-
- public function max(){
- $args = func_get_args();
- return $this->_minmax( false, $args );
- }
-
- public function getunit($n){
- return new Less_Tree_Anonymous($n->unit);
- }
-
- public function argb($color) {
- if (!$color instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to argb must be a color' . ($color instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- return new Less_Tree_Anonymous($color->toARGB());
- }
-
- public function percentage($n) {
- return new Less_Tree_Dimension($n->value * 100, '%');
- }
-
- public function color($n) {
-
- if( $n instanceof Less_Tree_Quoted ){
- $colorCandidate = $n->value;
- $returnColor = Less_Tree_Color::fromKeyword($colorCandidate);
- if( $returnColor ){
- return $returnColor;
- }
- if( preg_match('/^#([A-Fa-f0-9]{6}|[A-Fa-f0-9]{3})/',$colorCandidate) ){
- return new Less_Tree_Color(substr($colorCandidate, 1));
- }
- throw new Less_Exception_Compiler("argument must be a color keyword or 3/6 digit hex e.g. #FFF");
- } else {
- throw new Less_Exception_Compiler("argument must be a string");
- }
- }
-
-
- public function iscolor($n) {
- return $this->_isa($n, 'Less_Tree_Color');
- }
-
- public function isnumber($n) {
- return $this->_isa($n, 'Less_Tree_Dimension');
- }
-
- public function isstring($n) {
- return $this->_isa($n, 'Less_Tree_Quoted');
- }
-
- public function iskeyword($n) {
- return $this->_isa($n, 'Less_Tree_Keyword');
- }
-
- public function isurl($n) {
- return $this->_isa($n, 'Less_Tree_Url');
- }
-
- public function ispixel($n) {
- return $this->isunit($n, 'px');
- }
-
- public function ispercentage($n) {
- return $this->isunit($n, '%');
- }
-
- public function isem($n) {
- return $this->isunit($n, 'em');
- }
-
- /**
- * @param string $unit
- */
- public function isunit( $n, $unit ){
- return ($n instanceof Less_Tree_Dimension) && $n->unit->is( ( property_exists($unit,'value') ? $unit->value : $unit) ) ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false');
- }
-
- /**
- * @param string $type
- */
- private function _isa($n, $type) {
- return is_a($n, $type) ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false');
- }
-
- public function tint($color, $amount) {
- return $this->mix( $this->rgb(255,255,255), $color, $amount);
- }
-
- public function shade($color, $amount) {
- return $this->mix($this->rgb(0, 0, 0), $color, $amount);
- }
-
- public function extract($values, $index ){
- $index = (int)$index->value - 1; // (1-based index)
- // handle non-array values as an array of length 1
- // return 'undefined' if index is invalid
- if( property_exists($values,'value') && is_array($values->value) ){
- if( isset($values->value[$index]) ){
- return $values->value[$index];
- }
- return null;
-
- }elseif( (int)$index === 0 ){
- return $values;
- }
-
- return null;
- }
-
- public function length($values){
- $n = (property_exists($values,'value') && is_array($values->value)) ? count($values->value) : 1;
- return new Less_Tree_Dimension($n);
- }
-
- public function datauri($mimetypeNode, $filePathNode = null ) {
-
- $filePath = ( $filePathNode ? $filePathNode->value : null );
- $mimetype = $mimetypeNode->value;
-
- $args = 2;
- if( !$filePath ){
- $filePath = $mimetype;
- $args = 1;
- }
-
- $filePath = str_replace('\\','/',$filePath);
- if( Less_Environment::isPathRelative($filePath) ){
-
- if( Less_Parser::$options['relativeUrls'] ){
- $temp = $this->currentFileInfo['currentDirectory'];
- } else {
- $temp = $this->currentFileInfo['entryPath'];
- }
-
- if( !empty($temp) ){
- $filePath = Less_Environment::normalizePath(rtrim($temp,'/').'/'.$filePath);
- }
-
- }
-
-
- // detect the mimetype if not given
- if( $args < 2 ){
-
- /* incomplete
- $mime = require('mime');
- mimetype = mime.lookup(path);
-
- // use base 64 unless it's an ASCII or UTF-8 format
- var charset = mime.charsets.lookup(mimetype);
- useBase64 = ['US-ASCII', 'UTF-8'].indexOf(charset) < 0;
- if (useBase64) mimetype += ';base64';
- */
-
- $mimetype = Less_Mime::lookup($filePath);
-
- $charset = Less_Mime::charsets_lookup($mimetype);
- $useBase64 = !in_array($charset,array('US-ASCII', 'UTF-8'));
- if( $useBase64 ){ $mimetype .= ';base64'; }
-
- }else{
- $useBase64 = preg_match('/;base64$/',$mimetype);
- }
-
-
- if( file_exists($filePath) ){
- $buf = @file_get_contents($filePath);
- }else{
- $buf = false;
- }
-
-
- // IE8 cannot handle a data-uri larger than 32KB. If this is exceeded
- // and the --ieCompat flag is enabled, return a normal url() instead.
- $DATA_URI_MAX_KB = 32;
- $fileSizeInKB = round( strlen($buf) / 1024 );
- if( $fileSizeInKB >= $DATA_URI_MAX_KB ){
- $url = new Less_Tree_Url( ($filePathNode ? $filePathNode : $mimetypeNode), $this->currentFileInfo);
- return $url->compile($this);
- }
-
- if( $buf ){
- $buf = $useBase64 ? base64_encode($buf) : rawurlencode($buf);
- $filePath = '"data:' . $mimetype . ',' . $buf . '"';
- }
-
- return new Less_Tree_Url( new Less_Tree_Anonymous($filePath) );
- }
-
- //svg-gradient
- public function svggradient( $direction ){
-
- $throw_message = 'svg-gradient expects direction, start_color [start_position], [color position,]..., end_color [end_position]';
- $arguments = func_get_args();
-
- if( count($arguments) < 3 ){
- throw new Less_Exception_Compiler( $throw_message );
- }
-
- $stops = array_slice($arguments,1);
- $gradientType = 'linear';
- $rectangleDimension = 'x="0" y="0" width="1" height="1"';
- $useBase64 = true;
- $directionValue = $direction->toCSS();
-
-
- switch( $directionValue ){
- case "to bottom":
- $gradientDirectionSvg = 'x1="0%" y1="0%" x2="0%" y2="100%"';
- break;
- case "to right":
- $gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="0%"';
- break;
- case "to bottom right":
- $gradientDirectionSvg = 'x1="0%" y1="0%" x2="100%" y2="100%"';
- break;
- case "to top right":
- $gradientDirectionSvg = 'x1="0%" y1="100%" x2="100%" y2="0%"';
- break;
- case "ellipse":
- case "ellipse at center":
- $gradientType = "radial";
- $gradientDirectionSvg = 'cx="50%" cy="50%" r="75%"';
- $rectangleDimension = 'x="-50" y="-50" width="101" height="101"';
- break;
- default:
- throw new Less_Exception_Compiler( "svg-gradient direction must be 'to bottom', 'to right', 'to bottom right', 'to top right' or 'ellipse at center'" );
- }
-
- $returner = '<?xml version="1.0" ?>' .
- '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="100%" height="100%" viewBox="0 0 1 1" preserveAspectRatio="none">' .
- '<' . $gradientType . 'Gradient id="gradient" gradientUnits="userSpaceOnUse" ' . $gradientDirectionSvg . '>';
-
- for( $i = 0; $i < count($stops); $i++ ){
- if( is_object($stops[$i]) && property_exists($stops[$i],'value') ){
- $color = $stops[$i]->value[0];
- $position = $stops[$i]->value[1];
- }else{
- $color = $stops[$i];
- $position = null;
- }
-
- if( !($color instanceof Less_Tree_Color) || (!(($i === 0 || $i+1 === count($stops)) && $position === null) && !($position instanceof Less_Tree_Dimension)) ){
- throw new Less_Exception_Compiler( $throw_message );
- }
- if( $position ){
- $positionValue = $position->toCSS();
- }elseif( $i === 0 ){
- $positionValue = '0%';
- }else{
- $positionValue = '100%';
- }
- $alpha = $color->alpha;
- $returner .= '<stop offset="' . $positionValue . '" stop-color="' . $color->toRGB() . '"' . ($alpha < 1 ? ' stop-opacity="' . $alpha . '"' : '') . '/>';
- }
-
- $returner .= '</' . $gradientType . 'Gradient><rect ' . $rectangleDimension . ' fill="url(#gradient)" /></svg>';
-
-
- if( $useBase64 ){
- $returner = "'data:image/svg+xml;base64,".base64_encode($returner)."'";
- }else{
- $returner = "'data:image/svg+xml,".$returner."'";
- }
-
- return new Less_Tree_URL( new Less_Tree_Anonymous( $returner ) );
- }
-
-
- /**
- * Php version of javascript's `encodeURIComponent` function
- *
- * @param string $string The string to encode
- * @return string The encoded string
- */
- public static function encodeURIComponent($string){
- $revert = array('%21' => '!', '%2A' => '*', '%27' => "'", '%28' => '(', '%29' => ')');
- return strtr(rawurlencode($string), $revert);
- }
-
-
- // Color Blending
- // ref: http://www.w3.org/TR/compositing-1
-
- public function colorBlend( $mode, $color1, $color2 ){
- $ab = $color1->alpha; // backdrop
- $as = $color2->alpha; // source
- $r = array(); // result
-
- $ar = $as + $ab * (1 - $as);
- for( $i = 0; $i < 3; $i++ ){
- $cb = $color1->rgb[$i] / 255;
- $cs = $color2->rgb[$i] / 255;
- $cr = call_user_func( $mode, $cb, $cs );
- if( $ar ){
- $cr = ($as * $cs + $ab * ($cb - $as * ($cb + $cs - $cr))) / $ar;
- }
- $r[$i] = $cr * 255;
- }
-
- return new Less_Tree_Color($r, $ar);
- }
-
- public function multiply($color1 = null, $color2 = null ){
- if (!$color1 instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to multiply must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
- if (!$color2 instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The second argument to multiply must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- return $this->colorBlend( array($this,'colorBlendMultiply'), $color1, $color2 );
- }
-
- private function colorBlendMultiply($cb, $cs){
- return $cb * $cs;
- }
-
- public function screen($color1 = null, $color2 = null ){
- if (!$color1 instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to screen must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
- if (!$color2 instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The second argument to screen must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- return $this->colorBlend( array($this,'colorBlendScreen'), $color1, $color2 );
- }
-
- private function colorBlendScreen( $cb, $cs){
- return $cb + $cs - $cb * $cs;
- }
-
- public function overlay($color1 = null, $color2 = null){
- if (!$color1 instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to overlay must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
- if (!$color2 instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The second argument to overlay must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- return $this->colorBlend( array($this,'colorBlendOverlay'), $color1, $color2 );
- }
-
- private function colorBlendOverlay($cb, $cs ){
- $cb *= 2;
- return ($cb <= 1)
- ? $this->colorBlendMultiply($cb, $cs)
- : $this->colorBlendScreen($cb - 1, $cs);
- }
-
- public function softlight($color1 = null, $color2 = null){
- if (!$color1 instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to softlight must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
- if (!$color2 instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The second argument to softlight must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- return $this->colorBlend( array($this,'colorBlendSoftlight'), $color1, $color2 );
- }
-
- private function colorBlendSoftlight($cb, $cs ){
- $d = 1;
- $e = $cb;
- if( $cs > 0.5 ){
- $e = 1;
- $d = ($cb > 0.25) ? sqrt($cb)
- : ((16 * $cb - 12) * $cb + 4) * $cb;
- }
- return $cb - (1 - 2 * $cs) * $e * ($d - $cb);
- }
-
- public function hardlight($color1 = null, $color2 = null){
- if (!$color1 instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to hardlight must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
- if (!$color2 instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The second argument to hardlight must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- return $this->colorBlend( array($this,'colorBlendHardlight'), $color1, $color2 );
- }
-
- private function colorBlendHardlight( $cb, $cs ){
- return $this->colorBlendOverlay($cs, $cb);
- }
-
- public function difference($color1 = null, $color2 = null) {
- if (!$color1 instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to difference must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
- if (!$color2 instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The second argument to difference must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- return $this->colorBlend( array($this,'colorBlendDifference'), $color1, $color2 );
- }
-
- private function colorBlendDifference( $cb, $cs ){
- return abs($cb - $cs);
- }
-
- public function exclusion( $color1 = null, $color2 = null ){
- if (!$color1 instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to exclusion must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
- if (!$color2 instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The second argument to exclusion must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- return $this->colorBlend( array($this,'colorBlendExclusion'), $color1, $color2 );
- }
-
- private function colorBlendExclusion( $cb, $cs ){
- return $cb + $cs - 2 * $cb * $cs;
- }
-
- public function average($color1 = null, $color2 = null){
- if (!$color1 instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to average must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
- if (!$color2 instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The second argument to average must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- return $this->colorBlend( array($this,'colorBlendAverage'), $color1, $color2 );
- }
-
- // non-w3c functions:
- public function colorBlendAverage($cb, $cs ){
- return ($cb + $cs) / 2;
- }
-
- public function negation($color1 = null, $color2 = null ){
- if (!$color1 instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The first argument to negation must be a color' . ($color1 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
- if (!$color2 instanceof Less_Tree_Color) {
- throw new Less_Exception_Compiler('The second argument to negation must be a color' . ($color2 instanceof Less_Tree_Expression ? ' (did you forgot commas?)' : '') );
- }
-
- return $this->colorBlend( array($this,'colorBlendNegation'), $color1, $color2 );
- }
-
- public function colorBlendNegation($cb, $cs){
- return 1 - abs($cb + $cs - 1);
- }
-
- // ~ End of Color Blending
-
-}
-
-
-/**
- * Mime lookup
- *
- * @package Less
- * @subpackage node
- */
-class Less_Mime{
-
- // this map is intentionally incomplete
- // if you want more, install 'mime' dep
- static $_types = array(
- '.htm' => 'text/html',
- '.html'=> 'text/html',
- '.gif' => 'image/gif',
- '.jpg' => 'image/jpeg',
- '.jpeg'=> 'image/jpeg',
- '.png' => 'image/png',
- '.ttf' => 'application/x-font-ttf',
- '.otf' => 'application/x-font-otf',
- '.eot' => 'application/vnd.ms-fontobject',
- '.woff' => 'application/x-font-woff',
- '.svg' => 'image/svg+xml',
- );
-
- public static function lookup( $filepath ){
- $parts = explode('.',$filepath);
- $ext = '.'.strtolower(array_pop($parts));
-
- if( !isset(self::$_types[$ext]) ){
- return null;
- }
- return self::$_types[$ext];
- }
-
- public static function charsets_lookup( $type = null ){
- // assumes all text types are UTF-8
- return $type && preg_match('/^text\//',$type) ? 'UTF-8' : '';
- }
-}
-
-
-/**
- * Tree
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree{
-
- public $cache_string;
-
- public function toCSS(){
- $output = new Less_Output();
- $this->genCSS($output);
- return $output->toString();
- }
-
-
- /**
- * Generate CSS by adding it to the output object
- *
- * @param Less_Output $output The output
- * @return void
- */
- public function genCSS($output){}
-
-
- /**
- * @param Less_Tree_Ruleset[] $rules
- */
- public static function outputRuleset( $output, $rules ){
-
- $ruleCnt = count($rules);
- Less_Environment::$tabLevel++;
-
-
- // Compressed
- if( Less_Parser::$options['compress'] ){
- $output->add('{');
- for( $i = 0; $i < $ruleCnt; $i++ ){
- $rules[$i]->genCSS( $output );
- }
-
- $output->add( '}' );
- Less_Environment::$tabLevel--;
- return;
- }
-
-
- // Non-compressed
- $tabSetStr = "\n".str_repeat( Less_Parser::$options['indentation'] , Less_Environment::$tabLevel-1 );
- $tabRuleStr = $tabSetStr.Less_Parser::$options['indentation'];
-
- $output->add( " {" );
- for($i = 0; $i < $ruleCnt; $i++ ){
- $output->add( $tabRuleStr );
- $rules[$i]->genCSS( $output );
- }
- Less_Environment::$tabLevel--;
- $output->add( $tabSetStr.'}' );
-
- }
-
- public function accept($visitor){}
-
-
- public static function ReferencedArray($rules){
- foreach($rules as $rule){
- if( method_exists($rule, 'markReferenced') ){
- $rule->markReferenced();
- }
- }
- }
-
-
- /**
- * Requires php 5.3+
- */
- public static function __set_state($args){
-
- $class = get_called_class();
- $obj = new $class(null,null,null,null);
- foreach($args as $key => $val){
- $obj->$key = $val;
- }
- return $obj;
- }
-
-}
-
-
-/**
- * Parser output
- *
- * @package Less
- * @subpackage output
- */
-class Less_Output{
-
- /**
- * Output holder
- *
- * @var string
- */
- protected $strs = array();
-
- /**
- * Adds a chunk to the stack
- *
- * @param string $chunk The chunk to output
- * @param Less_FileInfo $fileInfo The file information
- * @param integer $index The index
- * @param mixed $mapLines
- */
- public function add($chunk, $fileInfo = null, $index = 0, $mapLines = null){
- $this->strs[] = $chunk;
- }
-
- /**
- * Is the output empty?
- *
- * @return boolean
- */
- public function isEmpty(){
- return count($this->strs) === 0;
- }
-
-
- /**
- * Converts the output to string
- *
- * @return string
- */
- public function toString(){
- return implode('',$this->strs);
- }
-
-}
-
-/**
- * Visitor
- *
- * @package Less
- * @subpackage visitor
- */
-class Less_Visitor{
-
- protected $methods = array();
- protected $_visitFnCache = array();
-
- public function __construct(){
- $this->_visitFnCache = get_class_methods(get_class($this));
- $this->_visitFnCache = array_flip($this->_visitFnCache);
- }
-
- public function visitObj( $node ){
-
- $funcName = 'visit'.$node->type;
- if( isset($this->_visitFnCache[$funcName]) ){
-
- $visitDeeper = true;
- $this->$funcName( $node, $visitDeeper );
-
- if( $visitDeeper ){
- $node->accept($this);
- }
-
- $funcName = $funcName . "Out";
- if( isset($this->_visitFnCache[$funcName]) ){
- $this->$funcName( $node );
- }
-
- }else{
- $node->accept($this);
- }
-
- return $node;
- }
-
- public function visitArray( $nodes ){
-
- array_map( array($this,'visitObj'), $nodes);
- return $nodes;
- }
-}
-
-
-
-/**
- * Replacing Visitor
- *
- * @package Less
- * @subpackage visitor
- */
-class Less_VisitorReplacing extends Less_Visitor{
-
- public function visitObj( $node ){
-
- $funcName = 'visit'.$node->type;
- if( isset($this->_visitFnCache[$funcName]) ){
-
- $visitDeeper = true;
- $node = $this->$funcName( $node, $visitDeeper );
-
- if( $node ){
- if( $visitDeeper && is_object($node) ){
- $node->accept($this);
- }
-
- $funcName = $funcName . "Out";
- if( isset($this->_visitFnCache[$funcName]) ){
- $this->$funcName( $node );
- }
- }
-
- }else{
- $node->accept($this);
- }
-
- return $node;
- }
-
- public function visitArray( $nodes ){
-
- $newNodes = array();
- foreach($nodes as $node){
- $evald = $this->visitObj($node);
- if( $evald ){
- if( is_array($evald) ){
- self::flatten($evald,$newNodes);
- }else{
- $newNodes[] = $evald;
- }
- }
- }
- return $newNodes;
- }
-
- public function flatten( $arr, &$out ){
-
- foreach($arr as $item){
- if( !is_array($item) ){
- $out[] = $item;
- continue;
- }
-
- foreach($item as $nestedItem){
- if( is_array($nestedItem) ){
- self::flatten( $nestedItem, $out);
- }else{
- $out[] = $nestedItem;
- }
- }
- }
-
- return $out;
- }
-
-}
-
-
-
-
-/**
- * Configurable
- *
- * @package Less
- * @subpackage Core
- */
-abstract class Less_Configurable {
-
- /**
- * Array of options
- *
- * @var array
- */
- protected $options = array();
-
- /**
- * Array of default options
- *
- * @var array
- */
- protected $defaultOptions = array();
-
-
- /**
- * Set options
- *
- * If $options is an object it will be converted into an array by called
- * it's toArray method.
- *
- * @throws Exception
- * @param array|object $options
- *
- */
- public function setOptions($options){
- $options = array_intersect_key($options,$this->defaultOptions);
- $this->options = array_merge($this->defaultOptions, $this->options, $options);
- }
-
-
- /**
- * Get an option value by name
- *
- * If the option is empty or not set a NULL value will be returned.
- *
- * @param string $name
- * @param mixed $default Default value if confiuration of $name is not present
- * @return mixed
- */
- public function getOption($name, $default = null){
- if(isset($this->options[$name])){
- return $this->options[$name];
- }
- return $default;
- }
-
-
- /**
- * Set an option
- *
- * @param string $name
- * @param mixed $value
- */
- public function setOption($name, $value){
- $this->options[$name] = $value;
- }
-
-}
-
-/**
- * Alpha
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Alpha extends Less_Tree{
- public $value;
- public $type = 'Alpha';
-
- public function __construct($val){
- $this->value = $val;
- }
-
- //function accept( $visitor ){
- // $this->value = $visitor->visit( $this->value );
- //}
-
- public function compile($env){
-
- if( is_object($this->value) ){
- $this->value = $this->value->compile($env);
- }
-
- return $this;
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output ){
-
- $output->add( "alpha(opacity=" );
-
- if( is_string($this->value) ){
- $output->add( $this->value );
- }else{
- $this->value->genCSS( $output);
- }
-
- $output->add( ')' );
- }
-
- public function toCSS(){
- return "alpha(opacity=" . (is_string($this->value) ? $this->value : $this->value->toCSS()) . ")";
- }
-
-
-}
-
-/**
- * Anonymous
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Anonymous extends Less_Tree{
- public $value;
- public $quote;
- public $index;
- public $mapLines;
- public $currentFileInfo;
- public $type = 'Anonymous';
-
- /**
- * @param integer $index
- * @param boolean $mapLines
- */
- public function __construct($value, $index = null, $currentFileInfo = null, $mapLines = null ){
- $this->value = $value;
- $this->index = $index;
- $this->mapLines = $mapLines;
- $this->currentFileInfo = $currentFileInfo;
- }
-
- public function compile(){
- return new Less_Tree_Anonymous($this->value, $this->index, $this->currentFileInfo, $this->mapLines);
- }
-
- public function compare($x){
- if( !is_object($x) ){
- return -1;
- }
-
- $left = $this->toCSS();
- $right = $x->toCSS();
-
- if( $left === $right ){
- return 0;
- }
-
- return $left < $right ? -1 : 1;
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output ){
- $output->add( $this->value, $this->currentFileInfo, $this->index, $this->mapLines );
- }
-
- public function toCSS(){
- return $this->value;
- }
-
-}
-
-
-/**
- * Assignment
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Assignment extends Less_Tree{
-
- public $key;
- public $value;
- public $type = 'Assignment';
-
- public function __construct($key, $val) {
- $this->key = $key;
- $this->value = $val;
- }
-
- public function accept( $visitor ){
- $this->value = $visitor->visitObj( $this->value );
- }
-
- public function compile($env) {
- return new Less_Tree_Assignment( $this->key, $this->value->compile($env));
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output ){
- $output->add( $this->key . '=' );
- $this->value->genCSS( $output );
- }
-
- public function toCss(){
- return $this->key . '=' . $this->value->toCSS();
- }
-}
-
-
-/**
- * Attribute
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Attribute extends Less_Tree{
-
- public $key;
- public $op;
- public $value;
- public $type = 'Attribute';
-
- public function __construct($key, $op, $value){
- $this->key = $key;
- $this->op = $op;
- $this->value = $value;
- }
-
- public function compile($env){
-
- $key_obj = is_object($this->key);
- $val_obj = is_object($this->value);
-
- if( !$key_obj && !$val_obj ){
- return $this;
- }
-
- return new Less_Tree_Attribute(
- $key_obj ? $this->key->compile($env) : $this->key ,
- $this->op,
- $val_obj ? $this->value->compile($env) : $this->value);
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output ){
- $output->add( $this->toCSS() );
- }
-
- public function toCSS(){
- $value = $this->key;
-
- if( $this->op ){
- $value .= $this->op;
- $value .= (is_object($this->value) ? $this->value->toCSS() : $this->value);
- }
-
- return '[' . $value . ']';
- }
-}
-
-
-/**
- * Call
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Call extends Less_Tree{
- public $value;
-
- protected $name;
- protected $args;
- protected $index;
- protected $currentFileInfo;
- public $type = 'Call';
-
- public function __construct($name, $args, $index, $currentFileInfo = null ){
- $this->name = $name;
- $this->args = $args;
- $this->index = $index;
- $this->currentFileInfo = $currentFileInfo;
- }
-
- public function accept( $visitor ){
- $this->args = $visitor->visitArray( $this->args );
- }
-
- //
- // When evaluating a function call,
- // we either find the function in `tree.functions` [1],
- // in which case we call it, passing the evaluated arguments,
- // or we simply print it out as it appeared originally [2].
- //
- // The *functions.js* file contains the built-in functions.
- //
- // The reason why we evaluate the arguments, is in the case where
- // we try to pass a variable to a function, like: `saturate(@color)`.
- // The function should receive the value, not the variable.
- //
- public function compile($env=null){
- $args = array();
- foreach($this->args as $a){
- $args[] = $a->compile($env);
- }
-
- $nameLC = strtolower($this->name);
- switch($nameLC){
- case '%':
- $nameLC = '_percent';
- break;
-
- case 'get-unit':
- $nameLC = 'getunit';
- break;
-
- case 'data-uri':
- $nameLC = 'datauri';
- break;
-
- case 'svg-gradient':
- $nameLC = 'svggradient';
- break;
- }
-
- $result = null;
- if( $nameLC === 'default' ){
- $result = Less_Tree_DefaultFunc::compile();
-
- }else{
-
- if( method_exists('Less_Functions',$nameLC) ){ // 1.
- try {
-
- $func = new Less_Functions($env, $this->currentFileInfo);
- $result = call_user_func_array( array($func,$nameLC),$args);
-
- } catch (Exception $e) {
- throw new Less_Exception_Compiler('error evaluating function `' . $this->name . '` '.$e->getMessage().' index: '. $this->index);
- }
- } elseif( isset( $env->functions[$nameLC] ) && is_callable( $env->functions[$nameLC] ) ) {
- try {
- $result = call_user_func_array( $env->functions[$nameLC], $args );
- } catch (Exception $e) {
- throw new Less_Exception_Compiler('error evaluating function `' . $this->name . '` '.$e->getMessage().' index: '. $this->index);
- }
- }
- }
-
- if( $result !== null ){
- return $result;
- }
-
-
- return new Less_Tree_Call( $this->name, $args, $this->index, $this->currentFileInfo );
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output ){
-
- $output->add( $this->name . '(', $this->currentFileInfo, $this->index );
- $args_len = count($this->args);
- for($i = 0; $i < $args_len; $i++ ){
- $this->args[$i]->genCSS( $output );
- if( $i + 1 < $args_len ){
- $output->add( ', ' );
- }
- }
-
- $output->add( ')' );
- }
-
-
- //public function toCSS(){
- // return $this->compile()->toCSS();
- //}
-
-}
-
-
-/**
- * Color
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Color extends Less_Tree{
- public $rgb;
- public $alpha;
- public $isTransparentKeyword;
- public $type = 'Color';
-
- public function __construct($rgb, $a = 1, $isTransparentKeyword = null ){
-
- if( $isTransparentKeyword ){
- $this->rgb = $rgb;
- $this->alpha = $a;
- $this->isTransparentKeyword = true;
- return;
- }
-
- $this->rgb = array();
- if( is_array($rgb) ){
- $this->rgb = $rgb;
- }else if( strlen($rgb) == 6 ){
- foreach(str_split($rgb, 2) as $c){
- $this->rgb[] = hexdec($c);
- }
- }else{
- foreach(str_split($rgb, 1) as $c){
- $this->rgb[] = hexdec($c.$c);
- }
- }
- $this->alpha = is_numeric($a) ? $a : 1;
- }
-
- public function compile(){
- return $this;
- }
-
- public function luma(){
- $r = $this->rgb[0] / 255;
- $g = $this->rgb[1] / 255;
- $b = $this->rgb[2] / 255;
-
- $r = ($r <= 0.03928) ? $r / 12.92 : pow((($r + 0.055) / 1.055), 2.4);
- $g = ($g <= 0.03928) ? $g / 12.92 : pow((($g + 0.055) / 1.055), 2.4);
- $b = ($b <= 0.03928) ? $b / 12.92 : pow((($b + 0.055) / 1.055), 2.4);
-
- return 0.2126 * $r + 0.7152 * $g + 0.0722 * $b;
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output ){
- $output->add( $this->toCSS() );
- }
-
- public function toCSS( $doNotCompress = false ){
- $compress = Less_Parser::$options['compress'] && !$doNotCompress;
- $alpha = Less_Functions::fround( $this->alpha );
-
-
- //
- // If we have some transparency, the only way to represent it
- // is via `rgba`. Otherwise, we use the hex representation,
- // which has better compatibility with older browsers.
- // Values are capped between `0` and `255`, rounded and zero-padded.
- //
- if( $alpha < 1 ){
- if( ( $alpha === 0 || $alpha === 0.0 ) && isset($this->isTransparentKeyword) && $this->isTransparentKeyword ){
- return 'transparent';
- }
-
- $values = array();
- foreach($this->rgb as $c){
- $values[] = Less_Functions::clamp( round($c), 255);
- }
- $values[] = $alpha;
-
- $glue = ($compress ? ',' : ', ');
- return "rgba(" . implode($glue, $values) . ")";
- }else{
-
- $color = $this->toRGB();
-
- if( $compress ){
-
- // Convert color to short format
- if( $color[1] === $color[2] && $color[3] === $color[4] && $color[5] === $color[6]) {
- $color = '#'.$color[1] . $color[3] . $color[5];
- }
- }
-
- return $color;
- }
- }
-
- //
- // Operations have to be done per-channel, if not,
- // channels will spill onto each other. Once we have
- // our result, in the form of an integer triplet,
- // we create a new Color node to hold the result.
- //
-
- /**
- * @param string $op
- */
- public function operate( $op, $other) {
- $rgb = array();
- $alpha = $this->alpha * (1 - $other->alpha) + $other->alpha;
- for ($c = 0; $c < 3; $c++) {
- $rgb[$c] = Less_Functions::operate( $op, $this->rgb[$c], $other->rgb[$c]);
- }
- return new Less_Tree_Color($rgb, $alpha);
- }
-
- public function toRGB(){
- return $this->toHex($this->rgb);
- }
-
- public function toHSL(){
- $r = $this->rgb[0] / 255;
- $g = $this->rgb[1] / 255;
- $b = $this->rgb[2] / 255;
- $a = $this->alpha;
-
- $max = max($r, $g, $b);
- $min = min($r, $g, $b);
- $l = ($max + $min) / 2;
- $d = $max - $min;
-
- $h = $s = 0;
- if( $max !== $min ){
- $s = $l > 0.5 ? $d / (2 - $max - $min) : $d / ($max + $min);
-
- switch ($max) {
- case $r: $h = ($g - $b) / $d + ($g < $b ? 6 : 0); break;
- case $g: $h = ($b - $r) / $d + 2; break;
- case $b: $h = ($r - $g) / $d + 4; break;
- }
- $h /= 6;
- }
- return array('h' => $h * 360, 's' => $s, 'l' => $l, 'a' => $a );
- }
-
- //Adapted from http://mjijackson.com/2008/02/rgb-to-hsl-and-rgb-to-hsv-color-model-conversion-algorithms-in-javascript
- public function toHSV() {
- $r = $this->rgb[0] / 255;
- $g = $this->rgb[1] / 255;
- $b = $this->rgb[2] / 255;
- $a = $this->alpha;
-
- $max = max($r, $g, $b);
- $min = min($r, $g, $b);
-
- $v = $max;
-
- $d = $max - $min;
- if ($max === 0) {
- $s = 0;
- } else {
- $s = $d / $max;
- }
-
- $h = 0;
- if( $max !== $min ){
- switch($max){
- case $r: $h = ($g - $b) / $d + ($g < $b ? 6 : 0); break;
- case $g: $h = ($b - $r) / $d + 2; break;
- case $b: $h = ($r - $g) / $d + 4; break;
- }
- $h /= 6;
- }
- return array('h'=> $h * 360, 's'=> $s, 'v'=> $v, 'a' => $a );
- }
-
- public function toARGB(){
- $argb = array_merge( (array) Less_Parser::round($this->alpha * 255), $this->rgb);
- return $this->toHex( $argb );
- }
-
- public function compare($x){
-
- if( !property_exists( $x, 'rgb' ) ){
- return -1;
- }
-
-
- return ($x->rgb[0] === $this->rgb[0] &&
- $x->rgb[1] === $this->rgb[1] &&
- $x->rgb[2] === $this->rgb[2] &&
- $x->alpha === $this->alpha) ? 0 : -1;
- }
-
- public function toHex( $v ){
-
- $ret = '#';
- foreach($v as $c){
- $c = Less_Functions::clamp( Less_Parser::round($c), 255);
- if( $c < 16 ){
- $ret .= '0';
- }
- $ret .= dechex($c);
- }
-
- return $ret;
- }
-
-
- /**
- * @param string $keyword
- */
- public static function fromKeyword( $keyword ){
- $keyword = strtolower($keyword);
-
- if( Less_Colors::hasOwnProperty($keyword) ){
- // detect named color
- return new Less_Tree_Color(substr(Less_Colors::color($keyword), 1));
- }
-
- if( $keyword === 'transparent' ){
- return new Less_Tree_Color( array(0, 0, 0), 0, true);
- }
- }
-
-}
-
-
-/**
- * Comment
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Comment extends Less_Tree{
-
- public $value;
- public $silent;
- public $isReferenced;
- public $currentFileInfo;
- public $type = 'Comment';
-
- public function __construct($value, $silent, $index = null, $currentFileInfo = null ){
- $this->value = $value;
- $this->silent = !! $silent;
- $this->currentFileInfo = $currentFileInfo;
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output ){
- //if( $this->debugInfo ){
- //$output->add( tree.debugInfo($env, $this), $this->currentFileInfo, $this->index);
- //}
- $output->add( trim($this->value) );//TODO shouldn't need to trim, we shouldn't grab the \n
- }
-
- public function toCSS(){
- return Less_Parser::$options['compress'] ? '' : $this->value;
- }
-
- public function isSilent(){
- $isReference = ($this->currentFileInfo && isset($this->currentFileInfo['reference']) && (!isset($this->isReferenced) || !$this->isReferenced) );
- $isCompressed = Less_Parser::$options['compress'] && !preg_match('/^\/\*!/', $this->value);
- return $this->silent || $isReference || $isCompressed;
- }
-
- public function compile(){
- return $this;
- }
-
- public function markReferenced(){
- $this->isReferenced = true;
- }
-
-}
-
-
-/**
- * Condition
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Condition extends Less_Tree{
-
- public $op;
- public $lvalue;
- public $rvalue;
- public $index;
- public $negate;
- public $type = 'Condition';
-
- public function __construct($op, $l, $r, $i = 0, $negate = false) {
- $this->op = trim($op);
- $this->lvalue = $l;
- $this->rvalue = $r;
- $this->index = $i;
- $this->negate = $negate;
- }
-
- public function accept($visitor){
- $this->lvalue = $visitor->visitObj( $this->lvalue );
- $this->rvalue = $visitor->visitObj( $this->rvalue );
- }
-
- public function compile($env) {
- $a = $this->lvalue->compile($env);
- $b = $this->rvalue->compile($env);
-
- switch( $this->op ){
- case 'and':
- $result = $a && $b;
- break;
-
- case 'or':
- $result = $a || $b;
- break;
-
- default:
- if( Less_Parser::is_method($a, 'compare') ){
- $result = $a->compare($b);
- }elseif( Less_Parser::is_method($b, 'compare') ){
- $result = $b->compare($a);
- }else{
- throw new Less_Exception_Compiler('Unable to perform comparison', null, $this->index);
- }
-
- switch ($result) {
- case -1:
- $result = $this->op === '<' || $this->op === '=<' || $this->op === '<=';
- break;
-
- case 0:
- $result = $this->op === '=' || $this->op === '>=' || $this->op === '=<' || $this->op === '<=';
- break;
-
- case 1:
- $result = $this->op === '>' || $this->op === '>=';
- break;
- }
- break;
- }
-
- return $this->negate ? !$result : $result;
- }
-
-}
-
-
-/**
- * DefaultFunc
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_DefaultFunc{
-
- static $error_;
- static $value_;
-
- public static function compile(){
- if( self::$error_ ){
- throw new Exception(self::$error_);
- }
- if( self::$value_ !== null ){
- return self::$value_ ? new Less_Tree_Keyword('true') : new Less_Tree_Keyword('false');
- }
- }
-
- public static function value( $v ){
- self::$value_ = $v;
- }
-
- public static function error( $e ){
- self::$error_ = $e;
- }
-
- public static function reset(){
- self::$value_ = self::$error_ = null;
- }
-}
-
-/**
- * DetachedRuleset
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_DetachedRuleset extends Less_Tree{
-
- public $ruleset;
- public $frames;
- public $type = 'DetachedRuleset';
-
- public function __construct( $ruleset, $frames = null ){
- $this->ruleset = $ruleset;
- $this->frames = $frames;
- }
-
- public function accept($visitor) {
- $this->ruleset = $visitor->visitObj($this->ruleset);
- }
-
- public function compile($env){
- if( $this->frames ){
- $frames = $this->frames;
- }else{
- $frames = $env->frames;
- }
- return new Less_Tree_DetachedRuleset($this->ruleset, $frames);
- }
-
- public function callEval($env) {
- if( $this->frames ){
- return $this->ruleset->compile( $env->copyEvalEnv( array_merge($this->frames,$env->frames) ) );
- }
- return $this->ruleset->compile( $env );
- }
-}
-
-
-
-/**
- * Dimension
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Dimension extends Less_Tree{
-
- public $value;
- public $unit;
- public $type = 'Dimension';
-
- public function __construct($value, $unit = null){
- $this->value = floatval($value);
-
- if( $unit && ($unit instanceof Less_Tree_Unit) ){
- $this->unit = $unit;
- }elseif( $unit ){
- $this->unit = new Less_Tree_Unit( array($unit) );
- }else{
- $this->unit = new Less_Tree_Unit( );
- }
- }
-
- public function accept( $visitor ){
- $this->unit = $visitor->visitObj( $this->unit );
- }
-
- public function compile(){
- return $this;
- }
-
- public function toColor() {
- return new Less_Tree_Color(array($this->value, $this->value, $this->value));
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output ){
-
- if( Less_Parser::$options['strictUnits'] && !$this->unit->isSingular() ){
- throw new Less_Exception_Compiler("Multiple units in dimension. Correct the units or use the unit function. Bad unit: ".$this->unit->toString());
- }
-
- $value = Less_Functions::fround( $this->value );
- $strValue = (string)$value;
-
- if( $value !== 0 && $value < 0.000001 && $value > -0.000001 ){
- // would be output 1e-6 etc.
- $strValue = number_format($strValue,10);
- $strValue = preg_replace('/\.?0+$/','', $strValue);
- }
-
- if( Less_Parser::$options['compress'] ){
- // Zero values doesn't need a unit
- if( $value === 0 && $this->unit->isLength() ){
- $output->add( $strValue );
- return $strValue;
- }
-
- // Float values doesn't need a leading zero
- if( $value > 0 && $value < 1 && $strValue[0] === '0' ){
- $strValue = substr($strValue,1);
- }
- }
-
- $output->add( $strValue );
- $this->unit->genCSS( $output );
- }
-
- public function __toString(){
- return $this->toCSS();
- }
-
- // In an operation between two Dimensions,
- // we default to the first Dimension's unit,
- // so `1px + 2em` will yield `3px`.
-
- /**
- * @param string $op
- */
- public function operate( $op, $other){
-
- $value = Less_Functions::operate( $op, $this->value, $other->value);
- $unit = clone $this->unit;
-
- if( $op === '+' || $op === '-' ){
-
- if( !$unit->numerator && !$unit->denominator ){
- $unit->numerator = $other->unit->numerator;
- $unit->denominator = $other->unit->denominator;
- }elseif( !$other->unit->numerator && !$other->unit->denominator ){
- // do nothing
- }else{
- $other = $other->convertTo( $this->unit->usedUnits());
-
- if( Less_Parser::$options['strictUnits'] && $other->unit->toString() !== $unit->toCSS() ){
- throw new Less_Exception_Compiler("Incompatible units. Change the units or use the unit function. Bad units: '" . $unit->toString() . "' and " . $other->unit->toString() . "'.");
- }
-
- $value = Less_Functions::operate( $op, $this->value, $other->value);
- }
- }elseif( $op === '*' ){
- $unit->numerator = array_merge($unit->numerator, $other->unit->numerator);
- $unit->denominator = array_merge($unit->denominator, $other->unit->denominator);
- sort($unit->numerator);
- sort($unit->denominator);
- $unit->cancel();
- }elseif( $op === '/' ){
- $unit->numerator = array_merge($unit->numerator, $other->unit->denominator);
- $unit->denominator = array_merge($unit->denominator, $other->unit->numerator);
- sort($unit->numerator);
- sort($unit->denominator);
- $unit->cancel();
- }
- return new Less_Tree_Dimension( $value, $unit);
- }
-
- public function compare($other) {
- if ($other instanceof Less_Tree_Dimension) {
-
- if( $this->unit->isEmpty() || $other->unit->isEmpty() ){
- $a = $this;
- $b = $other;
- } else {
- $a = $this->unify();
- $b = $other->unify();
- if( $a->unit->compare($b->unit) !== 0 ){
- return -1;
- }
- }
- $aValue = $a->value;
- $bValue = $b->value;
-
- if ($bValue > $aValue) {
- return -1;
- } elseif ($bValue < $aValue) {
- return 1;
- } else {
- return 0;
- }
- } else {
- return -1;
- }
- }
-
- public function unify() {
- return $this->convertTo(array('length'=> 'px', 'duration'=> 's', 'angle' => 'rad' ));
- }
-
- public function convertTo($conversions) {
- $value = $this->value;
- $unit = clone $this->unit;
-
- if( is_string($conversions) ){
- $derivedConversions = array();
- foreach( Less_Tree_UnitConversions::$groups as $i ){
- if( isset(Less_Tree_UnitConversions::${$i}[$conversions]) ){
- $derivedConversions = array( $i => $conversions);
- }
- }
- $conversions = $derivedConversions;
- }
-
-
- foreach($conversions as $groupName => $targetUnit){
- $group = Less_Tree_UnitConversions::${$groupName};
-
- //numerator
- foreach($unit->numerator as $i => $atomicUnit){
- $atomicUnit = $unit->numerator[$i];
- if( !isset($group[$atomicUnit]) ){
- continue;
- }
-
- $value = $value * ($group[$atomicUnit] / $group[$targetUnit]);
-
- $unit->numerator[$i] = $targetUnit;
- }
-
- //denominator
- foreach($unit->denominator as $i => $atomicUnit){
- $atomicUnit = $unit->denominator[$i];
- if( !isset($group[$atomicUnit]) ){
- continue;
- }
-
- $value = $value / ($group[$atomicUnit] / $group[$targetUnit]);
-
- $unit->denominator[$i] = $targetUnit;
- }
- }
-
- $unit->cancel();
-
- return new Less_Tree_Dimension( $value, $unit);
- }
-}
-
-
-/**
- * Directive
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Directive extends Less_Tree{
-
- public $name;
- public $value;
- public $rules;
- public $index;
- public $isReferenced;
- public $currentFileInfo;
- public $debugInfo;
- public $type = 'Directive';
-
- public function __construct($name, $value = null, $rules, $index = null, $currentFileInfo = null, $debugInfo = null ){
- $this->name = $name;
- $this->value = $value;
- if( $rules ){
- $this->rules = $rules;
- $this->rules->allowImports = true;
- }
-
- $this->index = $index;
- $this->currentFileInfo = $currentFileInfo;
- $this->debugInfo = $debugInfo;
- }
-
-
- public function accept( $visitor ){
- if( $this->rules ){
- $this->rules = $visitor->visitObj( $this->rules );
- }
- if( $this->value ){
- $this->value = $visitor->visitObj( $this->value );
- }
- }
-
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output ){
- $value = $this->value;
- $rules = $this->rules;
- $output->add( $this->name, $this->currentFileInfo, $this->index );
- if( $this->value ){
- $output->add(' ');
- $this->value->genCSS($output);
- }
- if( $this->rules ){
- Less_Tree::outputRuleset( $output, array($this->rules));
- } else {
- $output->add(';');
- }
- }
-
- public function compile($env){
-
- $value = $this->value;
- $rules = $this->rules;
- if( $value ){
- $value = $value->compile($env);
- }
-
- if( $rules ){
- $rules = $rules->compile($env);
- $rules->root = true;
- }
-
- return new Less_Tree_Directive( $this->name, $value, $rules, $this->index, $this->currentFileInfo, $this->debugInfo );
- }
-
-
- public function variable($name){
- if( $this->rules ){
- return $this->rules->variable($name);
- }
- }
-
- public function find($selector){
- if( $this->rules ){
- return $this->rules->find($selector, $this);
- }
- }
-
- //rulesets: function () { if (this.rules) return tree.Ruleset.prototype.rulesets.apply(this.rules); },
-
- public function markReferenced(){
- $this->isReferenced = true;
- if( $this->rules ){
- Less_Tree::ReferencedArray($this->rules->rules);
- }
- }
-
-}
-
-
-/**
- * Element
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Element extends Less_Tree{
-
- public $combinator = '';
- public $value = '';
- public $index;
- public $currentFileInfo;
- public $type = 'Element';
-
- public $value_is_object = false;
-
- public function __construct($combinator, $value, $index = null, $currentFileInfo = null ){
-
- $this->value = $value;
- $this->value_is_object = is_object($value);
-
- if( $combinator ){
- $this->combinator = $combinator;
- }
-
- $this->index = $index;
- $this->currentFileInfo = $currentFileInfo;
- }
-
- public function accept( $visitor ){
- if( $this->value_is_object ){ //object or string
- $this->value = $visitor->visitObj( $this->value );
- }
- }
-
- public function compile($env){
-
- if( Less_Environment::$mixin_stack ){
- return new Less_Tree_Element($this->combinator, ($this->value_is_object ? $this->value->compile($env) : $this->value), $this->index, $this->currentFileInfo );
- }
-
- if( $this->value_is_object ){
- $this->value = $this->value->compile($env);
- }
-
- return $this;
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output ){
- $output->add( $this->toCSS(), $this->currentFileInfo, $this->index );
- }
-
- public function toCSS(){
-
- if( $this->value_is_object ){
- $value = $this->value->toCSS();
- }else{
- $value = $this->value;
- }
-
-
- if( $value === '' && $this->combinator && $this->combinator === '&' ){
- return '';
- }
-
-
- return Less_Environment::$_outputMap[$this->combinator] . $value;
- }
-
-}
-
-
-/**
- * Expression
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Expression extends Less_Tree{
-
- public $value = array();
- public $parens = false;
- public $parensInOp = false;
- public $type = 'Expression';
-
- public function __construct( $value, $parens = null ){
- $this->value = $value;
- $this->parens = $parens;
- }
-
- public function accept( $visitor ){
- $this->value = $visitor->visitArray( $this->value );
- }
-
- public function compile($env) {
-
- $doubleParen = false;
-
- if( $this->parens && !$this->parensInOp ){
- Less_Environment::$parensStack++;
- }
-
- $returnValue = null;
- if( $this->value ){
-
- $count = count($this->value);
-
- if( $count > 1 ){
-
- $ret = array();
- foreach($this->value as $e){
- $ret[] = $e->compile($env);
- }
- $returnValue = new Less_Tree_Expression($ret);
-
- }else{
-
- if( ($this->value[0] instanceof Less_Tree_Expression) && $this->value[0]->parens && !$this->value[0]->parensInOp ){
- $doubleParen = true;
- }
-
- $returnValue = $this->value[0]->compile($env);
- }
-
- } else {
- $returnValue = $this;
- }
-
- if( $this->parens ){
- if( !$this->parensInOp ){
- Less_Environment::$parensStack--;
-
- }elseif( !Less_Environment::isMathOn() && !$doubleParen ){
- $returnValue = new Less_Tree_Paren($returnValue);
-
- }
- }
- return $returnValue;
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output ){
- $val_len = count($this->value);
- for( $i = 0; $i < $val_len; $i++ ){
- $this->value[$i]->genCSS( $output );
- if( $i + 1 < $val_len ){
- $output->add( ' ' );
- }
- }
- }
-
- public function throwAwayComments() {
-
- if( is_array($this->value) ){
- $new_value = array();
- foreach($this->value as $v){
- if( $v instanceof Less_Tree_Comment ){
- continue;
- }
- $new_value[] = $v;
- }
- $this->value = $new_value;
- }
- }
-}
-
-
-/**
- * Extend
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Extend extends Less_Tree{
-
- public $selector;
- public $option;
- public $index;
- public $selfSelectors = array();
- public $allowBefore;
- public $allowAfter;
- public $firstExtendOnThisSelectorPath;
- public $type = 'Extend';
- public $ruleset;
-
-
- public $object_id;
- public $parent_ids = array();
-
- /**
- * @param integer $index
- */
- public function __construct($selector, $option, $index){
- static $i = 0;
- $this->selector = $selector;
- $this->option = $option;
- $this->index = $index;
-
- switch($option){
- case "all":
- $this->allowBefore = true;
- $this->allowAfter = true;
- break;
- default:
- $this->allowBefore = false;
- $this->allowAfter = false;
- break;
- }
-
- $this->object_id = $i++;
- $this->parent_ids = array($this->object_id);
- }
-
- public function accept( $visitor ){
- $this->selector = $visitor->visitObj( $this->selector );
- }
-
- public function compile( $env ){
- Less_Parser::$has_extends = true;
- $this->selector = $this->selector->compile($env);
- return $this;
- //return new Less_Tree_Extend( $this->selector->compile($env), $this->option, $this->index);
- }
-
- public function findSelfSelectors( $selectors ){
- $selfElements = array();
-
-
- for( $i = 0, $selectors_len = count($selectors); $i < $selectors_len; $i++ ){
- $selectorElements = $selectors[$i]->elements;
- // duplicate the logic in genCSS function inside the selector node.
- // future TODO - move both logics into the selector joiner visitor
- if( $i && $selectorElements && $selectorElements[0]->combinator === "") {
- $selectorElements[0]->combinator = ' ';
- }
- $selfElements = array_merge( $selfElements, $selectors[$i]->elements );
- }
-
- $this->selfSelectors = array(new Less_Tree_Selector($selfElements));
- }
-
-}
-
-/**
- * CSS @import node
- *
- * The general strategy here is that we don't want to wait
- * for the parsing to be completed, before we start importing
- * the file. That's because in the context of a browser,
- * most of the time will be spent waiting for the server to respond.
- *
- * On creation, we push the import path to our import queue, though
- * `import,push`, we also pass it a callback, which it'll call once
- * the file has been fetched, and parsed.
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Import extends Less_Tree{
-
- public $options;
- public $index;
- public $path;
- public $features;
- public $currentFileInfo;
- public $css;
- public $skip;
- public $root;
- public $type = 'Import';
-
- public function __construct($path, $features, $options, $index, $currentFileInfo = null ){
- $this->options = $options;
- $this->index = $index;
- $this->path = $path;
- $this->features = $features;
- $this->currentFileInfo = $currentFileInfo;
-
- if( is_array($options) ){
- $this->options += array('inline'=>false);
-
- if( isset($this->options['less']) || $this->options['inline'] ){
- $this->css = !isset($this->options['less']) || !$this->options['less'] || $this->options['inline'];
- } else {
- $pathValue = $this->getPath();
- if( $pathValue && preg_match('/css([\?;].*)?$/',$pathValue) ){
- $this->css = true;
- }
- }
- }
- }
-
-//
-// The actual import node doesn't return anything, when converted to CSS.
-// The reason is that it's used at the evaluation stage, so that the rules
-// it imports can be treated like any other rules.
-//
-// In `eval`, we make sure all Import nodes get evaluated, recursively, so
-// we end up with a flat structure, which can easily be imported in the parent
-// ruleset.
-//
-
- public function accept($visitor){
-
- if( $this->features ){
- $this->features = $visitor->visitObj($this->features);
- }
- $this->path = $visitor->visitObj($this->path);
-
- if( !$this->options['inline'] && $this->root ){
- $this->root = $visitor->visit($this->root);
- }
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output ){
- if( $this->css ){
-
- $output->add( '@import ', $this->currentFileInfo, $this->index );
-
- $this->path->genCSS( $output );
- if( $this->features ){
- $output->add( ' ' );
- $this->features->genCSS( $output );
- }
- $output->add( ';' );
- }
- }
-
- public function toCSS(){
- $features = $this->features ? ' ' . $this->features->toCSS() : '';
-
- if ($this->css) {
- return "@import " . $this->path->toCSS() . $features . ";\n";
- } else {
- return "";
- }
- }
-
- /**
- * @return string
- */
- public function getPath(){
- if ($this->path instanceof Less_Tree_Quoted) {
- $path = $this->path->value;
- $path = ( isset($this->css) || preg_match('/(\.[a-z]*$)|([\?;].*)$/',$path)) ? $path : $path . '.less';
- } else if ($this->path instanceof Less_Tree_URL) {
- $path = $this->path->value->value;
- }else{
- return null;
- }
-
- //remove query string and fragment
- return preg_replace('/[\?#][^\?]*$/','',$path);
- }
-
- public function compileForImport( $env ){
- return new Less_Tree_Import( $this->path->compile($env), $this->features, $this->options, $this->index, $this->currentFileInfo);
- }
-
- public function compilePath($env) {
- $path = $this->path->compile($env);
- $rootpath = '';
- if( $this->currentFileInfo && $this->currentFileInfo['rootpath'] ){
- $rootpath = $this->currentFileInfo['rootpath'];
- }
-
-
- if( !($path instanceof Less_Tree_URL) ){
- if( $rootpath ){
- $pathValue = $path->value;
- // Add the base path if the import is relative
- if( $pathValue && Less_Environment::isPathRelative($pathValue) ){
- $path->value = $this->currentFileInfo['uri_root'].$pathValue;
- }
- }
- $path->value = Less_Environment::normalizePath($path->value);
- }
-
-
-
- return $path;
- }
-
- public function compile( $env ){
-
- $evald = $this->compileForImport($env);
-
- //get path & uri
- $path_and_uri = null;
- if( is_callable(Less_Parser::$options['import_callback']) ){
- $path_and_uri = call_user_func(Less_Parser::$options['import_callback'],$evald);
- }
-
- if( !$path_and_uri ){
- $path_and_uri = $evald->PathAndUri();
- }
-
- if( $path_and_uri ){
- list($full_path, $uri) = $path_and_uri;
- }else{
- $full_path = $uri = $evald->getPath();
- }
-
-
- //import once
- if( $evald->skip( $full_path, $env) ){
- return array();
- }
-
- if( $this->options['inline'] ){
- //todo needs to reference css file not import
- //$contents = new Less_Tree_Anonymous($this->root, 0, array('filename'=>$this->importedFilename), true );
-
- Less_Parser::AddParsedFile($full_path);
- $contents = new Less_Tree_Anonymous( file_get_contents($full_path), 0, array(), true );
-
- if( $this->features ){
- return new Less_Tree_Media( array($contents), $this->features->value );
- }
-
- return array( $contents );
- }
-
- // optional (need to be before "CSS" to support optional CSS imports. CSS should be checked only if empty($this->currentFileInfo))
- if( isset($this->options['optional']) && $this->options['optional'] && !file_exists($full_path) && (!$evald->css || !empty($this->currentFileInfo))) {
- return array();
- }
-
-
- // css ?
- if( $evald->css ){
- $features = ( $evald->features ? $evald->features->compile($env) : null );
- return new Less_Tree_Import( $this->compilePath( $env), $features, $this->options, $this->index);
- }
-
-
- return $this->ParseImport( $full_path, $uri, $env );
- }
-
-
- /**
- * Using the import directories, get the full absolute path and uri of the import
- *
- * @param Less_Tree_Import $evald
- */
- public function PathAndUri(){
-
- $evald_path = $this->getPath();
-
- if( $evald_path ){
-
- $import_dirs = array();
-
- if( Less_Environment::isPathRelative($evald_path) ){
- //if the path is relative, the file should be in the current directory
- $import_dirs[ $this->currentFileInfo['currentDirectory'] ] = $this->currentFileInfo['uri_root'];
-
- }else{
- //otherwise, the file should be relative to the server root
- $import_dirs[ $this->currentFileInfo['entryPath'] ] = $this->currentFileInfo['entryUri'];
-
- //if the user supplied entryPath isn't the actual root
- $import_dirs[ $_SERVER['DOCUMENT_ROOT'] ] = '';
-
- }
-
- // always look in user supplied import directories
- $import_dirs = array_merge( $import_dirs, Less_Parser::$options['import_dirs'] );
-
-
- foreach( $import_dirs as $rootpath => $rooturi){
- if( is_callable($rooturi) ){
- list($path, $uri) = call_user_func($rooturi, $evald_path);
- if( is_string($path) ){
- $full_path = $path;
- return array( $full_path, $uri );
- }
- }elseif( !empty($rootpath) ){
-
-
- if( $rooturi ){
- if( strpos($evald_path,$rooturi) === 0 ){
- $evald_path = substr( $evald_path, strlen($rooturi) );
- }
- }
-
- $path = rtrim($rootpath,'/\\').'/'.ltrim($evald_path,'/\\');
-
- if( file_exists($path) ){
- $full_path = Less_Environment::normalizePath($path);
- $uri = Less_Environment::normalizePath(dirname($rooturi.$evald_path));
- return array( $full_path, $uri );
- } elseif( file_exists($path.'.less') ){
- $full_path = Less_Environment::normalizePath($path.'.less');
- $uri = Less_Environment::normalizePath(dirname($rooturi.$evald_path.'.less'));
- return array( $full_path, $uri );
- }
- }
- }
- }
- }
-
-
- /**
- * Parse the import url and return the rules
- *
- * @return Less_Tree_Media|array
- */
- public function ParseImport( $full_path, $uri, $env ){
-
- $import_env = clone $env;
- if( (isset($this->options['reference']) && $this->options['reference']) || isset($this->currentFileInfo['reference']) ){
- $import_env->currentFileInfo['reference'] = true;
- }
-
- if( (isset($this->options['multiple']) && $this->options['multiple']) ){
- $import_env->importMultiple = true;
- }
-
- $parser = new Less_Parser($import_env);
- $root = $parser->parseFile($full_path, $uri, true);
-
-
- $ruleset = new Less_Tree_Ruleset(array(), $root->rules );
- $ruleset->evalImports($import_env);
-
- return $this->features ? new Less_Tree_Media($ruleset->rules, $this->features->value) : $ruleset->rules;
- }
-
-
- /**
- * Should the import be skipped?
- *
- * @return boolean|null
- */
- private function Skip($path, $env){
-
- $path = Less_Parser::winPath(realpath($path));
-
- if( $path && Less_Parser::FileParsed($path) ){
-
- if( isset($this->currentFileInfo['reference']) ){
- return true;
- }
-
- return !isset($this->options['multiple']) && !$env->importMultiple;
- }
-
- }
-}
-
-
-/**
- * Javascript
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Javascript extends Less_Tree{
-
- public $type = 'Javascript';
- public $escaped;
- public $expression;
- public $index;
-
- /**
- * @param boolean $index
- * @param boolean $escaped
- */
- public function __construct($string, $index, $escaped){
- $this->escaped = $escaped;
- $this->expression = $string;
- $this->index = $index;
- }
-
- public function compile(){
- return new Less_Tree_Anonymous('/* Sorry, can not do JavaScript evaluation in PHP... :( */');
- }
-
-}
-
-
-/**
- * Keyword
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Keyword extends Less_Tree{
-
- public $value;
- public $type = 'Keyword';
-
- /**
- * @param string $value
- */
- public function __construct($value){
- $this->value = $value;
- }
-
- public function compile(){
- return $this;
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output ){
-
- if( $this->value === '%') {
- throw new Less_Exception_Compiler("Invalid % without number");
- }
-
- $output->add( $this->value );
- }
-
- public function compare($other) {
- if ($other instanceof Less_Tree_Keyword) {
- return $other->value === $this->value ? 0 : 1;
- } else {
- return -1;
- }
- }
-}
-
-
-/**
- * Media
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Media extends Less_Tree{
-
- public $features;
- public $rules;
- public $index;
- public $currentFileInfo;
- public $isReferenced;
- public $type = 'Media';
-
- public function __construct($value = array(), $features = array(), $index = null, $currentFileInfo = null ){
-
- $this->index = $index;
- $this->currentFileInfo = $currentFileInfo;
-
- $selectors = $this->emptySelectors();
-
- $this->features = new Less_Tree_Value($features);
-
- $this->rules = array(new Less_Tree_Ruleset($selectors, $value));
- $this->rules[0]->allowImports = true;
- }
-
- public function accept( $visitor ){
- $this->features = $visitor->visitObj($this->features);
- $this->rules = $visitor->visitArray($this->rules);
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output ){
-
- $output->add( '@media ', $this->currentFileInfo, $this->index );
- $this->features->genCSS( $output );
- Less_Tree::outputRuleset( $output, $this->rules);
-
- }
-
- public function compile($env) {
-
- $media = new Less_Tree_Media(array(), array(), $this->index, $this->currentFileInfo );
-
- $strictMathBypass = false;
- if( Less_Parser::$options['strictMath'] === false) {
- $strictMathBypass = true;
- Less_Parser::$options['strictMath'] = true;
- }
-
- $media->features = $this->features->compile($env);
-
- if( $strictMathBypass ){
- Less_Parser::$options['strictMath'] = false;
- }
-
- $env->mediaPath[] = $media;
- $env->mediaBlocks[] = $media;
-
- array_unshift($env->frames, $this->rules[0]);
- $media->rules = array($this->rules[0]->compile($env));
- array_shift($env->frames);
-
- array_pop($env->mediaPath);
-
- return !$env->mediaPath ? $media->compileTop($env) : $media->compileNested($env);
- }
-
- public function variable($name) {
- return $this->rules[0]->variable($name);
- }
-
- public function find($selector) {
- return $this->rules[0]->find($selector, $this);
- }
-
- public function emptySelectors(){
- $el = new Less_Tree_Element('','&', $this->index, $this->currentFileInfo );
- $sels = array( new Less_Tree_Selector(array($el), array(), null, $this->index, $this->currentFileInfo) );
- $sels[0]->mediaEmpty = true;
- return $sels;
- }
-
- public function markReferenced(){
- $this->rules[0]->markReferenced();
- $this->isReferenced = true;
- Less_Tree::ReferencedArray($this->rules[0]->rules);
- }
-
- // evaltop
- public function compileTop($env) {
- $result = $this;
-
- if (count($env->mediaBlocks) > 1) {
- $selectors = $this->emptySelectors();
- $result = new Less_Tree_Ruleset($selectors, $env->mediaBlocks);
- $result->multiMedia = true;
- }
-
- $env->mediaBlocks = array();
- $env->mediaPath = array();
-
- return $result;
- }
-
- public function compileNested($env) {
- $path = array_merge($env->mediaPath, array($this));
-
- // Extract the media-query conditions separated with `,` (OR).
- foreach ($path as $key => $p) {
- $value = $p->features instanceof Less_Tree_Value ? $p->features->value : $p->features;
- $path[$key] = is_array($value) ? $value : array($value);
- }
-
- // Trace all permutations to generate the resulting media-query.
- //
- // (a, b and c) with nested (d, e) ->
- // a and d
- // a and e
- // b and c and d
- // b and c and e
-
- $permuted = $this->permute($path);
- $expressions = array();
- foreach($permuted as $path){
-
- for( $i=0, $len=count($path); $i < $len; $i++){
- $path[$i] = Less_Parser::is_method($path[$i], 'toCSS') ? $path[$i] : new Less_Tree_Anonymous($path[$i]);
- }
-
- for( $i = count($path) - 1; $i > 0; $i-- ){
- array_splice($path, $i, 0, array(new Less_Tree_Anonymous('and')));
- }
-
- $expressions[] = new Less_Tree_Expression($path);
- }
- $this->features = new Less_Tree_Value($expressions);
-
-
-
- // Fake a tree-node that doesn't output anything.
- return new Less_Tree_Ruleset(array(), array());
- }
-
- public function permute($arr) {
- if (!$arr)
- return array();
-
- if (count($arr) == 1)
- return $arr[0];
-
- $result = array();
- $rest = $this->permute(array_slice($arr, 1));
- foreach ($rest as $r) {
- foreach ($arr[0] as $a) {
- $result[] = array_merge(
- is_array($a) ? $a : array($a),
- is_array($r) ? $r : array($r)
- );
- }
- }
-
- return $result;
- }
-
- public function bubbleSelectors($selectors) {
-
- if( !$selectors) return;
-
- $this->rules = array(new Less_Tree_Ruleset( $selectors, array($this->rules[0])));
- }
-
-}
-
-
-/**
- * A simple css name-value pair
- * ex: width:100px;
- *
- * In bootstrap, there are about 600-1,000 simple name-value pairs (depending on how forgiving the match is) -vs- 6,020 dynamic rules (Less_Tree_Rule)
- * Using the name-value object can speed up bootstrap compilation slightly, but it breaks color keyword interpretation: color:red -> color:#FF0000;
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_NameValue extends Less_Tree{
-
- public $name;
- public $value;
- public $index;
- public $currentFileInfo;
- public $type = 'NameValue';
- public $important = '';
-
- public function __construct($name, $value = null, $index = null, $currentFileInfo = null ){
- $this->name = $name;
- $this->value = $value;
- $this->index = $index;
- $this->currentFileInfo = $currentFileInfo;
- }
-
- public function genCSS( $output ){
-
- $output->add(
- $this->name
- . Less_Environment::$_outputMap[': ']
- . $this->value
- . $this->important
- . (((Less_Environment::$lastRule && Less_Parser::$options['compress'])) ? "" : ";")
- , $this->currentFileInfo, $this->index);
- }
-
- public function compile ($env){
- return $this;
- }
-
- public function makeImportant(){
- $new = new Less_Tree_NameValue($this->name, $this->value, $this->index, $this->currentFileInfo);
- $new->important = ' !important';
- return $new;
- }
-
-
-}
-
-
-/**
- * Negative
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Negative extends Less_Tree{
-
- public $value;
- public $type = 'Negative';
-
- public function __construct($node){
- $this->value = $node;
- }
-
- //function accept($visitor) {
- // $this->value = $visitor->visit($this->value);
- //}
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output ){
- $output->add( '-' );
- $this->value->genCSS( $output );
- }
-
- public function compile($env) {
- if( Less_Environment::isMathOn() ){
- $ret = new Less_Tree_Operation('*', array( new Less_Tree_Dimension(-1), $this->value ) );
- return $ret->compile($env);
- }
- return new Less_Tree_Negative( $this->value->compile($env) );
- }
-}
-
-/**
- * Operation
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Operation extends Less_Tree{
-
- public $op;
- public $operands;
- public $isSpaced;
- public $type = 'Operation';
-
- /**
- * @param string $op
- */
- public function __construct($op, $operands, $isSpaced = false){
- $this->op = trim($op);
- $this->operands = $operands;
- $this->isSpaced = $isSpaced;
- }
-
- public function accept($visitor) {
- $this->operands = $visitor->visitArray($this->operands);
- }
-
- public function compile($env){
- $a = $this->operands[0]->compile($env);
- $b = $this->operands[1]->compile($env);
-
-
- if( Less_Environment::isMathOn() ){
-
- if( $a instanceof Less_Tree_Dimension && $b instanceof Less_Tree_Color ){
- $a = $a->toColor();
-
- }elseif( $b instanceof Less_Tree_Dimension && $a instanceof Less_Tree_Color ){
- $b = $b->toColor();
-
- }
-
- if( !method_exists($a,'operate') ){
- throw new Less_Exception_Compiler("Operation on an invalid type");
- }
-
- return $a->operate( $this->op, $b);
- }
-
- return new Less_Tree_Operation($this->op, array($a, $b), $this->isSpaced );
- }
-
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output ){
- $this->operands[0]->genCSS( $output );
- if( $this->isSpaced ){
- $output->add( " " );
- }
- $output->add( $this->op );
- if( $this->isSpaced ){
- $output->add( ' ' );
- }
- $this->operands[1]->genCSS( $output );
- }
-
-}
-
-
-/**
- * Paren
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Paren extends Less_Tree{
-
- public $value;
- public $type = 'Paren';
-
- public function __construct($value) {
- $this->value = $value;
- }
-
- public function accept($visitor){
- $this->value = $visitor->visitObj($this->value);
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output ){
- $output->add( '(' );
- $this->value->genCSS( $output );
- $output->add( ')' );
- }
-
- public function compile($env) {
- return new Less_Tree_Paren($this->value->compile($env));
- }
-
-}
-
-
-/**
- * Quoted
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Quoted extends Less_Tree{
- public $escaped;
- public $value;
- public $quote;
- public $index;
- public $currentFileInfo;
- public $type = 'Quoted';
-
- /**
- * @param string $str
- */
- public function __construct($str, $content = '', $escaped = false, $index = false, $currentFileInfo = null ){
- $this->escaped = $escaped;
- $this->value = $content;
- if( $str ){
- $this->quote = $str[0];
- }
- $this->index = $index;
- $this->currentFileInfo = $currentFileInfo;
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output ){
- if( !$this->escaped ){
- $output->add( $this->quote, $this->currentFileInfo, $this->index );
- }
- $output->add( $this->value );
- if( !$this->escaped ){
- $output->add( $this->quote );
- }
- }
-
- public function compile($env){
-
- $value = $this->value;
- if( preg_match_all('/`([^`]+)`/', $this->value, $matches) ){
- foreach($matches as $i => $match){
- $js = new Less_Tree_JavaScript($matches[1], $this->index, true);
- $js = $js->compile()->value;
- $value = str_replace($matches[0][$i], $js, $value);
- }
- }
-
- if( preg_match_all('/@\{([\w-]+)\}/',$value,$matches) ){
- foreach($matches[1] as $i => $match){
- $v = new Less_Tree_Variable('@' . $match, $this->index, $this->currentFileInfo );
- $v = $v->compile($env);
- $v = ($v instanceof Less_Tree_Quoted) ? $v->value : $v->toCSS();
- $value = str_replace($matches[0][$i], $v, $value);
- }
- }
-
- return new Less_Tree_Quoted($this->quote . $value . $this->quote, $value, $this->escaped, $this->index, $this->currentFileInfo);
- }
-
- public function compare($x) {
-
- if( !Less_Parser::is_method($x, 'toCSS') ){
- return -1;
- }
-
- $left = $this->toCSS();
- $right = $x->toCSS();
-
- if ($left === $right) {
- return 0;
- }
-
- return $left < $right ? -1 : 1;
- }
-}
-
-
-/**
- * Rule
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Rule extends Less_Tree{
-
- public $name;
- public $value;
- public $important;
- public $merge;
- public $index;
- public $inline;
- public $variable;
- public $currentFileInfo;
- public $type = 'Rule';
-
- /**
- * @param string $important
- */
- public function __construct($name, $value = null, $important = null, $merge = null, $index = null, $currentFileInfo = null, $inline = false){
- $this->name = $name;
- $this->value = ($value instanceof Less_Tree_Value || $value instanceof Less_Tree_Ruleset) ? $value : new Less_Tree_Value(array($value));
- $this->important = $important ? ' ' . trim($important) : '';
- $this->merge = $merge;
- $this->index = $index;
- $this->currentFileInfo = $currentFileInfo;
- $this->inline = $inline;
- $this->variable = ( is_string($name) && $name[0] === '@');
- }
-
- public function accept($visitor) {
- $this->value = $visitor->visitObj( $this->value );
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output ){
-
- $output->add( $this->name . Less_Environment::$_outputMap[': '], $this->currentFileInfo, $this->index);
- try{
- $this->value->genCSS( $output);
-
- }catch( Less_Exception_Parser $e ){
- $e->index = $this->index;
- $e->currentFile = $this->currentFileInfo;
- throw $e;
- }
- $output->add( $this->important . (($this->inline || (Less_Environment::$lastRule && Less_Parser::$options['compress'])) ? "" : ";"), $this->currentFileInfo, $this->index);
- }
-
- public function compile ($env){
-
- $name = $this->name;
- if( is_array($name) ){
- // expand 'primitive' name directly to get
- // things faster (~10% for benchmark.less):
- if( count($name) === 1 && $name[0] instanceof Less_Tree_Keyword ){
- $name = $name[0]->value;
- }else{
- $name = $this->CompileName($env,$name);
- }
- }
-
- $strictMathBypass = Less_Parser::$options['strictMath'];
- if( $name === "font" && !Less_Parser::$options['strictMath'] ){
- Less_Parser::$options['strictMath'] = true;
- }
-
- try {
- $evaldValue = $this->value->compile($env);
-
- if( !$this->variable && $evaldValue->type === "DetachedRuleset") {
- throw new Less_Exception_Compiler("Rulesets cannot be evaluated on a property.", null, $this->index, $this->currentFileInfo);
- }
-
- if( Less_Environment::$mixin_stack ){
- $return = new Less_Tree_Rule($name, $evaldValue, $this->important, $this->merge, $this->index, $this->currentFileInfo, $this->inline);
- }else{
- $this->name = $name;
- $this->value = $evaldValue;
- $return = $this;
- }
-
- }catch( Less_Exception_Parser $e ){
- if( !is_numeric($e->index) ){
- $e->index = $this->index;
- $e->currentFile = $this->currentFileInfo;
- }
- throw $e;
- }
-
- Less_Parser::$options['strictMath'] = $strictMathBypass;
-
- return $return;
- }
-
-
- public function CompileName( $env, $name ){
- $output = new Less_Output();
- foreach($name as $n){
- $n->compile($env)->genCSS($output);
- }
- return $output->toString();
- }
-
- public function makeImportant(){
- return new Less_Tree_Rule($this->name, $this->value, '!important', $this->merge, $this->index, $this->currentFileInfo, $this->inline);
- }
-
-}
-
-
-/**
- * Ruleset
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Ruleset extends Less_Tree{
-
- protected $lookups;
- public $_variables;
- public $_rulesets;
-
- public $strictImports;
-
- public $selectors;
- public $rules;
- public $root;
- public $allowImports;
- public $paths;
- public $firstRoot;
- public $type = 'Ruleset';
- public $multiMedia;
- public $allExtends;
-
- public $ruleset_id;
- public $originalRuleset;
-
- public $first_oelements;
-
- public function SetRulesetIndex(){
- $this->ruleset_id = Less_Parser::$next_id++;
- $this->originalRuleset = $this->ruleset_id;
-
- if( $this->selectors ){
- foreach($this->selectors as $sel){
- if( $sel->_oelements ){
- $this->first_oelements[$sel->_oelements[0]] = true;
- }
- }
- }
- }
-
- public function __construct($selectors, $rules, $strictImports = null){
- $this->selectors = $selectors;
- $this->rules = $rules;
- $this->lookups = array();
- $this->strictImports = $strictImports;
- $this->SetRulesetIndex();
- }
-
- public function accept( $visitor ){
- if( $this->paths ){
- $paths_len = count($this->paths);
- for($i = 0,$paths_len; $i < $paths_len; $i++ ){
- $this->paths[$i] = $visitor->visitArray($this->paths[$i]);
- }
- }elseif( $this->selectors ){
- $this->selectors = $visitor->visitArray($this->selectors);
- }
-
- if( $this->rules ){
- $this->rules = $visitor->visitArray($this->rules);
- }
- }
-
- public function compile($env){
-
- $ruleset = $this->PrepareRuleset($env);
-
-
- // Store the frames around mixin definitions,
- // so they can be evaluated like closures when the time comes.
- $rsRuleCnt = count($ruleset->rules);
- for( $i = 0; $i < $rsRuleCnt; $i++ ){
- if( $ruleset->rules[$i] instanceof Less_Tree_Mixin_Definition || $ruleset->rules[$i] instanceof Less_Tree_DetachedRuleset ){
- $ruleset->rules[$i] = $ruleset->rules[$i]->compile($env);
- }
- }
-
- $mediaBlockCount = 0;
- if( $env instanceof Less_Environment ){
- $mediaBlockCount = count($env->mediaBlocks);
- }
-
- // Evaluate mixin calls.
- $this->EvalMixinCalls( $ruleset, $env, $rsRuleCnt );
-
-
- // Evaluate everything else
- for( $i=0; $i<$rsRuleCnt; $i++ ){
- if(! ($ruleset->rules[$i] instanceof Less_Tree_Mixin_Definition || $ruleset->rules[$i] instanceof Less_Tree_DetachedRuleset) ){
- $ruleset->rules[$i] = $ruleset->rules[$i]->compile($env);
- }
- }
-
- // Evaluate everything else
- for( $i=0; $i<$rsRuleCnt; $i++ ){
- $rule = $ruleset->rules[$i];
-
- // for rulesets, check if it is a css guard and can be removed
- if( $rule instanceof Less_Tree_Ruleset && $rule->selectors && count($rule->selectors) === 1 ){
-
- // check if it can be folded in (e.g. & where)
- if( $rule->selectors[0]->isJustParentSelector() ){
- array_splice($ruleset->rules,$i--,1);
- $rsRuleCnt--;
-
- for($j = 0; $j < count($rule->rules); $j++ ){
- $subRule = $rule->rules[$j];
- if( !($subRule instanceof Less_Tree_Rule) || !$subRule->variable ){
- array_splice($ruleset->rules, ++$i, 0, array($subRule));
- $rsRuleCnt++;
- }
- }
-
- }
- }
- }
-
-
- // Pop the stack
- $env->shiftFrame();
-
- if ($mediaBlockCount) {
- $len = count($env->mediaBlocks);
- for($i = $mediaBlockCount; $i < $len; $i++ ){
- $env->mediaBlocks[$i]->bubbleSelectors($ruleset->selectors);
- }
- }
-
- return $ruleset;
- }
-
- /**
- * Compile Less_Tree_Mixin_Call objects
- *
- * @param Less_Tree_Ruleset $ruleset
- * @param integer $rsRuleCnt
- */
- private function EvalMixinCalls( $ruleset, $env, &$rsRuleCnt ){
- for($i=0; $i < $rsRuleCnt; $i++){
- $rule = $ruleset->rules[$i];
-
- if( $rule instanceof Less_Tree_Mixin_Call ){
- $rule = $rule->compile($env);
-
- $temp = array();
- foreach($rule as $r){
- if( ($r instanceof Less_Tree_Rule) && $r->variable ){
- // do not pollute the scope if the variable is
- // already there. consider returning false here
- // but we need a way to "return" variable from mixins
- if( !$ruleset->variable($r->name) ){
- $temp[] = $r;
- }
- }else{
- $temp[] = $r;
- }
- }
- $temp_count = count($temp)-1;
- array_splice($ruleset->rules, $i, 1, $temp);
- $rsRuleCnt += $temp_count;
- $i += $temp_count;
- $ruleset->resetCache();
-
- }elseif( $rule instanceof Less_Tree_RulesetCall ){
-
- $rule = $rule->compile($env);
- $rules = array();
- foreach($rule->rules as $r){
- if( ($r instanceof Less_Tree_Rule) && $r->variable ){
- continue;
- }
- $rules[] = $r;
- }
-
- array_splice($ruleset->rules, $i, 1, $rules);
- $temp_count = count($rules);
- $rsRuleCnt += $temp_count - 1;
- $i += $temp_count-1;
- $ruleset->resetCache();
- }
-
- }
- }
-
-
- /**
- * Compile the selectors and create a new ruleset object for the compile() method
- *
- */
- private function PrepareRuleset($env){
-
- $hasOnePassingSelector = false;
- $selectors = array();
- if( $this->selectors ){
- Less_Tree_DefaultFunc::error("it is currently only allowed in parametric mixin guards,");
-
- foreach($this->selectors as $s){
- $selector = $s->compile($env);
- $selectors[] = $selector;
- if( $selector->evaldCondition ){
- $hasOnePassingSelector = true;
- }
- }
-
- Less_Tree_DefaultFunc::reset();
- } else {
- $hasOnePassingSelector = true;
- }
-
- if( $this->rules && $hasOnePassingSelector ){
- $rules = $this->rules;
- }else{
- $rules = array();
- }
-
- $ruleset = new Less_Tree_Ruleset($selectors, $rules, $this->strictImports);
-
- $ruleset->originalRuleset = $this->ruleset_id;
-
- $ruleset->root = $this->root;
- $ruleset->firstRoot = $this->firstRoot;
- $ruleset->allowImports = $this->allowImports;
-
-
- // push the current ruleset to the frames stack
- $env->unshiftFrame($ruleset);
-
-
- // Evaluate imports
- if( $ruleset->root || $ruleset->allowImports || !$ruleset->strictImports ){
- $ruleset->evalImports($env);
- }
-
- return $ruleset;
- }
-
- function evalImports($env) {
-
- $rules_len = count($this->rules);
- for($i=0; $i < $rules_len; $i++){
- $rule = $this->rules[$i];
-
- if( $rule instanceof Less_Tree_Import ){
- $rules = $rule->compile($env);
- if( is_array($rules) ){
- array_splice($this->rules, $i, 1, $rules);
- $temp_count = count($rules)-1;
- $i += $temp_count;
- $rules_len += $temp_count;
- }else{
- array_splice($this->rules, $i, 1, array($rules));
- }
-
- $this->resetCache();
- }
- }
- }
-
- function makeImportant(){
-
- $important_rules = array();
- foreach($this->rules as $rule){
- if( $rule instanceof Less_Tree_Rule || $rule instanceof Less_Tree_Ruleset || $rule instanceof Less_Tree_NameValue ){
- $important_rules[] = $rule->makeImportant();
- }else{
- $important_rules[] = $rule;
- }
- }
-
- return new Less_Tree_Ruleset($this->selectors, $important_rules, $this->strictImports );
- }
-
- public function matchArgs($args){
- return !$args;
- }
-
- // lets you call a css selector with a guard
- public function matchCondition( $args, $env ){
- $lastSelector = end($this->selectors);
-
- if( !$lastSelector->evaldCondition ){
- return false;
- }
- if( $lastSelector->condition && !$lastSelector->condition->compile( $env->copyEvalEnv( $env->frames ) ) ){
- return false;
- }
- return true;
- }
-
- function resetCache(){
- $this->_rulesets = null;
- $this->_variables = null;
- $this->lookups = array();
- }
-
- public function variables(){
- $this->_variables = array();
- foreach( $this->rules as $r){
- if ($r instanceof Less_Tree_Rule && $r->variable === true) {
- $this->_variables[$r->name] = $r;
- }
- }
- }
-
- public function variable($name){
-
- if( is_null($this->_variables) ){
- $this->variables();
- }
- return isset($this->_variables[$name]) ? $this->_variables[$name] : null;
- }
-
- public function find( $selector, $self = null ){
-
- $key = implode(' ',$selector->_oelements);
-
- if( !isset($this->lookups[$key]) ){
-
- if( !$self ){
- $self = $this->ruleset_id;
- }
-
- $this->lookups[$key] = array();
-
- $first_oelement = $selector->_oelements[0];
-
- foreach($this->rules as $rule){
- if( $rule instanceof Less_Tree_Ruleset && $rule->ruleset_id != $self ){
-
- if( isset($rule->first_oelements[$first_oelement]) ){
-
- foreach( $rule->selectors as $ruleSelector ){
- $match = $selector->match($ruleSelector);
- if( $match ){
- if( $selector->elements_len > $match ){
- $this->lookups[$key] = array_merge($this->lookups[$key], $rule->find( new Less_Tree_Selector(array_slice($selector->elements, $match)), $self));
- } else {
- $this->lookups[$key][] = $rule;
- }
- break;
- }
- }
- }
- }
- }
- }
-
- return $this->lookups[$key];
- }
-
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output ){
-
- if( !$this->root ){
- Less_Environment::$tabLevel++;
- }
-
- $tabRuleStr = $tabSetStr = '';
- if( !Less_Parser::$options['compress'] ){
- if( Less_Environment::$tabLevel ){
- $tabRuleStr = "\n".str_repeat( Less_Parser::$options['indentation'] , Less_Environment::$tabLevel );
- $tabSetStr = "\n".str_repeat( Less_Parser::$options['indentation'] , Less_Environment::$tabLevel-1 );
- }else{
- $tabSetStr = $tabRuleStr = "\n";
- }
- }
-
-
- $ruleNodes = array();
- $rulesetNodes = array();
- foreach($this->rules as $rule){
-
- $class = get_class($rule);
- if( ($class === 'Less_Tree_Media') || ($class === 'Less_Tree_Directive') || ($this->root && $class === 'Less_Tree_Comment') || ($class === 'Less_Tree_Ruleset' && $rule->rules) ){
- $rulesetNodes[] = $rule;
- }else{
- $ruleNodes[] = $rule;
- }
- }
-
- // If this is the root node, we don't render
- // a selector, or {}.
- if( !$this->root ){
-
- /*
- debugInfo = tree.debugInfo(env, this, tabSetStr);
-
- if (debugInfo) {
- output.add(debugInfo);
- output.add(tabSetStr);
- }
- */
-
- $paths_len = count($this->paths);
- for( $i = 0; $i < $paths_len; $i++ ){
- $path = $this->paths[$i];
- $firstSelector = true;
-
- foreach($path as $p){
- $p->genCSS( $output, $firstSelector );
- $firstSelector = false;
- }
-
- if( $i + 1 < $paths_len ){
- $output->add( ',' . $tabSetStr );
- }
- }
-
- $output->add( (Less_Parser::$options['compress'] ? '{' : " {") . $tabRuleStr );
- }
-
- // Compile rules and rulesets
- $ruleNodes_len = count($ruleNodes);
- $rulesetNodes_len = count($rulesetNodes);
- for( $i = 0; $i < $ruleNodes_len; $i++ ){
- $rule = $ruleNodes[$i];
-
- // @page{ directive ends up with root elements inside it, a mix of rules and rulesets
- // In this instance we do not know whether it is the last property
- if( $i + 1 === $ruleNodes_len && (!$this->root || $rulesetNodes_len === 0 || $this->firstRoot ) ){
- Less_Environment::$lastRule = true;
- }
-
- $rule->genCSS( $output );
-
- if( !Less_Environment::$lastRule ){
- $output->add( $tabRuleStr );
- }else{
- Less_Environment::$lastRule = false;
- }
- }
-
- if( !$this->root ){
- $output->add( $tabSetStr . '}' );
- Less_Environment::$tabLevel--;
- }
-
- $firstRuleset = true;
- $space = ($this->root ? $tabRuleStr : $tabSetStr);
- for( $i = 0; $i < $rulesetNodes_len; $i++ ){
-
- if( $ruleNodes_len && $firstRuleset ){
- $output->add( $space );
- }elseif( !$firstRuleset ){
- $output->add( $space );
- }
- $firstRuleset = false;
- $rulesetNodes[$i]->genCSS( $output);
- }
-
- if( !Less_Parser::$options['compress'] && $this->firstRoot ){
- $output->add( "\n" );
- }
-
- }
-
-
- function markReferenced(){
- if( !$this->selectors ){
- return;
- }
- foreach($this->selectors as $selector){
- $selector->markReferenced();
- }
- }
-
- public function joinSelectors( $context, $selectors ){
- $paths = array();
- if( is_array($selectors) ){
- foreach($selectors as $selector) {
- $this->joinSelector( $paths, $context, $selector);
- }
- }
- return $paths;
- }
-
- public function joinSelector( &$paths, $context, $selector){
-
- $hasParentSelector = false;
-
- foreach($selector->elements as $el) {
- if( $el->value === '&') {
- $hasParentSelector = true;
- }
- }
-
- if( !$hasParentSelector ){
- if( $context ){
- foreach($context as $context_el){
- $paths[] = array_merge($context_el, array($selector) );
- }
- }else {
- $paths[] = array($selector);
- }
- return;
- }
-
-
- // The paths are [[Selector]]
- // The first list is a list of comma seperated selectors
- // The inner list is a list of inheritance seperated selectors
- // e.g.
- // .a, .b {
- // .c {
- // }
- // }
- // == [[.a] [.c]] [[.b] [.c]]
- //
-
- // the elements from the current selector so far
- $currentElements = array();
- // the current list of new selectors to add to the path.
- // We will build it up. We initiate it with one empty selector as we "multiply" the new selectors
- // by the parents
- $newSelectors = array(array());
-
-
- foreach( $selector->elements as $el){
-
- // non parent reference elements just get added
- if( $el->value !== '&' ){
- $currentElements[] = $el;
- } else {
- // the new list of selectors to add
- $selectorsMultiplied = array();
-
- // merge the current list of non parent selector elements
- // on to the current list of selectors to add
- if( $currentElements ){
- $this->mergeElementsOnToSelectors( $currentElements, $newSelectors);
- }
-
- // loop through our current selectors
- foreach($newSelectors as $sel){
-
- // if we don't have any parent paths, the & might be in a mixin so that it can be used
- // whether there are parents or not
- if( !$context ){
- // the combinator used on el should now be applied to the next element instead so that
- // it is not lost
- if( $sel ){
- $sel[0]->elements = array_slice($sel[0]->elements,0);
- $sel[0]->elements[] = new Less_Tree_Element($el->combinator, '', $el->index, $el->currentFileInfo );
- }
- $selectorsMultiplied[] = $sel;
- }else {
-
- // and the parent selectors
- foreach($context as $parentSel){
- // We need to put the current selectors
- // then join the last selector's elements on to the parents selectors
-
- // our new selector path
- $newSelectorPath = array();
- // selectors from the parent after the join
- $afterParentJoin = array();
- $newJoinedSelectorEmpty = true;
-
- //construct the joined selector - if & is the first thing this will be empty,
- // if not newJoinedSelector will be the last set of elements in the selector
- if( $sel ){
- $newSelectorPath = $sel;
- $lastSelector = array_pop($newSelectorPath);
- $newJoinedSelector = $selector->createDerived( array_slice($lastSelector->elements,0) );
- $newJoinedSelectorEmpty = false;
- }
- else {
- $newJoinedSelector = $selector->createDerived(array());
- }
-
- //put together the parent selectors after the join
- if ( count($parentSel) > 1) {
- $afterParentJoin = array_merge($afterParentJoin, array_slice($parentSel,1) );
- }
-
- if ( $parentSel ){
- $newJoinedSelectorEmpty = false;
-
- // join the elements so far with the first part of the parent
- $newJoinedSelector->elements[] = new Less_Tree_Element( $el->combinator, $parentSel[0]->elements[0]->value, $el->index, $el->currentFileInfo);
-
- $newJoinedSelector->elements = array_merge( $newJoinedSelector->elements, array_slice($parentSel[0]->elements, 1) );
- }
-
- if (!$newJoinedSelectorEmpty) {
- // now add the joined selector
- $newSelectorPath[] = $newJoinedSelector;
- }
-
- // and the rest of the parent
- $newSelectorPath = array_merge($newSelectorPath, $afterParentJoin);
-
- // add that to our new set of selectors
- $selectorsMultiplied[] = $newSelectorPath;
- }
- }
- }
-
- // our new selectors has been multiplied, so reset the state
- $newSelectors = $selectorsMultiplied;
- $currentElements = array();
- }
- }
-
- // if we have any elements left over (e.g. .a& .b == .b)
- // add them on to all the current selectors
- if( $currentElements ){
- $this->mergeElementsOnToSelectors($currentElements, $newSelectors);
- }
- foreach( $newSelectors as $new_sel){
- if( $new_sel ){
- $paths[] = $new_sel;
- }
- }
- }
-
- function mergeElementsOnToSelectors( $elements, &$selectors){
-
- if( !$selectors ){
- $selectors[] = array( new Less_Tree_Selector($elements) );
- return;
- }
-
-
- foreach( $selectors as &$sel){
-
- // if the previous thing in sel is a parent this needs to join on to it
- if( $sel ){
- $last = count($sel)-1;
- $sel[$last] = $sel[$last]->createDerived( array_merge($sel[$last]->elements, $elements) );
- }else{
- $sel[] = new Less_Tree_Selector( $elements );
- }
- }
- }
-}
-
-
-/**
- * RulesetCall
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_RulesetCall extends Less_Tree{
-
- public $variable;
- public $type = "RulesetCall";
-
- public function __construct($variable){
- $this->variable = $variable;
- }
-
- public function accept($visitor) {}
-
- public function compile( $env ){
- $variable = new Less_Tree_Variable($this->variable);
- $detachedRuleset = $variable->compile($env);
- return $detachedRuleset->callEval($env);
- }
-}
-
-
-
-/**
- * Selector
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Selector extends Less_Tree{
-
- public $elements;
- public $condition;
- public $extendList = array();
- public $_css;
- public $index;
- public $evaldCondition = false;
- public $type = 'Selector';
- public $currentFileInfo = array();
- public $isReferenced;
- public $mediaEmpty;
-
- public $elements_len = 0;
-
- public $_oelements;
- public $_oelements_len;
- public $cacheable = true;
-
- /**
- * @param boolean $isReferenced
- */
- public function __construct( $elements, $extendList = array() , $condition = null, $index=null, $currentFileInfo=null, $isReferenced=null ){
-
- $this->elements = $elements;
- $this->elements_len = count($elements);
- $this->extendList = $extendList;
- $this->condition = $condition;
- if( $currentFileInfo ){
- $this->currentFileInfo = $currentFileInfo;
- }
- $this->isReferenced = $isReferenced;
- if( !$condition ){
- $this->evaldCondition = true;
- }
-
- $this->CacheElements();
- }
-
- public function accept($visitor) {
- $this->elements = $visitor->visitArray($this->elements);
- $this->extendList = $visitor->visitArray($this->extendList);
- if( $this->condition ){
- $this->condition = $visitor->visitObj($this->condition);
- }
-
- if( $visitor instanceof Less_Visitor_extendFinder ){
- $this->CacheElements();
- }
- }
-
- public function createDerived( $elements, $extendList = null, $evaldCondition = null ){
- $newSelector = new Less_Tree_Selector( $elements, ($extendList ? $extendList : $this->extendList), null, $this->index, $this->currentFileInfo, $this->isReferenced);
- $newSelector->evaldCondition = $evaldCondition ? $evaldCondition : $this->evaldCondition;
- return $newSelector;
- }
-
-
- public function match( $other ){
-
- if( !$other->_oelements || ($this->elements_len < $other->_oelements_len) ){
- return 0;
- }
-
- for( $i = 0; $i < $other->_oelements_len; $i++ ){
- if( $this->elements[$i]->value !== $other->_oelements[$i]) {
- return 0;
- }
- }
-
- return $other->_oelements_len; // return number of matched elements
- }
-
-
- public function CacheElements(){
-
- $this->_oelements = array();
- $css = '';
-
- foreach($this->elements as $v){
-
- $css .= $v->combinator;
- if( !$v->value_is_object ){
- $css .= $v->value;
- continue;
- }
-
- if( !property_exists($v->value,'value') || !is_string($v->value->value) ){
- $this->cacheable = false;
- return;
- }
- $css .= $v->value->value;
- }
-
- $this->_oelements_len = preg_match_all('/[,&#\.\w-](?:[\w-]|(?:\\\\.))*/', $css, $matches);
- if( $this->_oelements_len ){
- $this->_oelements = $matches[0];
-
- if( $this->_oelements[0] === '&' ){
- array_shift($this->_oelements);
- $this->_oelements_len--;
- }
- }
- }
-
- public function isJustParentSelector(){
- return !$this->mediaEmpty &&
- count($this->elements) === 1 &&
- $this->elements[0]->value === '&' &&
- ($this->elements[0]->combinator === ' ' || $this->elements[0]->combinator === '');
- }
-
- public function compile($env) {
-
- $elements = array();
- foreach($this->elements as $el){
- $elements[] = $el->compile($env);
- }
-
- $extendList = array();
- foreach($this->extendList as $el){
- $extendList[] = $el->compile($el);
- }
-
- $evaldCondition = false;
- if( $this->condition ){
- $evaldCondition = $this->condition->compile($env);
- }
-
- return $this->createDerived( $elements, $extendList, $evaldCondition );
- }
-
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output, $firstSelector = true ){
-
- if( !$firstSelector && $this->elements[0]->combinator === "" ){
- $output->add(' ', $this->currentFileInfo, $this->index);
- }
-
- foreach($this->elements as $element){
- $element->genCSS( $output );
- }
- }
-
- public function markReferenced(){
- $this->isReferenced = true;
- }
-
- public function getIsReferenced(){
- return !isset($this->currentFileInfo['reference']) || !$this->currentFileInfo['reference'] || $this->isReferenced;
- }
-
- public function getIsOutput(){
- return $this->evaldCondition;
- }
-
-}
-
-
-/**
- * UnicodeDescriptor
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_UnicodeDescriptor extends Less_Tree{
-
- public $value;
- public $type = 'UnicodeDescriptor';
-
- public function __construct($value){
- $this->value = $value;
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output ){
- $output->add( $this->value );
- }
-
- public function compile(){
- return $this;
- }
-}
-
-
-
-/**
- * Unit
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Unit extends Less_Tree{
-
- var $numerator = array();
- var $denominator = array();
- public $backupUnit;
- public $type = 'Unit';
-
- public function __construct($numerator = array(), $denominator = array(), $backupUnit = null ){
- $this->numerator = $numerator;
- $this->denominator = $denominator;
- $this->backupUnit = $backupUnit;
- }
-
- public function __clone(){
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output ){
-
- if( $this->numerator ){
- $output->add( $this->numerator[0] );
- }elseif( $this->denominator ){
- $output->add( $this->denominator[0] );
- }elseif( !Less_Parser::$options['strictUnits'] && $this->backupUnit ){
- $output->add( $this->backupUnit );
- return ;
- }
- }
-
- public function toString(){
- $returnStr = implode('*',$this->numerator);
- foreach($this->denominator as $d){
- $returnStr .= '/'.$d;
- }
- return $returnStr;
- }
-
- public function __toString(){
- return $this->toString();
- }
-
-
- /**
- * @param Less_Tree_Unit $other
- */
- public function compare($other) {
- return $this->is( $other->toString() ) ? 0 : -1;
- }
-
- public function is($unitString){
- return $this->toString() === $unitString;
- }
-
- public function isLength(){
- $css = $this->toCSS();
- return !!preg_match('/px|em|%|in|cm|mm|pc|pt|ex/',$css);
- }
-
- public function isAngle() {
- return isset( Less_Tree_UnitConversions::$angle[$this->toCSS()] );
- }
-
- public function isEmpty(){
- return !$this->numerator && !$this->denominator;
- }
-
- public function isSingular() {
- return count($this->numerator) <= 1 && !$this->denominator;
- }
-
-
- public function usedUnits(){
- $result = array();
-
- foreach(Less_Tree_UnitConversions::$groups as $groupName){
- $group = Less_Tree_UnitConversions::${$groupName};
-
- foreach($this->numerator as $atomicUnit){
- if( isset($group[$atomicUnit]) && !isset($result[$groupName]) ){
- $result[$groupName] = $atomicUnit;
- }
- }
-
- foreach($this->denominator as $atomicUnit){
- if( isset($group[$atomicUnit]) && !isset($result[$groupName]) ){
- $result[$groupName] = $atomicUnit;
- }
- }
- }
-
- return $result;
- }
-
- public function cancel(){
- $counter = array();
- $backup = null;
-
- foreach($this->numerator as $atomicUnit){
- if( !$backup ){
- $backup = $atomicUnit;
- }
- $counter[$atomicUnit] = ( isset($counter[$atomicUnit]) ? $counter[$atomicUnit] : 0) + 1;
- }
-
- foreach($this->denominator as $atomicUnit){
- if( !$backup ){
- $backup = $atomicUnit;
- }
- $counter[$atomicUnit] = ( isset($counter[$atomicUnit]) ? $counter[$atomicUnit] : 0) - 1;
- }
-
- $this->numerator = array();
- $this->denominator = array();
-
- foreach($counter as $atomicUnit => $count){
- if( $count > 0 ){
- for( $i = 0; $i < $count; $i++ ){
- $this->numerator[] = $atomicUnit;
- }
- }elseif( $count < 0 ){
- for( $i = 0; $i < -$count; $i++ ){
- $this->denominator[] = $atomicUnit;
- }
- }
- }
-
- if( !$this->numerator && !$this->denominator && $backup ){
- $this->backupUnit = $backup;
- }
-
- sort($this->numerator);
- sort($this->denominator);
- }
-
-
-}
-
-
-
-/**
- * UnitConversions
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_UnitConversions{
-
- public static $groups = array('length','duration','angle');
-
- public static $length = array(
- 'm'=> 1,
- 'cm'=> 0.01,
- 'mm'=> 0.001,
- 'in'=> 0.0254,
- 'px'=> 0.000264583, // 0.0254 / 96,
- 'pt'=> 0.000352778, // 0.0254 / 72,
- 'pc'=> 0.004233333, // 0.0254 / 72 * 12
- );
-
- public static $duration = array(
- 's'=> 1,
- 'ms'=> 0.001
- );
-
- public static $angle = array(
- 'rad' => 0.1591549430919, // 1/(2*M_PI),
- 'deg' => 0.002777778, // 1/360,
- 'grad'=> 0.0025, // 1/400,
- 'turn'=> 1
- );
-
-}
-
-/**
- * Url
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Url extends Less_Tree{
-
- public $attrs;
- public $value;
- public $currentFileInfo;
- public $isEvald;
- public $type = 'Url';
-
- public function __construct($value, $currentFileInfo = null, $isEvald = null){
- $this->value = $value;
- $this->currentFileInfo = $currentFileInfo;
- $this->isEvald = $isEvald;
- }
-
- public function accept( $visitor ){
- $this->value = $visitor->visitObj($this->value);
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- public function genCSS( $output ){
- $output->add( 'url(' );
- $this->value->genCSS( $output );
- $output->add( ')' );
- }
-
- /**
- * @param Less_Functions $ctx
- */
- public function compile($ctx){
- $val = $this->value->compile($ctx);
-
- if( !$this->isEvald ){
- // Add the base path if the URL is relative
- if( Less_Parser::$options['relativeUrls']
- && $this->currentFileInfo
- && is_string($val->value)
- && Less_Environment::isPathRelative($val->value)
- ){
- $rootpath = $this->currentFileInfo['uri_root'];
- if ( !$val->quote ){
- $rootpath = preg_replace('/[\(\)\'"\s]/', '\\$1', $rootpath );
- }
- $val->value = $rootpath . $val->value;
- }
-
- $val->value = Less_Environment::normalizePath( $val->value);
- }
-
- // Add cache buster if enabled
- if( Less_Parser::$options['urlArgs'] ){
- if( !preg_match('/^\s*data:/',$val->value) ){
- $delimiter = strpos($val->value,'?') === false ? '?' : '&';
- $urlArgs = $delimiter . Less_Parser::$options['urlArgs'];
- $hash_pos = strpos($val->value,'#');
- if( $hash_pos !== false ){
- $val->value = substr_replace($val->value,$urlArgs, $hash_pos, 0);
- } else {
- $val->value .= $urlArgs;
- }
- }
- }
-
- return new Less_Tree_URL($val, $this->currentFileInfo, true);
- }
-
-}
-
-
-/**
- * Value
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Value extends Less_Tree{
-
- public $type = 'Value';
- public $value;
-
- public function __construct($value){
- $this->value = $value;
- }
-
- public function accept($visitor) {
- $this->value = $visitor->visitArray($this->value);
- }
-
- public function compile($env){
-
- $ret = array();
- $i = 0;
- foreach($this->value as $i => $v){
- $ret[] = $v->compile($env);
- }
- if( $i > 0 ){
- return new Less_Tree_Value($ret);
- }
- return $ret[0];
- }
-
- /**
- * @see Less_Tree::genCSS
- */
- function genCSS( $output ){
- $len = count($this->value);
- for($i = 0; $i < $len; $i++ ){
- $this->value[$i]->genCSS( $output );
- if( $i+1 < $len ){
- $output->add( Less_Environment::$_outputMap[','] );
- }
- }
- }
-
-}
-
-
-/**
- * Variable
- *
- * @package Less
- * @subpackage tree
- */
-class Less_Tree_Variable extends Less_Tree{
-
- public $name;
- public $index;
- public $currentFileInfo;
- public $evaluating = false;
- public $type = 'Variable';
-
- /**
- * @param string $name
- */
- public function __construct($name, $index = null, $currentFileInfo = null) {
- $this->name = $name;
- $this->index = $index;
- $this->currentFileInfo = $currentFileInfo;
- }
-
- public function compile($env) {
-
- if( $this->name[1] === '@' ){
- $v = new Less_Tree_Variable(substr($this->name, 1), $this->index + 1, $this->currentFileInfo);
- $name = '@' . $v->compile($env)->value;
- }else{
- $name = $this->name;
- }
-
- if ($this->evaluating) {
- throw new Less_Exception_Compiler("Recursive variable definition for " . $name, null, $this->index, $this->currentFileInfo);
- }
-
- $this->evaluating = true;
-
- foreach($env->frames as $frame){
- if( $v = $frame->variable($name) ){
- $r = $v->value->compile($env);
- $this->evaluating = false;
- return $r;
- }
- }
-
- throw new Less_Exception_Compiler("variable " . $name . " is undefined in file ".$this->currentFileInfo["filename"], null, $this->index, $this->currentFileInfo);
- }
-
-}
-
-
-
-class Less_Tree_Mixin_Call extends Less_Tree{
-
- public $selector;
- public $arguments;
- public $index;
- public $currentFileInfo;
-
- public $important;
- public $type = 'MixinCall';
-
- /**
- * less.js: tree.mixin.Call
- *
- */
- public function __construct($elements, $args, $index, $currentFileInfo, $important = false){
- $this->selector = new Less_Tree_Selector($elements);
- $this->arguments = $args;
- $this->index = $index;
- $this->currentFileInfo = $currentFileInfo;
- $this->important = $important;
- }
-
- //function accept($visitor){
- // $this->selector = $visitor->visit($this->selector);
- // $this->arguments = $visitor->visit($this->arguments);
- //}
-
-
- public function compile($env){
-
- $rules = array();
- $match = false;
- $isOneFound = false;
- $candidates = array();
- $defaultUsed = false;
- $conditionResult = array();
-
- $args = array();
- foreach($this->arguments as $a){
- $args[] = array('name'=> $a['name'], 'value' => $a['value']->compile($env) );
- }
-
- foreach($env->frames as $frame){
-
- $mixins = $frame->find($this->selector);
-
- if( !$mixins ){
- continue;
- }
-
- $isOneFound = true;
- $defNone = 0;
- $defTrue = 1;
- $defFalse = 2;
-
- // To make `default()` function independent of definition order we have two "subpasses" here.
- // At first we evaluate each guard *twice* (with `default() == true` and `default() == false`),
- // and build candidate list with corresponding flags. Then, when we know all possible matches,
- // we make a final decision.
-
- $mixins_len = count($mixins);
- for( $m = 0; $m < $mixins_len; $m++ ){
- $mixin = $mixins[$m];
-
- if( $this->IsRecursive( $env, $mixin ) ){
- continue;
- }
-
- if( $mixin->matchArgs($args, $env) ){
-
- $candidate = array('mixin' => $mixin, 'group' => $defNone);
-
- if( $mixin instanceof Less_Tree_Ruleset ){
-
- for( $f = 0; $f < 2; $f++ ){
- Less_Tree_DefaultFunc::value($f);
- $conditionResult[$f] = $mixin->matchCondition( $args, $env);
- }
- if( $conditionResult[0] || $conditionResult[1] ){
- if( $conditionResult[0] != $conditionResult[1] ){
- $candidate['group'] = $conditionResult[1] ? $defTrue : $defFalse;
- }
-
- $candidates[] = $candidate;
- }
- }else{
- $candidates[] = $candidate;
- }
-
- $match = true;
- }
- }
-
- Less_Tree_DefaultFunc::reset();
-
-
- $count = array(0, 0, 0);
- for( $m = 0; $m < count($candidates); $m++ ){
- $count[ $candidates[$m]['group'] ]++;
- }
-
- if( $count[$defNone] > 0 ){
- $defaultResult = $defFalse;
- } else {
- $defaultResult = $defTrue;
- if( ($count[$defTrue] + $count[$defFalse]) > 1 ){
- throw new Exception( 'Ambiguous use of `default()` found when matching for `' . $this->format($args) . '`' );
- }
- }
-
-
- $candidates_length = count($candidates);
- $length_1 = ($candidates_length == 1);
-
- for( $m = 0; $m < $candidates_length; $m++){
- $candidate = $candidates[$m]['group'];
- if( ($candidate === $defNone) || ($candidate === $defaultResult) ){
- try{
- $mixin = $candidates[$m]['mixin'];
- if( !($mixin instanceof Less_Tree_Mixin_Definition) ){
- $mixin = new Less_Tree_Mixin_Definition('', array(), $mixin->rules, null, false);
- $mixin->originalRuleset = $mixins[$m]->originalRuleset;
- }
- $rules = array_merge($rules, $mixin->evalCall($env, $args, $this->important)->rules);
- } catch (Exception $e) {
- //throw new Less_Exception_Compiler($e->getMessage(), $e->index, null, $this->currentFileInfo['filename']);
- throw new Less_Exception_Compiler($e->getMessage(), null, null, $this->currentFileInfo);
- }
- }
- }
-
- if( $match ){
- if( !$this->currentFileInfo || !isset($this->currentFileInfo['reference']) || !$this->currentFileInfo['reference'] ){
- Less_Tree::ReferencedArray($rules);
- }
-
- return $rules;
- }
- }
-
- if( $isOneFound ){
- throw new Less_Exception_Compiler('No matching definition was found for `'.$this->Format( $args ).'`', null, $this->index, $this->currentFileInfo);
-
- }else{
- throw new Less_Exception_Compiler(trim($this->selector->toCSS()) . " is undefined in ".$this->currentFileInfo['filename'], null, $this->index);
- }
-
- }
-
- /**
- * Format the args for use in exception messages
- *
- */
- private function Format($args){
- $message = array();
- if( $args ){
- foreach($args as $a){
- $argValue = '';
- if( $a['name'] ){
- $argValue .= $a['name'] . ':';
- }
- if( is_object($a['value']) ){
- $argValue .= $a['value']->toCSS();
- }else{
- $argValue .= '???';
- }
- $message[] = $argValue;
- }
- }
- return implode(', ',$message);
- }
-
-
- /**
- * Are we in a recursive mixin call?
- *
- * @return bool
- */
- private function IsRecursive( $env, $mixin ){
-
- foreach($env->frames as $recur_frame){
- if( !($mixin instanceof Less_Tree_Mixin_Definition) ){
-
- if( $mixin === $recur_frame ){
- return true;
- }
-
- if( isset($recur_frame->originalRuleset) && $mixin->ruleset_id === $recur_frame->originalRuleset ){
- return true;
- }
- }
- }
-
- return false;
- }
-
-}
-
-
-
-
-class Less_Tree_Mixin_Definition extends Less_Tree_Ruleset{
- public $name;
- public $selectors;
- public $params;
- public $arity = 0;
- public $rules;
- public $lookups = array();
- public $required = 0;
- public $frames = array();
- public $condition;
- public $variadic;
- public $type = 'MixinDefinition';
-
-
- // less.js : /lib/less/tree/mixin.js : tree.mixin.Definition
- public function __construct($name, $params, $rules, $condition, $variadic = false, $frames = array() ){
- $this->name = $name;
- $this->selectors = array(new Less_Tree_Selector(array( new Less_Tree_Element(null, $name))));
-
- $this->params = $params;
- $this->condition = $condition;
- $this->variadic = $variadic;
- $this->rules = $rules;
-
- if( $params ){
- $this->arity = count($params);
- foreach( $params as $p ){
- if (! isset($p['name']) || ($p['name'] && !isset($p['value']))) {
- $this->required++;
- }
- }
- }
-
- $this->frames = $frames;
- $this->SetRulesetIndex();
- }
-
-
-
- //function accept( $visitor ){
- // $this->params = $visitor->visit($this->params);
- // $this->rules = $visitor->visit($this->rules);
- // $this->condition = $visitor->visit($this->condition);
- //}
-
-
- public function toCSS(){
- return '';
- }
-
- // less.js : /lib/less/tree/mixin.js : tree.mixin.Definition.evalParams
- public function compileParams($env, $mixinFrames, $args = array() , &$evaldArguments = array() ){
- $frame = new Less_Tree_Ruleset(null, array());
- $params = $this->params;
- $mixinEnv = null;
- $argsLength = 0;
-
- if( $args ){
- $argsLength = count($args);
- for($i = 0; $i < $argsLength; $i++ ){
- $arg = $args[$i];
-
- if( $arg && $arg['name'] ){
- $isNamedFound = false;
-
- foreach($params as $j => $param){
- if( !isset($evaldArguments[$j]) && $arg['name'] === $params[$j]['name']) {
- $evaldArguments[$j] = $arg['value']->compile($env);
- array_unshift($frame->rules, new Less_Tree_Rule( $arg['name'], $arg['value']->compile($env) ) );
- $isNamedFound = true;
- break;
- }
- }
- if ($isNamedFound) {
- array_splice($args, $i, 1);
- $i--;
- $argsLength--;
- continue;
- } else {
- throw new Less_Exception_Compiler("Named argument for " . $this->name .' '.$args[$i]['name'] . ' not found');
- }
- }
- }
- }
-
- $argIndex = 0;
- foreach($params as $i => $param){
-
- if ( isset($evaldArguments[$i]) ){ continue; }
-
- $arg = null;
- if( isset($args[$argIndex]) ){
- $arg = $args[$argIndex];
- }
-
- if (isset($param['name']) && $param['name']) {
-
- if( isset($param['variadic']) ){
- $varargs = array();
- for ($j = $argIndex; $j < $argsLength; $j++) {
- $varargs[] = $args[$j]['value']->compile($env);
- }
- $expression = new Less_Tree_Expression($varargs);
- array_unshift($frame->rules, new Less_Tree_Rule($param['name'], $expression->compile($env)));
- }else{
- $val = ($arg && $arg['value']) ? $arg['value'] : false;
-
- if ($val) {
- $val = $val->compile($env);
- } else if ( isset($param['value']) ) {
-
- if( !$mixinEnv ){
- $mixinEnv = new Less_Environment();
- $mixinEnv->frames = array_merge( array($frame), $mixinFrames);
- }
-
- $val = $param['value']->compile($mixinEnv);
- $frame->resetCache();
- } else {
- throw new Less_Exception_Compiler("Wrong number of arguments for " . $this->name . " (" . $argsLength . ' for ' . $this->arity . ")");
- }
-
- array_unshift($frame->rules, new Less_Tree_Rule($param['name'], $val));
- $evaldArguments[$i] = $val;
- }
- }
-
- if ( isset($param['variadic']) && $args) {
- for ($j = $argIndex; $j < $argsLength; $j++) {
- $evaldArguments[$j] = $args[$j]['value']->compile($env);
- }
- }
- $argIndex++;
- }
-
- ksort($evaldArguments);
- $evaldArguments = array_values($evaldArguments);
-
- return $frame;
- }
-
- public function compile($env) {
- if( $this->frames ){
- return new Less_Tree_Mixin_Definition($this->name, $this->params, $this->rules, $this->condition, $this->variadic, $this->frames );
- }
- return new Less_Tree_Mixin_Definition($this->name, $this->params, $this->rules, $this->condition, $this->variadic, $env->frames );
- }
-
- public function evalCall($env, $args = NULL, $important = NULL) {
-
- Less_Environment::$mixin_stack++;
-
- $_arguments = array();
-
- if( $this->frames ){
- $mixinFrames = array_merge($this->frames, $env->frames);
- }else{
- $mixinFrames = $env->frames;
- }
-
- $frame = $this->compileParams($env, $mixinFrames, $args, $_arguments);
-
- $ex = new Less_Tree_Expression($_arguments);
- array_unshift($frame->rules, new Less_Tree_Rule('@arguments', $ex->compile($env)));
-
-
- $ruleset = new Less_Tree_Ruleset(null, $this->rules);
- $ruleset->originalRuleset = $this->ruleset_id;
-
-
- $ruleSetEnv = new Less_Environment();
- $ruleSetEnv->frames = array_merge( array($this, $frame), $mixinFrames );
- $ruleset = $ruleset->compile( $ruleSetEnv );
-
- if( $important ){
- $ruleset = $ruleset->makeImportant();
- }
-
- Less_Environment::$mixin_stack--;
-
- return $ruleset;
- }
-
-
- public function matchCondition($args, $env) {
-
- if( !$this->condition ){
- return true;
- }
-
- // set array to prevent error on array_merge
- if(!is_array($this->frames)) {
- $this->frames = array();
- }
-
- $frame = $this->compileParams($env, array_merge($this->frames,$env->frames), $args );
-
- $compile_env = new Less_Environment();
- $compile_env->frames = array_merge(
- array($frame) // the parameter variables
- , $this->frames // the parent namespace/mixin frames
- , $env->frames // the current environment frames
- );
-
- $compile_env->functions = $env->functions;
-
- return (bool)$this->condition->compile($compile_env);
- }
-
- public function matchArgs($args, $env = NULL){
- $argsLength = count($args);
-
- if( !$this->variadic ){
- if( $argsLength < $this->required ){
- return false;
- }
- if( $argsLength > count($this->params) ){
- return false;
- }
- }else{
- if( $argsLength < ($this->required - 1)){
- return false;
- }
- }
-
- $len = min($argsLength, $this->arity);
-
- for( $i = 0; $i < $len; $i++ ){
- if( !isset($this->params[$i]['name']) && !isset($this->params[$i]['variadic']) ){
- if( $args[$i]['value']->compile($env)->toCSS() != $this->params[$i]['value']->compile($env)->toCSS() ){
- return false;
- }
- }
- }
-
- return true;
- }
-
-}
-
-
-/**
- * Extend Finder Visitor
- *
- * @package Less
- * @subpackage visitor
- */
-class Less_Visitor_extendFinder extends Less_Visitor{
-
- public $contexts = array();
- public $allExtendsStack;
- public $foundExtends;
-
- public function __construct(){
- $this->contexts = array();
- $this->allExtendsStack = array(array());
- parent::__construct();
- }
-
- /**
- * @param Less_Tree_Ruleset $root
- */
- public function run($root){
- $root = $this->visitObj($root);
- $root->allExtends =& $this->allExtendsStack[0];
- return $root;
- }
-
- public function visitRule($ruleNode, &$visitDeeper ){
- $visitDeeper = false;
- }
-
- public function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ){
- $visitDeeper = false;
- }
-
- public function visitRuleset($rulesetNode){
-
- if( $rulesetNode->root ){
- return;
- }
-
- $allSelectorsExtendList = array();
-
- // get &:extend(.a); rules which apply to all selectors in this ruleset
- if( $rulesetNode->rules ){
- foreach($rulesetNode->rules as $rule){
- if( $rule instanceof Less_Tree_Extend ){
- $allSelectorsExtendList[] = $rule;
- $rulesetNode->extendOnEveryPath = true;
- }
- }
- }
-
-
- // now find every selector and apply the extends that apply to all extends
- // and the ones which apply to an individual extend
- foreach($rulesetNode->paths as $selectorPath){
- $selector = end($selectorPath); //$selectorPath[ count($selectorPath)-1];
-
- $j = 0;
- foreach($selector->extendList as $extend){
- $this->allExtendsStackPush($rulesetNode, $selectorPath, $extend, $j);
- }
- foreach($allSelectorsExtendList as $extend){
- $this->allExtendsStackPush($rulesetNode, $selectorPath, $extend, $j);
- }
- }
-
- $this->contexts[] = $rulesetNode->selectors;
- }
-
- public function allExtendsStackPush($rulesetNode, $selectorPath, $extend, &$j){
- $this->foundExtends = true;
- $extend = clone $extend;
- $extend->findSelfSelectors( $selectorPath );
- $extend->ruleset = $rulesetNode;
- if( $j === 0 ){
- $extend->firstExtendOnThisSelectorPath = true;
- }
-
- $end_key = count($this->allExtendsStack)-1;
- $this->allExtendsStack[$end_key][] = $extend;
- $j++;
- }
-
-
- public function visitRulesetOut( $rulesetNode ){
- if( !is_object($rulesetNode) || !$rulesetNode->root ){
- array_pop($this->contexts);
- }
- }
-
- public function visitMedia( $mediaNode ){
- $mediaNode->allExtends = array();
- $this->allExtendsStack[] =& $mediaNode->allExtends;
- }
-
- public function visitMediaOut(){
- array_pop($this->allExtendsStack);
- }
-
- public function visitDirective( $directiveNode ){
- $directiveNode->allExtends = array();
- $this->allExtendsStack[] =& $directiveNode->allExtends;
- }
-
- public function visitDirectiveOut(){
- array_pop($this->allExtendsStack);
- }
-}
-
-
-
-
-/*
-class Less_Visitor_import extends Less_VisitorReplacing{
-
- public $_visitor;
- public $_importer;
- public $importCount;
-
- function __construct( $evalEnv ){
- $this->env = $evalEnv;
- $this->importCount = 0;
- parent::__construct();
- }
-
-
- function run( $root ){
- $root = $this->visitObj($root);
- $this->isFinished = true;
-
- //if( $this->importCount === 0) {
- // $this->_finish();
- //}
- }
-
- function visitImport($importNode, &$visitDeeper ){
- $importVisitor = $this;
- $inlineCSS = $importNode->options['inline'];
-
- if( !$importNode->css || $inlineCSS ){
- $evaldImportNode = $importNode->compileForImport($this->env);
-
- if( $evaldImportNode && (!$evaldImportNode->css || $inlineCSS) ){
- $importNode = $evaldImportNode;
- $this->importCount++;
- $env = clone $this->env;
-
- if( (isset($importNode->options['multiple']) && $importNode->options['multiple']) ){
- $env->importMultiple = true;
- }
-
- //get path & uri
- $path_and_uri = null;
- if( is_callable(Less_Parser::$options['import_callback']) ){
- $path_and_uri = call_user_func(Less_Parser::$options['import_callback'],$importNode);
- }
-
- if( !$path_and_uri ){
- $path_and_uri = $importNode->PathAndUri();
- }
-
- if( $path_and_uri ){
- list($full_path, $uri) = $path_and_uri;
- }else{
- $full_path = $uri = $importNode->getPath();
- }
-
-
- //import once
- if( $importNode->skip( $full_path, $env) ){
- return array();
- }
-
- if( $importNode->options['inline'] ){
- //todo needs to reference css file not import
- //$contents = new Less_Tree_Anonymous($importNode->root, 0, array('filename'=>$importNode->importedFilename), true );
-
- Less_Parser::AddParsedFile($full_path);
- $contents = new Less_Tree_Anonymous( file_get_contents($full_path), 0, array(), true );
-
- if( $importNode->features ){
- return new Less_Tree_Media( array($contents), $importNode->features->value );
- }
-
- return array( $contents );
- }
-
-
- // css ?
- if( $importNode->css ){
- $features = ( $importNode->features ? $importNode->features->compile($env) : null );
- return new Less_Tree_Import( $importNode->compilePath( $env), $features, $importNode->options, $this->index);
- }
-
- return $importNode->ParseImport( $full_path, $uri, $env );
- }
-
- }
-
- $visitDeeper = false;
- return $importNode;
- }
-
-
- function visitRule( $ruleNode, &$visitDeeper ){
- $visitDeeper = false;
- return $ruleNode;
- }
-
- function visitDirective($directiveNode, $visitArgs){
- array_unshift($this->env->frames,$directiveNode);
- return $directiveNode;
- }
-
- function visitDirectiveOut($directiveNode) {
- array_shift($this->env->frames);
- }
-
- function visitMixinDefinition($mixinDefinitionNode, $visitArgs) {
- array_unshift($this->env->frames,$mixinDefinitionNode);
- return $mixinDefinitionNode;
- }
-
- function visitMixinDefinitionOut($mixinDefinitionNode) {
- array_shift($this->env->frames);
- }
-
- function visitRuleset($rulesetNode, $visitArgs) {
- array_unshift($this->env->frames,$rulesetNode);
- return $rulesetNode;
- }
-
- function visitRulesetOut($rulesetNode) {
- array_shift($this->env->frames);
- }
-
- function visitMedia($mediaNode, $visitArgs) {
- array_unshift($this->env->frames, $mediaNode->ruleset);
- return $mediaNode;
- }
-
- function visitMediaOut($mediaNode) {
- array_shift($this->env->frames);
- }
-
-}
-*/
-
-
-
-
-/**
- * Join Selector Visitor
- *
- * @package Less
- * @subpackage visitor
- */
-class Less_Visitor_joinSelector extends Less_Visitor{
-
- public $contexts = array( array() );
-
- /**
- * @param Less_Tree_Ruleset $root
- */
- public function run( $root ){
- return $this->visitObj($root);
- }
-
- public function visitRule( $ruleNode, &$visitDeeper ){
- $visitDeeper = false;
- }
-
- public function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ){
- $visitDeeper = false;
- }
-
- public function visitRuleset( $rulesetNode ){
-
- $paths = array();
-
- if( !$rulesetNode->root ){
- $selectors = array();
-
- if( $rulesetNode->selectors && $rulesetNode->selectors ){
- foreach($rulesetNode->selectors as $selector){
- if( $selector->getIsOutput() ){
- $selectors[] = $selector;
- }
- }
- }
-
- if( !$selectors ){
- $rulesetNode->selectors = null;
- $rulesetNode->rules = null;
- }else{
- $context = end($this->contexts); //$context = $this->contexts[ count($this->contexts) - 1];
- $paths = $rulesetNode->joinSelectors( $context, $selectors);
- }
-
- $rulesetNode->paths = $paths;
- }
-
- $this->contexts[] = $paths; //different from less.js. Placed after joinSelectors() so that $this->contexts will get correct $paths
- }
-
- public function visitRulesetOut(){
- array_pop($this->contexts);
- }
-
- public function visitMedia($mediaNode) {
- $context = end($this->contexts); //$context = $this->contexts[ count($this->contexts) - 1];
-
- if( !count($context) || (is_object($context[0]) && $context[0]->multiMedia) ){
- $mediaNode->rules[0]->root = true;
- }
- }
-
-}
-
-
-
-/**
- * Process Extends Visitor
- *
- * @package Less
- * @subpackage visitor
- */
-class Less_Visitor_processExtends extends Less_Visitor{
-
- public $allExtendsStack;
-
- /**
- * @param Less_Tree_Ruleset $root
- */
- public function run( $root ){
- $extendFinder = new Less_Visitor_extendFinder();
- $extendFinder->run( $root );
- if( !$extendFinder->foundExtends){
- return $root;
- }
-
- $root->allExtends = $this->doExtendChaining( $root->allExtends, $root->allExtends);
-
- $this->allExtendsStack = array();
- $this->allExtendsStack[] = &$root->allExtends;
-
- return $this->visitObj( $root );
- }
-
- private function doExtendChaining( $extendsList, $extendsListTarget, $iterationCount = 0){
- //
- // chaining is different from normal extension.. if we extend an extend then we are not just copying, altering and pasting
- // the selector we would do normally, but we are also adding an extend with the same target selector
- // this means this new extend can then go and alter other extends
- //
- // this method deals with all the chaining work - without it, extend is flat and doesn't work on other extend selectors
- // this is also the most expensive.. and a match on one selector can cause an extension of a selector we had already processed if
- // we look at each selector at a time, as is done in visitRuleset
-
- $extendsToAdd = array();
-
-
- //loop through comparing every extend with every target extend.
- // a target extend is the one on the ruleset we are looking at copy/edit/pasting in place
- // e.g. .a:extend(.b) {} and .b:extend(.c) {} then the first extend extends the second one
- // and the second is the target.
- // the seperation into two lists allows us to process a subset of chains with a bigger set, as is the
- // case when processing media queries
- for( $extendIndex = 0, $extendsList_len = count($extendsList); $extendIndex < $extendsList_len; $extendIndex++ ){
- for( $targetExtendIndex = 0; $targetExtendIndex < count($extendsListTarget); $targetExtendIndex++ ){
-
- $extend = $extendsList[$extendIndex];
- $targetExtend = $extendsListTarget[$targetExtendIndex];
-
- // look for circular references
- if( in_array($targetExtend->object_id, $extend->parent_ids,true) ){
- continue;
- }
-
- // find a match in the target extends self selector (the bit before :extend)
- $selectorPath = array( $targetExtend->selfSelectors[0] );
- $matches = $this->findMatch( $extend, $selectorPath);
-
-
- if( $matches ){
-
- // we found a match, so for each self selector..
- foreach($extend->selfSelectors as $selfSelector ){
-
-
- // process the extend as usual
- $newSelector = $this->extendSelector( $matches, $selectorPath, $selfSelector);
-
- // but now we create a new extend from it
- $newExtend = new Less_Tree_Extend( $targetExtend->selector, $targetExtend->option, 0);
- $newExtend->selfSelectors = $newSelector;
-
- // add the extend onto the list of extends for that selector
- end($newSelector)->extendList = array($newExtend);
- //$newSelector[ count($newSelector)-1]->extendList = array($newExtend);
-
- // record that we need to add it.
- $extendsToAdd[] = $newExtend;
- $newExtend->ruleset = $targetExtend->ruleset;
-
- //remember its parents for circular references
- $newExtend->parent_ids = array_merge($newExtend->parent_ids,$targetExtend->parent_ids,$extend->parent_ids);
-
- // only process the selector once.. if we have :extend(.a,.b) then multiple
- // extends will look at the same selector path, so when extending
- // we know that any others will be duplicates in terms of what is added to the css
- if( $targetExtend->firstExtendOnThisSelectorPath ){
- $newExtend->firstExtendOnThisSelectorPath = true;
- $targetExtend->ruleset->paths[] = $newSelector;
- }
- }
- }
- }
- }
-
- if( $extendsToAdd ){
- // try to detect circular references to stop a stack overflow.
- // may no longer be needed. $this->extendChainCount++;
- if( $iterationCount > 100) {
-
- try{
- $selectorOne = $extendsToAdd[0]->selfSelectors[0]->toCSS();
- $selectorTwo = $extendsToAdd[0]->selector->toCSS();
- }catch(Exception $e){
- $selectorOne = "{unable to calculate}";
- $selectorTwo = "{unable to calculate}";
- }
-
- throw new Less_Exception_Parser("extend circular reference detected. One of the circular extends is currently:" . $selectorOne . ":extend(" . $selectorTwo . ")");
- }
-
- // now process the new extends on the existing rules so that we can handle a extending b extending c ectending d extending e...
- $extendsToAdd = $this->doExtendChaining( $extendsToAdd, $extendsListTarget, $iterationCount+1);
- }
-
- return array_merge($extendsList, $extendsToAdd);
- }
-
-
- protected function visitRule( $ruleNode, &$visitDeeper ){
- $visitDeeper = false;
- }
-
- protected function visitMixinDefinition( $mixinDefinitionNode, &$visitDeeper ){
- $visitDeeper = false;
- }
-
- protected function visitSelector( $selectorNode, &$visitDeeper ){
- $visitDeeper = false;
- }
-
- protected function visitRuleset($rulesetNode){
-
-
- if( $rulesetNode->root ){
- return;
- }
-
- $allExtends = end($this->allExtendsStack);
- $paths_len = count($rulesetNode->paths);
-
- // look at each selector path in the ruleset, find any extend matches and then copy, find and replace
- foreach($allExtends as $allExtend){
- for($pathIndex = 0; $pathIndex < $paths_len; $pathIndex++ ){
-
- // extending extends happens initially, before the main pass
- if( isset($rulesetNode->extendOnEveryPath) && $rulesetNode->extendOnEveryPath ){
- continue;
- }
-
- $selectorPath = $rulesetNode->paths[$pathIndex];
-
- if( end($selectorPath)->extendList ){
- continue;
- }
-
- $this->ExtendMatch( $rulesetNode, $allExtend, $selectorPath);
-
- }
- }
- }
-
-
- private function ExtendMatch( $rulesetNode, $extend, $selectorPath ){
- $matches = $this->findMatch($extend, $selectorPath);
-
- if( $matches ){
- foreach($extend->selfSelectors as $selfSelector ){
- $rulesetNode->paths[] = $this->extendSelector($matches, $selectorPath, $selfSelector);
- }
- }
- }
-
-
-
- private function findMatch($extend, $haystackSelectorPath ){
-
-
- if( !$this->HasMatches($extend, $haystackSelectorPath) ){
- return false;
- }
-
-
- //
- // look through the haystack selector path to try and find the needle - extend.selector
- // returns an array of selector matches that can then be replaced
- //
- $needleElements = $extend->selector->elements;
- $potentialMatches = array();
- $potentialMatches_len = 0;
- $potentialMatch = null;
- $matches = array();
-
-
-
- // loop through the haystack elements
- $haystack_path_len = count($haystackSelectorPath);
- for($haystackSelectorIndex = 0; $haystackSelectorIndex < $haystack_path_len; $haystackSelectorIndex++ ){
- $hackstackSelector = $haystackSelectorPath[$haystackSelectorIndex];
-
- $haystack_elements_len = count($hackstackSelector->elements);
- for($hackstackElementIndex = 0; $hackstackElementIndex < $haystack_elements_len; $hackstackElementIndex++ ){
-
- $haystackElement = $hackstackSelector->elements[$hackstackElementIndex];
-
- // if we allow elements before our match we can add a potential match every time. otherwise only at the first element.
- if( $extend->allowBefore || ($haystackSelectorIndex === 0 && $hackstackElementIndex === 0) ){
- $potentialMatches[] = array('pathIndex'=> $haystackSelectorIndex, 'index'=> $hackstackElementIndex, 'matched'=> 0, 'initialCombinator'=> $haystackElement->combinator);
- $potentialMatches_len++;
- }
-
- for($i = 0; $i < $potentialMatches_len; $i++ ){
-
- $potentialMatch = &$potentialMatches[$i];
- $potentialMatch = $this->PotentialMatch( $potentialMatch, $needleElements, $haystackElement, $hackstackElementIndex );
-
-
- // if we are still valid and have finished, test whether we have elements after and whether these are allowed
- if( $potentialMatch && $potentialMatch['matched'] === $extend->selector->elements_len ){
- $potentialMatch['finished'] = true;
-
- if( !$extend->allowAfter && ($hackstackElementIndex+1 < $haystack_elements_len || $haystackSelectorIndex+1 < $haystack_path_len) ){
- $potentialMatch = null;
- }
- }
-
- // if null we remove, if not, we are still valid, so either push as a valid match or continue
- if( $potentialMatch ){
- if( $potentialMatch['finished'] ){
- $potentialMatch['length'] = $extend->selector->elements_len;
- $potentialMatch['endPathIndex'] = $haystackSelectorIndex;
- $potentialMatch['endPathElementIndex'] = $hackstackElementIndex + 1; // index after end of match
- $potentialMatches = array(); // we don't allow matches to overlap, so start matching again
- $potentialMatches_len = 0;
- $matches[] = $potentialMatch;
- }
- continue;
- }
-
- array_splice($potentialMatches, $i, 1);
- $potentialMatches_len--;
- $i--;
- }
- }
- }
-
- return $matches;
- }
-
-
- // Before going through all the nested loops, lets check to see if a match is possible
- // Reduces Bootstrap 3.1 compile time from ~6.5s to ~5.6s
- private function HasMatches($extend, $haystackSelectorPath){
-
- if( !$extend->selector->cacheable ){
- return true;
- }
-
- $first_el = $extend->selector->_oelements[0];
-
- foreach($haystackSelectorPath as $hackstackSelector){
- if( !$hackstackSelector->cacheable ){
- return true;
- }
-
- if( in_array($first_el, $hackstackSelector->_oelements) ){
- return true;
- }
- }
-
- return false;
- }
-
-
- /**
- * @param integer $hackstackElementIndex
- */
- private function PotentialMatch( $potentialMatch, $needleElements, $haystackElement, $hackstackElementIndex ){
-
-
- if( $potentialMatch['matched'] > 0 ){
-
- // selectors add " " onto the first element. When we use & it joins the selectors together, but if we don't
- // then each selector in haystackSelectorPath has a space before it added in the toCSS phase. so we need to work out
- // what the resulting combinator will be
- $targetCombinator = $haystackElement->combinator;
- if( $targetCombinator === '' && $hackstackElementIndex === 0 ){
- $targetCombinator = ' ';
- }
-
- if( $needleElements[ $potentialMatch['matched'] ]->combinator !== $targetCombinator ){
- return null;
- }
- }
-
- // if we don't match, null our match to indicate failure
- if( !$this->isElementValuesEqual( $needleElements[$potentialMatch['matched'] ]->value, $haystackElement->value) ){
- return null;
- }
-
- $potentialMatch['finished'] = false;
- $potentialMatch['matched']++;
-
- return $potentialMatch;
- }
-
-
- private function isElementValuesEqual( $elementValue1, $elementValue2 ){
-
- if( $elementValue1 === $elementValue2 ){
- return true;
- }
-
- if( is_string($elementValue1) || is_string($elementValue2) ) {
- return false;
- }
-
- if( $elementValue1 instanceof Less_Tree_Attribute ){
- return $this->isAttributeValuesEqual( $elementValue1, $elementValue2 );
- }
-
- $elementValue1 = $elementValue1->value;
- if( $elementValue1 instanceof Less_Tree_Selector ){
- return $this->isSelectorValuesEqual( $elementValue1, $elementValue2 );
- }
-
- return false;
- }
-
-
- /**
- * @param Less_Tree_Selector $elementValue1
- */
- private function isSelectorValuesEqual( $elementValue1, $elementValue2 ){
-
- $elementValue2 = $elementValue2->value;
- if( !($elementValue2 instanceof Less_Tree_Selector) || $elementValue1->elements_len !== $elementValue2->elements_len ){
- return false;
- }
-
- for( $i = 0; $i < $elementValue1->elements_len; $i++ ){
-
- if( $elementValue1->elements[$i]->combinator !== $elementValue2->elements[$i]->combinator ){
- if( $i !== 0 || ($elementValue1->elements[$i]->combinator || ' ') !== ($elementValue2->elements[$i]->combinator || ' ') ){
- return false;
- }
- }
-
- if( !$this->isElementValuesEqual($elementValue1->elements[$i]->value, $elementValue2->elements[$i]->value) ){
- return false;
- }
- }
-
- return true;
- }
-
-
- /**
- * @param Less_Tree_Attribute $elementValue1
- */
- private function isAttributeValuesEqual( $elementValue1, $elementValue2 ){
-
- if( $elementValue1->op !== $elementValue2->op || $elementValue1->key !== $elementValue2->key ){
- return false;
- }
-
- if( !$elementValue1->value || !$elementValue2->value ){
- if( $elementValue1->value || $elementValue2->value ) {
- return false;
- }
- return true;
- }
-
- $elementValue1 = ($elementValue1->value->value ? $elementValue1->value->value : $elementValue1->value );
- $elementValue2 = ($elementValue2->value->value ? $elementValue2->value->value : $elementValue2->value );
-
- return $elementValue1 === $elementValue2;
- }
-
-
- private function extendSelector($matches, $selectorPath, $replacementSelector){
-
- //for a set of matches, replace each match with the replacement selector
-
- $currentSelectorPathIndex = 0;
- $currentSelectorPathElementIndex = 0;
- $path = array();
- $selectorPath_len = count($selectorPath);
-
- for($matchIndex = 0, $matches_len = count($matches); $matchIndex < $matches_len; $matchIndex++ ){
-
-
- $match = $matches[$matchIndex];
- $selector = $selectorPath[ $match['pathIndex'] ];
-
- $firstElement = new Less_Tree_Element(
- $match['initialCombinator'],
- $replacementSelector->elements[0]->value,
- $replacementSelector->elements[0]->index,
- $replacementSelector->elements[0]->currentFileInfo
- );
-
- if( $match['pathIndex'] > $currentSelectorPathIndex && $currentSelectorPathElementIndex > 0 ){
- $last_path = end($path);
- $last_path->elements = array_merge( $last_path->elements, array_slice( $selectorPath[$currentSelectorPathIndex]->elements, $currentSelectorPathElementIndex));
- $currentSelectorPathElementIndex = 0;
- $currentSelectorPathIndex++;
- }
-
- $newElements = array_merge(
- array_slice($selector->elements, $currentSelectorPathElementIndex, ($match['index'] - $currentSelectorPathElementIndex) ) // last parameter of array_slice is different than the last parameter of javascript's slice
- , array($firstElement)
- , array_slice($replacementSelector->elements,1)
- );
-
- if( $currentSelectorPathIndex === $match['pathIndex'] && $matchIndex > 0 ){
- $last_key = count($path)-1;
- $path[$last_key]->elements = array_merge($path[$last_key]->elements,$newElements);
- }else{
- $path = array_merge( $path, array_slice( $selectorPath, $currentSelectorPathIndex, $match['pathIndex'] ));
- $path[] = new Less_Tree_Selector( $newElements );
- }
-
- $currentSelectorPathIndex = $match['endPathIndex'];
- $currentSelectorPathElementIndex = $match['endPathElementIndex'];
- if( $currentSelectorPathElementIndex >= count($selectorPath[$currentSelectorPathIndex]->elements) ){
- $currentSelectorPathElementIndex = 0;
- $currentSelectorPathIndex++;
- }
- }
-
- if( $currentSelectorPathIndex < $selectorPath_len && $currentSelectorPathElementIndex > 0 ){
- $last_path = end($path);
- $last_path->elements = array_merge( $last_path->elements, array_slice($selectorPath[$currentSelectorPathIndex]->elements, $currentSelectorPathElementIndex));
- $currentSelectorPathIndex++;
- }
-
- $slice_len = $selectorPath_len - $currentSelectorPathIndex;
- $path = array_merge($path, array_slice($selectorPath, $currentSelectorPathIndex, $slice_len));
-
- return $path;
- }
-
-
- protected function visitMedia( $mediaNode ){
- $newAllExtends = array_merge( $mediaNode->allExtends, end($this->allExtendsStack) );
- $this->allExtendsStack[] = $this->doExtendChaining($newAllExtends, $mediaNode->allExtends);
- }
-
- protected function visitMediaOut(){
- array_pop( $this->allExtendsStack );
- }
-
- protected function visitDirective( $directiveNode ){
- $newAllExtends = array_merge( $directiveNode->allExtends, end($this->allExtendsStack) );
- $this->allExtendsStack[] = $this->doExtendChaining($newAllExtends, $directiveNode->allExtends);
- }
-
- protected function visitDirectiveOut(){
- array_pop($this->allExtendsStack);
- }
-
-}
-
-/**
- * toCSS Visitor
- *
- * @package Less
- * @subpackage visitor
- */
-class Less_Visitor_toCSS extends Less_VisitorReplacing{
-
- private $charset;
-
- public function __construct(){
- parent::__construct();
- }
-
- /**
- * @param Less_Tree_Ruleset $root
- */
- public function run( $root ){
- return $this->visitObj($root);
- }
-
- public function visitRule( $ruleNode ){
- if( $ruleNode->variable ){
- return array();
- }
- return $ruleNode;
- }
-
- public function visitMixinDefinition($mixinNode){
- // mixin definitions do not get eval'd - this means they keep state
- // so we have to clear that state here so it isn't used if toCSS is called twice
- $mixinNode->frames = array();
- return array();
- }
-
- public function visitExtend(){
- return array();
- }
-
- public function visitComment( $commentNode ){
- if( $commentNode->isSilent() ){
- return array();
- }
- return $commentNode;
- }
-
- public function visitMedia( $mediaNode, &$visitDeeper ){
- $mediaNode->accept($this);
- $visitDeeper = false;
-
- if( !$mediaNode->rules ){
- return array();
- }
- return $mediaNode;
- }
-
- public function visitDirective( $directiveNode ){
- if( isset($directiveNode->currentFileInfo['reference']) && (!property_exists($directiveNode,'isReferenced') || !$directiveNode->isReferenced) ){
- return array();
- }
- if( $directiveNode->name === '@charset' ){
- // Only output the debug info together with subsequent @charset definitions
- // a comment (or @media statement) before the actual @charset directive would
- // be considered illegal css as it has to be on the first line
- if( isset($this->charset) && $this->charset ){
-
- //if( $directiveNode->debugInfo ){
- // $comment = new Less_Tree_Comment('/* ' . str_replace("\n",'',$directiveNode->toCSS())." */\n");
- // $comment->debugInfo = $directiveNode->debugInfo;
- // return $this->visit($comment);
- //}
-
-
- return array();
- }
- $this->charset = true;
- }
- return $directiveNode;
- }
-
- public function checkPropertiesInRoot( $rulesetNode ){
-
- if( !$rulesetNode->firstRoot ){
- return;
- }
-
- foreach($rulesetNode->rules as $ruleNode){
- if( $ruleNode instanceof Less_Tree_Rule && !$ruleNode->variable ){
- $msg = "properties must be inside selector blocks, they cannot be in the root. Index ".$ruleNode->index.($ruleNode->currentFileInfo ? (' Filename: '.$ruleNode->currentFileInfo['filename']) : null);
- throw new Less_Exception_Compiler($msg);
- }
- }
- }
-
-
- public function visitRuleset( $rulesetNode, &$visitDeeper ){
-
- $visitDeeper = false;
-
- $this->checkPropertiesInRoot( $rulesetNode );
-
- if( $rulesetNode->root ){
- return $this->visitRulesetRoot( $rulesetNode );
- }
-
- $rulesets = array();
- $rulesetNode->paths = $this->visitRulesetPaths($rulesetNode);
-
-
- // Compile rules and rulesets
- $nodeRuleCnt = count($rulesetNode->rules);
- for( $i = 0; $i < $nodeRuleCnt; ){
- $rule = $rulesetNode->rules[$i];
-
- if( property_exists($rule,'rules') ){
- // visit because we are moving them out from being a child
- $rulesets[] = $this->visitObj($rule);
- array_splice($rulesetNode->rules,$i,1);
- $nodeRuleCnt--;
- continue;
- }
- $i++;
- }
-
-
- // accept the visitor to remove rules and refactor itself
- // then we can decide now whether we want it or not
- if( $nodeRuleCnt > 0 ){
- $rulesetNode->accept($this);
-
- if( $rulesetNode->rules ){
-
- if( count($rulesetNode->rules) > 1 ){
- $this->_mergeRules( $rulesetNode->rules );
- $this->_removeDuplicateRules( $rulesetNode->rules );
- }
-
- // now decide whether we keep the ruleset
- if( $rulesetNode->paths ){
- //array_unshift($rulesets, $rulesetNode);
- array_splice($rulesets,0,0,array($rulesetNode));
- }
- }
-
- }
-
-
- if( count($rulesets) === 1 ){
- return $rulesets[0];
- }
- return $rulesets;
- }
-
-
- /**
- * Helper function for visitiRuleset
- *
- * return array|Less_Tree_Ruleset
- */
- private function visitRulesetRoot( $rulesetNode ){
- $rulesetNode->accept( $this );
- if( $rulesetNode->firstRoot || $rulesetNode->rules ){
- return $rulesetNode;
- }
- return array();
- }
-
-
- /**
- * Helper function for visitRuleset()
- *
- * @return array
- */
- private function visitRulesetPaths($rulesetNode){
-
- $paths = array();
- foreach($rulesetNode->paths as $p){
- if( $p[0]->elements[0]->combinator === ' ' ){
- $p[0]->elements[0]->combinator = '';
- }
-
- foreach($p as $pi){
- if( $pi->getIsReferenced() && $pi->getIsOutput() ){
- $paths[] = $p;
- break;
- }
- }
- }
-
- return $paths;
- }
-
- protected function _removeDuplicateRules( &$rules ){
- // remove duplicates
- $ruleCache = array();
- for( $i = count($rules)-1; $i >= 0 ; $i-- ){
- $rule = $rules[$i];
- if( $rule instanceof Less_Tree_Rule || $rule instanceof Less_Tree_NameValue ){
-
- if( !isset($ruleCache[$rule->name]) ){
- $ruleCache[$rule->name] = $rule;
- }else{
- $ruleList =& $ruleCache[$rule->name];
-
- if( $ruleList instanceof Less_Tree_Rule || $ruleList instanceof Less_Tree_NameValue ){
- $ruleList = $ruleCache[$rule->name] = array( $ruleCache[$rule->name]->toCSS() );
- }
-
- $ruleCSS = $rule->toCSS();
- if( array_search($ruleCSS,$ruleList) !== false ){
- array_splice($rules,$i,1);
- }else{
- $ruleList[] = $ruleCSS;
- }
- }
- }
- }
- }
-
- protected function _mergeRules( &$rules ){
- $groups = array();
-
- //obj($rules);
-
- $rules_len = count($rules);
- for( $i = 0; $i < $rules_len; $i++ ){
- $rule = $rules[$i];
-
- if( ($rule instanceof Less_Tree_Rule) && $rule->merge ){
-
- $key = $rule->name;
- if( $rule->important ){
- $key .= ',!';
- }
-
- if( !isset($groups[$key]) ){
- $groups[$key] = array();
- }else{
- array_splice($rules, $i--, 1);
- $rules_len--;
- }
-
- $groups[$key][] = $rule;
- }
- }
-
-
- foreach($groups as $parts){
-
- if( count($parts) > 1 ){
- $rule = $parts[0];
- $spacedGroups = array();
- $lastSpacedGroup = array();
- $parts_mapped = array();
- foreach($parts as $p){
- if( $p->merge === '+' ){
- if( $lastSpacedGroup ){
- $spacedGroups[] = self::toExpression($lastSpacedGroup);
- }
- $lastSpacedGroup = array();
- }
- $lastSpacedGroup[] = $p;
- }
-
- $spacedGroups[] = self::toExpression($lastSpacedGroup);
- $rule->value = self::toValue($spacedGroups);
- }
- }
-
- }
-
- public static function toExpression($values){
- $mapped = array();
- foreach($values as $p){
- $mapped[] = $p->value;
- }
- return new Less_Tree_Expression( $mapped );
- }
-
- public static function toValue($values){
- //return new Less_Tree_Value($values); ??
-
- $mapped = array();
- foreach($values as $p){
- $mapped[] = $p;
- }
- return new Less_Tree_Value($mapped);
- }
-}
-
-
-
-/**
- * Parser Exception
- *
- * @package Less
- * @subpackage exception
- */
-class Less_Exception_Parser extends Exception{
-
- /**
- * The current file
- *
- * @var Less_ImportedFile
- */
- public $currentFile;
-
- /**
- * The current parser index
- *
- * @var integer
- */
- public $index;
-
- protected $input;
-
- protected $details = array();
-
-
- /**
- * Constructor
- *
- * @param string $message
- * @param Exception $previous Previous exception
- * @param integer $index The current parser index
- * @param Less_FileInfo|string $currentFile The file
- * @param integer $code The exception code
- */
- public function __construct($message = null, Exception $previous = null, $index = null, $currentFile = null, $code = 0){
-
- if (PHP_VERSION_ID < 50300) {
- $this->previous = $previous;
- parent::__construct($message, $code);
- } else {
- parent::__construct($message, $code, $previous);
- }
-
- $this->currentFile = $currentFile;
- $this->index = $index;
-
- $this->genMessage();
- }
-
-
- protected function getInput(){
-
- if( !$this->input && $this->currentFile && $this->currentFile['filename'] && file_exists($this->currentFile['filename']) ){
- $this->input = file_get_contents( $this->currentFile['filename'] );
- }
- }
-
-
-
- /**
- * Converts the exception to string
- *
- * @return string
- */
- public function genMessage(){
-
- if( $this->currentFile && $this->currentFile['filename'] ){
- $this->message .= ' in '.basename($this->currentFile['filename']);
- }
-
- if( $this->index !== null ){
- $this->getInput();
- if( $this->input ){
- $line = self::getLineNumber();
- $this->message .= ' on line '.$line.', column '.self::getColumn();
-
- $lines = explode("\n",$this->input);
-
- $count = count($lines);
- $start_line = max(0, $line-3);
- $last_line = min($count, $start_line+6);
- $num_len = strlen($last_line);
- for( $i = $start_line; $i < $last_line; $i++ ){
- $this->message .= "\n".str_pad($i+1,$num_len,'0',STR_PAD_LEFT).'| '.$lines[$i];
- }
- }
- }
-
- }
-
- /**
- * Returns the line number the error was encountered
- *
- * @return integer
- */
- public function getLineNumber(){
- if( $this->index ){
- // https://bugs.php.net/bug.php?id=49790
- if (ini_get("mbstring.func_overload")) {
- return substr_count(substr($this->input, 0, $this->index), "\n") + 1;
- } else {
- return substr_count($this->input, "\n", 0, $this->index) + 1;
- }
- }
- return 1;
- }
-
-
- /**
- * Returns the column the error was encountered
- *
- * @return integer
- */
- public function getColumn(){
-
- $part = substr($this->input, 0, $this->index);
- $pos = strrpos($part,"\n");
- return $this->index - $pos;
- }
-
-}
-
-
-/**
- * Chunk Exception
- *
- * @package Less
- * @subpackage exception
- */
-class Less_Exception_Chunk extends Less_Exception_Parser{
-
-
- protected $parserCurrentIndex = 0;
-
- protected $emitFrom = 0;
-
- protected $input_len;
-
-
- /**
- * Constructor
- *
- * @param string $input
- * @param Exception $previous Previous exception
- * @param integer $index The current parser index
- * @param Less_FileInfo|string $currentFile The file
- * @param integer $code The exception code
- */
- public function __construct($input, Exception $previous = null, $index = null, $currentFile = null, $code = 0){
-
- $this->message = 'ParseError: Unexpected input'; //default message
-
- $this->index = $index;
-
- $this->currentFile = $currentFile;
-
- $this->input = $input;
- $this->input_len = strlen($input);
-
- $this->Chunks();
- $this->genMessage();
- }
-
-
- /**
- * See less.js chunks()
- * We don't actually need the chunks
- *
- */
- protected function Chunks(){
- $level = 0;
- $parenLevel = 0;
- $lastMultiCommentEndBrace = null;
- $lastOpening = null;
- $lastMultiComment = null;
- $lastParen = null;
-
- for( $this->parserCurrentIndex = 0; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++ ){
- $cc = $this->CharCode($this->parserCurrentIndex);
- if ((($cc >= 97) && ($cc <= 122)) || ($cc < 34)) {
- // a-z or whitespace
- continue;
- }
-
- switch ($cc) {
-
- // (
- case 40:
- $parenLevel++;
- $lastParen = $this->parserCurrentIndex;
- continue;
-
- // )
- case 41:
- $parenLevel--;
- if( $parenLevel < 0 ){
- return $this->fail("missing opening `(`");
- }
- continue;
-
- // ;
- case 59:
- //if (!$parenLevel) { $this->emitChunk(); }
- continue;
-
- // {
- case 123:
- $level++;
- $lastOpening = $this->parserCurrentIndex;
- continue;
-
- // }
- case 125:
- $level--;
- if( $level < 0 ){
- return $this->fail("missing opening `{`");
-
- }
- //if (!$level && !$parenLevel) { $this->emitChunk(); }
- continue;
- // \
- case 92:
- if ($this->parserCurrentIndex < $this->input_len - 1) { $this->parserCurrentIndex++; continue; }
- return $this->fail("unescaped `\\`");
-
- // ", ' and `
- case 34:
- case 39:
- case 96:
- $matched = 0;
- $currentChunkStartIndex = $this->parserCurrentIndex;
- for ($this->parserCurrentIndex = $this->parserCurrentIndex + 1; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++) {
- $cc2 = $this->CharCode($this->parserCurrentIndex);
- if ($cc2 > 96) { continue; }
- if ($cc2 == $cc) { $matched = 1; break; }
- if ($cc2 == 92) { // \
- if ($this->parserCurrentIndex == $this->input_len - 1) {
- return $this->fail("unescaped `\\`");
- }
- $this->parserCurrentIndex++;
- }
- }
- if ($matched) { continue; }
- return $this->fail("unmatched `" . chr($cc) . "`", $currentChunkStartIndex);
-
- // /, check for comment
- case 47:
- if ($parenLevel || ($this->parserCurrentIndex == $this->input_len - 1)) { continue; }
- $cc2 = $this->CharCode($this->parserCurrentIndex+1);
- if ($cc2 == 47) {
- // //, find lnfeed
- for ($this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len; $this->parserCurrentIndex++) {
- $cc2 = $this->CharCode($this->parserCurrentIndex);
- if (($cc2 <= 13) && (($cc2 == 10) || ($cc2 == 13))) { break; }
- }
- } else if ($cc2 == 42) {
- // /*, find */
- $lastMultiComment = $currentChunkStartIndex = $this->parserCurrentIndex;
- for ($this->parserCurrentIndex = $this->parserCurrentIndex + 2; $this->parserCurrentIndex < $this->input_len - 1; $this->parserCurrentIndex++) {
- $cc2 = $this->CharCode($this->parserCurrentIndex);
- if ($cc2 == 125) { $lastMultiCommentEndBrace = $this->parserCurrentIndex; }
- if ($cc2 != 42) { continue; }
- if ($this->CharCode($this->parserCurrentIndex+1) == 47) { break; }
- }
- if ($this->parserCurrentIndex == $this->input_len - 1) {
- return $this->fail("missing closing `*/`", $currentChunkStartIndex);
- }
- }
- continue;
-
- // *, check for unmatched */
- case 42:
- if (($this->parserCurrentIndex < $this->input_len - 1) && ($this->CharCode($this->parserCurrentIndex+1) == 47)) {
- return $this->fail("unmatched `/*`");
- }
- continue;
- }
- }
-
- if( $level !== 0 ){
- if( ($lastMultiComment > $lastOpening) && ($lastMultiCommentEndBrace > $lastMultiComment) ){
- return $this->fail("missing closing `}` or `*/`", $lastOpening);
- } else {
- return $this->fail("missing closing `}`", $lastOpening);
- }
- } else if ( $parenLevel !== 0 ){
- return $this->fail("missing closing `)`", $lastParen);
- }
-
-
- //chunk didn't fail
-
-
- //$this->emitChunk(true);
- }
-
- public function CharCode($pos){
- return ord($this->input[$pos]);
- }
-
-
- public function fail( $msg, $index = null ){
-
- if( !$index ){
- $this->index = $this->parserCurrentIndex;
- }else{
- $this->index = $index;
- }
- $this->message = 'ParseError: '.$msg;
- }
-
-
- /*
- function emitChunk( $force = false ){
- $len = $this->parserCurrentIndex - $this->emitFrom;
- if ((($len < 512) && !$force) || !$len) {
- return;
- }
- $chunks[] = substr($this->input, $this->emitFrom, $this->parserCurrentIndex + 1 - $this->emitFrom );
- $this->emitFrom = $this->parserCurrentIndex + 1;
- }
- */
-
-}
-
-
-/**
- * Compiler Exception
- *
- * @package Less
- * @subpackage exception
- */
-class Less_Exception_Compiler extends Less_Exception_Parser{
-
-}
-
-/**
- * Parser output with source map
- *
- * @package Less
- * @subpackage Output
- */
-class Less_Output_Mapped extends Less_Output {
-
- /**
- * The source map generator
- *
- * @var Less_SourceMap_Generator
- */
- protected $generator;
-
- /**
- * Current line
- *
- * @var integer
- */
- protected $lineNumber = 0;
-
- /**
- * Current column
- *
- * @var integer
- */
- protected $column = 0;
-
- /**
- * Array of contents map (file and its content)
- *
- * @var array
- */
- protected $contentsMap = array();
-
- /**
- * Constructor
- *
- * @param array $contentsMap Array of filename to contents map
- * @param Less_SourceMap_Generator $generator
- */
- public function __construct(array $contentsMap, $generator){
- $this->contentsMap = $contentsMap;
- $this->generator = $generator;
- }
-
- /**
- * Adds a chunk to the stack
- * The $index for less.php may be different from less.js since less.php does not chunkify inputs
- *
- * @param string $chunk
- * @param string $fileInfo
- * @param integer $index
- * @param mixed $mapLines
- */
- public function add($chunk, $fileInfo = null, $index = 0, $mapLines = null){
-
- //ignore adding empty strings
- if( $chunk === '' ){
- return;
- }
-
-
- $sourceLines = array();
- $sourceColumns = ' ';
-
-
- if( $fileInfo ){
-
- $url = $fileInfo['currentUri'];
-
- if( isset($this->contentsMap[$url]) ){
- $inputSource = substr($this->contentsMap[$url], 0, $index);
- $sourceLines = explode("\n", $inputSource);
- $sourceColumns = end($sourceLines);
- }else{
- throw new Exception('Filename '.$url.' not in contentsMap');
- }
-
- }
-
- $lines = explode("\n", $chunk);
- $columns = end($lines);
-
- if($fileInfo){
-
- if(!$mapLines){
- $this->generator->addMapping(
- $this->lineNumber + 1, // generated_line
- $this->column, // generated_column
- count($sourceLines), // original_line
- strlen($sourceColumns), // original_column
- $fileInfo
- );
- }else{
- for($i = 0, $count = count($lines); $i < $count; $i++){
- $this->generator->addMapping(
- $this->lineNumber + $i + 1, // generated_line
- $i === 0 ? $this->column : 0, // generated_column
- count($sourceLines) + $i, // original_line
- $i === 0 ? strlen($sourceColumns) : 0, // original_column
- $fileInfo
- );
- }
- }
- }
-
- if(count($lines) === 1){
- $this->column += strlen($columns);
- }else{
- $this->lineNumber += count($lines) - 1;
- $this->column = strlen($columns);
- }
-
- // add only chunk
- parent::add($chunk);
- }
-
-}
-
-/**
- * Encode / Decode Base64 VLQ.
- *
- * @package Less
- * @subpackage SourceMap
- */
-class Less_SourceMap_Base64VLQ {
-
- /**
- * Shift
- *
- * @var integer
- */
- private $shift = 5;
-
- /**
- * Mask
- *
- * @var integer
- */
- private $mask = 0x1F; // == (1 << shift) == 0b00011111
-
- /**
- * Continuation bit
- *
- * @var integer
- */
- private $continuationBit = 0x20; // == (mask - 1 ) == 0b00100000
-
- /**
- * Char to integer map
- *
- * @var array
- */
- private $charToIntMap = array(
- 'A' => 0, 'B' => 1, 'C' => 2, 'D' => 3, 'E' => 4, 'F' => 5, 'G' => 6,
- 'H' => 7,'I' => 8, 'J' => 9, 'K' => 10, 'L' => 11, 'M' => 12, 'N' => 13,
- 'O' => 14, 'P' => 15, 'Q' => 16, 'R' => 17, 'S' => 18, 'T' => 19, 'U' => 20,
- 'V' => 21, 'W' => 22, 'X' => 23, 'Y' => 24, 'Z' => 25, 'a' => 26, 'b' => 27,
- 'c' => 28, 'd' => 29, 'e' => 30, 'f' => 31, 'g' => 32, 'h' => 33, 'i' => 34,
- 'j' => 35, 'k' => 36, 'l' => 37, 'm' => 38, 'n' => 39, 'o' => 40, 'p' => 41,
- 'q' => 42, 'r' => 43, 's' => 44, 't' => 45, 'u' => 46, 'v' => 47, 'w' => 48,
- 'x' => 49, 'y' => 50, 'z' => 51, 0 => 52, 1 => 53, 2 => 54, 3 => 55, 4 => 56,
- 5 => 57, 6 => 58, 7 => 59, 8 => 60, 9 => 61, '+' => 62, '/' => 63,
- );
-
- /**
- * Integer to char map
- *
- * @var array
- */
- private $intToCharMap = array(
- 0 => 'A', 1 => 'B', 2 => 'C', 3 => 'D', 4 => 'E', 5 => 'F', 6 => 'G',
- 7 => 'H', 8 => 'I', 9 => 'J', 10 => 'K', 11 => 'L', 12 => 'M', 13 => 'N',
- 14 => 'O', 15 => 'P', 16 => 'Q', 17 => 'R', 18 => 'S', 19 => 'T', 20 => 'U',
- 21 => 'V', 22 => 'W', 23 => 'X', 24 => 'Y', 25 => 'Z', 26 => 'a', 27 => 'b',
- 28 => 'c', 29 => 'd', 30 => 'e', 31 => 'f', 32 => 'g', 33 => 'h', 34 => 'i',
- 35 => 'j', 36 => 'k', 37 => 'l', 38 => 'm', 39 => 'n', 40 => 'o', 41 => 'p',
- 42 => 'q', 43 => 'r', 44 => 's', 45 => 't', 46 => 'u', 47 => 'v', 48 => 'w',
- 49 => 'x', 50 => 'y', 51 => 'z', 52 => '0', 53 => '1', 54 => '2', 55 => '3',
- 56 => '4', 57 => '5', 58 => '6', 59 => '7', 60 => '8', 61 => '9', 62 => '+',
- 63 => '/',
- );
-
- /**
- * Constructor
- */
- public function __construct(){
- // I leave it here for future reference
- // foreach(str_split('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/') as $i => $char)
- // {
- // $this->charToIntMap[$char] = $i;
- // $this->intToCharMap[$i] = $char;
- // }
- }
-
- /**
- * Convert from a two-complement value to a value where the sign bit is
- * is placed in the least significant bit. For example, as decimals:
- * 1 becomes 2 (10 binary), -1 becomes 3 (11 binary)
- * 2 becomes 4 (100 binary), -2 becomes 5 (101 binary)
- * We generate the value for 32 bit machines, hence -2147483648 becomes 1, not 4294967297,
- * even on a 64 bit machine.
- * @param string $aValue
- */
- public function toVLQSigned($aValue){
- return 0xffffffff & ($aValue < 0 ? ((-$aValue) << 1) + 1 : ($aValue << 1) + 0);
- }
-
- /**
- * Convert to a two-complement value from a value where the sign bit is
- * is placed in the least significant bit. For example, as decimals:
- * 2 (10 binary) becomes 1, 3 (11 binary) becomes -1
- * 4 (100 binary) becomes 2, 5 (101 binary) becomes -2
- * We assume that the value was generated with a 32 bit machine in mind.
- * Hence
- * 1 becomes -2147483648
- * even on a 64 bit machine.
- * @param integer $aValue
- */
- public function fromVLQSigned($aValue){
- return $aValue & 1 ? $this->zeroFill(~$aValue + 2, 1) | (-1 - 0x7fffffff) : $this->zeroFill($aValue, 1);
- }
-
- /**
- * Return the base 64 VLQ encoded value.
- *
- * @param string $aValue The value to encode
- * @return string The encoded value
- */
- public function encode($aValue){
- $encoded = '';
- $vlq = $this->toVLQSigned($aValue);
- do
- {
- $digit = $vlq & $this->mask;
- $vlq = $this->zeroFill($vlq, $this->shift);
- if($vlq > 0){
- $digit |= $this->continuationBit;
- }
- $encoded .= $this->base64Encode($digit);
- } while($vlq > 0);
-
- return $encoded;
- }
-
- /**
- * Return the value decoded from base 64 VLQ.
- *
- * @param string $encoded The encoded value to decode
- * @return integer The decoded value
- */
- public function decode($encoded){
- $vlq = 0;
- $i = 0;
- do
- {
- $digit = $this->base64Decode($encoded[$i]);
- $vlq |= ($digit & $this->mask) << ($i * $this->shift);
- $i++;
- } while($digit & $this->continuationBit);
-
- return $this->fromVLQSigned($vlq);
- }
-
- /**
- * Right shift with zero fill.
- *
- * @param integer $a number to shift
- * @param integer $b number of bits to shift
- * @return integer
- */
- public function zeroFill($a, $b){
- return ($a >= 0) ? ($a >> $b) : ($a >> $b) & (PHP_INT_MAX >> ($b - 1));
- }
-
- /**
- * Encode single 6-bit digit as base64.
- *
- * @param integer $number
- * @return string
- * @throws Exception If the number is invalid
- */
- public function base64Encode($number){
- if($number < 0 || $number > 63){
- throw new Exception(sprintf('Invalid number "%s" given. Must be between 0 and 63.', $number));
- }
- return $this->intToCharMap[$number];
- }
-
- /**
- * Decode single 6-bit digit from base64
- *
- * @param string $char
- * @return number
- * @throws Exception If the number is invalid
- */
- public function base64Decode($char){
- if(!array_key_exists($char, $this->charToIntMap)){
- throw new Exception(sprintf('Invalid base 64 digit "%s" given.', $char));
- }
- return $this->charToIntMap[$char];
- }
-
-}
-
-
-/**
- * Source map generator
- *
- * @package Less
- * @subpackage Output
- */
-class Less_SourceMap_Generator extends Less_Configurable {
-
- /**
- * What version of source map does the generator generate?
- */
- const VERSION = 3;
-
- /**
- * Array of default options
- *
- * @var array
- */
- protected $defaultOptions = array(
- // an optional source root, useful for relocating source files
- // on a server or removing repeated values in the 'sources' entry.
- // This value is prepended to the individual entries in the 'source' field.
- 'sourceRoot' => '',
-
- // an optional name of the generated code that this source map is associated with.
- 'sourceMapFilename' => null,
-
- // url of the map
- 'sourceMapURL' => null,
-
- // absolute path to a file to write the map to
- 'sourceMapWriteTo' => null,
-
- // output source contents?
- 'outputSourceFiles' => false,
-
- // base path for filename normalization
- 'sourceMapRootpath' => '',
-
- // base path for filename normalization
- 'sourceMapBasepath' => ''
- );
-
- /**
- * The base64 VLQ encoder
- *
- * @var Less_SourceMap_Base64VLQ
- */
- protected $encoder;
-
- /**
- * Array of mappings
- *
- * @var array
- */
- protected $mappings = array();
-
- /**
- * The root node
- *
- * @var Less_Tree_Ruleset
- */
- protected $root;
-
- /**
- * Array of contents map
- *
- * @var array
- */
- protected $contentsMap = array();
-
- /**
- * File to content map
- *
- * @var array
- */
- protected $sources = array();
- protected $source_keys = array();
-
- /**
- * Constructor
- *
- * @param Less_Tree_Ruleset $root The root node
- * @param array $options Array of options
- */
- public function __construct(Less_Tree_Ruleset $root, $contentsMap, $options = array()){
- $this->root = $root;
- $this->contentsMap = $contentsMap;
- $this->encoder = new Less_SourceMap_Base64VLQ();
-
- $this->SetOptions($options);
-
- $this->options['sourceMapRootpath'] = $this->fixWindowsPath($this->options['sourceMapRootpath'], true);
- $this->options['sourceMapBasepath'] = $this->fixWindowsPath($this->options['sourceMapBasepath'], true);
- }
-
- /**
- * Generates the CSS
- *
- * @return string
- */
- public function generateCSS(){
- $output = new Less_Output_Mapped($this->contentsMap, $this);
-
- // catch the output
- $this->root->genCSS($output);
-
-
- $sourceMapUrl = $this->getOption('sourceMapURL');
- $sourceMapFilename = $this->getOption('sourceMapFilename');
- $sourceMapContent = $this->generateJson();
- $sourceMapWriteTo = $this->getOption('sourceMapWriteTo');
-
- if( !$sourceMapUrl && $sourceMapFilename ){
- $sourceMapUrl = $this->normalizeFilename($sourceMapFilename);
- }
-
- // write map to a file
- if( $sourceMapWriteTo ){
- $this->saveMap($sourceMapWriteTo, $sourceMapContent);
- }
-
- // inline the map
- if( !$sourceMapUrl ){
- $sourceMapUrl = sprintf('data:application/json,%s', Less_Functions::encodeURIComponent($sourceMapContent));
- }
-
- if( $sourceMapUrl ){
- $output->add( sprintf('/*# sourceMappingURL=%s */', $sourceMapUrl) );
- }
-
- return $output->toString();
- }
-
- /**
- * Saves the source map to a file
- *
- * @param string $file The absolute path to a file
- * @param string $content The content to write
- * @throws Exception If the file could not be saved
- */
- protected function saveMap($file, $content){
- $dir = dirname($file);
- // directory does not exist
- if( !is_dir($dir) ){
- // FIXME: create the dir automatically?
- throw new Exception(sprintf('The directory "%s" does not exist. Cannot save the source map.', $dir));
- }
- // FIXME: proper saving, with dir write check!
- if(file_put_contents($file, $content) === false){
- throw new Exception(sprintf('Cannot save the source map to "%s"', $file));
- }
- return true;
- }
-
- /**
- * Normalizes the filename
- *
- * @param string $filename
- * @return string
- */
- protected function normalizeFilename($filename){
-
- $filename = $this->fixWindowsPath($filename);
-
- $rootpath = $this->getOption('sourceMapRootpath');
- $basePath = $this->getOption('sourceMapBasepath');
-
- // "Trim" the 'sourceMapBasepath' from the output filename.
- if (strpos($filename, $basePath) === 0) {
- $filename = substr($filename, strlen($basePath));
- }
-
- // Remove extra leading path separators.
- if(strpos($filename, '\\') === 0 || strpos($filename, '/') === 0){
- $filename = substr($filename, 1);
- }
-
- return $rootpath . $filename;
- }
-
- /**
- * Adds a mapping
- *
- * @param integer $generatedLine The line number in generated file
- * @param integer $generatedColumn The column number in generated file
- * @param integer $originalLine The line number in original file
- * @param integer $originalColumn The column number in original file
- * @param string $sourceFile The original source file
- */
- public function addMapping($generatedLine, $generatedColumn, $originalLine, $originalColumn, $fileInfo ){
-
- $this->mappings[] = array(
- 'generated_line' => $generatedLine,
- 'generated_column' => $generatedColumn,
- 'original_line' => $originalLine,
- 'original_column' => $originalColumn,
- 'source_file' => $fileInfo['currentUri']
- );
-
- $this->sources[$fileInfo['currentUri']] = $fileInfo['filename'];
- }
-
-
- /**
- * Generates the JSON source map
- *
- * @return string
- * @see https://docs.google.com/document/d/1U1RGAehQwRypUTovF1KRlpiOFze0b-_2gc6fAH0KY0k/edit#
- */
- protected function generateJson(){
-
- $sourceMap = array();
- $mappings = $this->generateMappings();
-
- // File version (always the first entry in the object) and must be a positive integer.
- $sourceMap['version'] = self::VERSION;
-
-
- // An optional name of the generated code that this source map is associated with.
- $file = $this->getOption('sourceMapFilename');
- if( $file ){
- $sourceMap['file'] = $file;
- }
-
-
- // An optional source root, useful for relocating source files on a server or removing repeated values in the 'sources' entry. This value is prepended to the individual entries in the 'source' field.
- $root = $this->getOption('sourceRoot');
- if( $root ){
- $sourceMap['sourceRoot'] = $root;
- }
-
-
- // A list of original sources used by the 'mappings' entry.
- $sourceMap['sources'] = array();
- foreach($this->sources as $source_uri => $source_filename){
- $sourceMap['sources'][] = $this->normalizeFilename($source_filename);
- }
-
-
- // A list of symbol names used by the 'mappings' entry.
- $sourceMap['names'] = array();
-
- // A string with the encoded mapping data.
- $sourceMap['mappings'] = $mappings;
-
- if( $this->getOption('outputSourceFiles') ){
- // An optional list of source content, useful when the 'source' can't be hosted.
- // The contents are listed in the same order as the sources above.
- // 'null' may be used if some original sources should be retrieved by name.
- $sourceMap['sourcesContent'] = $this->getSourcesContent();
- }
-
- // less.js compat fixes
- if( count($sourceMap['sources']) && empty($sourceMap['sourceRoot']) ){
- unset($sourceMap['sourceRoot']);
- }
-
- return json_encode($sourceMap);
- }
-
- /**
- * Returns the sources contents
- *
- * @return array|null
- */
- protected function getSourcesContent(){
- if(empty($this->sources)){
- return;
- }
- $content = array();
- foreach($this->sources as $sourceFile){
- $content[] = file_get_contents($sourceFile);
- }
- return $content;
- }
-
- /**
- * Generates the mappings string
- *
- * @return string
- */
- public function generateMappings(){
-
- if( !count($this->mappings) ){
- return '';
- }
-
- $this->source_keys = array_flip(array_keys($this->sources));
-
-
- // group mappings by generated line number.
- $groupedMap = $groupedMapEncoded = array();
- foreach($this->mappings as $m){
- $groupedMap[$m['generated_line']][] = $m;
- }
- ksort($groupedMap);
-
- $lastGeneratedLine = $lastOriginalIndex = $lastOriginalLine = $lastOriginalColumn = 0;
-
- foreach($groupedMap as $lineNumber => $line_map){
- while(++$lastGeneratedLine < $lineNumber){
- $groupedMapEncoded[] = ';';
- }
-
- $lineMapEncoded = array();
- $lastGeneratedColumn = 0;
-
- foreach($line_map as $m){
- $mapEncoded = $this->encoder->encode($m['generated_column'] - $lastGeneratedColumn);
- $lastGeneratedColumn = $m['generated_column'];
-
- // find the index
- if( $m['source_file'] ){
- $index = $this->findFileIndex($m['source_file']);
- if( $index !== false ){
- $mapEncoded .= $this->encoder->encode($index - $lastOriginalIndex);
- $lastOriginalIndex = $index;
-
- // lines are stored 0-based in SourceMap spec version 3
- $mapEncoded .= $this->encoder->encode($m['original_line'] - 1 - $lastOriginalLine);
- $lastOriginalLine = $m['original_line'] - 1;
-
- $mapEncoded .= $this->encoder->encode($m['original_column'] - $lastOriginalColumn);
- $lastOriginalColumn = $m['original_column'];
- }
- }
-
- $lineMapEncoded[] = $mapEncoded;
- }
-
- $groupedMapEncoded[] = implode(',', $lineMapEncoded) . ';';
- }
-
- return rtrim(implode($groupedMapEncoded), ';');
- }
-
- /**
- * Finds the index for the filename
- *
- * @param string $filename
- * @return integer|false
- */
- protected function findFileIndex($filename){
- return $this->source_keys[$filename];
- }
-
- /**
- * fix windows paths
- * @param string $path
- * @return string
- */
- public function fixWindowsPath($path, $addEndSlash = false){
- $slash = ($addEndSlash) ? '/' : '';
- if( !empty($path) ){
- $path = str_replace('\\', '/', $path);
- $path = rtrim($path,'/') . $slash;
- }
-
- return $path;
- }
-
-} -
\ No newline at end of file
diff --git a/util/Line.class.php b/util/Line.class.php
@@ -1,152 +0,0 @@
-<?php
-
-/**
- * Darstellung einer Zeile in einem Freitext.<br>
- * <br>
- * Im Konstruktor wird die Zeile analysiert und es wird festgestellt, was
- * die Zeile f�r einen Inhalt hat (z.B. ein Listenelement, eine �berschrift, usw.)<br>
- *
- * @author Jan Dankert
- * @version $Revision$
- * @package openrat.services
- */
-class Line
-{
- var $source; // Der urspr�ngliche Inhalt
- var $value; // Der textuelle Inhalt (sofern vorhanden)
-
- var $isDefinition = false; // Definitionseintrag
- var $isList = false; // nicht-numerierte Liste
- var $isNumberedList = false; // numerierte Liste
- var $indent = 0; // Einschubtiefe der Liste
-
- var $isHeadline = false; // �berschrift
- var $isHeadlineUnderline = false; // unterstrichene �berschrift
- var $headlineLevel = 0; // Grad der �berschrift (1,2,3...)
-
-
- var $isTableOfContent = false; // Inhaltsverzeichnis
- var $isTable = false; // Tabelle
- var $isCode = false; // Code
- var $isQuote = false; // Zitat
- var $isQuotePraefix = false; // Zitatbeginn/-ende
-
- var $isUnparsed = false;
-
- var $isEmpty = false; // Zeile ist leer
- var $isNormal = false; // Zeile ist ohne besonderen Inhalt, d.h. keine Tabelle, kein Zitat, usw.
-
-
-
- /**
- * Erzeugt einen Zeilenobjekt, der Text im Parameter wird dabei geparst.
- */
- function __construct( $s )
- {
- global $conf;
- $text_markup = $conf['editor']['text-markup'];
-// Html::debug($text_markup);
-
- $list_numbered = $text_markup['list-numbered' ];
- $list_unnumbered = $text_markup['list-unnumbered'];
- $headline = $text_markup['headline' ];
-
- $this->source = $s;
- $this->value = $s;
-
- $this->isList = $this->isAnErsterStelle(ltrim($s),$list_unnumbered);
- $this->isNumberedList = $this->isAnErsterStelle(ltrim($s),$list_numbered );
- $this->indent = strlen($s) - strlen(ltrim($s)) + 1;
-
- if ( $this->isList || $this->isNumberedList )
- $this->value = ltrim(substr($s,$this->indent));
-
- $this->level = strspn( $s,$headline );
- $this->isHeadline = $this->level >= 1;
-
- if ( $this->isHeadline )
- $this->value = ltrim(substr($s,$this->level));
-
-
- $hl = array( 1 => $text_markup['headline_level1_underline'],
- 2 => $text_markup['headline_level2_underline'],
- 3 => $text_markup['headline_level3_underline'] );
-
- foreach($hl as $lev=>$char )
- {
- if ( substr($s,0,3*strlen($char))==str_repeat($char,3*strlen($char)) )
- {
- $this->isHeadlineUnderline = true;
- $this->isNormal = true;
- $this->level = intval($lev);
- }
- }
-
- if ( $this->isAnErsterStelle($s,$text_markup['table-of-content']) )
- {
- $this->isTableOfContent = true;
- $this->isNumberedList = false;
- $this->isList = false;
- $this->value = '';
- }
- elseif
- ( $this->isHeadline ||
- $this->isHeadlineUnderline ||
- $this->isList ||
- $this->isNumberedList )
- {
- ;
- }
- elseif ( $this->isAnErsterStelle($s,$text_markup['table-cell-sep']) )
- {
- $this->isTable = true;
- $this->value = '';
- }
- elseif ( $this->isAnErsterStelle($s,$text_markup['pre-begin']) && !$this->isHeadlineUnderline )
- {
- $this->isCode = true;
- $this->value = substr($s,strlen($text_markup['pre-begin']));
- }
- elseif ( strpos($s, $text_markup['definition-sep'])!== false )
- {
- $this->isDefinition = true;
- }
- elseif ( trim($s)==$text_markup['quote-line-begin'] )
- {
- $this->isQuote = true;
- }
- elseif ( $this->isAnErsterStelle($s,$text_markup['quote']) && strlen(trim($s))>1 )
- {
- $this->isQuotePraefix = true;
- $this->value = ltrim(substr($s,strlen($text_markup['quote'])));
- $this->level = strspn( str_replace(' ','',$s),$text_markup['quote'] );
- }
- elseif ( $this->isAnErsterStelle($s,'`') )
- {
- $this->isUnparsed = true;
- $this->value = substr($this->value,1);
- }
- elseif ( $s == '' )
- {
- $this->isEmpty = true;
- }
- else
- {
- $this->isNormal = true;
- }
- }
-
-
- /**
- * Stellt fest, ob $wort am Anfang von $text steht.
- */
- function isAnErsterStelle( $text,$wort )
- {
-// Html::debug($text,"Text");
-// Html::debug($wort,"Wort");
-// Html::debug(substr($text,0,strlen($wort))==$wort,'Ergebnis');
- return substr($text,0,strlen($wort))==$wort;
- }
-}
-
-?>-
\ No newline at end of file
diff --git a/util/Macro.class.php b/util/Macro.class.php
@@ -1,194 +0,0 @@
-<?php
-// OpenRat Content Management System
-// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-use cms\model\Page;
-
-
-/**
- * Service-Klasse fuer allgemeine Interface-Methoden. Insbesondere
- * in Code-Elementen kann und soll auf diese Methoden zurueckgegriffen
- * werden.
- * @author $Author$
- * @version $Revision$
- * @package openrat.services
- */
-class Macro
-{
- var $project;
- var $output = '';
- var $objectid = 0;
-
- /**
- * @var Page
- */
- var $page;
- var $parameters = array();
- var $description = '';
-
-
- /**
- * Ausführen des Makros. Diese Methode sollte durch die Unterklasse überschrieben werden.
- */
- public function execute()
- {
- // overwrite this in subclasses
- }
-
-
- /**
- * Holt die aktuellen Datenbankverbindung.
- */
- public function db()
- {
- return db_connection();
- }
-
-
- /**
- * Holt die aktuelle Objekt-Id.
- * @return number
- */
- public function getObjectId()
- {
- return $this->objectid;
- }
-
-
- /**
- * Holt die aktuelle Seite.
- * @return \cms\model\Page
- */
- public function getPage()
- {
- return new Page( $this->objectid );
- }
-
-
- /**
- * Holt das aktuelle Objekt.
- * @return Object
- */
- public function &getObject()
- {
- return Session::getObject();
- }
-
-
- /**
- * Setzt eine Objekt-Id.
- * @param int $objectid
- */
- public function setObjectId( $objectid )
- {
- $this->objectid = $objectid;
- }
-
-
- /**
- * Ermittelt die Id des Wurzelordners im aktuellen Projekt.
- */
- public function getRootObjectId()
- {
- $project = Session::getProject();
- return $project->getRootObjectId();
- }
-
-
- /**
- * DO NOT USE.
- */
- public function folderid()
- {
- global $SESS;
- return $SESS['folderid'];
- }
-
-
- /**
- * Löscht die bisher erzeugte Ausgabe.
- */
- public function delOutput()
- {
- $this->output = '';
- }
-
- /**
- * Ergänzt die Standardausgabe um den gewünschten Wert.
- */
- public function output( $text )
- {
- $this->output .= $text;
- }
-
-
- /**
- * Ergänzt die Standardausgabe um den gewünschten Wert. Es wird ein Zeilenendezeichen ergänzt.
- */
- public function outputLn( $text )
- {
- $this->output .= $text."\n";
- }
-
-
- /**
- * Ermittelt die bisher erstellte Ausgabe.
- * @return string
- */
- public function getOutput()
- {
- return $this->output;
- }
-
-
- /**
- * Setzt eine Sitzungsvariable.
- *
- * @param String $var
- * @param Object $value
- */
- public function setSessionVar( $var,$value )
- {
- Session::set( $var,$value );
- }
-
-
- /**
- * Ermittelt eine Sitzungsvariable.
- *
- * @param String $var
- * @return string
- */
- public function getSessionVar( $var )
- {
- return Session::get( $var );
- }
-
-
- /**
- * Ermittelt den Pfad auf ein Objekt.
- * @param Object
- * @return string
- */
- public function pathToObject( $obj )
- {
- if ( is_object($obj) )
- return $this->page->path_to_object($obj->objectid);
- else
- return $this->page->path_to_object($obj);
- }
-
-}-
\ No newline at end of file
diff --git a/util/Mail.class.php b/util/Mail.class.php
@@ -1,571 +0,0 @@
-<?php
-// OpenRat Content Management System
-// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-/**
- * Erzeugen und Versender einer E-Mail gemaess RFC 822.<br>
- * <br>
- * Die E-Mail kann entweder �ber
- * - die interne PHP-Funktion "mail()" versendet werden oder
- * - direkt per SMTP-Protokoll an einen SMTP-Server.<br>
- * Welcher Weg gew�hlt wird, kann konfiguriert werden.<br>
- * <br>
- * Prinzipiell spricht nichts gegen die interne PHP-Funktion mail(), wenn diese
- * aber nicht zu Verf�gung steht oder PHP ungeeignet konfiguriert ist, so kann
- * SMTP direkt verwendet werden. Hierbei sollte wenn m�glich ein Relay-Host
- * eingesetzt werden. Die Mail kann zwar auch direkt an Mail-Exchanger (MX) des
- * Empf�ngers geschickt werden, falls dieser aber Greylisting einsetzt ist eine
- * Zustellung nicht m�glich.<br>
- * <br>
- *
- * @author Jan Dankert
- * @version $Id$
- * @package serviceClasses
- */
-class Mail
-{
- var $from = '';
- var $to = '';
- var $bcc = '';
- var $cc = '';
- var $subject = '';
- var $text = '';
- var $header = array();
- var $nl = '';
-
- /**
- * Falls beim Versendern der E-Mail etwas schiefgeht, steht hier drin
- * die technische Fehlermeldung.
- *
- * @var String Fehler
- */
- var $error = array();
-
- /**
- * Set to true for debugging.
- * If true, All SMTP-Commands are written to error log.
- *
- * @var unknown_type
- */
- var $debug = true;
-
-
- /**
- * Konstruktor.
- * Es werden folgende Parameter erwartet
- * @param String $to Empf�nger
- * @param String der Textschl�ssel
- * @param String unbenutzt.
- * @return Mail
- */
- function Mail( $to,$text,$xy='' )
- {
- global $conf;
-
- // Zeilenumbruch CR/LF gem. RFC 822.
- $this->nl = chr(13).chr(10);
-
- if ( !empty($conf['mail']['from']) )
- $this->from = $this->header_encode($conf['mail']['from']);
-
- // Priorit�t definieren (sofern konfiguriert)
- if ( !empty($conf['mail']['priority']) )
- $this->header[] = 'X-Priority: '.$conf['mail']['priority'];
-
- $this->header[] = 'X-Mailer: '.$this->header_encode(OR_TITLE.' '.OR_VERSION);
- $this->header[] = 'Content-Type: text/plain; charset='.lang( 'CHARSET' );
- $this->subject = $this->header_encode(lang( 'mail_subject_'.$text ));
- $this->to = $this->header_encode($to);
-
- $this->text = $this->nl.wordwrap(str_replace(';',$this->nl,lang('mail_text_'.$text)),70,$this->nl).$this->nl;
-
- // Signatur anhaengen (sofern konfiguriert)
- if ( !empty($conf['mail']['signature']) )
- {
- $this->text .= $this->nl.'-- '.$this->nl;
- $this->text .= str_replace(';',$this->nl,$conf['mail']['signature']);
- $this->text .= $this->nl;
- }
-
- // Kopie-Empf�nger
- if ( !empty($conf['mail']['cc']) )
- $this->cc = $this->header_encode($conf['mail']['cc']);
-
- // Blindkopie-Empf�nger
- if ( !empty($conf['mail']['bcc']) )
- $this->bcc = $this->header_encode($conf['mail']['bcc']);
- }
-
-
-
- /**
- * Kodiert einen Text in das Format "Quoted-printable".<br>
- * See RFC 2045.
- * @param String $text Eingabe
- * @return Text im quoted-printable-Format
- */
- function quoted_printable_encode( $text )
- {
- $text = str_replace(' ','=20',$text);
-
- for( $i=128; $i<=255; $i++ )
- {
- $text = str_replace( chr($i),'='.dechex($i),$text );
- }
-
- return $text;
- }
-
-
-
- /**
- * Setzen einer Variablen in den Mail-Inhalt.
- */
- function setVar( $varName,$varInhalt)
- {
- $this->text = str_replace( '{'.$varName.'}', $varInhalt, $this->text );
- }
-
-
- /**
- * Mail absenden.
- * Die E-Mail wird versendet.
- *
- * @return boolean Erfolg
- */
- function send()
- {
- global $conf;
-
- $to_domain = array_pop( explode('@',$this->to) );
-
- // Prüfen gegen die Whitelist
- $white = @$conf['mail']['whitelist'];
-
- if ( !empty($white) )
- {
- if ( ! $this->containsDomain($to_domain,$white) )
- {
- // Wenn Domain nicht in Whitelist gefunden, dann Mail nicht verschicken.
- $this->error[] = 'Mail-Domain is not whitelisted';
- return false;
- }
- }
-
- // Prüfen gegen die Blacklist
- $black = @$conf['mail']['blacklist'];
-
- if ( !empty($black) )
- {
- if ( $this->containsDomain($to_domain,$black) )
- {
- // Wenn Domain in Blacklist gefunden, dann Mail nicht verschicken.
- $this->error[] = 'Mail-Domain is blacklisted';
- return false;
- }
- }
-
- // Header um Adressangaben erg�nzen.
- if ( !empty($this->from ) )
- $this->header[] = 'From: '.$this->from;
-
- if ( !empty($this->cc ) )
- $this->header[] = 'Cc: '.$this->cc;
-
- if ( !empty($this->bcc ) )
- $this->header[] = 'Bcc: '.$this->bcc;
-
- // Mail versenden
- if ( strtolower(@$conf['mail']['client']) == 'php' )
- {
- // PHP-interne Mailfunktion verwenden.
- $result = @mail( $this->to, // Empf�nger
- $this->subject, // Betreff
- $this->text, // Inhalt
- // Lt. RFC822 müssen Header mit CRLF enden.
- // ABER: Der Parameter "additional headers" verlangt offenbar \n am Zeilenende.
- implode("\n",$this->header) );
- if ( !$result )
- // Die E-Mail wurde nicht akzeptiert.
- // Genauer geht es leider nicht, da mail() nur einen boolean-Wert
- // zur�ck liefert.
- $this->error[] = 'Mail was NOT accepted by mail()';
-
- return $result;
- }
- else
- {
- // eigenen SMTP-Dialog verwenden.
- $smtpConf = $conf['mail']['smtp'];
-
- if ( !empty($smtpConf['host']))
- {
- // Eigenen Relay-Host verwenden.
- $mxHost = $smtpConf['host'];
- $mxPort = intval($smtpConf['port']);
- }
- else
- {
- // Mail direkt zustellen.
- $mxHost = $this->getMxHost($this->to);
-
- if ( empty($mxHost) )
- {
- $this->error[] = "No MX-Entry found. Mail could not be sent.";
- return false;
- }
-
- if ($smtpConf['ssl'])
- $mxPort = 465;
- else
- $mxPort = 25;
- }
-
-
- if ( !empty($smtpConf['localhost']))
- {
- $myHost = $smtpConf['localhost'];
- }
- else
- {
- $myHost = gethostbyaddr(getenv('REMOTE_ADDR'));
- }
-
- if ( $smtpConf['ssl'])
- $proto = 'ssl';
- else
- $proto = 'tcp';
-
- //connect to the host and port
- $smtpSocket = fsockopen($proto.'://'.$mxHost,$mxPort, $errno, $errstr, intval($smtpConf['timeout']));
-
- if ( !is_resource($smtpSocket) )
- {
- $this->error[] = 'Connection failed to: '.$proto.'://'.$mxHost.':'.$mxPort.' ('.$errstr.'/'.$errno.')';
- return false;
- }
-
- $smtpResponse = fgets($smtpSocket, 4096);
- if ( $this->debug)
- $this->error[] = trim($smtpResponse);
-
- if ( substr($smtpResponse,0,3) != '220' )
- {
- $this->error[] = 'No 220: '.trim($smtpResponse);
- return false;
- }
-
- if ( !is_resource($smtpSocket) )
- {
- $this->error[] = 'Connection failed to: '.$smtpConf['host'].':'.$smtpConf['port'].' ('.$smtpResponse.')';
- return false;
- }
-
- //you have to say HELO again after TLS is started
- $smtpResponse = $this->sendSmtpCommand($smtpSocket,'HELO '.$myHost);
-
- if ( substr($smtpResponse,0,3) != '250' )
- {
- $this->error[] = "No 2xx after HELO, server says: ".$smtpResponse;
- $this->sendSmtpQuit($smtpSocket);
- return false;
- }
-
- if ( $smtpConf['tls'] )
- {
- $smtpResponse = $this->sendSmtpCommand($smtpSocket,'STARTTLS');
- if ( substr($smtpResponse,0,3) == '220' )
- {
- // STARTTLS ist gelungen.
- //you have to say HELO again after TLS is started
- $smtpResponse = $this->sendSmtpCommand($smtpSocket,'HELO '.$myHost);
-
- if ( substr($smtpResponse,0,3) != '250' )
- {
- $this->error[] = "No 2xx after HELO, server says: ".$smtpResponse;
- $this->sendSmtpQuit($smtpSocket);
- return false;
- }
- }
- else
- {
- // STARTTLS ging in die Hose. Einfach weitermachen.
- }
- }
-
- // request for auth login
- if ( isset($smtpConf['auth_username']) && !empty($smtpConf['host']) && !empty($smtpConf['auth_username']))
- {
- $smtpResponse = $this->sendSmtpCommand($smtpSocket,"AUTH LOGIN");
- if ( substr($smtpResponse,0,3) != '334' )
- {
- $this->error[] = "No 334 after AUTH_LOGIN, server says: ".$smtpResponse;
- $this->sendSmtpQuit($smtpSocket);
- return false;
- }
-
- if ( $this->debug)
- $this->error[] = 'Login for '.$smtpConf['auth_username'];
-
- //send the username
- $smtpResponse = $this->sendSmtpCommand($smtpSocket, base64_encode($smtpConf['auth_username']));
- if ( substr($smtpResponse,0,3) != '334' )
- {
- $this->error[] = "No 3xx after setting username, server says: ".$smtpResponse;
- $this->sendSmtpQuit($smtpSocket);
- return false;
- }
-
- //send the password
- $smtpResponse = $this->sendSmtpCommand($smtpSocket, base64_encode($smtpConf['auth_password']));
- if ( substr($smtpResponse,0,3) != '235' )
- {
- $this->error[] = "No 235 after sending password, server says: ".$smtpResponse;
- $this->sendSmtpQuit($smtpSocket);
- return false;
- }
- }
-
- //email from
- $smtpResponse = $this->sendSmtpCommand($smtpSocket, 'MAIL FROM: <'.$conf['mail']['from'].'>');
- if ( substr($smtpResponse,0,3) != '250' )
- {
- $this->error[] = "No 2xx after MAIL_FROM, server says: ".$smtpResponse;
- $this->sendSmtpQuit($smtpSocket);
- return false;
- }
-
- //email to
- $smtpResponse = $this->sendSmtpCommand($smtpSocket, 'RCPT TO: <'.$this->to.'>');
- if ( substr($smtpResponse,0,3) != '250' )
- {
- $this->error[] = "No 2xx after RCPT_TO, server says: ".$smtpResponse;
- $this->sendSmtpQuit($smtpSocket);
- return false;
- }
-
- //the email
- $smtpResponse = $this->sendSmtpCommand($smtpSocket, "DATA");
- if ( substr($smtpResponse,0,3) != '354' )
- {
- $this->error[] = "No 354 after DATA, server says: ".$smtpResponse;
- $this->sendSmtpQuit($smtpSocket);
- return false;
- }
-
- $this->header[] = 'To: '.$this->to;
- $this->header[] = 'Subject: '.$this->subject;
- $this->header[] = 'Date: '.date('r');
- $this->header[] = 'Message-Id: '.'<'.getenv('REMOTE_ADDR').'.'.time().'.openrat@'.getenv('SERVER_NAME').'.'.getenv('HOSTNAME').'>';
-
- //observe the . after the newline, it signals the end of message
- $smtpResponse = $this->sendSmtpCommand($smtpSocket, implode($this->nl,$this->header).$this->nl.$this->nl.$this->text.$this->nl.'.');
- if ( substr($smtpResponse,0,3) != '250' )
- {
- $this->error[] = "No 2xx after putting DATA, server says: ".$smtpResponse;
- $this->sendSmtpQuit($smtpSocket);
- return false;
- }
-
- // say goodbye
- $this->sendSmtpQuit($smtpSocket);
- return true;
- }
- }
-
-
- /**
- * Sendet ein SMTP-Kommando zum SMTP-Server.
- *
- * @access private
- * @param Resource $socket TCP/IP-Socket zum SMTP-Server
- * @param unknown_type $cmd SMTP-Kommando
- * @return Server-Antwort
- */
- function sendSmtpCommand( $socket,$cmd )
- {
- if ( $this->debug )
- $this->error[] = 'CLIENT: >>> '.trim($cmd);
- if ( !is_resource($socket) )
- {
- // Die Verbindung ist geschlossen. Dies kann bei dieser
- // Implementierung eigentlich nur dann passieren, wenn
- // der Server die Verbindung schlie�t.
- // Dieser Client trennt die Verbindung nur nach einem "QUIT".
- $this->error[] = "Connection lost";
- return;
- }
-
- fputs($socket,$cmd.$this->nl);
- $response = trim(fgets($socket, 4096));
- if ( $this->debug )
- $this->error[] = 'SERVER: <<< '.$response;
- return $response;
- }
-
-
-
- /**
- * Sendet ein QUIT zum SMTP-Server, wartet die Antwort ab und
- * schlie�t danach die Verbindung.
- *
- * @param Resource Socket
- */
- function sendSmtpQuit( $socket )
- {
-
- if ( $this->debug )
- $this->error[] = "CLIENT: >>> QUIT";
- if ( !is_resource($socket) )
- return;
- // Wenn die Verbindung nicht mehr da ist, brauchen wir
- // auch kein QUIT mehr :)
-
-
- fputs($socket,'QUIT'.$this->nl);
- $response = trim(fgets($socket, 4096));
- if ( $this->debug )
- $this->error[] = 'SERVER: <<< '.$response;
-
- if ( substr($response,0,3) != '221' )
- $this->error[] = 'QUIT FAILED: '.$response;
-
- fclose($socket);
- }
-
-
-
- /**
- * Umwandlung von 8-bit-Zeichen in MIME-Header gemaess RFC 2047.<br>
- * Header d�rfen nur 7-bit-Zeichen enthalten. 8-bit-Zeichen m�ssen kodiert werden.
- *
- * @param String $text
- * @return String
- */
- function header_encode( $text )
- {
- global $conf;
-
- if ( empty($conf['mail']['header_encoding']) )
- return $text;
-
- $woerter = explode(' ',$text);
- $neu = array();
-
-
- foreach( $woerter as $wort )
- {
- $type = strtolower(substr($conf['mail']['header_encoding'],0,1));
- $neu_wort = '';
-
- if ( $type == 'b' )
- $neu_wort = base64_encode($wort);
- elseif ( $type == 'q' )
- $neu_wort = $this->quoted_printable_encode($wort);
- else
- Logger::error( 'Mail-Configuratin broken: UNKNOWN Header-Encoding type: '.$type);
-
- if ( strlen($wort)==strlen($neu_wort) )
- $neu[] = $wort;
- else
- $neu[] = '=?'.lang('CHARSET').'?'.$type.'?'.$neu_wort.'?=';
- }
-
- return implode(' ',$neu);
- }
-
-
- /**
- * Ermittelt den MX-Eintrag zu einer E-Mail-Adresse.<br>
- * Es wird der Eintrag mit der h�chsten Priorit�t ermittelt.
- *
- * @param String E-Mail-Adresse des Empf�ngers.
- * @return MX-Eintrag
- */
- function getMxHost( $to )
- {
- list($user,$host) = explode('@',$to.'@');
-
- if ( empty($host) )
- {
- $this->error[] = 'Illegal mail address - No hostname found.';
- return "";
- }
-
- list($host) = explode('>',$host);
-
- $mxHostsName = array();
- $mxHostsPrio = array();
- getmxrr($host,$mxHostsName,$mxHostsPrio);
-
- $mxList = array();
- foreach( $mxHostsName as $id=>$mxHostName )
- {
- $mxList[$mxHostName] = $mxHostsPrio[$id];
- }
- asort($mxList);
- return key($mxList);
- }
-
-
-
- /**
- * Stellt fest, ob die E-Mail-Adresse eine gueltige Syntax besitzt.
- *
- * Es wird nur die Syntax geprüft. Ob die Adresse wirklich existiert, steht dadurch noch lange
- * nicht fest. Dazu müsste man die MX-Records auflösen und einen Zustellversuch unternehmen.
- *
- * @param $email_address Adresse
- * @return true, falls Adresse OK, sonst false
- */
- function checkAddress( $email_address )
- {
- // Source: de.php.net/ereg
- return ereg("^[-A-Za-z0-9_]+[-A-Za-z0-9_.]*[@]{1}[-A-Za-z0-9_]+[-A-Za-z0-9_.]*[.]{1}[A-Za-z]{2,5}$", $email_address);
- }
-
-
-
- /**
- * Prüft, ob eine Domain in einer List von Domains enthalten ist.
- *
- * @param $checkDomain zu prüfende Domain
- * @param $domain_list Liste von Domains als kommaseparierte Liste
- * @return true, falls vorhanden, sonst false
- */
- function containsDomain($checkDomain, $domain_list)
- {
- $domains = explode(',',$domain_list);
-
- foreach( $domains as $domain )
- {
- $domain = trim($domain);
-
- if (empty($domain))
- continue;
-
- if ($domain == substr($checkDomain,-strlen($domain)))
- {
- return true;
- }
- }
- return false;
- }
-}
-
-
-?>
diff --git a/util/ProjectTree.class.php b/util/ProjectTree.class.php
@@ -1,515 +0,0 @@
-<?php
-use cms\model\Value;
-use cms\model\Element;
-use cms\model\Template;
-use cms\model\Page;
-use cms\model\Folder;
-use cms\model\Object;
-use cms\model\File;
-use cms\model\Link;
-
-// OpenRat Content Management System
-// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-/**
- * Darstellen der Projektstruktur
- * @author $Author$
- * @version $Revision$
- * @package openrat.services
- */
-class ProjectTree extends AbstractTree
-{
- var $projectId;
- var $userIsProjectAdmin = false;
-
- function root()
- {
- $treeElement = new TreeElement();
- $treeElement->text = lang('GLOBAL_PROJECT');
- $treeElement->description = lang('GLOBAL_PROJECT');
- $treeElement->type = 'project';
- $treeElement->icon = 'project';
-
- $this->addTreeElement( $treeElement );
- }
-
-
-
- function page( $id )
- {
- $page = new Page( $id );
- $page->load();
-
- $template = new Template( $page->templateid );
-
- foreach( $template->getElementIds() as $elementid )
- {
- $element = new Element( $elementid );
- $element->load();
-
- if ( $element->isWritable() )
- {
- $treeElement = new TreeElement();
- $treeElement->id = $id.'_'.$elementid;
- $treeElement->extraId['elementid'] = $elementid;
- $treeElement->text = $element->name;
- $treeElement->url = Html::url('pageelement','edit',
- $id.'_'.$elementid,
- array('elementid'=>$elementid,
- REQ_PARAM_TARGETSUBACTION=>'edit',REQ_PARAM_TARGET=>'content'));
- $treeElement->action = 'pageelement';
- $treeElement->icon = 'el_'.$element->type;
-
- $treeElement->description = lang('EL_'.$element->type);
- if ( $element->desc != '' )
- $treeElement->description .= ' - '.Text::maxLaenge( 25,$element->desc );
- else
- $treeElement->description .= ' - '.lang('GLOBAL_NO_DESCRIPTION_AVAILABLE');
- $treeElement->target = 'content';
-
- if ( in_array($element->type,array('link','list','include') ) )
- {
- $treeElement->type = 'value';
- $value = new Value();
- $value->pageid = $page->pageid;
- $value->element = $element;
- $value->load();
- $treeElement->internalId = $value->valueid;
- }
-
- $this->addTreeElement( $treeElement );
- }
- }
- }
-
-
- function value( $id )
- {
- //echo "id: $id";
- if ( $id != 0 )
- {
- $value = new Value();
- $value->loadWithId( $id );
-
- $objectid = intval($value->linkToObjectId);
- if ( $objectid != 0 )
- {
- $object = new Object( $objectid );
- $object->load();
-
- $treeElement = new TreeElement();
- $treeElement->id = $id;
- $treeElement->text = $object->name;
- if ( in_array($object->getType(),array('page','folder')))
- {
- $treeElement->type = $object->getType();
- $treeElement->internalId = $object->objectid;
- }
- $treeElement->url = Html::url($object->getType(),'',$objectid,array(REQ_PARAM_TARGET=>'content'));
- $treeElement->action = $object->getType();
- $treeElement->icon = $object->getType();
-
- $treeElement->description = lang('GLOBAL_'.$object->getType());
- if ( $object->desc != '' )
- $treeElement->description .= ' - '.Text::maxLaenge( 25,$object->desc );
- else
- $treeElement->description .= ' - '.lang('GLOBAL_NO_DESCRIPTION_AVAILABLE');
- $treeElement->target = 'content';
-
- $this->addTreeElement( $treeElement );
- }
- }
- }
-
-
- function link( $id )
- {
- $link = new Link( $id );
- $link->load();
-
- if ( $link->isLinkToObject )
- {
- $o = new Object( $link->linkedObjectId );
- $o->load();
-
- $treeElement = new TreeElement();
- $treeElement->id = $o->objectid;
- $treeElement->internalId = $o->objectid;
- $treeElement->target = 'content';
- $treeElement->text = $o->name;
- $treeElement->description= lang( 'GLOBAL_'.$o->getType() ).' '.$id;
-
- if ( $o->desc != '' )
- $treeElement->description .= ': '.$o->desc;
- else
- $treeElement->description .= ' - '.lang('GLOBAL_NO_DESCRIPTION_AVAILABLE');
-
- $treeElement->url = Html::url($o->getType(),'',$o->objectid,array(REQ_PARAM_TARGET=>'content') );
- $treeElement->action = $o->getType();
- $treeElement->icon = $o->getType();
-
- // Besonderheiten fuer bestimmte Objekttypen
-
- if ( $o->isPage )
- {
- // Nur wenn die Seite beschreibbar ist, werden die
- // Elemente im Baum angezeigt
- if ( $o->hasRight( ACL_WRITE ) )
- $treeElement->type='pageelements';
- }
- $this->addTreeElement( $treeElement );
- }
- }
-
-
- /**
- * Laedt Elemente zu einem Ordner
- * @return Array
- */
- function folder( $id )
- {
- global
- $SESS,
- $projectid;
-
- $f = new Folder( $id );
- $t = time();
-
- foreach( $f->getObjects() as $o )
- {
- // Wenn keine Leseberechtigung
- if ( !$o->hasRight( ACL_READ ) )
- continue;
-
- $treeElement = new TreeElement();
- $treeElement->id = $o->objectid;
- $treeElement->internalId = $o->objectid;
- $treeElement->target = 'content';
- $treeElement->text = $o->name;
- $treeElement->description= lang( 'GLOBAL_'.$o->getType() ).' '.$o->objectid;
-
- if ( $o->desc != '' )
- $treeElement->description .= ': '.$o->desc;
- else
- $treeElement->description .= ' - '.lang('GLOBAL_NO_DESCRIPTION_AVAILABLE');
-
- $treeElement->url = Html::url( $o->getType(),'',$o->objectid,array('readit'=>'__OID__'.$o->objectid.'__',REQ_PARAM_TARGET=>'content') );
- $treeElement->action = $o->getType();
- $treeElement->icon = $o->getType();
-
- // Besonderheiten fuer bestimmte Objekttypen
-
- if ( $o->isLink )
- {
- $treeElement->type='link';
- }
-
- if ( $o->isPage )
- {
- // Nur wenn die Seite beschreibbar ist, werden die
- // Elemente im Baum angezeigt
- if ( $o->hasRight( ACL_WRITE ) )
- $treeElement->type='page';
- }
-
- if ( $o->isFile )
- {
- $file = new File( $o->objectid );
- $file->load();
-
- if ( substr($file->mimeType(),0,6) == 'image/' )
- $treeElement->icon = 'image';
- else $treeElement->icon = 'file';
- }
-
- if ( $o->isFolder )
- {
- $treeElement->type = 'folder';
- }
-
-
- $this->addTreeElement( $treeElement );
- }
- }
-
-
- function project()
- {
- $language = Session::getProjectLanguage();
- $model = Session::getProjectModel();
- $user = Session::getUser();
-
- $project = Session::getProject();
- $this->projectid = $project->projectid;
-
- // Hoechster Ordner der Projektstruktur
- $folder = new Folder( $project->getRootObjectId() );
- $folder->load();
-
-
- // Ermitteln, ob der Benutzer Projektadministrator ist
- // Projektadministratoren haben das Recht, im Root-Ordner die Eigenschaften zu aendern.
- if ( $folder->hasRight( ACL_PROP ) )
- $this->userIsProjectAdmin = true;
-
- if ( $folder->hasRight( ACL_READ ) )
- {
- $treeElement = new TreeElement();
- $treeElement->id = $folder->objectid;
- // $treeElement->text = $folder->name;
- $treeElement->text = lang('FOLDER_ROOT');
- $treeElement->description = lang('FOLDER_ROOT_DESC');
- $treeElement->icon = 'folder';
- $treeElement->action = 'folder';
- $treeElement->url = Html::url( 'folder','',$folder->objectid,array(REQ_PARAM_TARGET=>'content') );
- $treeElement->target = 'content';
- $treeElement->type = 'folder';
- $treeElement->internalId = $folder->objectid;
- $this->addTreeElement( $treeElement );
- }
-
-
- if ( $this->userIsProjectAdmin )
- {
- // Templates
- $treeElement = new TreeElement();
- $treeElement->id = 0;
- $treeElement->text = lang('GLOBAL_TEMPLATES');
- $treeElement->url = Html::url('template','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content'));
- $treeElement->description= lang('GLOBAL_TEMPLATES_DESC');
- $treeElement->icon = 'templatelist';
- $treeElement->action = 'templatelist';
- $treeElement->target = 'content';
- $treeElement->type = 'templates';
- $this->addTreeElement( $treeElement );
- }
-
-
- // Sprachen
- $treeElement = new TreeElement();
- $treeElement->description= '';
- $treeElement->id = 0;
- $treeElement->action = 'languagelist';
- $treeElement->text = lang('GLOBAL_LANGUAGES');
- $treeElement->url = Html::url('language','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content'));
- $treeElement->icon = 'languagelist';
- $treeElement->description= lang('GLOBAL_LANGUAGES_DESC');
- $treeElement->target = 'content';
-
- // Nur fuer Projekt-Administratoren aufklappbar
- if ( $this->userIsProjectAdmin )
- $treeElement->type = 'languages';
-
- $this->addTreeElement( $treeElement );
-
-
- // Projektmodelle
- $treeElement = new TreeElement();
- $treeElement->description= '';
-
- // Nur fuer Projekt-Administratoren aufklappbar
- if ( $this->userIsProjectAdmin )
- $treeElement->type = 'models';
-
- $treeElement->id = 0;
- $treeElement->description= lang('GLOBAL_MODELS_DESC');
- $treeElement->text = lang('GLOBAL_MODELS');
- $treeElement->url = Html::url('model','listing',0,array(REQ_PARAM_TARGETSUBACTION=>'listing',REQ_PARAM_TARGET=>'content'));
- $treeElement->action = 'modellist';
- $treeElement->icon = 'modellist';
- $treeElement->target = 'content';
- $this->addTreeElement( $treeElement );
-
-
- // Sonstiges
-// $treeElement = new TreeElement();
-// $treeElement->text = lang('GLOBAL_OTHER');
-// $treeElement->description= lang('GLOBAL_OTHER_DESC');
-// $treeElement->icon = 'other';
-// $treeElement->type = 'other';
-// $this->addTreeElement( $treeElement );
-
- // Suche
- $treeElement = new TreeElement();
- $treeElement->id = 0;
- $treeElement->text = lang('GLOBAL_SEARCH');
- $treeElement->url = Html::url('search','',0,array(REQ_PARAM_TARGET=>'content'));
- $treeElement->action = 'search';
- $treeElement->icon = 'search';
- $treeElement->description = lang('GLOBAL_SEARCH_DESC');
- $treeElement->target = 'content';
- $this->addTreeElement( $treeElement );
-
- }
-
-
- function templates()
- {
- foreach( Template::getAll() as $id=>$name )
- {
- $treeElement = new TreeElement();
-
- $t = new Template( $id );
- $t->load();
- $treeElement->text = $t->name;
- $treeElement->id = $id;
- $treeElement->url = Html::url('template','src',$id,array(REQ_PARAM_TARGETSUBACTION=>'src',REQ_PARAM_TARGET=>'content'));
- $treeElement->icon = 'template';
- $treeElement->action = 'template';
- $treeElement->target = 'content';
- $treeElement->internalId = $id;
- $treeElement->type = 'template';
- $treeElement->description = $t->name.' ('.lang('GLOBAL_TEMPLATE').' '.$id.'): '.htmlentities(Text::maxLaenge( 40,$t->src ));
- $this->addTreeElement( $treeElement );
- }
- }
-
-
- function template( $id )
- {
-
- $t = new Template( $id );
- $t->load();
-
- // Anzeigen der Template-Elemente
- //
- foreach( $t->getElementIds() as $elementid )
- {
- $e = new Element( $elementid );
- $e->load();
-
- // "Code"-Element nur fuer Administratoren
- if ( $e->type == 'code' && !$this->userIsAdmin )
- continue;
-
- $treeElement = new TreeElement();
- $treeElement->id = $elementid;
- $treeElement->text = $e->name;
- $treeElement->url = Html::url('element','',$elementid,array(REQ_PARAM_TARGET=>'content') );
- $treeElement->icon = 'el_'.$e->type;
- $treeElement->action = 'element';
-
- if ( $e->desc == '' )
- $desc = lang('GLOBAL_NO_DESCRIPTION_AVAILABLE');
- else
- $desc = $e->desc;
- $treeElement->description = $e->name.' ('.lang('EL_'.$e->type).'): '.Text::maxLaenge( 40,$desc );
- $treeElement->target = 'content';
- $this->addTreeElement( $treeElement );
- }
- }
-
-
- /**
- * Sprachen
- */
- function languages()
- {
- // Sprachvarianten
- //
- $l = Session::getProjectLanguage();
- $languages = $l->getAll();
-
- foreach( $languages as $languageid=>$name )
- {
- $treeElement = new TreeElement();
- $treeElement->id = $languageid;
- $treeElement->text = $name;
- $treeElement->url = Html::url('language','edit',$languageid,
- array(REQ_PARAM_TARGETSUBACTION=>'edit',REQ_PARAM_TARGET=>'content') );
- $treeElement->icon = 'language';
- $treeElement->action = 'language';
- $treeElement->description = '';
- $treeElement->target = 'content';
- $this->addTreeElement( $treeElement );
- }
- }
-
-
- // Projektvarianten
- //
- function models()
- {
- $m = Session::getProjectModel();
- $models = $m->getAll();
-
- foreach( $models as $id=>$name )
- {
- $treeElement = new TreeElement();
- $treeElement->id = $id;
- $treeElement->text = $name;
- $treeElement->url = Html::url('model','edit',$id,
- array(REQ_PARAM_TARGETSUBACTION=>'edit',REQ_PARAM_TARGET=>'content'));
- $treeElement->action = 'model';
- $treeElement->icon = 'model';
- $treeElement->description = '';
- $treeElement->target = 'content';
- $this->addTreeElement( $treeElement );
- }
- }
-
-
- function other()
- {
-// Deaktiviert, da
-// - Dateien auf den Server laden unverst�ndlich/undurchsichtig erscheint
-// - M�glichkeit zum Entpacken von ZIP/TAR online besteht.
-// if ( $this->userIsProjectAdmin )
-// {
-// $treeElement = new TreeElement();
-// $treeElement->text = lang('GLOBAL_FILE_TRANSFER');
-// $treeElement->description = lang('GLOBAL_FILE_TRANSFER_DESC');
-// $treeElement->url = Html::url('main','transfer');
-// $treeElement->icon = 'transfer';
-// $treeElement->target = 'content';
-// $this->addTreeElement( $treeElement );
-// }
-
- $treeElement = new TreeElement();
- $treeElement->id = 0;
- $treeElement->text = lang('GLOBAL_SEARCH');
- $treeElement->url = Html::url('search');
- $treeElement->icon = 'search';
- $treeElement->action = 'search';
- $treeElement->description = lang('GLOBAL_SEARCH_DESC');
- $treeElement->target = 'content';
- $this->addTreeElement( $treeElement );
-
-
- $treeElement = new TreeElement();
- $treeElement->id = 0;
- $treeElement->text = lang('USER_YOURPROFILE');
- $treeElement->url = Html::url('profile','edit',0,array(REQ_PARAM_TARGET=>'content'));
- $treeElement->icon = 'user';
- $treeElement->action = 'profile';
- $treeElement->description = lang('USER_PROFILE_DESC');
- $treeElement->target = 'content';
- $this->addTreeElement( $treeElement );
-
-
- $treeElement = new TreeElement();
- $treeElement->id = 0;
- $treeElement->text = lang('GLOBAL_PROJECTS');
- $treeElement->url = Html::url('index','projectmenu',0,array(REQ_PARAM_TARGET=>'content'));
- $treeElement->icon = 'project';
- $treeElement->description = lang('GLOBAL_PROJECTS');
- $treeElement->target = 'content';
- $this->addTreeElement( $treeElement );
- }
-}
-
-?>-
\ No newline at end of file
diff --git a/util/Publish.class.php b/util/Publish.class.php
@@ -1,393 +0,0 @@
-<?php
-// OpenRat Content Management System
-// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-/**
- * Diese Klasse kapselt das Veroeffentlichen von Dateien.<br>
- * <br>
- * Hier werden<br>
- * - Dateien in das Zielverzeichnis kopiert<br>
- * - Dateien per FTP veroeffentlicht<br>
- * - Zielverzeichnisse aufgeraeumt<br>
- * - Systembefehle ausgefuehrt.
- *
- * @author Jan Dankert
- * @package openrat.services
- */
-class Publish
-{
- /**
- * Enthaelt bei Bedarf das FTP-Objekt. N�mlich dann, wenn
- * zu einem FTP-Server veroeffentlicht werden soll.
- * @var Object
- */
- var $ftp;
-
- /**
- * Flag, ob in das lokale Dateisystem veroeffentlicht werden soll.
- * @var boolean
- */
- var $with_local = false;
-
- /**
- * Flag, ob zu einem FTP-Server ver�ffentlicht werden soll.
- * @var boolean
- */
- var $with_ftp = false;
-
- var $local_destdir = '';
-
- /**
- * Enthaelt die gleichnamige Einstellung aus dem Projekt.
- * @var boolean
- */
- var $content_negotiation = false;
-
- /**
- * Enthaelt die gleichnamige Einstellung aus dem Projekt.
- * @var boolean
- */
- var $cut_index = false;
-
- /**
- * Enthaelt die gleichnamige Einstellung aus dem Projekt.
- * @var String
- */
- var $cmd_after_publish = '';
-
- /**
- * Enthaelt am Ende der Ver�ffentlichung ein Array mit den ver�ffentlichten Objekten.
- * @var Array
- */
- var $publishedObjects = array();
-
- /**
- * Enthaelt im Fehlerfall (wenn 'ok' auf 'false' steht) eine
- * Fehlermeldung.
- *
- * @var String
- */
- var $log = array();
-
- /**
- * Stellt nach der Ver�ffentlichung fest, ob der Vorgang erfolgreich ist.
- * Falls nicht, enth�lt die Variable 'log' eine Fehlermeldung.
- * @var boolean
- */
- var $ok = true;
-
- /**
- * Konstruktor.<br>
- * <br>
- * Oeffnet ggf. Verbindungen.
- *
- * @return Publish
- */
- function __construct()
- {
- global $conf;
- $confPublish = $conf['publish'];
-
- if ( $conf['security']['nopublish'] )
- {
- $this->ok = false;
- $this->log[] = 'publishing is disabled.';
- return;
- }
-
- $project = Session::getProject();
-
- // Feststellen, ob FTP benutzt wird.
- // Dazu muss FTP aktiviert sein (enable=true) und eine URL vorhanden sein.
- $ftpUrl = '';
- if ( $conf['publish']['ftp']['enable'] )
- {
- if ( $conf['publish']['ftp']['per_project'] && !empty($project->ftp_url) )
- $ftpUrl = $project->ftp_url;
- elseif ( !empty($conf['publish']['ftp']['host']) )
- $ftpUrl = $project->ftp_url;
- }
-
- if ( ! empty($ftpUrl) )
- {
- $this->with_ftp = true;
- $this->ftp = new Ftp( $project->ftp_url ); // Aufbauen einer FTP-Verbindung
-
- if ( ! $this->ftp->ok ) // FTP-Verbindung ok?
- {
- $this->ok = false;
- $this->log = $this->ftp->log;
- return; // Ende. Ohne FTP brauchen wir nicht weitermachen.
- }
-
- $this->ftp->passive = ( $project->ftp_passive == '1' );
- }
-
- $localDir = rtrim( $project->target_dir,'/' );
-
- if ( $confPublish['filesystem']['per_project'] && (!empty($localDir)) )
- {
- $this->local_destdir = $localDir; // Projekteinstellung verwenden.
- }
- else
- {
- if ( empty( $localDir))
- $localDir = $project->name;
- // Konfiguriertes Verzeichnis verwenden.
- $this->local_destdir = $confPublish['filesystem']['directory'].$localDir;
- }
-
-
- // Sofort pruefen, ob das Zielverzeichnis ueberhaupt beschreibbar ist.
- if ( $this->local_destdir != '' )
- {
- if ( !is_writeable( $this->local_destdir ) )
- {
- $this->ok = false;
- $this->log[] = 'directory not writable: '.$this->local_destdir;
- $this->log[] = 'please correct the file permissions.';
- return;
- }
-
- $this->with_local = true;
- }
-
- $this->content_negotiation = ( $project->content_negotiation == '1' );
- $this->cut_index = ( $project->cut_index == '1' );
-
- if ( $confPublish['command']['enable'] )
- {
- if ( $confPublish['command']['per_project'] && !empty($project->cmd_after_publish) )
- $this->cmd_after_publish = $project->cmd_after_publish;
- else
- $this->cmd_after_publish = @$confPublish['command']['command'];
- }
-
- // Im Systemkommando Variablen ersetzen
- $this->cmd_after_publish = str_replace('{name}' ,$project->name ,$this->cmd_after_publish);
- $this->cmd_after_publish = str_replace('{dir}' ,$this->local_destdir ,$this->cmd_after_publish);
- $this->cmd_after_publish = str_replace('{dirbase}',basename($this->local_destdir),$this->cmd_after_publish);
- }
-
-
-
- /**
- * Kopieren einer Datei aus dem tempor�ren Verzeichnis in das Zielverzeichnis.<br>
- * Falls notwenig, wird ein Hochladen per FTP ausgef�hrt.
- *
- * @param String $tmp_filename
- * @param String $dest_filename
- */
- function copy( $tmp_filename,$dest_filename,$lastChangeDate=null )
- {
- if ( !$this->ok)
- return;
-
- global $conf;
- $source = $tmp_filename;
-
- if ( $this->with_local )
- {
- $dest = $this->local_destdir.'/'.$dest_filename;
-
- if (!@copy( $source,$dest ));
- {
- if ( ! $this->mkdirs( dirname($dest) ) )
- return; // Fehler bei Verzeichniserstellung, also abbrechen.
-
- if (!@copy( $source,$dest ))
- {
- $this->ok = false;
- $this->log[] = 'failed copying local file:';
- $this->log[] = 'source : '.$source;
- $this->log[] = 'destination: '.$dest;
- return; // Fehler beim Kopieren, also abbrechen.
- }
- if ( ! is_null($lastChangeDate) )
- @touch( $dest,$lastChangeDate );
-
- Logger::debug("published: $dest");
- }
-
- if (!empty($conf['security']['chmod']))
- {
- // CHMOD auf der Datei ausfuehren.
- if ( ! @chmod($dest,octdec($conf['security']['chmod'])) )
- {
- $this->ok = false;
- $this->log[] = 'Unable to CHMOD file '.$dest;
- return;
- }
- }
- }
-
- if ( $this->with_ftp ) // Falls FTP aktiviert
- {
- $dest = $dest_filename;
- $this->ftp->put( $source,$dest );
-
- if ( ! $this->ftp->ok )
- {
- $this->ok = false;
- $this->log[] = $this->ftp->log;
- }
- }
- }
-
-
-
- /**
- * Rekursives Anlagen von Verzeichnisse
- * Nett gemacht.
- * Quelle: http://de3.php.net/manual/de/function.mkdir.php
- * Thx to acroyear at io dot com
- *
- * @param String Verzeichnis
- * @return boolean
- */
- function mkdirs( $strPath )
- {
- global $conf;
-
- if ( is_dir($strPath) )
- return true;
-
- $pStrPath = dirname($strPath);
- if ( !$this->mkdirs($pStrPath) )
- return false;
-
- if ( ! @mkdir($strPath,0777) )
- {
- $this->ok = false;
- $this->log[] = 'Cannot create directory: '.$strPath;
- return false;
- }
-
- // CHMOD auf dem Verzeichnis ausgef�hren.
- if (!empty($conf['security']['chmod_dir']))
- {
- if ( ! @chmod($strPath,octdec($conf['security']['chmod_dir'])) )
- {
- $this->ok = false;
- $this->log[] = 'Unable to CHMOD directory: '.$strPath;
- return false;
- }
- }
-
-
- return $this->ok;
- }
-
-
-
- /**
- * Beenden des Ver�ffentlichungs-Vorganges.<br>
- * Eine vorhandene FTP-Verbindung wird geschlossen.<br>
- * Falls entsprechend konfiguriert, wird ein Systemkommando ausgef�hrt.
- */
- public function close()
- {
- if ( $this->with_ftp )
- {
- Logger::debug('Closing FTP connection' );
- $this->ftp->close();
- }
-
- // Ausfuehren des Systemkommandos.
- if ( !empty($this->cmd_after_publish) && $this->ok )
- {
- $ausgabe = array();
- $rc = false;
- Logger::debug('Executing system command: '.$this->cmd_after_publish );
- $user = Session::getUser();
- putenv("CMS_USER_NAME=".$user->name );
- putenv("CMS_USER_ID=" .$user->userid);
- putenv("CMS_USER_MAIL=".$user->mail );
- exec( $this->cmd_after_publish,$ausgabe,$rc );
-
- if ( $rc != 0 ) // Wenn Returncode ungleich 0, dann Ausgabe ins Log schreiben und Fehler melden.
- {
- $this->log = $ausgabe;
- $this->log[] = 'OpenRat: System command failed - returncode is '.$rc;
- $this->ok = false;
-
- Logger::warn('System command '.$this->cmd_after_publish.' failed with status '.$rc );
-
- }
- else
- {
- Logger::debug('System command successful' );
- }
-
- }
- }
-
-
-
- /**
- * Aufraeumen des Zielverzeichnisses.<br><br>
- * Es wird der komplette Zielordner samt Unterverzeichnissen durchsucht. Jede
- * Datei, die laenger existiert als der aktuelle Request alt ist, wird geloescht.<br>
- * Natuerlich darf diese Funktion nur nach einem Gesamt-Veroeffentlichen ausgefuehrt werden.
- */
- function clean()
- {
- if ( $this->ok )
- return;
-
- if ( !empty($this->local_destdir) )
- $this->cleanFolder($this->local_destdir);
- }
-
-
-
- /**
- * Aufr�umen eines Verzeichnisses.<br><br>
- * Dateien, die l�nger existieren als der aktuelle Request alt ist, werden gel�scht.<br>
- *
- * @param String Verzeichnis
- */
- function cleanFolder( $folderName )
- {
- $dh = opendir( $folderName );
-
- while( $file = readdir($dh) )
- {
- if ( $file != '.' && $file != '..')
- {
- $fullpath = $folderName.'/'.$file;
-
- // Wenn eine Datei beschreibbar und entsprechend alt
- // ist, dann entfernen
- if ( is_file($fullpath) &&
- is_writable($fullpath) &&
- filemtime($fullpath) < START_TIME )
- unlink($fullpath);
-
- // Bei Ordnern rekursiv absteigen
- if ( is_dir( $fullpath) )
- {
- $this->cleanFolder($fullpath);
- @rmdir($fullpath);
- }
- }
- }
- }
-
-}
-
-?>-
\ No newline at end of file
diff --git a/util/Session.class.php b/util/Session.class.php
@@ -1,242 +0,0 @@
-<?php
-// OpenRat Content Management System
-// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-
-// PHP-Versionsunabhaengiges Array fuer die Session-Variablen ermitteln
-use cms\model\User;
-
-if (isset($_SESSION))
- $SESS = &$_SESSION;
-else $SESS = &$HTTP_SESSION_VARS;
-
-if ( isset($_FILES) )
- $FILES = &$_FILES;
-else $FILES = &$HTTP_POST_FILES;
-
-
-/**
- * Session-Funktionen zum Lesen/Schreiben in/von HTTP-Session
- * In der Session werden folgende Daten abgelegt
- * - Ausgewaehltes Projekt
- * - Ausgewaehlte Projectsprache
- * - Ausgewaehlte Projektvariante
- * - Angemeldeter Benutzer
- * - Auswahlbaum
- * - Geladene Sprachelemente
- * - Ausgewaehlter Ordner
- * - Ausgewaehltes Objekt
- * - Datenbankobjekt
- * Die Methoden dieser Klassen koennen statisch aufgerufen werden
- *
- * @author $Author$
- * @version $Revision$
- * @package openrat.service
- */
-
-class Session
-{
- public static function get( $var )
- {
- global $SESS;
- if ( isset($SESS['ors_'.$var]) )
- return $SESS['ors_'.$var];
- else
- return '';
- }
-
- public static function set( $var,$value )
- {
- global $SESS;
- $SESS[ 'ors_'.$var ] = $value;
- }
-
-
- /**
- * @return array
- */
- public static function getConfig()
- {
- return Session::get('config');
- }
-
- public static function setConfig( $var )
- {
- Session::set('config',$var);
- }
-
-
- /**
- * @return \cms\model\Model
- */
- public static function getProjectModel()
- {
- return Session::get('project_model');
- }
-
- public static function setProjectModel( $var )
- {
- Session::set('project_model',$var);
- }
-
-
- /**
- * @return \cms\model\Language
- */
- public static function getProjectLanguage()
- {
- return Session::get('project_language');
- }
-
- public static function setProjectLanguage( $var )
- {
- Session::set('project_language',$var);
- }
-
-
-
- public static function getObject()
- {
- return Session::get('object');
- }
-
- public static function setObject( $var )
- {
- Session::set('object',$var);
- }
-
-
- /**
- * @return \cms\model\Folder
- */
- public static function getFolder()
- {
- return Session::get('folder');
- }
-
- public static function setFolder( $var )
- {
- Session::set('folder',$var);
- }
-
-
-
- public static function getTree()
- {
- return Session::get('tree');
- }
-
- public static function setTree( $var )
- {
- Session::set('tree',$var);
- }
-
-
-
- public static function getElement()
- {
- return Session::get('element');
- }
-
- public static function setElement( $var )
- {
- Session::set('element',$var);
- }
-
-
- /**
- * @return \cms\model\Project
- */
- public static function getProject()
- {
- return Session::get('project');
- }
-
- public static function setProject( $var )
- {
- Session::set('project',$var);
- }
-
-
- /**
- * @return User
- */
- public static function getUser()
- {
- return Session::get('userObject');
- }
-
- public static function setUser( $var )
- {
- Session::set('userObject',$var);
- }
-
-
- /**
- * @return \database\Database
- */
- public static function getDatabase()
- {
- return Session::get('database');
- }
-
- public static function setDatabase( $var )
- {
- Session::set('database',$var);
- }
-
-
- /**
- * @return string
- */
- public static function getSubaction()
- {
- return Session::get('subaction');
- }
-
- public static function setSubaction( $var )
- {
- Session::set('subaction',$var);
- }
-
-
- public static function getClipboard()
- {
- return Session::get('clipboard');
- }
-
- public static function setClipboard( $var )
- {
- Session::set('clipboard',$var);
- }
-
-
- /**
- * Schliesst die aktuelle Session
- *
- * Diese Funktion sollte so schnell wie moeglich aufgerufen werden, da vorher
- * keine andere Seite (im Frameset!) geladen werden kann
- * Nach Aufruf dieser Methode sind keine Session-Zugriffe ueber diese Klasse mehr
- * moeglich.
- */
- public static function close()
- {
- session_write_close();
- }
-}
-
-?>-
\ No newline at end of file
diff --git a/util/Spyc.class.php b/util/Spyc.class.php
@@ -1,1161 +0,0 @@
-<?php
-/**
- * Spyc -- A Simple PHP YAML Class
- * @version 0.6.2
- * @author Vlad Andersen <vlad.andersen@gmail.com>
- * @author Chris Wanstrath <chris@ozmm.org>
- * @link https://github.com/mustangostang/spyc/
- * @copyright Copyright 2005-2006 Chris Wanstrath, 2006-2011 Vlad Andersen
- * @license http://www.opensource.org/licenses/mit-license.php MIT License
- * @package Spyc
- */
-
-if (!function_exists('spyc_load')) {
- /**
- * Parses YAML to array.
- * @param string $string YAML string.
- * @return array
- */
- function spyc_load ($string) {
- return Spyc::YAMLLoadString($string);
- }
-}
-
-if (!function_exists('spyc_load_file')) {
- /**
- * Parses YAML to array.
- * @param string $file Path to YAML file.
- * @return array
- */
- function spyc_load_file ($file) {
- return Spyc::YAMLLoad($file);
- }
-}
-
-if (!function_exists('spyc_dump')) {
- /**
- * Dumps array to YAML.
- * @param array $data Array.
- * @return string
- */
- function spyc_dump ($data) {
- return Spyc::YAMLDump($data, false, false, true);
- }
-}
-
-if (!class_exists('Spyc')) {
-
-/**
- * The Simple PHP YAML Class.
- *
- * This class can be used to read a YAML file and convert its contents
- * into a PHP array. It currently supports a very limited subsection of
- * the YAML spec.
- *
- * Usage:
- * <code>
- * $Spyc = new Spyc;
- * $array = $Spyc->load($file);
- * </code>
- * or:
- * <code>
- * $array = Spyc::YAMLLoad($file);
- * </code>
- * or:
- * <code>
- * $array = spyc_load_file($file);
- * </code>
- * @package Spyc
- */
-class Spyc {
-
- // SETTINGS
-
- const REMPTY = "\0\0\0\0\0";
-
- /**
- * Setting this to true will force YAMLDump to enclose any string value in
- * quotes. False by default.
- *
- * @var bool
- */
- public $setting_dump_force_quotes = false;
-
- /**
- * Setting this to true will forse YAMLLoad to use syck_load function when
- * possible. False by default.
- * @var bool
- */
- public $setting_use_syck_is_possible = false;
-
-
-
- /**#@+
- * @access private
- * @var mixed
- */
- private $_dumpIndent;
- private $_dumpWordWrap;
- private $_containsGroupAnchor = false;
- private $_containsGroupAlias = false;
- private $path;
- private $result;
- private $LiteralPlaceHolder = '___YAML_Literal_Block___';
- private $SavedGroups = array();
- private $indent;
- /**
- * Path modifier that should be applied after adding current element.
- * @var array
- */
- private $delayedPath = array();
-
- /**#@+
- * @access public
- * @var mixed
- */
- public $_nodeId;
-
-/**
- * Load a valid YAML string to Spyc.
- * @param string $input
- * @return array
- */
- public function load ($input) {
- return $this->_loadString($input);
- }
-
- /**
- * Load a valid YAML file to Spyc.
- * @param string $file
- * @return array
- */
- public function loadFile ($file) {
- return $this->_load($file);
- }
-
- /**
- * Load YAML into a PHP array statically
- *
- * The load method, when supplied with a YAML stream (string or file),
- * will do its best to convert YAML in a file into a PHP array. Pretty
- * simple.
- * Usage:
- * <code>
- * $array = Spyc::YAMLLoad('lucky.yaml');
- * print_r($array);
- * </code>
- * @access public
- * @return array
- * @param string $input Path of YAML file or string containing YAML
- */
- public static function YAMLLoad($input) {
- $Spyc = new Spyc;
- return $Spyc->_load($input);
- }
-
- /**
- * Load a string of YAML into a PHP array statically
- *
- * The load method, when supplied with a YAML string, will do its best
- * to convert YAML in a string into a PHP array. Pretty simple.
- *
- * Note: use this function if you don't want files from the file system
- * loaded and processed as YAML. This is of interest to people concerned
- * about security whose input is from a string.
- *
- * Usage:
- * <code>
- * $array = Spyc::YAMLLoadString("---\n0: hello world\n");
- * print_r($array);
- * </code>
- * @access public
- * @return array
- * @param string $input String containing YAML
- */
- public static function YAMLLoadString($input) {
- $Spyc = new Spyc;
- return $Spyc->_loadString($input);
- }
-
- /**
- * Dump YAML from PHP array statically
- *
- * The dump method, when supplied with an array, will do its best
- * to convert the array into friendly YAML. Pretty simple. Feel free to
- * save the returned string as nothing.yaml and pass it around.
- *
- * Oh, and you can decide how big the indent is and what the wordwrap
- * for folding is. Pretty cool -- just pass in 'false' for either if
- * you want to use the default.
- *
- * Indent's default is 2 spaces, wordwrap's default is 40 characters. And
- * you can turn off wordwrap by passing in 0.
- *
- * @access public
- * @return string
- * @param array|\stdClass $array PHP array
- * @param int $indent Pass in false to use the default, which is 2
- * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
- * @param bool $no_opening_dashes Do not start YAML file with "---\n"
- */
- public static function YAMLDump($array, $indent = false, $wordwrap = false, $no_opening_dashes = false) {
- $spyc = new Spyc;
- return $spyc->dump($array, $indent, $wordwrap, $no_opening_dashes);
- }
-
-
- /**
- * Dump PHP array to YAML
- *
- * The dump method, when supplied with an array, will do its best
- * to convert the array into friendly YAML. Pretty simple. Feel free to
- * save the returned string as tasteful.yaml and pass it around.
- *
- * Oh, and you can decide how big the indent is and what the wordwrap
- * for folding is. Pretty cool -- just pass in 'false' for either if
- * you want to use the default.
- *
- * Indent's default is 2 spaces, wordwrap's default is 40 characters. And
- * you can turn off wordwrap by passing in 0.
- *
- * @access public
- * @return string
- * @param array $array PHP array
- * @param int $indent Pass in false to use the default, which is 2
- * @param int $wordwrap Pass in 0 for no wordwrap, false for default (40)
- */
- public function dump($array,$indent = false,$wordwrap = false, $no_opening_dashes = false) {
- // Dumps to some very clean YAML. We'll have to add some more features
- // and options soon. And better support for folding.
-
- // New features and options.
- if ($indent === false or !is_numeric($indent)) {
- $this->_dumpIndent = 2;
- } else {
- $this->_dumpIndent = $indent;
- }
-
- if ($wordwrap === false or !is_numeric($wordwrap)) {
- $this->_dumpWordWrap = 40;
- } else {
- $this->_dumpWordWrap = $wordwrap;
- }
-
- // New YAML document
- $string = "";
- if (!$no_opening_dashes) $string = "---\n";
-
- // Start at the base of the array and move through it.
- if ($array) {
- $array = (array)$array;
- $previous_key = -1;
- foreach ($array as $key => $value) {
- if (!isset($first_key)) $first_key = $key;
- $string .= $this->_yamlize($key,$value,0,$previous_key, $first_key, $array);
- $previous_key = $key;
- }
- }
- return $string;
- }
-
- /**
- * Attempts to convert a key / value array item to YAML
- * @access private
- * @return string
- * @param $key The name of the key
- * @param $value The value of the item
- * @param $indent The indent of the current node
- */
- private function _yamlize($key,$value,$indent, $previous_key = -1, $first_key = 0, $source_array = null) {
- if(is_object($value)) $value = (array)$value;
- if (is_array($value)) {
- if (empty ($value))
- return $this->_dumpNode($key, array(), $indent, $previous_key, $first_key, $source_array);
- // It has children. What to do?
- // Make it the right kind of item
- $string = $this->_dumpNode($key, self::REMPTY, $indent, $previous_key, $first_key, $source_array);
- // Add the indent
- $indent += $this->_dumpIndent;
- // Yamlize the array
- $string .= $this->_yamlizeArray($value,$indent);
- } elseif (!is_array($value)) {
- // It doesn't have children. Yip.
- $string = $this->_dumpNode($key, $value, $indent, $previous_key, $first_key, $source_array);
- }
- return $string;
- }
-
- /**
- * Attempts to convert an array to YAML
- * @access private
- * @return string
- * @param $array The array you want to convert
- * @param $indent The indent of the current level
- */
- private function _yamlizeArray($array,$indent) {
- if (is_array($array)) {
- $string = '';
- $previous_key = -1;
- foreach ($array as $key => $value) {
- if (!isset($first_key)) $first_key = $key;
- $string .= $this->_yamlize($key, $value, $indent, $previous_key, $first_key, $array);
- $previous_key = $key;
- }
- return $string;
- } else {
- return false;
- }
- }
-
- /**
- * Returns YAML from a key and a value
- * @access private
- * @return string
- * @param $key The name of the key
- * @param $value The value of the item
- * @param $indent The indent of the current node
- */
- private function _dumpNode($key, $value, $indent, $previous_key = -1, $first_key = 0, $source_array = null) {
- // do some folding here, for blocks
- if (is_string ($value) && ((strpos($value,"\n") !== false || strpos($value,": ") !== false || strpos($value,"- ") !== false ||
- strpos($value,"*") !== false || strpos($value,"#") !== false || strpos($value,"<") !== false || strpos($value,">") !== false || strpos ($value, '%') !== false || strpos ($value, ' ') !== false ||
- strpos($value,"[") !== false || strpos($value,"]") !== false || strpos($value,"{") !== false || strpos($value,"}") !== false) || strpos($value,"&") !== false || strpos($value, "'") !== false || strpos($value, "!") === 0 ||
- substr ($value, -1, 1) == ':')
- ) {
- $value = $this->_doLiteralBlock($value,$indent);
- } else {
- $value = $this->_doFolding($value,$indent);
- }
-
- if ($value === array()) $value = '[ ]';
- if ($value === "") $value = '""';
- if (self::isTranslationWord($value)) {
- $value = $this->_doLiteralBlock($value, $indent);
- }
- if (trim ($value) != $value)
- $value = $this->_doLiteralBlock($value,$indent);
-
- if (is_bool($value)) {
- $value = $value ? "true" : "false";
- }
-
- if ($value === null) $value = 'null';
- if ($value === "'" . self::REMPTY . "'") $value = null;
-
- $spaces = str_repeat(' ',$indent);
-
- //if (is_int($key) && $key - 1 == $previous_key && $first_key===0) {
- if (is_array ($source_array) && array_keys($source_array) === range(0, count($source_array) - 1)) {
- // It's a sequence
- $string = $spaces.'- '.$value."\n";
- } else {
- // if ($first_key===0) throw new Exception('Keys are all screwy. The first one was zero, now it\'s "'. $key .'"');
- // It's mapped
- if (strpos($key, ":") !== false || strpos($key, "#") !== false) { $key = '"' . $key . '"'; }
- $string = rtrim ($spaces.$key.': '.$value)."\n";
- }
- return $string;
- }
-
- /**
- * Creates a literal block for dumping
- * @access private
- * @return string
- * @param $value
- * @param $indent int The value of the indent
- */
- private function _doLiteralBlock($value,$indent) {
- if ($value === "\n") return '\n';
- if (strpos($value, "\n") === false && strpos($value, "'") === false) {
- return sprintf ("'%s'", $value);
- }
- if (strpos($value, "\n") === false && strpos($value, '"') === false) {
- return sprintf ('"%s"', $value);
- }
- $exploded = explode("\n",$value);
- $newValue = '|';
- if (isset($exploded[0]) && ($exploded[0] == "|" || $exploded[0] == "|-" || $exploded[0] == ">")) {
- $newValue = $exploded[0];
- unset($exploded[0]);
- }
- $indent += $this->_dumpIndent;
- $spaces = str_repeat(' ',$indent);
- foreach ($exploded as $line) {
- $line = trim($line);
- if (strpos($line, '"') === 0 && strrpos($line, '"') == (strlen($line)-1) || strpos($line, "'") === 0 && strrpos($line, "'") == (strlen($line)-1)) {
- $line = substr($line, 1, -1);
- }
- $newValue .= "\n" . $spaces . ($line);
- }
- return $newValue;
- }
-
- /**
- * Folds a string of text, if necessary
- * @access private
- * @return string
- * @param $value The string you wish to fold
- */
- private function _doFolding($value,$indent) {
- // Don't do anything if wordwrap is set to 0
-
- if ($this->_dumpWordWrap !== 0 && is_string ($value) && strlen($value) > $this->_dumpWordWrap) {
- $indent += $this->_dumpIndent;
- $indent = str_repeat(' ',$indent);
- $wrapped = wordwrap($value,$this->_dumpWordWrap,"\n$indent");
- $value = ">\n".$indent.$wrapped;
- } else {
- if ($this->setting_dump_force_quotes && is_string ($value) && $value !== self::REMPTY)
- $value = '"' . $value . '"';
- if (is_numeric($value) && is_string($value))
- $value = '"' . $value . '"';
- }
-
-
- return $value;
- }
-
- private function isTrueWord($value) {
- $words = self::getTranslations(array('true', 'on', 'yes', 'y'));
- return in_array($value, $words, true);
- }
-
- private function isFalseWord($value) {
- $words = self::getTranslations(array('false', 'off', 'no', 'n'));
- return in_array($value, $words, true);
- }
-
- private function isNullWord($value) {
- $words = self::getTranslations(array('null', '~'));
- return in_array($value, $words, true);
- }
-
- private function isTranslationWord($value) {
- return (
- self::isTrueWord($value) ||
- self::isFalseWord($value) ||
- self::isNullWord($value)
- );
- }
-
- /**
- * Coerce a string into a native type
- * Reference: http://yaml.org/type/bool.html
- * TODO: Use only words from the YAML spec.
- * @access private
- * @param $value The value to coerce
- */
- private function coerceValue(&$value) {
- if (self::isTrueWord($value)) {
- $value = true;
- } else if (self::isFalseWord($value)) {
- $value = false;
- } else if (self::isNullWord($value)) {
- $value = null;
- }
- }
-
- /**
- * Given a set of words, perform the appropriate translations on them to
- * match the YAML 1.1 specification for type coercing.
- * @param $words The words to translate
- * @access private
- */
- private static function getTranslations(array $words) {
- $result = array();
- foreach ($words as $i) {
- $result = array_merge($result, array(ucfirst($i), strtoupper($i), strtolower($i)));
- }
- return $result;
- }
-
-// LOADING FUNCTIONS
-
- private function _load($input) {
- $Source = $this->loadFromSource($input);
- return $this->loadWithSource($Source);
- }
-
- private function _loadString($input) {
- $Source = $this->loadFromString($input);
- return $this->loadWithSource($Source);
- }
-
- private function loadWithSource($Source) {
- if (empty ($Source)) return array();
- if ($this->setting_use_syck_is_possible && function_exists ('syck_load')) {
- $array = syck_load (implode ("\n", $Source));
- return is_array($array) ? $array : array();
- }
-
- $this->path = array();
- $this->result = array();
-
- $cnt = count($Source);
- for ($i = 0; $i < $cnt; $i++) {
- $line = $Source[$i];
-
- $this->indent = strlen($line) - strlen(ltrim($line));
- $tempPath = $this->getParentPathByIndent($this->indent);
- $line = self::stripIndent($line, $this->indent);
- if (self::isComment($line)) continue;
- if (self::isEmpty($line)) continue;
- $this->path = $tempPath;
-
- $literalBlockStyle = self::startsLiteralBlock($line);
- if ($literalBlockStyle) {
- $line = rtrim ($line, $literalBlockStyle . " \n");
- $literalBlock = '';
- $line .= ' '.$this->LiteralPlaceHolder;
- $literal_block_indent = strlen($Source[$i+1]) - strlen(ltrim($Source[$i+1]));
- while (++$i < $cnt && $this->literalBlockContinues($Source[$i], $this->indent)) {
- $literalBlock = $this->addLiteralLine($literalBlock, $Source[$i], $literalBlockStyle, $literal_block_indent);
- }
- $i--;
- }
-
- // Strip out comments
- if (strpos ($line, '#')) {
- $line = preg_replace('/\s*#([^"\']+)$/','',$line);
- }
-
- while (++$i < $cnt && self::greedilyNeedNextLine($line)) {
- $line = rtrim ($line, " \n\t\r") . ' ' . ltrim ($Source[$i], " \t");
- }
- $i--;
-
- $lineArray = $this->_parseLine($line);
-
- if ($literalBlockStyle)
- $lineArray = $this->revertLiteralPlaceHolder ($lineArray, $literalBlock);
-
- $this->addArray($lineArray, $this->indent);
-
- foreach ($this->delayedPath as $indent => $delayedPath)
- $this->path[$indent] = $delayedPath;
-
- $this->delayedPath = array();
-
- }
- return $this->result;
- }
-
- private function loadFromSource ($input) {
- if (!empty($input) && strpos($input, "\n") === false && file_exists($input))
- $input = file_get_contents($input);
-
- return $this->loadFromString($input);
- }
-
- private function loadFromString ($input) {
- $lines = explode("\n",$input);
- foreach ($lines as $k => $_) {
- $lines[$k] = rtrim ($_, "\r");
- }
- return $lines;
- }
-
- /**
- * Parses YAML code and returns an array for a node
- * @access private
- * @return array
- * @param string $line A line from the YAML file
- */
- private function _parseLine($line) {
- if (!$line) return array();
- $line = trim($line);
- if (!$line) return array();
-
- $array = array();
-
- $group = $this->nodeContainsGroup($line);
- if ($group) {
- $this->addGroup($line, $group);
- $line = $this->stripGroup ($line, $group);
- }
-
- if ($this->startsMappedSequence($line))
- return $this->returnMappedSequence($line);
-
- if ($this->startsMappedValue($line))
- return $this->returnMappedValue($line);
-
- if ($this->isArrayElement($line))
- return $this->returnArrayElement($line);
-
- if ($this->isPlainArray($line))
- return $this->returnPlainArray($line);
-
-
- return $this->returnKeyValuePair($line);
-
- }
-
- /**
- * Finds the type of the passed value, returns the value as the new type.
- * @access private
- * @param string $value
- * @return mixed
- */
- private function _toType($value) {
- if ($value === '') return "";
- $first_character = $value[0];
- $last_character = substr($value, -1, 1);
-
- $is_quoted = false;
- do {
- if (!$value) break;
- if ($first_character != '"' && $first_character != "'") break;
- if ($last_character != '"' && $last_character != "'") break;
- $is_quoted = true;
- } while (0);
-
- if ($is_quoted) {
- $value = str_replace('\n', "\n", $value);
- if ($first_character == "'")
- return strtr(substr ($value, 1, -1), array ('\'\'' => '\'', '\\\''=> '\''));
- return strtr(substr ($value, 1, -1), array ('\\"' => '"', '\\\''=> '\''));
- }
-
- if (strpos($value, ' #') !== false && !$is_quoted)
- $value = preg_replace('/\s+#(.+)$/','',$value);
-
- if ($first_character == '[' && $last_character == ']') {
- // Take out strings sequences and mappings
- $innerValue = trim(substr ($value, 1, -1));
- if ($innerValue === '') return array();
- $explode = $this->_inlineEscape($innerValue);
- // Propagate value array
- $value = array();
- foreach ($explode as $v) {
- $value[] = $this->_toType($v);
- }
- return $value;
- }
-
- if (strpos($value,': ')!==false && $first_character != '{') {
- $array = explode(': ',$value);
- $key = trim($array[0]);
- array_shift($array);
- $value = trim(implode(': ',$array));
- $value = $this->_toType($value);
- return array($key => $value);
- }
-
- if ($first_character == '{' && $last_character == '}') {
- $innerValue = trim(substr ($value, 1, -1));
- if ($innerValue === '') return array();
- // Inline Mapping
- // Take out strings sequences and mappings
- $explode = $this->_inlineEscape($innerValue);
- // Propagate value array
- $array = array();
- foreach ($explode as $v) {
- $SubArr = $this->_toType($v);
- if (empty($SubArr)) continue;
- if (is_array ($SubArr)) {
- $array[key($SubArr)] = $SubArr[key($SubArr)]; continue;
- }
- $array[] = $SubArr;
- }
- return $array;
- }
-
- if ($value == 'null' || $value == 'NULL' || $value == 'Null' || $value == '' || $value == '~') {
- return null;
- }
-
- if ( is_numeric($value) && preg_match ('/^(-|)[1-9]+[0-9]*$/', $value) ){
- $intvalue = (int)$value;
- if ($intvalue != PHP_INT_MAX && $intvalue != ~PHP_INT_MAX)
- $value = $intvalue;
- return $value;
- }
-
- if ( is_string($value) && preg_match('/^0[xX][0-9a-fA-F]+$/', $value)) {
- // Hexadecimal value.
- return hexdec($value);
- }
-
- $this->coerceValue($value);
-
- if (is_numeric($value)) {
- if ($value === '0') return 0;
- if (rtrim ($value, 0) === $value)
- $value = (float)$value;
- return $value;
- }
-
- return $value;
- }
-
- /**
- * Used in inlines to check for more inlines or quoted strings
- * @access private
- * @return array
- */
- private function _inlineEscape($inline) {
- // There's gotta be a cleaner way to do this...
- // While pure sequences seem to be nesting just fine,
- // pure mappings and mappings with sequences inside can't go very
- // deep. This needs to be fixed.
-
- $seqs = array();
- $maps = array();
- $saved_strings = array();
- $saved_empties = array();
-
- // Check for empty strings
- $regex = '/("")|(\'\')/';
- if (preg_match_all($regex,$inline,$strings)) {
- $saved_empties = $strings[0];
- $inline = preg_replace($regex,'YAMLEmpty',$inline);
- }
- unset($regex);
-
- // Check for strings
- $regex = '/(?:(")|(?:\'))((?(1)[^"]+|[^\']+))(?(1)"|\')/';
- if (preg_match_all($regex,$inline,$strings)) {
- $saved_strings = $strings[0];
- $inline = preg_replace($regex,'YAMLString',$inline);
- }
- unset($regex);
-
- // echo $inline;
-
- $i = 0;
- do {
-
- // Check for sequences
- while (preg_match('/\[([^{}\[\]]+)\]/U',$inline,$matchseqs)) {
- $seqs[] = $matchseqs[0];
- $inline = preg_replace('/\[([^{}\[\]]+)\]/U', ('YAMLSeq' . (count($seqs) - 1) . 's'), $inline, 1);
- }
-
- // Check for mappings
- while (preg_match('/{([^\[\]{}]+)}/U',$inline,$matchmaps)) {
- $maps[] = $matchmaps[0];
- $inline = preg_replace('/{([^\[\]{}]+)}/U', ('YAMLMap' . (count($maps) - 1) . 's'), $inline, 1);
- }
-
- if ($i++ >= 10) break;
-
- } while (strpos ($inline, '[') !== false || strpos ($inline, '{') !== false);
-
- $explode = explode(',',$inline);
- $explode = array_map('trim', $explode);
- $stringi = 0; $i = 0;
-
- while (1) {
-
- // Re-add the sequences
- if (!empty($seqs)) {
- foreach ($explode as $key => $value) {
- if (strpos($value,'YAMLSeq') !== false) {
- foreach ($seqs as $seqk => $seq) {
- $explode[$key] = str_replace(('YAMLSeq'.$seqk.'s'),$seq,$value);
- $value = $explode[$key];
- }
- }
- }
- }
-
- // Re-add the mappings
- if (!empty($maps)) {
- foreach ($explode as $key => $value) {
- if (strpos($value,'YAMLMap') !== false) {
- foreach ($maps as $mapk => $map) {
- $explode[$key] = str_replace(('YAMLMap'.$mapk.'s'), $map, $value);
- $value = $explode[$key];
- }
- }
- }
- }
-
-
- // Re-add the strings
- if (!empty($saved_strings)) {
- foreach ($explode as $key => $value) {
- while (strpos($value,'YAMLString') !== false) {
- $explode[$key] = preg_replace('/YAMLString/',$saved_strings[$stringi],$value, 1);
- unset($saved_strings[$stringi]);
- ++$stringi;
- $value = $explode[$key];
- }
- }
- }
-
-
- // Re-add the empties
- if (!empty($saved_empties)) {
- foreach ($explode as $key => $value) {
- while (strpos($value,'YAMLEmpty') !== false) {
- $explode[$key] = preg_replace('/YAMLEmpty/', '', $value, 1);
- $value = $explode[$key];
- }
- }
- }
-
- $finished = true;
- foreach ($explode as $key => $value) {
- if (strpos($value,'YAMLSeq') !== false) {
- $finished = false; break;
- }
- if (strpos($value,'YAMLMap') !== false) {
- $finished = false; break;
- }
- if (strpos($value,'YAMLString') !== false) {
- $finished = false; break;
- }
- if (strpos($value,'YAMLEmpty') !== false) {
- $finished = false; break;
- }
- }
- if ($finished) break;
-
- $i++;
- if ($i > 10)
- break; // Prevent infinite loops.
- }
-
-
- return $explode;
- }
-
- private function literalBlockContinues ($line, $lineIndent) {
- if (!trim($line)) return true;
- if (strlen($line) - strlen(ltrim($line)) > $lineIndent) return true;
- return false;
- }
-
- private function referenceContentsByAlias ($alias) {
- do {
- if (!isset($this->SavedGroups[$alias])) { echo "Bad group name: $alias."; break; }
- $groupPath = $this->SavedGroups[$alias];
- $value = $this->result;
- foreach ($groupPath as $k) {
- $value = $value[$k];
- }
- } while (false);
- return $value;
- }
-
- private function addArrayInline ($array, $indent) {
- $CommonGroupPath = $this->path;
- if (empty ($array)) return false;
-
- foreach ($array as $k => $_) {
- $this->addArray(array($k => $_), $indent);
- $this->path = $CommonGroupPath;
- }
- return true;
- }
-
- private function addArray ($incoming_data, $incoming_indent) {
-
- // print_r ($incoming_data);
-
- if (count ($incoming_data) > 1)
- return $this->addArrayInline ($incoming_data, $incoming_indent);
-
- $key = key ($incoming_data);
- $value = isset($incoming_data[$key]) ? $incoming_data[$key] : null;
- if ($key === '__!YAMLZero') $key = '0';
-
- if ($incoming_indent == 0 && !$this->_containsGroupAlias && !$this->_containsGroupAnchor) { // Shortcut for root-level values.
- if ($key || $key === '' || $key === '0') {
- $this->result[$key] = $value;
- } else {
- $this->result[] = $value; end ($this->result); $key = key ($this->result);
- }
- $this->path[$incoming_indent] = $key;
- return;
- }
-
-
-
- $history = array();
- // Unfolding inner array tree.
- $history[] = $_arr = $this->result;
- foreach ($this->path as $k) {
- $history[] = $_arr = $_arr[$k];
- }
-
- if ($this->_containsGroupAlias) {
- $value = $this->referenceContentsByAlias($this->_containsGroupAlias);
- $this->_containsGroupAlias = false;
- }
-
-
- // Adding string or numeric key to the innermost level or $this->arr.
- if (is_string($key) && $key == '<<') {
- if (!is_array ($_arr)) { $_arr = array (); }
-
- $_arr = array_merge ($_arr, $value);
- } else if ($key || $key === '' || $key === '0') {
- if (!is_array ($_arr))
- $_arr = array ($key=>$value);
- else
- $_arr[$key] = $value;
- } else {
- if (!is_array ($_arr)) { $_arr = array ($value); $key = 0; }
- else { $_arr[] = $value; end ($_arr); $key = key ($_arr); }
- }
-
- $reverse_path = array_reverse($this->path);
- $reverse_history = array_reverse ($history);
- $reverse_history[0] = $_arr;
- $cnt = count($reverse_history) - 1;
- for ($i = 0; $i < $cnt; $i++) {
- $reverse_history[$i+1][$reverse_path[$i]] = $reverse_history[$i];
- }
- $this->result = $reverse_history[$cnt];
-
- $this->path[$incoming_indent] = $key;
-
- if ($this->_containsGroupAnchor) {
- $this->SavedGroups[$this->_containsGroupAnchor] = $this->path;
- if (is_array ($value)) {
- $k = key ($value);
- if (!is_int ($k)) {
- $this->SavedGroups[$this->_containsGroupAnchor][$incoming_indent + 2] = $k;
- }
- }
- $this->_containsGroupAnchor = false;
- }
-
- }
-
- private static function startsLiteralBlock ($line) {
- $lastChar = substr (trim($line), -1);
- if ($lastChar != '>' && $lastChar != '|') return false;
- if ($lastChar == '|') return $lastChar;
- // HTML tags should not be counted as literal blocks.
- if (preg_match ('#<.*?>$#', $line)) return false;
- return $lastChar;
- }
-
- private static function greedilyNeedNextLine($line) {
- $line = trim ($line);
- if (!strlen($line)) return false;
- if (substr ($line, -1, 1) == ']') return false;
- if ($line[0] == '[') return true;
- if (preg_match ('#^[^:]+?:\s*\[#', $line)) return true;
- return false;
- }
-
- private function addLiteralLine ($literalBlock, $line, $literalBlockStyle, $indent = -1) {
- $line = self::stripIndent($line, $indent);
- if ($literalBlockStyle !== '|') {
- $line = self::stripIndent($line);
- }
- $line = rtrim ($line, "\r\n\t ") . "\n";
- if ($literalBlockStyle == '|') {
- return $literalBlock . $line;
- }
- if (strlen($line) == 0)
- return rtrim($literalBlock, ' ') . "\n";
- if ($line == "\n" && $literalBlockStyle == '>') {
- return rtrim ($literalBlock, " \t") . "\n";
- }
- if ($line != "\n")
- $line = trim ($line, "\r\n ") . " ";
- return $literalBlock . $line;
- }
-
- function revertLiteralPlaceHolder ($lineArray, $literalBlock) {
- foreach ($lineArray as $k => $_) {
- if (is_array($_))
- $lineArray[$k] = $this->revertLiteralPlaceHolder ($_, $literalBlock);
- else if (substr($_, -1 * strlen ($this->LiteralPlaceHolder)) == $this->LiteralPlaceHolder)
- $lineArray[$k] = rtrim ($literalBlock, " \r\n");
- }
- return $lineArray;
- }
-
- private static function stripIndent ($line, $indent = -1) {
- if ($indent == -1) $indent = strlen($line) - strlen(ltrim($line));
- return substr ($line, $indent);
- }
-
- private function getParentPathByIndent ($indent) {
- if ($indent == 0) return array();
- $linePath = $this->path;
- do {
- end($linePath); $lastIndentInParentPath = key($linePath);
- if ($indent <= $lastIndentInParentPath) array_pop ($linePath);
- } while ($indent <= $lastIndentInParentPath);
- return $linePath;
- }
-
-
- private function clearBiggerPathValues ($indent) {
-
-
- if ($indent == 0) $this->path = array();
- if (empty ($this->path)) return true;
-
- foreach ($this->path as $k => $_) {
- if ($k > $indent) unset ($this->path[$k]);
- }
-
- return true;
- }
-
-
- private static function isComment ($line) {
- if (!$line) return false;
- if ($line[0] == '#') return true;
- if (trim($line, " \r\n\t") == '---') return true;
- return false;
- }
-
- private static function isEmpty ($line) {
- return (trim ($line) === '');
- }
-
-
- private function isArrayElement ($line) {
- if (!$line || !is_scalar($line)) return false;
- if (substr($line, 0, 2) != '- ') return false;
- if (strlen ($line) > 3)
- if (substr($line,0,3) == '---') return false;
-
- return true;
- }
-
- private function isHashElement ($line) {
- return strpos($line, ':');
- }
-
- private function isLiteral ($line) {
- if ($this->isArrayElement($line)) return false;
- if ($this->isHashElement($line)) return false;
- return true;
- }
-
-
- private static function unquote ($value) {
- if (!$value) return $value;
- if (!is_string($value)) return $value;
- if ($value[0] == '\'') return trim ($value, '\'');
- if ($value[0] == '"') return trim ($value, '"');
- return $value;
- }
-
- private function startsMappedSequence ($line) {
- return (substr($line, 0, 2) == '- ' && substr ($line, -1, 1) == ':');
- }
-
- private function returnMappedSequence ($line) {
- $array = array();
- $key = self::unquote(trim(substr($line,1,-1)));
- $array[$key] = array();
- $this->delayedPath = array(strpos ($line, $key) + $this->indent => $key);
- return array($array);
- }
-
- private function checkKeysInValue($value) {
- if (strchr('[{"\'', $value[0]) === false) {
- if (strchr($value, ': ') !== false) {
- throw new Exception('Too many keys: '.$value);
- }
- }
- }
-
- private function returnMappedValue ($line) {
- $this->checkKeysInValue($line);
- $array = array();
- $key = self::unquote (trim(substr($line,0,-1)));
- $array[$key] = '';
- return $array;
- }
-
- private function startsMappedValue ($line) {
- return (substr ($line, -1, 1) == ':');
- }
-
- private function isPlainArray ($line) {
- return ($line[0] == '[' && substr ($line, -1, 1) == ']');
- }
-
- private function returnPlainArray ($line) {
- return $this->_toType($line);
- }
-
- private function returnKeyValuePair ($line) {
- $array = array();
- $key = '';
- if (strpos ($line, ': ')) {
- // It's a key/value pair most likely
- // If the key is in double quotes pull it out
- if (($line[0] == '"' || $line[0] == "'") && preg_match('/^(["\'](.*)["\'](\s)*:)/',$line,$matches)) {
- $value = trim(str_replace($matches[1],'',$line));
- $key = $matches[2];
- } else {
- // Do some guesswork as to the key and the value
- $explode = explode(': ', $line);
- $key = trim(array_shift($explode));
- $value = trim(implode(': ', $explode));
- $this->checkKeysInValue($value);
- }
- // Set the type of the value. Int, string, etc
- $value = $this->_toType($value);
- if ($key === '0') $key = '__!YAMLZero';
- $array[$key] = $value;
- } else {
- $array = array ($line);
- }
- return $array;
-
- }
-
-
- private function returnArrayElement ($line) {
- if (strlen($line) <= 1) return array(array()); // Weird %)
- $array = array();
- $value = trim(substr($line,1));
- $value = $this->_toType($value);
- if ($this->isArrayElement($value)) {
- $value = $this->returnArrayElement($value);
- }
- $array[] = $value;
- return $array;
- }
-
-
- private function nodeContainsGroup ($line) {
- $symbolsForReference = 'A-z0-9_\-';
- if (strpos($line, '&') === false && strpos($line, '*') === false) return false; // Please die fast ;-)
- if ($line[0] == '&' && preg_match('/^(&['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1];
- if ($line[0] == '*' && preg_match('/^(\*['.$symbolsForReference.']+)/', $line, $matches)) return $matches[1];
- if (preg_match('/(&['.$symbolsForReference.']+)$/', $line, $matches)) return $matches[1];
- if (preg_match('/(\*['.$symbolsForReference.']+$)/', $line, $matches)) return $matches[1];
- if (preg_match ('#^\s*<<\s*:\s*(\*[^\s]+).*$#', $line, $matches)) return $matches[1];
- return false;
-
- }
-
- private function addGroup ($line, $group) {
- if ($group[0] == '&') $this->_containsGroupAnchor = substr ($group, 1);
- if ($group[0] == '*') $this->_containsGroupAlias = substr ($group, 1);
- //print_r ($this->path);
- }
-
- private function stripGroup ($line, $group) {
- $line = trim(str_replace($group, '', $line));
- return $line;
- }
-}
-}
-
-// Enable use of Spyc from command line
-// The syntax is the following: php Spyc.php spyc.yaml
-
-do {
- if (PHP_SAPI != 'cli') break;
- if (empty ($_SERVER['argc']) || $_SERVER['argc'] < 2) break;
- if (empty ($_SERVER['PHP_SELF']) || FALSE === strpos ($_SERVER['PHP_SELF'], 'Spyc.php') ) break;
- $file = $argv[1];
- echo json_encode (spyc_load_file ($file));
-} while (0);
diff --git a/util/Text.class.php b/util/Text.class.php
@@ -1,393 +0,0 @@
-<?php
-// OpenRat Content Management System
-// Copyright (C) 2002 Jan Dankert, jandankert@jandankert.de
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-
-/**
- * Nuetzliche Funktionen fuer das Bearbeiten von Texten/Zeichenketten
- * @author $Author$
- * @version $Revision$
- * @package openrat.services
- */
-class Text
-{
- /**
- *
- * @param unknown $key
- * @param unknown $text
- * @return string|unknown
- */
- public static function accessKey( $key, $text )
- {
- $pos = strpos(strtolower($text),strtolower($key));
-
- if ( $pos !== false )
- return substr($text,0,max($pos,0)).'<span class="accesskey">'.substr($text,$pos,1).'</span>'.substr($text,$pos+1);
- else
- return $text;
- }
-
- /**
- * Alias fuer Methode maxLength()
- *
- * @deprecated use maxlength() !
- */
- public static function maxLaenge( $laenge,$text )
- {
- return Text::maxLength($text,$laenge);
- }
-
-
- /**
- * Einen Text auf eine bestimmte Laenge begrenzen.
- *
- * Ist der Text zu lang, so wird abgeschnitten und
- * eine Zeichenkette angehaengt.
- *
- * @param String Text, der zu begrenzen ist
- * @param Integer maximale Laenge des Textes (optional)
- * @param Text, der an gekuerzten Text angehangen wird (optional)
- */
- public static function maxLength( $text,$laenge=20,$append='...',$where=STR_PAD_RIGHT )
- {
- if ( strlen($text) > $laenge )
- {
- if ( $where == STR_PAD_RIGHT )
- $text = substr($text,0,$laenge).$append;
- elseif ( $where == STR_PAD_BOTH )
- $text = substr($text,0,$laenge/2).$append.substr($text,strlen($text)-($laenge/2));
- }
-
- return $text;
- }
-
-
- /**
- * Umwandeln von BB-Code in Wiki-Textauszeichnungen
- *
- * @param text zu bearbeitender Text
- *
- * @return String Ausgabe
- */
- public static function bbCode2Wiki( $inhalt )
- {
- $inhalt = preg_replace('/\[b\]([^\[]*)\[\/b\]/i' , '*\\1*' ,$inhalt);
- $inhalt = preg_replace('/\[i\]([^\[]*)\[\/i\]/i' , '_\\1_' ,$inhalt);
- $inhalt = preg_replace('/\[code\]([^\[]*)\[\/code\]/i' , '=\\1=' ,$inhalt);
-
- $inhalt = preg_replace('/\[url\]([^\[]*)\[\/url\]/i' ,'"\\1"->"\\1"' ,$inhalt);
- $inhalt = preg_replace('/\[url=([^\[]*)\]([^\[]*)\[\/url\]/i' ,'"\\2"->"\\1"' ,$inhalt);
-
- return $inhalt;
- }
-
-
- /**
- * Umwandeln von einfachen HTML-Befehlen in Wiki-Textauszeichnungen
- *
- * @param text zu bearbeitender Text
- *
- * @return String Ausgabe
- */
- public static function Html2Wiki( $inhalt )
- {
- $inhalt = preg_replace('/<b(.*)>(.*)<\/b>/i','*\\2*' ,$inhalt);
- $inhalt = preg_replace('/<i(.*)>(.*)<\/i>/i','_\\2_' ,$inhalt);
- $inhalt = preg_replace('/<a(.*)href="(.*)">(.*)<\/a>/i','"\\3"->"\\2"' ,$inhalt);
-
- return $inhalt;
- }
-
-
- /**
- * HTML-Entitaeten fuer HTML-Tags verwenden
- *
- * @param String Text, in dem HTML-Tags umgewandelt werden sollen
- * @return String Ausgabe
- */
- public static function encodeHtml( $inhalt )
- {
- //$inhalt = str_replace('&','&',$inhalt);
- $inhalt = str_replace('"','"',$inhalt);
- $inhalt = str_replace('<','<' ,$inhalt);
- $inhalt = str_replace('>','>' ,$inhalt);
-
- return $inhalt;
- }
-
-
-
- /**
- * Ersetzt Sonderzeichen durch HTML-�quivalente.<br>
- * Z.B. Ersetzt "(c)" durch "©".
- */
- public static function replaceHtmlChars( $text )
- {
- global $conf;
-
- foreach( explode(' ',$conf['editor']['html']['replace']) as $repl )
- {
- list( $ersetze, $mit ) = explode(':',$repl.':');
- $text = str_replace($ersetze, $mit, $text);
- }
-
- return $text;
- }
-
-
-
- /**
- * HTML-Entitaeten fuer HTML-Tags verwenden
- *
- * @param String Text, in dem HTML-Tags umgewandelt werden sollen
- * @return String Ausgabe
- */
- public static function encodeHtmlSpecialChars( $inhalt )
- {
- return Text::replaceHtmlChars( $inhalt );
- }
-
-
-
- /**
- * Vergleicht 2 Text-Arrays und ermittelt eine Darstellung der Unterschiede
- *
- */
- public static function diff( $from_text,$to_text )
- {
- // Zaehler pro Textarray
- $pos_from = -1;
- $pos_to = -1;
-
- // Ergebnis-Arrays
- $from_out = array();
- $to_out = array();
-
- while( true )
- {
- $pos_from++;
- $pos_to ++;
-
- if ( !isset($from_text[$pos_from]) &&
- !isset($to_text [$pos_to ]) )
- {
- // Text in ist 'neu' und 'alt' zuende. Ende der Schleife.
- break;
- }
- elseif
- ( isset($from_text[$pos_from]) &&
- !isset($to_text [$pos_to]) )
- {
- // Text in 'neu' ist zuende, die Restzeilen von 'alt' werden ausgegeben
- while( isset($from_text[$pos_from]) )
- {
- $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>'old');
- $to_out [] = array();
- $pos_from++;
- }
- break;
- }
- elseif
- ( !isset($from_text[$pos_from]) &&
- isset($to_text [$pos_to]) )
- {
- // Umgekehrter Fall: Text in 'alt' ist zuende, Restzeilen aus 'neu' werden ausgegeben
- while( isset($to_text[$pos_to]) )
- {
- $from_out[] = array();
- $to_out [] = array('text'=>$to_text[$pos_to],'line'=>$pos_to+1,'type'=>'new');
- $pos_to++;
- }
- break;
- }
- elseif
- ( rtrim($from_text[$pos_from]) != rtrim($to_text[$pos_to]) )
- {
- // Zeilen sind vorhanden, aber ungleich
- // Wir suchen jetzt die naechsten beiden Zeilen, die gleich sind.
- $max_entf = min(count($from_text)-$pos_from-1,count($to_text)-$pos_to-1);
-
- #echo "suche start, max_entf=$max_entf, pos_from=$pos_from, pos_to=$pos_to<br/>";
-
- for ( $a=0; $a<=$max_entf; $a++ )
- {
- #echo "a ist $a<br/>";
- for ( $b=0; $b<=$max_entf; $b++ )
- {
- #echo "b ist $b<br/>";
- if ( trim($from_text[$pos_from+$b]) != '' &&
- $from_text[$pos_from+$b] == $to_text[$pos_to+$a] )
- {
- $pos_gef_from = $pos_from+$b;
- $pos_gef_to = $pos_to +$a;
- break;
- }
-
- if ( trim($from_text[$pos_from+$a]) != '' &&
- $from_text[$pos_from+$a] == $to_text[$pos_to+$b] )
- {
- $pos_gef_from = $pos_from+$a;
- $pos_gef_to = $pos_to +$b;
- break;
- }
- }
-
- if ( $b <=$max_entf)
- {
- break;
- }
- }
-
- if ( $a<=$max_entf )
- {
- // Gleiche Zeile gefunden
- #echo "gefunden, pos_gef_from=$pos_gef_from, pos_gef_to=$pos_gef_to<br/>";
-
- if ( $pos_gef_from - $pos_from == 0 )
- $type = 'new';
- elseif
- ( $pos_gef_to - $pos_to == 0 )
- $type = 'old';
- else
- $type = 'notequal';
-
- while( $pos_gef_from - $pos_from > 0 &&
- $pos_gef_to - $pos_to > 0 )
- {
- $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>$type);
- $to_out [] = array('text'=>$to_text [$pos_to ],'line'=>$pos_to+1 ,'type'=>$type);
-
- $pos_from++;
- $pos_to++;
- }
-
- while( $pos_gef_from - $pos_from > 0 )
- {
- $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>$type);
- $to_out [] = array();
- $pos_from++;
- }
-
- while( $pos_gef_to - $pos_to > 0 )
- {
- $from_out[] = array();
- $to_out [] = array('text'=>$to_text [$pos_to ],'line'=>$pos_to+1 ,'type'=>$type);
- $pos_to++;
- }
- $pos_from--;
- $pos_to--;
- }
- else
- {
- // Keine gleichen Zeilen gefunden
- #echo "nicht gefunden, i=$i, j=$j, pos_from war $pos_from, pos_to war $pos_to<br/>";
-
- while( true )
- {
- if ( !isset($from_text[$pos_from]) &&
- !isset($to_text [$pos_to ]) )
- {
- break;
- }
- elseif
- ( isset($from_text[$pos_from]) &&
- !isset($to_text [$pos_to ]) )
- {
- $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>'notequal');
- $to_out [] = array();
- }
- elseif
- ( !isset($from_text[$pos_from]) &&
- isset($to_text [$pos_to ]) )
- {
- $from_out[] = array();
- $to_out [] = array('text'=>$to_text [$pos_to ],'line'=>$pos_to+1 ,'type'=>'notequal');
- }
- else
- {
- $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>'notequal');
- $to_out [] = array('text'=>$to_text [$pos_to ],'line'=>$pos_to+1 ,'type'=>'notequal');
- }
- $pos_from++;
- $pos_to++;
- }
- }
- }
- else
- {
- // Zeilen sind gleich
- $from_out[] = array('text'=>$from_text[$pos_from],'line'=>$pos_from+1,'type'=>'equal');
- $to_out [] = array('text'=>$to_text [$pos_to ],'line'=>$pos_to+1 ,'type'=>'equal');
- }
- }
-
- return( array($from_out,$to_out) );
- }
-
-
- /**
- * Entfernt einen Text-Bereich aus einer Zeichenkette.<br>
- * Es wird angegeben, von wo bis wo entfernt werden soll.
- *
- * @param $text Text, aus dem entfernt wird
- * @param $von der Text, AB dem entfernt wird
- * @param $bis der Text, BIS ZU DEM entfernt wird
- * @return String Text
- */
- public static function entferneVonBis($text,$von,$bis)
- {
- $beg = strpos($text,$von);
- $end = strpos($text,$bis);
- if ( $beg!==false && $end !==false )
- $text = substr($text,0,$beg).substr($text,$end+strlen($bis));
- return $text;
- }
-
-
- /**
- * Saeubert eine Zeichenkette.
- *
- * Es werden ungueltige Zeichen aus einer Zeichenkette entfernt. Es wird mit einer Whitelist
- * gearbeitet, d.h. die erlaubten Zeichen werden angegeben.
- *
- * @param $eingabe Die Eingabe-Zeichenkette, aus der ungueltige Zeichen entfernt werden sollen.
- * @param $erlaubt Die erlaubten Zeichen (eine "White-List")
- * @return String die aufgeräumte Zeichenkette
- */
- public static function clean( $eingabe, $erlaubt )
- {
- $first = strtr( $eingabe, $erlaubt, str_repeat("\x01", strlen($erlaubt)) );
- $second = strtr( $eingabe, $first , str_repeat("\x00", strlen($first )) );
- return str_replace("\x00",'',$second);
- }
-
-
-
- public static function parseOID( $text )
- {
- $oids = array();
- $treffer = array();
-
- preg_match_all('/\"([^\"]*)__OID__([0-9]+)__([^\"]*)\"/', $text, $treffer,PREG_SET_ORDER);
-
- foreach( $treffer as $t )
- $oids[$t[2]] = $t[0];
-
- return $oids;
- }
-}
-
diff --git a/util/Transformer.class.php b/util/Transformer.class.php
@@ -1,102 +0,0 @@
-<?php
-
-/**
- * Transformieren eines Textes.<br>
- * Ein Text wird geparst und neu gerendert.
- *
- * @author $Author$
- * @version $Revision$
- * @package openrat.services
- */
-class Transformer
-{
- var $text = '';
- var $doc;
- var $page;
- var $element;
-
- function transform()
- {
- $this->parseDocument();
- $this->renderDocument();
-
- $this->text = $this->renderedText;
- }
-
-
-
- /**
- * Parsen eines Textes.<br>
- * Der Text muss in der Eigenschaft 'text' bereits zur Verf�gung stehen.<br>
- * Der Text wird geparst und als DOM (Document object model) intern gespeichert.
- */
-
- function parseDocument()
- {
- // Den Text zeilenweise aufteilen.
- $zeilen = explode("\n",$this->text);
-
- // Dokument erzeugen und den Text parsen.
- $parser = new WikiParser();
- $this->doc = new DocumentElement();
- $this->doc->element = $this->element;
- $this->doc->parse( $zeilen );
- $this->doc->page = $this->page;
- }
-
-
-
- /**
- * Das interne Dokumente wird gerendet.<br>
- * Die fertige Ausgabe steht anschliessend in der Eigenschaft "renderedText" zur Verf�gung.
- */
- function renderDocument()
- {
- $this->doc->encodeHtml = !$this->element->html;
-
- $text = $this->doc->render( $this->page->mimeType() );
-
- // Liste der verlinkten Objekt-Ids.
- // Die Objekt-Ids werden absteigend sortiert, damit z.B. '33' vor '3' ersetzt wird.
- $linkedObjectIds = $this->doc->linkedObjectIds;
- rsort( $linkedObjectIds,SORT_NUMERIC );
-
- // Links object:nnn ersetzen
- //
- // Das Dokument-Objekt hat keine Information ueber die aktuelle Seite,
- // daher werden die Links auf Objekte hier gesetzt.
- foreach( $linkedObjectIds as $objectId )
- {
- $targetPath = $this->page->path_to_object( $objectId );
-
- // Hack: Sonderzeichen muessen in URLs maskiert werden, aber nur bei URLs die aus Link-Objekten kommen, bei allem
- // anderen (insbesondere Preview-Links zu andereen Seiten) darf die Umsetzung nicht erfolgen.
- // Der Renderer kann dies nicht tun, denn der erzeugt nur "object://..."-URLs.
- // Beispiel: "...?a=1&b=2" wird zu "...?a=1&b=2"
- $o = new Object($objectId);
- try
- {
- $o->load();
- if ( $o->isLink )
- {
- $l = new Link($objectId);
- $l->load();
- if ( $l->isLinkToUrl && $this->page->mimeType() == 'text/html' )
- $targetPath = htmlspecialchars($targetPath);
- }
- }
- catch( ObjectNotFoundException $e)
- {
- $targetPath = 'javascript:alert("object '.$objectId.' not found");';
- }
-
-
- $text = str_replace( 'object:' .$objectId, $targetPath, $text );
- $text = str_replace( 'object://'.$objectId, $targetPath, $text );
- }
-
- $this->renderedText = $text;
- }
-}
-
-?>-
\ No newline at end of file
diff --git a/util/TreeElement.class.php b/util/TreeElement.class.php
@@ -1,75 +0,0 @@
-<?php
-
-// OpenRat Content Management System
-// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-/**
- * Darstellen eines Elementes in einer Baumstruktur
- * @author $Author$
- * @version $Revision$
- * @package openrat.services
- */
-class TreeElement
-{
- /**
- * @type Integer
- */
- var $id;
-
- var $extraId = array();
-
- var $internalId = 0;
-
- /**
- * Text des Baumelementes
- * @type String
- */
- var $text = "";
-
- /**
- * Beschreibung
- * @type String
- */
- var $description = "";
- var $url = "";
- var $icon = "";
- var $target = "";
- var $action = "";
-
- /**
- * Unterelemente
- * Ein Array von Ids
- * @type Array
- */
- var $subElementIds = array();
-
- /**
- * Typ des Elementes
- * In der Tree-Klasse muss es eine Methode mit diesem Namen geben, die das
- * Element laedt.
- * @type String
- */
- var $type = "";
-
-
- // Konstruktor
- function __construct()
- {
- }
-}
-
-?>-
\ No newline at end of file
diff --git a/util/Upload.class.php b/util/Upload.class.php
@@ -1,85 +0,0 @@
-<?php
-// OpenRat Content Management System
-// Copyright (C) 2002-2012 Jan Dankert, cms@jandankert.de
-//
-// This program is free software; you can redistribute it and/or
-// modify it under the terms of the GNU General Public License
-// as published by the Free Software Foundation; either version 2
-// of the License, or (at your option) any later version.
-//
-// This program is distributed in the hope that it will be useful,
-// but WITHOUT ANY WARRANTY; without even the implied warranty of
-// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-// GNU General Public License for more details.
-//
-// You should have received a copy of the GNU General Public License
-// along with this program; if not, write to the Free Software
-// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
-/**
- * Methoden fuer den Upload einer Datei
- *
- * @author $Author$
- * @version $Revision$
- * @package openrat.services
- */
-class Upload
-{
- var $filename;
- var $extension;
- var $value;
- var $size;
- var $error = '';
-
-
- /**
- * Stellt fest, ob der Upload geklappt hat.
- *
- * @return boolean
- */
- function isValid()
- {
- return empty($this->error);
- }
-
-
-
- /**
- * Bearbeitet den Upload einer Datei.<br>
- * Bei der Objekterzeugung wird die Datei bereits geladen.<br>
- *
- * @return Upload
- */
- function __construct( $name='file' ) // Konstruktor
- {
- global $FILES;
-
- if ( !isset($FILES[$name]) ||
- !isset($FILES[$name]['tmp_name']) ||
- !is_file($FILES[$name]['tmp_name']) )
- {
- $this->error = 'No file received.';
- return;
- }
-
- $this->size = filesize($FILES[$name]['tmp_name']);
-
- $fh = fopen( $FILES[$name]['tmp_name'],'r' );
-
- $this->value = fread($fh,$this->size);
- fclose( $fh );
-
- $this->filename = $FILES[$name]['name'];
- $this->extension = '';
-
- $p = strrpos( $this->filename,'.' ); // Letzten Punkt suchen
-
- if ($p!==false) // Wennn letzten Punkt gefunden, dann dort aufteilen
- {
- $this->extension = substr( $this->filename,$p+1 );
- $this->filename = substr( $this->filename,0,$p );
- }
- }
-}
-
-?>-
\ No newline at end of file
diff --git a/util/XML.class.php b/util/XML.class.php
@@ -1,169 +0,0 @@
-<?php
-/**
- * Multidimensional Array-to-XML.
- *
- * Example:
- * $xml = new XML();
- * header('Content-Type: application/xml');
- * echo $xml->encode( $yourBigArray );
- * exit;
- *
- * Author: Honor� Vasconcelos, Jan Dankert
- *
- * Original from:
- * Clean XML To Array: http://www.phpclasses.org/browse/package/3598.html
- *
- * License of this class: BSD-Licence.
- *
- * 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.
- *
- * Neither the name of the Author(s) nor the names of its contributors may be used
- * to endorse or promote products derived from this software without specific prior
- * written permission.
- *
- * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
- * "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 THE COPYRIGHT OWNER OR
- * 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.
- */
-class XML
-{
- /**
- * Parse multidimentional array to XML.
- *
- * @param array $array
- * @return String
- */
- var $xmlText = '';
-
-
- /**
- * Name of the root element.
- *
- * @var String
- */
- var $root = 'xml';
-
- /*
- * Char to indent with.
- *
- * @var String
- */
- var $indentChar = "\t";
-
-
- /**
- * Newline-Char
- * @var String
- */
- var $nl = "\n";
-
- /**
- * Encode a array to XML.
- *
- * @param Array $array
- * @return String (serialized XML)
- */
- function encode($array)
- {
- //star and end the XML document
- $this->xmlText = '<?xml version="1.0" encoding="utf-8"?>'.$this->nl;
- $this->xmlText .= '<'.$this->root.'>'.$this->nl;
- $this->array_transform($array,1);
- $this->xmlText .='</'.$this->root.'>';
-
- return $this->xmlText;
- }
-
-
- /**
- * @access private
- */
- function array_transform($array,$depth){
-
- foreach($array as $key => $value)
- {
- $attr = array();
- if ( is_numeric($key) )
- {
- // Array-Einträge mit numerischen Index können nicht direkt in ein XML-Element
- // umgewandelt werden, da nur-numerische Element-Namen nicht erlaubt sind.
- // Daher verwenden wir dann 'entry' als Elementnamen.
- $attr['id'] = $key;
- $key = 'entry';
- }
-
- $indent = str_repeat($this->indentChar,$depth);
-
- if ( empty($value) )
- {
- $this->xmlText .= $indent.$this->shortTag($key,$attr).$this->nl;
- }
- elseif ( is_object($value) )
- {
- // Der Inhalt ist ein Array, daher rekursiv verzweigen.
- $this->xmlText .= $indent.$this->openTag($key,$attr).$this->nl;
- $prop = get_object_vars($value);
- $this->array_transform($prop,$depth+1); // Rekursiver Aufruf
- $this->xmlText .= $indent.$this->closeTag($key).$this->nl;
- }
- elseif ( is_array($value) )
- {
- // Der Inhalt ist ein Array, daher rekursiv verzweigen.
- $this->xmlText .= $indent.$this->openTag($key,$attr).$this->nl;
- $this->array_transform($value,$depth+1); // Rekursiver Aufruf
- $this->xmlText .= $indent.$this->closeTag($key).$this->nl;
- }
- else
- {
- // Der Inhalt ist ein einfacher Inhalt (kein Array).
- $this->xmlText .= $indent.$this->openTag($key,$attr);
- $this->xmlText .= $value;
- $this->xmlText .= $this->closeTag($key).$this->nl;
- }
- }
- }
-
-
- function openTag($key,$attr)
- {
- $tag = '<'.$key;
- foreach( $attr as $attr_name=>$attr_value )
- $tag .= ' '.$attr_name.'="'.$attr_value.'"';
- $tag .= '>';
- return $tag;
- }
-
-
-
- function shortTag($key,$attr)
- {
- $tag = '<'.$key;
- foreach( $attr as $attr_name=>$attr_value )
- $tag .= ' '.$attr_name.'="'.$attr_value.'"';
- $tag .= ' />';
- return $tag;
- }
-
-
-
- function closeTag($key)
- {
- return '</'.$key.'>';
- }
-}
-
-?>-
\ No newline at end of file
diff --git a/util/config-default.php b/util/config-default.php
@@ -1,880 +0,0 @@
-<?php
-// DO NOT MAKE ANY CHANGES IN THIS FILE, please edit the file 'config.yml' or 'config-<host>.yml' instead.
-// This file should only be changed by developers.
-$conf = array();
-$conf['applications'] = array();
-$conf['applications']['']=0;
-$conf['applications']['phpmyadmin'] = array();
-$conf['applications']['phpmyadmin']['name']='PHPYourAdmin';
-$conf['applications']['phpmyadmin']['url']="https://example.com/anotherapplication/index.cgi";
-$conf['applications']['phpmyadmin']['param']="ticketidforopenrat";
-$conf['applications']['phpmyadmin']['group']='0';
-$conf['applications']['phpmyadmin']['description']="Your database administration";
-$conf['cache'] = array();
-$conf['cache']['conditional_get']=true;
-$conf['cache']['enable_cache']=false;
-$conf['cache']['tmp_dir']="";
-$conf['config'] = array();
-$conf['config']['auto_reload']= true;
-$conf['config']['session_destroy_on_config_reload']= true;
-$conf['content'] = array();
-$conf['content']['file'] = array();
-$conf['content']['file']['max_file_size']='1500';
-$conf['content']['revision-limit'] = array();
-$conf['content']['revision-limit']['enabled']= false;
-$conf['content']['revision-limit']['max-age']= 120;
-$conf['content']['revision-limit']['min-age']= 1;
-$conf['content']['revision-limit']['max-revisions']= 100;
-$conf['content']['revision-limit']['min-revisions']= 3;
-$conf['content']['language'] = array();
-$conf['content']['language']['use_default_language']= true;
-$conf['countries'] = array();
-$conf['countries']['']='0';
-$conf['countries']['AA']='Afar';
-$conf['countries']['AB']='Abkhazian';
-$conf['countries']['AF']='Afrikaans';
-$conf['countries']['AM']='Amharic';
-$conf['countries']['AR']='Arabic';
-$conf['countries']['AS']='Assamese';
-$conf['countries']['AY']='Aymara';
-$conf['countries']['AZ']='Azerbaijani';
-$conf['countries']['BA']='Bashkir';
-$conf['countries']['BE']='Byelorussian';
-$conf['countries']['BG']='Bulgarian';
-$conf['countries']['BH']='Bihari';
-$conf['countries']['BI']='Bislama';
-$conf['countries']['BN']='Bengali';
-$conf['countries']['BO']='Tibetan';
-$conf['countries']['BR']='Breton';
-$conf['countries']['CA']='Catalan';
-$conf['countries']['CO']='Corsican';
-$conf['countries']['CS']='Czech';
-$conf['countries']['CY']='Welsh';
-$conf['countries']['DA']='Danish';
-$conf['countries']['DE']='German';
-$conf['countries']['DZ']='Bhutani';
-$conf['countries']['EL']='Greek';
-$conf['countries']['EN']='English';
-$conf['countries']['EO']='Esperanto';
-$conf['countries']['ES']='Spanish';
-$conf['countries']['ET']='Estonian';
-$conf['countries']['EU']='Basque';
-$conf['countries']['FA']='Persian';
-$conf['countries']['FI']='Finnish';
-$conf['countries']['FJ']='Fiji';
-$conf['countries']['FO']='Faeroese';
-$conf['countries']['FR']='French';
-$conf['countries']['FY']='Frisian';
-$conf['countries']['GA']='Irish';
-$conf['countries']['GD']='Gaelic';
-$conf['countries']['GL']='Galician';
-$conf['countries']['GN']='Guarani';
-$conf['countries']['GU']='Gujarati';
-$conf['countries']['HA']='Hausa';
-$conf['countries']['HI']='Hindi';
-$conf['countries']['HR']='Croatian';
-$conf['countries']['HU']='Hungarian';
-$conf['countries']['HY']='Armenian';
-$conf['countries']['IA']='Interlingua';
-$conf['countries']['IE']='Interlingue';
-$conf['countries']['IK']='Inupiak';
-$conf['countries']['IN']='Indonesian';
-$conf['countries']['IS']='Icelandic';
-$conf['countries']['IT']='Italian';
-$conf['countries']['IW']='Hebrew';
-$conf['countries']['JA']='Japanese';
-$conf['countries']['JI']='Yiddish';
-$conf['countries']['JW']='Javanese';
-$conf['countries']['KA']='Georgian';
-$conf['countries']['KK']='Kazakh';
-$conf['countries']['KL']='Greenlandic';
-$conf['countries']['KM']='Cambodian';
-$conf['countries']['KN']='Kannada';
-$conf['countries']['KO']='Korean';
-$conf['countries']['KS']='Kashmiri';
-$conf['countries']['KU']='Kurdish';
-$conf['countries']['KY']='Kirghiz';
-$conf['countries']['LA']='Latin';
-$conf['countries']['LN']='Lingala';
-$conf['countries']['LO']='Laothian';
-$conf['countries']['LT']='Lithuanian';
-$conf['countries']['LV']='Latvian';
-$conf['countries']['MG']='Malagasy';
-$conf['countries']['MI']='Maori';
-$conf['countries']['MK']='Macedonian';
-$conf['countries']['ML']='Malayalam';
-$conf['countries']['MN']='Mongolian';
-$conf['countries']['MO']='Moldavian';
-$conf['countries']['MR']='Marathi';
-$conf['countries']['MS']='Malay';
-$conf['countries']['MT']='Maltese';
-$conf['countries']['MY']='Burmese';
-$conf['countries']['NA']='Nauru';
-$conf['countries']['NE']='Nepali';
-$conf['countries']['NL']='Dutch';
-$conf['countries']['_NO']='Norwegian';
-$conf['countries']['OC']='Occitan';
-$conf['countries']['OM']='Oromo';
-$conf['countries']['OR']='Oriya';
-$conf['countries']['PA']='Punjabi';
-$conf['countries']['PL']='Polish';
-$conf['countries']['PS']='Pashto';
-$conf['countries']['PT']='Portuguese';
-$conf['countries']['QU']='Quechua';
-$conf['countries']['RM']='Rhaeto-Romance';
-$conf['countries']['RN']='Kirundi';
-$conf['countries']['RO']='Romanian';
-$conf['countries']['RU']='Russian';
-$conf['countries']['RW']='Kinyarwanda';
-$conf['countries']['SA']='Sanskrit';
-$conf['countries']['SD']='Sindhi';
-$conf['countries']['SG']='Sangro';
-$conf['countries']['SH']='Serbo-Croatian';
-$conf['countries']['SI']='Singhalese';
-$conf['countries']['SK']='Slovak';
-$conf['countries']['SL']='Slovenian';
-$conf['countries']['SM']='Samoan';
-$conf['countries']['SN']='Shona';
-$conf['countries']['SO']='Somali';
-$conf['countries']['SQ']='Albanian';
-$conf['countries']['SR']='Serbian';
-$conf['countries']['SS']='Siswati';
-$conf['countries']['ST']='Sesotho';
-$conf['countries']['SU']='Sudanese';
-$conf['countries']['SV']='Swedish';
-$conf['countries']['SW']='Swahili';
-$conf['countries']['TA']='Tamil';
-$conf['countries']['TE']='Tegulu';
-$conf['countries']['TG']='Tajik';
-$conf['countries']['TH']='Thai';
-$conf['countries']['TI']='Tigrinya';
-$conf['countries']['TK']='Turkmen';
-$conf['countries']['TL']='Tagalog';
-$conf['countries']['TN']='Setswana';
-$conf['countries']['TO']='Tonga';
-$conf['countries']['TR']='Turkish';
-$conf['countries']['TS']='Tsonga';
-$conf['countries']['TT']='Tatar';
-$conf['countries']['TW']='Twi';
-$conf['countries']['UK']='Ukrainian';
-$conf['countries']['UR']='Urdu';
-$conf['countries']['UZ']='Uzbek';
-$conf['countries']['VI']='Vietnamese';
-$conf['countries']['VO']='Volapuk';
-$conf['countries']['WO']='Wolof';
-$conf['countries']['XH']='Xhosa';
-$conf['countries']['YO']='Yoruba';
-$conf['countries']['ZH']='Chinese';
-
-$conf['database'] = array();
-
-$conf['database-default']=array();
-$conf['database-default']['defaults']=array();
-$conf['database-default']['defaults']['prefix' ] = '';
-$conf['database-default']['defaults']['suffix' ] = '';
-$conf['database-default']['defaults']['enabled' ] = true;
-$conf['database-default']['defaults']['name' ] = '';
-$conf['database-default']['defaults']['description'] = '';
-$conf['database-default']['defaults']['type' ] = 'pdo';
-$conf['database-default']['defaults']['dsn' ] = 'mysql:localhost';
-$conf['database-default']['defaults']['user' ] = '';
-$conf['database-default']['defaults']['password' ] = '';
-$conf['database-default']['defaults']['host' ] = '';
-$conf['database-default']['defaults']['database' ] = '';
-$conf['database-default']['defaults']['base64' ] = false;
-$conf['database-default']['defaults']['persistent' ] = true;
-$conf['database-default']['defaults']['charset' ] = 'UTF-8';
-$conf['database-default']['defaults']['connection_sql'] = '';
-$conf['database-default']['defaults']['cmd' ] = '';
-$conf['database-default']['defaults']['prepare' ] = true;
-$conf['database-default']['defaults']['transaction'] = true;
-$conf['database-default']['defaults']['update' ] = array();
-$conf['database-default']['defaults']['auto_update'] = true;
-$conf['date'] = array();
-$conf['date']['format'] = array();
-$conf['date']['format']['SHORT']= "";
-$conf['date']['format']['ISO8601SHORT']= "Ymd";
-$conf['date']['format']['ISO8601']= "Y-m-d";
-$conf['date']['format']['ISO8601BAS']= "YmdTHis";
-$conf['date']['format']['ISO8601EXT']= "Y-m-dTH:i:s";
-$conf['date']['format']['ISO8601FULL']= "Y-m-dTH:i:sO";
-$conf['date']['format']['ISO8601WEEK']= "YWW";
-$conf['date']['format']['GER1']= "d.m.Y";
-$conf['date']['format']['GER2']= "d.m.Y, H:i";
-$conf['date']['format']['GER3']= "d.m.Y, H:i:s";
-$conf['date']['format']['GER4']= "d. F Y, H:i:s";
-$conf['date']['format']['ENGLONG']= "l dS of F Y h:i:s A";
-$conf['date']['format']['GMDATE']= "D, d M Y H:i:s GMT";
-$conf['date']['format']['RFC822']= "r";
-$conf['date']['format']['UNIX']= "U";
-$conf['date']['format']['LONG']= "F j, Y, g:i a";
-$conf['date']['timezone'] = array();
-$conf['date']['timezone']['-6']="New York";
-$conf['date']['timezone']['0']="UTC (GMT)";
-$conf['date']['timezone']['60']="MET (Middle European Time)";
-$conf['date']['timezone']['120']="MEST (Middle European Summertime)";
-$conf['editor'] = array();
-$conf['editor']['text-markup'] = array();
-$conf['editor']['text-markup']['strong-begin']= "*";
-$conf['editor']['text-markup']['strong-end']= "*";
-$conf['editor']['text-markup']['emphatic-begin']= "_";
-$conf['editor']['text-markup']['emphatic-end']= "_";
-$conf['editor']['text-markup']['image-begin']= "{";
-$conf['editor']['text-markup']['image-end']= "}";
-$conf['editor']['text-markup']['speech-begin']='QUOTE';
-$conf['editor']['text-markup']['speech-end']='QUOTE';
-$conf['editor']['text-markup']['code-begin']= "=";
-$conf['editor']['text-markup']['code-end']= "=";
-$conf['editor']['text-markup']['footnote-begin']= "[";
-$conf['editor']['text-markup']['footnote-end']= "]";
-$conf['editor']['text-markup']['pre-begin']= "=";
-$conf['editor']['text-markup']['pre-end']= "=";
-$conf['editor']['text-markup']['insert-begin']= "++";
-$conf['editor']['text-markup']['insert-end']= "++";
-$conf['editor']['text-markup']['remove-begin']= "--";
-$conf['editor']['text-markup']['remove-end']= "--";
-$conf['editor']['text-markup']['definition-sep']= "::";
-$conf['editor']['text-markup']['headline']= "+";
-$conf['editor']['text-markup']['headline_level1_underline']= "=";
-$conf['editor']['text-markup']['headline_level2_underline']= "-";
-$conf['editor']['text-markup']['headline_level3_underline']= ".";
-$conf['editor']['text-markup']['list-unnumbered']= "-";
-$conf['editor']['text-markup']['list-numbered']= "#";
-$conf['editor']['text-markup']['table-of-content']= "##TOC##";
-$conf['editor']['text-markup']['linkto']= "->";
-$conf['editor']['text-markup']['table-cell-sep']= "|";
-$conf['editor']['text-markup']['style-begin']= "'";
-$conf['editor']['text-markup']['style-end']= "'";
-$conf['editor']['text-markup']['quote']= ">";
-$conf['editor']['text-markup']['quote-line-begin']= ">";
-$conf['editor']['text-markup']['quote-line-end']= ">";
-$conf['editor']['text-markup']['macro-begin']= "<<";
-$conf['editor']['text-markup']['macro-end']= ">>";
-$conf['editor']['text-markup']['macro-attribute-quote']= "'";
-$conf['editor']['text-markup']['macro-attribute-value-seperator']= "=";
-$conf['editor']['html'] = array();
-$conf['editor']['html']['tag_strong']= "strong";
-$conf['editor']['html']['tag_emphatic']= "em";
-$conf['editor']['html']['tag_teletype']= "tt";
-$conf['editor']['html']['tag_speech']= "cite";
-$conf['editor']['html']['override_speech']=false;
-$conf['editor']['html']['override_speech_open']= "«";
-$conf['editor']['html']['override_speech_close']= "»";
-$conf['editor']['html']['rendermode']="sgml";
-$conf['editor']['html']['rendermode']="xml";
-$conf['editor']['html']['replace']= "EUR:€";
-$conf['editor']['wiki'] = array();
-$conf['editor']['wiki']['convert_html']=true;
-$conf['editor']['wiki']['convert_bbcode']=true;
-$conf['editor']['text'] = array();
-$conf['editor']['text']['linelength']='70';
-$conf['editor']['calendar'] = array();
-$conf['editor']['calendar']['weekday_offset']='1';
-$conf['editor']['text'] = array();
-$conf['editor']['text']['linelength']='70';
-$conf['editor']['macro'] = array();
-$conf['editor']['macro']['show_errors']=false;
-$conf['filename'] = array();
-$conf['filename']['edit']=true;
-$conf['filename']['default']='index';
-$conf['filename']['style']='short';
-$conf['filename']['url']='relative';
-$conf['ftp'] = array();
-$conf['ftp']['ascii']= "html,htm,php";
-$conf['help'] = array();
-$conf['help']['enabled']=true;
-$conf['help']['url']="http://help.openrat.de/";
-$conf['help']['suffix']=".html";
-$conf['html'] = array();
-$conf['html']['tag_teletype']='tt';
-$conf['html']['tag_emphatic']='em';
-$conf['html']['tag_strong']='strong';
-$conf['html']['tag_speech']='cite';
-$conf['html']['speech_open']= "&bdquo";
-$conf['html']['speech_close']= "&rdquo";
-$conf['i18n'] = array();
-$conf['i18n']['use_http']=true;
-$conf['i18n']['default']='de';
-$conf['i18n']['available']='de,en,es,fr,it,ru,cn';
-$conf['i18n']['locale'] = array();
-$conf['i18n']['locale']['de']="de_DE.utf8";
-$conf['i18n']['locale']['en']="en_US.utf8";
-$conf['image'] = array();
-$conf['image']['truecolor']=true;
-$conf['interface'] = array();
-$conf['interface']['tree_width']= "25%";
-$conf['interface']['file_separator']= " »";
-$conf['interface']['nice_urls']=false;
-$conf['interface']['url_sessionid']=false;
-$conf['interface']['theme']= 'default';
-$conf['interface']['timeout']='0';
-$conf['interface']['override_title']='';
-$conf['interface']['style'] = array();
-$conf['interface']['style']['default']='modern';
-$conf['interface']['config'] = array();
-$conf['interface']['config']['file_manager_url']="";
-$conf['interface']['config']['enable']=true;
-$conf['interface']['config']['show_system']=true;
-$conf['interface']['config']['show_interpreter']=true;
-$conf['interface']['config']['show_extensions']=true;
-$conf['interface']['frames'] = array();
-$conf['interface']['frames']['top']='_top';
-$conf['interface']['url'] = array();
-$conf['interface']['url']['fake_url']=false;
-$conf['interface']['url']['index']=false;
-$conf['interface']['url']['url_format']= "%s,%s.%i";
-$conf['interface']['url']['url_format']= "%s,%s,%d.do";
-$conf['interface']['url']['add_sessionid']=false;
-$conf['interface']['gravatar'] = array();
-$conf['interface']['gravatar']['enable']=true;
-$conf['interface']['gravatar']['size']='80';
-$conf['interface']['gravatar']['default']='404';
-$conf['interface']['gravatar']['rating']='g';
-$conf['interface']['session'] = array();
-$conf['interface']['session']['auto_extend']=true;
-$conf['ldap'] = array();
-$conf['ldap']['host']="localhost";
-$conf['ldap']['port']="389";
-$conf['ldap']['protocol']="2";
-$conf['ldap']['dn']= "uid={user},ou=users,dc=example,dc=com";
-$conf['ldap']['dn']= "";
-$conf['ldap']['search'] = array();
-$conf['ldap']['search']['anonymous']=true;
-$conf['ldap']['search']['user']= "uid=openrat,ou=users,dc=example,dc=com";
-$conf['ldap']['search']['password']= "verysecret";
-$conf['ldap']['search']['basedn']= "dc=example,dc=com";
-$conf['ldap']['search']['filter']= "(uid={user})";
-$conf['ldap']['search']['aliases']=true;
-$conf['ldap']['search']['timeout']= 30;
-$conf['ldap']['search']['add']=true;
-$conf['ldap']['authorize'] = array();
-$conf['ldap']['authorize']['group_filter']="(memberUid={dn})";
-$conf['ldap']['authorize']['group_name']="cn";
-$conf['ldap']['authorize']['auto_add']=true;
-$conf['login'] = array();
-$conf['login']['motd']='';
-$conf['login']['nologin']=false;
-$conf['login']['register']=false;
-$conf['login']['send_password']=false;
-$conf['login']['gpl'] = array();
-$conf['login']['gpl']['url']="http://www.gnu.org/licenses/old-licenses/gpl-2.0.html";
-$conf['login']['logo'] = array();
-$conf['login']['logo']['enabled']=false;
-$conf['login']['logo']['image']="./themes/default/images/logo.jpg" ;
-$conf['login']['logo']['url']="http://www.openrat.de" ;
-$conf['login']['start'] = array();
-$conf['login']['start']['start_lastchanged_object']=true;
-$conf['login']['start']['start_single_project']=true;
-$conf['login']['default-database']='db';
-$conf['log'] = array();
-$conf['log']['file']= null;
-$conf['log']['level']= "warn";
-$conf['log']['date_format']= "M j H:i:s";
-$conf['log']['ns_lookup']=false;
-$conf['log']['format']= "%time %level %host %user %action %text";
-$conf['mail'] = array();
-$conf['mail']['enabled']=true;
-$conf['mail']['from']="OpenRat <user@example.com>";
-$conf['mail']['signature']="http://www.openrat.de";
-$conf['mail']['cc']='0';
-$conf['mail']['bcc']='0';
-$conf['mail']['priority']='3';
-$conf['mail']['header_encoding']="Quoted-printable";
-$conf['mail']['client']='smtp';
-$conf['mail']['client']='php';
-$conf['mail']['whitelist']= "";
-$conf['mail']['blacklist']= "";
-$conf['mail']['smtp'] = array();
-$conf['mail']['smtp']['host']="mail.yourdomain.example";
-$conf['mail']['smtp']['host']="locahost";
-$conf['mail']['smtp']['port']="25";
-$conf['mail']['smtp']['auth_username']="your.user@something.example";
-$conf['mail']['smtp']['auth_password']="notsecret";
-$conf['mail']['smtp']['timeout']="45";
-$conf['mail']['smtp']['localhost']='0';
-$conf['mail']['smtp']['localhost']="your.fully.qualified.hostname.example";
-$conf['mail']['smtp']['tls']=false;
-$conf['mail']['smtp']['ssl']=false;
-
-$conf['mime-types'] = array();
-$conf['mime-types']['ez'] = 'application/andrew-inset';
-$conf['mime-types']['csm'] = 'application/cu-seeme';
-$conf['mime-types']['cu'] = 'application/cu-seeme';
-$conf['mime-types']['tsp'] = 'application/dsptype';
-$conf['mime-types']['spl'] = 'application/futuresplash';
-$conf['mime-types']['cpt'] = 'application/mac-compactpro';
-$conf['mime-types']['hqx'] = 'application/mac-binhex40';
-$conf['mime-types']['nb'] = 'application/mathematica';
-$conf['mime-types']['mdb'] = 'application/msaccess';
-$conf['mime-types']['doc'] = 'application/msword';
-$conf['mime-types']['dot'] = 'application/msword';
-$conf['mime-types']['bin'] = 'application/octet-stream';
-$conf['mime-types']['oda'] = 'application/oda';
-$conf['mime-types']['pdf'] = 'application/pdf';
-$conf['mime-types']['pgp'] = 'application/pgp-signature';
-$conf['mime-types']['ps'] = 'application/postscript';
-$conf['mime-types']['ai'] = 'application/postscript';
-$conf['mime-types']['eps'] = 'application/postscript';
-$conf['mime-types']['rtf'] = 'application/rtf';
-$conf['mime-types']['smi'] = 'application/smil';
-$conf['mime-types']['smil'] = 'application/smil';
-$conf['mime-types']['xls'] = 'application/vnd.ms-excel';
-$conf['mime-types']['xlb'] = 'application/vnd.ms-excel';
-$conf['mime-types']['ppt'] = 'application/vnd.ms-powerpoint';
-$conf['mime-types']['pps'] = 'application/vnd.ms-powerpoint';
-$conf['mime-types']['pot'] = 'application/vnd.ms-powerpoint';
-$conf['mime-types']['sdw'] = 'application/vnd.stardivision.writer';
-$conf['mime-types']['sgl'] = 'application/vnd.stardivision.writer-global';
-$conf['mime-types']['vor'] = 'application/vnd.stardivision.writer';
-$conf['mime-types']['sdc'] = 'application/vnd.stardivision.calc';
-$conf['mime-types']['sda'] = 'application/vnd.stardivision.draw';
-$conf['mime-types']['sdd'] = 'application/vnd.stardivision.impress';
-$conf['mime-types']['sdp'] = 'application/vnd.stardivision.impress-packed';
-$conf['mime-types']['smf'] = 'application/vnd.stardivision.math';
-$conf['mime-types']['sds'] = 'application/vnd.stardivision.chart';
-$conf['mime-types']['smd'] = 'application/vnd.stardivision.mail';
-$conf['mime-types']['wbxml'] = 'application/vnd.wap.wbxml ';
-$conf['mime-types']['wmlc'] = 'application/vnd.wap.wmlc';
-$conf['mime-types']['wmlsc'] = 'application/vnd.wap.wmlscriptc';
-$conf['mime-types']['wp5'] = 'application/wordperfect5.1';
-$conf['mime-types']['zip'] = 'application/zip';
-$conf['mime-types']['wk'] = 'application/x-123';
-$conf['mime-types']['bcpio'] = 'application/x-bcpio';
-$conf['mime-types']['vcd'] = 'application/x-cdlink ';
-$conf['mime-types']['pgn'] = 'application/x-chess-pgn';
-$conf['mime-types']['cpio'] = 'application/x-cpio';
-$conf['mime-types']['csh'] = 'application/x-csh';
-$conf['mime-types']['deb'] = 'application/x-debian-package';
-$conf['mime-types']['dcr'] = 'application/x-director';
-$conf['mime-types']['dir'] = 'application/x-director';
-$conf['mime-types']['dxr'] = 'application/x-director';
-$conf['mime-types']['wad'] = 'application/x-doom';
-$conf['mime-types']['dms'] = 'application/x-dms';
-$conf['mime-types']['dvi'] = 'application/x-dvi';
-$conf['mime-types']['pfa'] = 'application/x-font';
-$conf['mime-types']['pfb'] = 'application/x-font';
-$conf['mime-types']['gsf'] = 'application/x-font';
-$conf['mime-types']['pcf'] = 'application/x-font';
-$conf['mime-types']['spl'] = 'application/x-futuresplash ';
-$conf['mime-types']['gnumeric'] = 'application/x-gnumeric';
-$conf['mime-types']['gtar'] = 'application/x-gtar';
-$conf['mime-types']['tgz'] = 'application/x-gtar';
-$conf['mime-types']['taz'] = 'application/x-gtar';
-$conf['mime-types']['hdf'] = 'application/x-hdf';
-$conf['mime-types']['phtml'] = 'text/html';
-$conf['mime-types']['pht'] = 'text/html';
-$conf['mime-types']['php'] = 'text/html';
-$conf['mime-types']['phps'] = 'text/html';
-$conf['mime-types']['php3'] = 'text/html';
-$conf['mime-types']['php3p'] = 'text/html ';
-$conf['mime-types']['php4'] = 'text/html';
-$conf['mime-types']['docbook'] = 'application/docbook+xml';
-$conf['mime-types']['ica'] = 'application/x-ica';
-$conf['mime-types']['jar'] = 'application/x-java-archive';
-$conf['mime-types']['jnlp'] = 'application/x-java-jnlp-file';
-$conf['mime-types']['ser'] = 'application/x-java-serialized-object';
-$conf['mime-types']['class'] = 'application/x-java-vm';
-$conf['mime-types']['js'] = 'application/x-javascript';
-$conf['mime-types']['chrt'] = 'application/x-kchart';
-$conf['mime-types']['kil'] = 'application/x-killustrator';
-$conf['mime-types']['kpr'] = 'application/x-kpresenter';
-$conf['mime-types']['kpt'] = 'application/x-kpresenter';
-$conf['mime-types']['skp'] = 'application/x-koan ';
-$conf['mime-types']['skd'] = 'application/x-koan ';
-$conf['mime-types']['skt'] = 'application/x-koan ';
-$conf['mime-types']['skm'] = 'application/x-koan ';
-$conf['mime-types']['ksp'] = 'application/x-kspread';
-$conf['mime-types']['kwd'] = 'application/x-kword';
-$conf['mime-types'][' kwt'] = 'application/x-kword';
-$conf['mime-types']['latex'] = 'application/x-latex';
-$conf['mime-types']['lha'] = 'application/x-lha';
-$conf['mime-types']['lzh'] = 'application/x-lzh';
-$conf['mime-types']['lzx'] = 'application/x-lzx';
-$conf['mime-types']['frm'] = 'fbdocapplication/x-maker';
-$conf['mime-types']['maker'] = 'fbdocapplication/x-maker';
-$conf['mime-types']['frame'] = 'fbdocapplication/x-maker';
-$conf['mime-types']['fm'] = 'fbdocapplication/x-maker';
-$conf['mime-types']['fb'] = 'fbdocapplication/x-maker';
-$conf['mime-types']['book'] = 'fbdocapplication/x-maker';
-$conf['mime-types']['mif'] = 'application/x-mif';
-$conf['mime-types']['com'] = 'application/x-msdos-program';
-$conf['mime-types']['exe'] = 'application/x-msdos-program';
-$conf['mime-types']['bat'] = 'application/x-msdos-program';
-$conf['mime-types']['dll'] = 'application/x-msdos-program';
-$conf['mime-types']['msi'] = 'application/x-msi';
-$conf['mime-types']['nc'] = 'application/x-netcdf';
-$conf['mime-types']['cdf'] = 'application/x-netcdf';
-$conf['mime-types']['pac'] = 'application/x-ns-proxy-autoconfig';
-$conf['mime-types']['o'] = 'application/x-object';
-$conf['mime-types']['ogg'] = 'application/x-ogg';
-$conf['mime-types']['oza'] = 'application/x-oz-application';
-$conf['mime-types']['pl'] = 'application/x-perl';
-$conf['mime-types']['pm'] = 'application/x-perl';
-$conf['mime-types']['crl'] = 'application/x-pkcs7-crl';
-$conf['mime-types']['rpm'] = 'application/x-redhat-package-manager';
-$conf['mime-types']['shar'] = 'application/x-shar';
-$conf['mime-types']['swf'] = 'application/x-shockwave-flash';
-$conf['mime-types']['swfl'] = 'application/x-shockwave-flash';
-$conf['mime-types']['sh'] = 'application/x-sh ';
-$conf['mime-types']['sit'] = 'application/x-stuffit';
-$conf['mime-types']['sv4cpio'] = 'application/x-sv4cpio';
-$conf['mime-types']['sv4crc'] = 'application/x-sv4crc';
-$conf['mime-types']['tar'] = 'application/x-tar';
-$conf['mime-types']['tcl'] = 'application/x-tcl';
-$conf['mime-types']['tex'] = 'application/x-tex';
-$conf['mime-types']['gf'] = 'application/x-tex-gf';
-$conf['mime-types']['pk'] = 'application/x-tex-pk';
-$conf['mime-types']['texinfo'] = 'application/x-texinfo';
-$conf['mime-types']['texi'] = 'application/x-texinfo';
-$conf['mime-types']['; "~"'] = 'application/x-trash';
-$conf['mime-types'][';"%"'] = 'application/x-trash';
-$conf['mime-types']['bak'] = 'application/x-trash';
-$conf['mime-types']['old'] = 'application/x-trash';
-$conf['mime-types']['sik'] = 'application/x-trash';
-$conf['mime-types']['t'] = 'application/x-troff';
-$conf['mime-types']['tr'] = 'application/x-troff';
-$conf['mime-types']['roff'] = 'application/x-troff';
-$conf['mime-types']['man'] = 'application/x-troff-man';
-$conf['mime-types']['me'] = 'application/x-troff-me';
-$conf['mime-types']['ms'] = 'application/x-troff-ms';
-$conf['mime-types']['ustar'] = 'application/x-ustar';
-$conf['mime-types']['src'] = 'application/x-wais-source';
-$conf['mime-types']['wz'] = 'application/x-wingz';
-$conf['mime-types']['crt'] = 'application/x-x509-ca-cert';
-$conf['mime-types']['fig'] = 'application/x-xfig';
-$conf['mime-types']['au'] = 'audio/basic';
-$conf['mime-types']['snd'] = 'audio/basic';
-$conf['mime-types']['mid'] = 'audio/midi';
-$conf['mime-types']['midi'] = 'audio/midi';
-$conf['mime-types']['kar'] = 'audio/midi';
-$conf['mime-types']['mpga'] = 'audio/mpeg';
-$conf['mime-types']['mpega'] = 'audio/mpeg';
-$conf['mime-types']['mp2'] = 'audio/mpeg';
-$conf['mime-types']['mp3'] = 'audio/mpeg';
-$conf['mime-types']['m3u'] = 'audio/mpegurl';
-$conf['mime-types']['sid'] = 'audio/prs.sid';
-$conf['mime-types']['aif'] = 'audio/x-aiff';
-$conf['mime-types']['aiff'] = 'audio/x-aiff';
-$conf['mime-types']['aifc'] = 'audio/x-aiff';
-$conf['mime-types']['gsm'] = 'audio/x-gsm';
-$conf['mime-types']['m3u'] = 'audio/x-mpegurl';
-$conf['mime-types']['rpm'] = 'audio/x-pn-realaudio-plugin ';
-$conf['mime-types']['ra'] = 'audio/x-pn-realaudio';
-$conf['mime-types']['rm'] = 'audio/x-pn-realaudio';
-$conf['mime-types']['ram'] = 'audio/x-pn-realaudio';
-$conf['mime-types']['ra'] = 'audio/x-realaudio ';
-$conf['mime-types']['pls'] = 'audio/x-scpls';
-$conf['mime-types']['wav'] = 'audio/x-wav';
-$conf['mime-types']['pdb'] = 'chemical/x-pdb';
-$conf['mime-types']['xyz'] = 'chemical/x-xyz ';
-$conf['mime-types']['bmp'] = 'image/bmp';
-$conf['mime-types']['gif'] = 'image/gif';
-$conf['mime-types']['ief'] = 'image/ief';
-$conf['mime-types']['jpeg'] = 'image/jpeg';
-$conf['mime-types']['jpg'] = 'image/jpeg';
-$conf['mime-types']['jpe'] = 'image/jpeg';
-$conf['mime-types']['pcx'] = 'image/pcx';
-$conf['mime-types']['png'] = 'image/png';
-$conf['mime-types']['svg'] = 'image/svg+xml';
-$conf['mime-types']['svgz'] = 'image/svg+xml';
-$conf['mime-types']['tiff'] = 'image/tiff';
-$conf['mime-types']['tif'] = 'image/tiff';
-$conf['mime-types']['wbmp'] = 'image/vnd.wap.wbmp';
-$conf['mime-types']['ras'] = 'image/x-cmu-raster';
-$conf['mime-types']['cdr'] = 'image/x-coreldraw';
-$conf['mime-types']['pat'] = 'image/x-coreldrawpattern';
-$conf['mime-types']['cdt'] = 'image/x-coreldrawtemplate';
-$conf['mime-types']['cpt'] = 'image/x-corelphotopaint';
-$conf['mime-types']['djvu'] = 'image/x-djvu';
-$conf['mime-types']['djv'] = 'image/x-djvu';
-$conf['mime-types']['jng'] = 'image/x-jng';
-$conf['mime-types']['bmp'] = 'image/x-ms-bmp';
-$conf['mime-types']['pnm'] = 'image/x-portable-anymap';
-$conf['mime-types']['pbm'] = 'image/x-portable-bitmap';
-$conf['mime-types']['pgm'] = 'image/x-portable-graymap';
-$conf['mime-types']['ppm'] = 'image/x-portable-pixmap';
-$conf['mime-types']['rgb'] = 'image/x-rgb';
-$conf['mime-types']['xbm'] = 'image/x-xbitmap';
-$conf['mime-types']['xpm'] = 'image/x-xpixmap';
-$conf['mime-types']['xwd'] = 'image/x-xwindowdump';
-$conf['mime-types']['igs'] = 'model/iges';
-$conf['mime-types']['iges'] = 'model/iges';
-$conf['mime-types']['msh'] = 'model/mesh';
-$conf['mime-types']['mesh'] = 'model/mesh';
-$conf['mime-types']['silo'] = 'model/mesh';
-$conf['mime-types']['wrl'] = 'model/vrml';
-$conf['mime-types']['vrml'] = 'model/vrml';
-$conf['mime-types']['csv'] = 'text/comma-separated-values';
-$conf['mime-types']['css'] = 'text/css';
-$conf['mime-types']['htm'] = 'text/html';
-$conf['mime-types']['html'] = 'text/html';
-$conf['mime-types']['xhtml'] = 'text/html';
-$conf['mime-types']['mml'] = 'text/mathml';
-$conf['mime-types']['asc'] = 'text/plain';
-$conf['mime-types']['txt'] = 'text/plain';
-$conf['mime-types']['text'] = 'text/plain';
-$conf['mime-types']['diff'] = 'text/plain';
-$conf['mime-types']['rtx'] = 'text/richtext';
-$conf['mime-types']['rtf'] = 'text/rtf';
-$conf['mime-types']['tsv'] = 'text/tab-separated-values';
-$conf['mime-types']['wml'] = 'text/vnd.wap.wml';
-$conf['mime-types']['wmls'] = 'text/vnd.wap.wmlscript';
-$conf['mime-types']['xml'] = 'text/xml';
-$conf['mime-types']['xsl'] = 'text/xml';
-$conf['mime-types']['hpp'] = 'text/x-c++hdr';
-$conf['mime-types']['hxx'] = 'text/x-c++hdr';
-$conf['mime-types']['hh'] = 'text/x-c++hdr';
-$conf['mime-types']['cpp'] = 'text/x-c++src';
-$conf['mime-types']['cxx'] = 'text/x-c++src';
-$conf['mime-types']['cc'] = 'text/x-c++src';
-$conf['mime-types']['h'] = 'text/x-chdr';
-$conf['mime-types']['csh'] = 'text/x-csh';
-$conf['mime-types']['c'] = 'text/x-csrc';
-$conf['mime-types']['java'] = 'text/x-java';
-$conf['mime-types']['moc'] = 'text/x-moc';
-$conf['mime-types']['p'] = 'text/x-pascal';
-$conf['mime-types']['pas'] = 'text/x-pascal';
-$conf['mime-types']['etx'] = 'text/x-setext';
-$conf['mime-types']['sh'] = 'text/x-sh';
-$conf['mime-types']['tcl'] = 'text/x-tcl';
-$conf['mime-types']['tk'] = 'text/x-tcl';
-$conf['mime-types']['tex'] = 'text/x-tex';
-$conf['mime-types']['ltx'] = 'text/x-tex';
-$conf['mime-types']['sty'] = 'text/x-tex';
-$conf['mime-types']['cls'] = 'text/x-tex';
-$conf['mime-types']['vcs'] = 'text/x-vcalendar';
-$conf['mime-types']['vcf'] = 'text/x-vcard';
-$conf['mime-types']['dl'] = 'video/dl';
-$conf['mime-types']['fli'] = 'video/fli';
-$conf['mime-types']['gl'] = 'video/gl';
-$conf['mime-types']['mpeg'] = 'video/mpeg';
-$conf['mime-types']['mpg'] = 'video/mpeg';
-$conf['mime-types']['mpe'] = 'video/mpeg';
-$conf['mime-types']['qt'] = 'video/quicktime';
-$conf['mime-types']['mov'] = 'video/quicktime';
-$conf['mime-types']['mxu'] = 'video/vnd.mpegurl';
-$conf['mime-types']['mng'] = 'video/x-mng';
-$conf['mime-types']['asf'] = 'video/x-ms-asf';
-$conf['mime-types']['asx'] = 'video/x-ms-asf';
-$conf['mime-types']['avi'] = 'video/x-msvideo';
-$conf['mime-types']['movie'] = 'video/x-sgi-movie';
-$conf['mime-types']['ice'] = 'x-conference/x-cooltalk';
-$conf['mime-types']['vrm'] = 'x-world/x-vrml';
-$conf['mime-types']['vrml'] = 'x-world/x-vrml';
-$conf['mime-types']['wrl'] = 'x-world/x-vrml';
-
-$conf['publish'] = array();
-$conf['publish']['edit']=true;
-$conf['publish']['default']='index';
-$conf['publish']['format']= "{filename}{language_sep}{language}{type_sep}{type}";
-$conf['publish']['language_sep']= ".";
-$conf['publish']['type_sep']= ".";
-$conf['publish']['filename_language']='auto';
-$conf['publish']['filename_type']='always';
-$conf['publish']['style']="id";
-$conf['publish']['url']='relative';
-$conf['publish']['url']='absolute';
-$conf['publish']['enable_php_in_page_content']=false;
-$conf['publish']['enable_php_in_file_content']=false;
-$conf['publish']['escape_8bit_characters']=false;
-$conf['publish']['encode_utf8_in_html']=true;
-$conf['publish']['negotiation'] = array();
-$conf['publish']['negotiation']['page_negotiate_type']=true;
-$conf['publish']['negotiation']['page_negotiate_language']=true;
-$conf['publish']['negotiation']['file_negotiate_type']=true;
-$conf['publish']['filesystem'] = array();
-$conf['publish']['filesystem']['per_project']=true;
-$conf['publish']['filesystem']['directory']='/var/www/';
-$conf['publish']['command'] = array();
-$conf['publish']['command']['per_project']=true;
-$conf['publish']['command']['enable']=false;
-$conf['publish']['command']['command']='';
-$conf['publish']['ftp'] = array();
-$conf['publish']['ftp']['enable']=true;
-$conf['publish']['ftp']['per_project']=true;
-$conf['publish']['ftp']['port']='21';
-$conf['publish']['ftp']['host']='';
-$conf['publish']['ftp']['path']='';
-$conf['publish']['ftp']['user']='anonymous';
-$conf['publish']['ftp']['pass']='mail@example.com';
-$conf['replace'] = array();
-$conf['replace']['']='0';
-$conf['replace']['']='0';
-$conf['replace']['euro']= "EUR,€";
-$conf['replace']['copy']= "(c),©";
-$conf['search'] = array();
-$conf['search']['']='0';
-$conf['search']['quicksearch'] = array();
-$conf['search']['quicksearch']['flag'] = array();
-$conf['search']['quicksearch']['flag']['id']=true;
-$conf['search']['quicksearch']['flag']['name']=true;
-$conf['search']['quicksearch']['flag']['filename']=true;
-$conf['search']['quicksearch']['flag']['description']=true;
-$conf['search']['quicksearch']['flag']['content']=false;
-$conf['security'] = array();
-$conf['security']['readonly']=false;
-$conf['security']['nopublish']=false;
-$conf['security']['umask']='0';
-$conf['security']['chmod']='0';
-$conf['security']['chmod_dir']='0';
-$conf['security']['']='0';
-$conf['security']['disable_dynamic_code']=true;
-$conf['security']['show_system_info']=true;
-$conf['security']['use_post_token']=true;
-$conf['security']['renew_session_login']=false;
-$conf['security']['renew_session_logout']=false;
-$conf['security']['default'] = array();
-$conf['security']['default']['username']='';
-$conf['security']['default']['password']='';
-$conf['security']['guest'] = array();
-$conf['security']['guest']['enable']=false;
-$conf['security']['guest']['user']='guest';
-$conf['security']['login'] = array();
-$conf['security']['login']['type']='form';
-$conf['security']['auth'] = array();
-$conf['security']['auth']['type']='database';
-$conf['security']['auth']['userdn']=false;
-$conf['security']['authorize'] = array();
-$conf['security']['authorize']['type']='database';
-$conf['security']['authorize']['type']='ldap';
-
-$conf['security']['modules'] = array();
-$conf['security']['modules']['autologin']='Remember,Guest,SingleSignon';
-$conf['security']['modules']['preselect']='Ident,SSL,Cookie';
-$conf['security']['modules']['authenticate']='LdapUserDN,Database,Internal';
-
-$conf['security']['newuser'] = array();
-$conf['security']['newuser']['autoadd'] = true;
-$conf['security']['newuser']['autogroups'] = "";
-
-$conf['security']['password'] = array();
-$conf['security']['password']['random_length']=10;
-$conf['security']['password']['min_length']=6;
-$conf['security']['password']['pepper']= '';
-$conf['security']['password']['deny_after_expiration_duration'] = 72;
-$conf['security']['password']['force_change_if_cleartext']= true;
-$conf['security']['http'] = array();
-$conf['security']['http']['url']= "http://example.net/restricted-area";
-$conf['security']['authdb'] = array();
-$conf['security']['authdb']['enable'] = false;
-$conf['security']['authdb']['type']='postgresql';
-$conf['security']['authdb']['user']='dbuser';
-$conf['security']['authdb']['password']='dbpassword';
-$conf['security']['authdb']['host']= '127.0.0.1';
-$conf['security']['authdb']['database']='dbname';
-$conf['security']['authdb']['persistent']=false;
-$conf['security']['authdb']['prepare']=false;
-$conf['security']['authdb']['sql']= "select 1 from table where user={username} and password={password}";
-$conf['security']['authdb']['hash_algo']='md5';
-$conf['security']['authdb']['add']=true;
-$conf['security']['ssl'] = array();
-$conf['security']['ssl']['trust']=false;
-$conf['security']['ssl']['client_cert_dn_env'] = 'SSL_CLIENT_S_DN_CN';
-$conf['security']['openid'] = array();
-$conf['security']['openid']['enable']=false;
-$conf['security']['openid']['add']=false;
-$conf['security']['openid']['logo_url']='0';
-$conf['security']['openid']['logo_url']="http://openid.net/login-bg.gif";
-$conf['security']['openid']['trust_root']='http://your.server.example/openrat/';
-$conf['security']['openid']['trust_root']='0';
-$conf['security']['openid']['trusted_server']='openid1.example.com,openid2.example.com';
-$conf['security']['openid']['trusted_server']='0';
-$conf['security']['openid']['update_user']=true;
-$conf['security']['openid']['user_identity']=true;
-$conf['security']['openid']['provider']['name']='google';
-$conf['security']['openid']['provider']['google']['xrds_uri']="http://google.com/accounts/o8/id";
-$conf['security']['openid']['provider']['google']['map_attribute']="email";
-$conf['security']['openid']['provider']['google']['name']="Google";
-$conf['security']['openid']['provider']['google']['map_internal']="mail";
-$conf['security']['openid']['provider']['yahoo']['xrds_uri']="http://??????";
-$conf['security']['openid']['provider']['yahoo']['map_attribute']="usename";
-$conf['security']['openid']['provider']['yahoo']['map_internal']="mail";
-$conf['security']['sso'] = array();
-$conf['security']['sso']['enable']=false;
-$conf['security']['sso']['url']="http://localhost/check.php?phpsessid={id}&check=true";
-$conf['security']['sso']['url']="https://www.example.com/phpmyadmin/main.php?server=1";
-$conf['security']['sso']['auth_param_name']='authid';
-$conf['security']['sso']['auth_param_serialized']=true;
-$conf['security']['sso']['cookie']=true;
-$conf['security']['sso']['cookie_name']='0';
-$conf['security']['sso']['force']=true;
-$conf['security']['sso']['expect']='0';
-$conf['security']['sso']['expect_regexp']="/running on/";
-$conf['security']['sso']['username_regexp']="/running on localhost as ([a-z]+)@localhost/";
-$conf['security']['logout'] = array();
-$conf['security']['logout']['redirect_url']="http://your.intranet.example/";
-$conf['security']['logout']['redirect_url']='0';
-$conf['security']['user'] = array();
-$conf['security']['user']['show_admin_mail']=true;
-$conf['security']['user']['show_mail']=true;
-$conf['security']['user']['send_message']=true;
-$conf['security']['content-security-policy']=true;
-
-$conf['style-default'] = array();
-$conf['style-default']['name']='Unnamed';
-$conf['style-default']['title_background_color']='grey';
-$conf['style-default']['title_text_color']='white';
-$conf['style-default']['text_color' ]= 'black';
-$conf['style-default']['background_color' ]= '#d9d9d9';
-$conf['style-default']['inactive_background_color' ]= 'silver';
-
-
-$conf['style'] = array();
-$conf['style']['earlgrey']=array();
-$conf['style']['earlgrey']['name']='Earl grey';
-$conf['style']['earlgrey']['title_background_color']='grey';
-$conf['style']['earlgrey']['title_text_color']='white';
-$conf['style']['earlgrey']['text_color'] ='black';
-$conf['style']['earlgrey']['background_color'] = '#e9e9e9';
-$conf['style']['earlgrey']['inactive_background_color'] = 'silver';
-
-// $conf['style']['system']=array();
-// $conf['style']['system']['name']='System colors';
-// $conf['style']['system']['title_background_color']='Menu';
-// $conf['style']['system']['title_text_color']='MenuText';
-// $conf['style']['system']['text_color'] ='WindowText';
-// $conf['style']['system']['background_color'] = 'Background';
-// $conf['style']['system']['inactive_background_color'] = 'WindowFrame';
-
-$conf['style']['modern']=array();
-$conf['style']['modern']['name']='Blue sky';
-$conf['style']['modern']['title_background_color']='#3F6194';
-$conf['style']['modern']['title_text_color']='white';
-$conf['style']['modern']['text_color'] ='black';
-$conf['style']['modern']['background_color'] = '#F3F3F3';
-$conf['style']['modern']['inactive_background_color'] = '#CCCCCC';
-
-$conf['style']['moorweide']=array();
-$conf['style']['moorweide']['name']='Moorweide';
-$conf['style']['moorweide']['title_background_color']='#006633';
-$conf['style']['moorweide']['title_text_color']='white';
-$conf['style']['moorweide']['text_color'] ='black';
-$conf['style']['moorweide']['background_color'] = '#F5FFFA';
-$conf['style']['moorweide']['inactive_background_color'] = '#CEE6DA';
-
-$conf['style']['dark']=array();
-$conf['style']['dark']['name']='Dark';
-$conf['style']['dark']['title_background_color']='#868685';
-$conf['style']['dark']['title_text_color']='#DCDCDC';
-$conf['style']['dark']['text_color'] ='#FFFFFF';
-$conf['style']['dark']['background_color'] = '#201F1D';
-$conf['style']['dark']['inactive_background_color'] = '#868685';
-
-$conf['theme'] = array();
-$conf['theme']['compiler'] = array();
-$conf['theme']['compiler']['enable']=true;
-$conf['theme']['compiler']['cache']=true;
-$conf['theme']['compiler']['chmod']='';
-$conf['theme']['compiler']['compile_at_logout']=false;
-$conf['wiki'] = array();
-$conf['wiki']['convert_html']=true;
-$conf['wiki']['convert_bbcode']=true;
-$conf['wiki']['tag_strong']= "*";
-$conf['wiki']['tag_emphatic']= "_";
-
-$conf['application']['name' ] = OR_TITLE;
-$conf['application']['version' ] = OR_VERSION;
-$conf['application']['operator'] = OR_TITLE;
-$conf['production']= true;
-
-?>-
\ No newline at end of file
diff --git a/util/exception/OpenRatException.class.php b/util/exception/OpenRatException.class.php
@@ -1,25 +0,0 @@
-<?php
-
-class OpenRatException extends Exception
-{
- public $key;
-
- // Die Exception neu definieren, damit die Mitteilung nicht optional ist
- public function __construct($key, $message, $code = 0, Exception $previous = null) {
-
- $this->key = $key;
-
- // sicherstellen, dass alles korrekt zugewiesen wird
- parent::__construct($message, $code, $previous);
- }
-
- // maßgeschneiderte Stringdarstellung des Objektes
- public function __toString() {
- return __CLASS__ . ": ".$this->key." [{$this->code}]: '{$this->message}' in {$this->file}({$this->line})\n"
- . "{$this->getTraceAsString()}\n";
- }
-
-}
-
-
-?>-
\ No newline at end of file
diff --git a/util/exception/SecurityException.class.php b/util/exception/SecurityException.class.php
@@ -1,9 +0,0 @@
-<?php
-
-class SecurityException extends RuntimeException
-{
-
-}
-
-
-?>-
\ No newline at end of file
diff --git a/util/include.inc.php b/util/include.inc.php
@@ -1,65 +0,0 @@
-<?php
-
-require_once( OR_SERVICECLASSES_DIR."GlobalFunctions.class.".PHP_EXT );
-require_once( OR_SERVICECLASSES_DIR."Http.class.".PHP_EXT );
-require_once( OR_SERVICECLASSES_DIR."Html.class.".PHP_EXT );
-require_once( OR_SERVICECLASSES_DIR."Text.class.".PHP_EXT );
-require_once( OR_SERVICECLASSES_DIR."Mail.class.".PHP_EXT );
-require_once( OR_SERVICECLASSES_DIR."Ldap.class.".PHP_EXT );
-require_once( OR_SERVICECLASSES_DIR."FileUtils.class.".PHP_EXT );
-require_once( OR_SERVICECLASSES_DIR."JSON.class.".PHP_EXT );
-require_once( OR_SERVICECLASSES_DIR."Less.".PHP_EXT );
-require_once( OR_SERVICECLASSES_DIR."JSqueeze.class.".PHP_EXT );
-require_once( OR_SERVICECLASSES_DIR."Spyc.class.".PHP_EXT );
-require_once( OR_SERVICECLASSES_DIR."exception/OpenRatException.class.".PHP_EXT );
-require_once( OR_SERVICECLASSES_DIR."exception/SecurityException.class.".PHP_EXT );
-
-
-
-//if ( !empty($REQ[REQ_PARAM_ACTION]) && in_array($REQ[REQ_PARAM_ACTION],array('tree')) )
-{
- require_once( OR_SERVICECLASSES_DIR."TreeElement.class.".PHP_EXT );
- require_once( OR_SERVICECLASSES_DIR."AbstractTree.class.".PHP_EXT );
- require_once( OR_SERVICECLASSES_DIR."AdministrationTree.class.".PHP_EXT );
- require_once( OR_SERVICECLASSES_DIR."ProjectTree.class.".PHP_EXT );
-}
-
-// Veroeffentlichung
-//if ( !empty($REQ[REQ_PARAM_ACTION]) && in_array($REQ[REQ_PARAM_ACTION],array('file','page','pageelement','folder')) )
-{
- require_once( OR_SERVICECLASSES_DIR."Publish.class.".PHP_EXT );
- require_once( OR_SERVICECLASSES_DIR."Ftp.class.".PHP_EXT );
-}
-
-// Nur bei der Erzeugung von Seiten notwendig.
-//if ( !empty($REQ[REQ_PARAM_ACTION]) && in_array($REQ[REQ_PARAM_ACTION],array('pageelement','page','folder','element')) )
-{
- require_once( OR_SERVICECLASSES_DIR."Macro.class.".PHP_EXT );
- require_once( OR_SERVICECLASSES_DIR."Dynamic.class.".PHP_EXT );
-}
-
-// Nur bei der Erzeugung von Seiten notwendig.
-//if ( !empty($REQ[REQ_PARAM_ACTION]) && in_array($REQ[REQ_PARAM_ACTION],array('pageelement','page','folder')) )
-{
- require_once( OR_SERVICECLASSES_DIR."Api.class.".PHP_EXT );
- require_once( OR_SERVICECLASSES_DIR."Code.class.".PHP_EXT );
- require_once( OR_SERVICECLASSES_DIR."Transformer.class.".PHP_EXT );
- require_once( OR_SERVICECLASSES_DIR."Line.class.".PHP_EXT );
-}
-
-
-//if ( !empty($REQ[REQ_PARAM_ACTION]) && in_array($REQ[REQ_PARAM_ACTION],array('file','folder','filebrowser')) )
-{
- require_once( OR_SERVICECLASSES_DIR."Upload.class.".PHP_EXT );
-}
-
-
-//if ( !empty($REQ[REQ_PARAM_ACTION]) && in_array($REQ[REQ_PARAM_ACTION],array('file','folder')) )
-{
- require_once( OR_SERVICECLASSES_DIR."Upload.class.".PHP_EXT );
- require_once( OR_SERVICECLASSES_DIR."ArchiveTar.class.".PHP_EXT );
- require_once( OR_SERVICECLASSES_DIR."ArchiveUnzip.class.".PHP_EXT );
- require_once( OR_SERVICECLASSES_DIR."ArchiveZip.class.".PHP_EXT );
-}
-
-?>-
\ No newline at end of file