TinyMCE 4 optimisations. Use TinyMCE_Compressor.

This commit is contained in:
Sean Harvey 2016-04-01 17:09:24 +13:00 committed by Ingo Schommer
parent d93da92c37
commit b1b85b539e
8 changed files with 415 additions and 21 deletions

View File

@ -6,4 +6,4 @@
</FilesMatch> </FilesMatch>
<FilesMatch "silverstripe_version$"> <FilesMatch "silverstripe_version$">
Deny from all Deny from all
</FilesMatch> </FilesMatch>

View File

@ -0,0 +1 @@
{"version":3,"sources":["node_modules/browserify/node_modules/browser-pack/_prelude.js","admin/javascript/src/boot/index.js","admin/javascript/src/components/form-action/index.js","admin/javascript/src/sections/campaign-admin/controller.js","admin/javascript/src/sections/campaign-admin/index.js","admin/javascript/src/state/config/action-types.js","admin/javascript/src/state/config/actions.js","admin/javascript/src/state/config/reducer.js","admin/javascript/src/state/records/action-types.js","admin/javascript/src/state/records/reducer.js","admin/javascript/src/state/schema/action-types.js","admin/javascript/src/state/schema/reducer.js","node_modules/redux-logger/lib/index.js"],"names":[],"mappings":"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CF,SAqCO,sBAAa,qBAAb;AACH,mBAAa,OAAO,OAAP,CAAe,UAAf,CADf;AAEE,gBAAU,MAAM,UAAN,EACP,MADO,CACA;eAAU,OAAO,EAAP,KAAc,OAAO,OAAP,CAAe,EAAf;OAAxB,CADV,CAFF;;AAKE,aAAO,0BAAW,OAAO,MAAP,CAAc,EAAd,EAAkB,KAAlB,sBACf,YAAa,QADE,CAAX,CAAP,CALF;;AArCF;AA+CI,aAAO,KAAP,CADF;AA9CF,GAJoD;CAAtD;;kBAuDe;;;;;;;;AC7Df,IAAM,eAAe;AACnB,cAAY,YAAZ;CADI;;kBAIS;;;;;;;;kBCCS;;AALxB;;;;AACA;;;;;;;;AAEA,IAAM,eAAe,0BAAW,EAAX,CAAf;;AAES,SAAS,aAAT,GAA4D;MAArC,8DAAQ,4BAA6B;MAAf,+DAAS,oBAAM;;AACzE,UAAQ,OAAO,IAAP;;AAEN,SAAK,sBAAa,UAAb;AAAyB;AAC5B,YAAM,KAAK,OAAO,OAAP,CAAe,MAAf,CAAsB,UAAtB,CADiB;AAE5B,eAAO,0BAAW,OAAO,MAAP,CAAc,EAAd,EAAkB,KAAlB,sBAA4B,IAAK,OAAO,OAAP,CAAjC,CAAX,CAAP,CAF4B;OAA9B;;AAFF;AAQI,aAAO,KAAP,CADF;AAPF,GADyE;CAA5D;;;ACLf;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA","file":"bundle-framework.js","sourceRoot":"/source/","sourcesContent":["(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);var f=new Error(\"Cannot find module '\"+o+\"'\");throw f.code=\"MODULE_NOT_FOUND\",f}var l=n[o]={exports:{}};t[o][0].call(l.exports,function(e){var n=t[o][1][e];return s(n?n:e)},l,l.exports,e,t,n,r)}return n[o].exports}var i=typeof require==\"function\"&&require;for(var o=0;o<r.length;o++)s(r[o]);return s})","import $ from 'jQuery';\nimport { combineReducers, createStore, applyMiddleware } from 'redux';\nimport thunkMiddleware from 'redux-thunk';\nimport createLogger from 'redux-logger';\nimport reducerRegister from 'reducer-register';\n\nimport * as configActions from 'state/config/actions';\nimport ConfigReducer from 'state/config/reducer';\nimport SchemaReducer from 'state/schema/reducer';\nimport RecordsReducer from 'state/records/reducer';\n\n// Sections\n// eslint-disable-next-line no-unused-vars\nimport CampaignAdmin from 'sections/campaign-admin/index';\n\nfunction appBoot() {\n reducerRegister.add('config', ConfigReducer);\n reducerRegister.add('schemas', SchemaReducer);\n reducerRegister.add('records', RecordsReducer);\n\n const initialState = {};\n const rootReducer = combineReducers(reducerRegister.getAll());\n const createStoreWithMiddleware = applyMiddleware(thunkMiddleware, createLogger())(createStore);\n\n // TODO: The store needs to be passed into route callbacks on the route context.\n window.store = createStoreWithMiddleware(rootReducer, initialState);\n\n // Set the initial config state.\n configActions.setConfig(window.ss.config)(window.store.dispatch);\n}\n\n// TODO: This should be using `window.onload` but isn't because\n// Entwine hooks are being used to set up the <Provider>.\n// `window.onload` happens AFTER these Entwine hooks which means\n// the store is undefined when the <Provider> is constructed.\n$('body').entwine({ onadd: () => { appBoot(); } });\n","import React from 'react';\nimport SilverStripeComponent from 'silverstripe-component';\n\nclass FormActionComponent extends SilverStripeComponent {\n constructor(props) {\n super(props);\n\n this.handleClick = this.handleClick.bind(this);\n }\n\n render() {\n return (\n <button type={this.props.type} className={this.getButtonClasses()} onClick={this.handleClick}>\n {this.getLoadingIcon()}\n {this.props.label}\n </button>\n );\n }\n\n /**\n * Returns the necessary button classes based on the given props\n *\n * @returns string\n */\n getButtonClasses() {\n let buttonClasses = 'btn';\n\n // Add 'type' class\n buttonClasses += ` btn-${this.props.style}`;\n\n // If there is no text\n if (typeof this.props.label === 'undefined') {\n buttonClasses += ' no-text';\n }\n\n // Add icon class\n if (typeof this.props.icon !== 'undefined') {\n buttonClasses += ` font-icon-${this.props.icon}`;\n }\n\n // Add loading class\n if (this.props.loading === true) {\n buttonClasses += ' btn--loading';\n }\n\n // Add disabled class\n if (this.props.disabled === true) {\n buttonClasses += ' disabled';\n }\n\n return buttonClasses;\n }\n\n /**\n * Returns markup for the loading icon\n *\n * @returns object|null\n */\n getLoadingIcon() {\n if (this.props.loading) {\n return (\n <div className=\"btn__loading-icon\" >\n <svg viewBox=\"0 0 44 12\">\n <circle cx=\"6\" cy=\"6\" r=\"6\" />\n <circle cx=\"22\" cy=\"6\" r=\"6\" />\n <circle cx=\"38\" cy=\"6\" r=\"6\" />\n </svg>\n </div>\n );\n }\n\n return null;\n }\n\n /**\n * Event handler triggered when a user clicks the button.\n *\n * @param object event\n * @returns null\n */\n handleClick(event) {\n this.props.handleClick(event);\n }\n\n}\n\nFormActionComponent.propTypes = {\n handleClick: React.PropTypes.func.isRequired,\n label: React.PropTypes.string,\n type: React.PropTypes.string,\n loading: React.PropTypes.bool,\n icon: React.PropTypes.string,\n disabled: React.PropTypes.bool,\n style: React.PropTypes.string,\n};\n\nFormActionComponent.defaultProps = {\n type: 'button',\n style: 'secondary',\n};\n\nexport default FormActionComponent;\n","import React from 'react';\nimport { connect } from 'react-redux';\nimport SilverStripeComponent from 'silverstripe-component';\nimport FormAction from 'components/form-action/index';\nimport i18n from 'i18n';\nimport NorthHeader from 'components/north-header/index';\nimport FormBuilder from 'components/form-builder/index';\n\nclass CampaignAdminContainer extends SilverStripeComponent {\n\n constructor(props) {\n super(props);\n\n this.addCampaign = this.addCampaign.bind(this);\n }\n\n render() {\n const schemaUrl = this.props.config.forms.editForm.schemaUrl;\n\n return (\n <div>\n <NorthHeader />\n <FormAction\n label={i18n._t('Campaigns.ADDCAMPAIGN')}\n icon={'plus-circled'}\n handleClick={this.addCampaign}\n />\n <FormBuilder schemaUrl={schemaUrl} />\n </div>\n );\n }\n\n addCampaign() {\n // Add campaign\n }\n\n}\n\nCampaignAdminContainer.propTypes = {\n config: React.PropTypes.shape({\n forms: React.PropTypes.shape({\n editForm: React.PropTypes.shape({\n schemaUrl: React.PropTypes.string,\n }),\n }),\n }),\n sectionConfigKey: React.PropTypes.string.isRequired,\n};\n\nfunction mapStateToProps(state, ownProps) {\n return {\n config: state.config.sections[ownProps.sectionConfigKey],\n };\n}\n\nexport default connect(mapStateToProps)(CampaignAdminContainer);\n","import $ from 'jQuery';\nimport React from 'react';\nimport ReactDOM from 'react-dom';\nimport { Provider } from 'react-redux';\nimport CampaignAdmin from './controller';\n\n// eslint-disable-next-line no-shadow\n$.entwine('ss', ($) => {\n $('.cms-content.CampaignAdmin').entwine({\n onadd() {\n ReactDOM.render(\n <Provider store={window.store}>\n <CampaignAdmin sectionConfigKey=\"CampaignAdmin\" />\n </Provider>\n , this[0]);\n },\n\n onremove() {\n ReactDOM.unmountComponentAtNode(this[0]);\n },\n });\n});\n","export default {\n SET_CONFIG: 'SET_CONFIG',\n};\n","import ACTION_TYPES from './action-types';\n\n/**\n * Sets global config for the application.\n *\n * @param object config\n */\nexport function setConfig(config) {\n return (dispatch) =>\n dispatch({\n type: ACTION_TYPES.SET_CONFIG,\n payload: { config },\n });\n}\n","import deepFreeze from 'deep-freeze';\nimport ACTION_TYPES from './action-types';\n\nfunction configReducer(state = {}, action) {\n switch (action.type) {\n\n case ACTION_TYPES.SET_CONFIG:\n return deepFreeze(Object.assign({}, state, action.payload.config));\n\n default:\n return state;\n\n }\n}\n\nexport default configReducer;\n","export default {\n CREATE_RECORD: 'CREATE_RECORD',\n UPDATE_RECORD: 'UPDATE_RECORD',\n DELETE_RECORD: 'DELETE_RECORD',\n FETCH_RECORDS_REQUEST: 'FETCH_RECORDS_REQUEST',\n FETCH_RECORDS_FAILURE: 'FETCH_RECORDS_FAILURE',\n FETCH_RECORDS_SUCCESS: 'FETCH_RECORDS_SUCCESS',\n DELETE_RECORD_REQUEST: 'DELETE_RECORD_REQUEST',\n DELETE_RECORD_FAILURE: 'DELETE_RECORD_FAILURE',\n DELETE_RECORD_SUCCESS: 'DELETE_RECORD_SUCCESS',\n};\n","import deepFreeze from 'deep-freeze';\nimport ACTION_TYPES from './action-types';\n\nconst initialState = {\n};\n\nfunction recordsReducer(state = initialState, action) {\n let records;\n let recordType;\n\n switch (action.type) {\n\n case ACTION_TYPES.CREATE_RECORD:\n return deepFreeze(Object.assign({}, state, {\n\n }));\n\n case ACTION_TYPES.UPDATE_RECORD:\n return deepFreeze(Object.assign({}, state, {\n\n }));\n\n case ACTION_TYPES.DELETE_RECORD:\n return deepFreeze(Object.assign({}, state, {\n\n }));\n\n case ACTION_TYPES.FETCH_RECORDS_REQUEST:\n return state;\n\n case ACTION_TYPES.FETCH_RECORDS_FAILURE:\n return state;\n\n case ACTION_TYPES.FETCH_RECORDS_SUCCESS:\n recordType = action.payload.recordType;\n // TODO Automatic pluralisation from recordType\n records = action.payload.data._embedded[`${recordType}s`];\n return deepFreeze(Object.assign({}, state, {\n [recordType]: records,\n }));\n\n case ACTION_TYPES.DELETE_RECORD_REQUEST:\n return state;\n\n case ACTION_TYPES.DELETE_RECORD_FAILURE:\n return state;\n\n case ACTION_TYPES.DELETE_RECORD_SUCCESS:\n recordType = action.payload.recordType;\n records = state[recordType]\n .filter(record => record.ID !== action.payload.id);\n\n return deepFreeze(Object.assign({}, state, {\n [recordType]: records,\n }));\n\n default:\n return state;\n }\n}\n\nexport default recordsReducer;\n","const ACTION_TYPES = {\n SET_SCHEMA: 'SET_SCHEMA',\n};\n\nexport default ACTION_TYPES;\n","import deepFreeze from 'deep-freeze';\nimport ACTION_TYPES from './action-types';\n\nconst initialState = deepFreeze({});\n\nexport default function schemaReducer(state = initialState, action = null) {\n switch (action.type) {\n\n case ACTION_TYPES.SET_SCHEMA: {\n const id = action.payload.schema.schema_url;\n return deepFreeze(Object.assign({}, state, { [id]: action.payload }));\n }\n\n default:\n return state;\n }\n}\n","\"use strict\";\n\nfunction _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) { arr2[i] = arr[i]; } return arr2; } else { return Array.from(arr); } }\n\nfunction _typeof(obj) { return obj && typeof Symbol !== \"undefined\" && obj.constructor === Symbol ? \"symbol\" : typeof obj; }\n\nvar repeat = function repeat(str, times) {\n return new Array(times + 1).join(str);\n};\nvar pad = function pad(num, maxLength) {\n return repeat(\"0\", maxLength - num.toString().length) + num;\n};\nvar formatTime = function formatTime(time) {\n return \"@ \" + pad(time.getHours(), 2) + \":\" + pad(time.getMinutes(), 2) + \":\" + pad(time.getSeconds(), 2) + \".\" + pad(time.getMilliseconds(), 3);\n};\n\n// Use the new performance api to get better precision if available\nvar timer = typeof performance !== \"undefined\" && typeof performance.now === \"function\" ? performance : Date;\n\n/**\n * parse the level option of createLogger\n *\n * @property {string | function | object} level - console[level]\n * @property {object} action\n * @property {array} payload\n * @property {string} type\n */\n\nfunction getLogLevel(level, action, payload, type) {\n switch (typeof level === \"undefined\" ? \"undefined\" : _typeof(level)) {\n case \"object\":\n return typeof level[type] === \"function\" ? level[type].apply(level, _toConsumableArray(payload)) : level[type];\n case \"function\":\n return level(action);\n default:\n return level;\n }\n}\n\n/**\n * Creates logger with followed options\n *\n * @namespace\n * @property {object} options - options for logger\n * @property {string | function | object} options.level - console[level]\n * @property {boolean} options.duration - print duration of each action?\n * @property {boolean} options.timestamp - print timestamp with each action?\n * @property {object} options.colors - custom colors\n * @property {object} options.logger - implementation of the `console` API\n * @property {boolean} options.logErrors - should errors in action execution be caught, logged, and re-thrown?\n * @property {boolean} options.collapsed - is group collapsed?\n * @property {boolean} options.predicate - condition which resolves logger behavior\n * @property {function} options.stateTransformer - transform state before print\n * @property {function} options.actionTransformer - transform action before print\n * @property {function} options.errorTransformer - transform error before print\n */\n\nfunction createLogger() {\n var options = arguments.length <= 0 || arguments[0] === undefined ? {} : arguments[0];\n var _options$level = options.level;\n var level = _options$level === undefined ? \"log\" : _options$level;\n var _options$logger = options.logger;\n var logger = _options$logger === undefined ? console : _options$logger;\n var _options$logErrors = options.logErrors;\n var logErrors = _options$logErrors === undefined ? true : _options$logErrors;\n var collapsed = options.collapsed;\n var predicate = options.predicate;\n var _options$duration = options.duration;\n var duration = _options$duration === undefined ? false : _options$duration;\n var _options$timestamp = options.timestamp;\n var timestamp = _options$timestamp === undefined ? true : _options$timestamp;\n var transformer = options.transformer;\n var _options$stateTransfo = options.stateTransformer;\n var // deprecated\n stateTransformer = _options$stateTransfo === undefined ? function (state) {\n return state;\n } : _options$stateTransfo;\n var _options$actionTransf = options.actionTransformer;\n var actionTransformer = _options$actionTransf === undefined ? function (actn) {\n return actn;\n } : _options$actionTransf;\n var _options$errorTransfo = options.errorTransformer;\n var errorTransformer = _options$errorTransfo === undefined ? function (error) {\n return error;\n } : _options$errorTransfo;\n var _options$colors = options.colors;\n var colors = _options$colors === undefined ? {\n title: function title() {\n return \"#000000\";\n },\n prevState: function prevState() {\n return \"#9E9E9E\";\n },\n action: function action() {\n return \"#03A9F4\";\n },\n nextState: function nextState() {\n return \"#4CAF50\";\n },\n error: function error() {\n return \"#F20404\";\n }\n } : _options$colors;\n\n // exit if console undefined\n\n if (typeof logger === \"undefined\") {\n return function () {\n return function (next) {\n return function (action) {\n return next(action);\n };\n };\n };\n }\n\n if (transformer) {\n console.error(\"Option 'transformer' is deprecated, use stateTransformer instead\");\n }\n\n var logBuffer = [];\n function printBuffer() {\n logBuffer.forEach(function (logEntry, key) {\n var started = logEntry.started;\n var startedTime = logEntry.startedTime;\n var action = logEntry.action;\n var prevState = logEntry.prevState;\n var error = logEntry.error;\n var took = logEntry.took;\n var nextState = logEntry.nextState;\n\n var nextEntry = logBuffer[key + 1];\n if (nextEntry) {\n nextState = nextEntry.prevState;\n took = nextEntry.started - started;\n }\n // message\n var formattedAction = actionTransformer(action);\n var isCollapsed = typeof collapsed === \"function\" ? collapsed(function () {\n return nextState;\n }, action) : collapsed;\n\n var formattedTime = formatTime(startedTime);\n var titleCSS = colors.title ? \"color: \" + colors.title(formattedAction) + \";\" : null;\n var title = \"action \" + (timestamp ? formattedTime : \"\") + \" \" + formattedAction.type + \" \" + (duration ? \"(in \" + took.toFixed(2) + \" ms)\" : \"\");\n\n // render\n try {\n if (isCollapsed) {\n if (colors.title) logger.groupCollapsed(\"%c \" + title, titleCSS);else logger.groupCollapsed(title);\n } else {\n if (colors.title) logger.group(\"%c \" + title, titleCSS);else logger.group(title);\n }\n } catch (e) {\n logger.log(title);\n }\n\n var prevStateLevel = getLogLevel(level, formattedAction, [prevState], \"prevState\");\n var actionLevel = getLogLevel(level, formattedAction, [formattedAction], \"action\");\n var errorLevel = getLogLevel(level, formattedAction, [error, prevState], \"error\");\n var nextStateLevel = getLogLevel(level, formattedAction, [nextState], \"nextState\");\n\n if (prevStateLevel) {\n if (colors.prevState) logger[prevStateLevel](\"%c prev state\", \"color: \" + colors.prevState(prevState) + \"; font-weight: bold\", prevState);else logger[prevStateLevel](\"prev state\", prevState);\n }\n\n if (actionLevel) {\n if (colors.action) logger[actionLevel](\"%c action\", \"color: \" + colors.action(formattedAction) + \"; font-weight: bold\", formattedAction);else logger[actionLevel](\"action\", formattedAction);\n }\n\n if (error && errorLevel) {\n if (colors.error) logger[errorLevel](\"%c error\", \"color: \" + colors.error(error, prevState) + \"; font-weight: bold\", error);else logger[errorLevel](\"error\", error);\n }\n\n if (nextStateLevel) {\n if (colors.nextState) logger[nextStateLevel](\"%c next state\", \"color: \" + colors.nextState(nextState) + \"; font-weight: bold\", nextState);else logger[nextStateLevel](\"next state\", nextState);\n }\n\n try {\n logger.groupEnd();\n } catch (e) {\n logger.log(\"—— log end ——\");\n }\n });\n logBuffer.length = 0;\n }\n\n return function (_ref) {\n var getState = _ref.getState;\n return function (next) {\n return function (action) {\n // exit early if predicate function returns false\n if (typeof predicate === \"function\" && !predicate(getState, action)) {\n return next(action);\n }\n\n var logEntry = {};\n logBuffer.push(logEntry);\n\n logEntry.started = timer.now();\n logEntry.startedTime = new Date();\n logEntry.prevState = stateTransformer(getState());\n logEntry.action = action;\n\n var returnedValue = undefined;\n if (logErrors) {\n try {\n returnedValue = next(action);\n } catch (e) {\n logEntry.error = errorTransformer(e);\n }\n } else {\n returnedValue = next(action);\n }\n\n logEntry.took = timer.now() - logEntry.started;\n logEntry.nextState = stateTransformer(getState());\n\n printBuffer();\n\n if (logEntry.error) throw logEntry.error;\n return returnedValue;\n };\n };\n };\n}\n\nmodule.exports = createLogger;"]}

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -8,6 +8,14 @@
*/ */
class HtmlEditorField extends TextareaField { class HtmlEditorField extends TextareaField {
/**
* Use TinyMCE's GZIP compressor
*
* @config
* @var bool
*/
private static $use_gzip = true;
/** /**
* Should we check the valid_elements (& extended_valid_elements) rules from HtmlEditorConfig server side? * Should we check the valid_elements (& extended_valid_elements) rules from HtmlEditorConfig server side?
* *

View File

@ -409,20 +409,51 @@ class TinyMCEConfig extends HtmlEditorConfig {
); );
} }
} }
return $editor; return $editor;
} }
public function init() { /**
* Generate gzipped TinyMCE configuration including plugins and languages.
* This ends up "pre-loading" TinyMCE bundled with the required plugins
* so that multiple HTTP requests on the client don't need to be made.
*/
public function requireJS() {
require_once THIRDPARTY_PATH . '/tinymce/tiny_mce_gzip.php';
$useGzip = Config::inst()->get('HtmlEditorField', 'use_gzip');
$languages = array();
foreach(self::$configs as $configID => $config) {
$languages[] = $config->getOption('language');
}
// tinyMCE JS requirement
if($useGzip) {
$tag = TinyMCE_Compressor::renderTag(array(
'url' => THIRDPARTY_DIR . '/tinymce/tiny_mce_gzip.php',
'plugins' => implode(',', array_keys($this->getPlugins())),
'themes' => $this->getTheme(),
'languages' => implode(',', array_filter($languages))
), true);
preg_match('/src="([^"]*)"/', $tag, $matches);
Requirements::javascript(html_entity_decode($matches[1]));
} else {
Requirements::javascript(THIRDPARTY_DIR . '/tinymce/tinymce.min.js');
}
}
public function init() {
// These should be 'provides' by bundle-dist.js // These should be 'provides' by bundle-dist.js
Requirements::javascript(FRAMEWORK_DIR . "/thirdparty/jquery/jquery.js"); Requirements::javascript(FRAMEWORK_DIR . "/thirdparty/jquery/jquery.js");
Requirements::javascript(THIRDPARTY_DIR . '/jquery-ui/jquery-ui.js'); Requirements::javascript(THIRDPARTY_DIR . '/jquery-ui/jquery-ui.js');
Requirements::javascript(THIRDPARTY_DIR . '/jquery-entwine/dist/jquery.entwine-dist.js'); Requirements::javascript(THIRDPARTY_DIR . '/jquery-entwine/dist/jquery.entwine-dist.js');
Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/javascript/dist/ssui.core.js'); Requirements::javascript(FRAMEWORK_ADMIN_DIR . '/javascript/dist/ssui.core.js');
Requirements::javascript(THIRDPARTY_DIR . '/tinymce/jquery.tinymce.min.js'); // include TinyMCE Javascript
Requirements::javascript(THIRDPARTY_DIR . '/tinymce/tinymce.min.js'); $this->requireJS();
Requirements::javascript(FRAMEWORK_DIR ."/javascript/dist/HtmlEditorField.js"); Requirements::javascript(FRAMEWORK_DIR ."/javascript/dist/HtmlEditorField.js");
Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery-ui.css'); Requirements::css(THIRDPARTY_DIR . '/jquery-ui-themes/smoothness/jquery-ui.css');
} }
} }

