├── .gitignore ├── .gitmodules ├── Splitdown.php ├── css └── style.css ├── js └── splitdown.js ├── readme.md └── templates ├── editor.html ├── option-posttype.html ├── option-select-option.html └── option-showdown-extension.html /.gitignore: -------------------------------------------------------------------------------- 1 | .idea 2 | .DS_Store 3 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "js/showdown"] 2 | path = js/showdown 3 | url = https://github.com/coreyti/showdown.git 4 | [submodule "js/html2markdown"] 5 | path = js/html2markdown 6 | url = https://github.com/kates/html2markdown 7 | -------------------------------------------------------------------------------- /Splitdown.php: -------------------------------------------------------------------------------- 1 | post_type, get_option( 'splitdown_posttypes', array() ) ) ) 85 | return; 86 | 87 | $meta = get_post_meta( $post->ID, '_splitdown_markdown', TRUE ); 88 | 89 | $content = static::_load_template( 'editor.html', array( 'markdown' => $meta, 'html' => $post->post_content ) ); 90 | 91 | add_thickbox(); 92 | echo $content; 93 | } 94 | 95 | 96 | public static function save( $post_id ){ 97 | 98 | if ($_POST && isset($_POST["splitdown-markdown"])) { 99 | $html = $_POST[ 'splitdown-markdown' ]; 100 | } else { 101 | $html = ""; 102 | } 103 | 104 | if ($_POST && isset($_POST["content"])) { 105 | $markdown = $_POST[ 'content' ]; 106 | } else { 107 | $markdown = ""; 108 | } 109 | 110 | update_post_meta( $post_id, '_splitdown_markdown', $markdown ); 111 | 112 | // remove actions to avoid endless loop 113 | remove_action( 'save_post', array( __CLASS__, 'save' ) ); 114 | remove_action( 'edit_post', array( __CLASS__, 'save' ) ); 115 | 116 | // Note - this can cause Wordpress to hang on certain pages 117 | wp_update_post( array( 'ID' => $post_id, 'post_content' => $html ) ); 118 | 119 | add_action( 'save_post', array( __CLASS__, 'save' ) ); 120 | add_action( 'edit_post', array( __CLASS__, 'save' ) ); 121 | } 122 | 123 | public static function add_options(){ 124 | add_settings_section( 125 | 'splitdown_settings', 126 | 'Splitdown Settings', 127 | array( __CLASS__, 'options_section' ), 128 | 'writing' 129 | ); 130 | 131 | add_settings_field( 132 | 'splitdown_setting_post_types', 133 | 'Allowed Post types', 134 | array( __CLASS__, 'options_field_post_types' ), 135 | 'writing', 136 | 'splitdown_settings' 137 | ); 138 | 139 | add_settings_field( 140 | 'splitdown_setting_showdown_extension', 141 | 'Showdown extensions', 142 | array( __CLASS__, 'options_field_showdown_extensions' ), 143 | 'writing', 144 | 'splitdown_settings' 145 | ); 146 | 147 | register_setting( 'writing', 'splitdown_posttypes' ); 148 | register_setting( 'writing', 'splitdown_extensions' ); 149 | } 150 | 151 | 152 | public static function options_section(){ 153 | echo "Splitdown Markdown Editor Options"; 154 | } 155 | 156 | public static function options_field_post_types(){ 157 | $types = ""; 158 | $current = get_option( 'splitdown_posttypes', array() ); 159 | 160 | foreach( get_post_types() as $post_type ){ 161 | $vals = array( 162 | 'value' => $post_type, 163 | 'name' => $post_type, 164 | 'selected' => ( in_array( $post_type, $current ) ) ? 'selected' : '' 165 | ); 166 | 167 | $types .= static::_load_template( 'option-select-option.html', $vals ); 168 | $types .= "\n"; 169 | } 170 | 171 | echo static::_load_template( 'option-posttype.html', array( 'options' => $types ) ); 172 | } 173 | 174 | private static function _get_showdown_extensions(){ 175 | $path = __DIR__ . 176 | DIRECTORY_SEPARATOR . 'js' . 177 | DIRECTORY_SEPARATOR . 'showdown' . 178 | DIRECTORY_SEPARATOR . 'compressed' . 179 | DIRECTORY_SEPARATOR . 'extensions' . 180 | DIRECTORY_SEPARATOR; 181 | 182 | if( ( $data = scandir( $path ) ) === FALSE ) 183 | return apply_filters( 'splitdown_filter_showdown_extions', array() ); 184 | 185 | // Remove . and .. 186 | unset( $data[0] ); 187 | unset( $data[1] ); 188 | 189 | // fix array indices 190 | $data = array_merge( array(), $data ); 191 | 192 | return apply_filters( 'splitdown_filter_showdown_extions', $data ); 193 | } 194 | 195 | public static function options_field_showdown_extensions(){ 196 | $current = get_option( 'splitdown_extensions', array() ); 197 | $extensions = static::_get_showdown_extensions(); 198 | 199 | if( empty( $current ) ) 200 | $current = array(); 201 | 202 | $out = ""; 203 | 204 | foreach( $extensions as $extension ){ 205 | $vals = array( 206 | 'value' => $extension, 207 | 'name' => $extension, 208 | 'selected' => ( in_array( $extension, $current ) ) ? 'selected' : '' 209 | ); 210 | 211 | $out .= static::_load_template( 'option-select-option.html', $vals ); 212 | $out .= "\n"; 213 | } 214 | 215 | echo static::_load_template( 'option-showdown-extension.html', array( 'options' => $out ) ); 216 | } 217 | 218 | 219 | private static function _load_template( $template, array $values = array() ) { 220 | 221 | $path = __DIR__ . DIRECTORY_SEPARATOR . 'templates' . DIRECTORY_SEPARATOR . $template; 222 | 223 | if( file_exists( $path ) ){ 224 | $file = file_get_contents( $path ); 225 | 226 | //interpolate the template if values where provided 227 | if( count( $values ) > 0 ) { 228 | $out = array(); 229 | 230 | foreach( @$values as $key => $val ) 231 | $out[ '{:' . $key . '}' ] = $val; 232 | 233 | $file = strtr( $file, $out ); 234 | } 235 | 236 | return $file; 237 | } 238 | else 239 | throw new \Exception( "Template $template not found!" ); 240 | } 241 | 242 | } //Class End 243 | 244 | 245 | // Initilize the Plugin 246 | if ( class_exists( 'Splitdown' ) ) { 247 | 248 | if( ! version_compare( phpversion(), '5.3', '>=' ) ) 249 | exit( 'Sorry, PHP5.3+ is required to run this plugin.' ); 250 | 251 | add_action( 'plugins_loaded', array( 'Splitdown', 'get_instance' ) ); 252 | } 253 | ?> -------------------------------------------------------------------------------- /css/style.css: -------------------------------------------------------------------------------- 1 | html { height: 100%; } 2 | body { min-height: 100%; } 3 | 4 | .splitdown-editor, 5 | .splitdown-preview { 6 | background-color: #fff; 7 | border-radius: 0; 8 | -webkit-box-sizing: border-box; 9 | -moz-box-sizing: border-box; 10 | box-sizing: border-box; 11 | height: 100%; 12 | width: 100%; 13 | } 14 | 15 | .splitdown-editor { 16 | min-height: 500px; 17 | } 18 | 19 | .splitdown-preview { 20 | background: #fefefe; 21 | font-size: 13px; 22 | height: 500px; 23 | line-height: 150%; 24 | overflow-y: scroll; 25 | padding: 0 10px; 26 | } 27 | 28 | .splitdown-wrapper { 29 | margin-top: 30px; 30 | width: 100%; 31 | height: 100%; 32 | overflow: auto; 33 | margin-bottom: 20px; 34 | } 35 | 36 | .splitdown-mirror-left, 37 | .splitdown-mirror-right { 38 | background: #fff; 39 | border: 1px solid #ddd; 40 | -webkit-box-sizing: border-box; 41 | -moz-box-sizing: border-box; 42 | box-sizing: border-box; 43 | float: left; 44 | margin: 0; 45 | width: 49.5%; 46 | } 47 | 48 | .splitdown-mirror-left { 49 | min-height: 500px; 50 | height: 100%; 51 | margin: 0 1% 0 0; 52 | } 53 | 54 | .splitdown-mirror-right { 55 | min-height: 500px; 56 | } 57 | 58 | .splitdown-wrapper .wp-editor-area, 59 | .splitdown-wrapper textarea:focus { 60 | border: 0; 61 | margin: 0; 62 | -webkit-box-shadow: none; 63 | box-shadow: none; 64 | } 65 | 66 | .splitdown-mirror-toolbar { 67 | background-color: #f1f1f1; 68 | border-bottom: 1px solid #edece4; 69 | font-size: 13px; 70 | line-height: 30px; 71 | overflow: hidden; 72 | padding: 4px 10px; 73 | } 74 | 75 | .splitdown-label { 76 | color: #999; 77 | display: inline-block; 78 | line-height: 26px; 79 | vertical-align: baseline; 80 | } 81 | 82 | .splitdown-button { 83 | background-color: #c5c5c5; 84 | border-radius: 26px; 85 | color: #fff; 86 | cursor: pointer; 87 | float: right; 88 | font-weight: bold; 89 | height: 28px; 90 | margin-right: 5px; 91 | line-height: 28px; 92 | text-align: center; 93 | text-decoration: none; 94 | vertical-align: top; 95 | width: 28px; 96 | } 97 | 98 | .splitdown-button:hover, 99 | .splitdown-button:visited { 100 | color: #fff; 101 | text-decoration: none; 102 | } 103 | 104 | .splitdown-button:hover { 105 | background-color: #a5a5a5; 106 | } 107 | 108 | .wp-core-ui .button.splitdown-media-button { 109 | float: right; 110 | margin-right: 8px; 111 | } -------------------------------------------------------------------------------- /js/splitdown.js: -------------------------------------------------------------------------------- 1 | var Splitdown; 2 | 3 | Splitdown = { 4 | source: '', 5 | destination: '', 6 | converter: '', 7 | 8 | init: function () { 9 | Splitdown.source = jQuery( '#content' ); 10 | Splitdown.destination = jQuery( '#splitdown-preview' ); 11 | 12 | // Check to see if the source and destination exist 13 | if (!Splitdown.source.length || !Splitdown.destination.length) { 14 | return; 15 | } 16 | 17 | Splitdown.converter = new Showdown.converter(); 18 | 19 | this.source.on( 'keyup', this.update ); 20 | 21 | jQuery( '#splitdown-dmode').on( 'click', this.dmode ); 22 | 23 | jQuery(document).on(screenfull.raw.fullscreenchange, this.dmodeChange ); 24 | 25 | //only convert html to markdown when there is a preview but no markdown 26 | if( this.source.text().length == 0 && this.destination.html() != 0 ){ 27 | this.source.val( html2markdown( this.destination.html() ) ); 28 | } 29 | 30 | jQuery(document).ajaxSuccess( this.ajaxIntercept ); 31 | 32 | // Update at the start to generate the HTML (otherwise when we save after 33 | // no changes, the_content() will be blank!) 34 | this.update(); 35 | 36 | }, 37 | 38 | ajaxIntercept: function( event, xhr, settings){ 39 | // we need to parse the data string form the ajax settings... 40 | data = settings.data.replace( '#', ' ' ).split("&"); 41 | 42 | var n = [] 43 | jQuery.each( data, function( k, v ){ 44 | tmp = v.split( '=' ); 45 | n[ decodeURIComponent( tmp[0] )] = decodeURIComponent( tmp[1]).replace( /\+/g, ' ' ); 46 | } ); 47 | 48 | if( n[ 'action' ] == 'send-attachment-to-editor' ){ 49 | var url = n['html'].replace( /src/g, "src=\"" + n['attachment[url]'] + "\"" ); 50 | 51 | // we need this chuck of code for inserting the image at the cursor 52 | // taken form http://stackoverflow.com/questions/11076975/insert-text-into-textarea-at-cursor-position-javascript 53 | var cursorPos = Splitdown.source.prop('selectionStart'); 54 | var v = Splitdown.source.val(); 55 | var textBefore = v.substring(0, cursorPos ); 56 | var textAfter = v.substring( cursorPos, v.length ); 57 | 58 | Splitdown.source.val( textBefore+ html2markdown( url ) +textAfter ); 59 | Splitdown.update(); 60 | } 61 | }, 62 | 63 | update: function () { 64 | Splitdown.destination.html( 65 | Splitdown.converter.makeHtml( 66 | Splitdown.source.val() 67 | ) 68 | ); 69 | 70 | jQuery( '#splitdown-markdown').val( Splitdown.destination.html() ); 71 | }, 72 | 73 | dmode: function() { 74 | 75 | if (screenfull.enabled) { 76 | screenfull.toggle( jQuery( '#splitdown-wrapper' )[0] ); 77 | 78 | } 79 | }, 80 | 81 | dmodeChange: function(){ 82 | 83 | if( screenfull.isFullscreen ){ 84 | jQuery( '#splitdown-wrapper').css( 'height', document.body.offsetHeight +30 ); 85 | jQuery( '#splitdown-preview').css( 'height', '100%' ); 86 | jQuery( '#splitdown-editor').css( 'height', document.body.offsetHeight +30 ); 87 | } 88 | else { 89 | jQuery( '#splitdown-preview').css( 'height', '500px' ); 90 | jQuery( '#splitdown-wrapper').css( 'height', '100%' ); 91 | jQuery( '#splitdown-editor').css( 'height', '100%' ); 92 | } 93 | } 94 | }; 95 | 96 | jQuery( 'document' ).ready(function(){ 97 | 98 | Splitdown.init(); 99 | }); -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | #Splitdown 2 | ## A Markdown editor replacement for WordPress 3 | 4 | ### Installation 5 | 1. Clone the plugin to your plugins directory, usually located at wp-contents/plugins. 6 | 2. You need to run ```git submodule init``` and ```git submodule update``` to pull showdown.js and html2markdown. 7 | 3. In your WP Dashboard go to ```Settings``` and than ```Writing```, scroll down to the Splitdown section and select which post types you wish to use Splitdown on. 8 | 4. Enjoy 9 | 10 | ### Other Notes 11 | I implemented an experimental version of the WordPress Media Manager. At the moment it does not work in distraction free mode. 12 | Since Markdown doesn't support css classes for elements, I need to write a showdown extension to fix this, so images can be displayed properly. 13 | As always this may take some time to implement, because I work alone in my spare time on this. :) 14 | If you want to help, please feel free to fork this project and submit a pull request with your changes. 15 | 16 | ### Contributions 17 | If you find a bug or have a suggestion, please leave a ticket at https://github.com/Necrotex/Splitdown/issues. 18 | 19 | ### Support 20 | If you need support, please contact a WordPress expert. Issues are not for support questions. 21 | 22 | ### Javascript libraries used: 23 | + [showdown](https://github.com/coreyti/showdown) 24 | + [html2markdown](https://github.com/kates/html2markdown) 25 | -------------------------------------------------------------------------------- /templates/editor.html: -------------------------------------------------------------------------------- 1 | 24 |