[SPIP] +2.1.12
[velocampus/web/www.git] / www / extensions / compresseur / inc / compresseur.php
diff --git a/www/extensions/compresseur/inc/compresseur.php b/www/extensions/compresseur/inc/compresseur.php
new file mode 100644 (file)
index 0000000..0dbbf2e
--- /dev/null
@@ -0,0 +1,331 @@
+<?php
+
+if (!defined("_ECRIRE_INC_VERSION")) return;
+
+function compacte_ecrire_balise_script_dist($src){
+       return "<script type='text/javascript' src='$src'></script>";
+}
+function compacte_ecrire_balise_link_dist($src,$media=""){
+       return "<link rel='stylesheet'".($media?" media='$media'":"")." href='$src' type='text/css' />";
+}
+
+
+// http://doc.spip.org/@compacte_css
+function compacte_css ($contenu) {
+       // nettoyer la css de tout ce qui sert pas
+       $contenu = preg_replace(",/\*.*\*/,Ums","",$contenu); // pas de commentaires
+       $contenu = preg_replace(",\s(?=\s),Ums","",$contenu); // pas d'espaces consecutifs
+       $contenu = preg_replace("/\s?({|;|,|:)\s?/ms","$1",$contenu); // pas d'espaces dans les declarations css
+       $contenu = preg_replace("/\s}/ms","}",$contenu); // pas d'espaces dans les declarations css
+       // passser les codes couleurs en 3 car si possible
+       // uniquement si non precedees d'un [="'] ce qui indique qu'on est dans un filter(xx=#?...)
+       $contenu = preg_replace(";([:\s,(])#([0-9a-f])(\\2)([0-9a-f])(\\4)([0-9a-f])(\\6)(?=[^\w\-]);i","$1#$2$4$6",$contenu);
+       // supprimer les declarations vides
+       $contenu = preg_replace(",(^|})([^{}]*){},Ums","$1",$contenu);
+       $contenu = trim($contenu);
+
+       return $contenu;
+}
+
+// Compacte du javascript grace a Dean Edward's JavaScriptPacker
+// utile pour prive/jquery.js par exemple
+// http://doc.spip.org/@compacte_js
+function compacte_js($flux) {
+       // si la closure est demandee, on pourrait zapper cette etape
+       // mais avec le risque, en localhost, de depasser 200ko et d'echec
+       #if ($GLOBALS['meta']['auto_compress_closure'] == 'oui')
+       #       return $flux;
+
+       if (!strlen($flux))
+               return $flux;
+       include_spip('lib/JavascriptPacker/class.JavaScriptPacker');
+       $packer = new JavaScriptPacker($flux, 0, true, false);
+
+       // en cas d'echec (?) renvoyer l'original
+       if (!strlen($t = $packer->pack())) {
+               spip_log('erreur de compacte_js');
+               return $flux;
+       }
+
+       return $t;
+}
+
+
+// Appelee par compacte_head() si le webmestre le desire, cette fonction
+// compacte les scripts js dans un fichier statique pose dans local/
+// en entree : un <head> html.
+// http://doc.spip.org/@compacte_head_js
+function compacte_head_js($flux) {
+       $url_base = url_de_base();
+       $url_page = substr(generer_url_public('A'), 0, -1);
+       $dir = preg_quote($url_page,',').'|'.preg_quote(preg_replace(",^$url_base,",_DIR_RACINE,$url_page),',');
+
+       $scripts = array();
+       $flux_nocomment = preg_replace(",<!--.*-->,Uims","",$flux);
+       foreach (extraire_balises($flux_nocomment,'script') as $s) {
+               if (extraire_attribut($s, 'type') === 'text/javascript'
+               AND is_null(extraire_attribut($s, 'id')) # script avec un id : pas touche
+               AND $src = extraire_attribut($s, 'src')
+               AND !strlen(strip_tags($s))
+               AND (
+                       preg_match(',^('.$dir.')(.*)$,', $src, $r)
+                       OR (
+                               // ou si c'est un fichier
+                               $src = preg_replace(',^'.preg_quote(url_de_base(),',').',', '', $src)
+                               // enlever un timestamp eventuel derriere un nom de fichier statique
+                               AND $src2 = preg_replace(",[.]js[?].+$,",'.js',$src)
+                               // verifier qu'il n'y a pas de ../ ni / au debut (securite)
+                               AND !preg_match(',(^/|\.\.),', substr($src,strlen(_DIR_RACINE)))
+                               // et si il est lisible
+                               AND @is_readable($src2)
+                       )
+               )) {
+                       if ($r)
+                               $scripts[$s] = explode('&',
+                                       str_replace('&amp;', '&', $r[2]), 2);
+                       else
+                               $scripts[$s] = $src;
+               }
+       }
+
+       if (list($src,$comms) = filtre_cache_static($scripts,'js')){
+               $compacte_ecrire_balise_script = charger_fonction('compacte_ecrire_balise_script','');
+               $scripts = array_keys($scripts);
+               $flux = str_replace(reset($scripts),
+                       $comms .$compacte_ecrire_balise_script($src)."\n",
+                       $flux);
+               $flux = str_replace($scripts,"",$flux);
+       }
+
+       return $flux;
+}
+
+// Appelee par compacte_head() si le webmestre le desire, cette fonction
+// compacte les feuilles de style css dans un fichier statique pose dans local/
+// en entree : un <head> html.
+// http://doc.spip.org/@compacte_head_css
+function compacte_head_css($flux) {
+       $url_base = url_de_base();
+       $url_page = substr(generer_url_public('A'), 0, -1);
+       $dir = preg_quote($url_page,',').'|'.preg_quote(preg_replace(",^$url_base,",_DIR_RACINE,$url_page),',');
+
+       $css = array();
+       $flux_nocomment = preg_replace(",<!--.*-->,Uims","",$flux);
+       foreach (extraire_balises($flux_nocomment, 'link') as $s) {
+               if (extraire_attribut($s, 'rel') === 'stylesheet'
+               AND (!($type = extraire_attribut($s, 'type'))
+                       OR $type == 'text/css')
+               AND is_null(extraire_attribut($s, 'name')) # css nommee : pas touche
+               AND is_null(extraire_attribut($s, 'id'))   # idem
+               AND !strlen(strip_tags($s))
+               AND $src = preg_replace(",^$url_base,",_DIR_RACINE,extraire_attribut($s, 'href'))
+               AND (
+                       // regarder si c'est du format spip.php?page=xxx
+                       preg_match(',^('.$dir.')(.*)$,', $src, $r)
+                       OR (
+                               // ou si c'est un fichier
+                               // enlever un timestamp eventuel derriere un nom de fichier statique
+                               $src2 = preg_replace(",[.]css[?].+$,",'.css',$src)
+                               // verifier qu'il n'y a pas de ../ ni / au debut (securite)
+                               AND !preg_match(',(^/|\.\.),', substr($src2,strlen(_DIR_RACINE)))
+                               // et si il est lisible
+                               AND @is_readable($src2)
+                       )
+               )) {
+                       $media = strval(extraire_attribut($s, 'media'));
+                       if ($media==='') $media='all';
+                       if ($r)
+                               $css[$media][$s] = explode('&',
+                                       str_replace('&amp;', '&', $r[2]), 2);
+                       else
+                               $css[$media][$s] = $src;
+               }
+       }
+
+       // et mettre le tout dans un cache statique
+       foreach($css as $m=>$s){
+               // si plus d'une css pour ce media ou si c'est une css dynamique
+               if (count($s)>1 OR is_array(reset($s))){
+                       if (list($src,$comms) = filtre_cache_static($s,'css')){
+                               $compacte_ecrire_balise_link = charger_fonction('compacte_ecrire_balise_link','');
+                               $s = array_keys($s);
+                               $flux = str_replace(reset($s),
+                                                               $comms . $compacte_ecrire_balise_link($src,$m)."\n",
+                                                               $flux);
+                               $flux = str_replace($s,"",$flux);
+                       }
+               }
+       }
+
+       return $flux;
+}
+
+
+// http://doc.spip.org/@filtre_cache_static
+function filtre_cache_static($scripts,$type='js'){
+       $nom = "";
+       if (!is_array($scripts) && $scripts) $scripts = array($scripts);
+       if (count($scripts)){
+               $dir = sous_repertoire(_DIR_VAR,'cache-'.$type);
+               $nom = $dir . md5(serialize($scripts)) . ".$type";
+               if (
+                       $GLOBALS['var_mode']=='recalcul'
+                       OR !file_exists($nom)
+               ) {
+                       $fichier = "";
+                       $comms = array();
+                       $total = 0;
+                       foreach($scripts as $script){
+                               if (!is_array($script)) {
+                                       // c'est un fichier
+                                       $comm = $script;
+                                       // enlever le timestamp si besoin
+                                       $script = preg_replace(",[?].+$,",'',$script);
+                                       if ($type=='css'){
+                                               $fonctions = array('urls_absolues_css');
+                                               if (isset($GLOBALS['compresseur_filtres_css']) AND is_array($GLOBALS['compresseur_filtres_css']))
+                                                       $fonctions = $GLOBALS['compresseur_filtres_css'] + $fonctions;
+                                               $script = appliquer_fonctions_css_fichier($fonctions, $script);
+                                       }
+                                       lire_fichier($script, $contenu);
+                               }
+                               else {
+                                       // c'est un squelette
+                                       $comm = _SPIP_PAGE . "=$script[0]"
+                                               . (strlen($script[1])?"($script[1])":'');
+                                       parse_str($script[1],$contexte);
+                                       $contenu = recuperer_fond($script[0],$contexte);
+                                       if ($type=='css'){
+                                               $fonctions = array('urls_absolues_css');
+                                               if (isset($GLOBALS['compresseur_filtres_css']) AND is_array($GLOBALS['compresseur_filtres_css']))
+                                                       $fonctions = $GLOBALS['compresseur_filtres_css'] + $fonctions;
+                                               $contenu = appliquer_fonctions_css_contenu($fonctions, $contenu, self('&'));
+                                       }
+                               }
+                               $f = 'compacte_'.$type;
+                                       $fichier .= "/* $comm */\n". $f($contenu) . "\n\n";
+                               $comms[] = $comm;
+                               $total += strlen($contenu);
+                       }
+
+                       // calcul du % de compactage
+                       $pc = ($total?intval(1000*strlen($fichier)/$total)/10:0);
+                       $comms = "compact [\n\t".join("\n\t", $comms)."\n] $pc%";
+                       $fichier = "/* $comms */\n\n".$fichier;
+
+                       // ecrire
+                       ecrire_fichier($nom,$fichier,true);
+                       // ecrire une version .gz pour content-negociation par apache, cf. [11539]
+                       ecrire_fichier("$nom.gz",$fichier,true);
+               }
+
+               // closure compiler ou autre super-compresseurs
+               compresse_encore($nom, $type);
+
+       }
+
+       // Le commentaire detaille n'apparait qu'au recalcul, pour debug
+       return array($nom, (isset($comms) AND $comms) ? "<!-- $comms -->\n" : '');
+}
+
+// experimenter le Closure Compiler de Google
+function compresse_encore (&$nom, $type) {
+       # Closure Compiler n'accepte pas des POST plus gros que 200 000 octets
+       # au-dela il faut stocker dans un fichier, et envoyer l'url du fichier
+       # dans code_url ; en localhost ca ne marche evidemment pas
+       if (
+       $GLOBALS['meta']['auto_compress_closure'] == 'oui'
+       AND $type=='js'
+       ) {
+               lire_fichier($nom, $fichier);
+               $dest = dirname($nom).'/'.md5($fichier).'.js';
+               if (!@file_exists($dest)) {
+                       include_spip('inc/distant');
+
+                       $datas=array(
+                               'output_format' => 'text',
+                               'output_info' => 'compiled_code',
+                               'compilation_level' => 'SIMPLE_OPTIMIZATIONS', // 'SIMPLE_OPTIMIZATIONS', 'WHITESPACE_ONLY', 'ADVANCED_OPTIMIZATIONS'
+                       );
+                       if (strlen($fichier) < 200000)
+                               $datas['js_code'] = $fichier;
+                       else
+                               $datas['url_code'] = url_absolue($nom);
+
+                       $cc = recuperer_page('http://closure-compiler.appspot.com/compile',
+                               $trans=false, $get_headers=false,
+                               $taille_max = null,
+                               $datas,
+                               $boundary = -1);
+                       if ($cc AND !preg_match(',^\s*Error,', $cc)) {
+                               spip_log('Closure Compiler: success');
+                               $cc = "/* $nom + Closure Compiler */\n".$cc;
+                               ecrire_fichier ($dest, $cc, true);
+                               ecrire_fichier ("$dest.gz", $cc, true);
+                       } else
+                               ecrire_fichier ($dest, '', true);
+               }
+               if (@filesize($dest))
+                       $nom = $dest;
+       }
+}
+
+function appliquer_fonctions_css_fichier($fonctions,$css) {
+       if (!preg_match(',\.css$,i', $css, $r)) return $css;
+
+       $url_absolue_css = url_absolue($css);
+
+       // verifier qu'on a un array
+       if (is_string($fonctions))
+               $fonctions = array($fonctions);
+
+       $sign = implode(",",$fonctions);
+       $sign = substr(md5("$css-$sign"), 0,8);
+
+       $file = basename($css,'.css');
+       $file = sous_repertoire (_DIR_VAR, 'cache-css')
+               . preg_replace(",(.*?)(_rtl|_ltr)?$,","\\1-f-" . $sign . "\\2",$file)
+               . '.css';
+
+       if ((@filemtime($f) > @filemtime($css))
+       AND ($GLOBALS['var_mode'] != 'recalcul'))
+               return $f;
+
+       if ($url_absolue_css==$css){
+               if (strncmp($GLOBALS['meta']['adresse_site'],$css,$l=strlen($GLOBALS['meta']['adresse_site']))!=0
+                OR !lire_fichier(_DIR_RACINE . substr($css,$l), $contenu)){
+                               include_spip('inc/distant');
+                               if (!$contenu = recuperer_page($css))
+                                       return $css;
+               }
+       }
+       elseif (!lire_fichier($css, $contenu))
+               return $css;
+
+       $contenu = appliquer_fonctions_css_contenu($fonctions, $contenu, $css);
+
+       // ecrire la css
+       if (!ecrire_fichier($file, $contenu))
+               return $css;
+
+       return $file;
+}
+
+function appliquer_fonctions_css_contenu($fonctions, &$contenu, $base) {
+       foreach($fonctions as $f)
+               if (function_exists($f))
+                       $contenu = $f($contenu, $base);
+       return $contenu;
+}
+
+
+function compresseur_embarquer_images_css($contenu, $source){
+       #$path = suivre_lien(url_absolue($source),'./');
+       $base = ((substr($source,-1)=='/')?$source:(dirname($source).'/'));
+
+       return preg_replace_callback(
+               ",url\s*\(\s*['\"]?([^'\"/][^:]*[.](png|gif|jpg))['\"]?\s*\),Uims",
+               create_function('$x',
+                       'return "url(\"".filtre_embarque_fichier($x[1],"'.$base.'")."\")";'
+               ), $contenu);
+}
\ No newline at end of file