├── .ci-env.sh
├── .coveralls.yml
├── .david-dev
├── .gitignore
├── .gitmodules
├── .jscsrc
├── .jshintignore
├── .jshintrc
├── .travis.yml
├── Gruntfile.js
├── composer.json
├── contributing.md
├── css
└── customizer-resizer.css
├── customize-pane-resizer.php
├── images
├── grip.png
└── handle-horz.png
├── instance.php
├── js
└── customizer-resizer.js
├── package.json
├── php
├── class-exception.php
├── class-plugin-base.php
└── class-plugin.php
├── phpcs.ruleset.xml
├── phpunit.xml.dist
├── readme.md
├── readme.txt
└── tests
├── php
├── test-class-plugin-base.php
└── test-class-plugin.php
└── test-customize-pane-resizer.php
/.ci-env.sh:
--------------------------------------------------------------------------------
1 | PATH_INCLUDES='*.php *.js *.json php/* tests/* js/* css/*'
2 | PHPCS_IGNORE=vendor
3 |
--------------------------------------------------------------------------------
/.coveralls.yml:
--------------------------------------------------------------------------------
1 | service_name: travis-ci
2 | src_dir: .
3 | coverage_clover: build/logs/clover.xml
4 | json_path: build/logs/coveralls-upload.json
5 |
--------------------------------------------------------------------------------
/.david-dev:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xwp/wp-customize-pane-resizer/a5f6e62f35cd14a528bb75170a019ab9b1fdfd11/.david-dev
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
3 | # Grunt
4 | /build/
5 | /node_modules/
6 | npm-debug.log
7 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "dev-lib"]
2 | path = dev-lib
3 | url = https://github.com/xwp/wp-dev-lib.git
4 |
--------------------------------------------------------------------------------
/.jscsrc:
--------------------------------------------------------------------------------
1 | {
2 | "preset": "wordpress",
3 | "excludeFiles": [
4 | "**/*.min.js",
5 | "**/node_modules/**",
6 | "**/vendor/**"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/.jshintignore:
--------------------------------------------------------------------------------
1 | **/*.min.js
2 | **/node_modules/**
3 | **/vendor/**
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "boss": true,
3 | "curly": true,
4 | "eqeqeq": true,
5 | "eqnull": true,
6 | "es3": true,
7 | "expr": true,
8 | "immed": true,
9 | "noarg": true,
10 | "onevar": true,
11 | "quotmark": "single",
12 | "trailing": true,
13 | "undef": true,
14 | "unused": true,
15 |
16 | "browser": true,
17 |
18 | "globals": {
19 | "_": false,
20 | "Backbone": false,
21 | "jQuery": false,
22 | "wp": false
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | sudo: false
2 |
3 | language:
4 | - php
5 | - node_js
6 |
7 | php:
8 | - 5.4
9 | - 5.6
10 |
11 | node_js:
12 | - 0.10
13 |
14 | env:
15 | - WP_VERSION=latest WP_MULTISITE=0
16 | - WP_VERSION=latest WP_MULTISITE=1
17 | - WP_VERSION=trunk WP_MULTISITE=0
18 | - WP_VERSION=trunk WP_MULTISITE=1
19 |
20 | before_script:
21 | - export DEV_LIB_PATH=dev-lib
22 | - if [ ! -e "$DEV_LIB_PATH" ] && [ -L .travis.yml ]; then export DEV_LIB_PATH=$( dirname $( readlink .travis.yml ) ); fi
23 | - source $DEV_LIB_PATH/travis.before_script.sh
24 |
25 | script:
26 | - $DEV_LIB_PATH/travis.script.sh
27 |
28 | after_script:
29 | - $DEV_LIB_PATH/travis.after_script.sh
30 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | /* jshint node:true */
2 | module.exports = function( grunt ) {
3 | 'use strict';
4 |
5 | grunt.initConfig( {
6 |
7 | pkg: grunt.file.readJSON( 'package.json' ),
8 |
9 | // JavaScript linting with JSHint.
10 | jshint: {
11 | options: {
12 | jshintrc: '.jshintrc'
13 | },
14 | all: [
15 | 'Gruntfile.js',
16 | 'js/*.js'
17 | ]
18 | },
19 |
20 | // Check textdomain errors.
21 | checktextdomain: {
22 | options:{
23 | text_domain: '<%= pkg.name %>',
24 | keywords: [
25 | '__:1,2d',
26 | '_e:1,2d',
27 | '_x:1,2c,3d',
28 | 'esc_html__:1,2d',
29 | 'esc_html_e:1,2d',
30 | 'esc_html_x:1,2c,3d',
31 | 'esc_attr__:1,2d',
32 | 'esc_attr_e:1,2d',
33 | 'esc_attr_x:1,2c,3d',
34 | '_ex:1,2c,3d',
35 | '_n:1,2,4d',
36 | '_nx:1,2,4c,5d',
37 | '_n_noop:1,2,3d',
38 | '_nx_noop:1,2,3c,4d'
39 | ]
40 | },
41 | files: {
42 | src: [
43 | '**/*.php', // Include all files
44 | '!build/**', // Exclude build/
45 | '!node_modules/**', // Exclude node_modules/
46 | '!tests/**' // Exclude tests/
47 | ],
48 | expand: true
49 | }
50 | },
51 |
52 | // Build a deploy-able plugin
53 | copy: {
54 | build: {
55 | src: [
56 | '**',
57 | '!.*',
58 | '!.*/**',
59 | '!.DS_Store',
60 | '!build/**',
61 | '!composer.json',
62 | '!contributing.md',
63 | '!dev-lib/**',
64 | '!Gruntfile.js',
65 | '!node_modules/**',
66 | '!npm-debug.log',
67 | '!package.json',
68 | '!phpcs.ruleset.xml',
69 | '!phpunit.xml.dist',
70 | '!readme.md',
71 | '!tests/**'
72 | ],
73 | dest: 'build',
74 | expand: true,
75 | dot: true
76 | }
77 | },
78 |
79 | // Clean up the build
80 | clean: {
81 | build: {
82 | src: [ 'build' ]
83 | }
84 | },
85 |
86 | // VVV (Varying Vagrant Vagrants) Paths
87 | vvv: {
88 | 'plugin': '/srv/www/wordpress-develop/src/wp-content/plugins/<%= pkg.name %>',
89 | 'coverage': '/srv/www/default/coverage/<%= pkg.name %>'
90 | },
91 |
92 | // Shell actions
93 | shell: {
94 | options: {
95 | stdout: true,
96 | stderr: true
97 | },
98 | readme: {
99 | command: 'cd ./dev-lib && ./generate-markdown-readme' // Genrate the readme.md
100 | },
101 | phpunit: {
102 | command: 'vagrant ssh -c "cd <%= vvv.plugin %> && phpunit"'
103 | },
104 | phpunit_c: {
105 | command: 'vagrant ssh -c "cd <%= vvv.plugin %> && phpunit --coverage-html <%= vvv.coverage %>"'
106 | }
107 | },
108 |
109 | // Deploys a git Repo to the WordPress SVN repo
110 | wp_deploy: {
111 | deploy: {
112 | options: {
113 | plugin_slug: '<%= pkg.name %>',
114 | build_dir: 'build'
115 | }
116 | }
117 | },
118 |
119 | watch: {
120 | scripts: {
121 | files: ['js/*.js', '!js/*.min.js'],
122 | tasks: ['js'],
123 | options: {
124 | debounceDelay: 500
125 | }
126 | }
127 | }
128 |
129 | } );
130 |
131 | // Load tasks
132 | grunt.loadNpmTasks( 'grunt-contrib-watch' );
133 | grunt.loadNpmTasks( 'grunt-checktextdomain' );
134 | grunt.loadNpmTasks( 'grunt-contrib-clean' );
135 | grunt.loadNpmTasks( 'grunt-contrib-copy' );
136 | grunt.loadNpmTasks( 'grunt-contrib-jshint' );
137 | grunt.loadNpmTasks( 'grunt-shell' );
138 | grunt.loadNpmTasks( 'grunt-wp-deploy' );
139 |
140 | // Register tasks
141 | grunt.registerTask( 'js', [
142 | 'jshint'
143 | ] );
144 |
145 | grunt.registerTask( 'default', [
146 | 'js',
147 | 'checktextdomain'
148 | ] );
149 |
150 | grunt.registerTask( 'readme', [
151 | 'shell:readme'
152 | ] );
153 |
154 | grunt.registerTask( 'phpunit', [
155 | 'shell:phpunit'
156 | ] );
157 |
158 | grunt.registerTask( 'phpunit_c', [
159 | 'shell:phpunit_c'
160 | ] );
161 |
162 | grunt.registerTask( 'dev', [
163 | 'default',
164 | 'readme'
165 | ] );
166 |
167 | grunt.registerTask( 'build', [
168 | 'default',
169 | 'readme',
170 | 'copy'
171 | ] );
172 |
173 | grunt.registerTask( 'deploy', [
174 | 'build',
175 | 'wp_deploy',
176 | 'clean'
177 | ] );
178 |
179 | };
180 |
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "require-dev": {
3 | "satooshi/php-coveralls": "dev-master"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/contributing.md:
--------------------------------------------------------------------------------
1 | # Customize Pane Resizer Contributing Guide
2 |
3 | Before submitting your contribution, please make sure to take a moment and read through the following guidelines.
4 |
5 | ## Issue Reporting Guidelines
6 |
7 | - The issue list of this repo is **exclusively** for bug reports and feature requests.
8 | - Try to search for your issue, it may have already been answered or even fixed in the `wip` (Work in Progress) branch.
9 | - Check if the issue is reproducible with the latest stable version. If you are using a pre-release, please indicate the specific version you are using.
10 | - It is **required** that you clearly describe the steps necessary to reproduce the issue you are running into. Issues without clear reproducible steps will be closed immediately.
11 | - If your issue is resolved but still open, don't hesitate to close it. In case you found a solution by yourself, it could be helpful to explain how you fixed it.
12 |
13 | ## Pull Request Guidelines
14 |
15 | - Checkout a topic branch from `wip` and merge back against `wip`.
16 | - If you are not familiar with branching please read [_A successful Git branching model_](http://nvie.com/posts/a-successful-git-branching-model/) before you go any further.
17 | - **DO NOT** check-in the `build` directory with your commits.
18 | - Follow the [WordPress Coding Standards](https://make.wordpress.org/core/handbook/coding-standards/).
19 | - Make sure the default grunt task passes. (see [development setup](#development-setup))
20 | - If adding a new feature:
21 | - Add accompanying test case.
22 | - Provide convincing reason to add this feature. Ideally you should open a suggestion issue first and have it green-lit before working on it.
23 | - If fixing a bug:
24 | - Provide detailed description of the bug in the PR. Live demo preferred.
25 | - Add appropriate test coverage if applicable.
26 |
27 | ## Development Setup
28 |
29 | You will need [Node.js](http://nodejs.org), [Grunt](http://gruntjs.com), & [PHPUnit](https://phpunit.de/getting-started.html) installed on your system. To run the unit tests you must be developing within the WordPress Core. The simplest method to get a testing environment up is by using [Varying Vagrant Vagrants](https://github.com/Varying-Vagrant-Vagrants/VVV). However, if you are using MAMP then the following command will clone `trunk`.
30 |
31 | To clone the WordPress Core
32 |
33 | ``` bash
34 | $ git clone https://github.com/xwp/wordpress-develop.git
35 | ```
36 |
37 | To clone this repository
38 | ``` bash
39 | $ git clone --recursive git@github.com:xwp/wp-customize-pane-resizer.git customize-pane-resizer
40 | ```
41 |
42 | To install packages
43 |
44 | ``` bash
45 | # npm install -g grunt-cli
46 | $ npm install
47 | ```
48 |
49 | To lint:
50 |
51 | ``` bash
52 | $ grunt jshint
53 | ```
54 |
55 | To check the text domain:
56 |
57 | ``` bash
58 | $ grunt checktextdomain
59 | ```
60 |
61 | To create a pot file:
62 |
63 | ``` bash
64 | $ grunt makepot
65 | ```
66 |
67 | The default task (simply running `grunt`) will do the following: `jshint -> checktextdomain`.
68 |
69 | ### PHPUnit Testing
70 |
71 | Run tests:
72 |
73 | ``` bash
74 | $ phpunit
75 | ```
76 |
77 | Run tests with an HTML coverage report:
78 |
79 | ``` bash
80 | $ phpunit --coverage-html /tmp/report
81 | ```
82 |
83 | Travis CI will run the unit tests and perform sniffs against the WordPress Coding Standards whenever you push changes to your PR. Tests are required to pass successfully for a merge to be considered.
--------------------------------------------------------------------------------
/css/customizer-resizer.css:
--------------------------------------------------------------------------------
1 | .customizer-resizer {
2 | display: none;
3 | }
4 |
5 | /* TODO: UI experts to style adequately. */
6 | .resizable .customizer-resizer {
7 | display: block;
8 | height: 100%;
9 | width: 17px;
10 | position: absolute;
11 | right: -11px;
12 | cursor: pointer;
13 | cursor: col-resize;
14 | z-index: 9999999;
15 | background: none;
16 | }
17 |
18 | .resizable.fullwidth-customizer .customizer-resizer {
19 | width: 27px;
20 | right: -27px;
21 | }
22 |
23 | .resizable .customizer-resizer:before,
24 | .fullwidth-customizer.resizable .wp-full-overlay .customizer-resizer:after,
25 | .resizable .wp-full-overlay.collapsed .customizer-resizer:after {
26 | content: '';
27 | display: block;
28 | position: absolute;
29 | height: 100%;
30 | width: 6px;
31 | left: 6px;
32 | background: url(../images/grip.png) no-repeat center center;
33 |
34 | -webkit-transition-property: width;
35 | transition-property: width;
36 | -webkit-transition-duration: 0.4s;
37 | transition-duration: 0.4s;
38 | }
39 |
40 | .fullwidth-customizer.resizable .wp-full-overlay .customizer-resizer:before,
41 | .resizable .wp-full-overlay.collapsed .customizer-resizer:before,
42 | .fullwidth-customizer.resizable .wp-full-overlay .customizer-resizer:after,
43 | .resizable .wp-full-overlay.collapsed .customizer-resizer:after {
44 | background-color: #ddd; /* Match border-right for customizer */
45 | width: 27px;
46 | left: -1px;
47 | }
48 |
49 | .fullwidth-customizer.resizable .wp-full-overlay .customizer-resizer:before,
50 | .resizable .wp-full-overlay.collapsed .customizer-resizer:before {
51 | background: #ddd; /* Match border-right for customizer */
52 | }
53 |
54 | .fullwidth-customizer.resizable .wp-full-overlay .customizer-resizer:after,
55 | .resizable .wp-full-overlay.collapsed .customizer-resizer:after {
56 | background: url(../images/grip.png) repeat-x center center;
57 | width: 15px;
58 | left: 5px;
59 | }
60 |
61 | .fullwidth-customizer.resizable .wp-full-overlay .customizer-resizer:after {
62 | left: 6px;
63 | }
64 |
65 | .fullwidth-customizer.resizable .wp-full-overlay .customizer-resizer:before {
66 | width: 15px;
67 | left: 5px;
68 | }
69 |
70 | .resizable .wp-full-overlay-sidebar {
71 | /*
72 | Wider border to help indicate "grabbable"
73 | Example UI (Trac): http://b.ustin.co/1aSsJ
74 | */
75 | border-right-width: 7px;
76 | }
77 |
78 | .resizable.fullwidth-customizer .wp-full-overlay,
79 | .resizable.fullwidth-customizer .wp-full-overlay-sidebar {
80 | border-right-width: 27px;
81 | }
82 |
83 | .resizable .wp-full-overlay.collapsed .wp-full-overlay-sidebar {
84 | margin-left: -286px;
85 | }
86 |
87 | .resizable.wp-core-ui .wp-full-overlay.collapsed .collapse-sidebar {
88 | /* Bump button in so it's hovering over our newly widened border */
89 | left: 1px;
90 | z-index: 99999999;
91 | }
92 |
93 | .no-animation.wp-full-overlay,
94 | .no-animation.wp-full-overlay-sidebar,
95 | .no-animation.wp-full-overlay .collapse-sidebar,
96 | .no-animation.wp-full-overlay-main {
97 | /* No animation to slow down dragging responsiveness */
98 | -webkit-transition: none;
99 | transition: none;
100 | }
101 |
102 | /*
103 | Mimic @media screen and ( max-width: 640px ) styles when body has .fullwidth-customizer
104 | */
105 |
106 | .fullwidth-customizer #customize-controls {
107 | width: 100%;
108 | }
109 |
110 | .fullwidth-customizer .wp-full-overlay.expanded {
111 | margin-left: 0;
112 | }
113 |
114 | .fullwidth-customizer .wp-full-overlay-sidebar .wp-full-overlay-sidebar-content {
115 | bottom: 0;
116 | }
117 |
118 | .fullwidth-customizer .customize-controls-preview-toggle {
119 | display: block;
120 | position: absolute;
121 | top: 0;
122 | left: 48px;
123 | line-height: 45px;
124 | font-size: 14px;
125 | padding: 0 12px 0 12px;
126 | margin: 0;
127 | height: 45px;
128 | background: #eee;
129 | border-right: 1px solid #ddd;
130 | color: #444;
131 | cursor: pointer;
132 | -webkit-transition: color .1s ease-in-out, background .1s ease-in-out;
133 | transition: color .1s ease-in-out, background .1s ease-in-out;
134 | }
135 |
136 | .fullwidth-customizer #customize-footer-actions,
137 | .fullwidth-customizer #customize-preview,
138 | .fullwidth-customizer .customize-controls-preview-toggle .controls,
139 | .fullwidth-customizer .preview-only .wp-full-overlay-sidebar-content,
140 | .fullwidth-customizer .preview-only .customize-controls-preview-toggle .preview {
141 | display: none;
142 | }
143 |
144 | .fullwidth-customizer .customize-controls-preview-toggle .preview:before,
145 | .fullwidth-customizer .customize-controls-preview-toggle .controls:before {
146 | font: normal 20px/1 dashicons;
147 | content: "\f177";
148 | position: relative;
149 | top: 4px;
150 | margin-right: 6px;
151 | }
152 |
153 | .fullwidth-customizer .customize-controls-preview-toggle .controls:before {
154 | content: "\f540";
155 | }
156 |
157 | .fullwidth-customizer .preview-only #customize-controls {
158 | height: 45px;
159 | }
160 |
161 | .fullwidth-customizer .preview-only #customize-preview,
162 | .fullwidth-customizer .preview-only .customize-controls-preview-toggle .controls {
163 | display: block;
164 | }
165 |
166 | .fullwidth-customizer #customize-preview {
167 | top: 45px;
168 | bottom: 0;
169 | height: auto;
170 | }
171 |
172 | .fullwidth-customizer.wp-core-ui.wp-customizer .button {
173 | padding: 6px 14px;
174 | line-height: normal;
175 | font-size: 14px;
176 | vertical-align: middle;
177 | height: auto;
178 | margin-bottom: 4px;
179 | }
180 |
181 | .fullwidth-customizer #customize-header-actions .button-primary {
182 | margin-top: 6px;
183 | }
184 |
185 | #customize-controls .customize-control-site_icon img {
186 | display: block;
187 | max-width: 256px;
188 | margin: 0 auto;
189 | }
190 |
--------------------------------------------------------------------------------
/customize-pane-resizer.php:
--------------------------------------------------------------------------------
1 | =' ) ) {
34 | require_once __DIR__ . '/instance.php';
35 | } else {
36 | if ( defined( 'WP_CLI' ) ) {
37 | WP_CLI::warning( _customize_pane_resizer_php_version_text() );
38 | } else {
39 | add_action( 'admin_notices', '_customize_pane_resizer_php_version_error' );
40 | }
41 | }
42 |
43 | /**
44 | * Admin notice for incompatible versions of PHP.
45 | */
46 | function _customize_pane_resizer_php_version_error() {
47 | printf( '
', esc_html( _customize_pane_resizer_php_version_text() ) );
48 | }
49 |
50 | /**
51 | * String describing the minimum PHP version.
52 | *
53 | * @return string
54 | */
55 | function _customize_pane_resizer_php_version_text() {
56 | return __( 'Customize Pane Resizer plugin error: Your version of PHP is too old to run this plugin. You must be running PHP 5.3 or higher.', 'customize-pane-resizer' );
57 | }
58 |
--------------------------------------------------------------------------------
/images/grip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xwp/wp-customize-pane-resizer/a5f6e62f35cd14a528bb75170a019ab9b1fdfd11/images/grip.png
--------------------------------------------------------------------------------
/images/handle-horz.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/xwp/wp-customize-pane-resizer/a5f6e62f35cd14a528bb75170a019ab9b1fdfd11/images/handle-horz.png
--------------------------------------------------------------------------------
/instance.php:
--------------------------------------------------------------------------------
1 | ' );
54 |
55 | // Cache default customizer width
56 | width = app.$.customizer.outerWidth();
57 |
58 | // Cache selector.
59 | app.$.resizer = $( '.customizer-resizer' );
60 |
61 | // Checks window width to determine if customizer is resizable
62 | app.checkWindowWidth();
63 |
64 | // Disable customizer sizing animation.
65 | app.$.customizer.addClass( 'no-animation' );
66 | app.snapToStored();
67 |
68 | // Re-enable customizer sizing animation.
69 | app.$.customizer.removeClass( 'no-animation' );
70 |
71 | app.asap.done = true;
72 | };
73 |
74 | app.init = function() {
75 | // If elements were not available previously, re-initiate the caching/sizing.
76 | if ( ! app.cache.cached ) {
77 | app.asap();
78 | }
79 |
80 | // We need the iframe to bubble up mouse events.
81 | app.initIframeMouseEvents();
82 |
83 | app.$.resizer.on( 'mousedown', app.resizerEngage );
84 | $( '[aria-expanded]' ).on( 'click', app.toggleExpansion );
85 |
86 | $( window ).resize( _.debounce( app.checkWindowWidth, 50 ) );
87 | };
88 |
89 | app.toggleExpansion = function() {
90 | var isExpanded = 'true' === $( this ).attr( 'aria-expanded' );
91 | if ( isExpanded ) {
92 | app.snapToStored();
93 | } else {
94 | app.snapToDefault( true );
95 | }
96 | };
97 |
98 | app.checkWindowWidth = function() {
99 | var winWidth = app.$.window.width();
100 |
101 | // The breakpoint where mobile view is triggered.
102 | if ( winWidth < 640 ) {
103 | expanded = false;
104 | app.$.body.removeClass( 'resizable' );
105 | return app.snapToDefault();
106 | }
107 |
108 | if ( ! expanded ) {
109 | app.$.body.addClass( 'resizable' );
110 | expanded = true;
111 | }
112 | };
113 |
114 | app.resizerEngage = function( evt ) {
115 | if ( 1 !== evt.which ) {
116 | return;
117 | }
118 |
119 | var winWidth = app.$.window.width();
120 | var iframeWidth = winWidth - app.$.customizer.width();
121 |
122 | evt.preventDefault();
123 |
124 | if ( iframeWidth < 100 ) {
125 |
126 | // Decrease customizer width below threshold where it snaps to full-width.
127 | app.sizeCustomizer( winWidth - 320 );
128 | app.fullWidth( 'no' );
129 | } else {
130 |
131 | // Disable customizer sizing animation.
132 | app.$.customizer.addClass( 'no-animation' );
133 |
134 | // Add event listeners for the dragging duration.
135 | $( document ).on( 'mousemove', app.resizerMovement );
136 | $( document ).on( 'mouseup', app.resizerDisengage );
137 | }
138 | };
139 |
140 | app.resizerMovement = function( evt ) {
141 |
142 | // Check if the customizer is expanding (vs shrinking).
143 | var expanding = mouseLeft < evt.pageX;
144 |
145 | // Re-cache mouseLeft.
146 | mouseLeft = evt.pageX;
147 |
148 | var iframeWidth = app.$.window.width() - mouseLeft;
149 |
150 | // If iframe width is less than a workable width, snap full-screen.
151 | if ( iframeWidth < 290 && iframeWidth > 100 ) {
152 | app.snapToDefault();
153 | app.resizerDisengage();
154 |
155 | return app.fullWidth( 'yes' );
156 | }
157 |
158 | app.fullWidth( 'no' );
159 |
160 | // If we're expanding larger than default, increae the width.
161 | if ( mouseLeft >= ( width + 20 ) || mouseLeft >= width && expanding ) {
162 |
163 | // Offset by 3 pixels to put the cursor in the middle of the resizer bar.
164 | return app.sizeCustomizer( mouseLeft - 3 );
165 | }
166 |
167 | // If we're condensing, and close to our default, snap to it.
168 | if ( ! expanding && mouseLeft > ( width - 30 ) && mouseLeft < ( width + 20 ) ) {
169 | return app.snapToDefault();
170 | }
171 |
172 | // If we're condensing past our default, just trigger the collapse.
173 | if ( mouseLeft < ( width - 30 ) ) {
174 | app.snapToDefault();
175 | app.resizerDisengage();
176 | app.$.collapser.trigger( 'click' );
177 | }
178 | };
179 |
180 | app.resizerDisengage = function() {
181 |
182 | // Remove temp. event listeners.
183 | $( document ).off( 'mousemove', app.resizerMovement );
184 | $( document ).off( 'mouseup', app.resizerDisengage );
185 |
186 | // Re-enable customizer sizing animation.
187 | app.$.customizer.removeClass( 'no-animation' );
188 | };
189 |
190 | app.fullWidth = function( makeFull ) {
191 | var method = 'yes' === makeFull ? 'addClass' : 'removeClass';
192 | app.$.body[ method ]( 'fullwidth-customizer' );
193 | };
194 |
195 | app.snapToStored = function() {
196 |
197 | // If we have sessionStorage and a stored size, load that.
198 | if ( app.getStorage() ) {
199 | app.sizeCustomizer( app.size, true );
200 | }
201 | };
202 |
203 | app.snapToDefault = function( bypassStore ) {
204 | app.sizeCustomizer( '', bypassStore );
205 | };
206 |
207 | app.sizeCustomizer = function( size, bypassStore ) {
208 | bypassStore = bypassStore || false;
209 | app.size = size ? parseInt( size, 10 ) : '';
210 |
211 | // Overlay margin needs to be nudged (give more space).
212 | app.$.overlay.css({ 'margin-left' : app.size });
213 |
214 | // And of course, resize the customizer.
215 | app.$.customizer.width( app.size );
216 |
217 | // Unless told not to:
218 | if ( ! bypassStore ) {
219 |
220 | // Store the new value (if we have sessionStorage).
221 | app.setStorage();
222 | }
223 | };
224 |
225 | app.initIframeMouseEvents = function() {
226 |
227 | // Will recursively check for existence of iframe.
228 | setTimeout( function() {
229 | var $iframe = app.$.overlay.find( 'iframe' );
230 |
231 | if ( $iframe.length ) {
232 |
233 | // Init iframe event bubbling.
234 | app.bubbleMouseEvent( $iframe[0], 'mousemove' );
235 | app.bubbleMouseEvent( $iframe[0], 'mouseup' );
236 | } else {
237 | app.initIframeMouseEvents();
238 | }
239 | }, 100 );
240 | };
241 |
242 | app.bubbleMouseEvent = function( iframe, evtName ) {
243 | var longName = 'on' + evtName;
244 |
245 | // Save any previous handler.
246 | var existingMouseEvt = iframe.contentWindow[ longName ];
247 |
248 | // Attach a new listener.
249 | iframe.contentWindow[ longName ] = function( evt ) {
250 |
251 | // Fire existing listener.
252 | if ( existingMouseEvt ) {
253 | existingMouseEvt( evt );
254 | }
255 |
256 | // Create a new event for the this window.
257 | var newEvt = document.createEvent( 'MouseEvents' );
258 |
259 | // We'll need this to offset the mouse appropriately.
260 | var boundingClientRect = iframe.getBoundingClientRect();
261 |
262 | // Initialize the event, copying exiting event values (for the most part).
263 | newEvt.initMouseEvent(
264 | evtName,
265 | true, // bubbles
266 | false, // not cancelable
267 | window,
268 | evt.detail,
269 | evt.screenX,
270 | evt.screenY,
271 | evt.clientX + boundingClientRect.left,
272 | evt.clientY + boundingClientRect.top,
273 | evt.ctrlKey,
274 | evt.altKey,
275 | evt.shiftKey,
276 | evt.metaKey,
277 | evt.button,
278 | null // no related element
279 | );
280 |
281 | // Dispatch the event on the iframe element.
282 | iframe.dispatchEvent( newEvt );
283 | };
284 | };
285 |
286 | /**
287 | * Check if the browser supports sessionStorage and it's not disabled.
288 | *
289 | * Storage methods stolen from wp-includes/js/autosave.js.
290 | * We should consider globally accessible sessionStorage methods.
291 | *
292 | * @return {bool} Whether browser supports sessionStorage.
293 | */
294 | app.checkStorage = function() {
295 | if ( null !== hasStorage ) {
296 | return hasStorage;
297 | }
298 |
299 | var test = Math.random().toString();
300 | hasStorage = false;
301 |
302 | try {
303 | window.sessionStorage.setItem( 'wp-test', test );
304 | hasStorage = window.sessionStorage.getItem( 'wp-test' ) === test;
305 | window.sessionStorage.removeItem( 'wp-test' );
306 | } catch(e) {}
307 |
308 | return hasStorage;
309 | };
310 |
311 | /**
312 | * Initialize the local storage
313 | *
314 | * @return mixed False if no sessionStorage in the browser or an Object containing all postData for this blog
315 | */
316 | app.getStorage = function() {
317 |
318 | // Separate local storage containers for each blog_id
319 | if ( app.checkStorage() ) {
320 | app.size = sessionStorage.getItem( 'wp-customizer-width' );
321 | }
322 |
323 | return app.size;
324 | };
325 |
326 | /**
327 | * Set the storage for this blog
328 | *
329 | * Confirms that the data was saved successfully.
330 | *
331 | * @return bool
332 | */
333 | app.setStorage = function() {
334 | if ( app.checkStorage() && app.size && 'NaN' !== app.size ) {
335 | sessionStorage.setItem( 'wp-customizer-width', app.size );
336 | return sessionStorage.getItem( 'wp-customizer-width' ) !== null;
337 | }
338 |
339 | return false;
340 | };
341 |
342 | // Run items which will benefit from asap loading (width of customizer from storage).
343 | app.asap();
344 |
345 | // Bind to customizer ready for the rest.
346 | wp.customize.bind( 'ready', app.init );
347 |
348 | } )( window, document, jQuery, window.wp );
349 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "customize-pane-resizer",
3 | "title": "Customize Pane Resizer",
4 | "homepage": "https://github.com/xwp/wp-customize-pane-resizer",
5 | "repository": {
6 | "type": "git",
7 | "url": "https://github.com/xwp/wp-customize-pane-resizer.git"
8 | },
9 | "author": "WordPress.org",
10 | "license": "GPL-2.0+",
11 | "devDependencies": {
12 | "grunt": "~0.4.5",
13 | "grunt-checktextdomain": "~1.0.0",
14 | "grunt-contrib-clean": "^0.7.0",
15 | "grunt-contrib-copy": "~0.8.2",
16 | "grunt-contrib-cssmin": "^0.14.0",
17 | "grunt-contrib-jshint": "~0.11.3",
18 | "grunt-contrib-uglify": "^0.11.0",
19 | "grunt-contrib-watch": "^0.6.1",
20 | "grunt-shell": "~1.1.2",
21 | "grunt-wp-deploy": "^1.1.0"
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/php/class-exception.php:
--------------------------------------------------------------------------------
1 | locate_plugin();
55 | $this->slug = $location['dir_basename'];
56 | $this->dir_path = $location['dir_path'];
57 | $this->dir_url = $location['dir_url'];
58 | spl_autoload_register( array( $this, 'autoload' ) );
59 | }
60 |
61 | /**
62 | * Get reflection object for this class.
63 | *
64 | * @return \ReflectionObject
65 | */
66 | public function get_object_reflection() {
67 | static $reflection;
68 | if ( empty( $reflection ) ) {
69 | $reflection = new \ReflectionObject( $this );
70 | }
71 | return $reflection;
72 | }
73 |
74 | /**
75 | * Autoload matches cache.
76 | *
77 | * @var array
78 | */
79 | protected $autoload_matches_cache = array();
80 |
81 | /**
82 | * Autoload for classes that are in the same namespace as $this.
83 | *
84 | * @param string $class Class name.
85 | * @return void
86 | */
87 | public function autoload( $class ) {
88 | if ( ! isset( $this->autoload_matches_cache[ $class ] ) ) {
89 | if ( ! preg_match( '/^(?P.+)\\\\(?P[^\\\\]+)$/', $class, $matches ) ) {
90 | $matches = false;
91 | }
92 | $this->autoload_matches_cache[ $class ] = $matches;
93 | } else {
94 | $matches = $this->autoload_matches_cache[ $class ];
95 | }
96 | if ( empty( $matches ) ) {
97 | return;
98 | }
99 | if ( $this->get_object_reflection()->getNamespaceName() !== $matches['namespace'] ) {
100 | return;
101 | }
102 | $class_name = $matches['class'];
103 |
104 | $class_path = \trailingslashit( $this->dir_path );
105 | if ( $this->autoload_class_dir ) {
106 | $class_path .= \trailingslashit( $this->autoload_class_dir );
107 | }
108 | $class_path .= sprintf( 'class-%s.php', strtolower( str_replace( '_', '-', $class_name ) ) );
109 | if ( is_readable( $class_path ) ) {
110 | require_once $class_path;
111 | }
112 | }
113 |
114 | /**
115 | * Version of plugin_dir_url() which works for plugins installed in the plugins directory,
116 | * and for plugins bundled with themes.
117 | *
118 | * @throws \Exception If the plugin is not located in the expected location.
119 | * @return array
120 | */
121 | public function locate_plugin() {
122 | $file_name = $this->get_object_reflection()->getFileName();
123 | if ( '/' !== \DIRECTORY_SEPARATOR ) {
124 | $file_name = str_replace( \DIRECTORY_SEPARATOR, '/', $file_name ); // Windows compat.
125 | }
126 | $plugin_dir = preg_replace( '#(.*plugins[^/]*/[^/]+)(/.*)?#', '$1', $file_name, 1, $count );
127 | if ( 0 === $count ) {
128 | throw new \Exception( "Class not located within a directory tree containing 'plugins': $file_name" );
129 | }
130 |
131 | // Make sure that we can reliably get the relative path inside of the content directory.
132 | $content_dir = trailingslashit( WP_CONTENT_DIR );
133 | if ( '/' !== \DIRECTORY_SEPARATOR ) {
134 | $content_dir = str_replace( \DIRECTORY_SEPARATOR, '/', $content_dir ); // Windows compat.
135 | }
136 | if ( 0 !== strpos( $plugin_dir, $content_dir ) ) {
137 | throw new \Exception( 'Plugin dir is not inside of WP_CONTENT_DIR' );
138 | }
139 | $content_sub_path = substr( $plugin_dir, strlen( $content_dir ) );
140 | $dir_url = content_url( trailingslashit( $content_sub_path ) );
141 | $dir_path = $plugin_dir;
142 | $dir_basename = basename( $plugin_dir );
143 | return compact( 'dir_url', 'dir_path', 'dir_basename' );
144 | }
145 |
146 | /**
147 | * Return whether we're on WordPress.com VIP production.
148 | *
149 | * @return bool
150 | */
151 | public function is_wpcom_vip_prod() {
152 | return ( defined( '\WPCOM_IS_VIP_ENV' ) && \WPCOM_IS_VIP_ENV );
153 | }
154 |
155 | /**
156 | * Call trigger_error() if not on VIP production.
157 | *
158 | * @param string $message Warning message.
159 | * @param int $code Warning code.
160 | */
161 | public function trigger_warning( $message, $code = \E_USER_WARNING ) {
162 | if ( ! $this->is_wpcom_vip_prod() ) {
163 | trigger_error( esc_html( get_class( $this ) . ': ' . $message ), $code );
164 | }
165 | }
166 | }
167 |
--------------------------------------------------------------------------------
/php/class-plugin.php:
--------------------------------------------------------------------------------
1 | config = apply_filters( 'customize_pane_resizer_plugin_config', $this->config, $this );
39 |
40 | add_action( 'customize_controls_enqueue_scripts', array( $this, 'enqeue_scripts_styles' ) );
41 | }
42 |
43 | /**
44 | * Register scripts.
45 | *
46 | * @action wp_default_scripts
47 | */
48 | public function register_scripts() {
49 | }
50 |
51 | /**
52 | * Register styles.
53 | *
54 | * @action wp_default_styles
55 | */
56 | public function register_styles() {
57 | }
58 |
59 | /**
60 | * Enqueue our script to be loaded when the customizer is loaded.
61 | *
62 | * @action customize_controls_enqueue_scripts
63 | */
64 | public function enqeue_scripts_styles() {
65 | wp_register_script( 'customizer-resizer', "{$this->dir_url}js/customizer-resizer{$this->min}.js", array( 'customize-controls', 'jquery-ui-resizable' ), time(), 1 );
66 |
67 | wp_register_style( 'customizer-resizer', "{$this->dir_url}css/customizer-resizer{$this->min}.css", array( 'customize-controls' ), time() );
68 |
69 | wp_enqueue_script( 'customizer-resizer' );
70 | wp_enqueue_style( 'customizer-resizer' );
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/phpcs.ruleset.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 | Generally-applicable sniffs for WordPress plugins
4 |
5 |
6 |
7 |
8 |
9 | */dev-lib/*
10 | */node_modules/*
11 | */vendor/*
12 |
13 |
--------------------------------------------------------------------------------
/phpunit.xml.dist:
--------------------------------------------------------------------------------
1 | dev-lib/phpunit-plugin.xml
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 |
2 | # Customize Pane Resizer
3 |
4 | Feature plugin for WordPress Trac #32296: Allow the customizer be made wider
5 |
6 | **Contributors:** [jtsternberg](https://profiles.wordpress.org/jtsternberg), [westonruter](https://profiles.wordpress.org/westonruter), [wordpressdotorg](https://profiles.wordpress.org/wordpressdotorg)
7 | **Requires at least:** 4.4
8 | **Tested up to:** 4.4
9 | **Stable tag:** trunk (master)
10 | **License:** [GPLv2 or later](http://www.gnu.org/licenses/gpl-2.0.html)
11 |
12 | [](https://travis-ci.org/xwp/wp-customize-pane-resizer) [](https://coveralls.io/github/xwp/wp-customize-pane-resizer) [](http://gruntjs.com) [](https://david-dm.org/xwp/wp-customize-pane-resizer#info=devDependencies)
13 |
14 | ## Description ##
15 |
16 | Feature plugin for WordPress Trac [#32296](https://core.trac.wordpress.org/ticket/32296): Allow the customizer be made wider
17 |
18 |
--------------------------------------------------------------------------------
/readme.txt:
--------------------------------------------------------------------------------
1 | === Customize Pane Resizer ===
2 | Contributors: jtsternberg, westonruter, wordpressdotorg
3 | Requires at least: 4.4
4 | Tested up to: 4.4
5 | Stable tag: trunk
6 | License: GPLv2 or later
7 | License URI: http://www.gnu.org/licenses/gpl-2.0.html
8 |
9 | Feature plugin for WordPress Trac #32296: Allow the customizer be made wider
10 |
11 | == Description ==
12 |
13 | Feature plugin for WordPress Trac [#32296](https://core.trac.wordpress.org/ticket/32296): Allow the customizer be made wider
14 |
--------------------------------------------------------------------------------
/tests/php/test-class-plugin-base.php:
--------------------------------------------------------------------------------
1 | plugin = get_plugin_instance();
32 | }
33 |
34 | /**
35 | * Test locate_plugin.
36 | *
37 | * @see Plugin_Base::locate_plugin()
38 | */
39 | public function test_locate_plugin() {
40 | $location = $this->plugin->locate_plugin();
41 | $this->assertEquals( 'customize-pane-resizer', $location['dir_basename'] );
42 | $this->assertContains( 'plugins/customize-pane-resizer', $location['dir_path'] );
43 | $this->assertContains( 'plugins/customize-pane-resizer', $location['dir_url'] );
44 | }
45 |
46 | /**
47 | * Tests for trigger_warning().
48 | *
49 | * @see Plugin_Base::trigger_warning()
50 | */
51 | public function test_trigger_warning() {
52 | $obj = $this;
53 | set_error_handler( function ( $errno, $errstr ) use ( $obj ) {
54 | $obj->assertEquals( 'CustomizePaneResizer\Plugin: Param is 0!', $errstr );
55 | $obj->assertEquals( \E_USER_WARNING, $errno );
56 | } );
57 | $this->plugin->trigger_warning( 'Param is 0!', \E_USER_WARNING );
58 | restore_error_handler();
59 | }
60 |
61 | /**
62 | * Test is_wpcom_vip_prod().
63 | *
64 | * @see Plugin_Base::is_wpcom_vip_prod()
65 | */
66 | public function test_is_wpcom_vip_prod() {
67 | $this->assertFalse( $this->plugin->is_wpcom_vip_prod() );
68 | }
69 |
70 | /**
71 | * Test is_wpcom_vip_prod_true().
72 | *
73 | * @see Plugin_Base::is_wpcom_vip_prod()
74 | */
75 | public function test_is_wpcom_vip_prod_true() {
76 | define( 'WPCOM_IS_VIP_ENV', true );
77 | $this->assertTrue( $this->plugin->is_wpcom_vip_prod() );
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/tests/php/test-class-plugin.php:
--------------------------------------------------------------------------------
1 | assertEquals( 9, has_action( 'after_setup_theme', array( $plugin, 'init' ) ) );
25 | }
26 |
27 | /**
28 | * Test for init() method.
29 | *
30 | * @see Plugin::init()
31 | */
32 | public function test_init() {
33 | $plugin = get_plugin_instance();
34 |
35 | add_filter( 'customize_pane_resizer_plugin_config', array( $this, 'filter_config' ), 10, 2 );
36 | $plugin->init();
37 |
38 | $this->assertInternalType( 'array', $plugin->config );
39 | $this->assertArrayHasKey( 'foo', $plugin->config );
40 | $this->assertEquals( 10, has_action( 'customize_controls_enqueue_scripts', array( $plugin, 'enqeue_scripts_styles' ) ) );
41 | }
42 |
43 | /**
44 | * Filter to test 'customize_pane_resizer_plugin_config'.
45 | *
46 | * @see Plugin::init()
47 | * @param array $config Plugin config.
48 | * @param Plugin_Base $plugin Plugin instance.
49 | * @return array
50 | */
51 | public function filter_config( $config, $plugin ) {
52 | unset( $config, $plugin ); // Test should actually use these.
53 | return array( 'foo' => 'bar' );
54 | }
55 |
56 | /* Put other test functions here... */
57 | }
58 |
--------------------------------------------------------------------------------
/tests/test-customize-pane-resizer.php:
--------------------------------------------------------------------------------
1 | assertContains( '', $buffer );
27 | }
28 |
29 | /**
30 | * Test _customize_pane_resizer_php_version_text().
31 | *
32 | * @see _customize_pane_resizer_php_version_text()
33 | */
34 | public function test_customize_pane_resizer_php_version_text() {
35 | $this->assertContains( 'Customize Pane Resizer plugin error:', _customize_pane_resizer_php_version_text() );
36 | }
37 | }
38 |
--------------------------------------------------------------------------------