ne rien preciser $options = ""; elseif ( strpos($contenu,"@media")==false AND strpos($contenu,"@import")==false AND strpos($contenu,"@font-face")==false ){ $contenu = "@media $options {\n$contenu\n}\n"; $options=""; } else $options = array('media'=>$options); } if (!is_array($options)){ // nettoyer la css de tout ce qui sert pas // pas de commentaires $contenu = preg_replace(",/\*.*\*/,Ums","",$contenu); $contenu = preg_replace(",\s//[^\n]*\n,Ums","",$contenu); // espaces autour des retour lignes $contenu = str_replace("\r\n","\n",$contenu); $contenu = preg_replace(",\s+\n,ms","\n",$contenu); $contenu = preg_replace(",\n\s+,ms","\n",$contenu); // pas d'espaces consecutifs $contenu = preg_replace(",\s(?=\s),Ums","",$contenu); // pas d'espaces avant et apres { ; , $contenu = preg_replace("/\s?({|;|,)\s?/ms","$1",$contenu); // supprimer les espaces devant : sauf si suivi d'une lettre (:after, :first...) $contenu = preg_replace("/\s:([^a-z])/ims",":$1",$contenu); // supprimer les espaces apres : $contenu = preg_replace("/:\s/ms",":",$contenu); // pas d'espaces devant } $contenu = preg_replace("/\s}/ms","}",$contenu); // ni de point virgule sur la derniere declaration $contenu = preg_replace("/;}/ms","}",$contenu); // pas d'espace avant !important $contenu = preg_replace("/\s!\s?important/ms","!important",$contenu); // 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); // remplacer font-weight:bold par font-weight:700 $contenu = preg_replace("/font-weight:bold(?!er)/ims","font-weight:700",$contenu); // remplacer font-weight:normal par font-weight:400 $contenu = preg_replace("/font-weight:normal/ims","font-weight:400",$contenu); // si elle est 0, enlever la partie entière des unites decimales $contenu = preg_replace("/\b0+\.(\d+em)/ims",".$1",$contenu); // supprimer les declarations vides $contenu = preg_replace(",(^|})([^{}]*){},Ums","$1",$contenu); // zero est zero, quelle que soit l'unite $contenu = preg_replace("/([^0-9.]0)(em|px|pt|%)/ms","$1",$contenu); // renommer les couleurs par leurs versions courtes quand c'est possible $colors = array( 'source'=>array('black','fuchsia','white','yellow','#800000','#ffa500','#808000','#800080','#008000','#000080','#008080','#c0c0c0','#808080','#f00'), 'replace'=>array('#000' ,'#F0F' ,'#FFF' ,'#FF0' ,'maroon' ,'orange' ,'olive' ,'purple' ,'green' ,'navy' ,'teal' ,'silver' ,'gray' ,'red') ); foreach($colors['source'] as $k=>$v){ $colors['source'][$k]=";([:\s,(])".$v."(?=[^\w\-]);ms"; $colors['replace'][$k] = "$1".$colors['replace'][$k]; } $contenu = preg_replace($colors['source'],$colors['replace'],$contenu); // raccourcir les padding qui le peuvent (sur 3 ou 2 valeurs) $contenu = preg_replace(",padding:([^\s;}]+)\s([^\s;}]+)\s([^\s;}]+)\s(\\2),ims","padding:$1 $2 $3",$contenu); $contenu = preg_replace(",padding:([^\s;}]+)\s([^\s;}]+)\s(\\1)([;}!]),ims","padding:$1 $2$4",$contenu); // raccourcir les margin qui le peuvent (sur 3 ou 2 valeurs) $contenu = preg_replace(",margin:([^\s;}]+)\s([^\s;}]+)\s([^\s;}]+)\s(\\2),ims","margin:$1 $2 $3",$contenu); $contenu = preg_replace(",margin:([^\s;}]+)\s([^\s;}]+)\s(\\1)([;}!]),ims","margin:$1 $2$4",$contenu); $contenu = trim($contenu); return $contenu; } else { // compression avancee en utilisant csstidy // beaucoup plus lent, mais necessaire pour placer des @media la ou il faut // si il y a deja des @media ou des @import // modele de sortie plus ou moins compact $template = 'high'; if (isset($options['template']) AND in_array($options['template'],array('low','default','high','highest'))) $template = $options['template']; // @media eventuel pour prefixe toutes les css // et regrouper plusieurs css entre elles $media = ""; if (isset($options['media'])) $media = "@media ".$options['media']." "; include_spip("lib/csstidy/class.csstidy"); $css = new csstidy(); // essayer d'optimiser les font, margin, padding avec des ecritures raccourcies $css->set_cfg('optimise_shorthands',2); $css->set_cfg('template',$template); $css->parse($contenu); return $css->print->plain($media); } } /** * Compacte du javascript grace a Dean Edward's JavaScriptPacker * * Bench du 15/11/2010 sur jQuery.js : * JSMIN (https://github.com/rgrove/jsmin-php/) * 61% de la taille initiale / 2 895 ms * JavaScriptPacker * 62% de la taille initiale / 752 ms * * Closure Compiler * 44% de la taille initiale / 3 785 ms * * JavaScriptPacker + Closure Compiler * 43% de la taille initiale / 3 100 ms au total * * Il est donc plus rapide&efficace * - de packer d'abord en local avec JavaScriptPacker * - d'envoyer ensuite au closure compiler * Cela permet en outre d'avoir un niveau de compression decent si closure * compiler echoue * * Dans cette fonction on ne fait que le compactage local, * l'appel a closure compiler est fait une unique fois pour tous les js concatene * afin d'eviter les requetes externes * * @param string $flux * Contenu JS * @return string * Contenu JS minifié */ function minifier_js($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 minifier_js',_LOG_INFO_IMPORTANTE); return $flux; } return $t; } /** * Minification additionnelle de JS * * Compacter du javascript plus intensivement * grâce au google closure compiler * * @param string $content * Contenu JS à compresser * @param bool $file * Indique si $content est ou non un fichier, et retourne un fichier dans ce dernier cas * Si $file est une chaîne, c'est un nom de ficher sous lequel on écrit aussi le fichier destination * @return string * Contenu JS compressé */ function minifier_encore_js($content,$file=false) { # 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 ($file) { $nom = $content; lire_fichier($nom, $content); $dest = dirname($nom).'/'.md5($content.$file).'.js'; if (file_exists($dest) AND (!is_string($file) OR file_exists($file))) if (filesize($dest)) return is_string($file)?$file:$dest; else { spip_log("minifier_encore_js: Fichier $dest vide",_LOG_INFO); return $nom; } } if (!$file AND strlen($content)>200000) return $content; include_spip('inc/distant'); $datas=array( 'output_format' => 'text', 'output_info' => 'compiled_code', 'compilation_level' => 'SIMPLE_OPTIMIZATIONS', // 'SIMPLE_OPTIMIZATIONS', 'WHITESPACE_ONLY', 'ADVANCED_OPTIMIZATIONS' ); if (!$file OR strlen($content) < 200000) $datas['js_code'] = $content; 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; if ($file){ ecrire_fichier ($dest, $cc, true); ecrire_fichier ("$dest.gz", $cc, true); $content = $dest; if (is_string($file)){ ecrire_fichier ($file, $cc, true); spip_clearstatcache(true,$file); ecrire_fichier ("$file.gz", $cc, true); $content = $file; } } else $content = &$cc; } else { if ($file){ spip_log("minifier_encore_js:Echec appel Closure Compiler. Ecriture fichier $dest vide",_LOG_INFO_IMPORTANTE); ecrire_fichier ($dest, '', true); } } return $content; } /** * Une callback applicable sur chaque balise link qui minifie un fichier CSS * * @param string $contenu * @param string $balise * @return string */ function callback_minifier_css_file($contenu, $balise){ return minifier_css($contenu, extraire_attribut($balise,'media')); } /** * Une callback applicable sur chaque balise script qui minifie un JS * * @param string $contenu * @param string $balise * @return string */ function callback_minifier_js_file($contenu, $balise){ return minifier_js($contenu); } /** * Minifier du HTML * * @param string $flux HTML à compresser * @return string HTML compressé */ function minifier_html($flux){ // si pas de contenu ni de balise html, ne rien faire if (!strlen($flux) OR strpos($flux,"<")===FALSE) return $flux; static $options = null; if (is_null($options)){ $options = array(); if ($GLOBALS['meta']['auto_compress_css'] == 'oui') $options['cssMinifier'] = 'minifier_css'; if ($GLOBALS['meta']['auto_compress_js'] == 'oui') $options['jsMinifier'] = 'minifier_js'; include_spip('lib/minify_html/class.minify_html'); } return Minify_HTML_SPIP::minify($flux,$options); }