├── .dev-lib ├── .editorconfig ├── .eslintignore ├── .eslintrc ├── .gitignore ├── .gitmodules ├── .jscsrc ├── .jshintignore ├── .jshintrc ├── .travis.yml ├── Gruntfile.js ├── customizer-dev-tools.php ├── js ├── base.js ├── pane.js └── preview.js ├── package.json ├── php └── class-plugin.php ├── phpcs.xml ├── readme.md ├── readme.txt └── wp-assets ├── screenshot-1.png ├── screenshot-2.png ├── screenshot-3.png ├── screenshot-4.png ├── screenshot-5.png └── screenshot-6.png /.dev-lib: -------------------------------------------------------------------------------- 1 | PATH_INCLUDES='*.* php js' 2 | WPCS_GIT_TREE=develop 3 | ASSETS_DIR=wp-assets 4 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | dev-lib/.editorconfig -------------------------------------------------------------------------------- /.eslintignore: -------------------------------------------------------------------------------- 1 | dev-lib/.eslintignore -------------------------------------------------------------------------------- /.eslintrc: -------------------------------------------------------------------------------- 1 | dev-lib/.eslintrc -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .idea 3 | 4 | # Grunt 5 | /build/ 6 | /node_modules/ 7 | npm-debug.log 8 | 9 | # Composer 10 | composer.lock 11 | /vendor/ 12 | 13 | # Compiled files 14 | *.min.js 15 | *.min.css 16 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "dev-lib"] 2 | path = dev-lib 3 | url = https://github.com/xwp/wp-dev-lib.git 4 | branch = master 5 | -------------------------------------------------------------------------------- /.jscsrc: -------------------------------------------------------------------------------- 1 | dev-lib/.jscsrc -------------------------------------------------------------------------------- /.jshintignore: -------------------------------------------------------------------------------- 1 | **/*.min.js 2 | **/node_modules/** 3 | **/vendor/** 4 | -------------------------------------------------------------------------------- /.jshintrc: -------------------------------------------------------------------------------- 1 | dev-lib/.jshintrc -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | dist: precise 3 | 4 | language: 5 | - php 6 | - node_js 7 | 8 | php: 9 | - 5.6 10 | - 7.1 11 | 12 | env: 13 | - WP_VERSION=trunk WP_MULTISITE=0 14 | - WP_VERSION=trunk WP_MULTISITE=1 15 | 16 | install: 17 | - nvm install 6 && nvm use 6 18 | - export DEV_LIB_PATH=dev-lib 19 | - if [ ! -e "$DEV_LIB_PATH" ] && [ -L .travis.yml ]; then export DEV_LIB_PATH=$( dirname $( readlink .travis.yml ) ); fi 20 | - if [ ! -e "$DEV_LIB_PATH" ]; then git clone https://github.com/xwp/wp-dev-lib.git $DEV_LIB_PATH; fi 21 | - source $DEV_LIB_PATH/travis.install.sh 22 | 23 | script: 24 | - source $DEV_LIB_PATH/travis.script.sh 25 | 26 | after_script: 27 | - source $DEV_LIB_PATH/travis.after_script.sh 28 | -------------------------------------------------------------------------------- /Gruntfile.js: -------------------------------------------------------------------------------- 1 | /* eslint-env node */ 2 | /* jshint node:true */ 3 | 4 | module.exports = function( grunt ) { 5 | 'use strict'; 6 | 7 | grunt.initConfig( { 8 | 9 | pkg: grunt.file.readJSON( 'package.json' ), 10 | 11 | // JavaScript linting with JSHint. 12 | jshint: { 13 | options: { 14 | jshintrc: '.jshintrc' 15 | }, 16 | all: [ 17 | 'Gruntfile.js', 18 | '*.js' 19 | ] 20 | }, 21 | 22 | // Build a deploy-able plugin 23 | copy: { 24 | build: { 25 | src: [ 26 | '*.php', 27 | 'php/*.php', 28 | 'js/*.js', 29 | 'readme.txt', 30 | '!Gruntfile.js' 31 | ], 32 | dest: 'build', 33 | expand: true, 34 | dot: true 35 | } 36 | }, 37 | 38 | // Clean up the build 39 | clean: { 40 | build: { 41 | src: [ 'build' ] 42 | } 43 | }, 44 | 45 | // VVV (Varying Vagrant Vagrants) Paths 46 | vvv: { 47 | 'plugin': '/srv/www/wordpress-develop/src/wp-content/plugins/<%= pkg.name %>', 48 | 'coverage': '/srv/www/default/coverage/<%= pkg.name %>' 49 | }, 50 | 51 | // Shell actions 52 | shell: { 53 | options: { 54 | stdout: true, 55 | stderr: true 56 | }, 57 | readme: { 58 | command: 'cd ./dev-lib && ./generate-markdown-readme' // Generate the readme.md 59 | }, 60 | phpunit: { 61 | command: 'vagrant ssh -c "cd <%= vvv.plugin %> && phpunit"' 62 | }, 63 | phpunit_c: { 64 | command: 'vagrant ssh -c "cd <%= vvv.plugin %> && phpunit --coverage-html <%= vvv.coverage %>"' 65 | } 66 | }, 67 | 68 | // Deploys a git Repo to the WordPress SVN repo 69 | wp_deploy: { 70 | deploy: { 71 | options: { 72 | plugin_slug: '<%= pkg.name %>', 73 | build_dir: 'build', 74 | assets_dir: 'wp-assets' 75 | } 76 | } 77 | } 78 | 79 | } ); 80 | 81 | // Load tasks 82 | grunt.loadNpmTasks( 'grunt-contrib-clean' ); 83 | grunt.loadNpmTasks( 'grunt-contrib-copy' ); 84 | grunt.loadNpmTasks( 'grunt-contrib-jshint' ); 85 | grunt.loadNpmTasks( 'grunt-shell' ); 86 | grunt.loadNpmTasks( 'grunt-wp-deploy' ); 87 | 88 | // Register tasks 89 | grunt.registerTask( 'default', [ 90 | 'jshint' 91 | ] ); 92 | 93 | grunt.registerTask( 'readme', [ 94 | 'shell:readme' 95 | ] ); 96 | 97 | grunt.registerTask( 'phpunit', [ 98 | 'shell:phpunit' 99 | ] ); 100 | 101 | grunt.registerTask( 'phpunit_c', [ 102 | 'shell:phpunit_c' 103 | ] ); 104 | 105 | grunt.registerTask( 'dev', [ 106 | 'default', 107 | 'readme' 108 | ] ); 109 | 110 | grunt.registerTask( 'build', [ 111 | 'default', 112 | 'readme', 113 | 'copy' 114 | ] ); 115 | 116 | grunt.registerTask( 'deploy', [ 117 | 'build', 118 | 'wp_deploy', 119 | 'clean' 120 | ] ); 121 | 122 | }; 123 | -------------------------------------------------------------------------------- /customizer-dev-tools.php: -------------------------------------------------------------------------------- 1 | 1 ) { 202 | consoleParams.push( { 203 | format: '%O', 204 | value: params.slice( 1 ) 205 | } ); 206 | } 207 | 208 | component.log({ 209 | namespace: namespaceParts, 210 | params: consoleParams 211 | }); 212 | 213 | return originalTrigger.apply( this, arguments ); 214 | }; 215 | }; 216 | 217 | /** 218 | * Wrap messenger methods. 219 | * 220 | * @param {object} args Args. 221 | * @param {wp.customize.Messenger} args.object Object. 222 | * @param {string} args.name Name. 223 | * @param {function} [args.filter] Filter. 224 | * @returns {void} 225 | */ 226 | component.wrapMessengerMethods = function wrapMessengerMethods( args ) { 227 | 228 | var originalMethods; 229 | 230 | originalMethods = { 231 | send: { 232 | method: args.object.send 233 | }, 234 | trigger: { 235 | method: args.object.trigger, 236 | name: 'receive' 237 | } 238 | }; 239 | 240 | _.each( originalMethods, function( methodParams, methodName ) { 241 | args.object[ methodName ] = function() { // eslint-disable-line complexity 242 | var params, namespaceParts, id, consoleParams = []; 243 | 244 | params = Array.prototype.slice.call( arguments ); 245 | id = params[0]; 246 | namespaceParts = [ args.name, methodParams.name || methodName ]; 247 | 248 | consoleParams.push( { 249 | format: '%o', 250 | value: id 251 | } ); 252 | if ( 2 === params.length ) { 253 | consoleParams.push( { 254 | 'format': '( %o )', 255 | 'value': params[1] 256 | } ); 257 | } else if ( params.length > 1 ) { 258 | consoleParams.push( { 259 | 'format': '( %O )', 260 | 'value': params.slice( 1 ) 261 | } ); 262 | } 263 | 264 | component.log({ 265 | namespace: namespaceParts, 266 | params: consoleParams 267 | }); 268 | 269 | return methodParams.method.apply( this, arguments ); 270 | }; 271 | } ); 272 | }; 273 | 274 | /** 275 | * Add setting change handler. 276 | * 277 | * @todo Be wary of comparing large values. Show smaller contextual diffs. 278 | * @param {wp.customize.Setting|wp.customize.Value} setting Setting. 279 | * @returns {void} 280 | */ 281 | component.addSettingChangeListener = function addSettingChangeListener( setting ) { 282 | 283 | var originalFireWith = setting.callbacks.fireWith; 284 | setting.callbacks.fireWith = function fireWith( object, args ) { 285 | var newValue = _.clone( args[0] ), oldValue = _.clone( args[1] ); 286 | 287 | if ( $.isPlainObject( newValue ) && $.isPlainObject( oldValue ) ) { 288 | _.each( _.keys( newValue ), function( key ) { 289 | if ( _.isEqual( newValue[ key ], oldValue[ key ] ) ) { 290 | delete newValue[ key ]; 291 | delete oldValue[ key ]; 292 | } 293 | } ); 294 | } 295 | 296 | component.log({ 297 | namespace: [ 'setting', 'change' ], 298 | params: [ 299 | { 300 | values: [ setting.id, JSON.stringify( oldValue ), JSON.stringify( newValue ) ], 301 | format: '%o\n- %s\n+ %s' 302 | } 303 | ] 304 | }); 305 | 306 | return originalFireWith.call( setting.callbacks, object, args ); 307 | }; 308 | }; 309 | 310 | component.initLogging(); 311 | 312 | return component; 313 | 314 | } )( wp.customize, jQuery ); 315 | -------------------------------------------------------------------------------- /js/pane.js: -------------------------------------------------------------------------------- 1 | /* global wp, console, CustomizerDevTools, JSON */ 2 | ( function( component, api ) { 3 | 'use strict'; 4 | 5 | api.bind( 'ready', function() { 6 | component.capturePreviewObjects(); 7 | component.watchState( api.state ); 8 | } ); 9 | 10 | /** 11 | * Start logging. 12 | * 13 | * @param {string|RegExp} [filterPattern] Filter pattern. 14 | * @returns {void} 15 | */ 16 | component.startLogging = function startLogging( filterPattern ) { // eslint-disable-line complexity 17 | var filter = null, serializedLoggingFilterPatterns; 18 | if ( filterPattern ) { 19 | filter = filterPattern; 20 | } 21 | 22 | if ( 0 === component.loggingFilterPatterns.length ) { 23 | console.info( 'Reload the customizer to get initial console logs. Logging in the customizer will continue in this browser session. You can stop logging via CustomizerDevTools.stopLogging()' ); 24 | } 25 | 26 | if ( -1 === _.indexOf( component.loggingFilterPatterns, filter ) ) { 27 | component.loggingFilterPatterns.push( filter ); 28 | } 29 | 30 | serializedLoggingFilterPatterns = JSON.stringify( component.loggingFilterPatterns, function replacer( key, value ) { 31 | if ( value instanceof RegExp ) { 32 | return { 33 | source: value.source, 34 | flags: value.flags 35 | }; 36 | } else { 37 | return value; 38 | } 39 | } ); 40 | 41 | localStorage.setItem( 42 | component.loggingPatternFiltersStorageKey, 43 | serializedLoggingFilterPatterns 44 | ); 45 | 46 | api.previewer.send( 'dev-tools-start-logging', serializedLoggingFilterPatterns ); 47 | }; 48 | 49 | /** 50 | * Stop logging. 51 | * 52 | * @returns {void} 53 | */ 54 | component.stopLogging = function stopLogging() { 55 | component.loggingFilterPatterns = []; 56 | api.previewer.send( 'dev-tools-stop-logging', true ); 57 | }; 58 | 59 | /** 60 | * Expose Customizer preview window and wp.customize object persistently, even as iframe window is destroyed with each refresh. 61 | * 62 | * @returns {void} 63 | */ 64 | component.capturePreviewObjects = function capturePreviewObjects() { 65 | var onWindowChange = function( win ) { 66 | try { 67 | component.previewWindow = win; 68 | component.previewCustomize = component.previewWindow.wp.customize; 69 | } catch ( error ) { 70 | console.info( 'The wp.customize object from the customizer preview cannot be exposed as CustomizerDevTools.previewCustomize in the parent frame due to a cross-domain security restriction.', error ); 71 | component.previewWindow = null; 72 | component.previewCustomize = null; 73 | api.previewer.targetWindow.unbind( onWindowChange ); 74 | } 75 | }; 76 | 77 | api.previewer.targetWindow.bind( onWindowChange ); 78 | if ( api.previewer.targetWindow.get() ) { 79 | onWindowChange( api.previewer.targetWindow.get() ); 80 | } 81 | }; 82 | 83 | /** 84 | * Add construct state change listener. 85 | * 86 | * @param {wp.customize.Panel|wp.customize.Section|wp.customize.Control} construct Construct. 87 | * @returns {void} 88 | */ 89 | component.addConstructStateChangeListener = function addConstructStateChangeListener( construct ) { // eslint-disable-line complexity 90 | var type; 91 | if ( construct.extended( api.Control ) ) { 92 | type = 'control'; 93 | } else if ( construct.extended( api.Section ) ) { 94 | type = 'section'; 95 | } else if ( construct.extended( api.Panel ) ) { 96 | type = 'panel'; 97 | } else { 98 | type = 'unknown'; 99 | } 100 | 101 | _.each( [ 'active', 'expanded' ], function( property ) { 102 | var originalFireWith, value; 103 | if ( ! construct[ property ] ) { 104 | return; 105 | } 106 | value = construct[ property ]; 107 | originalFireWith = value.callbacks.fireWith; 108 | 109 | value.callbacks.fireWith = function fireWith( object, args ) { 110 | if ( args[1] !== args[0] ) { 111 | component.log({ 112 | namespace: [ type, property ], 113 | params: [ 114 | { 115 | value: construct.id, 116 | format: '%o' 117 | }, 118 | { 119 | values: [ args[1], args[0] ], 120 | format: '(%o → %o)' 121 | } 122 | ] 123 | }); 124 | } 125 | return originalFireWith.call( value.callbacks, object, args ); 126 | }; 127 | } ); 128 | }; 129 | 130 | /** 131 | * Watch state. 132 | * 133 | * @param {wp.customize.Values} state State. 134 | * @param {function} state.each Iterator method. 135 | * @param {function} state.has Add method. 136 | * @param {function} state.add Add method. 137 | * @returns {void} 138 | */ 139 | component.watchState = function watchState( state ) { 140 | var originalAdd = state.add; 141 | 142 | // Watch existing state values. 143 | state.each( function( stateValue, id ) { 144 | component.watchStateValue( id, stateValue ); 145 | } ); 146 | 147 | /** 148 | * Wrap the state.add method since the state ID is not exposed in the add event. 149 | * 150 | * @param {string} id State ID. 151 | * @param {wp.customize.Value} stateValue Value. 152 | * @param {function} stateValue.get Getter. 153 | * @returns {void} 154 | */ 155 | state.add = function addState( id, stateValue ) { 156 | if ( ! state.has( id ) ) { 157 | 158 | component.log({ 159 | namespace: [ 'state', 'add' ], 160 | params: [ 161 | { 162 | value: id, 163 | format: '%o' 164 | }, 165 | { 166 | value: stateValue.get(), 167 | format: '%o' 168 | } 169 | ] 170 | }); 171 | component.watchStateValue( id, stateValue ); 172 | } 173 | originalAdd.call( this, id, stateValue ); 174 | }; 175 | }; 176 | 177 | /** 178 | * Watch state value. 179 | * 180 | * The fireWith method is wrapped so that the event is guaranteed to log before 181 | * any of the registered callbacks. 182 | * 183 | * @param {string} id State ID. 184 | * @param {wp.customize.Value} stateValue State value. 185 | * @param {jQuery.Callbacks} stateValue.callbacks Callbacks. 186 | * @returns {void} 187 | */ 188 | component.watchStateValue = function watchStateValue( id, stateValue ) { 189 | var originalFireWith = stateValue.callbacks.fireWith; 190 | stateValue.callbacks.fireWith = function fireWith( object, args ) { 191 | 192 | component.log({ 193 | namespace: [ 'state', 'change' ], 194 | params: [ 195 | { 196 | value: id, 197 | format: '%o' 198 | }, 199 | { 200 | values: [ args[1], args[0] ], 201 | format: '(%o → %o)' 202 | } 203 | ] 204 | }); 205 | 206 | return originalFireWith.call( stateValue.callbacks, object, args ); 207 | }; 208 | }; 209 | 210 | component.wrapValuesMethods({ 211 | object: api, 212 | name: 'setting', 213 | ignoreUntilReady: true 214 | }); 215 | component.wrapValuesMethods({ 216 | object: api.panel, 217 | name: 'panel', 218 | ignoreUntilReady: true 219 | }); 220 | component.wrapValuesMethods({ 221 | object: api.section, 222 | name: 'section', 223 | ignoreUntilReady: true 224 | }); 225 | component.wrapValuesMethods({ 226 | object: api.control, 227 | name: 'control', 228 | ignoreUntilReady: true 229 | }); 230 | 231 | component.wrapTriggerMethod({ 232 | object: api, 233 | name: 'events', 234 | filter: function( id ) { 235 | return ! ( 'add' === id || 'change' === id || 'remove' === id ); 236 | } 237 | }); 238 | 239 | component.wrapMessengerMethods({ 240 | object: api.Previewer.prototype, 241 | name: 'messenger.previewer' 242 | }); 243 | 244 | component.wrapMessengerMethods({ 245 | object: api.PreviewFrame.prototype, 246 | name: 'messenger.previewframe' 247 | }); 248 | 249 | api.bind( 'add', component.addSettingChangeListener ); 250 | api.control.bind( 'add', component.addConstructStateChangeListener ); 251 | api.section.bind( 'add', component.addConstructStateChangeListener ); 252 | api.panel.bind( 'add', component.addConstructStateChangeListener ); 253 | 254 | } )( CustomizerDevTools, wp.customize ); 255 | -------------------------------------------------------------------------------- /js/preview.js: -------------------------------------------------------------------------------- 1 | /* global wp, CustomizerDevTools, console */ 2 | ( function( component, api ) { 3 | 'use strict'; 4 | 5 | component.wrapValuesMethods({ 6 | object: api, 7 | name: 'setting', 8 | ignoreUntilReady: true 9 | }); 10 | component.wrapValuesMethods({ 11 | object: api.selectiveRefresh.partial, 12 | name: 'partial', 13 | ignoreUntilReady: true 14 | }); 15 | 16 | component.wrapTriggerMethod({ 17 | object: api, 18 | name: 'events', 19 | filter: function( id ) { 20 | return ! ( 'add' === id || 'change' === id || 'remove' === id ); 21 | } 22 | }); 23 | 24 | component.wrapMessengerMethods({ 25 | object: api.Preview.prototype, 26 | name: 'messenger.preview' 27 | }); 28 | 29 | // @todo Add inspection of selective refresh events. 30 | 31 | api.bind( 'add', component.addSettingChangeListener ); 32 | 33 | api.bind( 'preview-ready', function() { 34 | api.preview.bind( 'dev-tools-start-logging', function startLoggingPreview( serializedLoggingFilterPatterns ) { 35 | var loggingFilterPatterns; 36 | try { 37 | loggingFilterPatterns = component.parseSerializedLoggingFilterPatterns( serializedLoggingFilterPatterns ); 38 | sessionStorage.setItem( component.loggingPatternFiltersStorageKey, serializedLoggingFilterPatterns ); 39 | component.loggingFilterPatterns = loggingFilterPatterns; 40 | } catch ( err ) { 41 | console.error( err ); 42 | } 43 | } ); 44 | 45 | api.preview.bind( 'dev-tools-stop-logging', function() { 46 | sessionStorage.removeItem( component.loggingPatternFiltersStorageKey ); 47 | component.loggingFilterPatterns = []; 48 | } ); 49 | } ); 50 | 51 | } )( CustomizerDevTools, wp.customize ); 52 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "customizer-dev-tools", 3 | "version": "0.1.1", 4 | "main": "index.js", 5 | "scripts": { 6 | "test": "echo \"Error: no test specified\" && exit 1" 7 | }, 8 | "repository": { 9 | "type": "git", 10 | "url": "git+https://github.com/xwp/customizer-dev-tools.git" 11 | }, 12 | "author": "XWP", 13 | "license": "GPL-2.0", 14 | "bugs": { 15 | "url": "https://github.com/xwp/customizer-dev-tools/issues" 16 | }, 17 | "homepage": "https://github.com/xwp/customizer-dev-tools#readme", 18 | "devDependencies": { 19 | "eslint": "^3.0.1", 20 | "grunt": "~0.4.5", 21 | "grunt-contrib-clean": "~1.0.0", 22 | "grunt-contrib-copy": "~1.0.0", 23 | "grunt-contrib-jshint": "~1.0.0", 24 | "grunt-shell": "~1.3.0", 25 | "grunt-wp-deploy": "^1.1.0" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /php/class-plugin.php: -------------------------------------------------------------------------------- 1 | version = $matches[1]; 30 | } 31 | } 32 | 33 | /** 34 | * Init. 35 | */ 36 | public function init() { 37 | load_plugin_textdomain( 'customizer-dev-tools' ); 38 | 39 | add_action( 'wp_default_scripts', array( $this, 'register_scripts' ), 11 ); 40 | add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqueue_controls_scripts' ) ); 41 | add_action( 'wp_enqueue_scripts', array( $this, 'enqueue_preview_scripts' ) ); 42 | } 43 | 44 | /** 45 | * Register scripts. 46 | * 47 | * @param \WP_Scripts $wp_scripts Scripts. 48 | */ 49 | public function register_scripts( \WP_Scripts $wp_scripts ) { 50 | $base_handle = 'customizer-dev-tools-base'; 51 | $src = plugins_url( 'js/base.js', __DIR__ ); 52 | $deps = array( 'customize-base' ); 53 | $in_footer = 0; 54 | $wp_scripts->add( $base_handle, $src, $deps, $this->version, $in_footer ); 55 | $wp_scripts->registered['customize-controls']->deps[] = $base_handle; 56 | $wp_scripts->registered['customize-preview']->deps[] = $base_handle; 57 | 58 | $handle_pane = 'customizer-dev-tools-pane'; 59 | $src = plugins_url( 'js/pane.js', __DIR__ ); 60 | $deps = array( 'customize-controls' ); 61 | $in_footer = 0; 62 | $wp_scripts->add( $handle_pane, $src, $deps, $this->version, $in_footer ); 63 | 64 | $handle_preview = 'customizer-dev-tools-preview'; 65 | $src = plugins_url( 'js/preview.js', __DIR__ ); 66 | $deps = array( 'customize-preview', 'customize-selective-refresh' ); 67 | $in_footer = 0; 68 | $wp_scripts->add( $handle_preview, $src, $deps, $this->version, $in_footer ); 69 | } 70 | 71 | /** 72 | * Enqueue pane (controls) scripts. 73 | */ 74 | public function enqueue_controls_scripts() { 75 | wp_add_inline_script( 'customizer-dev-tools-base', 'CustomizerDevTools.context = "pane";', 'after' ); 76 | wp_enqueue_script( 'customizer-dev-tools-pane' ); 77 | } 78 | 79 | /** 80 | * Enqueue preview scripts. 81 | */ 82 | public function enqueue_preview_scripts() { 83 | if ( ! is_customize_preview() ) { 84 | return; 85 | } 86 | wp_add_inline_script( 'customizer-dev-tools-base', 'CustomizerDevTools.context = "preview";', 'after' ); 87 | wp_enqueue_script( 'customizer-dev-tools-preview' ); 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /phpcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 0 9 | 10 | 11 | 0 12 | 13 | 14 | 0 15 | 16 | 17 | 18 | 19 | . 20 | 21 | */node_modules/* 22 | */vendor/* 23 | */dev-lib/* 24 | 25 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | 2 | # Customizer Dev Tools 3 | 4 | Tools for facilitating JavaScript development in the customizer. 5 | 6 | **Contributors:** [xwp](https://profiles.wordpress.org/xwp), [westonruter](https://profiles.wordpress.org/westonruter) 7 | **Tags:** [customizer](https://wordpress.org/plugins/tags/customizer), [customize](https://wordpress.org/plugins/tags/customize), [dev-tools](https://wordpress.org/plugins/tags/dev-tools), [debug](https://wordpress.org/plugins/tags/debug), [debugging](https://wordpress.org/plugins/tags/debugging), [developer](https://wordpress.org/plugins/tags/developer), [development](https://wordpress.org/plugins/tags/development) 8 | **Requires at least:** 4.7 9 | **Tested up to:** 4.9 10 | **Stable tag:** 0.1.1 11 | **License:** [GPLv2 or later](http://www.gnu.org/licenses/gpl-2.0.html) 12 | 13 | [![Build Status](https://travis-ci.org/xwp/wp-customizer-dev-tools.svg?branch=master)](https://travis-ci.org/xwp/wp-customizer-dev-tools) [![Built with Grunt](https://cdn.gruntjs.com/builtwith.svg)](http://gruntjs.com) 14 | 15 | ## Description ## 16 | 17 | The customizer is a single-page application that includes a lot of events, messages, and state changes which drive the application. Being aware of these things is essential for developing JavaScript for the customizer. 18 | 19 | To use, activate the plugin, open the customizer and the browser console and enter: 20 | 21 | ```js 22 | CustomizerDevTools.startLogging() 23 | ``` 24 | 25 | You can then either start interacting with the customizer app to see the log entries from that point, or you can reload the customizer to see the log entries emitted during the customizer initialization. 26 | 27 | Not everything in the customizer is currently implemented to emit a log entry. File an issue for any specific event that may be needed. 28 | 29 | Features: 30 | 31 | * Start logging of customizer events via running `CustomizerDevTools.startLogging()` from your browser console, and stop via `CustomizerDevTools.stopLogging()`. In the former, you can filter what is logged out by passing a string or regular expression (`RegExp` object) to match against the given log, or you can use the browser console's built-in log filtering. 32 | * Logs out all events triggered on `wp.customize`. 33 | * Logs out additions and changes to to `wp.customize.state`. 34 | * Logs changes to the `active` and `expanded` states for panels, sections, and controls. 35 | * Logs out messages sent and received by the pane (controls) and preview. 36 | * Logs out dynamic addition and removal of panels, sections, controls, partials, and settings (after the `ready` event triggers). 37 | * The `wp.customize` object from the Customizer preview is made persistently available from the parent frame via `CustomizerDevTools.previewCustomize`. This reference is updated whenever the preview refreshes, so you no longer have to change the frame window context to access this object. 38 | * In the same way, the current Customizer preview `window` is exposed as `CustomizerDevTools.previewWindow`. This is a shortcut for doing `wp.customize.previewer.targetWindow.get()`, and it has the added benefit of allowing the browser's dev tools to provide auto-completion. 39 | 40 | Make sure you also install the [Customizer Browser History](https://github.com/xwp/wp-customizer-browser-history) and [Customize Snapshots](https://github.com/xwp/wp-customize-snapshots) plugins so that you can reload the browser window and have the Customizer load with the same state as before you reloaded, including the persistence of the focused panel, section, control, the previewed URL, the scroll position in the preview, and which device is being previewed. 41 | 42 | Requires PHP≥5.3. 43 | 44 | ## Screenshots ## 45 | 46 | ### Messages sent/received and events triggered during Customizer load. 47 | 48 | ![Messages sent/received and events triggered during Customizer load.](wp-assets/screenshot-1.png) 49 | 50 | ### Logging the changing of the Site Title. 51 | 52 | ![Logging the changing of the Site Title.](wp-assets/screenshot-2.png) 53 | 54 | ### Logging the change of a nav menu item from a saved sate. 55 | 56 | ![Logging the change of a nav menu item from a saved sate.](wp-assets/screenshot-3.png) 57 | 58 | ### Expanding the Site Identity section and then navigating to edit a widget. 59 | 60 | ![Expanding the Site Identity section and then navigating to edit a widget.](wp-assets/screenshot-4.png) 61 | 62 | ### Changes to `wp.customize.state` when saving the customizer changes. 63 | 64 | ![Changes to `wp.customize.state` when saving the customizer changes.](wp-assets/screenshot-5.png) 65 | 66 | ### State changes, messages, and events related to saving. 67 | 68 | ![State changes, messages, and events related to saving.](wp-assets/screenshot-6.png) 69 | 70 | ## Changelog ## 71 | 72 | ### 0.1.1 [2017-11-14] ### 73 | * Add setting change listener for settings in preview. 74 | * Tested up to 4.9. 75 | 76 | ### 0.1.0 [2016-08-18] ### 77 | Initial release 78 | 79 | 80 | -------------------------------------------------------------------------------- /readme.txt: -------------------------------------------------------------------------------- 1 | === Customizer Dev Tools === 2 | Contributors: xwp, westonruter 3 | Tags: customizer, customize, dev-tools, debug, debugging, developer, development 4 | Requires at least: 4.7 5 | Tested up to: 4.9 6 | Stable tag: 0.1.1 7 | License: GPLv2 or later 8 | License URI: http://www.gnu.org/licenses/gpl-2.0.html 9 | 10 | Tools for facilitating JavaScript development in the customizer. 11 | 12 | == Description == 13 | 14 | The customizer is a single-page application that includes a lot of events, messages, and state changes which drive the application. Being aware of these things is essential for developing JavaScript for the customizer. 15 | 16 | To use, activate the plugin, open the customizer and the browser console and enter: 17 | 18 |
19 | CustomizerDevTools.startLogging()
20 | 
21 | 22 | You can then either start interacting with the customizer app to see the log entries from that point, or you can reload the customizer to see the log entries emitted during the customizer initialization. 23 | 24 | Not everything in the customizer is currently implemented to emit a log entry. File an issue for any specific event that may be needed. 25 | 26 | Features: 27 | 28 | * Start logging of customizer events via running `CustomizerDevTools.startLogging()` from your browser console, and stop via `CustomizerDevTools.stopLogging()`. In the former, you can filter what is logged out by passing a string or regular expression (`RegExp` object) to match against the given log, or you can use the browser console's built-in log filtering. 29 | * Logs out all events triggered on `wp.customize`. 30 | * Logs out additions and changes to to `wp.customize.state`. 31 | * Logs changes to the `active` and `expanded` states for panels, sections, and controls. 32 | * Logs out messages sent and received by the pane (controls) and preview. 33 | * Logs out dynamic addition and removal of panels, sections, controls, partials, and settings (after the `ready` event triggers). 34 | * The `wp.customize` object from the Customizer preview is made persistently available from the parent frame via `CustomizerDevTools.previewCustomize`. This reference is updated whenever the preview refreshes, so you no longer have to change the frame window context to access this object. 35 | * In the same way, the current Customizer preview `window` is exposed as `CustomizerDevTools.previewWindow`. This is a shortcut for doing `wp.customize.previewer.targetWindow.get()`, and it has the added benefit of allowing the browser's dev tools to provide auto-completion. 36 | 37 | Make sure you also install the [Customizer Browser History](https://github.com/xwp/wp-customizer-browser-history) and [Customize Snapshots](https://github.com/xwp/wp-customize-snapshots) plugins so that you can reload the browser window and have the Customizer load with the same state as before you reloaded, including the persistence of the focused panel, section, control, the previewed URL, the scroll position in the preview, and which device is being previewed. 38 | 39 | Requires PHP≥5.3. 40 | 41 | == Screenshots == 42 | 43 | 1. Messages sent/received and events triggered during Customizer load. 44 | 2. Logging the changing of the Site Title. 45 | 3. Logging the change of a nav menu item from a saved sate. 46 | 4. Expanding the Site Identity section and then navigating to edit a widget. 47 | 5. Changes to `wp.customize.state` when saving the customizer changes. 48 | 6. State changes, messages, and events related to saving. 49 | 50 | == Changelog == 51 | 52 | = 0.1.1 [2017-11-14] = 53 | 54 | * Add setting change listener for settings in preview. 55 | * Tested up to 4.9. 56 | 57 | = 0.1.0 [2016-08-18] = 58 | 59 | Initial release 60 | -------------------------------------------------------------------------------- /wp-assets/screenshot-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwp/wp-customizer-dev-tools/1e9e102070c146113edce6e1f1ad6395d98fdf29/wp-assets/screenshot-1.png -------------------------------------------------------------------------------- /wp-assets/screenshot-2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwp/wp-customizer-dev-tools/1e9e102070c146113edce6e1f1ad6395d98fdf29/wp-assets/screenshot-2.png -------------------------------------------------------------------------------- /wp-assets/screenshot-3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwp/wp-customizer-dev-tools/1e9e102070c146113edce6e1f1ad6395d98fdf29/wp-assets/screenshot-3.png -------------------------------------------------------------------------------- /wp-assets/screenshot-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwp/wp-customizer-dev-tools/1e9e102070c146113edce6e1f1ad6395d98fdf29/wp-assets/screenshot-4.png -------------------------------------------------------------------------------- /wp-assets/screenshot-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwp/wp-customizer-dev-tools/1e9e102070c146113edce6e1f1ad6395d98fdf29/wp-assets/screenshot-5.png -------------------------------------------------------------------------------- /wp-assets/screenshot-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/xwp/wp-customizer-dev-tools/1e9e102070c146113edce6e1f1ad6395d98fdf29/wp-assets/screenshot-6.png --------------------------------------------------------------------------------