View File

@ -91,18 +91,6 @@ var jquerySizesConfig = {
files: ['/lib/jquery.sizes.js'] files: ['/lib/jquery.sizes.js']
}; };
var tinymceConfig = {
src: PATHS.MODULES + '/tinymce',
dest: PATHS.FRAMEWORK_THIRDPARTY + '/tinymce',
files: [
'/tinymce.min.js', // Exclude unminified file to keep repository size down
'/jquery.tinymce.min.js',
'/themes/**',
'/skins/**',
'/plugins/**'
]
};
/** /**
* Copies files from a source directory to a destination directory. * Copies files from a source directory to a destination directory.
* *
@ -248,7 +236,6 @@ gulp.task('sanity', function () {
diffFiles(blueimpLoadImageConfig); diffFiles(blueimpLoadImageConfig);
diffFiles(blueimpTmplConfig); diffFiles(blueimpTmplConfig);
diffFiles(jquerySizesConfig); diffFiles(jquerySizesConfig);
diffFiles(tinymceConfig);
}); });
gulp.task('thirdparty', function () { gulp.task('thirdparty', function () {

365
thirdparty/tinymce/tiny_mce_gzip.php vendored Executable file
View File

@ -0,0 +1,365 @@
<?php
/**
* tinymce.gzip.php
*
* Copyright, Moxiecode Systems AB
* Released under LGPL License.
*
* License: http://tinymce.moxiecode.com/license
* Contributing: http://tinymce.moxiecode.com/contributing
*/
// Handle incoming request if it's a script call
if (TinyMCE_Compressor::getParam("js")) {
// Default settings
$tinyMCECompressor = new TinyMCE_Compressor(array(
/*
* Add any site-specific defaults here that you may wish to implement. For example:
*
* "languages" => "en",
* "cache_dir" => realpath(dirname(__FILE__) . "/../../_cache"),
* "files" => "somescript,anotherscript",
* "expires" => "1m",
*/
));
// Handle request, compress and stream to client
$tinyMCECompressor->handleRequest();
}
/**
* This class combines and compresses the TinyMCE core, plugins, themes and
* language packs into one disk cached gzipped request. It improves the loading speed of TinyMCE dramatically but
* still provides dynamic initialization.
*
* Example of direct usage:
* require_once("../js/tinymce/tinymce.gzip.php");
*
* // Renders script tag with compressed scripts
* TinyMCE_Compressor::renderTag(array(
* "url" => "../js/tinymce/tinymce.gzip.php",
* "plugins" => "pagebreak,style",
* "themes" => "advanced",
* "languages" => "en"
* ));
*/
class TinyMCE_Compressor {
private $files, $settings;
private static $defaultSettings = array(
"plugins" => "",
"themes" => "",
"languages" => "",
"disk_cache" => false,
"expires" => "30d",
"cache_dir" => "",
"compress" => true,
"files" => "",
"source" => true,
);
/**
* Constructs a new compressor instance.
*
* @param Array $settings Name/value array with non-default settings for the compressor instance.
*/
public function __construct($settings = array()) {
$this->settings = array_merge(self::$defaultSettings, $settings);
if (empty($this->settings["cache_dir"])) {
$this->settings["cache_dir"] = dirname(__FILE__);
}
}
/**
* Adds a file to the concatenation/compression process.
*
* @param String $path Path to the file to include in the compressed package/output.
*/
public function &addFile($file) {
$this->files .= ($this->files ? "," : "") . $file;
return $this;
}
/**
* Handles the incoming HTTP request and sends back a compressed script depending on settings and client support.
*/
public function handleRequest() {
$files = array();
$supportsGzip = false;
$expiresOffset = $this->parseTime($this->settings["expires"]);
$tinymceDir = dirname(__FILE__);
// Plugins
$plugins = self::getParam("plugins");
if ($plugins) {
$this->settings["plugins"] = $plugins;
}
$plugins = array_unique(preg_split('/,/', $this->settings["plugins"], -1, PREG_SPLIT_NO_EMPTY));
// Themes
$themes = self::getParam("themes");
if ($themes) {
$this->settings["themes"] = $themes;
}
$themes = array_unique(preg_split('/,/', $this->settings["themes"], -1, PREG_SPLIT_NO_EMPTY));
// Languages
$languages = self::getParam("languages");
if ($languages) {
$this->settings["languages"] = $languages;
}
$languages = array_unique(preg_split('/,/', $this->settings["languages"], -1, PREG_SPLIT_NO_EMPTY));
// Files
$tagFiles = self::getParam("files");
if ($tagFiles) {
$this->settings["files"] = $tagFiles;
}
// Diskcache option
$diskCache = self::getParam("diskcache");
if ($diskCache) {
$this->settings["disk_cache"] = ($diskCache === "true");
}
// Source or minified version
$src = self::getParam("src");
if ($src) {
$this->settings["source"] = ($src === "true");
}
// Add core js
if (self::getParam("core", "true") === "true") {
$files[] = "tinymce";
}
// Add core languages
foreach ($languages as $language) {
$files[] = "langs/" . $language;
}
// Add plugins
foreach ($plugins as $plugin) {
$files[] = "plugins/" . $plugin . "/plugin";
foreach ($languages as $language) {
$files[] = "plugins/" . $plugin . "/langs/" . $language;
}
}
// Add themes
foreach ($themes as $theme) {
$files[] = "themes/" . $theme . "/theme";
foreach ($languages as $language) {
$files[] = "themes/" . $theme . "/langs/" . $language;
}
}
// Add any specified files.
$allFiles = array_merge($files, array_unique(preg_split('/,/', $this->settings['files'], -1, PREG_SPLIT_NO_EMPTY)));
// Process source files
for ($i = 0; $i < count($allFiles); $i++) {
$file = $allFiles[$i];
if ($this->settings["source"] && file_exists($file . ".js")) {
$file .= ".js";
} else if (file_exists($file . ".min.js")) {
$file .= ".min.js";
} else {
$file = "";
}
$allFiles[$i] = $file;
}
// Generate hash for all files
$hash = md5(implode('', $allFiles));
// Check if it supports gzip
$zlibOn = ini_get('zlib.output_compression') || (ini_set('zlib.output_compression', 0) === false);
$encodings = (isset($_SERVER['HTTP_ACCEPT_ENCODING'])) ? strtolower($_SERVER['HTTP_ACCEPT_ENCODING']) : "";
$encoding = preg_match( '/\b(x-gzip|gzip)\b/', $encodings, $match) ? $match[1] : "";
// Is northon antivirus header
if (isset($_SERVER['---------------'])) {
$encoding = "x-gzip";
}
$supportsGzip = $this->settings['compress'] && !empty($encoding) && !$zlibOn && function_exists('gzencode');
// Set cache file name
$cacheFile = $this->settings["cache_dir"] . "/tinymce.gzip-" . $hash . ($supportsGzip ? ".gz" : ".js");
// Set headers
header("Content-type: text/javascript");
header("Vary: Accept-Encoding"); // Handle proxies
header("Expires: " . gmdate("D, d M Y H:i:s", time() + $expiresOffset) . " GMT");
header("Cache-Control: public, max-age=" . $expiresOffset);
if ($supportsGzip) {
header("Content-Encoding: " . $encoding);
}
// Use cached file
if ($this->settings['disk_cache'] && file_exists($cacheFile)) {
readfile($cacheFile);
return;
}
// Set base URL for where tinymce is loaded from
$buffer = "var tinyMCEPreInit={base:'" . dirname($_SERVER["SCRIPT_NAME"]) . "',suffix:'.min'};";
// Load all tinymce script files into buffer
foreach ($allFiles as $file) {
if ($file) {
$fileContents = $this->getFileContents($tinymceDir . "/" . $file);
// $buffer .= "\n//-FILE-$tinymceDir/$file (". strlen($fileContents) . " bytes)\n";
$buffer .= $fileContents;
}
}
// Mark all themes, plugins and languages as done
$buffer .= 'tinymce.each("' . implode(',', $files) . '".split(","),function(f){tinymce.ScriptLoader.markDone(tinyMCE.baseURL+"/"+f+".js");});';
// Compress data
if ($supportsGzip) {
$buffer = gzencode($buffer, 9, FORCE_GZIP);
}
// Write cached file
if ($this->settings["disk_cache"]) {
@file_put_contents($cacheFile, $buffer);
}
// Stream contents to client
echo $buffer;
}
/**
* Renders a script tag that loads the TinyMCE script.
*
* @param Array $settings Name/value array with settings for the script tag.
* @param Bool $return The script tag is return instead of being output if true
* @return String the tag is returned if $return is true
*/
public static function renderTag($tagSettings, $return = false) {
$settings = array_merge(self::$defaultSettings, $tagSettings);
if (empty($settings["cache_dir"])) {
$settings["cache_dir"] = dirname(__FILE__);
}
$scriptSrc = $settings["url"] . "?js=1";
// Add plugins
if (isset($settings["plugins"])) {
$scriptSrc .= "&plugins=" . (is_array($settings["plugins"]) ? implode(',', $settings["plugins"]) : $settings["plugins"]);
}
// Add themes
if (isset($settings["themes"])) {
$scriptSrc .= "&themes=" . (is_array($settings["themes"]) ? implode(',', $settings["themes"]) : $settings["themes"]);
}
// Add languages
if (isset($settings["languages"])) {
$scriptSrc .= "&languages=" . (is_array($settings["languages"]) ? implode(',', $settings["languages"]) : $settings["languages"]);
}
// Add disk_cache
if (isset($settings["disk_cache"])) {
$scriptSrc .= "&diskcache=" . ($settings["disk_cache"] === true ? "true" : "false");
}
// Add any explicitly specified files if the default settings have been overriden by the tag ones
/*
* Specifying tag files will override (rather than merge with) any site-specific ones set in the
* TinyMCE_Compressor object creation. Note that since the parameter parser limits content to alphanumeric
* only base filenames can be specified. The file extension is assumed to be ".js" and the directory is
* the TinyMCE root directory. A typical use of this is to include a script which initiates the TinyMCE object.
*/
if (isset($tagSettings["files"])) {
$scriptSrc .= "&files=" .(is_array($settings["files"]) ? implode(',', $settings["files"]) : $settings["files"]);
}
// Add src flag
if (isset($settings["source"])) {
$scriptSrc .= "&src=" . ($settings["source"] === true ? "true" : "false");
}
$scriptTag = '<script src="' . htmlspecialchars($scriptSrc) . '"></script>';
if ($return) {
return $scriptTag;
} else {
echo $scriptTag;
}
}
/**
* Returns a sanitized query string parameter.
*
* @param String $name Name of the query string param to get.
* @param String $default Default value if the query string item shouldn't exist.
* @return String Sanitized query string parameter value.
*/
public static function getParam($name, $default = "") {
if (!isset($_GET[$name])) {
return $default;
}
return preg_replace("/[^0-9a-z\-_,]+/i", "", $_GET[$name]); // Sanatize for security, remove anything but 0-9,a-z,-_,
}
/**
* Parses the specified time format into seconds. Supports formats like 10h, 10d, 10m.
*
* @param String $time Time format to convert into seconds.
* @return Int Number of seconds for the specified format.
*/
private function parseTime($time) {
$multipel = 1;
// Hours
if (strpos($time, "h") > 0) {
$multipel = 3600;
}
// Days
if (strpos($time, "d") > 0) {
$multipel = 86400;
}
// Months
if (strpos($time, "m") > 0) {
$multipel = 2592000;
}
// Trim string
return intval($time) * $multipel;
}
/**
* Returns the contents of the script file if it exists and removes the UTF-8 BOM header if it exists.
*
* @param String $file File to load.
* @return String File contents or empty string if it doesn't exist.
*/
private function getFileContents($file) {
$content = file_get_contents($file);
// Remove UTF-8 BOM
if (substr($content, 0, 3) === pack("CCC", 0xef, 0xbb, 0xbf)) {
$content = substr($content, 3);
}
return $content;
}
}
?>