├── .gitattributes ├── tools ├── ant │ └── ant-contrib.jar ├── readme.installation.txt └── BuildTools.js ├── changelog_jsp.txt ├── .gitignore ├── changelog_net.txt ├── readme.md ├── changelog_php.txt ├── package.json ├── changelog_cf.txt ├── Jakefile.js ├── changelog_perl.txt ├── tinymce.gzip.js ├── tinymce.gzip.ashx ├── tinymce.gzip.jsp ├── tinymce.gzip.cfm ├── tinymce.gzip.pl └── tinymce.gzip.php /.gitattributes: -------------------------------------------------------------------------------- 1 | * crlf=input 2 | * -text 3 | -------------------------------------------------------------------------------- /tools/ant/ant-contrib.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/tinymce/tinymce_compressor/HEAD/tools/ant/ant-contrib.jar -------------------------------------------------------------------------------- /tools/readme.installation.txt: -------------------------------------------------------------------------------- 1 | Full documentation here: 2 | http://www.tinymce.com/wiki.php/Tutorials:Using_the_gzip_compressors 3 | -------------------------------------------------------------------------------- /changelog_jsp.txt: -------------------------------------------------------------------------------- 1 | Version 4.0 (2013-11-xx) 2 | Added new rewritten compressor for TinyMCE 4.0. It's now a simple drop in replacement. 3 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | bin 2 | obj 3 | tmp 4 | .settings 5 | *~ 6 | *.diff 7 | *.patch 8 | *.bak 9 | *.log 10 | .DS_Store 11 | /.project 12 | node_modules 13 | -------------------------------------------------------------------------------- /changelog_net.txt: -------------------------------------------------------------------------------- 1 | Version 4.0.1 (2013-10-03) 2 | Fixed bug where it sometimes wouldn't properly init TinyMCE when loaded from cache. 3 | Version 4.0 (2013-08-22) 4 | Added new rewritten compressor for TinyMCE 4.0. It's now a simple drop in replacement. 5 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | TinyMCE Compressor 2 | =================== 3 | 4 | Building the compressors 5 | --------------------------------- 6 | 1. Install Node.js 7 | 2. Open a console and go to the project directory 8 | 3. Write "npm install" to install all Node.js modules needed 9 | 4. Build by writing "jake" 10 | 5. Zips are created in the "tmp" directory 11 | -------------------------------------------------------------------------------- /changelog_php.txt: -------------------------------------------------------------------------------- 1 | Version 4.0.2 (2013-10-16) 2 | Fixed bug where compressor didn't work if the development version wasn't used. 3 | Version 4.0.1 (2013-10-03) 4 | Fixed bug where it sometimes wouldn't properly init TinyMCE when loaded from cache. 5 | Version 4.0 (2013-08-22) 6 | Added new rewritten compressor for TinyMCE 4.0. It's now a simple drop in replacement. 7 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tinymce_compressor", 3 | "version": "4.0.0", 4 | "description": "TinyMCE Gzip compressor", 5 | "author": "Johan Sörlin ", 6 | "bugs": { "url" : "http://www.tinymce.com/develop/bugtracker.php" }, 7 | "engines": { 8 | "node" : ">=0.8.0" 9 | }, 10 | "dependencies": { 11 | "uglify-js": ">= 2.0.0", 12 | "moxie-zip": ">= 0.0.1" 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /changelog_cf.txt: -------------------------------------------------------------------------------- 1 | Version 2.1.2 (2011-03-23) 2 | Fixed bug where the eval function of the tiny_mce_gzip.js file wouldn't execute properly if scripts where loaded before the body. 3 | Version 2.1.1.1 (2010-12-16) 4 | Fixed issues with code evaluation, it's now using a better method. 5 | Version 2.1.1 (2009-03-03) 6 | Added HTTP headers when serving the files. 7 | Changes line endings to CRLF to avoid conflicts. 8 | Version 2.1.0 (2009-02-26) 9 | Complete re-write by Jules Gravinese, no jar file installation required. 10 | Version 2.0.2 (2008-07-09) 11 | Fixed bug where IE would load the files twice. 12 | Reduced the overall script size since it's now using the markDone method. 13 | Version 2.0.1 (2008-03-12) 14 | Fixed bug where Firefox 3 wasn't loading the contents correctly. 15 | Version 2.0 (2008-01-30) 16 | Final version of the 2.x branch released. 17 | Version 2.0rc1 (2008-01-14) 18 | Moved package from beta to release candidate. 19 | Version 2.0b2 (2007-11-21) 20 | Fixed typo of que to queue so it compatible with TinyMCE 3.0b1. 21 | Version 2.0b1 (2007-11-15) 22 | Fixed bug with IE6 casting an error on async property. 23 | Version 2.0a2 (2007-11-xx) 24 | Fixed bug where on demand loading didn't work correctly in IE and Safari. 25 | Version 2.0a1 (2007-11-01) 26 | Rewritten compressor for TinyMCE 3.x has on demand and progressive loading features. 27 | -------------------------------------------------------------------------------- /Jakefile.js: -------------------------------------------------------------------------------- 1 | var zip = require('./tools/BuildTools').zip; 2 | var getReleaseDetails = require('./tools/BuildTools').getReleaseDetails; 3 | var fs = require("fs"); 4 | var UglifyJS = require("uglify-js"); 5 | 6 | desc("Default build task"); 7 | task("default", ["release"], function () {}); 8 | 9 | task("release", [], function () { 10 | if (!fs.existsSync("tmp")) { 11 | fs.mkdirSync("tmp"); 12 | } 13 | 14 | function createPackage(page, suffix) { 15 | var details = getReleaseDetails("changelog_" + suffix + ".txt"); 16 | 17 | zip({ 18 | baseDir: "tinymce_compressor", 19 | 20 | from: [ 21 | "tinymce.gzip.js", 22 | ["changelog_" + suffix + ".txt", "changelog.txt"], 23 | page, 24 | ["tools/readme.installation.txt", "readme.txt"] 25 | ], 26 | 27 | dataFilter: function(args) { 28 | if (args.zipFilePath == 'tinymce.gzip.js') { 29 | var source = args.data.toString().replace(/tinymce\.gzip\.php/g, page); 30 | 31 | var ast = UglifyJS.parse(source); 32 | ast.figure_out_scope(); 33 | ast = ast.transform(UglifyJS.Compressor()); 34 | ast.figure_out_scope(); 35 | ast.compute_char_frequency(); 36 | ast.mangle_names(); 37 | 38 | var stream = UglifyJS.OutputStream(); 39 | ast.print(stream); 40 | 41 | args.data = stream.toString(); 42 | } 43 | }, 44 | 45 | to: "tmp/tinymce_compressor_" + details.version + "_" + suffix + ".zip" 46 | }); 47 | } 48 | 49 | createPackage("tinymce.gzip.php", "php"); 50 | createPackage("tinymce.gzip.ashx", "net"); 51 | createPackage("tinymce.gzip.jsp", "jsp"); 52 | createPackage("tinymce.gzip.cfm", "cf"); 53 | createPackage("tinymce.gzip.pl", "perl"); 54 | }); 55 | -------------------------------------------------------------------------------- /changelog_perl.txt: -------------------------------------------------------------------------------- 1 | Version 2.0.4 (2011-03-23) 2 | Fixed bug where the eval function of the tiny_mce_gzip.js file wouldn't execute properly if scripts where loaded before the body. 3 | 4 | Version 2.0.3 (2010-02-20) 5 | Added to the main tinymce_compressor github repo 6 | Moved the docs in README into POD in tiny_mce_gzip.pl itself 7 | 8 | Version 2.0.2 (2008-12-23) 9 | Tested with TinyMCE version 3.2.1.1 10 | Updated the tiny_mce_gzip.js from the PHP compressor version 2.0.2 11 | 12 | Version 2.0.0 (2008-03-28) 13 | Tested with TinyMCE version 3.0.5 14 | 15 | Updated the tiny_mce_gzip.js from the PHP compressor version 2.0.1 16 | Added support for the 'suffix' and 'core' parameters 17 | custom_files were not being included - fixed 18 | Added the 'charset' parameter - not supported by the JS file 19 | 20 | Version 1.0.3 (2007-07-14) 21 | Tested with TinyMCE version 2.1.1 22 | 23 | The get_params() sub now accepts capital letters, not just lower case. 24 | This was to allow filenames with capital letters, eg 'ru_KOI8-R.js' 25 | 26 | Added the optional 'charset' parameter to tinyMCE_GZ.init, so that 27 | the correct character set encoding can be specified for language packs 28 | such as 'ru_KOI8-R.js'. 29 | 30 | Thanks to Yury Don for both of these. 31 | 32 | IE7 now tries the native XMLHttpRequest first. 33 | 34 | Version 1.0.2 (2007-04-25) 35 | The 'compress' option now defaults to true, as per the original 36 | PHP compressor. (Reported by Nicholas Oxhøj) 37 | 38 | Version 1.0.1 (2007-04-19) 39 | Tested with TinyMCE version 2.1.0 40 | 41 | Fixed two bugs in the TinyMCE Perl compressor: 42 | - the query string was not being properly decoded, and so comma separated 43 | lists were not being recognised 44 | - some plugins do not need language packs (eg inlinepopup) 45 | 46 | Version 1.0 (2007-03-14) 47 | First release of the Perl version of the TinyMCE compressor by Clinton Gormley. 48 | This release is based on the code from the PHP version of the compressor. 49 | 50 | -------------------------------------------------------------------------------- /tools/BuildTools.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Various build tools for Jake. 3 | */ 4 | 5 | /*jshint smarttabs:true, undef:true, node:true, latedef:true, curly:true, bitwise:true */ 6 | "use strict"; 7 | 8 | var fs = require("fs"); 9 | var path = require("path"); 10 | 11 | exports.zip = function (options) { 12 | var ZipWriter = require('moxie-zip').ZipWriter; 13 | var archive = new ZipWriter(); 14 | 15 | function process(filePath, zipFilePath) { 16 | var args, stat = fs.statSync(filePath); 17 | 18 | zipFilePath = zipFilePath || filePath; 19 | filePath = filePath.replace(/\\/g, '/'); 20 | zipFilePath = zipFilePath.replace(/\\/g, '/'); 21 | 22 | if (options.pathFilter) { 23 | args = {filePath: filePath, zipFilePath: zipFilePath}; 24 | options.pathFilter(args); 25 | zipFilePath = args.zipFilePath; 26 | } 27 | 28 | if (options.exclude) { 29 | for (var i = 0; i < options.exclude.length; i++) { 30 | var pattern = options.exclude[i]; 31 | 32 | if (pattern instanceof RegExp) { 33 | if (pattern.test(filePath)) { 34 | return; 35 | } 36 | } else { 37 | if (filePath === pattern) { 38 | return; 39 | } 40 | } 41 | } 42 | } 43 | 44 | if (stat.isFile()) { 45 | var data = fs.readFileSync(filePath); 46 | 47 | if (options.dataFilter) { 48 | args = {filePath: filePath, zipFilePath: zipFilePath, data: data}; 49 | options.dataFilter(args); 50 | data = args.data; 51 | } 52 | 53 | archive.addData(path.join(options.baseDir, zipFilePath), data); 54 | } else if (stat.isDirectory()) { 55 | fs.readdirSync(filePath).forEach(function(fileName) { 56 | process(path.join(filePath, fileName), path.join(zipFilePath, fileName)); 57 | }); 58 | } 59 | } 60 | 61 | options.baseDir = (options.baseDir || '').replace(/\\/g, '/'); 62 | 63 | options.from.forEach(function(filePath) { 64 | if (filePath instanceof Array) { 65 | process(filePath[0], filePath[1]); 66 | } else { 67 | process(filePath); 68 | } 69 | }); 70 | 71 | archive.saveAs(options.to); 72 | }; 73 | 74 | exports.getReleaseDetails = function (filePath) { 75 | var firstLine = ("" + fs.readFileSync(filePath)).split('\n')[0]; 76 | 77 | return { 78 | version: /^Version ([0-9xabrc.]+)/.exec(firstLine)[1], 79 | releaseDate: /^Version [^\(]+\(([^\)]+)\)/.exec(firstLine)[1] 80 | }; 81 | }; 82 | -------------------------------------------------------------------------------- /tinymce.gzip.js: -------------------------------------------------------------------------------- 1 | (function() { 2 | var tinymce, loaded = {}, urls = [], callbacks = [], loading, realInit; 3 | 4 | function loadScript(url, callback) { 5 | var elm, head = document.getElementsByTagName('head')[0] || document.body; 6 | 7 | // Execute callback when script is loaded 8 | function done() { 9 | elm.parentNode.removeChild(elm); 10 | 11 | if (elm) { 12 | elm.onreadystatechange = elm.onload = elm = null; 13 | } 14 | 15 | callback(); 16 | } 17 | 18 | function error() { 19 | // Report the error so it's easier for people to spot loading errors 20 | if (typeof(console) !== "undefined" && console.log) { 21 | console.log("Failed to load: " + url); 22 | } 23 | } 24 | 25 | // Create new script element 26 | elm = document.createElement('script'); 27 | elm.type = 'text/javascript'; 28 | 29 | // Use modern onload event when it's available 30 | if ("onload" in elm) { 31 | elm.onload = done; 32 | head.appendChild(elm); 33 | 34 | // Add src after appending it to the document seems to work better on IE 10 35 | elm.src = url; 36 | } else { 37 | elm.onreadystatechange = function() { 38 | if (elm.src && /loaded|complete/.test(elm.readyState)) { 39 | done(); 40 | } 41 | }; 42 | 43 | elm.src = url; 44 | head.appendChild(elm); 45 | } 46 | 47 | elm.onerror = error; 48 | } 49 | 50 | function buildUrl(themes, plugins, languages) { 51 | var url = ''; 52 | 53 | function getQueryPart(type, items) { 54 | if (items) { 55 | for (var i = items.length - 1; i >= 0; i--) { 56 | if (loaded[type + '_' + items[i]]) { 57 | items.splice(i, 1); 58 | } else { 59 | loaded[type + '_' + items[i]] = true; 60 | } 61 | } 62 | 63 | if (items.length) { 64 | return '&' + type + 's=' + items.join(','); 65 | } 66 | } 67 | 68 | return ''; 69 | } 70 | 71 | url += getQueryPart("plugin", plugins); 72 | url += getQueryPart("theme", themes); 73 | url += getQueryPart("language", languages); 74 | 75 | if (url) { 76 | if (loaded.core) { 77 | url += '&core=false'; 78 | } else { 79 | loaded.core = true; 80 | } 81 | 82 | url = tinymce.baseURL + '/tinymce.gzip.php?js=true' + url; 83 | } 84 | 85 | return url; 86 | } 87 | 88 | function splitValue(value) { 89 | if (typeof(value) == "string") { 90 | return value.split(/[, ]/); 91 | } 92 | 93 | var items = []; 94 | 95 | if (value) { 96 | for (var i = 0; i < value.length; i++) { 97 | items = items.concat(splitValue(value[i])); 98 | } 99 | } 100 | 101 | return items; 102 | } 103 | 104 | function loadNext() { 105 | var url = urls.shift(); 106 | 107 | if (!url) { 108 | for (var i = 0; i < callbacks.length; i++) { 109 | callbacks[i](); 110 | } 111 | 112 | callbacks = []; 113 | loading = false; 114 | } else { 115 | loadScript(url, loadNext); 116 | } 117 | } 118 | 119 | function init(settings) { 120 | var themes = [], plugins = [], languages = []; 121 | 122 | themes.push(settings.theme || 'modern'); 123 | 124 | var pluginList = splitValue(settings.plugins); 125 | for (var i = 0; i < pluginList.length; i++) { 126 | plugins.push(pluginList[i]); 127 | } 128 | 129 | if (settings.language) { 130 | languages.push(settings.language); 131 | } 132 | 133 | urls.push(buildUrl(themes, plugins, languages)); 134 | 135 | callbacks.push(function() { 136 | if (/complete|interactive/.test(document.readyState)) { 137 | window.tinymce.dom.Event.domLoaded = true; 138 | } 139 | 140 | if (window.tinymce.init != init) { 141 | realInit = window.tinymce.init; 142 | window.tinymce.init = init; 143 | } 144 | 145 | realInit.call(window.tinymce, settings); 146 | }); 147 | 148 | if (!loading) { 149 | loading = true; 150 | loadNext(); 151 | } 152 | } 153 | 154 | function getBaseUrl() { 155 | var scripts = document.getElementsByTagName('script'); 156 | for (var i = 0; i < scripts.length; i++) { 157 | var src = scripts[i].src; 158 | 159 | if (src.indexOf('tinymce.gzip.js') != -1) { 160 | return src.substring(0, src.lastIndexOf('/')); 161 | } 162 | } 163 | } 164 | 165 | tinymce = { 166 | init: init, 167 | baseURL: getBaseUrl(), 168 | suffix: ".min" 169 | }; 170 | 171 | window.tinyMCE_GZ = { 172 | init: function(settings, callback) { 173 | urls.push(buildUrl(splitValue(settings.themes), splitValue(settings.plugins), splitValue(settings.languages))); 174 | 175 | callbacks.push(function() { 176 | window.tinymce.dom.Event.domLoaded = 1; 177 | callback(); 178 | }); 179 | 180 | if (!loading) { 181 | loading = true; 182 | loadNext(); 183 | } 184 | } 185 | }; 186 | 187 | window.tinymce = window.tinyMCE = tinymce; 188 | })(); -------------------------------------------------------------------------------- /tinymce.gzip.ashx: -------------------------------------------------------------------------------- 1 | <%@ WebHandler Language="C#" Class="Handler" %> 2 | /** 3 | * tinymce.gzip.ashx 4 | * 5 | * Copyright, Moxiecode Systems AB 6 | * Released under LGPL License. 7 | * 8 | * License: http://tinymce.moxiecode.com/license 9 | * Contributing: http://tinymce.moxiecode.com/contributing 10 | * 11 | * This file compresses the TinyMCE JavaScript using GZip and 12 | * enables the browser to do two requests instead of one for each .js file. 13 | * 14 | * It's a good idea to use the diskcache option since it reduces the servers workload. 15 | */ 16 | 17 | using System; 18 | using System.Web; 19 | using System.IO; 20 | using System.IO.Compression; 21 | using System.Security.Cryptography; 22 | using System.Text; 23 | using System.Text.RegularExpressions; 24 | 25 | public class Handler : IHttpHandler { 26 | private HttpResponse Response; 27 | private HttpRequest Request; 28 | private HttpServerUtility Server; 29 | 30 | public void ProcessRequest(HttpContext context) { 31 | this.Response = context.Response; 32 | this.Request = context.Request; 33 | this.Server = context.Server; 34 | this.StreamGzipContents(); 35 | } 36 | 37 | public bool IsReusable { 38 | get { 39 | return false; 40 | } 41 | } 42 | 43 | #region private 44 | 45 | private void StreamGzipContents() { 46 | string cacheKey = "", cacheFile = "", content = "", enc, suffix, cachePath; 47 | string[] plugins, languages, themes; 48 | bool diskCache, supportsGzip, isJS, compress, core; 49 | int i, x, expiresOffset; 50 | GZipStream gzipStream; 51 | Encoding encoding = Encoding.GetEncoding("windows-1252"); 52 | byte[] buff; 53 | 54 | // Get input 55 | plugins = GetParam("plugins", "").Split(','); 56 | languages = GetParam("languages", "").Split(','); 57 | themes = GetParam("themes", "").Split(','); 58 | diskCache = GetParam("diskcache", "") == "true"; 59 | isJS = GetParam("js", "") == "true"; 60 | compress = GetParam("compress", "true") == "true"; 61 | core = GetParam("core", "true") == "true"; 62 | suffix = GetParam("suffix", ".min"); 63 | cachePath = Server.MapPath("."); // Cache path, this is where the .gz files will be stored 64 | expiresOffset = 10; // Cache for 10 days in browser cache 65 | 66 | // Custom extra javascripts to pack 67 | string[] custom = {/* 68 | "some custom .js file", 69 | "some custom .js file" 70 | */}; 71 | 72 | // Set response headers 73 | Response.ContentType = "text/javascript"; 74 | Response.Charset = "UTF-8"; 75 | Response.Buffer = false; 76 | 77 | // Setup cache 78 | Response.Cache.SetExpires(DateTime.Now.AddDays(expiresOffset)); 79 | Response.Cache.SetCacheability(HttpCacheability.Public); 80 | Response.Cache.SetValidUntilExpires(false); 81 | 82 | // Vary by all parameters and some headers 83 | Response.Cache.VaryByHeaders["Accept-Encoding"] = true; 84 | Response.Cache.VaryByParams["theme"] = true; 85 | Response.Cache.VaryByParams["language"] = true; 86 | Response.Cache.VaryByParams["plugins"] = true; 87 | Response.Cache.VaryByParams["lang"] = true; 88 | Response.Cache.VaryByParams["index"] = true; 89 | 90 | // Setup cache info 91 | if (diskCache) { 92 | cacheKey = GetParam("plugins", "") + GetParam("languages", "") + GetParam("themes", ""); 93 | 94 | for (i = 0; i < custom.Length; i++) 95 | cacheKey += custom[i]; 96 | 97 | cacheKey = MD5(cacheKey); 98 | 99 | if (compress) 100 | cacheFile = cachePath + "/tinymce.gzip-" + cacheKey + ".gz"; 101 | else 102 | cacheFile = cachePath + "/tinymce.gzip-" + cacheKey + ".js"; 103 | } 104 | 105 | // Check if it supports gzip 106 | enc = Regex.Replace("" + Request.Headers["Accept-Encoding"], @"\s+", "").ToLower(); 107 | supportsGzip = enc.IndexOf("gzip") != -1 || Request.Headers["---------------"] != null; 108 | enc = enc.IndexOf("x-gzip") != -1 ? "x-gzip" : "gzip"; 109 | 110 | // Use cached file disk cache 111 | if (diskCache && supportsGzip && File.Exists(cacheFile)) { 112 | Response.AppendHeader("Content-Encoding", enc); 113 | Response.WriteFile(cacheFile); 114 | return; 115 | } 116 | 117 | // Add core 118 | if (core) { 119 | // Set base URL for where tinymce is loaded from 120 | String uri = Request.Url.AbsolutePath; 121 | uri = uri.Substring(0, uri.LastIndexOf('/')); 122 | content += "var tinymce={base:'" + uri + "',suffix:'.min'};"; 123 | content += GetFileContents("tinymce." + suffix + ".js"); 124 | } 125 | 126 | // Add core languages 127 | for (x = 0; x < languages.Length; x++) 128 | content += GetFileContents("langs/" + languages[x] + ".js"); 129 | 130 | // Add themes 131 | for (i = 0; i < themes.Length; i++) { 132 | content += GetFileContents("themes/" + themes[i] + "/theme." + suffix + ".js"); 133 | 134 | for (x = 0; x < languages.Length; x++) 135 | content += GetFileContents("themes/" + themes[i] + "/langs/" + languages[x] + ".js"); 136 | } 137 | 138 | // Add plugins 139 | for (i = 0; i < plugins.Length; i++) { 140 | content += GetFileContents("plugins/" + plugins[i] + "/plugin." + suffix + ".js"); 141 | 142 | for (x = 0; x < languages.Length; x++) 143 | content += GetFileContents("plugins/" + plugins[i] + "/langs/" + languages[x] + ".js"); 144 | } 145 | 146 | // Add custom files 147 | for (i = 0; i < custom.Length; i++) 148 | content += GetFileContents(custom[i]); 149 | 150 | // Generate GZIP'd content 151 | if (supportsGzip) { 152 | if (compress) 153 | Response.AppendHeader("Content-Encoding", enc); 154 | 155 | if (diskCache && cacheKey != "") { 156 | // Gzip compress 157 | if (compress) { 158 | using (Stream fileStream = File.Create(cacheFile)) { 159 | gzipStream = new GZipStream(fileStream, CompressionMode.Compress, true); 160 | buff = encoding.GetBytes(content.ToCharArray()); 161 | gzipStream.Write(buff, 0, buff.Length); 162 | gzipStream.Close(); 163 | } 164 | } else { 165 | using (StreamWriter sw = File.CreateText(cacheFile)) { 166 | sw.Write(content); 167 | } 168 | } 169 | 170 | // Write to stream 171 | Response.WriteFile(cacheFile); 172 | } else { 173 | gzipStream = new GZipStream(Response.OutputStream, CompressionMode.Compress, true); 174 | buff = encoding.GetBytes(content.ToCharArray()); 175 | gzipStream.Write(buff, 0, buff.Length); 176 | gzipStream.Close(); 177 | } 178 | } else 179 | Response.Write(content); 180 | } 181 | 182 | private string GetParam(string name, string def) { 183 | string value = !String.IsNullOrEmpty(Request.QueryString[name]) ? "" + Request.QueryString[name] : def; 184 | 185 | return Regex.Replace(value, @"[^0-9a-zA-Z\\-_,]+", ""); 186 | } 187 | 188 | private string GetFileContents(string path) { 189 | try { 190 | string content; 191 | 192 | path = Server.MapPath(path); 193 | 194 | if (!File.Exists(path)) 195 | return ""; 196 | 197 | StreamReader sr = new StreamReader(path); 198 | content = sr.ReadToEnd(); 199 | sr.Close(); 200 | 201 | return content; 202 | } catch (Exception ex) { 203 | // Ignore any errors 204 | } 205 | 206 | return ""; 207 | } 208 | 209 | private string MD5(string str) { 210 | MD5 md5 = new MD5CryptoServiceProvider(); 211 | byte[] result = md5.ComputeHash(Encoding.ASCII.GetBytes(str)); 212 | str = BitConverter.ToString(result); 213 | 214 | return str.Replace("-", ""); 215 | } 216 | 217 | #endregion 218 | } 219 | -------------------------------------------------------------------------------- /tinymce.gzip.jsp: -------------------------------------------------------------------------------- 1 | <%@page import="java.io.*,java.util.zip.*,java.net.URI" %> 2 | <% 3 | /** 4 | * tinymce.gzip.jsp 5 | * 6 | * Copyright, Moxiecode Systems AB 7 | * Released under LGPL License. 8 | * 9 | * License: http://tinymce.moxiecode.com/license 10 | * Contributing: http://tinymce.moxiecode.com/contributing 11 | * 12 | * This file compresses the TinyMCE JavaScript using GZip and 13 | * enables the browser to do two requests instead of one for each .js file. 14 | * 15 | * It's a good idea to use the diskcache option since it reduces the servers workload. 16 | */ 17 | 18 | String cacheKey = "", cacheFile = "", content = "", enc, suffix, cachePath; 19 | String plugins[], languages[], themes[]; 20 | boolean diskCache, supportsGzip, isJS, compress, core; 21 | int i, x, bytes, expiresOffset; 22 | ServletOutputStream outStream = response.getOutputStream(); 23 | OutputStreamWriter bow; 24 | ByteArrayOutputStream bos; 25 | GZIPOutputStream gzipStream; 26 | FileOutputStream fout; 27 | FileInputStream fin; 28 | byte buff[]; 29 | 30 | // Get input 31 | plugins = getParam(request, "plugins", "").split(","); 32 | languages = getParam(request, "languages", "").split(","); 33 | themes = getParam(request, "themes", "").split(","); 34 | diskCache = getParam(request, "diskcache", "").equals("true"); 35 | isJS = getParam(request, "js", "").equals("true"); 36 | compress = getParam(request, "compress", "true").equals("true"); 37 | core = getParam(request, "core", "true").equals("true"); 38 | suffix = getParam(request, "suffix", "").length() == 0 ? ".min" : ""; 39 | cachePath = mapPath(request, "."); // Cache path, this is where the .gz files will be stored 40 | expiresOffset = 3600 * 24 * 10; // Cache for 10 days in browser cache 41 | 42 | // Custom extra javascripts to pack 43 | String custom[] = {/* 44 | "some custom .js file", 45 | "some custom .js file" 46 | */}; 47 | 48 | // Headers 49 | response.setContentType("text/javascript"); 50 | response.addHeader("Vary", "Accept-Encoding"); // Handle proxies 51 | response.setDateHeader("Expires", System.currentTimeMillis() + (expiresOffset * 1000)); 52 | 53 | // Is called directly then auto init with default settings 54 | if (!isJS) { 55 | out.print(getFileContents(mapPath(request, "tiny_mce_gzip.js"))); 56 | return; 57 | } 58 | 59 | // Setup cache info 60 | if (diskCache) { 61 | cacheKey = getParam(request, "plugins", "") + getParam(request, "languages", "") + getParam(request, "themes", ""); 62 | 63 | for (i = 0; i < custom.length; i++) { 64 | cacheKey += custom[i]; 65 | } 66 | 67 | cacheKey = md5(cacheKey); 68 | 69 | if (compress) { 70 | cacheFile = cachePath + File.separatorChar + "tinymce." + cacheKey + ".gz"; 71 | } else { 72 | cacheFile = cachePath + File.separatorChar + "tinymce." + cacheKey + ".js"; 73 | } 74 | } 75 | 76 | // Check if it supports gzip 77 | supportsGzip = false; 78 | enc = request.getHeader("Accept-Encoding"); 79 | if (enc != null) { 80 | enc.replaceAll("\\s+", "").toLowerCase(); 81 | supportsGzip = enc.indexOf("gzip") != -1 || request.getHeader("---------------") != null; 82 | enc = enc.indexOf("x-gzip") != -1 ? "x-gzip" : "gzip"; 83 | } 84 | 85 | // Use cached file disk cache 86 | if (diskCache && supportsGzip && new File(cacheFile).exists()) { 87 | if (compress) 88 | response.addHeader("Content-Encoding", enc); 89 | 90 | fin = new FileInputStream(cacheFile); 91 | buff = new byte[1024]; 92 | 93 | while ((bytes = fin.read(buff, 0, buff.length)) != -1) { 94 | outStream.write(buff, 0, bytes); 95 | } 96 | 97 | fin.close(); 98 | outStream.close(); 99 | return; 100 | } 101 | 102 | // Add core 103 | if (core) { 104 | // Set base URL for where tinymce is loaded from 105 | String uri = request.getRequestURI(); 106 | uri = uri.substring(0, uri.lastIndexOf('/')); 107 | content += "var tinymce={base:'" + uri + "',suffix:'.min'};"; 108 | content += getFileContents(mapPath(request, "tinymce" + suffix + ".js")); 109 | } 110 | 111 | // Add core languages 112 | for (x = 0; x < languages.length; x++) { 113 | content += getFileContents(mapPath(request, "langs/" + languages[x] + ".js")); 114 | } 115 | 116 | // Add themes 117 | for (i = 0; i < themes.length; i++) { 118 | content += getFileContents(mapPath(request, "themes/" + themes[i] + "/theme" + suffix + ".js")); 119 | 120 | for (x = 0; x < languages.length; x++) { 121 | content += getFileContents(mapPath(request, "themes/" + themes[i] + "/langs/" + languages[x] + ".js")); 122 | } 123 | } 124 | 125 | // Add plugins 126 | for (i = 0; i < plugins.length; i++) { 127 | content += getFileContents(mapPath(request, "plugins/" + plugins[i] + "/plugin" + suffix + ".js")); 128 | 129 | for (x = 0; x < languages.length; x++) { 130 | content += getFileContents(mapPath(request, "plugins/" + plugins[i] + "/langs/" + languages[x] + ".js")); 131 | } 132 | } 133 | 134 | // Add custom files 135 | for (i = 0; i < custom.length; i++) { 136 | content += getFileContents(mapPath(request, custom[i])); 137 | } 138 | 139 | // Generate GZIP'd content 140 | if (supportsGzip) { 141 | if (compress) { 142 | response.addHeader("Content-Encoding", enc); 143 | } 144 | 145 | if (diskCache && cacheKey != "") { 146 | bos = new ByteArrayOutputStream(); 147 | 148 | // Gzip compress 149 | if (compress) { 150 | gzipStream = new GZIPOutputStream(bos); 151 | gzipStream.write(content.getBytes("iso-8859-1")); 152 | gzipStream.close(); 153 | } else { 154 | bow = new OutputStreamWriter(bos); 155 | bow.write(content); 156 | bow.close(); 157 | } 158 | 159 | // Write to file 160 | try { 161 | fout = new FileOutputStream(cacheFile); 162 | fout.write(bos.toByteArray()); 163 | fout.close(); 164 | } catch (IOException e) { 165 | // Ignore 166 | } 167 | 168 | // Write to stream 169 | outStream.write(bos.toByteArray()); 170 | } else { 171 | gzipStream = new GZIPOutputStream(outStream); 172 | gzipStream.write(content.getBytes("iso-8859-1")); 173 | gzipStream.close(); 174 | } 175 | } else { 176 | out.write(content); 177 | } 178 | %><%! 179 | /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ 180 | 181 | public String getParam(HttpServletRequest request, String name, String def) { 182 | String value = request.getParameter(name) != null ? "" + request.getParameter(name) : def; 183 | 184 | return value.replaceAll("[^0-9a-zA-Z\\-_,]+", ""); 185 | } 186 | 187 | public String getFileContents(String path) { 188 | try { 189 | if (!new File(path).exists()) { 190 | return ""; 191 | } 192 | 193 | FileInputStream fis = new FileInputStream(path); 194 | int x = fis.available(); 195 | byte b[] = new byte[x]; 196 | 197 | fis.read(b); 198 | 199 | return new String(b); 200 | } catch (IOException e) { 201 | // Ignore 202 | } 203 | 204 | return ""; 205 | } 206 | 207 | public String mapPath(HttpServletRequest request, String path) { 208 | String absPath = getServletContext().getRealPath(request.getRequestURI()); 209 | 210 | absPath = absPath.substring(0, absPath.lastIndexOf(File.separatorChar) + 1); 211 | 212 | return absPath + path.replace('/', File.separatorChar); 213 | } 214 | 215 | public String md5(String str) { 216 | try { 217 | java.security.MessageDigest md5 = java.security.MessageDigest.getInstance("MD5"); 218 | 219 | char[] charArray = str.toCharArray(); 220 | byte[] byteArray = new byte[charArray.length]; 221 | 222 | for (int i = 0; i < charArray.length; i++) { 223 | byteArray[i] = (byte) charArray[i]; 224 | } 225 | 226 | byte[] md5Bytes = md5.digest(byteArray); 227 | StringBuffer hexValue = new StringBuffer(); 228 | 229 | for (int i = 0; i < md5Bytes.length; i++) { 230 | int val = ((int) md5Bytes[i] ) & 0xff; 231 | 232 | if (val < 16) { 233 | hexValue.append("0"); 234 | } 235 | 236 | hexValue.append(Integer.toHexString(val)); 237 | } 238 | 239 | return hexValue.toString(); 240 | } catch (java.security.NoSuchAlgorithmException e) { 241 | // Ignore 242 | } 243 | 244 | return ""; 245 | } 246 | %> -------------------------------------------------------------------------------- /tinymce.gzip.cfm: -------------------------------------------------------------------------------- 1 | 7 | 8 | 9 | // -------------------------------------------------------------------- 10 | // This file was concatenated (and most likely also cached and gzipped) 11 | // by TinyMCE CF GZIP, a ColdFusion based Javascript Concatenater, 12 | // Compressor, and Cacher for TinyMCE. 13 | // V1, Mon Feb 9 9:00:00 -0500 2009 14 | // 15 | // Copyright (c) 2009 Jules Gravinese (http://www.webveteran.com/) 16 | // 17 | // TinyMCE CF GZIP is licensed under LGPL license. 18 | // More details can be found here: http://tinymce.moxiecode.com/license.php 19 | // 20 | // The gzip functions were adapted and incorporated by permission 21 | // from Artur Kordowski's Zip CFC 1.2 : http://zipcfc.riaforge.org/ 22 | // 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 64 | 65 | 66 | 67 | #getFileContents("tiny_mce_gzip.js")# tinyMCE_GZ.init({}); 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | #credits# 162 | // This uncompressed concatenated JS: #numberformat((content.length() + credits.length())/1024, .00)# KB 163 | // -------------------------------------------------------------------- 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | /* Create Objects */ 187 | ioInput = CreateObject("java","java.io.FileInputStream"); 188 | ioOutput = CreateObject("java","java.io.FileOutputStream"); 189 | gzOutput = CreateObject("java","java.util.zip.GZIPOutputStream"); 190 | 191 | /* Set Variables */ 192 | this.os = Server.OS.Name; 193 | 194 | if(FindNoCase("Windows", this.os)) this.slash = "\"; 195 | else this.slash = "/"; 196 | 197 | /* Default variables */ 198 | l = 0; 199 | buffer = RepeatString(" ",1024).getBytes(); 200 | gzFileName = ""; 201 | outputFile = ""; 202 | 203 | /* Convert to the right path format */ 204 | arguments.gzipFilePath = PathFormat(cachePath); 205 | arguments.filePath = PathFormat(arguments.fileJS); 206 | 207 | /* Check if the 'extractPath' string is closed */ 208 | lastChr = Right(arguments.gzipFilePath, 1); 209 | 210 | /* Set an slash at the end of string */ 211 | if(lastChr NEQ this.slash) 212 | arguments.gzipFilePath = arguments.gzipFilePath & this.slash; 213 | 214 | try 215 | { 216 | 217 | /* Set output gzip file name */ 218 | gzFileName = getFileFromPath(arguments.filePath) & ".gz"; 219 | outputFile = arguments.gzipFilePath & gzFileName; 220 | 221 | ioInput.init(arguments.filePath); 222 | ioOutput.init(outputFile); 223 | gzOutput.init(ioOutput); 224 | 225 | l = ioInput.read(buffer); 226 | 227 | while(l GT 0) 228 | { 229 | gzOutput.write(buffer, 0, l); 230 | l = ioInput.read(buffer); 231 | } 232 | 233 | /* Close the GZip file */ 234 | gzOutput.close(); 235 | ioOutput.close(); 236 | ioInput.close(); 237 | 238 | /* Return true */ 239 | return true; 240 | } 241 | 242 | catch(Any expr) 243 | { return false; } 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 284 | 285 | 286 | 287 | 288 | 289 | 290 | 291 | -------------------------------------------------------------------------------- /tinymce.gzip.pl: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | use strict; 3 | use warnings FATAL => 'all', NONFATAL => 'redefine'; 4 | 5 | 6 | ################################################### 7 | # 8 | # @author Clinton Gormley 9 | # @copyright Copyright 2008, Clinton Gormley, All rights reserved. 10 | # 11 | # This file compresses the TinyMCE JavaScript using GZip and 12 | # enables the browser to do two requests instead of one for each .js file. 13 | # Notice: This script defaults the button_tile_map option to true for 14 | # extra performance. 15 | # 16 | ################################################## 17 | our $VERSION = '2.0.3'; 18 | 19 | # Default cache path is subdir to tinymce 20 | my $cache_path = 'cache'; 21 | 22 | # Custom extra javascripts to pack 23 | my @custom_files = qw(); 24 | 25 | # Cache for 10 days 26 | our $cache_for = 3600 * 24 * 10; 27 | 28 | 29 | ################################################## 30 | 31 | use File::Spec(); 32 | use Compress::Zlib(); 33 | use Digest::MD5 qw(md5_hex); 34 | use File::Slurp qw(slurp write_file); 35 | 36 | my %P = %{ get_params() }; 37 | our $cwd = get_cwd(); 38 | 39 | my @extra_headers 40 | = $P{charset} 41 | ? 'Content-type: text/javascript; charset=' . $P{charset} 42 | : 'Content-type: text/javascript'; 43 | 44 | # If this file is requested directly, send the JS compressor file 45 | # and init with default settings 46 | unless ( $P{js} eq 'true' ) { 47 | print headers(@extra_headers); 48 | print slurp_file('tiny_mce_gzip.js'); 49 | print "tinyMCE_GZ.init({})\n"; 50 | exit; 51 | } 52 | 53 | my %modules = ( 54 | custom_files => \@custom_files, 55 | ( map { $_ => [ split( ',', $P{$_} ) ] } qw(plugins languages themes) ) 56 | ); 57 | 58 | ## Check if it supports gzip 59 | my $accept_header = $ENV{HTTP_ACCEPT_ENCODING} || ''; 60 | my ($supports_gzip) = ( $accept_header =~ m/((?:x-)?gzip)\b/i ); 61 | my $compress = ( $supports_gzip && $P{compress} ne 'false' ) ? 1 : 0; 62 | 63 | # Get params for cache 64 | $cache_path 65 | = File::Spec->file_name_is_absolute($cache_path) 66 | ? $cache_path 67 | : File::Spec->catdir( $cwd, 'cache' ); 68 | my $cache_key = get_cache_key( \%modules, $P{suffix} ); 69 | my $js_file = File::Spec->catfile( $cache_path, $cache_key . '.js' ); 70 | my $gz_file = File::Spec->catfile( $cache_path, $cache_key . '.gz' ); 71 | my $cache_file = $compress ? $gz_file : $js_file; 72 | 73 | # Use cached data or generate new? 74 | my $data 75 | = ( $P{diskcache} eq 'true' && -e $cache_file ) 76 | ? slurp_file($cache_file) 77 | : generate_and_cache_data( \%modules, $compress, $js_file, 78 | $gz_file, $P{core}, $P{suffix} ); 79 | 80 | # Send data 81 | if ($compress) { 82 | push @extra_headers, "Content-Encoding: $supports_gzip"; 83 | } 84 | 85 | print headers(@extra_headers); 86 | print $data; 87 | 88 | exit; 89 | 90 | #=================================== 91 | sub get_params { 92 | #=================================== 93 | my $qs = $ENV{QUERY_STRING} || ''; 94 | $qs =~ s/(%([0-9a-fA-F]{2,2}))/my $c = hex($2); $c < 256 ? chr($c) : $1/eg; 95 | my @raw_params = split( /[&;]+/, $qs ); 96 | my %parsed = map { $_ => '' } qw( 97 | plugins languages themes 98 | diskcache js compress 99 | core suffix charset ); 100 | while ( my $pair = shift @raw_params ) { 101 | my ( $key, $value ) = split( /=/, $pair ); 102 | next unless exists $parsed{$key}; 103 | $value ||= ''; 104 | $value =~ tr/0-9a-zA-Z\-_,//cd; 105 | $parsed{$key} = $value; 106 | } 107 | $parsed{suffix} = $parsed{suffix} eq '_src' ? '_src' : ''; 108 | return \%parsed; 109 | } 110 | 111 | #=================================== 112 | sub headers { 113 | #=================================== 114 | my @extra_headers = @_; 115 | my $date = scalar( gmtime( time + $cache_for ) ) . " GMT"; 116 | return <catfile(@_); 127 | unless ( File::Spec->file_name_is_absolute($file) ) { 128 | $file = File::Spec->catfile( $cwd, $file ); 129 | } 130 | return slurp( $file, binmode => ':raw' ); 131 | } 132 | 133 | #=================================== 134 | sub get_cwd { 135 | #=================================== 136 | return File::Spec->catpath( 137 | File::Spec->no_upwards( ( File::Spec->splitpath($0) )[ 0, 1 ] ) ); 138 | } 139 | 140 | #=================================== 141 | sub get_cache_key { 142 | #=================================== 143 | my $modules = shift; 144 | my $suffix = shift; 145 | my $cache_key = md5_hex( 146 | join( '', 147 | ( map { @{ $modules->{$_} } } 148 | qw(plugins languages themes custom_files ) 149 | ), 150 | $suffix 151 | ) 152 | ); 153 | 154 | # Untaint cache_key 155 | ($cache_key) = ( $cache_key =~ /^([0-9a-f]+)$/ ); 156 | die "Couldn't generate cache key - problem with MD5 libraries?" 157 | unless $cache_key; 158 | 159 | return $cache_key; 160 | } 161 | 162 | #=================================== 163 | sub generate_and_cache_data { 164 | #=================================== 165 | my $modules = shift; 166 | my $compress = shift; 167 | my $js_file = shift; 168 | my $gz_file = shift; 169 | my $core = shift; 170 | my $suffix = shift; 171 | 172 | # Core file plus langs 173 | my @langs = @{ $modules->{languages} }; 174 | 175 | my $js_data = join( '', map { slurp_file( 'langs', "$_.js" ) } @langs ); 176 | 177 | # Themes plus their langs 178 | foreach my $theme ( @{ $modules->{themes} } ) { 179 | $js_data 180 | .= slurp_file( 'themes', $theme, "editor_template${suffix}.js" ) 181 | . join( '', 182 | map { slurp_file( 'themes', $theme, 'langs', "$_.js" ) } 183 | @langs ); 184 | } 185 | 186 | # Plugins plus their langs 187 | foreach my $plugin ( @{ $modules->{plugins} } ) { 188 | $js_data 189 | .= slurp_file( 'plugins', $plugin, "editor_plugin${suffix}.js" ) 190 | . join( 191 | '', 192 | map { 193 | eval { 194 | slurp_file( 'plugins', $plugin, 'langs', "$_.js" ); 195 | } 196 | || '' 197 | } @langs 198 | ); 199 | } 200 | 201 | # Any custom files 202 | $js_data .= slurp_file($_) for ( @{ $modules->{custom_files} } ); 203 | 204 | # If the core is required, add that too 205 | unless ( $core eq 'false' ) { 206 | $js_data 207 | = slurp_file("tiny_mce${suffix}.js") 208 | . 'tinyMCE_GZ.start();' 209 | . $js_data 210 | . 'tinyMCE_GZ.end();'; 211 | } 212 | 213 | # Compress data 214 | my $gz_data = Compress::Zlib::memGzip($js_data) 215 | or die "Couldn't gzip data"; 216 | 217 | # write files to disk 218 | write_file( $js_file, { binmode => ':raw' }, $js_data ); 219 | write_file( $gz_file, { binmode => ':raw' }, $gz_data ); 220 | 221 | # Choose the correct data to be sent 222 | return $compress ? $gz_data : $js_data; 223 | } 224 | 225 | 1; 226 | 227 | =head1 NAME 228 | 229 | TinyMCE Compressor Perl version 2.0.3 230 | 231 | =head1 DESCRIPTION 232 | 233 | TinyMCE Compressor gzips all javascript files in TinyMCE to a single 234 | streamable file. This makes the overall download size 75% smaller and 235 | the number of requests will also be reduced. The overall initialisation 236 | time for TinyMCE will be reduced dramatically if you use this script. 237 | 238 | The Perl fork of the TinyMCE compressor project page is at 239 | L 240 | 241 | =head2 Installation 242 | 243 | Here is a step by step list on how to install the GZip compressor. 244 | 245 | =over 246 | 247 | =item Prerequisites 248 | 249 | Use CPAN to install L, L, 250 | L and L. 251 | 252 | =item Installing files 253 | 254 | Copy the tiny_mce_gzip.js and tiny_mce_gzip.pl to the tiny_mce 255 | directory. The same directory that contains the tiny_mce.js file. 256 | 257 | =item Create a cache directory 258 | 259 | Create the sub directory 'C' under your tiny_mce 260 | directory and give your web server permission to write to it, eg: 261 | 262 | cd /path/to/tinymce 263 | mkdir cache 264 | chown apache cache 265 | chmod u+rwx,og-rwx cache 266 | 267 | 268 | B If you upgrade your Tiny MCE editor, you will need to 269 | clear out the cache directory. 270 | 271 | =item Update your code 272 | 273 | Remove the current script tag: 274 | 275 | 276 | 277 | And replace it with: 278 | 279 | 280 | 281 | Add the new GZip initialization call (see below) that will 282 | tell the compressor what to include in the output. This should be the 283 | sum of all themes, plugins and languages contained on page. 284 | 285 | =back 286 | 287 | =head2 Running under mod_perl 288 | 289 | You either need to set up your web server to execute 290 | C as a CGI script, or you can configure Apache to run 291 | it under mod_perl, which will greatly speed up the response. 292 | 293 | To do this, you could use a configuration like this: 294 | 295 | 296 | SetHandler perl-script 297 | PerlResponseHandler ModPerl::Registry 298 | PerlOptions +ParseHeaders 299 | Options +ExecCGI 300 | Order allow,deny 301 | Allow from all 302 | 303 | 304 | =head2 Example of configuration 305 | 306 | The example below will pack both themes and all plugins into one 307 | file/stream. Remove the things you don't need or add you custom plugins 308 | to the settings below. Remember that the tinyMCE_GZ.init call must be 309 | placed in B 310 | 311 | 312 | 320 | 321 | 322 | 323 | 328 | 329 | =head2 Troubleshooting 330 | 331 | =over 332 | 333 | =item * 334 | 335 | The GZip compressor can fail to load if the server has odd settings or 336 | is missing the required support for it to function. To see compilation 337 | errors or other problems we suggest that you use HTTP debugging tools 338 | like HTTP Fiddler or, in Firefox, the Firebug addon, or point you 339 | browser directly to the GZip file. 340 | 341 | =item * 342 | 343 | Consult the changelog of this script and make sure that you use the 344 | latest version of TinyMCE. These two parts are pretty much tied 345 | together so there is no guarantee that it will work with older versions 346 | of TinyMCE. 347 | 348 | =back 349 | 350 | Visit the TinyMCE forum for help with the TinyMCE Gzip Compressor. 351 | 352 | =head2 Changelog and Bugs 353 | 354 | See the ChangeLog here : changelog.txt 355 | 356 | Please report any bugs that you find to clint@traveljury.com 357 | 358 | =head2 License notice 359 | 360 | The perl part of this library has been written by Clinton Gormley 361 | (clint@traveljury.com). 362 | 363 | The javascript part has been taken from the PHP compressor available at 364 | MoxieCode. 365 | 366 | This library is under LGPL license but it uses the zlib library, which 367 | is free to use in commercial applications. (Read the zlib licence). 368 | 369 | =cut 370 | -------------------------------------------------------------------------------- /tinymce.gzip.php: -------------------------------------------------------------------------------- 1 | "en", 20 | * "cache_dir" => realpath(dirname(__FILE__) . "/../../_cache"), 21 | * "files" => "somescript,anotherscript", 22 | * "expires" => "1m", 23 | */ 24 | )); 25 | 26 | // Handle request, compress and stream to client 27 | $tinyMCECompressor->handleRequest(); 28 | } 29 | 30 | /** 31 | * This class combines and compresses the TinyMCE core, plugins, themes and 32 | * language packs into one disk cached gzipped request. It improves the loading speed of TinyMCE dramatically but 33 | * still provides dynamic initialization. 34 | * 35 | * Example of direct usage: 36 | * require_once("../js/tinymce/tinymce.gzip.php"); 37 | * 38 | * // Renders script tag with compressed scripts 39 | * TinyMCE_Compressor::renderTag(array( 40 | * "url" => "../js/tinymce/tinymce.gzip.php", 41 | * "plugins" => "pagebreak,style", 42 | * "themes" => "advanced", 43 | * "languages" => "en" 44 | * )); 45 | */ 46 | class TinyMCE_Compressor { 47 | private $files, $settings; 48 | private static $defaultSettings = array( 49 | "plugins" => "", 50 | "themes" => "", 51 | "languages" => "", 52 | "disk_cache" => false, 53 | "expires" => "30d", 54 | "cache_dir" => "", 55 | "compress" => true, 56 | "files" => "", 57 | "source" => true, 58 | ); 59 | 60 | /** 61 | * Constructs a new compressor instance. 62 | * 63 | * @param Array $settings Name/value array with non-default settings for the compressor instance. 64 | */ 65 | public function __construct($settings = array()) { 66 | $this->settings = array_merge(self::$defaultSettings, $settings); 67 | 68 | if (empty($this->settings["cache_dir"])) { 69 | $this->settings["cache_dir"] = dirname(__FILE__); 70 | } 71 | } 72 | 73 | /** 74 | * Adds a file to the concatenation/compression process. 75 | * 76 | * @param String $path Path to the file to include in the compressed package/output. 77 | */ 78 | public function &addFile($file) { 79 | $this->files .= ($this->files ? "," : "") . $file; 80 | 81 | return $this; 82 | } 83 | 84 | /** 85 | * Handles the incoming HTTP request and sends back a compressed script depending on settings and client support. 86 | */ 87 | public function handleRequest() { 88 | $files = array(); 89 | $supportsGzip = false; 90 | $expiresOffset = $this->parseTime($this->settings["expires"]); 91 | $tinymceDir = dirname(__FILE__); 92 | 93 | // Plugins 94 | $plugins = self::getParam("plugins"); 95 | if ($plugins) { 96 | $this->settings["plugins"] = $plugins; 97 | } 98 | 99 | $plugins = array_unique(preg_split('/,/', $this->settings["plugins"], -1, PREG_SPLIT_NO_EMPTY)); 100 | 101 | // Themes 102 | $themes = self::getParam("themes"); 103 | if ($themes) { 104 | $this->settings["themes"] = $themes; 105 | } 106 | 107 | $themes = array_unique(preg_split('/,/', $this->settings["themes"], -1, PREG_SPLIT_NO_EMPTY)); 108 | 109 | // Languages 110 | $languages = self::getParam("languages"); 111 | if ($languages) { 112 | $this->settings["languages"] = $languages; 113 | } 114 | 115 | $languages = array_unique(preg_split('/,/', $this->settings["languages"], -1, PREG_SPLIT_NO_EMPTY)); 116 | 117 | // Files 118 | $tagFiles = self::getParam("files"); 119 | if ($tagFiles) { 120 | $this->settings["files"] = $tagFiles; 121 | } 122 | 123 | // Diskcache option 124 | $diskCache = self::getParam("diskcache"); 125 | if ($diskCache) { 126 | $this->settings["disk_cache"] = ($diskCache === "true"); 127 | } 128 | 129 | // Source or minified version 130 | $src = self::getParam("src"); 131 | if ($src) { 132 | $this->settings["source"] = ($src === "true"); 133 | } 134 | 135 | // Add core js 136 | if (self::getParam("core", "true") === "true") { 137 | $files[] = "tinymce"; 138 | } 139 | 140 | // Add core languages 141 | foreach ($languages as $language) { 142 | $files[] = "langs/" . $language; 143 | } 144 | 145 | // Add plugins 146 | foreach ($plugins as $plugin) { 147 | $files[] = "plugins/" . $plugin . "/plugin"; 148 | 149 | foreach ($languages as $language) { 150 | $files[] = "plugins/" . $plugin . "/langs/" . $language; 151 | } 152 | } 153 | 154 | // Add themes 155 | foreach ($themes as $theme) { 156 | $files[] = "themes/" . $theme . "/theme"; 157 | 158 | foreach ($languages as $language) { 159 | $files[] = "themes/" . $theme . "/langs/" . $language; 160 | } 161 | } 162 | 163 | // Add any specified files. 164 | $allFiles = array_merge($files, array_unique(preg_split('/,/', $this->settings['files'], -1, PREG_SPLIT_NO_EMPTY))); 165 | 166 | // Process source files 167 | for ($i = 0; $i < count($allFiles); $i++) { 168 | $file = $allFiles[$i]; 169 | 170 | if ($this->settings["source"] && file_exists($file . ".js")) { 171 | $file .= ".js"; 172 | } else if (file_exists($file . ".min.js")) { 173 | $file .= ".min.js"; 174 | } else { 175 | $file = ""; 176 | } 177 | 178 | $allFiles[$i] = $file; 179 | } 180 | 181 | // Generate hash for all files 182 | $hash = md5(implode('', $allFiles)); 183 | 184 | // Check if it supports gzip 185 | $zlibOn = ini_get('zlib.output_compression') || (ini_set('zlib.output_compression', 0) === false); 186 | $encodings = (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) ? strtolower($_SERVER['HTTP_ACCEPT_ENCODING']) : ""; 187 | $encoding = preg_match( '/\b(x-gzip|gzip)\b/', $encodings, $match) ? $match[1] : ""; 188 | 189 | // Is northon antivirus header 190 | if (isset($_SERVER['---------------'])) { 191 | $encoding = "x-gzip"; 192 | } 193 | 194 | $supportsGzip = $this->settings['compress'] && !empty($encoding) && !$zlibOn && function_exists('gzencode'); 195 | 196 | // Set cache file name 197 | $cacheFile = $this->settings["cache_dir"] . "/tinymce.gzip-" . $hash . ($supportsGzip ? ".gz" : ".js"); 198 | 199 | // Set headers 200 | header("Content-type: text/javascript"); 201 | header("Vary: Accept-Encoding"); // Handle proxies 202 | header("Expires: " . gmdate("D, d M Y H:i:s", time() + $expiresOffset) . " GMT"); 203 | header("Cache-Control: public, max-age=" . $expiresOffset); 204 | 205 | if ($supportsGzip) { 206 | header("Content-Encoding: " . $encoding); 207 | } 208 | 209 | // Use cached file 210 | if ($this->settings['disk_cache'] && file_exists($cacheFile)) { 211 | readfile($cacheFile); 212 | return; 213 | } 214 | 215 | // Set base URL for where tinymce is loaded from 216 | $buffer = "var tinyMCEPreInit={base:'" . dirname($_SERVER["SCRIPT_NAME"]) . "',suffix:'.min'};"; 217 | 218 | // Load all tinymce script files into buffer 219 | foreach ($allFiles as $file) { 220 | if ($file) { 221 | $fileContents = $this->getFileContents($tinymceDir . "/" . $file); 222 | // $buffer .= "\n//-FILE-$tinymceDir/$file (". strlen($fileContents) . " bytes)\n"; 223 | $buffer .= $fileContents; 224 | } 225 | } 226 | 227 | // Mark all themes, plugins and languages as done 228 | $buffer .= 'tinymce.each("' . implode(',', $files) . '".split(","),function(f){tinymce.ScriptLoader.markDone(tinyMCE.baseURL+"/"+f+".js");});'; 229 | 230 | // Compress data 231 | if ($supportsGzip) { 232 | $buffer = gzencode($buffer, 9, FORCE_GZIP); 233 | } 234 | 235 | // Write cached file 236 | if ($this->settings["disk_cache"]) { 237 | @file_put_contents($cacheFile, $buffer); 238 | } 239 | 240 | // Stream contents to client 241 | echo $buffer; 242 | } 243 | 244 | /** 245 | * Renders a script tag that loads the TinyMCE script. 246 | * 247 | * @param Array $settings Name/value array with settings for the script tag. 248 | * @param Bool $return The script tag is return instead of being output if true 249 | * @return String the tag is returned if $return is true 250 | */ 251 | public static function renderTag($tagSettings, $return = false) { 252 | $settings = array_merge(self::$defaultSettings, $tagSettings); 253 | 254 | if (empty($settings["cache_dir"])) { 255 | $settings["cache_dir"] = dirname(__FILE__); 256 | } 257 | 258 | $scriptSrc = $settings["url"] . "?js=1"; 259 | 260 | // Add plugins 261 | if (isset($settings["plugins"])) { 262 | $scriptSrc .= "&plugins=" . (is_array($settings["plugins"]) ? implode(',', $settings["plugins"]) : $settings["plugins"]); 263 | } 264 | 265 | // Add themes 266 | if (isset($settings["themes"])) { 267 | $scriptSrc .= "&themes=" . (is_array($settings["themes"]) ? implode(',', $settings["themes"]) : $settings["themes"]); 268 | } 269 | 270 | // Add languages 271 | if (isset($settings["languages"])) { 272 | $scriptSrc .= "&languages=" . (is_array($settings["languages"]) ? implode(',', $settings["languages"]) : $settings["languages"]); 273 | } 274 | 275 | // Add disk_cache 276 | if (isset($settings["disk_cache"])) { 277 | $scriptSrc .= "&diskcache=" . ($settings["disk_cache"] === true ? "true" : "false"); 278 | } 279 | 280 | // Add any explicitly specified files if the default settings have been overriden by the tag ones 281 | /* 282 | * Specifying tag files will override (rather than merge with) any site-specific ones set in the 283 | * TinyMCE_Compressor object creation. Note that since the parameter parser limits content to alphanumeric 284 | * only base filenames can be specified. The file extension is assumed to be ".js" and the directory is 285 | * the TinyMCE root directory. A typical use of this is to include a script which initiates the TinyMCE object. 286 | */ 287 | if (isset($tagSettings["files"])) { 288 | $scriptSrc .= "&files=" .(is_array($settings["files"]) ? implode(',', $settings["files"]) : $settings["files"]); 289 | } 290 | 291 | // Add src flag 292 | if (isset($settings["source"])) { 293 | $scriptSrc .= "&src=" . ($settings["source"] === true ? "true" : "false"); 294 | } 295 | 296 | $scriptTag = ''; 297 | 298 | if ($return) { 299 | return $scriptTag; 300 | } else { 301 | echo $scriptTag; 302 | } 303 | } 304 | 305 | /** 306 | * Returns a sanitized query string parameter. 307 | * 308 | * @param String $name Name of the query string param to get. 309 | * @param String $default Default value if the query string item shouldn't exist. 310 | * @return String Sanitized query string parameter value. 311 | */ 312 | public static function getParam($name, $default = "") { 313 | if (!isset($_GET[$name])) { 314 | return $default; 315 | } 316 | 317 | return preg_replace("/[^0-9a-z\-_,]+/i", "", $_GET[$name]); // Sanatize for security, remove anything but 0-9,a-z,-_, 318 | } 319 | 320 | /** 321 | * Parses the specified time format into seconds. Supports formats like 10h, 10d, 10m. 322 | * 323 | * @param String $time Time format to convert into seconds. 324 | * @return Int Number of seconds for the specified format. 325 | */ 326 | private function parseTime($time) { 327 | $multipel = 1; 328 | 329 | // Hours 330 | if (strpos($time, "h") > 0) { 331 | $multipel = 3600; 332 | } 333 | 334 | // Days 335 | if (strpos($time, "d") > 0) { 336 | $multipel = 86400; 337 | } 338 | 339 | // Months 340 | if (strpos($time, "m") > 0) { 341 | $multipel = 2592000; 342 | } 343 | 344 | // Trim string 345 | return intval($time) * $multipel; 346 | } 347 | 348 | /** 349 | * Returns the contents of the script file if it exists and removes the UTF-8 BOM header if it exists. 350 | * 351 | * @param String $file File to load. 352 | * @return String File contents or empty string if it doesn't exist. 353 | */ 354 | private function getFileContents($file) { 355 | $content = file_get_contents($file); 356 | 357 | // Remove UTF-8 BOM 358 | if (substr($content, 0, 3) === pack("CCC", 0xef, 0xbb, 0xbf)) { 359 | $content = substr($content, 3); 360 | } 361 | 362 | return $content; 363 | } 364 | } 365 | ?> 366 | --------------------------------------------------------------------------------