MCE Table Buttons - Version 3.0

Version Description

  • Support for WordPress 3.9 and newer, which includes a major visual editor upgrade (TinyMCE 4)
Download this release

Release Info

Developer jakemgold
Plugin Icon 128x128 MCE Table Buttons
Version 3.0
Comparing to
See all releases

Code changes from version 2.0 to 3.0

mce_table_buttons.php CHANGED
@@ -2,48 +2,128 @@
2
  /**
3
  Plugin Name: MCE Table Buttons
4
  Plugin URI: http://10up.com/plugins-modules/wordpress-mce-table-buttons/
5
- Description: Add <strong>buttons for table editing</strong> to the WordPress WYSIWYG editor with this <strong>light weight</strong> plug-in.
6
- Version: 2.0
7
  Author: Jake Goldman, 10up, Oomph
8
  Author URI: http://10up.com
9
  License: GPLv2 or later
10
  */
11
 
12
  class MCE_Table_Buttons {
13
- public function __construct() {
14
- add_action( 'admin_init', array( $this, 'admin_init' ) );
15
- add_action( 'content_save_pre', array( $this, 'content_save_pre'), 100 );
16
- }
17
-
18
- public function admin_init() {
19
- add_filter( 'mce_external_plugins', array( $this, 'mce_external_plugins' ) );
20
- add_filter( 'mce_buttons_3', array( $this, 'mce_buttons_3' ) );
21
- add_filter( 'style_loader_tag', array( $this, 'style_loader_tag' ), 50, 2 );
22
- }
23
-
24
- public function mce_external_plugins( $plugin_array ) {
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
  $plugin_dir_url = plugin_dir_url( __FILE__ );
26
- $plugin_array['table'] = $plugin_dir_url . 'table/editor_plugin.js';
27
- $plugin_array['mcetablebuttons'] = $plugin_dir_url . 'assets/mce-table-buttons.js';
28
- return $plugin_array;
29
  }
30
-
31
- public function mce_buttons_3( $buttons ) {
 
 
 
 
 
 
32
  array_push( $buttons, 'tablecontrols' );
33
- return $buttons;
34
  }
35
 
36
- public function style_loader_tag( $tag, $handle ) {
37
- if ( $handle == 'editor-buttons' ) {
38
- remove_filter( 'style_loader_tag', array( $this, 'style_loader_tag' ), 50, 2 );
39
- wp_register_style( 'mce-table-buttons', plugin_dir_url( __FILE__ ) . 'assets/mce-table-buttons.css' );
40
- wp_print_styles( 'mce-table-buttons' );
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
41
  }
42
 
43
- return $tag;
44
  }
45
-
46
- public function content_save_pre( $content ) {
 
 
 
 
 
 
47
  if ( substr( $content, -8 ) == '</table>' )
48
  $content .= "\n<br />";
49
 
@@ -51,4 +131,4 @@ class MCE_Table_Buttons {
51
  }
52
  }
53
 
54
- $mce_table_buttons = new MCE_Table_Buttons;
2
  /**
3
  Plugin Name: MCE Table Buttons
4
  Plugin URI: http://10up.com/plugins-modules/wordpress-mce-table-buttons/
5
+ Description: Add <strong>controls for table editing</strong> to the visual content editor with this <strong>light weight</strong> plug-in.
6
+ Version: 3.0
7
  Author: Jake Goldman, 10up, Oomph
8
  Author URI: http://10up.com
9
  License: GPLv2 or later
10
  */
11
 
12
  class MCE_Table_Buttons {
13
+
14
+ /**
15
+ * Handles initializing this class and returning the singleton instance after it's been cached.
16
+ *
17
+ * @return null|MCE_Table_Buttons
18
+ */
19
+ public static function get_instance() {
20
+ // Store the instance locally to avoid private static replication
21
+ static $instance = null;
22
+
23
+ if ( null === $instance ) {
24
+ $instance = new self();
25
+ self::_add_actions();
26
+ }
27
+
28
+ return $instance;
29
+ }
30
+
31
+ /**
32
+ * An empty constructor
33
+ */
34
+ public function __construct() { /* Purposely do nothing here */ }
35
+
36
+ /**
37
+ * Handles registering hooks that initialize this plugin.
38
+ */
39
+ public static function _add_actions() {
40
+ add_filter( 'the_editor', array( __CLASS__, 'the_editor' ) ); // most convenient hook
41
+ add_action( 'content_save_pre', array( __CLASS__, 'content_save_pre'), 100 );
42
+ }
43
+
44
+ /**
45
+ * The Editor is really a filter, but happens to be our most convenient hook to set everything up
46
+ *
47
+ * @param string $editor
48
+ * @return string Editor fields
49
+ */
50
+ public static function the_editor( $editor ) {
51
+ global $tinymce_version;
52
+
53
+ if ( version_compare( $tinymce_version, '400', '<' ) ) {
54
+ add_filter( 'mce_external_plugins', array( __CLASS__, 'mce_external_plugins_3_8' ) );
55
+ add_filter( 'mce_buttons_3', array( __CLASS__, 'mce_buttons_3_8' ) );
56
+ wp_register_style( 'mce-table-buttons', plugin_dir_url( __FILE__ ) . 'tinymce3-assets/mce-table-buttons.css' );
57
+ wp_print_styles( 'mce-table-buttons' );
58
+ } else {
59
+ add_filter( 'mce_external_plugins', array( __CLASS__, 'mce_external_plugins_3_9' ) );
60
+ add_filter( 'mce_buttons_2', array( __CLASS__, 'mce_buttons_3_9' ) );
61
+ }
62
+
63
+ remove_filter( 'the_editor', array( __CLASS__, 'the_editor' ) ); // only needs to run once
64
+
65
+ return $editor;
66
+ }
67
+
68
+ /**
69
+ * Initialize TinyMCE 3.x table plugin and custom TinyMCE plugin for third editor row
70
+ *
71
+ * @param array $plugin_array Array of TinyMCE plugins
72
+ * @return array Array of TinyMCE plugins
73
+ */
74
+ public static function mce_external_plugins_3_8( $plugin_array ) {
75
  $plugin_dir_url = plugin_dir_url( __FILE__ );
76
+ $plugin_array['table'] = $plugin_dir_url . 'tinymce3-table/editor_plugin.js';
77
+ $plugin_array['mcetablebuttons'] = $plugin_dir_url . 'tinymce3-assets/mce-table-buttons.js';
78
+ return $plugin_array;
79
  }
80
+
81
+ /**
82
+ * Add TinyMCE 3.x table control buttons to a third row of editor buttons
83
+ *
84
+ * @param array $buttons Buttons for the third row
85
+ * @return array Buttons for the third row
86
+ */
87
+ public static function mce_buttons_3_8( $buttons ) {
88
  array_push( $buttons, 'tablecontrols' );
89
+ return $buttons;
90
  }
91
 
92
+ /**
93
+ * Initialize TinyMCE 4.x table plugin
94
+ *
95
+ * @param array $plugin_array Array of TinyMCE plugins
96
+ * @return array Array of TinyMCE plugins
97
+ */
98
+ public static function mce_external_plugins_3_9( $plugin_array ) {
99
+ $variant = ( defined('SCRIPT_DEBUG') && SCRIPT_DEBUG ) ? '' : '.min';
100
+ $plugin_array['table'] = plugin_dir_url( __FILE__ ) . 'tinymce4-table/plugin' . $variant . '.js';
101
+ return $plugin_array;
102
+ }
103
+
104
+ /**
105
+ * Add TinyMCE 4.x table control to the second row, after other formatting controls
106
+ *
107
+ * @param array $buttons Buttons for the second row
108
+ * @return array Buttons for the second row
109
+ */
110
+ public static function mce_buttons_3_9( $buttons ) {
111
+ // in case someone is manipulating other buttons, drop table controls at the end of the row
112
+ if ( ! $pos = array_search( 'undo', $buttons ) ) {
113
+ array_push( $buttons, 'table' );
114
+ return $buttons;
115
  }
116
 
117
+ return array_merge( array_slice( $buttons, 0, $pos ), array( 'table' ), array_slice( $buttons, $pos ) );
118
  }
119
+
120
+ /**
121
+ * Fixes weirdness resulting from wpautop and formatting clean up not built for tables
122
+ *
123
+ * @param string $content Editor content before WordPress massaging
124
+ * @return string Editor content before WordPress massaging
125
+ */
126
+ public static function content_save_pre( $content ) {
127
  if ( substr( $content, -8 ) == '</table>' )
128
  $content .= "\n<br />";
129
 
131
  }
132
  }
133
 
134
+ MCE_Table_Buttons::get_instance();
readme.txt CHANGED
@@ -3,37 +3,41 @@ Contributors: jakemgold, 10up, thinkoomph
3
  Donate link: http://10up.com/plugins-modules/wordpress-mce-table-buttons/
4
  Tags: tables, table, editor, WYSIWYG, buttons, tinymce
5
  Requires at least: 3.4
6
- Tested up to: 3.5
7
- Stable tag: 2.0
8
 
9
- Adds table editing buttons to a new, third row in the WordPress WYSIWYG editor.
10
 
11
  == Description ==
12
 
13
- Adds table editing buttons to a new, third row in the WordPress WYSIWYG editor.
14
 
15
- A light weight plug-in that restores the table editing buttons from the full version of TinyMCE, optimized for WordPress. Note that this should not be used with other plug-ins that significantly alter the editor's default behavior.
16
 
17
- The third, editor toolbar is hidden or displayed with the "kitchen sink" (just like the second toolbar row).
18
 
19
  == Installation ==
20
 
21
  1. Install easily with the WordPress plugin control panel or manually download the plugin and upload the folder
22
  `mce-table-buttons` to the `/wp-content/plugins/` directory
23
- 1. Activate the plugin through the 'Plugins' menu in WordPress
24
  1. Show the toolbar in the editor by opening the "kitchen sink" (the last button in the first row)
25
 
26
  == Screenshots ==
27
 
28
- 1. The editor with the new table editing controls.
 
29
 
30
  == Changelog ==
31
-
32
- = 2.0 =
33
- * New button icons that better conform with WordPress's editor design
34
- * Retina (HiDPI) ready button icons!
35
- * Upgraded to latest version of TinyMCE tables plugin (fixes a lot of edge case bugs)
36
- * Rewrote code for hiding / display toolbar with kitchen sink (now a TinyMCE plug-in instead of a workaround) - the table buttons no longer briefly appear before page loading is finished
 
 
 
37
 
38
  = 1.5 =
39
  * Table toolbar is hidden or displayed along with the kitchen sink (yay!)
3
  Donate link: http://10up.com/plugins-modules/wordpress-mce-table-buttons/
4
  Tags: tables, table, editor, WYSIWYG, buttons, tinymce
5
  Requires at least: 3.4
6
+ Tested up to: 3.9
7
+ Stable tag: 3.0
8
 
9
+ Adds table editing controls to the visual content editor (TinyMCE).
10
 
11
  == Description ==
12
 
13
+ Adds table editing controls to the visual content editor (TinyMCE).
14
 
15
+ A light weight plug-in that adds the table editing controls from the full version of TinyMCE, optimized for WordPress. Note that this may not work in conjunction with other plug-ins that significantly alter or replace the visual editor's default behavior.
16
 
17
+ Note that the table controls are contained in the kitchen sink toolbar, toggled with the last button on the first row of controls.
18
 
19
  == Installation ==
20
 
21
  1. Install easily with the WordPress plugin control panel or manually download the plugin and upload the folder
22
  `mce-table-buttons` to the `/wp-content/plugins/` directory
23
+ 1. Activate the plugin through the 'Plugins' menu in WordPress
24
  1. Show the toolbar in the editor by opening the "kitchen sink" (the last button in the first row)
25
 
26
  == Screenshots ==
27
 
28
+ 1. The editor with the new table editing controls in WordPress 3.9 or newer.
29
+ 1. The editor with the new table editing controls in WordPress 3.4 through 3.8.
30
 
31
  == Changelog ==
32
+
33
+ = 3.0 =
34
+ * Support for WordPress 3.9 and newer, which includes a major visual editor upgrade (TinyMCE 4)
35
+
36
+ = 2.0 =
37
+ * New button icons that better conform with WordPress's editor design
38
+ * Retina (HiDPI) ready button icons!
39
+ * Upgraded to latest version of TinyMCE tables plugin (fixes a lot of edge case bugs)
40
+ * Rewrote code for hiding / display toolbar with kitchen sink (now a TinyMCE plug-in instead of a workaround) - the table buttons no longer briefly appear before page loading is finished
41
 
42
  = 1.5 =
43
  * Table toolbar is hidden or displayed along with the kitchen sink (yay!)
screenshot-1.png CHANGED
Binary file
screenshot-2.png ADDED
Binary file
{assets → tinymce3-assets}/mce-table-buttons-2x.png RENAMED
File without changes
{assets → tinymce3-assets}/mce-table-buttons.css RENAMED
File without changes
{assets → tinymce3-assets}/mce-table-buttons.js RENAMED
File without changes
{assets → tinymce3-assets}/mce-table-buttons.png RENAMED
File without changes
{table → tinymce3-table}/cell.htm RENAMED
File without changes
{table → tinymce3-table}/css/cell.css RENAMED
File without changes
{table → tinymce3-table}/css/row.css RENAMED
File without changes
{table → tinymce3-table}/css/table.css RENAMED
File without changes
{table → tinymce3-table}/editor_plugin.js RENAMED
File without changes
{table → tinymce3-table}/editor_plugin_src.js RENAMED
File without changes
{table → tinymce3-table}/js/cell.js RENAMED
File without changes
{table → tinymce3-table}/js/merge_cells.js RENAMED
File without changes
{table → tinymce3-table}/js/row.js RENAMED
File without changes
{table → tinymce3-table}/js/table.js RENAMED
File without changes
{table → tinymce3-table}/langs/en_dlg.js RENAMED
File without changes
{table → tinymce3-table}/merge_cells.htm RENAMED
File without changes
{table → tinymce3-table}/row.htm RENAMED
File without changes
{table → tinymce3-table}/table.htm RENAMED
File without changes
tinymce4-table/plugin.js ADDED
@@ -0,0 +1,2260 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Compiled inline version. (Library mode)
3
+ */
4
+
5
+ /*jshint smarttabs:true, undef:true, latedef:true, curly:true, bitwise:true, camelcase:true */
6
+ /*globals $code */
7
+
8
+ (function(exports, undefined) {
9
+ "use strict";
10
+
11
+ var modules = {};
12
+
13
+ function require(ids, callback) {
14
+ var module, defs = [];
15
+
16
+ for (var i = 0; i < ids.length; ++i) {
17
+ module = modules[ids[i]] || resolve(ids[i]);
18
+ if (!module) {
19
+ throw 'module definition dependecy not found: ' + ids[i];
20
+ }
21
+
22
+ defs.push(module);
23
+ }
24
+
25
+ callback.apply(null, defs);
26
+ }
27
+
28
+ function define(id, dependencies, definition) {
29
+ if (typeof id !== 'string') {
30
+ throw 'invalid module definition, module id must be defined and be a string';
31
+ }
32
+
33
+ if (dependencies === undefined) {
34
+ throw 'invalid module definition, dependencies must be specified';
35
+ }
36
+
37
+ if (definition === undefined) {
38
+ throw 'invalid module definition, definition function must be specified';
39
+ }
40
+
41
+ require(dependencies, function() {
42
+ modules[id] = definition.apply(null, arguments);
43
+ });
44
+ }
45
+
46
+ function defined(id) {
47
+ return !!modules[id];
48
+ }
49
+
50
+ function resolve(id) {
51
+ var target = exports;
52
+ var fragments = id.split(/[.\/]/);
53
+
54
+ for (var fi = 0; fi < fragments.length; ++fi) {
55
+ if (!target[fragments[fi]]) {
56
+ return;
57
+ }
58
+
59
+ target = target[fragments[fi]];
60
+ }
61
+
62
+ return target;
63
+ }
64
+
65
+ function expose(ids) {
66
+ for (var i = 0; i < ids.length; i++) {
67
+ var target = exports;
68
+ var id = ids[i];
69
+ var fragments = id.split(/[.\/]/);
70
+
71
+ for (var fi = 0; fi < fragments.length - 1; ++fi) {
72
+ if (target[fragments[fi]] === undefined) {
73
+ target[fragments[fi]] = {};
74
+ }
75
+
76
+ target = target[fragments[fi]];
77
+ }
78
+
79
+ target[fragments[fragments.length - 1]] = modules[id];
80
+ }
81
+ }
82
+
83
+ // Included from: js/tinymce/plugins/table/classes/TableGrid.js
84
+
85
+ /**
86
+ * TableGrid.js
87
+ *
88
+ * Copyright, Moxiecode Systems AB
89
+ * Released under LGPL License.
90
+ *
91
+ * License: http://www.tinymce.com/license
92
+ * Contributing: http://www.tinymce.com/contributing
93
+ */
94
+
95
+ /**
96
+ * This class creates a grid out of a table element. This
97
+ * makes it a whole lot easier to handle complex tables with
98
+ * col/row spans.
99
+ *
100
+ * @class tinymce.tableplugin.TableGrid
101
+ * @private
102
+ */
103
+ define("tinymce/tableplugin/TableGrid", [
104
+ "tinymce/util/Tools",
105
+ "tinymce/Env"
106
+ ], function(Tools, Env) {
107
+ var each = Tools.each;
108
+
109
+ function getSpanVal(td, name) {
110
+ return parseInt(td.getAttribute(name) || 1, 10);
111
+ }
112
+
113
+ return function(editor, table) {
114
+ var grid, startPos, endPos, selectedCell, selection = editor.selection, dom = selection.dom;
115
+
116
+ function buildGrid() {
117
+ var startY = 0;
118
+
119
+ grid = [];
120
+
121
+ each(['thead', 'tbody', 'tfoot'], function(part) {
122
+ var rows = dom.select('> ' + part + ' tr', table);
123
+
124
+ each(rows, function(tr, y) {
125
+ y += startY;
126
+
127
+ each(dom.select('> td, > th', tr), function(td, x) {
128
+ var x2, y2, rowspan, colspan;
129
+
130
+ // Skip over existing cells produced by rowspan
131
+ if (grid[y]) {
132
+ while (grid[y][x]) {
133
+ x++;
134
+ }
135
+ }
136
+
137
+ // Get col/rowspan from cell
138
+ rowspan = getSpanVal(td, 'rowspan');
139
+ colspan = getSpanVal(td, 'colspan');
140
+
141
+ // Fill out rowspan/colspan right and down
142
+ for (y2 = y; y2 < y + rowspan; y2++) {
143
+ if (!grid[y2]) {
144
+ grid[y2] = [];
145
+ }
146
+
147
+ for (x2 = x; x2 < x + colspan; x2++) {
148
+ grid[y2][x2] = {
149
+ part: part,
150
+ real: y2 == y && x2 == x,
151
+ elm: td,
152
+ rowspan: rowspan,
153
+ colspan: colspan
154
+ };
155
+ }
156
+ }
157
+ });
158
+ });
159
+
160
+ startY += rows.length;
161
+ });
162
+ }
163
+
164
+ function cloneNode(node, children) {
165
+ node = node.cloneNode(children);
166
+ node.removeAttribute('id');
167
+
168
+ return node;
169
+ }
170
+
171
+ function getCell(x, y) {
172
+ var row;
173
+
174
+ row = grid[y];
175
+ if (row) {
176
+ return row[x];
177
+ }
178
+ }
179
+
180
+ function setSpanVal(td, name, val) {
181
+ if (td) {
182
+ val = parseInt(val, 10);
183
+
184
+ if (val === 1) {
185
+ td.removeAttribute(name, 1);
186
+ } else {
187
+ td.setAttribute(name, val, 1);
188
+ }
189
+ }
190
+ }
191
+
192
+ function isCellSelected(cell) {
193
+ return cell && (dom.hasClass(cell.elm, 'mce-item-selected') || cell == selectedCell);
194
+ }
195
+
196
+ function getSelectedRows() {
197
+ var rows = [];
198
+
199
+ each(table.rows, function(row) {
200
+ each(row.cells, function(cell) {
201
+ if (dom.hasClass(cell, 'mce-item-selected') || (selectedCell && cell == selectedCell.elm)) {
202
+ rows.push(row);
203
+ return false;
204
+ }
205
+ });
206
+ });
207
+
208
+ return rows;
209
+ }
210
+
211
+ function deleteTable() {
212
+ var rng = dom.createRng();
213
+
214
+ rng.setStartAfter(table);
215
+ rng.setEndAfter(table);
216
+
217
+ selection.setRng(rng);
218
+
219
+ dom.remove(table);
220
+ }
221
+
222
+ function cloneCell(cell) {
223
+ var formatNode, cloneFormats = {};
224
+
225
+ if (editor.settings.table_clone_elements !== false) {
226
+ cloneFormats = Tools.makeMap(
227
+ (editor.settings.table_clone_elements || 'strong em b i span font h1 h2 h3 h4 h5 h6 p div').toUpperCase(),
228
+ /[ ,]/
229
+ );
230
+ }
231
+
232
+ // Clone formats
233
+ Tools.walk(cell, function(node) {
234
+ var curNode;
235
+
236
+ if (node.nodeType == 3) {
237
+ each(dom.getParents(node.parentNode, null, cell).reverse(), function(node) {
238
+ if (!cloneFormats[node.nodeName]) {
239
+ return;
240
+ }
241
+
242
+ node = cloneNode(node, false);
243
+
244
+ if (!formatNode) {
245
+ formatNode = curNode = node;
246
+ } else if (curNode) {
247
+ curNode.appendChild(node);
248
+ }
249
+
250
+ curNode = node;
251
+ });
252
+
253
+ // Add something to the inner node
254
+ if (curNode) {
255
+ curNode.innerHTML = Env.ie ? '&nbsp;' : '<br data-mce-bogus="1" />';
256
+ }
257
+
258
+ return false;
259
+ }
260
+ }, 'childNodes');
261
+
262
+ cell = cloneNode(cell, false);
263
+ setSpanVal(cell, 'rowSpan', 1);
264
+ setSpanVal(cell, 'colSpan', 1);
265
+
266
+ if (formatNode) {
267
+ cell.appendChild(formatNode);
268
+ } else {
269
+ if (!Env.ie) {
270
+ cell.innerHTML = '<br data-mce-bogus="1" />';
271
+ }
272
+ }
273
+
274
+ return cell;
275
+ }
276
+
277
+ function cleanup() {
278
+ var rng = dom.createRng(), row;
279
+
280
+ // Empty rows
281
+ each(dom.select('tr', table), function(tr) {
282
+ if (tr.cells.length === 0) {
283
+ dom.remove(tr);
284
+ }
285
+ });
286
+
287
+ // Empty table
288
+ if (dom.select('tr', table).length === 0) {
289
+ rng.setStartBefore(table);
290
+ rng.setEndBefore(table);
291
+ selection.setRng(rng);
292
+ dom.remove(table);
293
+ return;
294
+ }
295
+
296
+ // Empty header/body/footer
297
+ each(dom.select('thead,tbody,tfoot', table), function(part) {
298
+ if (part.rows.length === 0) {
299
+ dom.remove(part);
300
+ }
301
+ });
302
+
303
+ // Restore selection to start position if it still exists
304
+ buildGrid();
305
+
306
+ // If we have a valid startPos object
307
+ if (startPos) {
308
+ // Restore the selection to the closest table position
309
+ row = grid[Math.min(grid.length - 1, startPos.y)];
310
+ if (row) {
311
+ selection.select(row[Math.min(row.length - 1, startPos.x)].elm, true);
312
+ selection.collapse(true);
313
+ }
314
+ }
315
+ }
316
+
317
+ function fillLeftDown(x, y, rows, cols) {
318
+ var tr, x2, r, c, cell;
319
+
320
+ tr = grid[y][x].elm.parentNode;
321
+ for (r = 1; r <= rows; r++) {
322
+ tr = dom.getNext(tr, 'tr');
323
+
324
+ if (tr) {
325
+ // Loop left to find real cell
326
+ for (x2 = x; x2 >= 0; x2--) {
327
+ cell = grid[y + r][x2].elm;
328
+
329
+ if (cell.parentNode == tr) {
330
+ // Append clones after
331
+ for (c = 1; c <= cols; c++) {
332
+ dom.insertAfter(cloneCell(cell), cell);
333
+ }
334
+
335
+ break;
336
+ }
337
+ }
338
+
339
+ if (x2 == -1) {
340
+ // Insert nodes before first cell
341
+ for (c = 1; c <= cols; c++) {
342
+ tr.insertBefore(cloneCell(tr.cells[0]), tr.cells[0]);
343
+ }
344
+ }
345
+ }
346
+ }
347
+ }
348
+
349
+ function split() {
350
+ each(grid, function(row, y) {
351
+ each(row, function(cell, x) {
352
+ var colSpan, rowSpan, i;
353
+
354
+ if (isCellSelected(cell)) {
355
+ cell = cell.elm;
356
+ colSpan = getSpanVal(cell, 'colspan');
357
+ rowSpan = getSpanVal(cell, 'rowspan');
358
+
359
+ if (colSpan > 1 || rowSpan > 1) {
360
+ setSpanVal(cell, 'rowSpan', 1);
361
+ setSpanVal(cell, 'colSpan', 1);
362
+
363
+ // Insert cells right
364
+ for (i = 0; i < colSpan - 1; i++) {
365
+ dom.insertAfter(cloneCell(cell), cell);
366
+ }
367
+
368
+ fillLeftDown(x, y, rowSpan - 1, colSpan);
369
+ }
370
+ }
371
+ });
372
+ });
373
+ }
374
+
375
+ function merge(cell, cols, rows) {
376
+ var pos, startX, startY, endX, endY, x, y, startCell, endCell, children, count;
377
+
378
+ // Use specified cell and cols/rows
379
+ if (cell) {
380
+ pos = getPos(cell);
381
+ startX = pos.x;
382
+ startY = pos.y;
383
+ endX = startX + (cols - 1);
384
+ endY = startY + (rows - 1);
385
+ } else {
386
+ startPos = endPos = null;
387
+
388
+ // Calculate start/end pos by checking for selected cells in grid works better with context menu
389
+ each(grid, function(row, y) {
390
+ each(row, function(cell, x) {
391
+ if (isCellSelected(cell)) {
392
+ if (!startPos) {
393
+ startPos = {x: x, y: y};
394
+ }
395
+
396
+ endPos = {x: x, y: y};
397
+ }
398
+ });
399
+ });
400
+
401
+ // Use selection, but make sure startPos is valid before accessing
402
+ if (startPos) {
403
+ startX = startPos.x;
404
+ startY = startPos.y;
405
+ endX = endPos.x;
406
+ endY = endPos.y;
407
+ }
408
+ }
409
+
410
+ // Find start/end cells
411
+ startCell = getCell(startX, startY);
412
+ endCell = getCell(endX, endY);
413
+
414
+ // Check if the cells exists and if they are of the same part for example tbody = tbody
415
+ if (startCell && endCell && startCell.part == endCell.part) {
416
+ // Split and rebuild grid
417
+ split();
418
+ buildGrid();
419
+
420
+ // Set row/col span to start cell
421
+ startCell = getCell(startX, startY).elm;
422
+ setSpanVal(startCell, 'colSpan', (endX - startX) + 1);
423
+ setSpanVal(startCell, 'rowSpan', (endY - startY) + 1);
424
+
425
+ // Remove other cells and add it's contents to the start cell
426
+ for (y = startY; y <= endY; y++) {
427
+ for (x = startX; x <= endX; x++) {
428
+ if (!grid[y] || !grid[y][x]) {
429
+ continue;
430
+ }
431
+
432
+ cell = grid[y][x].elm;
433
+
434
+ /*jshint loopfunc:true */
435
+ /*eslint loop-func:0 */
436
+ if (cell != startCell) {
437
+ // Move children to startCell
438
+ children = Tools.grep(cell.childNodes);
439
+ each(children, function(node) {
440
+ startCell.appendChild(node);
441
+ });
442
+
443
+ // Remove bogus nodes if there is children in the target cell
444
+ if (children.length) {
445
+ children = Tools.grep(startCell.childNodes);
446
+ count = 0;
447
+ each(children, function(node) {
448
+ if (node.nodeName == 'BR' && dom.getAttrib(node, 'data-mce-bogus') && count++ < children.length - 1) {
449
+ startCell.removeChild(node);
450
+ }
451
+ });
452
+ }
453
+
454
+ dom.remove(cell);
455
+ }
456
+ }
457
+ }
458
+
459
+ // Remove empty rows etc and restore caret location
460
+ cleanup();
461
+ }
462
+ }
463
+
464
+ function insertRow(before) {
465
+ var posY, cell, lastCell, x, rowElm, newRow, newCell, otherCell, rowSpan;
466
+
467
+ // Find first/last row
468
+ each(grid, function(row, y) {
469
+ each(row, function(cell) {
470
+ if (isCellSelected(cell)) {
471
+ cell = cell.elm;
472
+ rowElm = cell.parentNode;
473
+ newRow = cloneNode(rowElm, false);
474
+ posY = y;
475
+
476
+ if (before) {
477
+ return false;
478
+ }
479
+ }
480
+ });
481
+
482
+ if (before) {
483
+ return !posY;
484
+ }
485
+ });
486
+
487
+ // If posY is undefined there is nothing for us to do here...just return to avoid crashing below
488
+ if (posY === undefined) {
489
+ return;
490
+ }
491
+
492
+ for (x = 0; x < grid[0].length; x++) {
493
+ // Cell not found could be because of an invalid table structure
494
+ if (!grid[posY][x]) {
495
+ continue;
496
+ }
497
+
498
+ cell = grid[posY][x].elm;
499
+
500
+ if (cell != lastCell) {
501
+ if (!before) {
502
+ rowSpan = getSpanVal(cell, 'rowspan');
503
+ if (rowSpan > 1) {
504
+ setSpanVal(cell, 'rowSpan', rowSpan + 1);
505
+ continue;
506
+ }
507
+ } else {
508
+ // Check if cell above can be expanded
509
+ if (posY > 0 && grid[posY - 1][x]) {
510
+ otherCell = grid[posY - 1][x].elm;
511
+ rowSpan = getSpanVal(otherCell, 'rowSpan');
512
+ if (rowSpan > 1) {
513
+ setSpanVal(otherCell, 'rowSpan', rowSpan + 1);
514
+ continue;
515
+ }
516
+ }
517
+ }
518
+
519
+ // Insert new cell into new row
520
+ newCell = cloneCell(cell);
521
+ setSpanVal(newCell, 'colSpan', cell.colSpan);
522
+
523
+ newRow.appendChild(newCell);
524
+
525
+ lastCell = cell;
526
+ }
527
+ }
528
+
529
+ if (newRow.hasChildNodes()) {
530
+ if (!before) {
531
+ dom.insertAfter(newRow, rowElm);
532
+ } else {
533
+ rowElm.parentNode.insertBefore(newRow, rowElm);
534
+ }
535
+ }
536
+ }
537
+
538
+ function insertCol(before) {
539
+ var posX, lastCell;
540
+
541
+ // Find first/last column
542
+ each(grid, function(row) {
543
+ each(row, function(cell, x) {
544
+ if (isCellSelected(cell)) {
545
+ posX = x;
546
+
547
+ if (before) {
548
+ return false;
549
+ }
550
+ }
551
+ });
552
+
553
+ if (before) {
554
+ return !posX;
555
+ }
556
+ });
557
+
558
+ each(grid, function(row, y) {
559
+ var cell, rowSpan, colSpan;
560
+
561
+ if (!row[posX]) {
562
+ return;
563
+ }
564
+
565
+ cell = row[posX].elm;
566
+ if (cell != lastCell) {
567
+ colSpan = getSpanVal(cell, 'colspan');
568
+ rowSpan = getSpanVal(cell, 'rowspan');
569
+
570
+ if (colSpan == 1) {
571
+ if (!before) {
572
+ dom.insertAfter(cloneCell(cell), cell);
573
+ fillLeftDown(posX, y, rowSpan - 1, colSpan);
574
+ } else {
575
+ cell.parentNode.insertBefore(cloneCell(cell), cell);
576
+ fillLeftDown(posX, y, rowSpan - 1, colSpan);
577
+ }
578
+ } else {
579
+ setSpanVal(cell, 'colSpan', cell.colSpan + 1);
580
+ }
581
+
582
+ lastCell = cell;
583
+ }
584
+ });
585
+ }
586
+
587
+ function deleteCols() {
588
+ var cols = [];
589
+
590
+ // Get selected column indexes
591
+ each(grid, function(row) {
592
+ each(row, function(cell, x) {
593
+ if (isCellSelected(cell) && Tools.inArray(cols, x) === -1) {
594
+ each(grid, function(row) {
595
+ var cell = row[x].elm, colSpan;
596
+
597
+ colSpan = getSpanVal(cell, 'colSpan');
598
+
599
+ if (colSpan > 1) {
600
+ setSpanVal(cell, 'colSpan', colSpan - 1);
601
+ } else {
602
+ dom.remove(cell);
603
+ }
604
+ });
605
+
606
+ cols.push(x);
607
+ }
608
+ });
609
+ });
610
+
611
+ cleanup();
612
+ }
613
+
614
+ function deleteRows() {
615
+ var rows;
616
+
617
+ function deleteRow(tr) {
618
+ var nextTr, pos, lastCell;
619
+
620
+ nextTr = dom.getNext(tr, 'tr');
621
+
622
+ // Move down row spanned cells
623
+ each(tr.cells, function(cell) {
624
+ var rowSpan = getSpanVal(cell, 'rowSpan');
625
+
626
+ if (rowSpan > 1) {
627
+ setSpanVal(cell, 'rowSpan', rowSpan - 1);
628
+ pos = getPos(cell);
629
+ fillLeftDown(pos.x, pos.y, 1, 1);
630
+ }
631
+ });
632
+
633
+ // Delete cells
634
+ pos = getPos(tr.cells[0]);
635
+ each(grid[pos.y], function(cell) {
636
+ var rowSpan;
637
+
638
+ cell = cell.elm;
639
+
640
+ if (cell != lastCell) {
641
+ rowSpan = getSpanVal(cell, 'rowSpan');
642
+
643
+ if (rowSpan <= 1) {
644
+ dom.remove(cell);
645
+ } else {
646
+ setSpanVal(cell, 'rowSpan', rowSpan - 1);
647
+ }
648
+
649
+ lastCell = cell;
650
+ }
651
+ });
652
+ }
653
+
654
+ // Get selected rows and move selection out of scope
655
+ rows = getSelectedRows();
656
+
657
+ // Delete all selected rows
658
+ each(rows.reverse(), function(tr) {
659
+ deleteRow(tr);
660
+ });
661
+
662
+ cleanup();
663
+ }
664
+
665
+ function cutRows() {
666
+ var rows = getSelectedRows();
667
+
668
+ dom.remove(rows);
669
+ cleanup();
670
+
671
+ return rows;
672
+ }
673
+
674
+ function copyRows() {
675
+ var rows = getSelectedRows();
676
+
677
+ each(rows, function(row, i) {
678
+ rows[i] = cloneNode(row, true);
679
+ });
680
+
681
+ return rows;
682
+ }
683
+
684
+ function pasteRows(rows, before) {
685
+ var selectedRows = getSelectedRows(),
686
+ targetRow = selectedRows[before ? 0 : selectedRows.length - 1],
687
+ targetCellCount = targetRow.cells.length;
688
+
689
+ // Nothing to paste
690
+ if (!rows) {
691
+ return;
692
+ }
693
+
694
+ // Calc target cell count
695
+ each(grid, function(row) {
696
+ var match;
697
+
698
+ targetCellCount = 0;
699
+ each(row, function(cell) {
700
+ if (cell.real) {
701
+ targetCellCount += cell.colspan;
702
+ }
703
+
704
+ if (cell.elm.parentNode == targetRow) {
705
+ match = 1;
706
+ }
707
+ });
708
+
709
+ if (match) {
710
+ return false;
711
+ }
712
+ });
713
+
714
+ if (!before) {
715
+ rows.reverse();
716
+ }
717
+
718
+ each(rows, function(row) {
719
+ var i, cellCount = row.cells.length, cell;
720
+
721
+ // Remove col/rowspans
722
+ for (i = 0; i < cellCount; i++) {
723
+ cell = row.cells[i];
724
+ setSpanVal(cell, 'colSpan', 1);
725
+ setSpanVal(cell, 'rowSpan', 1);
726
+ }
727
+
728
+ // Needs more cells
729
+ for (i = cellCount; i < targetCellCount; i++) {
730
+ row.appendChild(cloneCell(row.cells[cellCount - 1]));
731
+ }
732
+
733
+ // Needs less cells
734
+ for (i = targetCellCount; i < cellCount; i++) {
735
+ dom.remove(row.cells[i]);
736
+ }
737
+
738
+ // Add before/after
739
+ if (before) {
740
+ targetRow.parentNode.insertBefore(row, targetRow);
741
+ } else {
742
+ dom.insertAfter(row, targetRow);
743
+ }
744
+ });
745
+
746
+ // Remove current selection
747
+ dom.removeClass(dom.select('td.mce-item-selected,th.mce-item-selected'), 'mce-item-selected');
748
+ }
749
+
750
+ function getPos(target) {
751
+ var pos;
752
+
753
+ each(grid, function(row, y) {
754
+ each(row, function(cell, x) {
755
+ if (cell.elm == target) {
756
+ pos = {x : x, y : y};
757
+ return false;
758
+ }
759
+ });
760
+
761
+ return !pos;
762
+ });
763
+
764
+ return pos;
765
+ }
766
+
767
+ function setStartCell(cell) {
768
+ startPos = getPos(cell);
769
+ }
770
+
771
+ function findEndPos() {
772
+ var maxX, maxY;
773
+
774
+ maxX = maxY = 0;
775
+
776
+ each(grid, function(row, y) {
777
+ each(row, function(cell, x) {
778
+ var colSpan, rowSpan;
779
+
780
+ if (isCellSelected(cell)) {
781
+ cell = grid[y][x];
782
+
783
+ if (x > maxX) {
784
+ maxX = x;
785
+ }
786
+
787
+ if (y > maxY) {
788
+ maxY = y;
789
+ }
790
+
791
+ if (cell.real) {
792
+ colSpan = cell.colspan - 1;
793
+ rowSpan = cell.rowspan - 1;
794
+
795
+ if (colSpan) {
796
+ if (x + colSpan > maxX) {
797
+ maxX = x + colSpan;
798
+ }
799
+ }
800
+
801
+ if (rowSpan) {
802
+ if (y + rowSpan > maxY) {
803
+ maxY = y + rowSpan;
804
+ }
805
+ }
806
+ }
807
+ }
808
+ });
809
+ });
810
+
811
+ return {x : maxX, y : maxY};
812
+ }
813
+
814
+ function setEndCell(cell) {
815
+ var startX, startY, endX, endY, maxX, maxY, colSpan, rowSpan, x, y;
816
+
817
+ endPos = getPos(cell);
818
+
819
+ if (startPos && endPos) {
820
+ // Get start/end positions
821
+ startX = Math.min(startPos.x, endPos.x);
822
+ startY = Math.min(startPos.y, endPos.y);
823
+ endX = Math.max(startPos.x, endPos.x);
824
+ endY = Math.max(startPos.y, endPos.y);
825
+
826
+ // Expand end positon to include spans
827
+ maxX = endX;
828
+ maxY = endY;
829
+
830
+ // Expand startX
831
+ for (y = startY; y <= maxY; y++) {
832
+ cell = grid[y][startX];
833
+
834
+ if (!cell.real) {
835
+ if (startX - (cell.colspan - 1) < startX) {
836
+ startX -= cell.colspan - 1;
837
+ }
838
+ }
839
+ }
840
+
841
+ // Expand startY
842
+ for (x = startX; x <= maxX; x++) {
843
+ cell = grid[startY][x];
844
+
845
+ if (!cell.real) {
846
+ if (startY - (cell.rowspan - 1) < startY) {
847
+ startY -= cell.rowspan - 1;
848
+ }
849
+ }
850
+ }
851
+
852
+ // Find max X, Y
853
+ for (y = startY; y <= endY; y++) {
854
+ for (x = startX; x <= endX; x++) {
855
+ cell = grid[y][x];
856
+
857
+ if (cell.real) {
858
+ colSpan = cell.colspan - 1;
859
+ rowSpan = cell.rowspan - 1;
860
+
861
+ if (colSpan) {
862
+ if (x + colSpan > maxX) {
863
+ maxX = x + colSpan;
864
+ }
865
+ }
866
+
867
+ if (rowSpan) {
868
+ if (y + rowSpan > maxY) {
869
+ maxY = y + rowSpan;
870
+ }
871
+ }
872
+ }
873
+ }
874
+ }
875
+
876
+ // Remove current selection
877
+ dom.removeClass(dom.select('td.mce-item-selected,th.mce-item-selected'), 'mce-item-selected');
878
+
879
+ // Add new selection
880
+ for (y = startY; y <= maxY; y++) {
881
+ for (x = startX; x <= maxX; x++) {
882
+ if (grid[y][x]) {
883
+ dom.addClass(grid[y][x].elm, 'mce-item-selected');
884
+ }
885
+ }
886
+ }
887
+ }
888
+ }
889
+
890
+ table = table || dom.getParent(selection.getStart(), 'table');
891
+
892
+ buildGrid();
893
+
894
+ selectedCell = dom.getParent(selection.getStart(), 'th,td');
895
+ if (selectedCell) {
896
+ startPos = getPos(selectedCell);
897
+ endPos = findEndPos();
898
+ selectedCell = getCell(startPos.x, startPos.y);
899
+ }
900
+
901
+ Tools.extend(this, {
902
+ deleteTable: deleteTable,
903
+ split: split,
904
+ merge: merge,
905
+ insertRow: insertRow,
906
+ insertCol: insertCol,
907
+ deleteCols: deleteCols,
908
+ deleteRows: deleteRows,
909
+ cutRows: cutRows,
910
+ copyRows: copyRows,
911
+ pasteRows: pasteRows,
912
+ getPos: getPos,
913
+ setStartCell: setStartCell,
914
+ setEndCell: setEndCell
915
+ });
916
+ };
917
+ });
918
+
919
+ // Included from: js/tinymce/plugins/table/classes/Quirks.js
920
+
921
+ /**
922
+ * Quirks.js
923
+ *
924
+ * Copyright, Moxiecode Systems AB
925
+ * Released under LGPL License.
926
+ *
927
+ * License: http://www.tinymce.com/license
928
+ * Contributing: http://www.tinymce.com/contributing
929
+ */
930
+
931
+ /**
932
+ * This class includes fixes for various browser quirks.
933
+ *
934
+ * @class tinymce.tableplugin.Quirks
935
+ * @private
936
+ */
937
+ define("tinymce/tableplugin/Quirks", [
938
+ "tinymce/util/VK",
939
+ "tinymce/Env",
940
+ "tinymce/util/Tools"
941
+ ], function(VK, Env, Tools) {
942
+ var each = Tools.each;
943
+
944
+ function getSpanVal(td, name) {
945
+ return parseInt(td.getAttribute(name) || 1, 10);
946
+ }
947
+
948
+ return function(editor) {
949
+ /**
950
+ * Fixed caret movement around tables on WebKit.
951
+ */
952
+ function moveWebKitSelection() {
953
+ function eventHandler(e) {
954
+ var key = e.keyCode;
955
+
956
+ function handle(upBool, sourceNode) {
957
+ var siblingDirection = upBool ? 'previousSibling' : 'nextSibling';
958
+ var currentRow = editor.dom.getParent(sourceNode, 'tr');
959
+ var siblingRow = currentRow[siblingDirection];
960
+
961
+ if (siblingRow) {
962
+ moveCursorToRow(editor, sourceNode, siblingRow, upBool);
963
+ e.preventDefault();
964
+ return true;
965
+ } else {
966
+ var tableNode = editor.dom.getParent(currentRow, 'table');
967
+ var middleNode = currentRow.parentNode;
968
+ var parentNodeName = middleNode.nodeName.toLowerCase();
969
+ if (parentNodeName === 'tbody' || parentNodeName === (upBool ? 'tfoot' : 'thead')) {
970
+ var targetParent = getTargetParent(upBool, tableNode, middleNode, 'tbody');
971
+ if (targetParent !== null) {
972
+ return moveToRowInTarget(upBool, targetParent, sourceNode);
973
+ }
974
+ }
975
+ return escapeTable(upBool, currentRow, siblingDirection, tableNode);
976
+ }
977
+ }
978
+
979
+ function getTargetParent(upBool, topNode, secondNode, nodeName) {
980
+ var tbodies = editor.dom.select('>' + nodeName, topNode);
981
+ var position = tbodies.indexOf(secondNode);
982
+ if (upBool && position === 0 || !upBool && position === tbodies.length - 1) {
983
+ return getFirstHeadOrFoot(upBool, topNode);
984
+ } else if (position === -1) {
985
+ var topOrBottom = secondNode.tagName.toLowerCase() === 'thead' ? 0 : tbodies.length - 1;
986
+ return tbodies[topOrBottom];
987
+ } else {
988
+ return tbodies[position + (upBool ? -1 : 1)];
989
+ }
990
+ }
991
+
992
+ function getFirstHeadOrFoot(upBool, parent) {
993
+ var tagName = upBool ? 'thead' : 'tfoot';
994
+ var headOrFoot = editor.dom.select('>' + tagName, parent);
995
+ return headOrFoot.length !== 0 ? headOrFoot[0] : null;
996
+ }
997
+
998
+ function moveToRowInTarget(upBool, targetParent, sourceNode) {
999
+ var targetRow = getChildForDirection(targetParent, upBool);
1000
+
1001
+ if (targetRow) {
1002
+ moveCursorToRow(editor, sourceNode, targetRow, upBool);
1003
+ }
1004
+
1005
+ e.preventDefault();
1006
+ return true;
1007
+ }
1008
+
1009
+ function escapeTable(upBool, currentRow, siblingDirection, table) {
1010
+ var tableSibling = table[siblingDirection];
1011
+
1012
+ if (tableSibling) {
1013
+ moveCursorToStartOfElement(tableSibling);
1014
+ return true;
1015
+ } else {
1016
+ var parentCell = editor.dom.getParent(table, 'td,th');
1017
+ if (parentCell) {
1018
+ return handle(upBool, parentCell, e);
1019
+ } else {
1020
+ var backUpSibling = getChildForDirection(currentRow, !upBool);
1021
+ moveCursorToStartOfElement(backUpSibling);
1022
+ e.preventDefault();
1023
+ return false;
1024
+ }
1025
+ }
1026
+ }
1027
+
1028
+ function getChildForDirection(parent, up) {
1029
+ var child = parent && parent[up ? 'lastChild' : 'firstChild'];
1030
+ // BR is not a valid table child to return in this case we return the table cell
1031
+ return child && child.nodeName === 'BR' ? editor.dom.getParent(child, 'td,th') : child;
1032
+ }
1033
+
1034
+ function moveCursorToStartOfElement(n) {
1035
+ editor.selection.setCursorLocation(n, 0);
1036
+ }
1037
+
1038
+ function isVerticalMovement() {
1039
+ return key == VK.UP || key == VK.DOWN;
1040
+ }
1041
+
1042
+ function isInTable(editor) {
1043
+ var node = editor.selection.getNode();
1044
+ var currentRow = editor.dom.getParent(node, 'tr');
1045
+ return currentRow !== null;
1046
+ }
1047
+
1048
+ function columnIndex(column) {
1049
+ var colIndex = 0;
1050
+ var c = column;
1051
+ while (c.previousSibling) {
1052
+ c = c.previousSibling;
1053
+ colIndex = colIndex + getSpanVal(c, "colspan");
1054
+ }
1055
+ return colIndex;
1056
+ }
1057
+
1058
+ function findColumn(rowElement, columnIndex) {
1059
+ var c = 0, r = 0;
1060
+
1061
+ each(rowElement.children, function(cell, i) {
1062
+ c = c + getSpanVal(cell, "colspan");
1063
+ r = i;
1064
+ if (c > columnIndex) {
1065
+ return false;
1066
+ }
1067
+ });
1068
+ return r;
1069
+ }
1070
+
1071
+ function moveCursorToRow(ed, node, row, upBool) {
1072
+ var srcColumnIndex = columnIndex(editor.dom.getParent(node, 'td,th'));
1073
+ var tgtColumnIndex = findColumn(row, srcColumnIndex);
1074
+ var tgtNode = row.childNodes[tgtColumnIndex];
1075
+ var rowCellTarget = getChildForDirection(tgtNode, upBool);
1076
+ moveCursorToStartOfElement(rowCellTarget || tgtNode);
1077
+ }
1078
+
1079
+ function shouldFixCaret(preBrowserNode) {
1080
+ var newNode = editor.selection.getNode();
1081
+ var newParent = editor.dom.getParent(newNode, 'td,th');
1082
+ var oldParent = editor.dom.getParent(preBrowserNode, 'td,th');
1083
+
1084
+ return newParent && newParent !== oldParent && checkSameParentTable(newParent, oldParent);
1085
+ }
1086
+
1087
+ function checkSameParentTable(nodeOne, NodeTwo) {
1088
+ return editor.dom.getParent(nodeOne, 'TABLE') === editor.dom.getParent(NodeTwo, 'TABLE');
1089
+ }
1090
+
1091
+ if (isVerticalMovement() && isInTable(editor)) {
1092
+ var preBrowserNode = editor.selection.getNode();
1093
+ setTimeout(function() {
1094
+ if (shouldFixCaret(preBrowserNode)) {
1095
+ handle(!e.shiftKey && key === VK.UP, preBrowserNode, e);
1096
+ }
1097
+ }, 0);
1098
+ }
1099
+ }
1100
+
1101
+ editor.on('KeyDown', function(e) {
1102
+ eventHandler(e);
1103
+ });
1104
+ }
1105
+
1106
+ function fixBeforeTableCaretBug() {
1107
+ // Checks if the selection/caret is at the start of the specified block element
1108
+ function isAtStart(rng, par) {
1109
+ var doc = par.ownerDocument, rng2 = doc.createRange(), elm;
1110
+
1111
+ rng2.setStartBefore(par);
1112
+ rng2.setEnd(rng.endContainer, rng.endOffset);
1113
+
1114
+ elm = doc.createElement('body');
1115
+ elm.appendChild(rng2.cloneContents());
1116
+
1117
+ // Check for text characters of other elements that should be treated as content
1118
+ return elm.innerHTML.replace(/<(br|img|object|embed|input|textarea)[^>]*>/gi, '-').replace(/<[^>]+>/g, '').length === 0;
1119
+ }
1120
+
1121
+ // Fixes an bug where it's impossible to place the caret before a table in Gecko
1122
+ // this fix solves it by detecting when the caret is at the beginning of such a table
1123
+ // and then manually moves the caret infront of the table
1124
+ editor.on('KeyDown', function(e) {
1125
+ var rng, table, dom = editor.dom;
1126
+
1127
+ // On gecko it's not possible to place the caret before a table
1128
+ if (e.keyCode == 37 || e.keyCode == 38) {
1129
+ rng = editor.selection.getRng();
1130
+ table = dom.getParent(rng.startContainer, 'table');
1131
+
1132
+ if (table && editor.getBody().firstChild == table) {
1133
+ if (isAtStart(rng, table)) {
1134
+ rng = dom.createRng();
1135
+
1136
+ rng.setStartBefore(table);
1137
+ rng.setEndBefore(table);
1138
+
1139
+ editor.selection.setRng(rng);
1140
+
1141
+ e.preventDefault();
1142
+ }
1143
+ }
1144
+ }
1145
+ });
1146
+ }
1147
+
1148
+ // Fixes an issue on Gecko where it's impossible to place the caret behind a table
1149
+ // This fix will force a paragraph element after the table but only when the forced_root_block setting is enabled
1150
+ function fixTableCaretPos() {
1151
+ editor.on('KeyDown SetContent VisualAid', function() {
1152
+ var last;
1153
+
1154
+ // Skip empty text nodes from the end
1155
+ for (last = editor.getBody().lastChild; last; last = last.previousSibling) {
1156
+ if (last.nodeType == 3) {
1157
+ if (last.nodeValue.length > 0) {
1158
+ break;
1159
+ }
1160
+ } else if (last.nodeType == 1 && !last.getAttribute('data-mce-bogus')) {
1161
+ break;
1162
+ }
1163
+ }
1164
+
1165
+ if (last && last.nodeName == 'TABLE') {
1166
+ if (editor.settings.forced_root_block) {
1167
+ editor.dom.add(
1168
+ editor.getBody(),
1169
+ editor.settings.forced_root_block,
1170
+ editor.settings.forced_root_block_attrs,
1171
+ Env.ie && Env.ie < 11 ? '&nbsp;' : '<br data-mce-bogus="1" />'
1172
+ );
1173
+ } else {
1174
+ editor.dom.add(editor.getBody(), 'br', {'data-mce-bogus': '1'});
1175
+ }
1176
+ }
1177
+ });
1178
+
1179
+ editor.on('PreProcess', function(o) {
1180
+ var last = o.node.lastChild;
1181
+
1182
+ if (last && (last.nodeName == "BR" || (last.childNodes.length == 1 &&
1183
+ (last.firstChild.nodeName == 'BR' || last.firstChild.nodeValue == '\u00a0'))) &&
1184
+ last.previousSibling && last.previousSibling.nodeName == "TABLE") {
1185
+ editor.dom.remove(last);
1186
+ }
1187
+ });
1188
+ }
1189
+
1190
+ // this nasty hack is here to work around some WebKit selection bugs.
1191
+ function fixTableCellSelection() {
1192
+ function tableCellSelected(ed, rng, n, currentCell) {
1193
+ // The decision of when a table cell is selected is somewhat involved. The fact that this code is
1194
+ // required is actually a pointer to the root cause of this bug. A cell is selected when the start
1195
+ // and end offsets are 0, the start container is a text, and the selection node is either a TR (most cases)
1196
+ // or the parent of the table (in the case of the selection containing the last cell of a table).
1197
+ var TEXT_NODE = 3, table = ed.dom.getParent(rng.startContainer, 'TABLE');
1198
+ var tableParent, allOfCellSelected, tableCellSelection;
1199
+
1200
+ if (table) {
1201
+ tableParent = table.parentNode;
1202
+ }
1203
+
1204
+ allOfCellSelected = rng.startContainer.nodeType == TEXT_NODE &&
1205
+ rng.startOffset === 0 &&
1206
+ rng.endOffset === 0 &&
1207
+ currentCell &&
1208
+ (n.nodeName == "TR" || n == tableParent);
1209
+
1210
+ tableCellSelection = (n.nodeName == "TD" || n.nodeName == "TH") && !currentCell;
1211
+
1212
+ return allOfCellSelected || tableCellSelection;
1213
+ }
1214
+
1215
+ function fixSelection() {
1216
+ var rng = editor.selection.getRng();
1217
+ var n = editor.selection.getNode();
1218
+ var currentCell = editor.dom.getParent(rng.startContainer, 'TD,TH');
1219
+
1220
+ if (!tableCellSelected(editor, rng, n, currentCell)) {
1221
+ return;
1222
+ }
1223
+
1224
+ if (!currentCell) {
1225
+ currentCell = n;
1226
+ }
1227
+
1228
+ // Get the very last node inside the table cell
1229
+ var end = currentCell.lastChild;
1230
+ while (end.lastChild) {
1231
+ end = end.lastChild;
1232
+ }
1233
+
1234
+ // Select the entire table cell. Nothing outside of the table cell should be selected.
1235
+ rng.setEnd(end, end.nodeValue.length);
1236
+ editor.selection.setRng(rng);
1237
+ }
1238
+
1239
+ editor.on('KeyDown', function() {
1240
+ fixSelection();
1241
+ });
1242
+
1243
+ editor.on('MouseDown', function(e) {
1244
+ if (e.button != 2) {
1245
+ fixSelection();
1246
+ }
1247
+ });
1248
+ }
1249
+
1250
+ /**
1251
+ * Delete table if all cells are selected.
1252
+ */
1253
+ function deleteTable() {
1254
+ editor.on('keydown', function(e) {
1255
+ if ((e.keyCode == VK.DELETE || e.keyCode == VK.BACKSPACE) && !e.isDefaultPrevented()) {
1256
+ var table = editor.dom.getParent(editor.selection.getStart(), 'table');
1257
+
1258
+ if (table) {
1259
+ var cells = editor.dom.select('td,th', table), i = cells.length;
1260
+ while (i--) {
1261
+ if (!editor.dom.hasClass(cells[i], 'mce-item-selected')) {
1262
+ return;
1263
+ }
1264
+ }
1265
+
1266
+ e.preventDefault();
1267
+ editor.execCommand('mceTableDelete');
1268
+ }
1269
+ }
1270
+ });
1271
+ }
1272
+
1273
+ deleteTable();
1274
+
1275
+ if (Env.webkit) {
1276
+ moveWebKitSelection();
1277
+ fixTableCellSelection();
1278
+ }
1279
+
1280
+ if (Env.gecko) {
1281
+ fixBeforeTableCaretBug();
1282
+ fixTableCaretPos();
1283
+ }
1284
+
1285
+ if (Env.ie > 10) {
1286
+ fixBeforeTableCaretBug();
1287
+ fixTableCaretPos();
1288
+ }
1289
+ };
1290
+ });
1291
+
1292
+ // Included from: js/tinymce/plugins/table/classes/CellSelection.js
1293
+
1294
+ /**
1295
+ * CellSelection.js
1296
+ *
1297
+ * Copyright, Moxiecode Systems AB
1298
+ * Released under LGPL License.
1299
+ *
1300
+ * License: http://www.tinymce.com/license
1301
+ * Contributing: http://www.tinymce.com/contributing
1302
+ */
1303
+
1304
+ /**
1305
+ * This class handles table cell selection by faking it using a css class that gets applied
1306
+ * to cells when dragging the mouse from one cell to another.
1307
+ *
1308
+ * @class tinymce.tableplugin.CellSelection
1309
+ * @private
1310
+ */
1311
+ define("tinymce/tableplugin/CellSelection", [
1312
+ "tinymce/tableplugin/TableGrid",
1313
+ "tinymce/dom/TreeWalker",
1314
+ "tinymce/util/Tools"
1315
+ ], function(TableGrid, TreeWalker, Tools) {
1316
+ return function(editor) {
1317
+ var dom = editor.dom, tableGrid, startCell, startTable, hasCellSelection = true;
1318
+
1319
+ function clear() {
1320
+ // Restore selection possibilities
1321
+ editor.getBody().style.webkitUserSelect = '';
1322
+
1323
+ if (hasCellSelection) {
1324
+ editor.dom.removeClass(
1325
+ editor.dom.select('td.mce-item-selected,th.mce-item-selected'),
1326
+ 'mce-item-selected'
1327
+ );
1328
+
1329
+ hasCellSelection = false;
1330
+ }
1331
+ }
1332
+
1333
+ function cellSelectionHandler(e) {
1334
+ var sel, table, target = e.target;
1335
+
1336
+ if (startCell && (tableGrid || target != startCell) && (target.nodeName == 'TD' || target.nodeName == 'TH')) {
1337
+ table = dom.getParent(target, 'table');
1338
+ if (table == startTable) {
1339
+ if (!tableGrid) {
1340
+ tableGrid = new TableGrid(editor, table);
1341
+ tableGrid.setStartCell(startCell);
1342
+
1343
+ editor.getBody().style.webkitUserSelect = 'none';
1344
+ }
1345
+
1346
+ tableGrid.setEndCell(target);
1347
+ hasCellSelection = true;
1348
+ }
1349
+
1350
+ // Remove current selection
1351
+ sel = editor.selection.getSel();
1352
+
1353
+ try {
1354
+ if (sel.removeAllRanges) {
1355
+ sel.removeAllRanges();
1356
+ } else {
1357
+ sel.empty();
1358
+ }
1359
+ } catch (ex) {
1360
+ // IE9 might throw errors here
1361
+ }
1362
+
1363
+ e.preventDefault();
1364
+ }
1365
+ }
1366
+
1367
+ // Add cell selection logic
1368
+ editor.on('MouseDown', function(e) {
1369
+ if (e.button != 2) {
1370
+ clear();
1371
+
1372
+ startCell = dom.getParent(e.target, 'td,th');
1373
+ startTable = dom.getParent(startCell, 'table');
1374
+ }
1375
+ });
1376
+
1377
+ editor.on('mouseover', cellSelectionHandler);
1378
+
1379
+ editor.on('remove', function() {
1380
+ dom.unbind(editor.getDoc(), 'mouseover', cellSelectionHandler);
1381
+ });
1382
+
1383
+ editor.on('MouseUp', function() {
1384
+ var rng, sel = editor.selection, selectedCells, walker, node, lastNode, endNode;
1385
+
1386
+ function setPoint(node, start) {
1387
+ var walker = new TreeWalker(node, node);
1388
+
1389
+ do {
1390
+ // Text node
1391
+ if (node.nodeType == 3 && Tools.trim(node.nodeValue).length !== 0) {
1392
+ if (start) {
1393
+ rng.setStart(node, 0);
1394
+ } else {
1395
+ rng.setEnd(node, node.nodeValue.length);
1396
+ }
1397
+
1398
+ return;
1399
+ }
1400
+
1401
+ // BR element
1402
+ if (node.nodeName == 'BR') {
1403
+ if (start) {
1404
+ rng.setStartBefore(node);
1405
+ } else {
1406
+ rng.setEndBefore(node);
1407
+ }
1408
+
1409
+ return;
1410
+ }
1411
+ } while ((node = (start ? walker.next() : walker.prev())));
1412
+ }
1413
+
1414
+ // Move selection to startCell
1415
+ if (startCell) {
1416
+ if (tableGrid) {
1417
+ editor.getBody().style.webkitUserSelect = '';
1418
+ }
1419
+
1420
+ // Try to expand text selection as much as we can only Gecko supports cell selection
1421
+ selectedCells = dom.select('td.mce-item-selected,th.mce-item-selected');
1422
+ if (selectedCells.length > 0) {
1423
+ rng = dom.createRng();
1424
+ node = selectedCells[0];
1425
+ endNode = selectedCells[selectedCells.length - 1];
1426
+ rng.setStartBefore(node);
1427
+ rng.setEndAfter(node);
1428
+
1429
+ setPoint(node, 1);
1430
+ walker = new TreeWalker(node, dom.getParent(selectedCells[0], 'table'));
1431
+
1432
+ do {
1433
+ if (node.nodeName == 'TD' || node.nodeName == 'TH') {
1434
+ if (!dom.hasClass(node, 'mce-item-selected')) {
1435
+ break;
1436
+ }
1437
+
1438
+ lastNode = node;
1439
+ }
1440
+ } while ((node = walker.next()));
1441
+
1442
+ setPoint(lastNode);
1443
+
1444
+ sel.setRng(rng);
1445
+ }
1446
+
1447
+ editor.nodeChanged();
1448
+ startCell = tableGrid = startTable = null;
1449
+ }
1450
+ });
1451
+
1452
+ editor.on('KeyUp', function() {
1453
+ clear();
1454
+ });
1455
+
1456
+ return {
1457
+ clear: clear
1458
+ };
1459
+ };
1460
+ });
1461
+
1462
+ // Included from: js/tinymce/plugins/table/classes/Plugin.js
1463
+
1464
+ /**
1465
+ * Plugin.js
1466
+ *
1467
+ * Copyright, Moxiecode Systems AB
1468
+ * Released under LGPL License.
1469
+ *
1470
+ * License: http://www.tinymce.com/license
1471
+ * Contributing: http://www.tinymce.com/contributing
1472
+ */
1473
+
1474
+ /**
1475
+ * This class contains all core logic for the table plugin.
1476
+ *
1477
+ * @class tinymce.tableplugin.Plugin
1478
+ * @private
1479
+ */
1480
+ define("tinymce/tableplugin/Plugin", [
1481
+ "tinymce/tableplugin/TableGrid",
1482
+ "tinymce/tableplugin/Quirks",
1483
+ "tinymce/tableplugin/CellSelection",
1484
+ "tinymce/util/Tools",
1485
+ "tinymce/dom/TreeWalker",
1486
+ "tinymce/Env",
1487
+ "tinymce/PluginManager"
1488
+ ], function(TableGrid, Quirks, CellSelection, Tools, TreeWalker, Env, PluginManager) {
1489
+ var each = Tools.each;
1490
+
1491
+ function Plugin(editor) {
1492
+ var winMan, clipboardRows, self = this; // Might be selected cells on reload
1493
+
1494
+ function removePxSuffix(size) {
1495
+ return size ? size.replace(/px$/, '') : "";
1496
+ }
1497
+
1498
+ function addSizeSuffix(size) {
1499
+ if (/^[0-9]+$/.test(size)) {
1500
+ size += "px";
1501
+ }
1502
+
1503
+ return size;
1504
+ }
1505
+
1506
+ function unApplyAlign(elm) {
1507
+ each('left center right'.split(' '), function(name) {
1508
+ editor.formatter.remove('align' + name, {}, elm);
1509
+ });
1510
+ }
1511
+
1512
+ function tableDialog() {
1513
+ var dom = editor.dom, tableElm, data;
1514
+
1515
+ tableElm = dom.getParent(editor.selection.getStart(), 'table');
1516
+
1517
+ data = {
1518
+ width: removePxSuffix(dom.getStyle(tableElm, 'width') || dom.getAttrib(tableElm, 'width')),
1519
+ height: removePxSuffix(dom.getStyle(tableElm, 'height') || dom.getAttrib(tableElm, 'height')),
1520
+ cellspacing: dom.getAttrib(tableElm, 'cellspacing'),
1521
+ cellpadding: dom.getAttrib(tableElm, 'cellpadding'),
1522
+ border: dom.getAttrib(tableElm, 'border'),
1523
+ caption: !!dom.select('caption', tableElm)[0]
1524
+ };
1525
+
1526
+ each('left center right'.split(' '), function(name) {
1527
+ if (editor.formatter.matchNode(tableElm, 'align' + name)) {
1528
+ data.align = name;
1529
+ }
1530
+ });
1531
+
1532
+ editor.windowManager.open({
1533
+ title: "Table properties",
1534
+ items: {
1535
+ type: 'form',
1536
+ layout: 'grid',
1537
+ columns: 2,
1538
+ data: data,
1539
+ defaults: {
1540
+ type: 'textbox',
1541
+ maxWidth: 50
1542
+ },
1543
+ items: [
1544
+ {label: 'Width', name: 'width'},
1545
+ {label: 'Height', name: 'height'},
1546
+ {label: 'Cell spacing', name: 'cellspacing'},
1547
+ {label: 'Cell padding', name: 'cellpadding'},
1548
+ {label: 'Border', name: 'border'},
1549
+ {label: 'Caption', name: 'caption', type: 'checkbox'},
1550
+ {
1551
+ label: 'Alignment',
1552
+ minWidth: 90,
1553
+ name: 'align',
1554
+ type: 'listbox',
1555
+ text: 'None',
1556
+ maxWidth: null,
1557
+ values: [
1558
+ {text: 'None', value: ''},
1559
+ {text: 'Left', value: 'left'},
1560
+ {text: 'Center', value: 'center'},
1561
+ {text: 'Right', value: 'right'}
1562
+ ]
1563
+ }
1564
+ ]
1565
+ },
1566
+
1567
+ onsubmit: function() {
1568
+ var data = this.toJSON(), captionElm;
1569
+
1570
+ editor.undoManager.transact(function() {
1571
+ editor.dom.setAttribs(tableElm, {
1572
+ cellspacing: data.cellspacing,
1573
+ cellpadding: data.cellpadding,
1574
+ border: data.border
1575
+ });
1576
+
1577
+ editor.dom.setStyles(tableElm, {
1578
+ width: addSizeSuffix(data.width),
1579
+ height: addSizeSuffix(data.height)
1580
+ });
1581
+
1582
+ // Toggle caption on/off
1583
+ captionElm = dom.select('caption', tableElm)[0];
1584
+
1585
+ if (captionElm && !data.caption) {
1586
+ dom.remove(captionElm);
1587
+ }
1588
+
1589
+ if (!captionElm && data.caption) {
1590
+ captionElm = dom.create('caption');
1591
+ captionElm.innerHTML = !Env.ie ? '<br data-mce-bogus="1"/>' : '\u00a0';
1592
+ tableElm.insertBefore(captionElm, tableElm.firstChild);
1593
+ }
1594
+
1595
+ unApplyAlign(tableElm);
1596
+ if (data.align) {
1597
+ editor.formatter.apply('align' + data.align, {}, tableElm);
1598
+ }
1599
+
1600
+ editor.focus();
1601
+ editor.addVisual();
1602
+ });
1603
+ }
1604
+ });
1605
+ }
1606
+
1607
+ function mergeDialog(grid, cell) {
1608
+ editor.windowManager.open({
1609
+ title: "Merge cells",
1610
+ body: [
1611
+ {label: 'Cols', name: 'cols', type: 'textbox', size: 10},
1612
+ {label: 'Rows', name: 'rows', type: 'textbox', size: 10}
1613
+ ],
1614
+ onsubmit: function() {
1615
+ var data = this.toJSON();
1616
+
1617
+ editor.undoManager.transact(function() {
1618
+ grid.merge(cell, data.cols, data.rows);
1619
+ });
1620
+ }
1621
+ });
1622
+ }
1623
+
1624
+ function cellDialog() {
1625
+ var dom = editor.dom, cellElm, data, cells = [];
1626
+
1627
+ // Get selected cells or the current cell
1628
+ cells = editor.dom.select('td.mce-item-selected,th.mce-item-selected');
1629
+ cellElm = editor.dom.getParent(editor.selection.getStart(), 'td,th');
1630
+ if (!cells.length && cellElm) {
1631
+ cells.push(cellElm);
1632
+ }
1633
+
1634
+ cellElm = cellElm || cells[0];
1635
+
1636
+ if (!cellElm) {
1637
+ // If this element is null, return now to avoid crashing.
1638
+ return;
1639
+ }
1640
+
1641
+ data = {
1642
+ width: removePxSuffix(dom.getStyle(cellElm, 'width') || dom.getAttrib(cellElm, 'width')),
1643
+ height: removePxSuffix(dom.getStyle(cellElm, 'height') || dom.getAttrib(cellElm, 'height')),
1644
+ scope: dom.getAttrib(cellElm, 'scope')
1645
+ };
1646
+
1647
+ data.type = cellElm.nodeName.toLowerCase();
1648
+
1649
+ each('left center right'.split(' '), function(name) {
1650
+ if (editor.formatter.matchNode(cellElm, 'align' + name)) {
1651
+ data.align = name;
1652
+ }
1653
+ });
1654
+
1655
+ editor.windowManager.open({
1656
+ title: "Cell properties",
1657
+ items: {
1658
+ type: 'form',
1659
+ data: data,
1660
+ layout: 'grid',
1661
+ columns: 2,
1662
+ defaults: {
1663
+ type: 'textbox',
1664
+ maxWidth: 50
1665
+ },
1666
+ items: [
1667
+ {label: 'Width', name: 'width'},
1668
+ {label: 'Height', name: 'height'},
1669
+ {
1670
+ label: 'Cell type',
1671
+ name: 'type',
1672
+ type: 'listbox',
1673
+ text: 'None',
1674
+ minWidth: 90,
1675
+ maxWidth: null,
1676
+ values: [
1677
+ {text: 'Cell', value: 'td'},
1678
+ {text: 'Header cell', value: 'th'}
1679
+ ]
1680
+ },
1681
+ {
1682
+ label: 'Scope',
1683
+ name: 'scope',
1684
+ type: 'listbox',
1685
+ text: 'None',
1686
+ minWidth: 90,
1687
+ maxWidth: null,
1688
+ values: [
1689
+ {text: 'None', value: ''},
1690
+ {text: 'Row', value: 'row'},
1691
+ {text: 'Column', value: 'col'},
1692
+ {text: 'Row group', value: 'rowgroup'},
1693
+ {text: 'Column group', value: 'colgroup'}
1694
+ ]
1695
+ },
1696
+ {
1697
+ label: 'Alignment',
1698
+ name: 'align',
1699
+ type: 'listbox',
1700
+ text: 'None',
1701
+ minWidth: 90,
1702
+ maxWidth: null,
1703
+ values: [
1704
+ {text: 'None', value: ''},
1705
+ {text: 'Left', value: 'left'},
1706
+ {text: 'Center', value: 'center'},
1707
+ {text: 'Right', value: 'right'}
1708
+ ]
1709
+ }
1710
+ ]
1711
+ },
1712
+
1713
+ onsubmit: function() {
1714
+ var data = this.toJSON();
1715
+
1716
+ editor.undoManager.transact(function() {
1717
+ each(cells, function(cellElm) {
1718
+ editor.dom.setAttrib(cellElm, 'scope', data.scope);
1719
+
1720
+ editor.dom.setStyles(cellElm, {
1721
+ width: addSizeSuffix(data.width),
1722
+ height: addSizeSuffix(data.height)
1723
+ });
1724
+
1725
+ // Switch cell type
1726
+ if (data.type && cellElm.nodeName.toLowerCase() != data.type) {
1727
+ cellElm = dom.rename(cellElm, data.type);
1728
+ }
1729
+
1730
+ // Apply/remove alignment
1731
+ unApplyAlign(cellElm);
1732
+ if (data.align) {
1733
+ editor.formatter.apply('align' + data.align, {}, cellElm);
1734
+ }
1735
+ });
1736
+
1737
+ editor.focus();
1738
+ });
1739
+ }
1740
+ });
1741
+ }
1742
+
1743
+ function rowDialog() {
1744
+ var dom = editor.dom, tableElm, cellElm, rowElm, data, rows = [];
1745
+
1746
+ tableElm = editor.dom.getParent(editor.selection.getStart(), 'table');
1747
+ cellElm = editor.dom.getParent(editor.selection.getStart(), 'td,th');
1748
+
1749
+ each(tableElm.rows, function(row) {
1750
+ each(row.cells, function(cell) {
1751
+ if (dom.hasClass(cell, 'mce-item-selected') || cell == cellElm) {
1752
+ rows.push(row);
1753
+ return false;
1754
+ }
1755
+ });
1756
+ });
1757
+
1758
+ rowElm = rows[0];
1759
+ if (!rowElm) {
1760
+ // If this element is null, return now to avoid crashing.
1761
+ return;
1762
+ }
1763
+
1764
+ data = {
1765
+ height: removePxSuffix(dom.getStyle(rowElm, 'height') || dom.getAttrib(rowElm, 'height')),
1766
+ scope: dom.getAttrib(rowElm, 'scope')
1767
+ };
1768
+
1769
+ data.type = rowElm.parentNode.nodeName.toLowerCase();
1770
+
1771
+ each('left center right'.split(' '), function(name) {
1772
+ if (editor.formatter.matchNode(rowElm, 'align' + name)) {
1773
+ data.align = name;
1774
+ }
1775
+ });
1776
+
1777
+ editor.windowManager.open({
1778
+ title: "Row properties",
1779
+ items: {
1780
+ type: 'form',
1781
+ data: data,
1782
+ columns: 2,
1783
+ defaults: {
1784
+ type: 'textbox'
1785
+ },
1786
+ items: [
1787
+ {
1788
+ type: 'listbox',
1789
+ name: 'type',
1790
+ label: 'Row type',
1791
+ text: 'None',
1792
+ maxWidth: null,
1793
+ values: [
1794
+ {text: 'Header', value: 'thead'},
1795
+ {text: 'Body', value: 'tbody'},
1796
+ {text: 'Footer', value: 'tfoot'}
1797
+ ]
1798
+ },
1799
+ {
1800
+ type: 'listbox',
1801
+ name: 'align',
1802
+ label: 'Alignment',
1803
+ text: 'None',
1804
+ maxWidth: null,
1805
+ values: [
1806
+ {text: 'None', value: ''},
1807
+ {text: 'Left', value: 'left'},
1808
+ {text: 'Center', value: 'center'},
1809
+ {text: 'Right', value: 'right'}
1810
+ ]
1811
+ },
1812
+ {label: 'Height', name: 'height'}
1813
+ ]
1814
+ },
1815
+
1816
+ onsubmit: function() {
1817
+ var data = this.toJSON(), tableElm, oldParentElm, parentElm;
1818
+
1819
+ editor.undoManager.transact(function() {
1820
+ var toType = data.type;
1821
+
1822
+ each(rows, function(rowElm) {
1823
+ editor.dom.setAttrib(rowElm, 'scope', data.scope);
1824
+
1825
+ editor.dom.setStyles(rowElm, {
1826
+ height: addSizeSuffix(data.height)
1827
+ });
1828
+
1829
+ if (toType != rowElm.parentNode.nodeName.toLowerCase()) {
1830
+ tableElm = dom.getParent(rowElm, 'table');
1831
+
1832
+ oldParentElm = rowElm.parentNode;
1833
+ parentElm = dom.select(toType, tableElm)[0];
1834
+ if (!parentElm) {
1835
+ parentElm = dom.create(toType);
1836
+ if (tableElm.firstChild) {
1837
+ tableElm.insertBefore(parentElm, tableElm.firstChild);
1838
+ } else {
1839
+ tableElm.appendChild(parentElm);
1840
+ }
1841
+ }
1842
+
1843
+ parentElm.appendChild(rowElm);
1844
+
1845
+ if (!oldParentElm.hasChildNodes()) {
1846
+ dom.remove(oldParentElm);
1847
+ }
1848
+ }
1849
+
1850
+ // Apply/remove alignment
1851
+ unApplyAlign(rowElm);
1852
+ if (data.align) {
1853
+ editor.formatter.apply('align' + data.align, {}, rowElm);
1854
+ }
1855
+ });
1856
+
1857
+ editor.focus();
1858
+ });
1859
+ }
1860
+ });
1861
+ }
1862
+
1863
+ function cmd(command) {
1864
+ return function() {
1865
+ editor.execCommand(command);
1866
+ };
1867
+ }
1868
+
1869
+ function insertTable(cols, rows) {
1870
+ var y, x, html;
1871
+
1872
+ html = '<table><tbody>';
1873
+
1874
+ for (y = 0; y < rows; y++) {
1875
+ html += '<tr>';
1876
+
1877
+ for (x = 0; x < cols; x++) {
1878
+ html += '<td>' + (Env.ie ? " " : '<br>') + '</td>';
1879
+ }
1880
+
1881
+ html += '</tr>';
1882
+ }
1883
+
1884
+ html += '</tbody></table>';
1885
+
1886
+ editor.insertContent(html);
1887
+ }
1888
+
1889
+ function handleDisabledState(ctrl, selector) {
1890
+ function bindStateListener() {
1891
+ ctrl.disabled(!editor.dom.getParent(editor.selection.getStart(), selector));
1892
+
1893
+ editor.selection.selectorChanged(selector, function(state) {
1894
+ ctrl.disabled(!state);
1895
+ });
1896
+ }
1897
+
1898
+ if (editor.initialized) {
1899
+ bindStateListener();
1900
+ } else {
1901
+ editor.on('init', bindStateListener);
1902
+ }
1903
+ }
1904
+
1905
+ function postRender() {
1906
+ /*jshint validthis:true*/
1907
+ handleDisabledState(this, 'table');
1908
+ }
1909
+
1910
+ function postRenderCell() {
1911
+ /*jshint validthis:true*/
1912
+ handleDisabledState(this, 'td,th');
1913
+ }
1914
+
1915
+ function generateTableGrid() {
1916
+ var html = '';
1917
+
1918
+ html = '<table role="grid" class="mce-grid mce-grid-border" aria-readonly="true">';
1919
+
1920
+ for (var y = 0; y < 10; y++) {
1921
+ html += '<tr>';
1922
+
1923
+ for (var x = 0; x < 10; x++) {
1924
+ html += '<td role="gridcell" tabindex="-1"><a id="mcegrid' + (y * 10 + x) + '" href="#" ' +
1925
+ 'data-mce-x="' + x + '" data-mce-y="' + y + '" ' +
1926
+ '' + (x + y === 0 ? ' class="mce-active"' : '') + '></a></td>';
1927
+ }
1928
+
1929
+ html += '</tr>';
1930
+ }
1931
+
1932
+ html += '</table>';
1933
+
1934
+ html += '<div class="mce-text-center" role="presentation">1 x 1</div>';
1935
+
1936
+ return html;
1937
+ }
1938
+
1939
+ function selectGrid(tx, ty, control) {
1940
+ var table = control.getEl().getElementsByTagName('table')[0];
1941
+ var rel = control.parent().rel, x, y, focusCell, cell;
1942
+
1943
+ if (control.isRtl() || rel == 'tl-tr') {
1944
+ for (y = 9; y >= 0; y--) {
1945
+ for (x = 0; x < 10; x++) {
1946
+ cell = table.rows[y].childNodes[x].firstChild;
1947
+
1948
+ editor.dom.toggleClass(
1949
+ cell,
1950
+ 'mce-active',
1951
+ x >= tx && y <= ty
1952
+ );
1953
+
1954
+ if (x >= tx && y <= ty) {
1955
+ focusCell = cell;
1956
+ }
1957
+ }
1958
+ }
1959
+
1960
+ tx = 9 - tx;
1961
+ table.nextSibling.innerHTML = tx + ' x ' + (ty + 1);
1962
+ } else {
1963
+ for (y = 0; y < 10; y++) {
1964
+ for (x = 0; x < 10; x++) {
1965
+ cell = table.rows[y].childNodes[x].firstChild;
1966
+
1967
+ editor.dom.toggleClass(
1968
+ cell,
1969
+ 'mce-active',
1970
+ x <= tx && y <= ty
1971
+ );
1972
+
1973
+ if (x <= tx && y <= ty) {
1974
+ focusCell = cell;
1975
+ }
1976
+ }
1977
+ }
1978
+
1979
+ table.nextSibling.innerHTML = (tx + 1) + ' x ' + (ty + 1);
1980
+ }
1981
+
1982
+ return focusCell.parentNode;
1983
+ }
1984
+
1985
+ editor.addMenuItem('inserttable', {
1986
+ text: 'Insert table',
1987
+ icon: 'table',
1988
+ context: 'table',
1989
+ onhide: function() {
1990
+ var elements = this.menu.items()[0].getEl().getElementsByTagName('a');
1991
+ editor.dom.removeClass(elements, 'mce-active');
1992
+ editor.dom.addClass(elements[0], 'mce-active');
1993
+ },
1994
+ menu: [
1995
+ {
1996
+ type: 'container',
1997
+ html: generateTableGrid(),
1998
+
1999
+ onPostRender: function() {
2000
+ this.lastX = this.lastY = 0;
2001
+ },
2002
+
2003
+ onmousemove: function(e) {
2004
+ var target = e.target, x, y;
2005
+
2006
+ if (target.nodeName == 'A') {
2007
+ x = parseInt(target.getAttribute('data-mce-x'), 10);
2008
+ y = parseInt(target.getAttribute('data-mce-y'), 10);
2009
+
2010
+ if (x !== this.lastX || y !== this.lastY) {
2011
+ selectGrid(x, y, e.control);
2012
+
2013
+ this.lastX = x;
2014
+ this.lastY = y;
2015
+ }
2016
+ }
2017
+ },
2018
+
2019
+ onkeydown: function(e) {
2020
+ var x = this.lastX, y = this.lastY, isHandled;
2021
+
2022
+ switch (e.keyCode) {
2023
+ case 37: // DOM_VK_LEFT
2024
+ if (x > 0) {
2025
+ x--;
2026
+ isHandled = true;
2027
+ }
2028
+ break;
2029
+
2030
+ case 39: // DOM_VK_RIGHT
2031
+ isHandled = true;
2032
+
2033
+ if (x < 9) {
2034
+ x++;
2035
+ }
2036
+ break;
2037
+
2038
+ case 38: // DOM_VK_UP
2039
+ isHandled = true;
2040
+
2041
+ if (y > 0) {
2042
+ y--;
2043
+ }
2044
+ break;
2045
+
2046
+ case 40: // DOM_VK_DOWN
2047
+ isHandled = true;
2048
+
2049
+ if (y < 9) {
2050
+ y++;
2051
+ }
2052
+ break;
2053
+ }
2054
+
2055
+ if (isHandled) {
2056
+ e.preventDefault();
2057
+ e.stopPropagation();
2058
+
2059
+ selectGrid(x, y, e.control).focus();
2060
+
2061
+ this.lastX = x;
2062
+ this.lastY = y;
2063
+ }
2064
+ },
2065
+
2066
+ onclick: function(e) {
2067
+ if (e.target.nodeName == 'A') {
2068
+ e.preventDefault();
2069
+ e.stopPropagation();
2070
+ this.parent().cancel();
2071
+
2072
+ insertTable(this.lastX + 1, this.lastY + 1);
2073
+ }
2074
+ }
2075
+ }
2076
+ ]
2077
+ });
2078
+
2079
+ editor.addMenuItem('tableprops', {
2080
+ text: 'Table properties',
2081
+ context: 'table',
2082
+ onPostRender: postRender,
2083
+ onclick: tableDialog
2084
+ });
2085
+
2086
+ editor.addMenuItem('deletetable', {
2087
+ text: 'Delete table',
2088
+ context: 'table',
2089
+ onPostRender: postRender,
2090
+ cmd: 'mceTableDelete'
2091
+ });
2092
+
2093
+ editor.addMenuItem('cell', {
2094
+ separator: 'before',
2095
+ text: 'Cell',
2096
+ context: 'table',
2097
+ menu: [
2098
+ {text: 'Cell properties', onclick: cmd('mceTableCellProps'), onPostRender: postRenderCell},
2099
+ {text: 'Merge cells', onclick: cmd('mceTableMergeCells'), onPostRender: postRenderCell},
2100
+ {text: 'Split cell', onclick: cmd('mceTableSplitCells'), onPostRender: postRenderCell}
2101
+ ]
2102
+ });
2103
+
2104
+ editor.addMenuItem('row', {
2105
+ text: 'Row',
2106
+ context: 'table',
2107
+ menu: [
2108
+ {text: 'Insert row before', onclick: cmd('mceTableInsertRowBefore'), onPostRender: postRenderCell},
2109
+ {text: 'Insert row after', onclick: cmd('mceTableInsertRowAfter'), onPostRender: postRenderCell},
2110
+ {text: 'Delete row', onclick: cmd('mceTableDeleteRow'), onPostRender: postRenderCell},
2111
+ {text: 'Row properties', onclick: cmd('mceTableRowProps'), onPostRender: postRenderCell},
2112
+ {text: '-'},
2113
+ {text: 'Cut row', onclick: cmd('mceTableCutRow'), onPostRender: postRenderCell},
2114
+ {text: 'Copy row', onclick: cmd('mceTableCopyRow'), onPostRender: postRenderCell},
2115
+ {text: 'Paste row before', onclick: cmd('mceTablePasteRowBefore'), onPostRender: postRenderCell},
2116
+ {text: 'Paste row after', onclick: cmd('mceTablePasteRowAfter'), onPostRender: postRenderCell}
2117
+ ]
2118
+ });
2119
+
2120
+ editor.addMenuItem('column', {
2121
+ text: 'Column',
2122
+ context: 'table',
2123
+ menu: [
2124
+ {text: 'Insert column before', onclick: cmd('mceTableInsertColBefore'), onPostRender: postRenderCell},
2125
+ {text: 'Insert column after', onclick: cmd('mceTableInsertColAfter'), onPostRender: postRenderCell},
2126
+ {text: 'Delete column', onclick: cmd('mceTableDeleteCol'), onPostRender: postRenderCell}
2127
+ ]
2128
+ });
2129
+
2130
+ var menuItems = [];
2131
+ each("inserttable tableprops deletetable | cell row column".split(' '), function(name) {
2132
+ if (name == '|') {
2133
+ menuItems.push({text: '-'});
2134
+ } else {
2135
+ menuItems.push(editor.menuItems[name]);
2136
+ }
2137
+ });
2138
+
2139
+ editor.addButton("table", {
2140
+ type: "menubutton",
2141
+ title: "Table",
2142
+ menu: menuItems
2143
+ });
2144
+
2145
+ // Select whole table is a table border is clicked
2146
+ if (!Env.isIE) {
2147
+ editor.on('click', function(e) {
2148
+ e = e.target;
2149
+
2150
+ if (e.nodeName === 'TABLE') {
2151
+ editor.selection.select(e);
2152
+ editor.nodeChanged();
2153
+ }
2154
+ });
2155
+ }
2156
+
2157
+ self.quirks = new Quirks(editor);
2158
+
2159
+ editor.on('Init', function() {
2160
+ winMan = editor.windowManager;
2161
+ self.cellSelection = new CellSelection(editor);
2162
+ });
2163
+
2164
+ // Register action commands
2165
+ each({
2166
+ mceTableSplitCells: function(grid) {
2167
+ grid.split();
2168
+ },
2169
+
2170
+ mceTableMergeCells: function(grid) {
2171
+ var rowSpan, colSpan, cell;
2172
+
2173
+ cell = editor.dom.getParent(editor.selection.getStart(), 'th,td');
2174
+ if (cell) {
2175
+ rowSpan = cell.rowSpan;
2176
+ colSpan = cell.colSpan;
2177
+ }
2178
+
2179
+ if (!editor.dom.select('td.mce-item-selected,th.mce-item-selected').length) {
2180
+ mergeDialog(grid, cell);
2181
+ } else {
2182
+ grid.merge();
2183
+ }
2184
+ },
2185
+
2186
+ mceTableInsertRowBefore: function(grid) {
2187
+ grid.insertRow(true);
2188
+ },
2189
+
2190
+ mceTableInsertRowAfter: function(grid) {
2191
+ grid.insertRow();
2192
+ },
2193
+
2194
+ mceTableInsertColBefore: function(grid) {
2195
+ grid.insertCol(true);
2196
+ },
2197
+
2198
+ mceTableInsertColAfter: function(grid) {
2199
+ grid.insertCol();
2200
+ },
2201
+
2202
+ mceTableDeleteCol: function(grid) {
2203
+ grid.deleteCols();
2204
+ },
2205
+
2206
+ mceTableDeleteRow: function(grid) {
2207
+ grid.deleteRows();
2208
+ },
2209
+
2210
+ mceTableCutRow: function(grid) {
2211
+ clipboardRows = grid.cutRows();
2212
+ },
2213
+
2214
+ mceTableCopyRow: function(grid) {
2215
+ clipboardRows = grid.copyRows();
2216
+ },
2217
+
2218
+ mceTablePasteRowBefore: function(grid) {
2219
+ grid.pasteRows(clipboardRows, true);
2220
+ },
2221
+
2222
+ mceTablePasteRowAfter: function(grid) {
2223
+ grid.pasteRows(clipboardRows);
2224
+ },
2225
+
2226
+ mceTableDelete: function(grid) {
2227
+ grid.deleteTable();
2228
+ }
2229
+ }, function(func, name) {
2230
+ editor.addCommand(name, function() {
2231
+ var grid = new TableGrid(editor);
2232
+
2233
+ if (grid) {
2234
+ func(grid);
2235
+ editor.execCommand('mceRepaint');
2236
+ self.cellSelection.clear();
2237
+ }
2238
+ });
2239
+ });
2240
+
2241
+ // Register dialog commands
2242
+ each({
2243
+ mceInsertTable: function() {
2244
+ tableDialog();
2245
+ },
2246
+
2247
+ mceTableRowProps: rowDialog,
2248
+ mceTableCellProps: cellDialog
2249
+ }, function(func, name) {
2250
+ editor.addCommand(name, function(ui, val) {
2251
+ func(val);
2252
+ });
2253
+ });
2254
+ }
2255
+
2256
+ PluginManager.add('table', Plugin);
2257
+ });
2258
+
2259
+ expose(["tinymce/tableplugin/TableGrid","tinymce/tableplugin/Quirks","tinymce/tableplugin/CellSelection","tinymce/tableplugin/Plugin"]);
2260
+ })(this);
tinymce4-table/plugin.min.js ADDED
@@ -0,0 +1 @@
 
1
+ !function(e,t){"use strict";function n(e,t){for(var n,o=[],i=0;i<e.length;++i){if(n=l[e[i]]||r(e[i]),!n)throw"module definition dependecy not found: "+e[i];o.push(n)}t.apply(null,o)}function o(e,o,i){if("string"!=typeof e)throw"invalid module definition, module id must be defined and be a string";if(o===t)throw"invalid module definition, dependencies must be specified";if(i===t)throw"invalid module definition, definition function must be specified";n(o,function(){l[e]=i.apply(null,arguments)})}function i(e){return!!l[e]}function r(t){for(var n=e,o=t.split(/[.\/]/),i=0;i<o.length;++i){if(!n[o[i]])return;n=n[o[i]]}return n}function a(n){for(var o=0;o<n.length;o++){for(var i=e,r=n[o],a=r.split(/[.\/]/),s=0;s<a.length-1;++s)i[a[s]]===t&&(i[a[s]]={}),i=i[a[s]];i[a[a.length-1]]=l[r]}}var l={},s="tinymce/tableplugin/TableGrid",c="tinymce/util/Tools",d="tinymce/Env",u="tinymce/tableplugin/Quirks",f="tinymce/util/VK",m="tinymce/tableplugin/CellSelection",p="tinymce/dom/TreeWalker",g="tinymce/tableplugin/Plugin",h="tinymce/PluginManager";o(s,[c,d],function(e,n){function o(e,t){return parseInt(e.getAttribute(t)||1,10)}var i=e.each;return function(r,a){function l(){var e=0;A=[],i(["thead","tbody","tfoot"],function(t){var n=I.select("> "+t+" tr",a);i(n,function(n,r){r+=e,i(I.select("> td, > th",n),function(e,n){var i,a,l,s;if(A[r])for(;A[r][n];)n++;for(l=o(e,"rowspan"),s=o(e,"colspan"),a=r;r+l>a;a++)for(A[a]||(A[a]=[]),i=n;n+s>i;i++)A[a][i]={part:t,real:a==r&&i==n,elm:e,rowspan:l,colspan:s}})}),e+=n.length})}function s(e,t){return e=e.cloneNode(t),e.removeAttribute("id"),e}function c(e,t){var n;return n=A[t],n?n[e]:void 0}function d(e,t,n){e&&(n=parseInt(n,10),1===n?e.removeAttribute(t,1):e.setAttribute(t,n,1))}function u(e){return e&&(I.hasClass(e.elm,"mce-item-selected")||e==M)}function f(){var e=[];return i(a.rows,function(t){i(t.cells,function(n){return I.hasClass(n,"mce-item-selected")||M&&n==M.elm?(e.push(t),!1):void 0})}),e}function m(){var e=I.createRng();e.setStartAfter(a),e.setEndAfter(a),E.setRng(e),I.remove(a)}function p(t){var o,a={};return r.settings.table_clone_elements!==!1&&(a=e.makeMap((r.settings.table_clone_elements||"strong em b i span font h1 h2 h3 h4 h5 h6 p div").toUpperCase(),/[ ,]/)),e.walk(t,function(e){var r;return 3==e.nodeType?(i(I.getParents(e.parentNode,null,t).reverse(),function(e){a[e.nodeName]&&(e=s(e,!1),o?r&&r.appendChild(e):o=r=e,r=e)}),r&&(r.innerHTML=n.ie?"&nbsp;":'<br data-mce-bogus="1" />'),!1):void 0},"childNodes"),t=s(t,!1),d(t,"rowSpan",1),d(t,"colSpan",1),o?t.appendChild(o):n.ie||(t.innerHTML='<br data-mce-bogus="1" />'),t}function g(){var e=I.createRng(),t;return i(I.select("tr",a),function(e){0===e.cells.length&&I.remove(e)}),0===I.select("tr",a).length?(e.setStartBefore(a),e.setEndBefore(a),E.setRng(e),void I.remove(a)):(i(I.select("thead,tbody,tfoot",a),function(e){0===e.rows.length&&I.remove(e)}),l(),void(B&&(t=A[Math.min(A.length-1,B.y)],t&&(E.select(t[Math.min(t.length-1,B.x)].elm,!0),E.collapse(!0)))))}function h(e,t,n,o){var i,r,a,l,s;for(i=A[t][e].elm.parentNode,a=1;n>=a;a++)if(i=I.getNext(i,"tr")){for(r=e;r>=0;r--)if(s=A[t+a][r].elm,s.parentNode==i){for(l=1;o>=l;l++)I.insertAfter(p(s),s);break}if(-1==r)for(l=1;o>=l;l++)i.insertBefore(p(i.cells[0]),i.cells[0])}}function v(){i(A,function(e,t){i(e,function(e,n){var i,r,a;if(u(e)&&(e=e.elm,i=o(e,"colspan"),r=o(e,"rowspan"),i>1||r>1)){for(d(e,"rowSpan",1),d(e,"colSpan",1),a=0;i-1>a;a++)I.insertAfter(p(e),e);h(n,t,r-1,i)}})})}function b(t,n,o){var r,a,s,f,m,p,h,b,y,w,x;if(t?(r=S(t),a=r.x,s=r.y,f=a+(n-1),m=s+(o-1)):(B=_=null,i(A,function(e,t){i(e,function(e,n){u(e)&&(B||(B={x:n,y:t}),_={x:n,y:t})})}),B&&(a=B.x,s=B.y,f=_.x,m=_.y)),b=c(a,s),y=c(f,m),b&&y&&b.part==y.part){for(v(),l(),b=c(a,s).elm,d(b,"colSpan",f-a+1),d(b,"rowSpan",m-s+1),h=s;m>=h;h++)for(p=a;f>=p;p++)A[h]&&A[h][p]&&(t=A[h][p].elm,t!=b&&(w=e.grep(t.childNodes),i(w,function(e){b.appendChild(e)}),w.length&&(w=e.grep(b.childNodes),x=0,i(w,function(e){"BR"==e.nodeName&&I.getAttrib(e,"data-mce-bogus")&&x++<w.length-1&&b.removeChild(e)})),I.remove(t)));g()}}function y(e){var n,r,a,l,c,f,m,g,h;if(i(A,function(t,o){return i(t,function(t){return u(t)&&(t=t.elm,c=t.parentNode,f=s(c,!1),n=o,e)?!1:void 0}),e?!n:void 0}),n!==t){for(l=0;l<A[0].length;l++)if(A[n][l]&&(r=A[n][l].elm,r!=a)){if(e){if(n>0&&A[n-1][l]&&(g=A[n-1][l].elm,h=o(g,"rowSpan"),h>1)){d(g,"rowSpan",h+1);continue}}else if(h=o(r,"rowspan"),h>1){d(r,"rowSpan",h+1);continue}m=p(r),d(m,"colSpan",r.colSpan),f.appendChild(m),a=r}f.hasChildNodes()&&(e?c.parentNode.insertBefore(f,c):I.insertAfter(f,c))}}function w(e){var t,n;i(A,function(n){return i(n,function(n,o){return u(n)&&(t=o,e)?!1:void 0}),e?!t:void 0}),i(A,function(i,r){var a,l,s;i[t]&&(a=i[t].elm,a!=n&&(s=o(a,"colspan"),l=o(a,"rowspan"),1==s?e?(a.parentNode.insertBefore(p(a),a),h(t,r,l-1,s)):(I.insertAfter(p(a),a),h(t,r,l-1,s)):d(a,"colSpan",a.colSpan+1),n=a))})}function x(){var t=[];i(A,function(n){i(n,function(n,r){u(n)&&-1===e.inArray(t,r)&&(i(A,function(e){var t=e[r].elm,n;n=o(t,"colSpan"),n>1?d(t,"colSpan",n-1):I.remove(t)}),t.push(r))})}),g()}function C(){function e(e){var t,n,r;t=I.getNext(e,"tr"),i(e.cells,function(e){var t=o(e,"rowSpan");t>1&&(d(e,"rowSpan",t-1),n=S(e),h(n.x,n.y,1,1))}),n=S(e.cells[0]),i(A[n.y],function(e){var t;e=e.elm,e!=r&&(t=o(e,"rowSpan"),1>=t?I.remove(e):d(e,"rowSpan",t-1),r=e)})}var t;t=f(),i(t.reverse(),function(t){e(t)}),g()}function P(){var e=f();return I.remove(e),g(),e}function R(){var e=f();return i(e,function(t,n){e[n]=s(t,!0)}),e}function T(e,t){var n=f(),o=n[t?0:n.length-1],r=o.cells.length;e&&(i(A,function(e){var t;return r=0,i(e,function(e){e.real&&(r+=e.colspan),e.elm.parentNode==o&&(t=1)}),t?!1:void 0}),t||e.reverse(),i(e,function(e){var n,i=e.cells.length,a;for(n=0;i>n;n++)a=e.cells[n],d(a,"colSpan",1),d(a,"rowSpan",1);for(n=i;r>n;n++)e.appendChild(p(e.cells[i-1]));for(n=r;i>n;n++)I.remove(e.cells[n]);t?o.parentNode.insertBefore(e,o):I.insertAfter(e,o)}),I.removeClass(I.select("td.mce-item-selected,th.mce-item-selected"),"mce-item-selected"))}function S(e){var t;return i(A,function(n,o){return i(n,function(n,i){return n.elm==e?(t={x:i,y:o},!1):void 0}),!t}),t}function N(e){B=S(e)}function k(){var e,t;return e=t=0,i(A,function(n,o){i(n,function(n,i){var r,a;u(n)&&(n=A[o][i],i>e&&(e=i),o>t&&(t=o),n.real&&(r=n.colspan-1,a=n.rowspan-1,r&&i+r>e&&(e=i+r),a&&o+a>t&&(t=o+a)))})}),{x:e,y:t}}function D(e){var t,n,o,i,r,a,l,s,c,d;if(_=S(e),B&&_){for(t=Math.min(B.x,_.x),n=Math.min(B.y,_.y),o=Math.max(B.x,_.x),i=Math.max(B.y,_.y),r=o,a=i,d=n;a>=d;d++)e=A[d][t],e.real||t-(e.colspan-1)<t&&(t-=e.colspan-1);for(c=t;r>=c;c++)e=A[n][c],e.real||n-(e.rowspan-1)<n&&(n-=e.rowspan-1);for(d=n;i>=d;d++)for(c=t;o>=c;c++)e=A[d][c],e.real&&(l=e.colspan-1,s=e.rowspan-1,l&&c+l>r&&(r=c+l),s&&d+s>a&&(a=d+s));for(I.removeClass(I.select("td.mce-item-selected,th.mce-item-selected"),"mce-item-selected"),d=n;a>=d;d++)for(c=t;r>=c;c++)A[d][c]&&I.addClass(A[d][c].elm,"mce-item-selected")}}var A,B,_,M,E=r.selection,I=E.dom;a=a||I.getParent(E.getStart(),"table"),l(),M=I.getParent(E.getStart(),"th,td"),M&&(B=S(M),_=k(),M=c(B.x,B.y)),e.extend(this,{deleteTable:m,split:v,merge:b,insertRow:y,insertCol:w,deleteCols:x,deleteRows:C,cutRows:P,copyRows:R,pasteRows:T,getPos:S,setStartCell:N,setEndCell:D})}}),o(u,[f,d,c],function(e,t,n){function o(e,t){return parseInt(e.getAttribute(t)||1,10)}var i=n.each;return function(n){function r(){function t(t){function r(e,o){var i=e?"previousSibling":"nextSibling",r=n.dom.getParent(o,"tr"),l=r[i];if(l)return h(n,o,l,e),t.preventDefault(),!0;var d=n.dom.getParent(r,"table"),u=r.parentNode,f=u.nodeName.toLowerCase();if("tbody"===f||f===(e?"tfoot":"thead")){var m=a(e,d,u,"tbody");if(null!==m)return s(e,m,o)}return c(e,r,i,d)}function a(e,t,o,i){var r=n.dom.select(">"+i,t),a=r.indexOf(o);if(e&&0===a||!e&&a===r.length-1)return l(e,t);if(-1===a){var s="thead"===o.tagName.toLowerCase()?0:r.length-1;return r[s]}return r[a+(e?-1:1)]}function l(e,t){var o=e?"thead":"tfoot",i=n.dom.select(">"+o,t);return 0!==i.length?i[0]:null}function s(e,o,i){var r=d(o,e);return r&&h(n,i,r,e),t.preventDefault(),!0}function c(e,o,i,a){var l=a[i];if(l)return u(l),!0;var s=n.dom.getParent(a,"td,th");if(s)return r(e,s,t);var c=d(o,!e);return u(c),t.preventDefault(),!1}function d(e,t){var o=e&&e[t?"lastChild":"firstChild"];return o&&"BR"===o.nodeName?n.dom.getParent(o,"td,th"):o}function u(e){n.selection.setCursorLocation(e,0)}function f(){return y==e.UP||y==e.DOWN}function m(e){var t=e.selection.getNode(),n=e.dom.getParent(t,"tr");return null!==n}function p(e){for(var t=0,n=e;n.previousSibling;)n=n.previousSibling,t+=o(n,"colspan");return t}function g(e,t){var n=0,r=0;return i(e.children,function(e,i){return n+=o(e,"colspan"),r=i,n>t?!1:void 0}),r}function h(e,t,o,i){var r=p(n.dom.getParent(t,"td,th")),a=g(o,r),l=o.childNodes[a],s=d(l,i);u(s||l)}function v(e){var t=n.selection.getNode(),o=n.dom.getParent(t,"td,th"),i=n.dom.getParent(e,"td,th");return o&&o!==i&&b(o,i)}function b(e,t){return n.dom.getParent(e,"TABLE")===n.dom.getParent(t,"TABLE")}var y=t.keyCode;if(f()&&m(n)){var w=n.selection.getNode();setTimeout(function(){v(w)&&r(!t.shiftKey&&y===e.UP,w,t)},0)}}n.on("KeyDown",function(e){t(e)})}function a(){function e(e,t){var n=t.ownerDocument,o=n.createRange(),i;return o.setStartBefore(t),o.setEnd(e.endContainer,e.endOffset),i=n.createElement("body"),i.appendChild(o.cloneContents()),0===i.innerHTML.replace(/<(br|img|object|embed|input|textarea)[^>]*>/gi,"-").replace(/<[^>]+>/g,"").length}n.on("KeyDown",function(t){var o,i,r=n.dom;(37==t.keyCode||38==t.keyCode)&&(o=n.selection.getRng(),i=r.getParent(o.startContainer,"table"),i&&n.getBody().firstChild==i&&e(o,i)&&(o=r.createRng(),o.setStartBefore(i),o.setEndBefore(i),n.selection.setRng(o),t.preventDefault()))})}function l(){n.on("KeyDown SetContent VisualAid",function(){var e;for(e=n.getBody().lastChild;e;e=e.previousSibling)if(3==e.nodeType){if(e.nodeValue.length>0)break}else if(1==e.nodeType&&!e.getAttribute("data-mce-bogus"))break;e&&"TABLE"==e.nodeName&&(n.settings.forced_root_block?n.dom.add(n.getBody(),n.settings.forced_root_block,n.settings.forced_root_block_attrs,t.ie&&t.ie<11?"&nbsp;":'<br data-mce-bogus="1" />'):n.dom.add(n.getBody(),"br",{"data-mce-bogus":"1"}))}),n.on("PreProcess",function(e){var t=e.node.lastChild;t&&("BR"==t.nodeName||1==t.childNodes.length&&("BR"==t.firstChild.nodeName||"\xa0"==t.firstChild.nodeValue))&&t.previousSibling&&"TABLE"==t.previousSibling.nodeName&&n.dom.remove(t)})}function s(){function e(e,t,n,o){var i=3,r=e.dom.getParent(t.startContainer,"TABLE"),a,l,s;return r&&(a=r.parentNode),l=t.startContainer.nodeType==i&&0===t.startOffset&&0===t.endOffset&&o&&("TR"==n.nodeName||n==a),s=("TD"==n.nodeName||"TH"==n.nodeName)&&!o,l||s}function t(){var t=n.selection.getRng(),o=n.selection.getNode(),i=n.dom.getParent(t.startContainer,"TD,TH");if(e(n,t,o,i)){i||(i=o);for(var r=i.lastChild;r.lastChild;)r=r.lastChild;t.setEnd(r,r.nodeValue.length),n.selection.setRng(t)}}n.on("KeyDown",function(){t()}),n.on("MouseDown",function(e){2!=e.button&&t()})}function c(){n.on("keydown",function(t){if((t.keyCode==e.DELETE||t.keyCode==e.BACKSPACE)&&!t.isDefaultPrevented()){var o=n.dom.getParent(n.selection.getStart(),"table");if(o){for(var i=n.dom.select("td,th",o),r=i.length;r--;)if(!n.dom.hasClass(i[r],"mce-item-selected"))return;t.preventDefault(),n.execCommand("mceTableDelete")}}})}c(),t.webkit&&(r(),s()),t.gecko&&(a(),l()),t.ie>10&&(a(),l())}}),o(m,[s,p,c],function(e,t,n){return function(o){function i(){o.getBody().style.webkitUserSelect="",d&&(o.dom.removeClass(o.dom.select("td.mce-item-selected,th.mce-item-selected"),"mce-item-selected"),d=!1)}function r(t){var n,i,r=t.target;if(s&&(l||r!=s)&&("TD"==r.nodeName||"TH"==r.nodeName)){i=a.getParent(r,"table"),i==c&&(l||(l=new e(o,i),l.setStartCell(s),o.getBody().style.webkitUserSelect="none"),l.setEndCell(r),d=!0),n=o.selection.getSel();try{n.removeAllRanges?n.removeAllRanges():n.empty()}catch(u){}t.preventDefault()}}var a=o.dom,l,s,c,d=!0;return o.on("MouseDown",function(e){2!=e.button&&(i(),s=a.getParent(e.target,"td,th"),c=a.getParent(s,"table"))}),o.on("mouseover",r),o.on("remove",function(){a.unbind(o.getDoc(),"mouseover",r)}),o.on("MouseUp",function(){function e(e,o){var r=new t(e,e);do{if(3==e.nodeType&&0!==n.trim(e.nodeValue).length)return void(o?i.setStart(e,0):i.setEnd(e,e.nodeValue.length));if("BR"==e.nodeName)return void(o?i.setStartBefore(e):i.setEndBefore(e))}while(e=o?r.next():r.prev())}var i,r=o.selection,d,u,f,m,p;if(s){if(l&&(o.getBody().style.webkitUserSelect=""),d=a.select("td.mce-item-selected,th.mce-item-selected"),d.length>0){i=a.createRng(),f=d[0],p=d[d.length-1],i.setStartBefore(f),i.setEndAfter(f),e(f,1),u=new t(f,a.getParent(d[0],"table"));do if("TD"==f.nodeName||"TH"==f.nodeName){if(!a.hasClass(f,"mce-item-selected"))break;m=f}while(f=u.next());e(m),r.setRng(i)}o.nodeChanged(),s=l=c=null}}),o.on("KeyUp",function(){i()}),{clear:i}}}),o(g,[s,u,m,c,p,d,h],function(e,t,n,o,i,r,a){function l(o){function i(e){return e?e.replace(/px$/,""):""}function a(e){return/^[0-9]+$/.test(e)&&(e+="px"),e}function l(e){s("left center right".split(" "),function(t){o.formatter.remove("align"+t,{},e)})}function c(){var e=o.dom,t,n;t=e.getParent(o.selection.getStart(),"table"),n={width:i(e.getStyle(t,"width")||e.getAttrib(t,"width")),height:i(e.getStyle(t,"height")||e.getAttrib(t,"height")),cellspacing:e.getAttrib(t,"cellspacing"),cellpadding:e.getAttrib(t,"cellpadding"),border:e.getAttrib(t,"border"),caption:!!e.select("caption",t)[0]},s("left center right".split(" "),function(e){o.formatter.matchNode(t,"align"+e)&&(n.align=e)}),o.windowManager.open({title:"Table properties",items:{type:"form",layout:"grid",columns:2,data:n,defaults:{type:"textbox",maxWidth:50},items:[{label:"Width",name:"width"},{label:"Height",name:"height"},{label:"Cell spacing",name:"cellspacing"},{label:"Cell padding",name:"cellpadding"},{label:"Border",name:"border"},{label:"Caption",name:"caption",type:"checkbox"},{label:"Alignment",minWidth:90,name:"align",type:"listbox",text:"None",maxWidth:null,values:[{text:"None",value:""},{text:"Left",value:"left"},{text:"Center",value:"center"},{text:"Right",value:"right"}]}]},onsubmit:function(){var n=this.toJSON(),i;o.undoManager.transact(function(){o.dom.setAttribs(t,{cellspacing:n.cellspacing,cellpadding:n.cellpadding,border:n.border}),o.dom.setStyles(t,{width:a(n.width),height:a(n.height)}),i=e.select("caption",t)[0],i&&!n.caption&&e.remove(i),!i&&n.caption&&(i=e.create("caption"),i.innerHTML=r.ie?"\xa0":'<br data-mce-bogus="1"/>',t.insertBefore(i,t.firstChild)),l(t),n.align&&o.formatter.apply("align"+n.align,{},t),o.focus(),o.addVisual()})}})}function d(e,t){o.windowManager.open({title:"Merge cells",body:[{label:"Cols",name:"cols",type:"textbox",size:10},{label:"Rows",name:"rows",type:"textbox",size:10}],onsubmit:function(){var n=this.toJSON();o.undoManager.transact(function(){e.merge(t,n.cols,n.rows)})}})}function u(){var e=o.dom,t,n,r=[];r=o.dom.select("td.mce-item-selected,th.mce-item-selected"),t=o.dom.getParent(o.selection.getStart(),"td,th"),!r.length&&t&&r.push(t),t=t||r[0],t&&(n={width:i(e.getStyle(t,"width")||e.getAttrib(t,"width")),height:i(e.getStyle(t,"height")||e.getAttrib(t,"height")),scope:e.getAttrib(t,"scope")},n.type=t.nodeName.toLowerCase(),s("left center right".split(" "),function(e){o.formatter.matchNode(t,"align"+e)&&(n.align=e)}),o.windowManager.open({title:"Cell properties",items:{type:"form",data:n,layout:"grid",columns:2,defaults:{type:"textbox",maxWidth:50},items:[{label:"Width",name:"width"},{label:"Height",name:"height"},{label:"Cell type",name:"type",type:"listbox",text:"None",minWidth:90,maxWidth:null,values:[{text:"Cell",value:"td"},{text:"Header cell",value:"th"}]},{label:"Scope",name:"scope",type:"listbox",text:"None",minWidth:90,maxWidth:null,values:[{text:"None",value:""},{text:"Row",value:"row"},{text:"Column",value:"col"},{text:"Row group",value:"rowgroup"},{text:"Column group",value:"colgroup"}]},{label:"Alignment",name:"align",type:"listbox",text:"None",minWidth:90,maxWidth:null,values:[{text:"None",value:""},{text:"Left",value:"left"},{text:"Center",value:"center"},{text:"Right",value:"right"}]}]},onsubmit:function(){var t=this.toJSON();o.undoManager.transact(function(){s(r,function(n){o.dom.setAttrib(n,"scope",t.scope),o.dom.setStyles(n,{width:a(t.width),height:a(t.height)}),t.type&&n.nodeName.toLowerCase()!=t.type&&(n=e.rename(n,t.type)),l(n),t.align&&o.formatter.apply("align"+t.align,{},n)}),o.focus()})}}))}function f(){var e=o.dom,t,n,r,c,d=[];t=o.dom.getParent(o.selection.getStart(),"table"),n=o.dom.getParent(o.selection.getStart(),"td,th"),s(t.rows,function(t){s(t.cells,function(o){return e.hasClass(o,"mce-item-selected")||o==n?(d.push(t),!1):void 0})}),r=d[0],r&&(c={height:i(e.getStyle(r,"height")||e.getAttrib(r,"height")),scope:e.getAttrib(r,"scope")},c.type=r.parentNode.nodeName.toLowerCase(),s("left center right".split(" "),function(e){o.formatter.matchNode(r,"align"+e)&&(c.align=e)}),o.windowManager.open({title:"Row properties",items:{type:"form",data:c,columns:2,defaults:{type:"textbox"},items:[{type:"listbox",name:"type",label:"Row type",text:"None",maxWidth:null,values:[{text:"Header",value:"thead"},{text:"Body",value:"tbody"},{text:"Footer",value:"tfoot"}]},{type:"listbox",name:"align",label:"Alignment",text:"None",maxWidth:null,values:[{text:"None",value:""},{text:"Left",value:"left"},{text:"Center",value:"center"},{text:"Right",value:"right"}]},{label:"Height",name:"height"}]},onsubmit:function(){var t=this.toJSON(),n,i,r;o.undoManager.transact(function(){var c=t.type;s(d,function(s){o.dom.setAttrib(s,"scope",t.scope),o.dom.setStyles(s,{height:a(t.height)}),c!=s.parentNode.nodeName.toLowerCase()&&(n=e.getParent(s,"table"),i=s.parentNode,r=e.select(c,n)[0],r||(r=e.create(c),n.firstChild?n.insertBefore(r,n.firstChild):n.appendChild(r)),r.appendChild(s),i.hasChildNodes()||e.remove(i)),l(s),t.align&&o.formatter.apply("align"+t.align,{},s)}),o.focus()})}}))}function m(e){return function(){o.execCommand(e)}}function p(e,t){var n,i,a;for(a="<table><tbody>",n=0;t>n;n++){for(a+="<tr>",i=0;e>i;i++)a+="<td>"+(r.ie?" ":"<br>")+"</td>";a+="</tr>"}a+="</tbody></table>",o.insertContent(a)}function g(e,t){function n(){e.disabled(!o.dom.getParent(o.selection.getStart(),t)),o.selection.selectorChanged(t,function(t){e.disabled(!t)})}o.initialized?n():o.on("init",n)}function h(){g(this,"table")}function v(){g(this,"td,th")}function b(){var e="";e='<table role="grid" class="mce-grid mce-grid-border" aria-readonly="true">';for(var t=0;10>t;t++){e+="<tr>";for(var n=0;10>n;n++)e+='<td role="gridcell" tabindex="-1"><a id="mcegrid'+(10*t+n)+'" href="#" data-mce-x="'+n+'" data-mce-y="'+t+'" '+(n+t===0?' class="mce-active"':"")+"></a></td>";e+="</tr>"}return e+="</table>",e+='<div class="mce-text-center" role="presentation">1 x 1</div>'}function y(e,t,n){var i=n.getEl().getElementsByTagName("table")[0],r=n.parent().rel,a,l,s,c;if(n.isRtl()||"tl-tr"==r){for(l=9;l>=0;l--)for(a=0;10>a;a++)c=i.rows[l].childNodes[a].firstChild,o.dom.toggleClass(c,"mce-active",a>=e&&t>=l),a>=e&&t>=l&&(s=c);e=9-e,i.nextSibling.innerHTML=e+" x "+(t+1)}else{for(l=0;10>l;l++)for(a=0;10>a;a++)c=i.rows[l].childNodes[a].firstChild,o.dom.toggleClass(c,"mce-active",e>=a&&t>=l),e>=a&&t>=l&&(s=c);i.nextSibling.innerHTML=e+1+" x "+(t+1)}return s.parentNode}var w,x,C=this;o.addMenuItem("inserttable",{text:"Insert table",icon:"table",context:"table",onhide:function(){var e=this.menu.items()[0].getEl().getElementsByTagName("a");o.dom.removeClass(e,"mce-active"),o.dom.addClass(e[0],"mce-active")},menu:[{type:"container",html:b(),onPostRender:function(){this.lastX=this.lastY=0},onmousemove:function(e){var t=e.target,n,o;"A"==t.nodeName&&(n=parseInt(t.getAttribute("data-mce-x"),10),o=parseInt(t.getAttribute("data-mce-y"),10),(n!==this.lastX||o!==this.lastY)&&(y(n,o,e.control),this.lastX=n,this.lastY=o))},onkeydown:function(e){var t=this.lastX,n=this.lastY,o;switch(e.keyCode){case 37:t>0&&(t--,o=!0);break;case 39:o=!0,9>t&&t++;break;case 38:o=!0,n>0&&n--;break;case 40:o=!0,9>n&&n++}o&&(e.preventDefault(),e.stopPropagation(),y(t,n,e.control).focus(),this.lastX=t,this.lastY=n)},onclick:function(e){"A"==e.target.nodeName&&(e.preventDefault(),e.stopPropagation(),this.parent().cancel(),p(this.lastX+1,this.lastY+1))}}]}),o.addMenuItem("tableprops",{text:"Table properties",context:"table",onPostRender:h,onclick:c}),o.addMenuItem("deletetable",{text:"Delete table",context:"table",onPostRender:h,cmd:"mceTableDelete"}),o.addMenuItem("cell",{separator:"before",text:"Cell",context:"table",menu:[{text:"Cell properties",onclick:m("mceTableCellProps"),onPostRender:v},{text:"Merge cells",onclick:m("mceTableMergeCells"),onPostRender:v},{text:"Split cell",onclick:m("mceTableSplitCells"),onPostRender:v}]}),o.addMenuItem("row",{text:"Row",context:"table",menu:[{text:"Insert row before",onclick:m("mceTableInsertRowBefore"),onPostRender:v},{text:"Insert row after",onclick:m("mceTableInsertRowAfter"),onPostRender:v},{text:"Delete row",onclick:m("mceTableDeleteRow"),onPostRender:v},{text:"Row properties",onclick:m("mceTableRowProps"),onPostRender:v},{text:"-"},{text:"Cut row",onclick:m("mceTableCutRow"),onPostRender:v},{text:"Copy row",onclick:m("mceTableCopyRow"),onPostRender:v},{text:"Paste row before",onclick:m("mceTablePasteRowBefore"),onPostRender:v},{text:"Paste row after",onclick:m("mceTablePasteRowAfter"),onPostRender:v}]}),o.addMenuItem("column",{text:"Column",context:"table",menu:[{text:"Insert column before",onclick:m("mceTableInsertColBefore"),onPostRender:v},{text:"Insert column after",onclick:m("mceTableInsertColAfter"),onPostRender:v},{text:"Delete column",onclick:m("mceTableDeleteCol"),onPostRender:v}]});var P=[];s("inserttable tableprops deletetable | cell row column".split(" "),function(e){P.push("|"==e?{text:"-"}:o.menuItems[e])}),o.addButton("table",{type:"menubutton",title:"Table",menu:P}),r.isIE||o.on("click",function(e){e=e.target,"TABLE"===e.nodeName&&(o.selection.select(e),o.nodeChanged())}),C.quirks=new t(o),o.on("Init",function(){w=o.windowManager,C.cellSelection=new n(o)}),s({mceTableSplitCells:function(e){e.split()},mceTableMergeCells:function(e){var t,n,i;i=o.dom.getParent(o.selection.getStart(),"th,td"),i&&(t=i.rowSpan,n=i.colSpan),o.dom.select("td.mce-item-selected,th.mce-item-selected").length?e.merge():d(e,i)},mceTableInsertRowBefore:function(e){e.insertRow(!0)},mceTableInsertRowAfter:function(e){e.insertRow()},mceTableInsertColBefore:function(e){e.insertCol(!0)},mceTableInsertColAfter:function(e){e.insertCol()},mceTableDeleteCol:function(e){e.deleteCols()},mceTableDeleteRow:function(e){e.deleteRows()},mceTableCutRow:function(e){x=e.cutRows()},mceTableCopyRow:function(e){x=e.copyRows()},mceTablePasteRowBefore:function(e){e.pasteRows(x,!0)},mceTablePasteRowAfter:function(e){e.pasteRows(x)},mceTableDelete:function(e){e.deleteTable()}},function(t,n){o.addCommand(n,function(){var n=new e(o);n&&(t(n),o.execCommand("mceRepaint"),C.cellSelection.clear())})}),s({mceInsertTable:function(){c()},mceTableRowProps:f,mceTableCellProps:u},function(e,t){o.addCommand(t,function(t,n){e(n)})})}var s=o.each;a.add("table",l)}),a([s,u,m,g])}(this);