mosaico 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (425) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +8 -0
  3. data/Gemfile +7 -0
  4. data/LICENSE +680 -0
  5. data/README.md +179 -0
  6. data/Rakefile +5 -0
  7. data/app/assets/images/mosaico/logo_transparent.png +0 -0
  8. data/app/assets/javascripts/mosaico/application.js.erb +51 -0
  9. data/app/assets/javascripts/mosaico/plugins/debug.js.erb +10 -0
  10. data/app/assets/javascripts/mosaico/plugins/logo.js.erb +10 -0
  11. data/app/assets/javascripts/mosaico/plugins/preview.js +85 -0
  12. data/app/assets/javascripts/mosaico/plugins/save.js.erb +72 -0
  13. data/app/assets/javascripts/mosaico/plugins/template_path.js.erb +22 -0
  14. data/app/assets/javascripts/mosaico/plugins/translations.js +10 -0
  15. data/app/assets/javascripts/mosaico/utilities.js +25 -0
  16. data/app/assets/stylesheets/mosaico/application.css +6 -0
  17. data/app/controllers/mosaico/application_controller.rb +17 -0
  18. data/app/controllers/mosaico/images_controller.rb +142 -0
  19. data/app/controllers/mosaico/projects_controller.rb +31 -0
  20. data/app/controllers/mosaico/templates_controller.rb +8 -0
  21. data/app/helpers/mosaico/application_helper.rb +7 -0
  22. data/app/models/mosaico/image.rb +9 -0
  23. data/app/models/mosaico/placeholder_image.rb +9 -0
  24. data/app/models/mosaico/project.rb +13 -0
  25. data/app/models/mosaico/uploaded_image.rb +26 -0
  26. data/app/views/layouts/mosaico/application.html.erb +13 -0
  27. data/app/views/mosaico/projects/new.html.erb +1 -0
  28. data/app/views/mosaico/projects/show.html.erb +1 -0
  29. data/app/views/mosaico/shared/_metadata.html.erb +12 -0
  30. data/config/routes.rb +12 -0
  31. data/db/migrate/20170817202255_add_images.rb +14 -0
  32. data/db/migrate/20170824233755_add_projects.rb +11 -0
  33. data/lib/mosaico.rb +100 -0
  34. data/lib/mosaico/engine.rb +72 -0
  35. data/lib/mosaico/local_backend.rb +23 -0
  36. data/lib/mosaico/local_image_backend.rb +7 -0
  37. data/lib/mosaico/local_placeholder_backend.rb +7 -0
  38. data/lib/mosaico/placeholder.png +0 -0
  39. data/lib/mosaico/template.rb +89 -0
  40. data/lib/mosaico/versafix_template.rb +43 -0
  41. data/lib/mosaico/version.rb +4 -0
  42. data/lib/tasks/makeThumbs.js.erb +57 -0
  43. data/lib/tasks/thumbs.rake +35 -0
  44. data/mosaico.gemspec +22 -0
  45. data/screenshot.png +0 -0
  46. data/vendor/assets/fonts/LiberationSans-Regular.ttf +0 -0
  47. data/vendor/assets/mosaico-0.16.0/mosaico/.jsbeautifyrc +6 -0
  48. data/vendor/assets/mosaico-0.16.0/mosaico/.jshintrc +8 -0
  49. data/vendor/assets/mosaico-0.16.0/mosaico/.travis.yml +6 -0
  50. data/vendor/assets/mosaico-0.16.0/mosaico/CONTRIBUTING.md +37 -0
  51. data/vendor/assets/mosaico-0.16.0/mosaico/Dockerfile +21 -0
  52. data/vendor/assets/mosaico-0.16.0/mosaico/Gruntfile.js +241 -0
  53. data/vendor/assets/mosaico-0.16.0/mosaico/LICENSE +680 -0
  54. data/vendor/assets/mosaico-0.16.0/mosaico/NOTICE.txt +87 -0
  55. data/vendor/assets/mosaico-0.16.0/mosaico/README.md +73 -0
  56. data/vendor/assets/mosaico-0.16.0/mosaico/backend/README.txt +23 -0
  57. data/vendor/assets/mosaico-0.16.0/mosaico/backend/main.js +162 -0
  58. data/vendor/assets/mosaico-0.16.0/mosaico/bower.json +76 -0
  59. data/vendor/assets/mosaico-0.16.0/mosaico/build/mosaico-material.css +7425 -0
  60. data/vendor/assets/mosaico-0.16.0/mosaico/build/mosaico.css +7133 -0
  61. data/vendor/assets/mosaico-0.16.0/mosaico/build/mosaico.debug.js +1517 -0
  62. data/vendor/assets/mosaico-0.16.0/mosaico/build/mosaico.js +12828 -0
  63. data/vendor/assets/mosaico-0.16.0/mosaico/build/templates.js +15 -0
  64. data/vendor/assets/mosaico-0.16.0/mosaico/dist/fa/fonts/fontawesome-webfont.eot +0 -0
  65. data/vendor/assets/mosaico-0.16.0/mosaico/dist/fa/fonts/fontawesome-webfont.svg +685 -0
  66. data/vendor/assets/mosaico-0.16.0/mosaico/dist/fa/fonts/fontawesome-webfont.ttf +0 -0
  67. data/vendor/assets/mosaico-0.16.0/mosaico/dist/fa/fonts/fontawesome-webfont.woff +0 -0
  68. data/vendor/assets/mosaico-0.16.0/mosaico/dist/fa/fonts/fontawesome-webfont.woff2 +0 -0
  69. data/vendor/assets/mosaico-0.16.0/mosaico/dist/img/byvoxmail.png +0 -0
  70. data/vendor/assets/mosaico-0.16.0/mosaico/dist/img/mosaico-badge.gif +0 -0
  71. data/vendor/assets/mosaico-0.16.0/mosaico/dist/img/mosaico-v.gif +0 -0
  72. data/vendor/assets/mosaico-0.16.0/mosaico/dist/img/mosaico32.png +0 -0
  73. data/vendor/assets/mosaico-0.16.0/mosaico/dist/img/mosaicologo.png +0 -0
  74. data/vendor/assets/mosaico-0.16.0/mosaico/dist/img/screenshot-orig.png +0 -0
  75. data/vendor/assets/mosaico-0.16.0/mosaico/dist/img/screenshot.png +0 -0
  76. data/vendor/assets/mosaico-0.16.0/mosaico/dist/lang/README.md +26 -0
  77. data/vendor/assets/mosaico-0.16.0/mosaico/dist/lang/mosaico-de.json +88 -0
  78. data/vendor/assets/mosaico-0.16.0/mosaico/dist/lang/mosaico-en.json +88 -0
  79. data/vendor/assets/mosaico-0.16.0/mosaico/dist/lang/mosaico-es.json +88 -0
  80. data/vendor/assets/mosaico-0.16.0/mosaico/dist/lang/mosaico-fr.json +88 -0
  81. data/vendor/assets/mosaico-0.16.0/mosaico/dist/lang/mosaico-it.json +88 -0
  82. data/vendor/assets/mosaico-0.16.0/mosaico/dist/lang/mosaico-nl.json +88 -0
  83. data/vendor/assets/mosaico-0.16.0/mosaico/dist/lang/mosaico-sv.json +88 -0
  84. data/vendor/assets/mosaico-0.16.0/mosaico/dist/mosaico-material.min.css +5 -0
  85. data/vendor/assets/mosaico-0.16.0/mosaico/dist/mosaico-material.min.css.map +1 -0
  86. data/vendor/assets/mosaico-0.16.0/mosaico/dist/mosaico.min.css +5 -0
  87. data/vendor/assets/mosaico-0.16.0/mosaico/dist/mosaico.min.css.map +1 -0
  88. data/vendor/assets/mosaico-0.16.0/mosaico/dist/mosaico.min.js +1517 -0
  89. data/vendor/assets/mosaico-0.16.0/mosaico/dist/mosaico.min.js.map +147 -0
  90. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/canvas-to-blob.min.js +2 -0
  91. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/jquery-ui.min.css +7 -0
  92. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/jquery-ui.min.js +13 -0
  93. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/jquery.fileupload-image.js +324 -0
  94. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/jquery.fileupload-process.js +175 -0
  95. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/jquery.fileupload-validate.js +122 -0
  96. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/jquery.fileupload.js +1482 -0
  97. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/jquery.iframe-transport.js +217 -0
  98. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/jquery.min.js +5 -0
  99. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/jquery.min.map +1 -0
  100. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/jquery.ui.touch-punch.min.js +11 -0
  101. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/knockout-jqueryui.min.js +1 -0
  102. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/knockout.js +123 -0
  103. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/load-image.all.min.js +2 -0
  104. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/notoregular/NotoSans-Regular-webfont.eot +0 -0
  105. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/notoregular/NotoSans-Regular-webfont.ttf +0 -0
  106. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/notoregular/NotoSans-Regular-webfont.woff +0 -0
  107. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/notoregular/stylesheet.css +9 -0
  108. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/advlist/plugin.js +101 -0
  109. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/advlist/plugin.min.js +1 -0
  110. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/anchor/plugin.js +55 -0
  111. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/anchor/plugin.min.js +1 -0
  112. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/autolink/plugin.js +204 -0
  113. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/autolink/plugin.min.js +1 -0
  114. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/autoresize/plugin.js +162 -0
  115. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/autoresize/plugin.min.js +1 -0
  116. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/autosave/plugin.js +165 -0
  117. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/autosave/plugin.min.js +1 -0
  118. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/bbcode/plugin.js +123 -0
  119. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/bbcode/plugin.min.js +1 -0
  120. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/charmap/plugin.js +462 -0
  121. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/charmap/plugin.min.js +1 -0
  122. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/code/plugin.js +60 -0
  123. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/code/plugin.min.js +1 -0
  124. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/codesample/css/prism.css +138 -0
  125. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/codesample/plugin.js +1319 -0
  126. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/codesample/plugin.min.js +1 -0
  127. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/colorpicker/plugin.js +112 -0
  128. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/colorpicker/plugin.min.js +1 -0
  129. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/contextmenu/plugin.js +100 -0
  130. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/contextmenu/plugin.min.js +1 -0
  131. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/directionality/plugin.js +64 -0
  132. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/directionality/plugin.min.js +1 -0
  133. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/emoticons/img/smiley-cool.gif +0 -0
  134. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/emoticons/img/smiley-cry.gif +0 -0
  135. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/emoticons/img/smiley-embarassed.gif +0 -0
  136. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/emoticons/img/smiley-foot-in-mouth.gif +0 -0
  137. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/emoticons/img/smiley-frown.gif +0 -0
  138. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/emoticons/img/smiley-innocent.gif +0 -0
  139. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/emoticons/img/smiley-kiss.gif +0 -0
  140. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/emoticons/img/smiley-laughing.gif +0 -0
  141. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/emoticons/img/smiley-money-mouth.gif +0 -0
  142. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/emoticons/img/smiley-sealed.gif +0 -0
  143. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/emoticons/img/smiley-smile.gif +0 -0
  144. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/emoticons/img/smiley-surprised.gif +0 -0
  145. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/emoticons/img/smiley-tongue-out.gif +0 -0
  146. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/emoticons/img/smiley-undecided.gif +0 -0
  147. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/emoticons/img/smiley-wink.gif +0 -0
  148. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/emoticons/img/smiley-yell.gif +0 -0
  149. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/emoticons/plugin.js +65 -0
  150. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/emoticons/plugin.min.js +1 -0
  151. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/fullpage/plugin.js +490 -0
  152. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/fullpage/plugin.min.js +1 -0
  153. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/fullscreen/plugin.js +154 -0
  154. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/fullscreen/plugin.min.js +1 -0
  155. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/hr/plugin.js +30 -0
  156. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/hr/plugin.min.js +1 -0
  157. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/image/plugin.js +630 -0
  158. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/image/plugin.min.js +1 -0
  159. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/imagetools/plugin.js +2944 -0
  160. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/imagetools/plugin.min.js +1 -0
  161. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/importcss/plugin.js +273 -0
  162. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/importcss/plugin.min.js +1 -0
  163. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/insertdatetime/plugin.js +121 -0
  164. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/insertdatetime/plugin.min.js +1 -0
  165. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/layer/plugin.js +225 -0
  166. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/layer/plugin.min.js +1 -0
  167. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/legacyoutput/plugin.js +208 -0
  168. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/legacyoutput/plugin.min.js +1 -0
  169. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/link/plugin.js +403 -0
  170. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/link/plugin.min.js +1 -0
  171. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/lists/plugin.js +965 -0
  172. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/lists/plugin.min.js +1 -0
  173. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/media/moxieplayer.swf +0 -0
  174. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/media/plugin.js +879 -0
  175. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/media/plugin.min.js +1 -0
  176. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/nonbreaking/plugin.js +53 -0
  177. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/nonbreaking/plugin.min.js +1 -0
  178. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/noneditable/plugin.js +101 -0
  179. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/noneditable/plugin.min.js +1 -0
  180. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/pagebreak/plugin.js +88 -0
  181. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/pagebreak/plugin.min.js +1 -0
  182. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/paste/plugin.js +1844 -0
  183. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/paste/plugin.min.js +1 -0
  184. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/preview/plugin.js +101 -0
  185. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/preview/plugin.min.js +1 -0
  186. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/print/plugin.js +32 -0
  187. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/print/plugin.min.js +1 -0
  188. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/save/plugin.js +98 -0
  189. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/save/plugin.min.js +1 -0
  190. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/searchreplace/plugin.js +609 -0
  191. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/searchreplace/plugin.min.js +1 -0
  192. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/spellchecker/plugin.js +1031 -0
  193. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/spellchecker/plugin.min.js +1 -0
  194. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/tabfocus/plugin.js +120 -0
  195. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/tabfocus/plugin.min.js +1 -0
  196. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/table/plugin.js +4400 -0
  197. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/table/plugin.min.js +2 -0
  198. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/template/plugin.js +276 -0
  199. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/template/plugin.min.js +1 -0
  200. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/textcolor/plugin.js +297 -0
  201. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/textcolor/plugin.min.js +1 -0
  202. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/textpattern/plugin.js +268 -0
  203. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/textpattern/plugin.min.js +1 -0
  204. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/visualblocks/css/visualblocks.css +135 -0
  205. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/visualblocks/plugin.js +86 -0
  206. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/visualblocks/plugin.min.js +1 -0
  207. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/visualchars/plugin.js +123 -0
  208. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/visualchars/plugin.min.js +1 -0
  209. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/wordcount/plugin.js +69 -0
  210. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/plugins/wordcount/plugin.min.js +1 -0
  211. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/gray-flat/Variables.less +196 -0
  212. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/gray-flat/content.inline.min.css +1 -0
  213. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/gray-flat/content.min.css +1 -0
  214. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/gray-flat/fonts/readme.md +1 -0
  215. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/gray-flat/fonts/tinymce-small.eot +0 -0
  216. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/gray-flat/fonts/tinymce-small.json +1277 -0
  217. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/gray-flat/fonts/tinymce-small.svg +63 -0
  218. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/gray-flat/fonts/tinymce-small.ttf +0 -0
  219. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/gray-flat/fonts/tinymce-small.woff +0 -0
  220. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/gray-flat/fonts/tinymce.eot +0 -0
  221. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/gray-flat/fonts/tinymce.json +1972 -0
  222. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/gray-flat/fonts/tinymce.svg +98 -0
  223. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/gray-flat/fonts/tinymce.ttf +0 -0
  224. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/gray-flat/fonts/tinymce.woff +0 -0
  225. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/gray-flat/img/anchor.gif +0 -0
  226. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/gray-flat/img/loader.gif +0 -0
  227. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/gray-flat/img/object.gif +0 -0
  228. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/gray-flat/img/trans.gif +0 -0
  229. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/gray-flat/skin.ie7.min.css +1 -0
  230. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/gray-flat/skin.json +79 -0
  231. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/gray-flat/skin.min.css +1 -0
  232. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/lightgray/content.inline.min.css +1 -0
  233. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/lightgray/content.min.css +1 -0
  234. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/lightgray/fonts/tinymce-small.eot +0 -0
  235. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/lightgray/fonts/tinymce-small.svg +63 -0
  236. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/lightgray/fonts/tinymce-small.ttf +0 -0
  237. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/lightgray/fonts/tinymce-small.woff +0 -0
  238. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/lightgray/fonts/tinymce.eot +0 -0
  239. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/lightgray/fonts/tinymce.svg +129 -0
  240. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/lightgray/fonts/tinymce.ttf +0 -0
  241. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/lightgray/fonts/tinymce.woff +0 -0
  242. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/lightgray/img/anchor.gif +0 -0
  243. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/lightgray/img/loader.gif +0 -0
  244. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/lightgray/img/object.gif +0 -0
  245. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/lightgray/img/trans.gif +0 -0
  246. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/lightgray/skin.ie7.min.css +1 -0
  247. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/skins/lightgray/skin.min.css +1 -0
  248. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/themes/inlite/theme.js +1828 -0
  249. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/themes/inlite/theme.min.js +1 -0
  250. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/themes/modern/theme.js +934 -0
  251. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/themes/modern/theme.min.js +1 -0
  252. data/vendor/assets/mosaico-0.16.0/mosaico/dist/vendor/tinymce.min.js +14 -0
  253. data/vendor/assets/mosaico-0.16.0/mosaico/editor.html +63 -0
  254. data/vendor/assets/mosaico-0.16.0/mosaico/favicon.ico +0 -0
  255. data/vendor/assets/mosaico-0.16.0/mosaico/index.html +368 -0
  256. data/vendor/assets/mosaico-0.16.0/mosaico/package.json +92 -0
  257. data/vendor/assets/mosaico-0.16.0/mosaico/res/img/byvoxmail.png +0 -0
  258. data/vendor/assets/mosaico-0.16.0/mosaico/res/img/mosaico-badge.gif +0 -0
  259. data/vendor/assets/mosaico-0.16.0/mosaico/res/img/mosaico-v.gif +0 -0
  260. data/vendor/assets/mosaico-0.16.0/mosaico/res/img/mosaico32.png +0 -0
  261. data/vendor/assets/mosaico-0.16.0/mosaico/res/img/mosaicologo.png +0 -0
  262. data/vendor/assets/mosaico-0.16.0/mosaico/res/img/screenshot-orig.png +0 -0
  263. data/vendor/assets/mosaico-0.16.0/mosaico/res/img/screenshot.png +0 -0
  264. data/vendor/assets/mosaico-0.16.0/mosaico/res/lang/README.md +26 -0
  265. data/vendor/assets/mosaico-0.16.0/mosaico/res/lang/mosaico-de.json +88 -0
  266. data/vendor/assets/mosaico-0.16.0/mosaico/res/lang/mosaico-en.json +88 -0
  267. data/vendor/assets/mosaico-0.16.0/mosaico/res/lang/mosaico-es.json +88 -0
  268. data/vendor/assets/mosaico-0.16.0/mosaico/res/lang/mosaico-fr.json +88 -0
  269. data/vendor/assets/mosaico-0.16.0/mosaico/res/lang/mosaico-it.json +88 -0
  270. data/vendor/assets/mosaico-0.16.0/mosaico/res/lang/mosaico-nl.json +88 -0
  271. data/vendor/assets/mosaico-0.16.0/mosaico/res/lang/mosaico-sv.json +88 -0
  272. data/vendor/assets/mosaico-0.16.0/mosaico/res/vendor/skins/gray-flat/Variables.less +196 -0
  273. data/vendor/assets/mosaico-0.16.0/mosaico/res/vendor/skins/gray-flat/content.inline.min.css +1 -0
  274. data/vendor/assets/mosaico-0.16.0/mosaico/res/vendor/skins/gray-flat/content.min.css +1 -0
  275. data/vendor/assets/mosaico-0.16.0/mosaico/res/vendor/skins/gray-flat/fonts/readme.md +1 -0
  276. data/vendor/assets/mosaico-0.16.0/mosaico/res/vendor/skins/gray-flat/fonts/tinymce-small.eot +0 -0
  277. data/vendor/assets/mosaico-0.16.0/mosaico/res/vendor/skins/gray-flat/fonts/tinymce-small.json +1277 -0
  278. data/vendor/assets/mosaico-0.16.0/mosaico/res/vendor/skins/gray-flat/fonts/tinymce-small.svg +63 -0
  279. data/vendor/assets/mosaico-0.16.0/mosaico/res/vendor/skins/gray-flat/fonts/tinymce-small.ttf +0 -0
  280. data/vendor/assets/mosaico-0.16.0/mosaico/res/vendor/skins/gray-flat/fonts/tinymce-small.woff +0 -0
  281. data/vendor/assets/mosaico-0.16.0/mosaico/res/vendor/skins/gray-flat/fonts/tinymce.eot +0 -0
  282. data/vendor/assets/mosaico-0.16.0/mosaico/res/vendor/skins/gray-flat/fonts/tinymce.json +1972 -0
  283. data/vendor/assets/mosaico-0.16.0/mosaico/res/vendor/skins/gray-flat/fonts/tinymce.svg +98 -0
  284. data/vendor/assets/mosaico-0.16.0/mosaico/res/vendor/skins/gray-flat/fonts/tinymce.ttf +0 -0
  285. data/vendor/assets/mosaico-0.16.0/mosaico/res/vendor/skins/gray-flat/fonts/tinymce.woff +0 -0
  286. data/vendor/assets/mosaico-0.16.0/mosaico/res/vendor/skins/gray-flat/img/anchor.gif +0 -0
  287. data/vendor/assets/mosaico-0.16.0/mosaico/res/vendor/skins/gray-flat/img/loader.gif +0 -0
  288. data/vendor/assets/mosaico-0.16.0/mosaico/res/vendor/skins/gray-flat/img/object.gif +0 -0
  289. data/vendor/assets/mosaico-0.16.0/mosaico/res/vendor/skins/gray-flat/img/trans.gif +0 -0
  290. data/vendor/assets/mosaico-0.16.0/mosaico/res/vendor/skins/gray-flat/skin.ie7.min.css +1 -0
  291. data/vendor/assets/mosaico-0.16.0/mosaico/res/vendor/skins/gray-flat/skin.json +79 -0
  292. data/vendor/assets/mosaico-0.16.0/mosaico/res/vendor/skins/gray-flat/skin.min.css +1 -0
  293. data/vendor/assets/mosaico-0.16.0/mosaico/server-config.js +12 -0
  294. data/vendor/assets/mosaico-0.16.0/mosaico/spec/converter-spec.js +115 -0
  295. data/vendor/assets/mosaico-0.16.0/mosaico/spec/data/template-versafix-1.model.json +137 -0
  296. data/vendor/assets/mosaico-0.16.0/mosaico/spec/declarations-spec.js +282 -0
  297. data/vendor/assets/mosaico-0.16.0/mosaico/spec/mensch-spec.js +114 -0
  298. data/vendor/assets/mosaico-0.16.0/mosaico/spec/model-spec.js +88 -0
  299. data/vendor/assets/mosaico-0.16.0/mosaico/spec/stylesheet-spec.js +177 -0
  300. data/vendor/assets/mosaico-0.16.0/mosaico/src/css/app_standalone.less +7 -0
  301. data/vendor/assets/mosaico-0.16.0/mosaico/src/css/app_standalone_material.less +350 -0
  302. data/vendor/assets/mosaico-0.16.0/mosaico/src/css/safarihack.css +17 -0
  303. data/vendor/assets/mosaico-0.16.0/mosaico/src/css/style_elements.less +497 -0
  304. data/vendor/assets/mosaico-0.16.0/mosaico/src/css/style_elements_jquery.less +344 -0
  305. data/vendor/assets/mosaico-0.16.0/mosaico/src/css/style_elements_mixins.less +406 -0
  306. data/vendor/assets/mosaico-0.16.0/mosaico/src/css/style_elements_moxie.less +130 -0
  307. data/vendor/assets/mosaico-0.16.0/mosaico/src/css/style_mosaico.less +529 -0
  308. data/vendor/assets/mosaico-0.16.0/mosaico/src/css/style_mosaico_content.less +391 -0
  309. data/vendor/assets/mosaico-0.16.0/mosaico/src/css/style_mosaico_tools.less +1043 -0
  310. data/vendor/assets/mosaico-0.16.0/mosaico/src/css/style_reset.less +45 -0
  311. data/vendor/assets/mosaico-0.16.0/mosaico/src/css/style_variables.less +156 -0
  312. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/app.js +167 -0
  313. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/bindings/bind-iframe.js +64 -0
  314. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/bindings/blocks.js +118 -0
  315. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/bindings/choose-template.js +7 -0
  316. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/bindings/colorpicker.js +58 -0
  317. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/bindings/csstext.js +16 -0
  318. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/bindings/droppable.js +87 -0
  319. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/bindings/eventable.js +33 -0
  320. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/bindings/extender-pagination.js +60 -0
  321. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/bindings/extsortables.js +103 -0
  322. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/bindings/fileupload.js +373 -0
  323. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/bindings/if-subs.js +110 -0
  324. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/bindings/jqueryui-spinner.js +31 -0
  325. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/bindings/jqueryui-tabs.js +17 -0
  326. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/bindings/script-template.js +30 -0
  327. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/bindings/scrollfix.js +48 -0
  328. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/bindings/scrollintoview.js +83 -0
  329. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/bindings/string-template.js +76 -0
  330. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/bindings/tooltips.js +36 -0
  331. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/bindings/validated-value.js +40 -0
  332. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/bindings/virtuals.js +102 -0
  333. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/bindings/wysiwygs.js +304 -0
  334. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/converter/checkmodel.js +114 -0
  335. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/converter/declarations.js +279 -0
  336. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/converter/domutils.js +93 -0
  337. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/converter/editor.js +411 -0
  338. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/converter/main.js +42 -0
  339. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/converter/model.js +536 -0
  340. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/converter/parser.js +485 -0
  341. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/converter/stylesheet.js +205 -0
  342. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/converter/utils.js +156 -0
  343. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/converter/wrapper.js +308 -0
  344. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/ext/color.js +53 -0
  345. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/ext/inliner.js +35 -0
  346. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/ext/localstorage.js +100 -0
  347. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/ko-bindings.js +22 -0
  348. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/template-loader.js +446 -0
  349. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/timed-call.js +31 -0
  350. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/undomanager/undomain.js +49 -0
  351. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/undomanager/undoserializer.js +120 -0
  352. data/vendor/assets/mosaico-0.16.0/mosaico/src/js/viewmodel.js +595 -0
  353. data/vendor/assets/mosaico-0.16.0/mosaico/src/tmpl/array.tmpl.html +1 -0
  354. data/vendor/assets/mosaico-0.16.0/mosaico/src/tmpl/block-show.tmpl.html +1 -0
  355. data/vendor/assets/mosaico-0.16.0/mosaico/src/tmpl/block-wysiwyg.tmpl.html +18 -0
  356. data/vendor/assets/mosaico-0.16.0/mosaico/src/tmpl/blocks-show.tmpl.html +1 -0
  357. data/vendor/assets/mosaico-0.16.0/mosaico/src/tmpl/blocks-wysiwyg.tmpl.html +2 -0
  358. data/vendor/assets/mosaico-0.16.0/mosaico/src/tmpl/customstyle.tmpl.html +3 -0
  359. data/vendor/assets/mosaico-0.16.0/mosaico/src/tmpl/empty.tmpl.html +0 -0
  360. data/vendor/assets/mosaico-0.16.0/mosaico/src/tmpl/error.tmpl.html +1 -0
  361. data/vendor/assets/mosaico-0.16.0/mosaico/src/tmpl/gallery-images.tmpl.html +16 -0
  362. data/vendor/assets/mosaico-0.16.0/mosaico/src/tmpl/img-wysiwyg.tmpl.html +43 -0
  363. data/vendor/assets/mosaico-0.16.0/mosaico/src/tmpl/main.tmpl.html +102 -0
  364. data/vendor/assets/mosaico-0.16.0/mosaico/src/tmpl/toolbox.tmpl.html +111 -0
  365. data/vendor/assets/mosaico-0.16.0/mosaico/tasks/combineKOTemplates.js +24 -0
  366. data/vendor/assets/mosaico-0.16.0/mosaico/tasks/lib/phantom-thumbnailer-editor.js +168 -0
  367. data/vendor/assets/mosaico-0.16.0/mosaico/tasks/makeThumbs.js +79 -0
  368. data/vendor/assets/mosaico-0.16.0/mosaico/templates/tedc15/README.md +1 -0
  369. data/vendor/assets/mosaico-0.16.0/mosaico/templates/tedc15/edres/_full.png +0 -0
  370. data/vendor/assets/mosaico-0.16.0/mosaico/templates/tedc15/edres/footerBlock.png +0 -0
  371. data/vendor/assets/mosaico-0.16.0/mosaico/templates/tedc15/edres/headerBlock.png +0 -0
  372. data/vendor/assets/mosaico-0.16.0/mosaico/templates/tedc15/edres/heroBlock.png +0 -0
  373. data/vendor/assets/mosaico-0.16.0/mosaico/templates/tedc15/edres/socialBlock.png +0 -0
  374. data/vendor/assets/mosaico-0.16.0/mosaico/templates/tedc15/edres/threetwoBlock.png +0 -0
  375. data/vendor/assets/mosaico-0.16.0/mosaico/templates/tedc15/edres/twoColumnBlock.png +0 -0
  376. data/vendor/assets/mosaico-0.16.0/mosaico/templates/tedc15/template-tedc15.html +567 -0
  377. data/vendor/assets/mosaico-0.16.0/mosaico/templates/tutorial/edres/HeaderAndText.png +0 -0
  378. data/vendor/assets/mosaico-0.16.0/mosaico/templates/tutorial/edres/_full.png +0 -0
  379. data/vendor/assets/mosaico-0.16.0/mosaico/templates/tutorial/edres/fixedlist.png +0 -0
  380. data/vendor/assets/mosaico-0.16.0/mosaico/templates/tutorial/mosaico-tutorial.md +335 -0
  381. data/vendor/assets/mosaico-0.16.0/mosaico/templates/tutorial/mosaico-tutorial.pdf +0 -0
  382. data/vendor/assets/mosaico-0.16.0/mosaico/templates/tutorial/screenshot_384.jpg +0 -0
  383. data/vendor/assets/mosaico-0.16.0/mosaico/templates/tutorial/screenshot_385.jpg +0 -0
  384. data/vendor/assets/mosaico-0.16.0/mosaico/templates/tutorial/screenshot_386.jpg +0 -0
  385. data/vendor/assets/mosaico-0.16.0/mosaico/templates/tutorial/screenshot_387.jpg +0 -0
  386. data/vendor/assets/mosaico-0.16.0/mosaico/templates/tutorial/screenshot_407.jpg +0 -0
  387. data/vendor/assets/mosaico-0.16.0/mosaico/templates/tutorial/screenshot_408.jpg +0 -0
  388. data/vendor/assets/mosaico-0.16.0/mosaico/templates/tutorial/template-tutorial.html +68 -0
  389. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/edres/_full.png +0 -0
  390. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/edres/buttonBlock.png +0 -0
  391. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/edres/doubleArticleBlock.png +0 -0
  392. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/edres/doubleImageBlock.png +0 -0
  393. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/edres/hrBlock.png +0 -0
  394. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/edres/imageBlock.png +0 -0
  395. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/edres/logoBlock.png +0 -0
  396. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/edres/sideArticleBlock.png +0 -0
  397. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/edres/singleArticleBlock.png +0 -0
  398. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/edres/socialBlock.png +0 -0
  399. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/edres/spacerBlock.png +0 -0
  400. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/edres/textBlock.png +0 -0
  401. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/edres/titleBlock.png +0 -0
  402. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/edres/tripleArticleBlock.png +0 -0
  403. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/edres/tripleImageBlock.png +0 -0
  404. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/img/social_def/facebook_bw_ok.png +0 -0
  405. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/img/social_def/facebook_ok.png +0 -0
  406. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/img/social_def/flickr_bw_ok.png +0 -0
  407. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/img/social_def/flickr_ok.png +0 -0
  408. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/img/social_def/google+_bw_ok.png +0 -0
  409. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/img/social_def/google+_ok.png +0 -0
  410. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/img/social_def/instagram_bw_ok.png +0 -0
  411. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/img/social_def/instagram_ok.png +0 -0
  412. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/img/social_def/linkedin_bw_ok.png +0 -0
  413. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/img/social_def/linkedin_ok.png +0 -0
  414. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/img/social_def/twitter_bw_ok.png +0 -0
  415. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/img/social_def/twitter_ok.png +0 -0
  416. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/img/social_def/vimeo_bw_ok.png +0 -0
  417. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/img/social_def/vimeo_ok.png +0 -0
  418. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/img/social_def/web_bw_ok.png +0 -0
  419. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/img/social_def/web_ok.png +0 -0
  420. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/img/social_def/youtube_bw_ok.png +0 -0
  421. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/img/social_def/youtube_ok.png +0 -0
  422. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/img/sponsor.gif +0 -0
  423. data/vendor/assets/mosaico-0.16.0/mosaico/templates/versafix-1/template-versafix-1.html +1531 -0
  424. data/vendor/assets/mosaico-0.16.0/mosaico/uploads/README.md +1 -0
  425. metadata +536 -0
@@ -0,0 +1 @@
1
+ !function(e,t){"use strict";function n(e,t){for(var n,r=[],o=0;o<e.length;++o){if(n=a[e[o]]||i(e[o]),!n)throw"module definition dependecy not found: "+e[o];r.push(n)}t.apply(null,r)}function r(e,r,i){if("string"!=typeof e)throw"invalid module definition, module id must be defined and be a string";if(r===t)throw"invalid module definition, dependencies must be specified";if(i===t)throw"invalid module definition, definition function must be specified";n(r,function(){a[e]=i.apply(null,arguments)})}function i(t){for(var n=e,r=t.split(/[.\/]/),i=0;i<r.length;++i){if(!n[r[i]])return;n=n[r[i]]}return n}function o(n){var r,i,o,s,l;for(r=0;r<n.length;r++){i=e,o=n[r],s=o.split(/[.\/]/);for(var c=0;c<s.length-1;++c)i[s[c]]===t&&(i[s[c]]={}),i=i[s[c]];i[s[s.length-1]]=a[o]}if(e.AMDLC_TESTS){l=e.privateModules||{};for(o in a)l[o]=a[o];for(r=0;r<n.length;r++)delete l[n[r]];e.privateModules=l}}var a={};r("tinymce/spellcheckerplugin/DomTextMatcher",[],function(){function e(e){return e&&1==e.nodeType&&"false"===e.contentEditable}return function(t,n){function r(e,t){if(!e[0])throw"findAndReplaceDOMText cannot handle zero-length matches";return{start:e.index,end:e.index+e[0].length,text:e[0],data:t}}function i(t){var n;if(3===t.nodeType)return t.data;if(E[t.nodeName]&&!N[t.nodeName])return"";if(e(t))return"\n";if(n="",(N[t.nodeName]||_[t.nodeName])&&(n+="\n"),t=t.firstChild)do n+=i(t);while(t=t.nextSibling);return n}function o(t,n,r){var i,o,a,s,l,c=[],u=0,d=t,f=0;n=n.slice(0),n.sort(function(e,t){return e.start-t.start}),l=n.shift();e:for(;;){if((N[d.nodeName]||_[d.nodeName]||e(d))&&u++,3===d.nodeType&&(!o&&d.length+u>=l.end?(o=d,s=l.end-u):i&&c.push(d),!i&&d.length+u>l.start&&(i=d,a=l.start-u),u+=d.length),i&&o){if(d=r({startNode:i,startNodeIndex:a,endNode:o,endNodeIndex:s,innerNodes:c,match:l.text,matchIndex:f}),u-=o.length-s,i=null,o=null,c=[],l=n.shift(),f++,!l)break}else if(E[d.nodeName]&&!N[d.nodeName]||!d.firstChild){if(d.nextSibling){d=d.nextSibling;continue}}else if(!e(d)){d=d.firstChild;continue}for(;;){if(d.nextSibling){d=d.nextSibling;break}if(d.parentNode===t)break e;d=d.parentNode}}}function a(e){function t(t,n){var r=S[n];r.stencil||(r.stencil=e(r));var i=r.stencil.cloneNode(!1);return i.setAttribute("data-mce-index",n),t&&i.appendChild(k.doc.createTextNode(t)),i}return function(e){var n,r,i,o=e.startNode,a=e.endNode,s=e.matchIndex,l=k.doc;if(o===a){var c=o;i=c.parentNode,e.startNodeIndex>0&&(n=l.createTextNode(c.data.substring(0,e.startNodeIndex)),i.insertBefore(n,c));var u=t(e.match,s);return i.insertBefore(u,c),e.endNodeIndex<c.length&&(r=l.createTextNode(c.data.substring(e.endNodeIndex)),i.insertBefore(r,c)),c.parentNode.removeChild(c),u}n=l.createTextNode(o.data.substring(0,e.startNodeIndex)),r=l.createTextNode(a.data.substring(e.endNodeIndex));for(var d=t(o.data.substring(e.startNodeIndex),s),f=[],h=0,p=e.innerNodes.length;h<p;++h){var m=e.innerNodes[h],g=t(m.data,s);m.parentNode.replaceChild(g,m),f.push(g)}var v=t(a.data.substring(0,e.endNodeIndex),s);return i=o.parentNode,i.insertBefore(n,o),i.insertBefore(d,o),i.removeChild(o),i=a.parentNode,i.insertBefore(v,a),i.insertBefore(r,a),i.removeChild(a),v}}function s(e){var t=e.parentNode;t.insertBefore(e.firstChild,e),e.parentNode.removeChild(e)}function l(e){var n=t.getElementsByTagName("*"),r=[];e="number"==typeof e?""+e:null;for(var i=0;i<n.length;i++){var o=n[i],a=o.getAttribute("data-mce-index");null!==a&&a.length&&(a!==e&&null!==e||r.push(o))}return r}function c(e){for(var t=S.length;t--;)if(S[t]===e)return t;return-1}function u(e){var t=[];return d(function(n,r){e(n,r)&&t.push(n)}),S=t,this}function d(e){for(var t=0,n=S.length;t<n&&e(S[t],t)!==!1;t++);return this}function f(e){return S.length&&o(t,S,a(e)),this}function h(e,t){if(w&&e.global)for(;x=e.exec(w);)S.push(r(x,t));return this}function p(e){var t,n=l(e?c(e):null);for(t=n.length;t--;)s(n[t]);return this}function m(e){return S[e.getAttribute("data-mce-index")]}function g(e){return l(c(e))[0]}function v(e,t,n){return S.push({start:e,end:e+t,text:w.substr(e,t),data:n}),this}function y(e){var t=l(c(e)),r=n.dom.createRng();return r.setStartBefore(t[0]),r.setEndAfter(t[t.length-1]),r}function b(e,t){var r=y(e);return r.deleteContents(),t.length>0&&r.insertNode(n.dom.doc.createTextNode(t)),r}function C(){return S.splice(0,S.length),p(),this}var x,w,N,E,_,S=[],k=n.dom;return N=n.schema.getBlockElements(),E=n.schema.getWhiteSpaceElements(),_=n.schema.getShortEndedElements(),w=i(t),{text:w,matches:S,each:d,filter:u,reset:C,matchFromElement:m,elementFromMatch:g,find:h,add:v,wrap:f,unwrap:p,replace:b,rangeFromMatch:y,indexOf:c}}}),r("tinymce/spellcheckerplugin/Plugin",["tinymce/spellcheckerplugin/DomTextMatcher","tinymce/PluginManager","tinymce/util/Tools","tinymce/ui/Menu","tinymce/dom/DOMUtils","tinymce/util/XHR","tinymce/util/URI","tinymce/util/JSON"],function(e,t,n,r,i,o,a,s){t.add("spellchecker",function(l,c){function u(){return B.textMatcher||(B.textMatcher=new e(l.getBody(),l)),B.textMatcher}function d(e,t){var r=[];return n.each(t,function(e){r.push({selectable:!0,text:e.name,data:e.value})}),r}function f(e){for(var t in e)return!1;return!0}function h(e,t){var o=[],a=k[e];n.each(a,function(e){o.push({text:e,onclick:function(){l.insertContent(l.dom.encode(e)),l.dom.remove(t),y()}})}),o.push({text:"-"}),A&&o.push({text:"Add to Dictionary",onclick:function(){b(e,t)}}),o.push.apply(o,[{text:"Ignore",onclick:function(){C(e,t)}},{text:"Ignore all",onclick:function(){C(e,t,!0)}}]),R=new r({items:o,context:"contextmenu",onautohide:function(e){e.target.className.indexOf("spellchecker")!=-1&&e.preventDefault()},onhide:function(){R.remove(),R=null}}),R.renderTo(document.body);var s=i.DOM.getPos(l.getContentAreaContainer()),c=l.dom.getPos(t[0]),u=l.dom.getRoot();"BODY"==u.nodeName?(c.x-=u.ownerDocument.documentElement.scrollLeft||u.scrollLeft,c.y-=u.ownerDocument.documentElement.scrollTop||u.scrollTop):(c.x-=u.scrollLeft,c.y-=u.scrollTop),s.x+=c.x,s.y+=c.y,R.moveTo(s.x,s.y+t[0].offsetHeight)}function p(){return l.getParam("spellchecker_wordchar_pattern")||new RegExp('[^\\s!"#$%&()*+,-./:;<=>?@[\\]^_{|}`\xa7\xa9\xab\xae\xb1\xb6\xb7\xb8\xbb\xbc\xbd\xbe\xbf\xd7\xf7\xa4\u201d\u201c\u201e\xa0\u2002\u2003\u2009]+',"g")}function m(e,t,r,i){var u={method:e,lang:D.spellchecker_language},d="";u["addToDictionary"==e?"word":"text"]=t,n.each(u,function(e,t){d&&(d+="&"),d+=t+"="+encodeURIComponent(e)}),o.send({url:new a(c).toAbsolute(D.spellchecker_rpc_url),type:"post",content_type:"application/x-www-form-urlencoded",data:d,success:function(e){if(e=s.parse(e))e.error?i(e.error):r(e);else{var t=l.translate("Server response wasn't proper JSON.");i(t)}},error:function(){var e=l.translate("The spelling service was not found: (")+D.spellchecker_rpc_url+l.translate(")");i(e)}})}function g(e,t,n,r){var i=D.spellchecker_callback||m;i.call(B,e,t,n,r)}function v(){function e(e){l.notificationManager.open({text:e,type:"error"}),l.setProgressState(!1),x()}x()||(l.setProgressState(!0),g("spellcheck",u().text,_,e),l.focus())}function y(){l.dom.select("span.mce-spellchecker-word").length||x()}function b(e,t){l.setProgressState(!0),g("addToDictionary",e,function(){l.setProgressState(!1),l.dom.remove(t,!0),y()},function(e){l.notificationManager.open({text:e,type:"error"}),l.setProgressState(!1)})}function C(e,t,r){l.selection.collapse(),r?n.each(l.dom.select("span.mce-spellchecker-word"),function(t){t.getAttribute("data-mce-word")==e&&l.dom.remove(t,!0)}):l.dom.remove(t,!0),y()}function x(){if(u().reset(),B.textMatcher=null,T)return T=!1,l.fire("SpellcheckEnd"),!0}function w(e){var t=e.getAttribute("data-mce-index");return"number"==typeof t?""+t:t}function N(e){var t,r=[];if(t=n.toArray(l.getBody().getElementsByTagName("span")),t.length)for(var i=0;i<t.length;i++){var o=w(t[i]);null!==o&&o.length&&o===e.toString()&&r.push(t[i])}return r}function E(e){var t=D.spellchecker_language;e.control.items().each(function(e){e.active(e.settings.data===t)})}function _(e){var t;if(e.words?(A=!!e.dictionary,t=e.words):t=e,l.setProgressState(!1),f(t)){var n=l.translate("No misspellings found.");return l.notificationManager.open({text:n,type:"info"}),void(T=!1)}k=t,u().find(p()).filter(function(e){return!!t[e.text]}).wrap(function(e){return l.dom.create("span",{class:"mce-spellchecker-word","data-mce-bogus":1,"data-mce-word":e.text})}),T=!0,l.fire("SpellcheckStart")}var S,k,T,R,A,B=this,D=l.settings;if(/(^|[ ,])tinymcespellchecker([, ]|$)/.test(D.plugins)&&t.get("tinymcespellchecker"))return void("undefined"!=typeof console&&console.log&&console.log("Spell Checker Pro is incompatible with Spell Checker plugin! Remove 'spellchecker' from the 'plugins' option."));var L=D.spellchecker_languages||"English=en,Danish=da,Dutch=nl,Finnish=fi,French=fr_FR,German=de,Italian=it,Polish=pl,Portuguese=pt_BR,Spanish=es,Swedish=sv";S=d("Language",n.map(L.split(","),function(e){return e=e.split("="),{name:e[0],value:e[1]}})),l.on("click",function(e){var t=e.target;if("mce-spellchecker-word"==t.className){e.preventDefault();var n=N(w(t));if(n.length>0){var r=l.dom.createRng();r.setStartBefore(n[0]),r.setEndAfter(n[n.length-1]),l.selection.setRng(r),h(t.getAttribute("data-mce-word"),n)}}}),l.addMenuItem("spellchecker",{text:"Spellcheck",context:"tools",onclick:v,selectable:!0,onPostRender:function(){var e=this;e.active(T),l.on("SpellcheckStart SpellcheckEnd",function(){e.active(T)})}});var M={tooltip:"Spellcheck",onclick:v,onPostRender:function(){var e=this;l.on("SpellcheckStart SpellcheckEnd",function(){e.active(T)})}};S.length>1&&(M.type="splitbutton",M.menu=S,M.onshow=E,M.onselect=function(e){D.spellchecker_language=e.control.settings.data}),l.addButton("spellchecker",M),l.addCommand("mceSpellCheck",v),l.on("remove",function(){R&&(R.remove(),R=null)}),l.on("change",y),this.getTextMatcher=u,this.getWordCharPattern=p,this.markErrors=_,this.getLanguage=function(){return D.spellchecker_language},D.spellchecker_language=D.spellchecker_language||D.language||"en"})}),o(["tinymce/spellcheckerplugin/DomTextMatcher"])}(this);
@@ -0,0 +1,120 @@
1
+ /**
2
+ * plugin.js
3
+ *
4
+ * Released under LGPL License.
5
+ * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
6
+ *
7
+ * License: http://www.tinymce.com/license
8
+ * Contributing: http://www.tinymce.com/contributing
9
+ */
10
+
11
+ /*global tinymce:true */
12
+
13
+ tinymce.PluginManager.add('tabfocus', function(editor) {
14
+ var DOM = tinymce.DOM, each = tinymce.each, explode = tinymce.explode;
15
+
16
+ function tabCancel(e) {
17
+ if (e.keyCode === 9 && !e.ctrlKey && !e.altKey && !e.metaKey) {
18
+ e.preventDefault();
19
+ }
20
+ }
21
+
22
+ function tabHandler(e) {
23
+ var x, el, v, i;
24
+
25
+ if (e.keyCode !== 9 || e.ctrlKey || e.altKey || e.metaKey || e.isDefaultPrevented()) {
26
+ return;
27
+ }
28
+
29
+ function find(direction) {
30
+ el = DOM.select(':input:enabled,*[tabindex]:not(iframe)');
31
+
32
+ function canSelectRecursive(e) {
33
+ return e.nodeName === "BODY" || (e.type != 'hidden' &&
34
+ e.style.display != "none" &&
35
+ e.style.visibility != "hidden" && canSelectRecursive(e.parentNode));
36
+ }
37
+
38
+ function canSelect(el) {
39
+ return /INPUT|TEXTAREA|BUTTON/.test(el.tagName) && tinymce.get(e.id) && el.tabIndex != -1 && canSelectRecursive(el);
40
+ }
41
+
42
+ each(el, function(e, i) {
43
+ if (e.id == editor.id) {
44
+ x = i;
45
+ return false;
46
+ }
47
+ });
48
+ if (direction > 0) {
49
+ for (i = x + 1; i < el.length; i++) {
50
+ if (canSelect(el[i])) {
51
+ return el[i];
52
+ }
53
+ }
54
+ } else {
55
+ for (i = x - 1; i >= 0; i--) {
56
+ if (canSelect(el[i])) {
57
+ return el[i];
58
+ }
59
+ }
60
+ }
61
+
62
+ return null;
63
+ }
64
+
65
+ v = explode(editor.getParam('tab_focus', editor.getParam('tabfocus_elements', ':prev,:next')));
66
+
67
+ if (v.length == 1) {
68
+ v[1] = v[0];
69
+ v[0] = ':prev';
70
+ }
71
+
72
+ // Find element to focus
73
+ if (e.shiftKey) {
74
+ if (v[0] == ':prev') {
75
+ el = find(-1);
76
+ } else {
77
+ el = DOM.get(v[0]);
78
+ }
79
+ } else {
80
+ if (v[1] == ':next') {
81
+ el = find(1);
82
+ } else {
83
+ el = DOM.get(v[1]);
84
+ }
85
+ }
86
+
87
+ if (el) {
88
+ var focusEditor = tinymce.get(el.id || el.name);
89
+
90
+ if (el.id && focusEditor) {
91
+ focusEditor.focus();
92
+ } else {
93
+ tinymce.util.Delay.setTimeout(function() {
94
+ if (!tinymce.Env.webkit) {
95
+ window.focus();
96
+ }
97
+
98
+ el.focus();
99
+ }, 10);
100
+ }
101
+
102
+ e.preventDefault();
103
+ }
104
+ }
105
+
106
+ editor.on('init', function() {
107
+ if (editor.inline) {
108
+ // Remove default tabIndex in inline mode
109
+ tinymce.DOM.setAttrib(editor.getBody(), 'tabIndex', null);
110
+ }
111
+
112
+ editor.on('keyup', tabCancel);
113
+
114
+ if (tinymce.Env.gecko) {
115
+ editor.on('keypress keydown', tabHandler);
116
+ } else {
117
+ editor.on('keydown', tabHandler);
118
+ }
119
+ });
120
+ });
@@ -0,0 +1 @@
1
+ tinymce.PluginManager.add("tabfocus",function(e){function t(e){9!==e.keyCode||e.ctrlKey||e.altKey||e.metaKey||e.preventDefault()}function n(t){function n(n){function o(e){return"BODY"===e.nodeName||"hidden"!=e.type&&"none"!=e.style.display&&"hidden"!=e.style.visibility&&o(e.parentNode)}function l(e){return/INPUT|TEXTAREA|BUTTON/.test(e.tagName)&&tinymce.get(t.id)&&e.tabIndex!=-1&&o(e)}if(s=r.select(":input:enabled,*[tabindex]:not(iframe)"),i(s,function(t,n){if(t.id==e.id)return a=n,!1}),n>0){for(c=a+1;c<s.length;c++)if(l(s[c]))return s[c]}else for(c=a-1;c>=0;c--)if(l(s[c]))return s[c];return null}var a,s,l,c;if(!(9!==t.keyCode||t.ctrlKey||t.altKey||t.metaKey||t.isDefaultPrevented())&&(l=o(e.getParam("tab_focus",e.getParam("tabfocus_elements",":prev,:next"))),1==l.length&&(l[1]=l[0],l[0]=":prev"),s=t.shiftKey?":prev"==l[0]?n(-1):r.get(l[0]):":next"==l[1]?n(1):r.get(l[1]))){var u=tinymce.get(s.id||s.name);s.id&&u?u.focus():tinymce.util.Delay.setTimeout(function(){tinymce.Env.webkit||window.focus(),s.focus()},10),t.preventDefault()}}var r=tinymce.DOM,i=tinymce.each,o=tinymce.explode;e.on("init",function(){e.inline&&tinymce.DOM.setAttrib(e.getBody(),"tabIndex",null),e.on("keyup",t),tinymce.Env.gecko?e.on("keypress keydown",n):e.on("keydown",n)})});
@@ -0,0 +1,4400 @@
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
+ var i, target, id, fragments, privateModules;
67
+
68
+ for (i = 0; i < ids.length; i++) {
69
+ target = exports;
70
+ id = ids[i];
71
+ fragments = id.split(/[.\/]/);
72
+
73
+ for (var fi = 0; fi < fragments.length - 1; ++fi) {
74
+ if (target[fragments[fi]] === undefined) {
75
+ target[fragments[fi]] = {};
76
+ }
77
+
78
+ target = target[fragments[fi]];
79
+ }
80
+
81
+ target[fragments[fragments.length - 1]] = modules[id];
82
+ }
83
+
84
+ // Expose private modules for unit tests
85
+ if (exports.AMDLC_TESTS) {
86
+ privateModules = exports.privateModules || {};
87
+
88
+ for (id in modules) {
89
+ privateModules[id] = modules[id];
90
+ }
91
+
92
+ for (i = 0; i < ids.length; i++) {
93
+ delete privateModules[ids[i]];
94
+ }
95
+
96
+ exports.privateModules = privateModules;
97
+ }
98
+ }
99
+
100
+ // Included from: js/tinymce/plugins/table/classes/Utils.js
101
+
102
+ /**
103
+ * Utils.js
104
+ *
105
+ * Released under LGPL License.
106
+ * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
107
+ *
108
+ * License: http://www.tinymce.com/license
109
+ * Contributing: http://www.tinymce.com/contributing
110
+ */
111
+
112
+ /**
113
+ * Various utility functions.
114
+ *
115
+ * @class tinymce.tableplugin.Utils
116
+ * @private
117
+ */
118
+ define("tinymce/tableplugin/Utils", [
119
+ "tinymce/Env"
120
+ ], function(Env) {
121
+ var setSpanVal = function (name) {
122
+ return function (td, val) {
123
+ if (td) {
124
+ val = parseInt(val, 10);
125
+
126
+ if (val === 1 || val === 0) {
127
+ td.removeAttribute(name, 1);
128
+ } else {
129
+ td.setAttribute(name, val, 1);
130
+ }
131
+ }
132
+ };
133
+ };
134
+
135
+ var getSpanVal = function (name) {
136
+ return function (td) {
137
+ return parseInt(td.getAttribute(name) || 1, 10);
138
+ };
139
+ };
140
+
141
+ function paddCell(cell) {
142
+ if (!Env.ie || Env.ie > 9) {
143
+ if (!cell.hasChildNodes()) {
144
+ cell.innerHTML = '<br data-mce-bogus="1" />';
145
+ }
146
+ }
147
+ }
148
+
149
+ return {
150
+ setColSpan: setSpanVal('colSpan'),
151
+ setRowSpan: setSpanVal('rowspan'),
152
+ getColSpan: getSpanVal('colSpan'),
153
+ getRowSpan: getSpanVal('rowSpan'),
154
+ setSpanVal: function (td, name, value) {
155
+ setSpanVal(name)(td, value);
156
+ },
157
+ getSpanVal: function (td, name) {
158
+ return getSpanVal(name)(td);
159
+ },
160
+ paddCell: paddCell
161
+ };
162
+ });
163
+
164
+ // Included from: js/tinymce/plugins/table/classes/SplitCols.js
165
+
166
+ /**
167
+ * SplitCols.js
168
+ *
169
+ * Released under LGPL License.
170
+ * Copyright (c) 1999-2016 Ephox Corp. All rights reserved
171
+ *
172
+ * License: http://www.tinymce.com/license
173
+ * Contributing: http://www.tinymce.com/contributing
174
+ */
175
+
176
+ /**
177
+ * Contains logic for handling splitting of merged rows.
178
+ *
179
+ * @class tinymce.tableplugin.SplitCols
180
+ * @private
181
+ */
182
+ define("tinymce/tableplugin/SplitCols", [
183
+ "tinymce/util/Tools",
184
+ "tinymce/tableplugin/Utils"
185
+ ], function(Tools, Utils) {
186
+ var getCellAt = function (grid, x, y) {
187
+ return grid[y] ? grid[y][x] : null;
188
+ };
189
+
190
+ var getCellElmAt = function (grid, x, y) {
191
+ var cell = getCellAt(grid, x, y);
192
+ return cell ? cell.elm : null;
193
+ };
194
+
195
+ var countHoles = function (grid, x, y, delta) {
196
+ var y2, cell, count = 0, elm = getCellElmAt(grid, x, y);
197
+
198
+ for (y2 = y; delta > 0 ? y2 < grid.length : y2 >= 0; y2 += delta) {
199
+ cell = getCellAt(grid, x, y2);
200
+ if (elm !== cell.elm) {
201
+ break;
202
+ }
203
+
204
+ count++;
205
+ }
206
+
207
+ return count;
208
+ };
209
+
210
+ var findRealElm = function (grid, x, y) {
211
+ var cell, row = grid[y];
212
+
213
+ for (var x2 = x; x2 < row.length; x2++) {
214
+ cell = row[x2];
215
+ if (cell.real) {
216
+ return cell.elm;
217
+ }
218
+ }
219
+
220
+ return null;
221
+ };
222
+
223
+ var getRowSplitInfo = function (grid, y) {
224
+ var cell, result = [], row = grid[y];
225
+
226
+ for (var x = 0; x < row.length; x++) {
227
+ cell = row[x];
228
+ result.push({
229
+ elm: cell.elm,
230
+ above: countHoles(grid, x, y, -1) - 1,
231
+ below: countHoles(grid, x, y, 1) - 1
232
+ });
233
+
234
+ x += Utils.getColSpan(cell.elm) - 1;
235
+ }
236
+
237
+ return result;
238
+ };
239
+
240
+ var createCell = function (info, rowSpan) {
241
+ var doc = info.elm.ownerDocument;
242
+ var newCell = doc.createElement('td');
243
+
244
+ Utils.setColSpan(newCell, Utils.getColSpan(info.elm));
245
+ Utils.setRowSpan(newCell, rowSpan);
246
+ Utils.paddCell(newCell);
247
+
248
+ return newCell;
249
+ };
250
+
251
+ var insertOrAppendCell = function (grid, newCell, x, y) {
252
+ var realCellElm = findRealElm(grid, x + 1, y);
253
+
254
+ if (!realCellElm) {
255
+ realCellElm = findRealElm(grid, 0, y);
256
+ realCellElm.parentNode.appendChild(newCell);
257
+ } else {
258
+ realCellElm.parentNode.insertBefore(newCell, realCellElm);
259
+ }
260
+ };
261
+
262
+ var splitAbove = function (grid, info, x, y) {
263
+ if (info.above !== 0) {
264
+ Utils.setRowSpan(info.elm, info.above);
265
+ var cell = createCell(info, info.below + 1);
266
+ insertOrAppendCell(grid, cell, x, y);
267
+ return cell;
268
+ }
269
+
270
+ return null;
271
+ };
272
+
273
+ var splitBelow = function (grid, info, x, y) {
274
+ if (info.below !== 0) {
275
+ Utils.setRowSpan(info.elm, info.above + 1);
276
+ var cell = createCell(info, info.below);
277
+ insertOrAppendCell(grid, cell, x, y + 1);
278
+ return cell;
279
+ }
280
+
281
+ return null;
282
+ };
283
+
284
+ var splitAt = function (grid, x, y, before) {
285
+ var rowInfos = getRowSplitInfo(grid, y);
286
+ var rowElm = getCellElmAt(grid, x, y).parentNode;
287
+ var cells = [];
288
+
289
+ Tools.each(rowInfos, function (info, x) {
290
+ var cell = before ? splitAbove(grid, info, x, y) : splitBelow(grid, info, x, y);
291
+ if (cell !== null) {
292
+ cells.push(cells);
293
+ }
294
+ });
295
+
296
+ return {
297
+ cells: cells,
298
+ row: rowElm
299
+ };
300
+ };
301
+
302
+ return {
303
+ splitAt: splitAt
304
+ };
305
+ });
306
+
307
+ // Included from: js/tinymce/plugins/table/classes/TableGrid.js
308
+
309
+ /**
310
+ * TableGrid.js
311
+ *
312
+ * Released under LGPL License.
313
+ * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
314
+ *
315
+ * License: http://www.tinymce.com/license
316
+ * Contributing: http://www.tinymce.com/contributing
317
+ */
318
+
319
+ /**
320
+ * This class creates a grid out of a table element. This
321
+ * makes it a whole lot easier to handle complex tables with
322
+ * col/row spans.
323
+ *
324
+ * @class tinymce.tableplugin.TableGrid
325
+ * @private
326
+ */
327
+ define("tinymce/tableplugin/TableGrid", [
328
+ "tinymce/util/Tools",
329
+ "tinymce/Env",
330
+ "tinymce/tableplugin/Utils",
331
+ "tinymce/tableplugin/SplitCols"
332
+ ], function(Tools, Env, Utils, SplitCols) {
333
+ var each = Tools.each, getSpanVal = Utils.getSpanVal, setSpanVal = Utils.setSpanVal;
334
+
335
+ return function(editor, table, selectedCell) {
336
+ var grid, gridWidth, startPos, endPos, selection = editor.selection, dom = selection.dom;
337
+
338
+ function removeCellSelection() {
339
+ editor.$('td[data-mce-selected],th[data-mce-selected]').removeAttr('data-mce-selected');
340
+ }
341
+
342
+ function isEditorBody(node) {
343
+ return node === editor.getBody();
344
+ }
345
+
346
+ function getChildrenByName(node, names) {
347
+ if (!node) {
348
+ return [];
349
+ }
350
+
351
+ names = Tools.map(names.split(','), function(name) {
352
+ return name.toLowerCase();
353
+ });
354
+
355
+ return Tools.grep(node.childNodes, function(node) {
356
+ return Tools.inArray(names, node.nodeName.toLowerCase()) !== -1;
357
+ });
358
+ }
359
+
360
+ function buildGrid() {
361
+ var startY = 0;
362
+
363
+ grid = [];
364
+ gridWidth = 0;
365
+
366
+ each(['thead', 'tbody', 'tfoot'], function(part) {
367
+ var partElm = getChildrenByName(table, part)[0];
368
+ var rows = getChildrenByName(partElm, 'tr');
369
+
370
+ each(rows, function(tr, y) {
371
+ y += startY;
372
+
373
+ each(getChildrenByName(tr, 'td,th'), function(td, x) {
374
+ var x2, y2, rowspan, colspan;
375
+
376
+ // Skip over existing cells produced by rowspan
377
+ if (grid[y]) {
378
+ while (grid[y][x]) {
379
+ x++;
380
+ }
381
+ }
382
+
383
+ // Get col/rowspan from cell
384
+ rowspan = getSpanVal(td, 'rowspan');
385
+ colspan = getSpanVal(td, 'colspan');
386
+
387
+ // Fill out rowspan/colspan right and down
388
+ for (y2 = y; y2 < y + rowspan; y2++) {
389
+ if (!grid[y2]) {
390
+ grid[y2] = [];
391
+ }
392
+
393
+ for (x2 = x; x2 < x + colspan; x2++) {
394
+ grid[y2][x2] = {
395
+ part: part,
396
+ real: y2 == y && x2 == x,
397
+ elm: td,
398
+ rowspan: rowspan,
399
+ colspan: colspan
400
+ };
401
+ }
402
+ }
403
+
404
+ gridWidth = Math.max(gridWidth, x + 1);
405
+ });
406
+ });
407
+
408
+ startY += rows.length;
409
+ });
410
+ }
411
+
412
+ function fireNewRow(node) {
413
+ editor.fire('newrow', {
414
+ node: node
415
+ });
416
+
417
+ return node;
418
+ }
419
+
420
+ function fireNewCell(node) {
421
+ editor.fire('newcell', {
422
+ node: node
423
+ });
424
+
425
+ return node;
426
+ }
427
+
428
+ function cloneNode(node, children) {
429
+ node = node.cloneNode(children);
430
+ node.removeAttribute('id');
431
+
432
+ return node;
433
+ }
434
+
435
+ function getCell(x, y) {
436
+ var row;
437
+
438
+ row = grid[y];
439
+ if (row) {
440
+ return row[x];
441
+ }
442
+ }
443
+
444
+ function getRow(grid, y) {
445
+ return grid[y] ? grid[y] : null;
446
+ }
447
+
448
+ function getColumn(grid, x) {
449
+ var out = [];
450
+
451
+ for (var y = 0; y < grid.length; y++) {
452
+ out.push(getCell(x, y));
453
+ }
454
+
455
+ return out;
456
+ }
457
+
458
+ function isCellSelected(cell) {
459
+ return cell && (!!dom.getAttrib(cell.elm, 'data-mce-selected') || cell == selectedCell);
460
+ }
461
+
462
+ function getSelectedRows() {
463
+ var rows = [];
464
+
465
+ each(table.rows, function(row) {
466
+ each(row.cells, function(cell) {
467
+ if (dom.getAttrib(cell, 'data-mce-selected') || (selectedCell && cell == selectedCell.elm)) {
468
+ rows.push(row);
469
+ return false;
470
+ }
471
+ });
472
+ });
473
+
474
+ return rows;
475
+ }
476
+
477
+ function deleteTable() {
478
+ var rng = dom.createRng();
479
+
480
+ if (isEditorBody(table)) {
481
+ return;
482
+ }
483
+
484
+ rng.setStartAfter(table);
485
+ rng.setEndAfter(table);
486
+
487
+ selection.setRng(rng);
488
+
489
+ dom.remove(table);
490
+ }
491
+
492
+ function cloneCell(cell) {
493
+ var formatNode, cloneFormats = {};
494
+
495
+ if (editor.settings.table_clone_elements !== false) {
496
+ cloneFormats = Tools.makeMap(
497
+ (editor.settings.table_clone_elements || 'strong em b i span font h1 h2 h3 h4 h5 h6 p div').toUpperCase(),
498
+ /[ ,]/
499
+ );
500
+ }
501
+
502
+ // Clone formats
503
+ Tools.walk(cell, function(node) {
504
+ var curNode;
505
+
506
+ if (node.nodeType == 3) {
507
+ each(dom.getParents(node.parentNode, null, cell).reverse(), function(node) {
508
+ if (!cloneFormats[node.nodeName]) {
509
+ return;
510
+ }
511
+
512
+ node = cloneNode(node, false);
513
+
514
+ if (!formatNode) {
515
+ formatNode = curNode = node;
516
+ } else if (curNode) {
517
+ curNode.appendChild(node);
518
+ }
519
+
520
+ curNode = node;
521
+ });
522
+
523
+ // Add something to the inner node
524
+ if (curNode) {
525
+ curNode.innerHTML = Env.ie && Env.ie < 10 ? '&nbsp;' : '<br data-mce-bogus="1" />';
526
+ }
527
+
528
+ return false;
529
+ }
530
+ }, 'childNodes');
531
+
532
+ cell = cloneNode(cell, false);
533
+ fireNewCell(cell);
534
+
535
+ setSpanVal(cell, 'rowSpan', 1);
536
+ setSpanVal(cell, 'colSpan', 1);
537
+
538
+ if (formatNode) {
539
+ cell.appendChild(formatNode);
540
+ } else {
541
+ Utils.paddCell(cell);
542
+ }
543
+
544
+ return cell;
545
+ }
546
+
547
+ function cleanup() {
548
+ var rng = dom.createRng(), row;
549
+
550
+ // Empty rows
551
+ each(dom.select('tr', table), function(tr) {
552
+ if (tr.cells.length === 0) {
553
+ dom.remove(tr);
554
+ }
555
+ });
556
+
557
+ // Empty table
558
+ if (dom.select('tr', table).length === 0) {
559
+ rng.setStartBefore(table);
560
+ rng.setEndBefore(table);
561
+ selection.setRng(rng);
562
+ dom.remove(table);
563
+ return;
564
+ }
565
+
566
+ // Empty header/body/footer
567
+ each(dom.select('thead,tbody,tfoot', table), function(part) {
568
+ if (part.rows.length === 0) {
569
+ dom.remove(part);
570
+ }
571
+ });
572
+
573
+ // Restore selection to start position if it still exists
574
+ buildGrid();
575
+
576
+ // If we have a valid startPos object
577
+ if (startPos) {
578
+ // Restore the selection to the closest table position
579
+ row = grid[Math.min(grid.length - 1, startPos.y)];
580
+ if (row) {
581
+ selection.select(row[Math.min(row.length - 1, startPos.x)].elm, true);
582
+ selection.collapse(true);
583
+ }
584
+ }
585
+ }
586
+
587
+ function fillLeftDown(x, y, rows, cols) {
588
+ var tr, x2, r, c, cell;
589
+
590
+ tr = grid[y][x].elm.parentNode;
591
+ for (r = 1; r <= rows; r++) {
592
+ tr = dom.getNext(tr, 'tr');
593
+
594
+ if (tr) {
595
+ // Loop left to find real cell
596
+ for (x2 = x; x2 >= 0; x2--) {
597
+ cell = grid[y + r][x2].elm;
598
+
599
+ if (cell.parentNode == tr) {
600
+ // Append clones after
601
+ for (c = 1; c <= cols; c++) {
602
+ dom.insertAfter(cloneCell(cell), cell);
603
+ }
604
+
605
+ break;
606
+ }
607
+ }
608
+
609
+ if (x2 == -1) {
610
+ // Insert nodes before first cell
611
+ for (c = 1; c <= cols; c++) {
612
+ tr.insertBefore(cloneCell(tr.cells[0]), tr.cells[0]);
613
+ }
614
+ }
615
+ }
616
+ }
617
+ }
618
+
619
+ function split() {
620
+ each(grid, function(row, y) {
621
+ each(row, function(cell, x) {
622
+ var colSpan, rowSpan, i;
623
+
624
+ if (isCellSelected(cell)) {
625
+ cell = cell.elm;
626
+ colSpan = getSpanVal(cell, 'colspan');
627
+ rowSpan = getSpanVal(cell, 'rowspan');
628
+
629
+ if (colSpan > 1 || rowSpan > 1) {
630
+ setSpanVal(cell, 'rowSpan', 1);
631
+ setSpanVal(cell, 'colSpan', 1);
632
+
633
+ // Insert cells right
634
+ for (i = 0; i < colSpan - 1; i++) {
635
+ dom.insertAfter(cloneCell(cell), cell);
636
+ }
637
+
638
+ fillLeftDown(x, y, rowSpan - 1, colSpan);
639
+ }
640
+ }
641
+ });
642
+ });
643
+ }
644
+
645
+ function findItemsOutsideOfRange(items, start, end) {
646
+ var out = [];
647
+
648
+ for (var i = 0; i < items.length; i++) {
649
+ if (i < start || i > end) {
650
+ out.push(items[i]);
651
+ }
652
+ }
653
+
654
+ return out;
655
+ }
656
+
657
+ function getFakeCells(cells) {
658
+ return Tools.grep(cells, function (cell) {
659
+ return cell.real === false;
660
+ });
661
+ }
662
+
663
+ function getUniqueElms(cells) {
664
+ var elms = [];
665
+
666
+ for (var i = 0; i < cells.length; i++) {
667
+ var elm = cells[i].elm;
668
+ if (elms[elms.length - 1] !== elm) {
669
+ elms.push(elm);
670
+ }
671
+ }
672
+
673
+ return elms;
674
+ }
675
+
676
+ function reduceRowSpans(grid, startX, startY, endX, endY) {
677
+ var count = 0;
678
+
679
+ for (var y = startY; y <= endY; y++) {
680
+ var allCells = findItemsOutsideOfRange(getRow(grid, y), startX, endX);
681
+ var fakeCells = getFakeCells(allCells);
682
+
683
+ if (allCells.length === fakeCells.length) {
684
+ Tools.each(getUniqueElms(fakeCells), function (elm) {
685
+ Utils.setRowSpan(elm, Utils.getRowSpan(elm) - 1);
686
+ });
687
+
688
+ count++;
689
+ }
690
+ }
691
+
692
+ return count;
693
+ }
694
+
695
+ function reduceColSpans(grid, startX, startY, endX, endY) {
696
+ var count = 0;
697
+
698
+ for (var x = startX; x <= endX; x++) {
699
+ var allCells = findItemsOutsideOfRange(getColumn(grid, x), startY, endY);
700
+ var fakeCells = getFakeCells(allCells);
701
+
702
+ if (allCells.length === fakeCells.length) {
703
+ Tools.each(getUniqueElms(fakeCells), function (elm) {
704
+ Utils.setColSpan(elm, Utils.getColSpan(elm) - 1);
705
+ });
706
+
707
+ count++;
708
+ }
709
+ }
710
+
711
+ return count;
712
+ }
713
+
714
+ function merge(cell, cols, rows) {
715
+ var pos, startX, startY, endX, endY, x, y, startCell, endCell, children, count, reducedRows, reducedCols;
716
+
717
+ // Use specified cell and cols/rows
718
+ if (cell) {
719
+ pos = getPos(cell);
720
+ startX = pos.x;
721
+ startY = pos.y;
722
+ endX = startX + (cols - 1);
723
+ endY = startY + (rows - 1);
724
+ } else {
725
+ startPos = endPos = null;
726
+
727
+ // Calculate start/end pos by checking for selected cells in grid works better with context menu
728
+ each(grid, function(row, y) {
729
+ each(row, function(cell, x) {
730
+ if (isCellSelected(cell)) {
731
+ if (!startPos) {
732
+ startPos = {x: x, y: y};
733
+ }
734
+
735
+ endPos = {x: x, y: y};
736
+ }
737
+ });
738
+ });
739
+
740
+ // Use selection, but make sure startPos is valid before accessing
741
+ if (startPos) {
742
+ startX = startPos.x;
743
+ startY = startPos.y;
744
+ endX = endPos.x;
745
+ endY = endPos.y;
746
+ }
747
+ }
748
+
749
+ // Find start/end cells
750
+ startCell = getCell(startX, startY);
751
+ endCell = getCell(endX, endY);
752
+
753
+ // Check if the cells exists and if they are of the same part for example tbody = tbody
754
+ if (startCell && endCell && startCell.part == endCell.part) {
755
+ // Split and rebuild grid
756
+ split();
757
+ buildGrid();
758
+
759
+ reducedRows = reduceRowSpans(grid, startX, startY, endX, endY);
760
+ reducedCols = reduceColSpans(grid, startX, startY, endX, endY);
761
+
762
+ // Set row/col span to start cell
763
+ startCell = getCell(startX, startY).elm;
764
+ var colSpan = (endX - startX - reducedCols) + 1;
765
+ var rowSpan = (endY - startY - reducedRows) + 1;
766
+
767
+ // All cells in table selected then just make it a table with one cell
768
+ if (colSpan === gridWidth && rowSpan === grid.length) {
769
+ colSpan = 1;
770
+ rowSpan = 1;
771
+ }
772
+
773
+ // Multiple whole rows selected then just make it one rowSpan
774
+ if (colSpan === gridWidth && rowSpan > 1) {
775
+ rowSpan = 1;
776
+ }
777
+
778
+ setSpanVal(startCell, 'colSpan', colSpan);
779
+ setSpanVal(startCell, 'rowSpan', rowSpan);
780
+
781
+ // Remove other cells and add it's contents to the start cell
782
+ for (y = startY; y <= endY; y++) {
783
+ for (x = startX; x <= endX; x++) {
784
+ if (!grid[y] || !grid[y][x]) {
785
+ continue;
786
+ }
787
+
788
+ cell = grid[y][x].elm;
789
+
790
+ /*jshint loopfunc:true */
791
+ /*eslint no-loop-func:0 */
792
+ if (cell != startCell) {
793
+ // Move children to startCell
794
+ children = Tools.grep(cell.childNodes);
795
+ each(children, function(node) {
796
+ startCell.appendChild(node);
797
+ });
798
+
799
+ // Remove bogus nodes if there is children in the target cell
800
+ if (children.length) {
801
+ children = Tools.grep(startCell.childNodes);
802
+ count = 0;
803
+ each(children, function(node) {
804
+ if (node.nodeName == 'BR' && count++ < children.length - 1) {
805
+ startCell.removeChild(node);
806
+ }
807
+ });
808
+ }
809
+
810
+ dom.remove(cell);
811
+ }
812
+ }
813
+ }
814
+
815
+ // Remove empty rows etc and restore caret location
816
+ cleanup();
817
+ }
818
+ }
819
+
820
+ function insertRow(before) {
821
+ var posY, cell, lastCell, x, rowElm, newRow, newCell, otherCell, rowSpan, spanValue;
822
+
823
+ // Find first/last row
824
+ each(grid, function(row, y) {
825
+ each(row, function(cell) {
826
+ if (isCellSelected(cell)) {
827
+ cell = cell.elm;
828
+ rowElm = cell.parentNode;
829
+ newRow = fireNewRow(cloneNode(rowElm, false));
830
+ posY = y;
831
+
832
+ if (before) {
833
+ return false;
834
+ }
835
+ }
836
+ });
837
+
838
+ if (before) {
839
+ return !posY;
840
+ }
841
+ });
842
+
843
+ // If posY is undefined there is nothing for us to do here...just return to avoid crashing below
844
+ if (posY === undefined) {
845
+ return;
846
+ }
847
+
848
+ for (x = 0, spanValue = 0; x < grid[0].length; x += spanValue) {
849
+ // Cell not found could be because of an invalid table structure
850
+ if (!grid[posY][x]) {
851
+ continue;
852
+ }
853
+
854
+ cell = grid[posY][x].elm;
855
+ spanValue = getSpanVal(cell, 'colspan');
856
+
857
+ if (cell != lastCell) {
858
+ if (!before) {
859
+ rowSpan = getSpanVal(cell, 'rowspan');
860
+ if (rowSpan > 1) {
861
+ setSpanVal(cell, 'rowSpan', rowSpan + 1);
862
+ continue;
863
+ }
864
+ } else {
865
+ // Check if cell above can be expanded
866
+ if (posY > 0 && grid[posY - 1][x]) {
867
+ otherCell = grid[posY - 1][x].elm;
868
+ rowSpan = getSpanVal(otherCell, 'rowSpan');
869
+ if (rowSpan > 1) {
870
+ setSpanVal(otherCell, 'rowSpan', rowSpan + 1);
871
+ continue;
872
+ }
873
+ }
874
+ }
875
+
876
+ // Insert new cell into new row
877
+ newCell = cloneCell(cell);
878
+ setSpanVal(newCell, 'colSpan', cell.colSpan);
879
+
880
+ newRow.appendChild(newCell);
881
+
882
+ lastCell = cell;
883
+ }
884
+ }
885
+
886
+ if (newRow.hasChildNodes()) {
887
+ if (!before) {
888
+ dom.insertAfter(newRow, rowElm);
889
+ } else {
890
+ rowElm.parentNode.insertBefore(newRow, rowElm);
891
+ }
892
+ }
893
+ }
894
+
895
+ function insertCol(before) {
896
+ var posX, lastCell;
897
+
898
+ // Find first/last column
899
+ each(grid, function(row) {
900
+ each(row, function(cell, x) {
901
+ if (isCellSelected(cell)) {
902
+ posX = x;
903
+
904
+ if (before) {
905
+ return false;
906
+ }
907
+ }
908
+ });
909
+
910
+ if (before) {
911
+ return !posX;
912
+ }
913
+ });
914
+
915
+ each(grid, function(row, y) {
916
+ var cell, rowSpan, colSpan;
917
+
918
+ if (!row[posX]) {
919
+ return;
920
+ }
921
+
922
+ cell = row[posX].elm;
923
+ if (cell != lastCell) {
924
+ colSpan = getSpanVal(cell, 'colspan');
925
+ rowSpan = getSpanVal(cell, 'rowspan');
926
+
927
+ if (colSpan == 1) {
928
+ if (!before) {
929
+ dom.insertAfter(cloneCell(cell), cell);
930
+ fillLeftDown(posX, y, rowSpan - 1, colSpan);
931
+ } else {
932
+ cell.parentNode.insertBefore(cloneCell(cell), cell);
933
+ fillLeftDown(posX, y, rowSpan - 1, colSpan);
934
+ }
935
+ } else {
936
+ setSpanVal(cell, 'colSpan', cell.colSpan + 1);
937
+ }
938
+
939
+ lastCell = cell;
940
+ }
941
+ });
942
+ }
943
+
944
+ function getSelectedCells(grid) {
945
+ return Tools.grep(getAllCells(grid), isCellSelected);
946
+ }
947
+
948
+ function getAllCells(grid) {
949
+ var cells = [];
950
+
951
+ each(grid, function(row) {
952
+ each(row, function(cell) {
953
+ cells.push(cell);
954
+ });
955
+ });
956
+
957
+ return cells;
958
+ }
959
+
960
+ function deleteCols() {
961
+ var cols = [];
962
+
963
+ if (isEditorBody(table)) {
964
+ if (grid[0].length == 1) {
965
+ return;
966
+ }
967
+
968
+ if (getSelectedCells(grid).length == getAllCells(grid).length) {
969
+ return;
970
+ }
971
+ }
972
+
973
+ // Get selected column indexes
974
+ each(grid, function(row) {
975
+ each(row, function(cell, x) {
976
+ if (isCellSelected(cell) && Tools.inArray(cols, x) === -1) {
977
+ each(grid, function(row) {
978
+ var cell = row[x].elm, colSpan;
979
+
980
+ colSpan = getSpanVal(cell, 'colSpan');
981
+
982
+ if (colSpan > 1) {
983
+ setSpanVal(cell, 'colSpan', colSpan - 1);
984
+ } else {
985
+ dom.remove(cell);
986
+ }
987
+ });
988
+
989
+ cols.push(x);
990
+ }
991
+ });
992
+ });
993
+
994
+ cleanup();
995
+ }
996
+
997
+ function deleteRows() {
998
+ var rows;
999
+
1000
+ function deleteRow(tr) {
1001
+ var pos, lastCell;
1002
+
1003
+ // Move down row spanned cells
1004
+ each(tr.cells, function(cell) {
1005
+ var rowSpan = getSpanVal(cell, 'rowSpan');
1006
+
1007
+ if (rowSpan > 1) {
1008
+ setSpanVal(cell, 'rowSpan', rowSpan - 1);
1009
+ pos = getPos(cell);
1010
+ fillLeftDown(pos.x, pos.y, 1, 1);
1011
+ }
1012
+ });
1013
+
1014
+ // Delete cells
1015
+ pos = getPos(tr.cells[0]);
1016
+ each(grid[pos.y], function(cell) {
1017
+ var rowSpan;
1018
+
1019
+ cell = cell.elm;
1020
+
1021
+ if (cell != lastCell) {
1022
+ rowSpan = getSpanVal(cell, 'rowSpan');
1023
+
1024
+ if (rowSpan <= 1) {
1025
+ dom.remove(cell);
1026
+ } else {
1027
+ setSpanVal(cell, 'rowSpan', rowSpan - 1);
1028
+ }
1029
+
1030
+ lastCell = cell;
1031
+ }
1032
+ });
1033
+ }
1034
+
1035
+ // Get selected rows and move selection out of scope
1036
+ rows = getSelectedRows();
1037
+
1038
+ if (isEditorBody(table) && rows.length == table.rows.length) {
1039
+ return;
1040
+ }
1041
+
1042
+ // Delete all selected rows
1043
+ each(rows.reverse(), function(tr) {
1044
+ deleteRow(tr);
1045
+ });
1046
+
1047
+ cleanup();
1048
+ }
1049
+
1050
+ function cutRows() {
1051
+ var rows = getSelectedRows();
1052
+
1053
+ if (isEditorBody(table) && rows.length == table.rows.length) {
1054
+ return;
1055
+ }
1056
+
1057
+ dom.remove(rows);
1058
+ cleanup();
1059
+
1060
+ return rows;
1061
+ }
1062
+
1063
+ function copyRows() {
1064
+ var rows = getSelectedRows();
1065
+
1066
+ each(rows, function(row, i) {
1067
+ rows[i] = cloneNode(row, true);
1068
+ });
1069
+
1070
+ return rows;
1071
+ }
1072
+
1073
+ function pasteRows(rows, before) {
1074
+ var splitResult, targetRow, newRows;
1075
+
1076
+ // Nothing to paste
1077
+ if (!rows) {
1078
+ return;
1079
+ }
1080
+
1081
+ splitResult = SplitCols.splitAt(grid, startPos.x, startPos.y, before);
1082
+ targetRow = splitResult.row;
1083
+ Tools.each(splitResult.cells, fireNewCell);
1084
+
1085
+ newRows = Tools.map(rows, function (row) {
1086
+ return row.cloneNode(true);
1087
+ });
1088
+
1089
+ if (!before) {
1090
+ newRows.reverse();
1091
+ }
1092
+
1093
+ each(newRows, function(row) {
1094
+ var i, cellCount = row.cells.length, cell;
1095
+
1096
+ fireNewRow(row);
1097
+
1098
+ // Remove col/rowspans
1099
+ for (i = 0; i < cellCount; i++) {
1100
+ cell = row.cells[i];
1101
+
1102
+ fireNewCell(cell);
1103
+ setSpanVal(cell, 'colSpan', 1);
1104
+ setSpanVal(cell, 'rowSpan', 1);
1105
+ }
1106
+
1107
+ // Needs more cells
1108
+ for (i = cellCount; i < gridWidth; i++) {
1109
+ row.appendChild(fireNewCell(cloneCell(row.cells[cellCount - 1])));
1110
+ }
1111
+
1112
+ // Needs less cells
1113
+ for (i = gridWidth; i < cellCount; i++) {
1114
+ dom.remove(row.cells[i]);
1115
+ }
1116
+
1117
+ // Add before/after
1118
+ if (before) {
1119
+ targetRow.parentNode.insertBefore(row, targetRow);
1120
+ } else {
1121
+ dom.insertAfter(row, targetRow);
1122
+ }
1123
+ });
1124
+
1125
+ removeCellSelection();
1126
+ }
1127
+
1128
+ function getPos(target) {
1129
+ var pos;
1130
+
1131
+ each(grid, function(row, y) {
1132
+ each(row, function(cell, x) {
1133
+ if (cell.elm == target) {
1134
+ pos = {x: x, y: y};
1135
+ return false;
1136
+ }
1137
+ });
1138
+
1139
+ return !pos;
1140
+ });
1141
+
1142
+ return pos;
1143
+ }
1144
+
1145
+ function setStartCell(cell) {
1146
+ startPos = getPos(cell);
1147
+ }
1148
+
1149
+ function findEndPos() {
1150
+ var maxX, maxY;
1151
+
1152
+ maxX = maxY = 0;
1153
+
1154
+ each(grid, function(row, y) {
1155
+ each(row, function(cell, x) {
1156
+ var colSpan, rowSpan;
1157
+
1158
+ if (isCellSelected(cell)) {
1159
+ cell = grid[y][x];
1160
+
1161
+ if (x > maxX) {
1162
+ maxX = x;
1163
+ }
1164
+
1165
+ if (y > maxY) {
1166
+ maxY = y;
1167
+ }
1168
+
1169
+ if (cell.real) {
1170
+ colSpan = cell.colspan - 1;
1171
+ rowSpan = cell.rowspan - 1;
1172
+
1173
+ if (colSpan) {
1174
+ if (x + colSpan > maxX) {
1175
+ maxX = x + colSpan;
1176
+ }
1177
+ }
1178
+
1179
+ if (rowSpan) {
1180
+ if (y + rowSpan > maxY) {
1181
+ maxY = y + rowSpan;
1182
+ }
1183
+ }
1184
+ }
1185
+ }
1186
+ });
1187
+ });
1188
+
1189
+ return {x: maxX, y: maxY};
1190
+ }
1191
+
1192
+ function setEndCell(cell) {
1193
+ var startX, startY, endX, endY, maxX, maxY, colSpan, rowSpan, x, y;
1194
+
1195
+ endPos = getPos(cell);
1196
+
1197
+ if (startPos && endPos) {
1198
+ // Get start/end positions
1199
+ startX = Math.min(startPos.x, endPos.x);
1200
+ startY = Math.min(startPos.y, endPos.y);
1201
+ endX = Math.max(startPos.x, endPos.x);
1202
+ endY = Math.max(startPos.y, endPos.y);
1203
+
1204
+ // Expand end position to include spans
1205
+ maxX = endX;
1206
+ maxY = endY;
1207
+
1208
+ // This logic tried to expand the selection to always be a rectangle
1209
+ // Expand startX
1210
+ /*for (y = startY; y <= maxY; y++) {
1211
+ cell = grid[y][startX];
1212
+
1213
+ if (!cell.real) {
1214
+ newX = startX - (cell.colspan - 1);
1215
+ if (newX < startX && newX >= 0) {
1216
+ startX = newX;
1217
+ }
1218
+ }
1219
+ }
1220
+
1221
+ // Expand startY
1222
+ for (x = startX; x <= maxX; x++) {
1223
+ cell = grid[startY][x];
1224
+
1225
+ if (!cell.real) {
1226
+ newY = startY - (cell.rowspan - 1);
1227
+ if (newY < startY && newY >= 0) {
1228
+ startY = newY;
1229
+ }
1230
+ }
1231
+ }*/
1232
+
1233
+ // Find max X, Y
1234
+ for (y = startY; y <= endY; y++) {
1235
+ for (x = startX; x <= endX; x++) {
1236
+ cell = grid[y][x];
1237
+
1238
+ if (cell.real) {
1239
+ colSpan = cell.colspan - 1;
1240
+ rowSpan = cell.rowspan - 1;
1241
+
1242
+ if (colSpan) {
1243
+ if (x + colSpan > maxX) {
1244
+ maxX = x + colSpan;
1245
+ }
1246
+ }
1247
+
1248
+ if (rowSpan) {
1249
+ if (y + rowSpan > maxY) {
1250
+ maxY = y + rowSpan;
1251
+ }
1252
+ }
1253
+ }
1254
+ }
1255
+ }
1256
+
1257
+ removeCellSelection();
1258
+
1259
+ // Add new selection
1260
+ for (y = startY; y <= maxY; y++) {
1261
+ for (x = startX; x <= maxX; x++) {
1262
+ if (grid[y][x]) {
1263
+ dom.setAttrib(grid[y][x].elm, 'data-mce-selected', '1');
1264
+ }
1265
+ }
1266
+ }
1267
+ }
1268
+ }
1269
+
1270
+ function moveRelIdx(cellElm, delta) {
1271
+ var pos, index, cell;
1272
+
1273
+ pos = getPos(cellElm);
1274
+ index = pos.y * gridWidth + pos.x;
1275
+
1276
+ do {
1277
+ index += delta;
1278
+ cell = getCell(index % gridWidth, Math.floor(index / gridWidth));
1279
+
1280
+ if (!cell) {
1281
+ break;
1282
+ }
1283
+
1284
+ if (cell.elm != cellElm) {
1285
+ selection.select(cell.elm, true);
1286
+
1287
+ if (dom.isEmpty(cell.elm)) {
1288
+ selection.collapse(true);
1289
+ }
1290
+
1291
+ return true;
1292
+ }
1293
+ } while (cell.elm == cellElm);
1294
+
1295
+ return false;
1296
+ }
1297
+
1298
+ function splitCols(before) {
1299
+ if (startPos) {
1300
+ var splitResult = SplitCols.splitAt(grid, startPos.x, startPos.y, before);
1301
+ Tools.each(splitResult.cells, fireNewCell);
1302
+ }
1303
+ }
1304
+
1305
+ table = table || dom.getParent(selection.getStart(true), 'table');
1306
+
1307
+ buildGrid();
1308
+
1309
+ selectedCell = selectedCell || dom.getParent(selection.getStart(true), 'th,td');
1310
+
1311
+ if (selectedCell) {
1312
+ startPos = getPos(selectedCell);
1313
+ endPos = findEndPos();
1314
+ selectedCell = getCell(startPos.x, startPos.y);
1315
+ }
1316
+
1317
+ Tools.extend(this, {
1318
+ deleteTable: deleteTable,
1319
+ split: split,
1320
+ merge: merge,
1321
+ insertRow: insertRow,
1322
+ insertCol: insertCol,
1323
+ splitCols: splitCols,
1324
+ deleteCols: deleteCols,
1325
+ deleteRows: deleteRows,
1326
+ cutRows: cutRows,
1327
+ copyRows: copyRows,
1328
+ pasteRows: pasteRows,
1329
+ getPos: getPos,
1330
+ setStartCell: setStartCell,
1331
+ setEndCell: setEndCell,
1332
+ moveRelIdx: moveRelIdx,
1333
+ refresh: buildGrid
1334
+ });
1335
+ };
1336
+ });
1337
+
1338
+ // Included from: js/tinymce/plugins/table/classes/Quirks.js
1339
+
1340
+ /**
1341
+ * Quirks.js
1342
+ *
1343
+ * Released under LGPL License.
1344
+ * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
1345
+ *
1346
+ * License: http://www.tinymce.com/license
1347
+ * Contributing: http://www.tinymce.com/contributing
1348
+ */
1349
+
1350
+ /**
1351
+ * This class includes fixes for various browser quirks.
1352
+ *
1353
+ * @class tinymce.tableplugin.Quirks
1354
+ * @private
1355
+ */
1356
+ define("tinymce/tableplugin/Quirks", [
1357
+ "tinymce/util/VK",
1358
+ "tinymce/util/Delay",
1359
+ "tinymce/Env",
1360
+ "tinymce/util/Tools",
1361
+ "tinymce/tableplugin/Utils"
1362
+ ], function(VK, Delay, Env, Tools, Utils) {
1363
+ var each = Tools.each, getSpanVal = Utils.getSpanVal;
1364
+
1365
+ return function(editor) {
1366
+ /**
1367
+ * Fixed caret movement around tables on WebKit.
1368
+ */
1369
+ function moveWebKitSelection() {
1370
+ function eventHandler(e) {
1371
+ var key = e.keyCode;
1372
+
1373
+ function handle(upBool, sourceNode) {
1374
+ var siblingDirection = upBool ? 'previousSibling' : 'nextSibling';
1375
+ var currentRow = editor.dom.getParent(sourceNode, 'tr');
1376
+ var siblingRow = currentRow[siblingDirection];
1377
+
1378
+ if (siblingRow) {
1379
+ moveCursorToRow(editor, sourceNode, siblingRow, upBool);
1380
+ e.preventDefault();
1381
+ return true;
1382
+ }
1383
+
1384
+ var tableNode = editor.dom.getParent(currentRow, 'table');
1385
+ var middleNode = currentRow.parentNode;
1386
+ var parentNodeName = middleNode.nodeName.toLowerCase();
1387
+ if (parentNodeName === 'tbody' || parentNodeName === (upBool ? 'tfoot' : 'thead')) {
1388
+ var targetParent = getTargetParent(upBool, tableNode, middleNode, 'tbody');
1389
+ if (targetParent !== null) {
1390
+ return moveToRowInTarget(upBool, targetParent, sourceNode);
1391
+ }
1392
+ }
1393
+
1394
+ return escapeTable(upBool, currentRow, siblingDirection, tableNode);
1395
+ }
1396
+
1397
+ function getTargetParent(upBool, topNode, secondNode, nodeName) {
1398
+ var tbodies = editor.dom.select('>' + nodeName, topNode);
1399
+ var position = tbodies.indexOf(secondNode);
1400
+ if (upBool && position === 0 || !upBool && position === tbodies.length - 1) {
1401
+ return getFirstHeadOrFoot(upBool, topNode);
1402
+ } else if (position === -1) {
1403
+ var topOrBottom = secondNode.tagName.toLowerCase() === 'thead' ? 0 : tbodies.length - 1;
1404
+ return tbodies[topOrBottom];
1405
+ }
1406
+
1407
+ return tbodies[position + (upBool ? -1 : 1)];
1408
+ }
1409
+
1410
+ function getFirstHeadOrFoot(upBool, parent) {
1411
+ var tagName = upBool ? 'thead' : 'tfoot';
1412
+ var headOrFoot = editor.dom.select('>' + tagName, parent);
1413
+ return headOrFoot.length !== 0 ? headOrFoot[0] : null;
1414
+ }
1415
+
1416
+ function moveToRowInTarget(upBool, targetParent, sourceNode) {
1417
+ var targetRow = getChildForDirection(targetParent, upBool);
1418
+
1419
+ if (targetRow) {
1420
+ moveCursorToRow(editor, sourceNode, targetRow, upBool);
1421
+ }
1422
+
1423
+ e.preventDefault();
1424
+ return true;
1425
+ }
1426
+
1427
+ function escapeTable(upBool, currentRow, siblingDirection, table) {
1428
+ var tableSibling = table[siblingDirection];
1429
+
1430
+ if (tableSibling) {
1431
+ moveCursorToStartOfElement(tableSibling);
1432
+ return true;
1433
+ }
1434
+
1435
+ var parentCell = editor.dom.getParent(table, 'td,th');
1436
+ if (parentCell) {
1437
+ return handle(upBool, parentCell, e);
1438
+ }
1439
+
1440
+ var backUpSibling = getChildForDirection(currentRow, !upBool);
1441
+ moveCursorToStartOfElement(backUpSibling);
1442
+ e.preventDefault();
1443
+ return false;
1444
+ }
1445
+
1446
+ function getChildForDirection(parent, up) {
1447
+ var child = parent && parent[up ? 'lastChild' : 'firstChild'];
1448
+ // BR is not a valid table child to return in this case we return the table cell
1449
+ return child && child.nodeName === 'BR' ? editor.dom.getParent(child, 'td,th') : child;
1450
+ }
1451
+
1452
+ function moveCursorToStartOfElement(n) {
1453
+ editor.selection.setCursorLocation(n, 0);
1454
+ }
1455
+
1456
+ function isVerticalMovement() {
1457
+ return key == VK.UP || key == VK.DOWN;
1458
+ }
1459
+
1460
+ function isInTable(editor) {
1461
+ var node = editor.selection.getNode();
1462
+ var currentRow = editor.dom.getParent(node, 'tr');
1463
+ return currentRow !== null;
1464
+ }
1465
+
1466
+ function columnIndex(column) {
1467
+ var colIndex = 0;
1468
+ var c = column;
1469
+ while (c.previousSibling) {
1470
+ c = c.previousSibling;
1471
+ colIndex = colIndex + getSpanVal(c, "colspan");
1472
+ }
1473
+ return colIndex;
1474
+ }
1475
+
1476
+ function findColumn(rowElement, columnIndex) {
1477
+ var c = 0, r = 0;
1478
+
1479
+ each(rowElement.children, function(cell, i) {
1480
+ c = c + getSpanVal(cell, "colspan");
1481
+ r = i;
1482
+ if (c > columnIndex) {
1483
+ return false;
1484
+ }
1485
+ });
1486
+ return r;
1487
+ }
1488
+
1489
+ function moveCursorToRow(ed, node, row, upBool) {
1490
+ var srcColumnIndex = columnIndex(editor.dom.getParent(node, 'td,th'));
1491
+ var tgtColumnIndex = findColumn(row, srcColumnIndex);
1492
+ var tgtNode = row.childNodes[tgtColumnIndex];
1493
+ var rowCellTarget = getChildForDirection(tgtNode, upBool);
1494
+ moveCursorToStartOfElement(rowCellTarget || tgtNode);
1495
+ }
1496
+
1497
+ function shouldFixCaret(preBrowserNode) {
1498
+ var newNode = editor.selection.getNode();
1499
+ var newParent = editor.dom.getParent(newNode, 'td,th');
1500
+ var oldParent = editor.dom.getParent(preBrowserNode, 'td,th');
1501
+
1502
+ return newParent && newParent !== oldParent && checkSameParentTable(newParent, oldParent);
1503
+ }
1504
+
1505
+ function checkSameParentTable(nodeOne, NodeTwo) {
1506
+ return editor.dom.getParent(nodeOne, 'TABLE') === editor.dom.getParent(NodeTwo, 'TABLE');
1507
+ }
1508
+
1509
+ if (isVerticalMovement() && isInTable(editor)) {
1510
+ var preBrowserNode = editor.selection.getNode();
1511
+ Delay.setEditorTimeout(editor, function() {
1512
+ if (shouldFixCaret(preBrowserNode)) {
1513
+ handle(!e.shiftKey && key === VK.UP, preBrowserNode, e);
1514
+ }
1515
+ }, 0);
1516
+ }
1517
+ }
1518
+
1519
+ editor.on('KeyDown', function(e) {
1520
+ eventHandler(e);
1521
+ });
1522
+ }
1523
+
1524
+ function fixBeforeTableCaretBug() {
1525
+ // Checks if the selection/caret is at the start of the specified block element
1526
+ function isAtStart(rng, par) {
1527
+ var doc = par.ownerDocument, rng2 = doc.createRange(), elm;
1528
+
1529
+ rng2.setStartBefore(par);
1530
+ rng2.setEnd(rng.endContainer, rng.endOffset);
1531
+
1532
+ elm = doc.createElement('body');
1533
+ elm.appendChild(rng2.cloneContents());
1534
+
1535
+ // Check for text characters of other elements that should be treated as content
1536
+ return elm.innerHTML.replace(/<(br|img|object|embed|input|textarea)[^>]*>/gi, '-').replace(/<[^>]+>/g, '').length === 0;
1537
+ }
1538
+
1539
+ // Fixes an bug where it's impossible to place the caret before a table in Gecko
1540
+ // this fix solves it by detecting when the caret is at the beginning of such a table
1541
+ // and then manually moves the caret infront of the table
1542
+ editor.on('KeyDown', function(e) {
1543
+ var rng, table, dom = editor.dom;
1544
+
1545
+ // On gecko it's not possible to place the caret before a table
1546
+ if (e.keyCode == 37 || e.keyCode == 38) {
1547
+ rng = editor.selection.getRng();
1548
+ table = dom.getParent(rng.startContainer, 'table');
1549
+
1550
+ if (table && editor.getBody().firstChild == table) {
1551
+ if (isAtStart(rng, table)) {
1552
+ rng = dom.createRng();
1553
+
1554
+ rng.setStartBefore(table);
1555
+ rng.setEndBefore(table);
1556
+
1557
+ editor.selection.setRng(rng);
1558
+
1559
+ e.preventDefault();
1560
+ }
1561
+ }
1562
+ }
1563
+ });
1564
+ }
1565
+
1566
+ // Fixes an issue on Gecko where it's impossible to place the caret behind a table
1567
+ // This fix will force a paragraph element after the table but only when the forced_root_block setting is enabled
1568
+ function fixTableCaretPos() {
1569
+ editor.on('KeyDown SetContent VisualAid', function() {
1570
+ var last;
1571
+
1572
+ // Skip empty text nodes from the end
1573
+ for (last = editor.getBody().lastChild; last; last = last.previousSibling) {
1574
+ if (last.nodeType == 3) {
1575
+ if (last.nodeValue.length > 0) {
1576
+ break;
1577
+ }
1578
+ } else if (last.nodeType == 1 && (last.tagName == 'BR' || !last.getAttribute('data-mce-bogus'))) {
1579
+ break;
1580
+ }
1581
+ }
1582
+
1583
+ if (last && last.nodeName == 'TABLE') {
1584
+ if (editor.settings.forced_root_block) {
1585
+ editor.dom.add(
1586
+ editor.getBody(),
1587
+ editor.settings.forced_root_block,
1588
+ editor.settings.forced_root_block_attrs,
1589
+ Env.ie && Env.ie < 10 ? '&nbsp;' : '<br data-mce-bogus="1" />'
1590
+ );
1591
+ } else {
1592
+ editor.dom.add(editor.getBody(), 'br', {'data-mce-bogus': '1'});
1593
+ }
1594
+ }
1595
+ });
1596
+
1597
+ editor.on('PreProcess', function(o) {
1598
+ var last = o.node.lastChild;
1599
+
1600
+ if (last && (last.nodeName == "BR" || (last.childNodes.length == 1 &&
1601
+ (last.firstChild.nodeName == 'BR' || last.firstChild.nodeValue == '\u00a0'))) &&
1602
+ last.previousSibling && last.previousSibling.nodeName == "TABLE") {
1603
+ editor.dom.remove(last);
1604
+ }
1605
+ });
1606
+ }
1607
+
1608
+ // this nasty hack is here to work around some WebKit selection bugs.
1609
+ function fixTableCellSelection() {
1610
+ function tableCellSelected(ed, rng, n, currentCell) {
1611
+ // The decision of when a table cell is selected is somewhat involved. The fact that this code is
1612
+ // required is actually a pointer to the root cause of this bug. A cell is selected when the start
1613
+ // and end offsets are 0, the start container is a text, and the selection node is either a TR (most cases)
1614
+ // or the parent of the table (in the case of the selection containing the last cell of a table).
1615
+ var TEXT_NODE = 3, table = ed.dom.getParent(rng.startContainer, 'TABLE');
1616
+ var tableParent, allOfCellSelected, tableCellSelection;
1617
+
1618
+ if (table) {
1619
+ tableParent = table.parentNode;
1620
+ }
1621
+
1622
+ allOfCellSelected = rng.startContainer.nodeType == TEXT_NODE &&
1623
+ rng.startOffset === 0 &&
1624
+ rng.endOffset === 0 &&
1625
+ currentCell &&
1626
+ (n.nodeName == "TR" || n == tableParent);
1627
+
1628
+ tableCellSelection = (n.nodeName == "TD" || n.nodeName == "TH") && !currentCell;
1629
+
1630
+ return allOfCellSelected || tableCellSelection;
1631
+ }
1632
+
1633
+ function fixSelection() {
1634
+ var rng = editor.selection.getRng();
1635
+ var n = editor.selection.getNode();
1636
+ var currentCell = editor.dom.getParent(rng.startContainer, 'TD,TH');
1637
+
1638
+ if (!tableCellSelected(editor, rng, n, currentCell)) {
1639
+ return;
1640
+ }
1641
+
1642
+ if (!currentCell) {
1643
+ currentCell = n;
1644
+ }
1645
+
1646
+ // Get the very last node inside the table cell
1647
+ var end = currentCell.lastChild;
1648
+ while (end.lastChild) {
1649
+ end = end.lastChild;
1650
+ }
1651
+
1652
+ // Select the entire table cell. Nothing outside of the table cell should be selected.
1653
+ if (end.nodeType == 3) {
1654
+ rng.setEnd(end, end.data.length);
1655
+ editor.selection.setRng(rng);
1656
+ }
1657
+ }
1658
+
1659
+ editor.on('KeyDown', function() {
1660
+ fixSelection();
1661
+ });
1662
+
1663
+ editor.on('MouseDown', function(e) {
1664
+ if (e.button != 2) {
1665
+ fixSelection();
1666
+ }
1667
+ });
1668
+ }
1669
+
1670
+ /**
1671
+ * Delete table if all cells are selected.
1672
+ */
1673
+ function deleteTable() {
1674
+ function placeCaretInCell(cell) {
1675
+ editor.selection.select(cell, true);
1676
+ editor.selection.collapse(true);
1677
+ }
1678
+
1679
+ function clearCell(cell) {
1680
+ editor.$(cell).empty();
1681
+ Utils.paddCell(cell);
1682
+ }
1683
+
1684
+ editor.on('keydown', function(e) {
1685
+ if ((e.keyCode == VK.DELETE || e.keyCode == VK.BACKSPACE) && !e.isDefaultPrevented()) {
1686
+ var table, tableCells, selectedTableCells, cell;
1687
+
1688
+ table = editor.dom.getParent(editor.selection.getStart(), 'table');
1689
+ if (table) {
1690
+ tableCells = editor.dom.select('td,th', table);
1691
+ selectedTableCells = Tools.grep(tableCells, function(cell) {
1692
+ return !!editor.dom.getAttrib(cell, 'data-mce-selected');
1693
+ });
1694
+
1695
+ if (selectedTableCells.length === 0) {
1696
+ // If caret is within an empty table cell then empty it for real
1697
+ cell = editor.dom.getParent(editor.selection.getStart(), 'td,th');
1698
+ if (editor.selection.isCollapsed() && cell && editor.dom.isEmpty(cell)) {
1699
+ e.preventDefault();
1700
+ clearCell(cell);
1701
+ placeCaretInCell(cell);
1702
+ }
1703
+
1704
+ return;
1705
+ }
1706
+
1707
+ e.preventDefault();
1708
+
1709
+ editor.undoManager.transact(function() {
1710
+ if (tableCells.length == selectedTableCells.length) {
1711
+ editor.execCommand('mceTableDelete');
1712
+ } else {
1713
+ Tools.each(selectedTableCells, clearCell);
1714
+ placeCaretInCell(selectedTableCells[0]);
1715
+ }
1716
+ });
1717
+ }
1718
+ }
1719
+ });
1720
+ }
1721
+
1722
+ deleteTable();
1723
+
1724
+ if (Env.webkit) {
1725
+ moveWebKitSelection();
1726
+ fixTableCellSelection();
1727
+ }
1728
+
1729
+ if (Env.gecko) {
1730
+ fixBeforeTableCaretBug();
1731
+ fixTableCaretPos();
1732
+ }
1733
+
1734
+ if (Env.ie > 9) {
1735
+ fixBeforeTableCaretBug();
1736
+ fixTableCaretPos();
1737
+ }
1738
+ };
1739
+ });
1740
+
1741
+ // Included from: js/tinymce/plugins/table/classes/CellSelection.js
1742
+
1743
+ /**
1744
+ * CellSelection.js
1745
+ *
1746
+ * Released under LGPL License.
1747
+ * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
1748
+ *
1749
+ * License: http://www.tinymce.com/license
1750
+ * Contributing: http://www.tinymce.com/contributing
1751
+ */
1752
+
1753
+ /**
1754
+ * This class handles table cell selection by faking it using a css class that gets applied
1755
+ * to cells when dragging the mouse from one cell to another.
1756
+ *
1757
+ * @class tinymce.tableplugin.CellSelection
1758
+ * @private
1759
+ */
1760
+ define("tinymce/tableplugin/CellSelection", [
1761
+ "tinymce/tableplugin/TableGrid",
1762
+ "tinymce/dom/TreeWalker",
1763
+ "tinymce/util/Tools"
1764
+ ], function(TableGrid, TreeWalker, Tools) {
1765
+ return function(editor, selectionChange) {
1766
+ var dom = editor.dom, tableGrid, startCell, startTable, lastMouseOverTarget, hasCellSelection = true, resizing, dragging;
1767
+
1768
+ function clear(force) {
1769
+ // Restore selection possibilities
1770
+ editor.getBody().style.webkitUserSelect = '';
1771
+
1772
+ if (force || hasCellSelection) {
1773
+ editor.$('td[data-mce-selected],th[data-mce-selected]').removeAttr('data-mce-selected');
1774
+ hasCellSelection = false;
1775
+ }
1776
+ }
1777
+
1778
+ var endSelection = function () {
1779
+ startCell = tableGrid = startTable = lastMouseOverTarget = null;
1780
+ selectionChange(false);
1781
+ };
1782
+
1783
+ function isCellInTable(table, cell) {
1784
+ if (!table || !cell) {
1785
+ return false;
1786
+ }
1787
+
1788
+ return table === dom.getParent(cell, 'table');
1789
+ }
1790
+
1791
+ function cellSelectionHandler(e) {
1792
+ var sel, target = e.target, currentCell;
1793
+
1794
+ if (resizing || dragging) {
1795
+ return;
1796
+ }
1797
+
1798
+ // Fake mouse enter by keeping track of last mouse over
1799
+ if (target === lastMouseOverTarget) {
1800
+ return;
1801
+ }
1802
+
1803
+ lastMouseOverTarget = target;
1804
+
1805
+ if (startTable && startCell) {
1806
+ currentCell = dom.getParent(target, 'td,th');
1807
+
1808
+ if (!isCellInTable(startTable, currentCell)) {
1809
+ currentCell = dom.getParent(startTable, 'td,th');
1810
+ }
1811
+
1812
+ // Selection inside first cell is normal until we have expanted
1813
+ if (startCell === currentCell && !hasCellSelection) {
1814
+ return;
1815
+ }
1816
+
1817
+ selectionChange(true);
1818
+
1819
+ if (isCellInTable(startTable, currentCell)) {
1820
+ e.preventDefault();
1821
+
1822
+ if (!tableGrid) {
1823
+ tableGrid = new TableGrid(editor, startTable, startCell);
1824
+ editor.getBody().style.webkitUserSelect = 'none';
1825
+ }
1826
+
1827
+ tableGrid.setEndCell(currentCell);
1828
+ hasCellSelection = true;
1829
+
1830
+ // Remove current selection
1831
+ sel = editor.selection.getSel();
1832
+
1833
+ try {
1834
+ if (sel.removeAllRanges) {
1835
+ sel.removeAllRanges();
1836
+ } else {
1837
+ sel.empty();
1838
+ }
1839
+ } catch (ex) {
1840
+ // IE9 might throw errors here
1841
+ }
1842
+ }
1843
+ }
1844
+ }
1845
+
1846
+ editor.on('SelectionChange', function(e) {
1847
+ if (hasCellSelection) {
1848
+ e.stopImmediatePropagation();
1849
+ }
1850
+ }, true);
1851
+
1852
+ // Add cell selection logic
1853
+ editor.on('MouseDown', function(e) {
1854
+ if (e.button != 2 && !resizing && !dragging) {
1855
+ clear();
1856
+
1857
+ startCell = dom.getParent(e.target, 'td,th');
1858
+ startTable = dom.getParent(startCell, 'table');
1859
+ }
1860
+ });
1861
+
1862
+ editor.on('mouseover', cellSelectionHandler);
1863
+
1864
+ editor.on('remove', function() {
1865
+ dom.unbind(editor.getDoc(), 'mouseover', cellSelectionHandler);
1866
+ clear();
1867
+ });
1868
+
1869
+ editor.on('MouseUp', function() {
1870
+ var rng, sel = editor.selection, selectedCells, walker, node, lastNode;
1871
+
1872
+ function setPoint(node, start) {
1873
+ var walker = new TreeWalker(node, node);
1874
+
1875
+ do {
1876
+ // Text node
1877
+ if (node.nodeType == 3 && Tools.trim(node.nodeValue).length !== 0) {
1878
+ if (start) {
1879
+ rng.setStart(node, 0);
1880
+ } else {
1881
+ rng.setEnd(node, node.nodeValue.length);
1882
+ }
1883
+
1884
+ return;
1885
+ }
1886
+
1887
+ // BR element
1888
+ if (node.nodeName == 'BR') {
1889
+ if (start) {
1890
+ rng.setStartBefore(node);
1891
+ } else {
1892
+ rng.setEndBefore(node);
1893
+ }
1894
+
1895
+ return;
1896
+ }
1897
+ } while ((node = (start ? walker.next() : walker.prev())));
1898
+ }
1899
+
1900
+ // Move selection to startCell
1901
+ if (startCell) {
1902
+ if (tableGrid) {
1903
+ editor.getBody().style.webkitUserSelect = '';
1904
+ }
1905
+
1906
+ // Try to expand text selection as much as we can only Gecko supports cell selection
1907
+ selectedCells = dom.select('td[data-mce-selected],th[data-mce-selected]');
1908
+ if (selectedCells.length > 0) {
1909
+ rng = dom.createRng();
1910
+ node = selectedCells[0];
1911
+ rng.setStartBefore(node);
1912
+ rng.setEndAfter(node);
1913
+
1914
+ setPoint(node, 1);
1915
+ walker = new TreeWalker(node, dom.getParent(selectedCells[0], 'table'));
1916
+
1917
+ do {
1918
+ if (node.nodeName == 'TD' || node.nodeName == 'TH') {
1919
+ if (!dom.getAttrib(node, 'data-mce-selected')) {
1920
+ break;
1921
+ }
1922
+
1923
+ lastNode = node;
1924
+ }
1925
+ } while ((node = walker.next()));
1926
+
1927
+ setPoint(lastNode);
1928
+
1929
+ sel.setRng(rng);
1930
+ }
1931
+
1932
+ editor.nodeChanged();
1933
+ endSelection();
1934
+ }
1935
+ });
1936
+
1937
+ editor.on('KeyUp Drop SetContent', function(e) {
1938
+ clear(e.type == 'setcontent');
1939
+ endSelection();
1940
+ resizing = false;
1941
+ });
1942
+
1943
+ editor.on('ObjectResizeStart ObjectResized', function(e) {
1944
+ resizing = e.type != 'objectresized';
1945
+ });
1946
+
1947
+ editor.on('dragstart', function () {
1948
+ dragging = true;
1949
+ });
1950
+
1951
+ editor.on('drop dragend', function () {
1952
+ dragging = false;
1953
+ });
1954
+
1955
+ return {
1956
+ clear: clear
1957
+ };
1958
+ };
1959
+ });
1960
+
1961
+ // Included from: js/tinymce/plugins/table/classes/Dialogs.js
1962
+
1963
+ /**
1964
+ * Dialogs.js
1965
+ *
1966
+ * Released under LGPL License.
1967
+ * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
1968
+ *
1969
+ * License: http://www.tinymce.com/license
1970
+ * Contributing: http://www.tinymce.com/contributing
1971
+ */
1972
+
1973
+ /*eslint dot-notation:0*/
1974
+
1975
+ /**
1976
+ * ...
1977
+ *
1978
+ * @class tinymce.tableplugin.Dialogs
1979
+ * @private
1980
+ */
1981
+ define("tinymce/tableplugin/Dialogs", [
1982
+ "tinymce/util/Tools",
1983
+ "tinymce/Env"
1984
+ ], function(Tools, Env) {
1985
+ var each = Tools.each;
1986
+
1987
+ return function(editor) {
1988
+ var self = this;
1989
+
1990
+ function createColorPickAction() {
1991
+ var colorPickerCallback = editor.settings.color_picker_callback;
1992
+
1993
+ if (colorPickerCallback) {
1994
+ return function() {
1995
+ var self = this;
1996
+
1997
+ colorPickerCallback.call(
1998
+ editor,
1999
+ function(value) {
2000
+ self.value(value).fire('change');
2001
+ },
2002
+ self.value()
2003
+ );
2004
+ };
2005
+ }
2006
+ }
2007
+
2008
+ function createStyleForm(dom) {
2009
+ return {
2010
+ title: 'Advanced',
2011
+ type: 'form',
2012
+ defaults: {
2013
+ onchange: function() {
2014
+ updateStyle(dom, this.parents().reverse()[0], this.name() == "style");
2015
+ }
2016
+ },
2017
+ items: [
2018
+ {
2019
+ label: 'Style',
2020
+ name: 'style',
2021
+ type: 'textbox'
2022
+ },
2023
+
2024
+ {
2025
+ type: 'form',
2026
+ padding: 0,
2027
+ formItemDefaults: {
2028
+ layout: 'grid',
2029
+ alignH: ['start', 'right']
2030
+ },
2031
+ defaults: {
2032
+ size: 7
2033
+ },
2034
+ items: [
2035
+ {
2036
+ label: 'Border color',
2037
+ type: 'colorbox',
2038
+ name: 'borderColor',
2039
+ onaction: createColorPickAction()
2040
+ },
2041
+
2042
+ {
2043
+ label: 'Background color',
2044
+ type: 'colorbox',
2045
+ name: 'backgroundColor',
2046
+ onaction: createColorPickAction()
2047
+ }
2048
+ ]
2049
+ }
2050
+ ]
2051
+ };
2052
+ }
2053
+
2054
+ function removePxSuffix(size) {
2055
+ return size ? size.replace(/px$/, '') : "";
2056
+ }
2057
+
2058
+ function addSizeSuffix(size) {
2059
+ if (/^[0-9]+$/.test(size)) {
2060
+ size += "px";
2061
+ }
2062
+
2063
+ return size;
2064
+ }
2065
+
2066
+ function unApplyAlign(elm) {
2067
+ each('left center right'.split(' '), function(name) {
2068
+ editor.formatter.remove('align' + name, {}, elm);
2069
+ });
2070
+ }
2071
+
2072
+ function unApplyVAlign(elm) {
2073
+ each('top middle bottom'.split(' '), function(name) {
2074
+ editor.formatter.remove('valign' + name, {}, elm);
2075
+ });
2076
+ }
2077
+
2078
+ function buildListItems(inputList, itemCallback, startItems) {
2079
+ function appendItems(values, output) {
2080
+ output = output || [];
2081
+
2082
+ Tools.each(values, function(item) {
2083
+ var menuItem = {text: item.text || item.title};
2084
+
2085
+ if (item.menu) {
2086
+ menuItem.menu = appendItems(item.menu);
2087
+ } else {
2088
+ menuItem.value = item.value;
2089
+
2090
+ if (itemCallback) {
2091
+ itemCallback(menuItem);
2092
+ }
2093
+ }
2094
+
2095
+ output.push(menuItem);
2096
+ });
2097
+
2098
+ return output;
2099
+ }
2100
+
2101
+ return appendItems(inputList, startItems || []);
2102
+ }
2103
+
2104
+ function updateStyle(dom, win, isStyleCtrl) {
2105
+ var data = win.toJSON();
2106
+ var css = dom.parseStyle(data.style);
2107
+
2108
+ if (isStyleCtrl) {
2109
+ win.find('#borderColor').value(css["border-color"] || '')[0].fire('change');
2110
+ win.find('#backgroundColor').value(css["background-color"] || '')[0].fire('change');
2111
+ } else {
2112
+ css["border-color"] = data.borderColor;
2113
+ css["background-color"] = data.backgroundColor;
2114
+ }
2115
+
2116
+ win.find('#style').value(dom.serializeStyle(dom.parseStyle(dom.serializeStyle(css))));
2117
+ }
2118
+
2119
+ function appendStylesToData(dom, data, elm) {
2120
+ var css = dom.parseStyle(dom.getAttrib(elm, 'style'));
2121
+
2122
+ if (css["border-color"]) {
2123
+ data.borderColor = css["border-color"];
2124
+ }
2125
+
2126
+ if (css["background-color"]) {
2127
+ data.backgroundColor = css["background-color"];
2128
+ }
2129
+
2130
+ data.style = dom.serializeStyle(css);
2131
+ }
2132
+
2133
+ function mergeStyles(dom, elm, styles) {
2134
+ var css = dom.parseStyle(dom.getAttrib(elm, 'style'));
2135
+
2136
+ each(styles, function(style) {
2137
+ css[style.name] = style.value;
2138
+ });
2139
+
2140
+ dom.setAttrib(elm, 'style', dom.serializeStyle(dom.parseStyle(dom.serializeStyle(css))));
2141
+ }
2142
+
2143
+ self.tableProps = function() {
2144
+ self.table(true);
2145
+ };
2146
+
2147
+ self.table = function(isProps) {
2148
+ var dom = editor.dom, tableElm, colsCtrl, rowsCtrl, classListCtrl, data = {}, generalTableForm, stylesToMerge;
2149
+
2150
+ function onSubmitTableForm() {
2151
+
2152
+ //Explore the layers of the table till we find the first layer of tds or ths
2153
+ function styleTDTH(elm, name, value) {
2154
+ if (elm.tagName === "TD" || elm.tagName === "TH") {
2155
+ dom.setStyle(elm, name, value);
2156
+ } else {
2157
+ if (elm.children) {
2158
+ for (var i = 0; i < elm.children.length; i++) {
2159
+ styleTDTH(elm.children[i], name, value);
2160
+ }
2161
+ }
2162
+ }
2163
+ }
2164
+
2165
+ var captionElm;
2166
+
2167
+ updateStyle(dom, this);
2168
+ data = Tools.extend(data, this.toJSON());
2169
+
2170
+ if (data["class"] === false) {
2171
+ delete data["class"];
2172
+ }
2173
+
2174
+ editor.undoManager.transact(function() {
2175
+ if (!tableElm) {
2176
+ tableElm = editor.plugins.table.insertTable(data.cols || 1, data.rows || 1);
2177
+ }
2178
+
2179
+ editor.dom.setAttribs(tableElm, {
2180
+ style: data.style,
2181
+ 'class': data['class']
2182
+ });
2183
+
2184
+ if (editor.settings.table_style_by_css) {
2185
+ stylesToMerge = [];
2186
+ stylesToMerge.push({name: 'border', value: data.border});
2187
+ stylesToMerge.push({name: 'border-spacing', value: addSizeSuffix(data.cellspacing)});
2188
+ mergeStyles(dom, tableElm, stylesToMerge);
2189
+ dom.setAttribs(tableElm, {
2190
+ 'data-mce-border-color': data.borderColor,
2191
+ 'data-mce-cell-padding': data.cellpadding,
2192
+ 'data-mce-border': data.border
2193
+ });
2194
+ if (tableElm.children) {
2195
+ for (var i = 0; i < tableElm.children.length; i++) {
2196
+ styleTDTH(tableElm.children[i], 'border', data.border);
2197
+ styleTDTH(tableElm.children[i], 'padding', addSizeSuffix(data.cellpadding));
2198
+ }
2199
+ }
2200
+ } else {
2201
+ editor.dom.setAttribs(tableElm, {
2202
+ border: data.border,
2203
+ cellpadding: data.cellpadding,
2204
+ cellspacing: data.cellspacing
2205
+ });
2206
+ }
2207
+
2208
+ if (dom.getAttrib(tableElm, 'width') && !editor.settings.table_style_by_css) {
2209
+ dom.setAttrib(tableElm, 'width', removePxSuffix(data.width));
2210
+ } else {
2211
+ dom.setStyle(tableElm, 'width', addSizeSuffix(data.width));
2212
+ }
2213
+
2214
+ dom.setStyle(tableElm, 'height', addSizeSuffix(data.height));
2215
+
2216
+ // Toggle caption on/off
2217
+ captionElm = dom.select('caption', tableElm)[0];
2218
+
2219
+ if (captionElm && !data.caption) {
2220
+ dom.remove(captionElm);
2221
+ }
2222
+
2223
+ if (!captionElm && data.caption) {
2224
+ captionElm = dom.create('caption');
2225
+ captionElm.innerHTML = !Env.ie ? '<br data-mce-bogus="1"/>' : '\u00a0';
2226
+ tableElm.insertBefore(captionElm, tableElm.firstChild);
2227
+ }
2228
+ unApplyAlign(tableElm);
2229
+ if (data.align) {
2230
+ editor.formatter.apply('align' + data.align, {}, tableElm);
2231
+ }
2232
+
2233
+ editor.focus();
2234
+ editor.addVisual();
2235
+ });
2236
+ }
2237
+
2238
+ function getTDTHOverallStyle(elm, name) {
2239
+ var cells = editor.dom.select("td,th", elm), firstChildStyle;
2240
+
2241
+ function checkChildren(firstChildStyle, elms) {
2242
+
2243
+ for (var i = 0; i < elms.length; i++) {
2244
+ var currentStyle = dom.getStyle(elms[i], name);
2245
+ if (typeof firstChildStyle === "undefined") {
2246
+ firstChildStyle = currentStyle;
2247
+ }
2248
+ if (firstChildStyle != currentStyle) {
2249
+ return "";
2250
+ }
2251
+ }
2252
+
2253
+ return firstChildStyle;
2254
+
2255
+ }
2256
+
2257
+ firstChildStyle = checkChildren(firstChildStyle, cells);
2258
+
2259
+ return firstChildStyle;
2260
+ }
2261
+
2262
+ if (isProps === true) {
2263
+ tableElm = dom.getParent(editor.selection.getStart(), 'table');
2264
+
2265
+ if (tableElm) {
2266
+ data = {
2267
+ width: removePxSuffix(dom.getStyle(tableElm, 'width') || dom.getAttrib(tableElm, 'width')),
2268
+ height: removePxSuffix(dom.getStyle(tableElm, 'height') || dom.getAttrib(tableElm, 'height')),
2269
+ cellspacing: removePxSuffix(dom.getStyle(tableElm, 'border-spacing') ||
2270
+ dom.getAttrib(tableElm, 'cellspacing')),
2271
+ cellpadding: dom.getAttrib(tableElm, 'data-mce-cell-padding') || dom.getAttrib(tableElm, 'cellpadding') ||
2272
+ getTDTHOverallStyle(tableElm, 'padding'),
2273
+ border: dom.getAttrib(tableElm, 'data-mce-border') || dom.getAttrib(tableElm, 'border') ||
2274
+ getTDTHOverallStyle(tableElm, 'border'),
2275
+ borderColor: dom.getAttrib(tableElm, 'data-mce-border-color'),
2276
+ caption: !!dom.select('caption', tableElm)[0],
2277
+ 'class': dom.getAttrib(tableElm, 'class')
2278
+ };
2279
+
2280
+ each('left center right'.split(' '), function(name) {
2281
+ if (editor.formatter.matchNode(tableElm, 'align' + name)) {
2282
+ data.align = name;
2283
+ }
2284
+ });
2285
+ }
2286
+ } else {
2287
+ colsCtrl = {label: 'Cols', name: 'cols'};
2288
+ rowsCtrl = {label: 'Rows', name: 'rows'};
2289
+ }
2290
+
2291
+ if (editor.settings.table_class_list) {
2292
+ if (data["class"]) {
2293
+ data["class"] = data["class"].replace(/\s*mce\-item\-table\s*/g, '');
2294
+ }
2295
+
2296
+ classListCtrl = {
2297
+ name: 'class',
2298
+ type: 'listbox',
2299
+ label: 'Class',
2300
+ values: buildListItems(
2301
+ editor.settings.table_class_list,
2302
+ function(item) {
2303
+ if (item.value) {
2304
+ item.textStyle = function() {
2305
+ return editor.formatter.getCssText({block: 'table', classes: [item.value]});
2306
+ };
2307
+ }
2308
+ }
2309
+ )
2310
+ };
2311
+ }
2312
+
2313
+ generalTableForm = {
2314
+ type: 'form',
2315
+ layout: 'flex',
2316
+ direction: 'column',
2317
+ labelGapCalc: 'children',
2318
+ padding: 0,
2319
+ items: [
2320
+ {
2321
+ type: 'form',
2322
+ labelGapCalc: false,
2323
+ padding: 0,
2324
+ layout: 'grid',
2325
+ columns: 2,
2326
+ defaults: {
2327
+ type: 'textbox',
2328
+ maxWidth: 50
2329
+ },
2330
+ items: (editor.settings.table_appearance_options !== false) ? [
2331
+ colsCtrl,
2332
+ rowsCtrl,
2333
+ {label: 'Width', name: 'width'},
2334
+ {label: 'Height', name: 'height'},
2335
+ {label: 'Cell spacing', name: 'cellspacing'},
2336
+ {label: 'Cell padding', name: 'cellpadding'},
2337
+ {label: 'Border', name: 'border'},
2338
+ {label: 'Caption', name: 'caption', type: 'checkbox'}
2339
+ ] : [
2340
+ colsCtrl,
2341
+ rowsCtrl,
2342
+ {label: 'Width', name: 'width'},
2343
+ {label: 'Height', name: 'height'}
2344
+ ]
2345
+ },
2346
+
2347
+ {
2348
+ label: 'Alignment',
2349
+ name: 'align',
2350
+ type: 'listbox',
2351
+ text: 'None',
2352
+ values: [
2353
+ {text: 'None', value: ''},
2354
+ {text: 'Left', value: 'left'},
2355
+ {text: 'Center', value: 'center'},
2356
+ {text: 'Right', value: 'right'}
2357
+ ]
2358
+ },
2359
+
2360
+ classListCtrl
2361
+ ]
2362
+ };
2363
+
2364
+ if (editor.settings.table_advtab !== false) {
2365
+ appendStylesToData(dom, data, tableElm);
2366
+
2367
+ editor.windowManager.open({
2368
+ title: "Table properties",
2369
+ data: data,
2370
+ bodyType: 'tabpanel',
2371
+ body: [
2372
+ {
2373
+ title: 'General',
2374
+ type: 'form',
2375
+ items: generalTableForm
2376
+ },
2377
+ createStyleForm(dom)
2378
+ ],
2379
+
2380
+ onsubmit: onSubmitTableForm
2381
+ });
2382
+ } else {
2383
+ editor.windowManager.open({
2384
+ title: "Table properties",
2385
+ data: data,
2386
+ body: generalTableForm,
2387
+ onsubmit: onSubmitTableForm
2388
+ });
2389
+ }
2390
+ };
2391
+
2392
+ self.merge = function(grid, cell) {
2393
+ editor.windowManager.open({
2394
+ title: "Merge cells",
2395
+ body: [
2396
+ {label: 'Cols', name: 'cols', type: 'textbox', value: '1', size: 10},
2397
+ {label: 'Rows', name: 'rows', type: 'textbox', value: '1', size: 10}
2398
+ ],
2399
+ onsubmit: function() {
2400
+ var data = this.toJSON();
2401
+
2402
+ editor.undoManager.transact(function() {
2403
+ grid.merge(cell, data.cols, data.rows);
2404
+ });
2405
+ }
2406
+ });
2407
+ };
2408
+
2409
+ self.cell = function() {
2410
+ var dom = editor.dom, cellElm, data, classListCtrl, cells = [];
2411
+
2412
+ function onSubmitCellForm() {
2413
+ updateStyle(dom, this);
2414
+ data = Tools.extend(data, this.toJSON());
2415
+
2416
+ editor.undoManager.transact(function() {
2417
+ each(cells, function(cellElm) {
2418
+ editor.dom.setAttribs(cellElm, {
2419
+ scope: data.scope,
2420
+ style: data.style,
2421
+ 'class': data['class']
2422
+ });
2423
+
2424
+ editor.dom.setStyles(cellElm, {
2425
+ width: addSizeSuffix(data.width),
2426
+ height: addSizeSuffix(data.height)
2427
+ });
2428
+
2429
+ // Switch cell type
2430
+ if (data.type && cellElm.nodeName.toLowerCase() != data.type) {
2431
+ cellElm = dom.rename(cellElm, data.type);
2432
+ }
2433
+
2434
+ // Apply/remove alignment
2435
+ unApplyAlign(cellElm);
2436
+ if (data.align) {
2437
+ editor.formatter.apply('align' + data.align, {}, cellElm);
2438
+ }
2439
+
2440
+ // Apply/remove vertical alignment
2441
+ unApplyVAlign(cellElm);
2442
+ if (data.valign) {
2443
+ editor.formatter.apply('valign' + data.valign, {}, cellElm);
2444
+ }
2445
+ });
2446
+
2447
+ editor.focus();
2448
+ });
2449
+ }
2450
+
2451
+ // Get selected cells or the current cell
2452
+ cells = editor.dom.select('td[data-mce-selected],th[data-mce-selected]');
2453
+ cellElm = editor.dom.getParent(editor.selection.getStart(), 'td,th');
2454
+ if (!cells.length && cellElm) {
2455
+ cells.push(cellElm);
2456
+ }
2457
+
2458
+ cellElm = cellElm || cells[0];
2459
+
2460
+ if (!cellElm) {
2461
+ // If this element is null, return now to avoid crashing.
2462
+ return;
2463
+ }
2464
+
2465
+ data = {
2466
+ width: removePxSuffix(dom.getStyle(cellElm, 'width') || dom.getAttrib(cellElm, 'width')),
2467
+ height: removePxSuffix(dom.getStyle(cellElm, 'height') || dom.getAttrib(cellElm, 'height')),
2468
+ scope: dom.getAttrib(cellElm, 'scope'),
2469
+ 'class': dom.getAttrib(cellElm, 'class')
2470
+ };
2471
+
2472
+ data.type = cellElm.nodeName.toLowerCase();
2473
+
2474
+ each('left center right'.split(' '), function(name) {
2475
+ if (editor.formatter.matchNode(cellElm, 'align' + name)) {
2476
+ data.align = name;
2477
+ }
2478
+ });
2479
+
2480
+ each('top middle bottom'.split(' '), function(name) {
2481
+ if (editor.formatter.matchNode(cellElm, 'valign' + name)) {
2482
+ data.valign = name;
2483
+ }
2484
+ });
2485
+
2486
+ if (editor.settings.table_cell_class_list) {
2487
+ classListCtrl = {
2488
+ name: 'class',
2489
+ type: 'listbox',
2490
+ label: 'Class',
2491
+ values: buildListItems(
2492
+ editor.settings.table_cell_class_list,
2493
+ function(item) {
2494
+ if (item.value) {
2495
+ item.textStyle = function() {
2496
+ return editor.formatter.getCssText({block: 'td', classes: [item.value]});
2497
+ };
2498
+ }
2499
+ }
2500
+ )
2501
+ };
2502
+ }
2503
+
2504
+ var generalCellForm = {
2505
+ type: 'form',
2506
+ layout: 'flex',
2507
+ direction: 'column',
2508
+ labelGapCalc: 'children',
2509
+ padding: 0,
2510
+ items: [
2511
+ {
2512
+ type: 'form',
2513
+ layout: 'grid',
2514
+ columns: 2,
2515
+ labelGapCalc: false,
2516
+ padding: 0,
2517
+ defaults: {
2518
+ type: 'textbox',
2519
+ maxWidth: 50
2520
+ },
2521
+ items: [
2522
+ {label: 'Width', name: 'width'},
2523
+ {label: 'Height', name: 'height'},
2524
+ {
2525
+ label: 'Cell type',
2526
+ name: 'type',
2527
+ type: 'listbox',
2528
+ text: 'None',
2529
+ minWidth: 90,
2530
+ maxWidth: null,
2531
+ values: [
2532
+ {text: 'Cell', value: 'td'},
2533
+ {text: 'Header cell', value: 'th'}
2534
+ ]
2535
+ },
2536
+ {
2537
+ label: 'Scope',
2538
+ name: 'scope',
2539
+ type: 'listbox',
2540
+ text: 'None',
2541
+ minWidth: 90,
2542
+ maxWidth: null,
2543
+ values: [
2544
+ {text: 'None', value: ''},
2545
+ {text: 'Row', value: 'row'},
2546
+ {text: 'Column', value: 'col'},
2547
+ {text: 'Row group', value: 'rowgroup'},
2548
+ {text: 'Column group', value: 'colgroup'}
2549
+ ]
2550
+ },
2551
+ {
2552
+ label: 'H Align',
2553
+ name: 'align',
2554
+ type: 'listbox',
2555
+ text: 'None',
2556
+ minWidth: 90,
2557
+ maxWidth: null,
2558
+ values: [
2559
+ {text: 'None', value: ''},
2560
+ {text: 'Left', value: 'left'},
2561
+ {text: 'Center', value: 'center'},
2562
+ {text: 'Right', value: 'right'}
2563
+ ]
2564
+ },
2565
+ {
2566
+ label: 'V Align',
2567
+ name: 'valign',
2568
+ type: 'listbox',
2569
+ text: 'None',
2570
+ minWidth: 90,
2571
+ maxWidth: null,
2572
+ values: [
2573
+ {text: 'None', value: ''},
2574
+ {text: 'Top', value: 'top'},
2575
+ {text: 'Middle', value: 'middle'},
2576
+ {text: 'Bottom', value: 'bottom'}
2577
+ ]
2578
+ }
2579
+ ]
2580
+ },
2581
+
2582
+ classListCtrl
2583
+ ]
2584
+ };
2585
+
2586
+ if (editor.settings.table_cell_advtab !== false) {
2587
+ appendStylesToData(dom, data, cellElm);
2588
+
2589
+ editor.windowManager.open({
2590
+ title: "Cell properties",
2591
+ bodyType: 'tabpanel',
2592
+ data: data,
2593
+ body: [
2594
+ {
2595
+ title: 'General',
2596
+ type: 'form',
2597
+ items: generalCellForm
2598
+ },
2599
+
2600
+ createStyleForm(dom)
2601
+ ],
2602
+
2603
+ onsubmit: onSubmitCellForm
2604
+ });
2605
+ } else {
2606
+ editor.windowManager.open({
2607
+ title: "Cell properties",
2608
+ data: data,
2609
+ body: generalCellForm,
2610
+ onsubmit: onSubmitCellForm
2611
+ });
2612
+ }
2613
+ };
2614
+
2615
+ self.row = function() {
2616
+ var dom = editor.dom, tableElm, cellElm, rowElm, classListCtrl, data, rows = [], generalRowForm;
2617
+
2618
+ function onSubmitRowForm() {
2619
+ var tableElm, oldParentElm, parentElm;
2620
+
2621
+ updateStyle(dom, this);
2622
+ data = Tools.extend(data, this.toJSON());
2623
+
2624
+ editor.undoManager.transact(function() {
2625
+ var toType = data.type;
2626
+
2627
+ each(rows, function(rowElm) {
2628
+ editor.dom.setAttribs(rowElm, {
2629
+ scope: data.scope,
2630
+ style: data.style,
2631
+ 'class': data['class']
2632
+ });
2633
+
2634
+ editor.dom.setStyles(rowElm, {
2635
+ height: addSizeSuffix(data.height)
2636
+ });
2637
+
2638
+ if (toType != rowElm.parentNode.nodeName.toLowerCase()) {
2639
+ tableElm = dom.getParent(rowElm, 'table');
2640
+
2641
+ oldParentElm = rowElm.parentNode;
2642
+ parentElm = dom.select(toType, tableElm)[0];
2643
+ if (!parentElm) {
2644
+ parentElm = dom.create(toType);
2645
+ if (tableElm.firstChild) {
2646
+ tableElm.insertBefore(parentElm, tableElm.firstChild);
2647
+ } else {
2648
+ tableElm.appendChild(parentElm);
2649
+ }
2650
+ }
2651
+
2652
+ parentElm.appendChild(rowElm);
2653
+
2654
+ if (!oldParentElm.hasChildNodes()) {
2655
+ dom.remove(oldParentElm);
2656
+ }
2657
+ }
2658
+
2659
+ // Apply/remove alignment
2660
+ unApplyAlign(rowElm);
2661
+ if (data.align) {
2662
+ editor.formatter.apply('align' + data.align, {}, rowElm);
2663
+ }
2664
+ });
2665
+
2666
+ editor.focus();
2667
+ });
2668
+ }
2669
+
2670
+ tableElm = editor.dom.getParent(editor.selection.getStart(), 'table');
2671
+ cellElm = editor.dom.getParent(editor.selection.getStart(), 'td,th');
2672
+
2673
+ each(tableElm.rows, function(row) {
2674
+ each(row.cells, function(cell) {
2675
+ if (dom.getAttrib(cell, 'data-mce-selected') || cell == cellElm) {
2676
+ rows.push(row);
2677
+ return false;
2678
+ }
2679
+ });
2680
+ });
2681
+
2682
+ rowElm = rows[0];
2683
+ if (!rowElm) {
2684
+ // If this element is null, return now to avoid crashing.
2685
+ return;
2686
+ }
2687
+
2688
+ data = {
2689
+ height: removePxSuffix(dom.getStyle(rowElm, 'height') || dom.getAttrib(rowElm, 'height')),
2690
+ scope: dom.getAttrib(rowElm, 'scope'),
2691
+ 'class': dom.getAttrib(rowElm, 'class')
2692
+ };
2693
+
2694
+ data.type = rowElm.parentNode.nodeName.toLowerCase();
2695
+
2696
+ each('left center right'.split(' '), function(name) {
2697
+ if (editor.formatter.matchNode(rowElm, 'align' + name)) {
2698
+ data.align = name;
2699
+ }
2700
+ });
2701
+
2702
+ if (editor.settings.table_row_class_list) {
2703
+ classListCtrl = {
2704
+ name: 'class',
2705
+ type: 'listbox',
2706
+ label: 'Class',
2707
+ values: buildListItems(
2708
+ editor.settings.table_row_class_list,
2709
+ function(item) {
2710
+ if (item.value) {
2711
+ item.textStyle = function() {
2712
+ return editor.formatter.getCssText({block: 'tr', classes: [item.value]});
2713
+ };
2714
+ }
2715
+ }
2716
+ )
2717
+ };
2718
+ }
2719
+
2720
+ generalRowForm = {
2721
+ type: 'form',
2722
+ columns: 2,
2723
+ padding: 0,
2724
+ defaults: {
2725
+ type: 'textbox'
2726
+ },
2727
+ items: [
2728
+ {
2729
+ type: 'listbox',
2730
+ name: 'type',
2731
+ label: 'Row type',
2732
+ text: 'None',
2733
+ maxWidth: null,
2734
+ values: [
2735
+ {text: 'Header', value: 'thead'},
2736
+ {text: 'Body', value: 'tbody'},
2737
+ {text: 'Footer', value: 'tfoot'}
2738
+ ]
2739
+ },
2740
+ {
2741
+ type: 'listbox',
2742
+ name: 'align',
2743
+ label: 'Alignment',
2744
+ text: 'None',
2745
+ maxWidth: null,
2746
+ values: [
2747
+ {text: 'None', value: ''},
2748
+ {text: 'Left', value: 'left'},
2749
+ {text: 'Center', value: 'center'},
2750
+ {text: 'Right', value: 'right'}
2751
+ ]
2752
+ },
2753
+ {label: 'Height', name: 'height'},
2754
+ classListCtrl
2755
+ ]
2756
+ };
2757
+
2758
+ if (editor.settings.table_row_advtab !== false) {
2759
+ appendStylesToData(dom, data, rowElm);
2760
+
2761
+ editor.windowManager.open({
2762
+ title: "Row properties",
2763
+ data: data,
2764
+ bodyType: 'tabpanel',
2765
+ body: [
2766
+ {
2767
+ title: 'General',
2768
+ type: 'form',
2769
+ items: generalRowForm
2770
+ },
2771
+ createStyleForm(dom)
2772
+ ],
2773
+
2774
+ onsubmit: onSubmitRowForm
2775
+ });
2776
+ } else {
2777
+ editor.windowManager.open({
2778
+ title: "Row properties",
2779
+ data: data,
2780
+ body: generalRowForm,
2781
+ onsubmit: onSubmitRowForm
2782
+ });
2783
+ }
2784
+ };
2785
+ };
2786
+ });
2787
+
2788
+ // Included from: js/tinymce/plugins/table/classes/ResizeBars.js
2789
+
2790
+ /**
2791
+ * ResizeBars.js
2792
+ *
2793
+ * Released under LGPL License.
2794
+ * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
2795
+ *
2796
+ * License: http://www.tinymce.com/license
2797
+ * Contributing: http://www.tinymce.com/contributing
2798
+ */
2799
+
2800
+ /**
2801
+ * This class handles table column and row resizing by adding divs over the columns and rows of the table.
2802
+ * These divs are then manipulated using mouse events to resize the underlying table.
2803
+ *
2804
+ * @class tinymce.tableplugin.ResizeBars
2805
+ * @private
2806
+ */
2807
+ define("tinymce/tableplugin/ResizeBars", [
2808
+ "tinymce/util/Tools",
2809
+ "tinymce/util/VK"
2810
+ ], function(Tools, VK) {
2811
+ var hoverTable;
2812
+
2813
+ return function(editor) {
2814
+ var RESIZE_BAR_CLASS = 'mce-resize-bar',
2815
+ RESIZE_BAR_ROW_CLASS = 'mce-resize-bar-row',
2816
+ RESIZE_BAR_ROW_CURSOR_STYLE = 'row-resize',
2817
+ RESIZE_BAR_ROW_DATA_ATTRIBUTE = 'data-row',
2818
+ RESIZE_BAR_ROW_DATA_INITIAL_TOP_ATTRIBUTE = 'data-initial-top',
2819
+ RESIZE_BAR_COL_CLASS = 'mce-resize-bar-col',
2820
+ RESIZE_BAR_COL_CURSOR_STYLE = 'col-resize',
2821
+ RESIZE_BAR_COL_DATA_ATTRIBUTE = 'data-col',
2822
+ RESIZE_BAR_COL_DATA_INITIAL_LEFT_ATTRIBUTE = 'data-initial-left',
2823
+ RESIZE_BAR_THICKNESS = 4,
2824
+ RESIZE_MINIMUM_WIDTH = 10,
2825
+ RESIZE_MINIMUM_HEIGHT = 10,
2826
+ RESIZE_BAR_DRAGGING_CLASS = 'mce-resize-bar-dragging';
2827
+
2828
+ var percentageBasedSizeRegex = new RegExp(/(\d+(\.\d+)?%)/),
2829
+ pixelBasedSizeRegex = new RegExp(/px|em/);
2830
+
2831
+ var delayDrop, dragging, blockerElement, dragBar, lastX, lastY;
2832
+
2833
+ // Get the absolute position's top edge.
2834
+ function getTopEdge(index, row) {
2835
+ return {
2836
+ index: index,
2837
+ y: editor.dom.getPos(row).y
2838
+ };
2839
+ }
2840
+
2841
+ // Get the absolute position's bottom edge.
2842
+ function getBottomEdge(index, row) {
2843
+ return {
2844
+ index: index,
2845
+ y: editor.dom.getPos(row).y + row.offsetHeight
2846
+ };
2847
+ }
2848
+
2849
+ // Get the absolute position's left edge.
2850
+ function getLeftEdge(index, cell) {
2851
+ return {
2852
+ index: index,
2853
+ x: editor.dom.getPos(cell).x
2854
+ };
2855
+ }
2856
+
2857
+ // Get the absolute position's right edge.
2858
+ function getRightEdge(index, cell) {
2859
+ return {
2860
+ index: index,
2861
+ x: editor.dom.getPos(cell).x + cell.offsetWidth
2862
+ };
2863
+ }
2864
+
2865
+ function isRtl() {
2866
+ var dir = editor.getBody().dir;
2867
+ return dir === 'rtl';
2868
+ }
2869
+
2870
+ function isInline() {
2871
+ return editor.inline;
2872
+ }
2873
+
2874
+ function getBody() {
2875
+ return isInline ? editor.getBody().ownerDocument.body : editor.getBody();
2876
+ }
2877
+
2878
+ function getInnerEdge(index, cell) {
2879
+ return isRtl() ? getRightEdge(index, cell) : getLeftEdge(index, cell);
2880
+ }
2881
+
2882
+ function getOuterEdge(index, cell) {
2883
+ return isRtl() ? getLeftEdge(index, cell) : getRightEdge(index, cell);
2884
+ }
2885
+
2886
+ function getPercentageWidthFallback(element, table) {
2887
+ return getComputedStyleSize(element, 'width') / getComputedStyleSize(table, 'width') * 100;
2888
+ }
2889
+
2890
+ function getComputedStyleSize(element, property) {
2891
+ var widthString = editor.dom.getStyle(element, property, true);
2892
+ var width = parseInt(widthString, 10);
2893
+ return width;
2894
+ }
2895
+
2896
+ function getCurrentTablePercentWidth(table) {
2897
+ var tableWidth = getComputedStyleSize(table, 'width');
2898
+ var tableParentWidth = getComputedStyleSize(table.parentElement, 'width');
2899
+ return tableWidth / tableParentWidth * 100;
2900
+ }
2901
+
2902
+ function getCellPercentDelta(table, delta) {
2903
+ var tableWidth = getComputedStyleSize(table, 'width');
2904
+ return delta / tableWidth * 100;
2905
+ }
2906
+
2907
+ function getTablePercentDelta(table, delta) {
2908
+ var tableParentWidth = getComputedStyleSize(table.parentElement, 'width');
2909
+ return delta / tableParentWidth * 100;
2910
+ }
2911
+
2912
+ // Find the left/right (ltr/rtl) or top side locations of the cells to measure.
2913
+ // This is the location of the borders we need to draw over.
2914
+ function findPositions(getInner, getOuter, thingsToMeasure) {
2915
+ var tablePositions = [];
2916
+
2917
+ // Skip the first item in the array = no left (LTR), right (RTL) or top bars
2918
+ for (var i = 1; i < thingsToMeasure.length; i++) {
2919
+ // Get the element from the details
2920
+ var item = thingsToMeasure[i].element;
2921
+
2922
+ // We need to zero index this again
2923
+ tablePositions.push(getInner(i - 1, item));
2924
+ }
2925
+
2926
+ var lastTableLineToMake = thingsToMeasure[thingsToMeasure.length - 1];
2927
+ tablePositions.push(getOuter(thingsToMeasure.length - 1, lastTableLineToMake.element));
2928
+
2929
+ return tablePositions;
2930
+ }
2931
+
2932
+ // Clear the bars.
2933
+ function clearBars() {
2934
+ var bars = editor.dom.select('.' + RESIZE_BAR_CLASS, getBody());
2935
+ Tools.each(bars, function(bar) {
2936
+ editor.dom.remove(bar);
2937
+ });
2938
+ }
2939
+
2940
+ // Refresh the bars.
2941
+ function refreshBars(tableElement) {
2942
+ clearBars();
2943
+ drawBars(tableElement);
2944
+ }
2945
+
2946
+ // Generates a resize bar object for the editor to add.
2947
+ function generateBar(classToAdd, cursor, left, top, height, width, indexAttr, index) {
2948
+ var bar = {
2949
+ 'data-mce-bogus': 'all',
2950
+ 'class': RESIZE_BAR_CLASS + ' ' + classToAdd,
2951
+ 'unselectable': 'on',
2952
+ 'data-mce-resize': false,
2953
+ style: 'cursor: ' + cursor + '; ' +
2954
+ 'margin: 0; ' +
2955
+ 'padding: 0; ' +
2956
+ 'position: absolute; ' +
2957
+ 'left: ' + left + 'px; ' +
2958
+ 'top: ' + top + 'px; ' +
2959
+ 'height: ' + height + 'px; ' +
2960
+ 'width: ' + width + 'px; '
2961
+ };
2962
+
2963
+ bar[indexAttr] = index;
2964
+
2965
+ return bar;
2966
+ }
2967
+
2968
+ // Draw the row bars over the row borders.
2969
+ function drawRows(rowPositions, tableWidth, tablePosition) {
2970
+ Tools.each(rowPositions, function(rowPosition) {
2971
+ var left = tablePosition.x,
2972
+ top = rowPosition.y - RESIZE_BAR_THICKNESS / 2,
2973
+ height = RESIZE_BAR_THICKNESS,
2974
+ width = tableWidth;
2975
+
2976
+ editor.dom.add(getBody(), 'div',
2977
+ generateBar(RESIZE_BAR_ROW_CLASS, RESIZE_BAR_ROW_CURSOR_STYLE,
2978
+ left, top, height, width, RESIZE_BAR_ROW_DATA_ATTRIBUTE, rowPosition.index));
2979
+ });
2980
+ }
2981
+
2982
+ // Draw the column bars over the column borders.
2983
+ function drawCols(cellPositions, tableHeight, tablePosition) {
2984
+ Tools.each(cellPositions, function(cellPosition) {
2985
+ var left = cellPosition.x - RESIZE_BAR_THICKNESS / 2,
2986
+ top = tablePosition.y,
2987
+ height = tableHeight,
2988
+ width = RESIZE_BAR_THICKNESS;
2989
+
2990
+ editor.dom.add(getBody(), 'div',
2991
+ generateBar(RESIZE_BAR_COL_CLASS, RESIZE_BAR_COL_CURSOR_STYLE,
2992
+ left, top, height, width, RESIZE_BAR_COL_DATA_ATTRIBUTE, cellPosition.index));
2993
+ });
2994
+ }
2995
+
2996
+ // Get a matrix of the cells in each row and the rows in the table.
2997
+ function getTableDetails(table) {
2998
+ return Tools.map(table.rows, function(row) {
2999
+
3000
+ var cells = Tools.map(row.cells, function(cell) {
3001
+
3002
+ var rowspan = cell.hasAttribute('rowspan') ? parseInt(cell.getAttribute('rowspan'), 10) : 1;
3003
+ var colspan = cell.hasAttribute('colspan') ? parseInt(cell.getAttribute('colspan'), 10) : 1;
3004
+
3005
+ return {
3006
+ element: cell,
3007
+ rowspan: rowspan,
3008
+ colspan: colspan
3009
+ };
3010
+ });
3011
+
3012
+ return {
3013
+ element: row,
3014
+ cells: cells
3015
+ };
3016
+
3017
+ });
3018
+
3019
+ }
3020
+
3021
+ // Get a grid model of the table.
3022
+ function getTableGrid(tableDetails) {
3023
+ function key(rowIndex, colIndex) {
3024
+ return rowIndex + ',' + colIndex;
3025
+ }
3026
+
3027
+ function getAt(rowIndex, colIndex) {
3028
+ return access[key(rowIndex, colIndex)];
3029
+ }
3030
+
3031
+ function getAllCells() {
3032
+ var allCells = [];
3033
+ Tools.each(rows, function(row) {
3034
+ allCells = allCells.concat(row.cells);
3035
+ });
3036
+ return allCells;
3037
+ }
3038
+
3039
+ function getAllRows() {
3040
+ return rows;
3041
+ }
3042
+
3043
+ var access = {};
3044
+ var rows = [];
3045
+
3046
+ var maxRows = 0;
3047
+ var maxCols = 0;
3048
+
3049
+ Tools.each(tableDetails, function(row, rowIndex) {
3050
+ var currentRow = [];
3051
+
3052
+ Tools.each(row.cells, function(cell) {
3053
+
3054
+ var start = 0;
3055
+
3056
+ while (access[key(rowIndex, start)] !== undefined) {
3057
+ start++;
3058
+ }
3059
+
3060
+ var current = {
3061
+ element: cell.element,
3062
+ colspan: cell.colspan,
3063
+ rowspan: cell.rowspan,
3064
+ rowIndex: rowIndex,
3065
+ colIndex: start
3066
+ };
3067
+
3068
+ for (var i = 0; i < cell.colspan; i++) {
3069
+ for (var j = 0; j < cell.rowspan; j++) {
3070
+ var cr = rowIndex + j;
3071
+ var cc = start + i;
3072
+ access[key(cr, cc)] = current;
3073
+ maxRows = Math.max(maxRows, cr + 1);
3074
+ maxCols = Math.max(maxCols, cc + 1);
3075
+ }
3076
+ }
3077
+
3078
+ currentRow.push(current);
3079
+ });
3080
+
3081
+ rows.push({
3082
+ element: row.element,
3083
+ cells: currentRow
3084
+ });
3085
+ });
3086
+
3087
+ return {
3088
+ grid: {
3089
+ maxRows: maxRows,
3090
+ maxCols: maxCols
3091
+ },
3092
+ getAt: getAt,
3093
+ getAllCells: getAllCells,
3094
+ getAllRows: getAllRows
3095
+ };
3096
+ }
3097
+
3098
+ function range(start, end) {
3099
+ var r = [];
3100
+
3101
+ for (var i = start; i < end; i++) {
3102
+ r.push(i);
3103
+ }
3104
+
3105
+ return r;
3106
+ }
3107
+
3108
+ // Attempt to get a representative single block for this column.
3109
+ // If we can't find a single block, all blocks in this row/column are spanned
3110
+ // and we'll need to fallback to getting the first cell in the row/column.
3111
+ function decide(getBlock, isSingle, getFallback) {
3112
+ var inBlock = getBlock();
3113
+ var singleInBlock;
3114
+
3115
+ for (var i = 0; i < inBlock.length; i++) {
3116
+ if (isSingle(inBlock[i])) {
3117
+ singleInBlock = inBlock[i];
3118
+ }
3119
+ }
3120
+ return singleInBlock ? singleInBlock : getFallback();
3121
+ }
3122
+
3123
+ // Attempt to get representative blocks for the width of each column.
3124
+ function getColumnBlocks(tableGrid) {
3125
+ var cols = range(0, tableGrid.grid.maxCols);
3126
+ var rows = range(0, tableGrid.grid.maxRows);
3127
+
3128
+ return Tools.map(cols, function(col) {
3129
+ function getBlock() {
3130
+ var details = [];
3131
+ for (var i = 0; i < rows.length; i++) {
3132
+ var detail = tableGrid.getAt(i, col);
3133
+ if (detail && detail.colIndex === col) {
3134
+ details.push(detail);
3135
+ }
3136
+ }
3137
+
3138
+ return details;
3139
+ }
3140
+
3141
+ function isSingle(detail) {
3142
+ return detail.colspan === 1;
3143
+ }
3144
+
3145
+ function getFallback() {
3146
+ var item;
3147
+
3148
+ for (var i = 0; i < rows.length; i++) {
3149
+ item = tableGrid.getAt(i, col);
3150
+ if (item) {
3151
+ return item;
3152
+ }
3153
+ }
3154
+
3155
+ return null;
3156
+ }
3157
+
3158
+ return decide(getBlock, isSingle, getFallback);
3159
+ });
3160
+ }
3161
+
3162
+ // Attempt to get representative blocks for the height of each row.
3163
+ function getRowBlocks(tableGrid) {
3164
+ var cols = range(0, tableGrid.grid.maxCols);
3165
+ var rows = range(0, tableGrid.grid.maxRows);
3166
+
3167
+ return Tools.map(rows, function(row) {
3168
+ function getBlock() {
3169
+ var details = [];
3170
+ for (var i = 0; i < cols.length; i++) {
3171
+ var detail = tableGrid.getAt(row, i);
3172
+ if (detail && detail.rowIndex === row) {
3173
+ details.push(detail);
3174
+ }
3175
+ }
3176
+ return details;
3177
+ }
3178
+
3179
+ function isSingle(detail) {
3180
+ return detail.rowspan === 1;
3181
+ }
3182
+
3183
+ function getFallback() {
3184
+ return tableGrid.getAt(row, 0);
3185
+ }
3186
+
3187
+ return decide(getBlock, isSingle, getFallback);
3188
+ });
3189
+ }
3190
+
3191
+ // Draw resize bars over the left/right (ltr/rtl) or top side locations of the cells to measure.
3192
+ // This is the location of the borders we need to draw over.
3193
+ function drawBars(table) {
3194
+ var tableDetails = getTableDetails(table);
3195
+ var tableGrid = getTableGrid(tableDetails);
3196
+ var rows = getRowBlocks(tableGrid);
3197
+ var cols = getColumnBlocks(tableGrid);
3198
+
3199
+ var tablePosition = editor.dom.getPos(table);
3200
+ var rowPositions = rows.length > 0 ? findPositions(getTopEdge, getBottomEdge, rows) : [];
3201
+ var colPositions = cols.length > 0 ? findPositions(getInnerEdge, getOuterEdge, cols) : [];
3202
+
3203
+ drawRows(rowPositions, table.offsetWidth, tablePosition);
3204
+ drawCols(colPositions, table.offsetHeight, tablePosition);
3205
+ }
3206
+
3207
+ // Attempt to deduce the width/height of a column/row that has more than one cell spanned.
3208
+ function deduceSize(deducables, index, isPercentageBased, table) {
3209
+ if (index < 0 || index >= deducables.length - 1) {
3210
+ return "";
3211
+ }
3212
+
3213
+ var current = deducables[index];
3214
+
3215
+ if (current) {
3216
+ current = {
3217
+ value: current,
3218
+ delta: 0
3219
+ };
3220
+ } else {
3221
+ var reversedUpToIndex = deducables.slice(0, index).reverse();
3222
+ for (var i = 0; i < reversedUpToIndex.length; i++) {
3223
+ if (reversedUpToIndex[i]) {
3224
+ current = {
3225
+ value: reversedUpToIndex[i],
3226
+ delta: i + 1
3227
+ };
3228
+ }
3229
+ }
3230
+ }
3231
+
3232
+ var next = deducables[index + 1];
3233
+
3234
+ if (next) {
3235
+ next = {
3236
+ value: next,
3237
+ delta: 1
3238
+ };
3239
+ } else {
3240
+ var rest = deducables.slice(index + 1);
3241
+ for (var j = 0; j < rest.length; j++) {
3242
+ if (rest[j]) {
3243
+ next = {
3244
+ value: rest[j],
3245
+ delta: j + 1
3246
+ };
3247
+ }
3248
+ }
3249
+ }
3250
+
3251
+ var extras = next.delta - current.delta;
3252
+ var pixelWidth = Math.abs(next.value - current.value) / extras;
3253
+ return isPercentageBased ? pixelWidth / getComputedStyleSize(table, 'width') * 100 : pixelWidth;
3254
+ }
3255
+
3256
+ function getStyleOrAttrib(element, property) {
3257
+ var sizeString = editor.dom.getStyle(element, property);
3258
+ if (!sizeString) {
3259
+ sizeString = editor.dom.getAttrib(element, property);
3260
+ }
3261
+ if (!sizeString) {
3262
+ sizeString = editor.dom.getStyle(element, property, true);
3263
+ }
3264
+ return sizeString;
3265
+ }
3266
+
3267
+ function getWidth(element, isPercentageBased, table) {
3268
+ var widthString = getStyleOrAttrib(element, 'width');
3269
+
3270
+ var widthNumber = parseInt(widthString, 10);
3271
+
3272
+ var getWidthFallback = isPercentageBased ? getPercentageWidthFallback(element, table) : getComputedStyleSize(element, 'width');
3273
+
3274
+ // If this is percentage based table, but this cell isn't percentage based.
3275
+ // Or if this is a pixel based table, but this cell isn't pixel based.
3276
+ if (isPercentageBased && !isPercentageBasedSize(widthString) ||
3277
+ !isPercentageBased && !isPixelBasedSize(widthString)) {
3278
+ // set the widthnumber to 0
3279
+ widthNumber = 0;
3280
+ }
3281
+
3282
+ return !isNaN(widthNumber) && widthNumber > 0 ?
3283
+ widthNumber : getWidthFallback;
3284
+ }
3285
+
3286
+ // Attempt to get the css width from column representative cells.
3287
+ function getWidths(tableGrid, isPercentageBased, table) {
3288
+
3289
+ var cols = getColumnBlocks(tableGrid);
3290
+
3291
+ var backups = Tools.map(cols, function(col) {
3292
+ return getInnerEdge(col.colIndex, col.element).x;
3293
+ });
3294
+
3295
+ var widths = [];
3296
+
3297
+ for (var i = 0; i < cols.length; i++) {
3298
+ var span = cols[i].element.hasAttribute('colspan') ? parseInt(cols[i].element.getAttribute('colspan'), 10) : 1;
3299
+ // Deduce if the column has colspan of more than 1
3300
+ var width = span > 1 ? deduceSize(backups, i) : getWidth(cols[i].element, isPercentageBased, table);
3301
+ // If everything's failed and we still don't have a width
3302
+ width = width ? width : RESIZE_MINIMUM_WIDTH;
3303
+ widths.push(width);
3304
+ }
3305
+
3306
+ return widths;
3307
+ }
3308
+
3309
+ // Attempt to get the pixel height from a cell.
3310
+ function getPixelHeight(element) {
3311
+
3312
+ var heightString = getStyleOrAttrib(element, 'height');
3313
+
3314
+ var heightNumber = parseInt(heightString, 10);
3315
+
3316
+ if (isPercentageBasedSize(heightString)) {
3317
+ heightNumber = 0;
3318
+ }
3319
+
3320
+ return !isNaN(heightNumber) && heightNumber > 0 ?
3321
+ heightNumber : getComputedStyleSize(element, 'height');
3322
+ }
3323
+
3324
+ // Attempt to get the css height from row representative cells.
3325
+ function getPixelHeights(tableGrid) {
3326
+
3327
+ var rows = getRowBlocks(tableGrid);
3328
+
3329
+ var backups = Tools.map(rows, function(row) {
3330
+ return getTopEdge(row.rowIndex, row.element).y;
3331
+ });
3332
+
3333
+ var heights = [];
3334
+
3335
+ for (var i = 0; i < rows.length; i++) {
3336
+ var span = rows[i].element.hasAttribute('rowspan') ? parseInt(rows[i].element.getAttribute('rowspan'), 10) : 1;
3337
+
3338
+ var height = span > 1 ? deduceSize(backups, i) : getPixelHeight(rows[i].element);
3339
+
3340
+ height = height ? height : RESIZE_MINIMUM_HEIGHT;
3341
+ heights.push(height);
3342
+ }
3343
+
3344
+ return heights;
3345
+ }
3346
+
3347
+ // Determine how much each column's css width will need to change.
3348
+ // Sizes = result = pixels widths OR percentage based widths
3349
+ function determineDeltas(sizes, column, step, min, isPercentageBased) {
3350
+
3351
+ var result = sizes.slice(0);
3352
+
3353
+ function generateZeros(array) {
3354
+ return Tools.map(array, function() {
3355
+ return 0;
3356
+ });
3357
+ }
3358
+
3359
+ function onOneColumn() {
3360
+ var deltas;
3361
+ if (isPercentageBased) {
3362
+ // If we have one column in a percent based table, that column should be 100% of the width of the table.
3363
+ deltas = [100 - result[0]];
3364
+ } else {
3365
+ var newNext = Math.max(min, result[0] + step);
3366
+ deltas = [newNext - result[0]];
3367
+ }
3368
+ return deltas;
3369
+ }
3370
+
3371
+ function onLeftOrMiddle(index, next) {
3372
+
3373
+ var startZeros = generateZeros(result.slice(0, index));
3374
+ var endZeros = generateZeros(result.slice(next + 1));
3375
+ var deltas;
3376
+
3377
+ if (step >= 0) {
3378
+ var newNext = Math.max(min, result[next] - step);
3379
+ deltas = startZeros.concat([step, newNext - result[next]]).concat(endZeros);
3380
+ } else {
3381
+ var newThis = Math.max(min, result[index] + step);
3382
+ var diffx = result[index] - newThis;
3383
+ deltas = startZeros.concat([newThis - result[index], diffx]).concat(endZeros);
3384
+ }
3385
+
3386
+ return deltas;
3387
+ }
3388
+
3389
+ function onRight(previous, index) {
3390
+ var startZeros = generateZeros(result.slice(0, index));
3391
+ var deltas;
3392
+
3393
+ if (step >= 0) {
3394
+ deltas = startZeros.concat([step]);
3395
+ } else {
3396
+ var size = Math.max(min, result[index] + step);
3397
+ deltas = startZeros.concat([size - result[index]]);
3398
+ }
3399
+
3400
+ return deltas;
3401
+
3402
+ }
3403
+
3404
+ var deltas;
3405
+
3406
+ if (sizes.length === 0) { // No Columns
3407
+ deltas = [];
3408
+ } else if (sizes.length === 1) { // One Column
3409
+ deltas = onOneColumn();
3410
+ } else if (column === 0) { // Left Column
3411
+ deltas = onLeftOrMiddle(0, 1);
3412
+ } else if (column > 0 && column < sizes.length - 1) { // Middle Column
3413
+ deltas = onLeftOrMiddle(column, column + 1);
3414
+ } else if (column === sizes.length - 1) { // Right Column
3415
+ deltas = onRight(column - 1, column);
3416
+ } else {
3417
+ deltas = [];
3418
+ }
3419
+
3420
+ return deltas;
3421
+ }
3422
+
3423
+ function total(start, end, measures) {
3424
+ var r = 0;
3425
+ for (var i = start; i < end; i++) {
3426
+ r += measures[i];
3427
+ }
3428
+ return r;
3429
+ }
3430
+
3431
+ // Combine cell's css widths to determine widths of colspan'd cells.
3432
+ function recalculateWidths(tableGrid, widths) {
3433
+ var allCells = tableGrid.getAllCells();
3434
+ return Tools.map(allCells, function(cell) {
3435
+ var width = total(cell.colIndex, cell.colIndex + cell.colspan, widths);
3436
+ return {
3437
+ element: cell.element,
3438
+ width: width,
3439
+ colspan: cell.colspan
3440
+ };
3441
+ });
3442
+ }
3443
+
3444
+ // Combine cell's css heights to determine heights of rowspan'd cells.
3445
+ function recalculateCellHeights(tableGrid, heights) {
3446
+ var allCells = tableGrid.getAllCells();
3447
+ return Tools.map(allCells, function(cell) {
3448
+ var height = total(cell.rowIndex, cell.rowIndex + cell.rowspan, heights);
3449
+ return {
3450
+ element: cell.element,
3451
+ height: height,
3452
+ rowspan: cell.rowspan
3453
+ };
3454
+ });
3455
+ }
3456
+
3457
+ // Calculate row heights.
3458
+ function recalculateRowHeights(tableGrid, heights) {
3459
+ var allRows = tableGrid.getAllRows();
3460
+ return Tools.map(allRows, function(row, i) {
3461
+ return {
3462
+ element: row.element,
3463
+ height: heights[i]
3464
+ };
3465
+ });
3466
+ }
3467
+
3468
+ function isPercentageBasedSize(size) {
3469
+ return percentageBasedSizeRegex.test(size);
3470
+ }
3471
+
3472
+ function isPixelBasedSize(size) {
3473
+ return pixelBasedSizeRegex.test(size);
3474
+ }
3475
+
3476
+ // Adjust the width of the column of table at index, with delta.
3477
+ function adjustWidth(table, delta, index) {
3478
+ var tableDetails = getTableDetails(table);
3479
+ var tableGrid = getTableGrid(tableDetails);
3480
+
3481
+ function setSizes(newSizes, styleExtension) {
3482
+ Tools.each(newSizes, function(cell) {
3483
+ editor.dom.setStyle(cell.element, 'width', cell.width + styleExtension);
3484
+ editor.dom.setAttrib(cell.element, 'width', null);
3485
+ });
3486
+ }
3487
+
3488
+ function getNewTablePercentWidth() {
3489
+ return index < tableGrid.grid.maxCols - 1 ? getCurrentTablePercentWidth(table) :
3490
+ getCurrentTablePercentWidth(table) + getTablePercentDelta(table, delta);
3491
+ }
3492
+
3493
+ function getNewTablePixelWidth() {
3494
+ return index < tableGrid.grid.maxCols - 1 ? getComputedStyleSize(table, 'width') :
3495
+ getComputedStyleSize(table, 'width') + delta;
3496
+ }
3497
+
3498
+ function setTableSize(newTableWidth, styleExtension, isPercentBased) {
3499
+ if (index == tableGrid.grid.maxCols - 1 || !isPercentBased) {
3500
+ editor.dom.setStyle(table, 'width', newTableWidth + styleExtension);
3501
+ editor.dom.setAttrib(table, 'width', null);
3502
+ }
3503
+ }
3504
+
3505
+ var percentageBased = isPercentageBasedSize(table.width) ||
3506
+ isPercentageBasedSize(table.style.width);
3507
+
3508
+ var widths = getWidths(tableGrid, percentageBased, table);
3509
+
3510
+ var step = percentageBased ? getCellPercentDelta(table, delta) : delta;
3511
+ // TODO: change the min for percentage maybe?
3512
+ var deltas = determineDeltas(widths, index, step, RESIZE_MINIMUM_WIDTH, percentageBased, table);
3513
+ var newWidths = [];
3514
+
3515
+ for (var i = 0; i < deltas.length; i++) {
3516
+ newWidths.push(deltas[i] + widths[i]);
3517
+ }
3518
+
3519
+ var newSizes = recalculateWidths(tableGrid, newWidths);
3520
+ var styleExtension = percentageBased ? '%' : 'px';
3521
+ var newTableWidth = percentageBased ? getNewTablePercentWidth() :
3522
+ getNewTablePixelWidth();
3523
+
3524
+ editor.undoManager.transact(function() {
3525
+ setSizes(newSizes, styleExtension);
3526
+ setTableSize(newTableWidth, styleExtension, percentageBased);
3527
+ });
3528
+ }
3529
+
3530
+ // Adjust the height of the row of table at index, with delta.
3531
+ function adjustHeight(table, delta, index) {
3532
+ var tableDetails = getTableDetails(table);
3533
+ var tableGrid = getTableGrid(tableDetails);
3534
+
3535
+ var heights = getPixelHeights(tableGrid);
3536
+
3537
+ var newHeights = [], newTotalHeight = 0;
3538
+
3539
+ for (var i = 0; i < heights.length; i++) {
3540
+ newHeights.push(i === index ? delta + heights[i] : heights[i]);
3541
+ newTotalHeight += newTotalHeight[i];
3542
+ }
3543
+
3544
+ var newCellSizes = recalculateCellHeights(tableGrid, newHeights);
3545
+ var newRowSizes = recalculateRowHeights(tableGrid, newHeights);
3546
+
3547
+ editor.undoManager.transact(function() {
3548
+
3549
+ Tools.each(newRowSizes, function(row) {
3550
+ editor.dom.setStyle(row.element, 'height', row.height + 'px');
3551
+ editor.dom.setAttrib(row.element, 'height', null);
3552
+ });
3553
+
3554
+ Tools.each(newCellSizes, function(cell) {
3555
+ editor.dom.setStyle(cell.element, 'height', cell.height + 'px');
3556
+ editor.dom.setAttrib(cell.element, 'height', null);
3557
+ });
3558
+
3559
+ editor.dom.setStyle(table, 'height', newTotalHeight + 'px');
3560
+ editor.dom.setAttrib(table, 'height', null);
3561
+ });
3562
+ }
3563
+
3564
+ function scheduleDelayedDropEvent() {
3565
+ delayDrop = setTimeout(function() {
3566
+ drop();
3567
+ }, 200);
3568
+ }
3569
+
3570
+ function cancelDelayedDropEvent() {
3571
+ clearTimeout(delayDrop);
3572
+ }
3573
+
3574
+ function getBlockerElement() {
3575
+ var blocker = document.createElement('div');
3576
+
3577
+ blocker.setAttribute('style', 'margin: 0; ' +
3578
+ 'padding: 0; ' +
3579
+ 'position: fixed; ' +
3580
+ 'left: 0px; ' +
3581
+ 'top: 0px; ' +
3582
+ 'height: 100%; ' +
3583
+ 'width: 100%;');
3584
+ blocker.setAttribute('data-mce-bogus', 'all');
3585
+
3586
+ return blocker;
3587
+ }
3588
+
3589
+ function bindBlockerEvents(blocker, dragHandler) {
3590
+ editor.dom.bind(blocker, 'mouseup', function() {
3591
+ drop();
3592
+ });
3593
+
3594
+ editor.dom.bind(blocker, 'mousemove', function(e) {
3595
+ cancelDelayedDropEvent();
3596
+
3597
+ if (dragging) {
3598
+ dragHandler(e);
3599
+ }
3600
+ });
3601
+
3602
+ editor.dom.bind(blocker, 'mouseout', function() {
3603
+ scheduleDelayedDropEvent();
3604
+ });
3605
+
3606
+ }
3607
+
3608
+ function drop() {
3609
+ editor.dom.remove(blockerElement);
3610
+
3611
+ if (dragging) {
3612
+ editor.dom.removeClass(dragBar, RESIZE_BAR_DRAGGING_CLASS);
3613
+ dragging = false;
3614
+
3615
+ var index, delta;
3616
+
3617
+ if (isCol(dragBar)) {
3618
+ var initialLeft = parseInt(editor.dom.getAttrib(dragBar, RESIZE_BAR_COL_DATA_INITIAL_LEFT_ATTRIBUTE), 10);
3619
+ var newLeft = editor.dom.getPos(dragBar).x;
3620
+ index = parseInt(editor.dom.getAttrib(dragBar, RESIZE_BAR_COL_DATA_ATTRIBUTE), 10);
3621
+ delta = isRtl() ? initialLeft - newLeft : newLeft - initialLeft;
3622
+ adjustWidth(hoverTable, delta, index);
3623
+ } else if (isRow(dragBar)) {
3624
+ var initialTop = parseInt(editor.dom.getAttrib(dragBar, RESIZE_BAR_ROW_DATA_INITIAL_TOP_ATTRIBUTE), 10);
3625
+ var newTop = editor.dom.getPos(dragBar).y;
3626
+ index = parseInt(editor.dom.getAttrib(dragBar, RESIZE_BAR_ROW_DATA_ATTRIBUTE), 10);
3627
+ delta = newTop - initialTop;
3628
+ adjustHeight(hoverTable, delta, index);
3629
+ }
3630
+ refreshBars(hoverTable);
3631
+ editor.nodeChanged();
3632
+ }
3633
+ }
3634
+
3635
+ function setupBaseDrag(bar, dragHandler) {
3636
+ blockerElement = blockerElement ? blockerElement : getBlockerElement();
3637
+ dragging = true;
3638
+ editor.dom.addClass(bar, RESIZE_BAR_DRAGGING_CLASS);
3639
+ dragBar = bar;
3640
+ bindBlockerEvents(blockerElement, dragHandler);
3641
+ editor.dom.add(getBody(), blockerElement);
3642
+ }
3643
+
3644
+ function isCol(target) {
3645
+ return editor.dom.hasClass(target, RESIZE_BAR_COL_CLASS);
3646
+ }
3647
+
3648
+ function isRow(target) {
3649
+ return editor.dom.hasClass(target, RESIZE_BAR_ROW_CLASS);
3650
+ }
3651
+
3652
+ function colDragHandler(event) {
3653
+ lastX = lastX !== undefined ? lastX : event.clientX; // we need a firstX
3654
+ var deltaX = event.clientX - lastX;
3655
+ lastX = event.clientX;
3656
+ var oldLeft = editor.dom.getPos(dragBar).x;
3657
+ editor.dom.setStyle(dragBar, 'left', oldLeft + deltaX + 'px');
3658
+ }
3659
+
3660
+ function rowDragHandler(event) {
3661
+ lastY = lastY !== undefined ? lastY : event.clientY;
3662
+ var deltaY = event.clientY - lastY;
3663
+ lastY = event.clientY;
3664
+ var oldTop = editor.dom.getPos(dragBar).y;
3665
+ editor.dom.setStyle(dragBar, 'top', oldTop + deltaY + 'px');
3666
+ }
3667
+
3668
+ function setupColDrag(bar) {
3669
+ lastX = undefined;
3670
+ setupBaseDrag(bar, colDragHandler);
3671
+ }
3672
+
3673
+ function setupRowDrag(bar) {
3674
+ lastY = undefined;
3675
+ setupBaseDrag(bar, rowDragHandler);
3676
+ }
3677
+
3678
+ function mouseDownHandler(e) {
3679
+ var target = e.target, body = editor.getBody();
3680
+
3681
+ // Since this code is working on global events we need to work on a global hoverTable state
3682
+ // and make sure that the state is correct according to the events fired
3683
+ if (!editor.$.contains(body, hoverTable) && hoverTable !== body) {
3684
+ return;
3685
+ }
3686
+
3687
+ if (isCol(target)) {
3688
+ e.preventDefault();
3689
+ var initialLeft = editor.dom.getPos(target).x;
3690
+ editor.dom.setAttrib(target, RESIZE_BAR_COL_DATA_INITIAL_LEFT_ATTRIBUTE, initialLeft);
3691
+ setupColDrag(target);
3692
+ } else if (isRow(target)) {
3693
+ e.preventDefault();
3694
+ var initialTop = editor.dom.getPos(target).y;
3695
+ editor.dom.setAttrib(target, RESIZE_BAR_ROW_DATA_INITIAL_TOP_ATTRIBUTE, initialTop);
3696
+ setupRowDrag(target);
3697
+ } else {
3698
+ clearBars();
3699
+ }
3700
+ }
3701
+
3702
+ editor.on('init', function() {
3703
+ // Needs to be like this for inline mode, editor.on does not bind to elements in the document body otherwise
3704
+ editor.dom.bind(getBody(), 'mousedown', mouseDownHandler);
3705
+ });
3706
+
3707
+ // If we're updating the table width via the old mechanic, we need to update the constituent cells' widths/heights too.
3708
+ editor.on('ObjectResized', function(e) {
3709
+ var table = e.target;
3710
+ if (table.nodeName === 'TABLE') {
3711
+ var newCellSizes = [];
3712
+ Tools.each(table.rows, function(row) {
3713
+ Tools.each(row.cells, function(cell) {
3714
+ var width = editor.dom.getStyle(cell, 'width', true);
3715
+ newCellSizes.push({
3716
+ cell: cell,
3717
+ width: width
3718
+ });
3719
+ });
3720
+ });
3721
+ Tools.each(newCellSizes, function(newCellSize) {
3722
+ editor.dom.setStyle(newCellSize.cell, 'width', newCellSize.width);
3723
+ editor.dom.setAttrib(newCellSize.cell, 'width', null);
3724
+ });
3725
+ }
3726
+ });
3727
+
3728
+ editor.on('mouseover', function(e) {
3729
+ if (!dragging) {
3730
+ var tableElement = editor.dom.getParent(e.target, 'table');
3731
+
3732
+ if (e.target.nodeName === 'TABLE' || tableElement) {
3733
+ hoverTable = tableElement;
3734
+ refreshBars(tableElement);
3735
+ }
3736
+ }
3737
+ });
3738
+
3739
+ // Prevents the user from moving the caret inside the resize bars on Chrome
3740
+ // Only does it on arrow keys since clearBars might be an epxensive operation
3741
+ // since it's querying the DOM
3742
+ editor.on('keydown', function(e) {
3743
+ switch (e.keyCode) {
3744
+ case VK.LEFT:
3745
+ case VK.RIGHT:
3746
+ case VK.UP:
3747
+ case VK.DOWN:
3748
+ clearBars();
3749
+ break;
3750
+ }
3751
+ });
3752
+
3753
+ editor.on('remove', function() {
3754
+ clearBars();
3755
+ editor.dom.unbind(getBody(), 'mousedown', mouseDownHandler);
3756
+ });
3757
+
3758
+ return {
3759
+ adjustWidth: adjustWidth,
3760
+ adjustHeight: adjustHeight,
3761
+ clearBars: clearBars,
3762
+ drawBars: drawBars,
3763
+ determineDeltas: determineDeltas,
3764
+ getTableGrid: getTableGrid,
3765
+ getTableDetails: getTableDetails,
3766
+ getWidths: getWidths,
3767
+ getPixelHeights: getPixelHeights,
3768
+ isPercentageBasedSize: isPercentageBasedSize,
3769
+ isPixelBasedSize: isPixelBasedSize,
3770
+ recalculateWidths: recalculateWidths,
3771
+ recalculateCellHeights: recalculateCellHeights,
3772
+ recalculateRowHeights: recalculateRowHeights
3773
+ };
3774
+ };
3775
+ });
3776
+
3777
+ // Included from: js/tinymce/plugins/table/classes/Plugin.js
3778
+
3779
+ /**
3780
+ * Plugin.js
3781
+ *
3782
+ * Released under LGPL License.
3783
+ * Copyright (c) 1999-2015 Ephox Corp. All rights reserved
3784
+ *
3785
+ * License: http://www.tinymce.com/license
3786
+ * Contributing: http://www.tinymce.com/contributing
3787
+ */
3788
+
3789
+ /**
3790
+ * This class contains all core logic for the table plugin.
3791
+ *
3792
+ * @class tinymce.tableplugin.Plugin
3793
+ * @private
3794
+ */
3795
+ define("tinymce/tableplugin/Plugin", [
3796
+ "tinymce/tableplugin/TableGrid",
3797
+ "tinymce/tableplugin/Quirks",
3798
+ "tinymce/tableplugin/CellSelection",
3799
+ "tinymce/tableplugin/Dialogs",
3800
+ "tinymce/tableplugin/ResizeBars",
3801
+ "tinymce/util/Tools",
3802
+ "tinymce/dom/TreeWalker",
3803
+ "tinymce/Env",
3804
+ "tinymce/PluginManager"
3805
+ ], function(TableGrid, Quirks, CellSelection, Dialogs, ResizeBars, Tools, TreeWalker, Env, PluginManager) {
3806
+ var each = Tools.each;
3807
+
3808
+ function Plugin(editor) {
3809
+ var clipboardRows, self = this, dialogs = new Dialogs(editor), resizeBars;
3810
+
3811
+ if (editor.settings.object_resizing && editor.settings.table_resize_bars !== false &&
3812
+ (editor.settings.object_resizing === true || editor.settings.object_resizing === 'table')) {
3813
+ resizeBars = ResizeBars(editor);
3814
+ }
3815
+
3816
+ function cmd(command) {
3817
+ return function() {
3818
+ editor.execCommand(command);
3819
+ };
3820
+ }
3821
+
3822
+ function insertTable(cols, rows) {
3823
+ var y, x, html, tableElm;
3824
+
3825
+ html = '<table id="__mce"><tbody>';
3826
+
3827
+ for (y = 0; y < rows; y++) {
3828
+ html += '<tr>';
3829
+
3830
+ for (x = 0; x < cols; x++) {
3831
+ html += '<td>' + (Env.ie && Env.ie < 10 ? '&nbsp;' : '<br>') + '</td>';
3832
+ }
3833
+
3834
+ html += '</tr>';
3835
+ }
3836
+
3837
+ html += '</tbody></table>';
3838
+
3839
+ editor.undoManager.transact(function() {
3840
+ editor.insertContent(html);
3841
+
3842
+ tableElm = editor.dom.get('__mce');
3843
+ editor.dom.setAttrib(tableElm, 'id', null);
3844
+
3845
+ editor.$('tr', tableElm).each(function(index, row) {
3846
+ editor.fire('newrow', {
3847
+ node: row
3848
+ });
3849
+
3850
+ editor.$('th,td', row).each(function(index, cell) {
3851
+ editor.fire('newcell', {
3852
+ node: cell
3853
+ });
3854
+ });
3855
+ });
3856
+
3857
+ editor.dom.setAttribs(tableElm, editor.settings.table_default_attributes || {});
3858
+ editor.dom.setStyles(tableElm, editor.settings.table_default_styles || {});
3859
+ });
3860
+
3861
+ return tableElm;
3862
+ }
3863
+
3864
+ function handleDisabledState(ctrl, selector, sameParts) {
3865
+ function bindStateListener() {
3866
+ var selectedElm, selectedCells, parts = {}, sum = 0, state;
3867
+
3868
+ selectedCells = editor.dom.select('td[data-mce-selected],th[data-mce-selected]');
3869
+ selectedElm = selectedCells[0];
3870
+ if (!selectedElm) {
3871
+ selectedElm = editor.selection.getStart();
3872
+ }
3873
+
3874
+ // Make sure that we don't have a selection inside thead and tbody at the same time
3875
+ if (sameParts && selectedCells.length > 0) {
3876
+ each(selectedCells, function(cell) {
3877
+ return parts[cell.parentNode.parentNode.nodeName] = 1;
3878
+ });
3879
+
3880
+ each(parts, function(value) {
3881
+ sum += value;
3882
+ });
3883
+
3884
+ state = sum !== 1;
3885
+ } else {
3886
+ state = !editor.dom.getParent(selectedElm, selector);
3887
+ }
3888
+
3889
+ ctrl.disabled(state);
3890
+
3891
+ editor.selection.selectorChanged(selector, function(state) {
3892
+ ctrl.disabled(!state);
3893
+ });
3894
+ }
3895
+
3896
+ if (editor.initialized) {
3897
+ bindStateListener();
3898
+ } else {
3899
+ editor.on('init', bindStateListener);
3900
+ }
3901
+ }
3902
+
3903
+ function postRender() {
3904
+ /*jshint validthis:true*/
3905
+ handleDisabledState(this, 'table');
3906
+ }
3907
+
3908
+ function postRenderCell() {
3909
+ /*jshint validthis:true*/
3910
+ handleDisabledState(this, 'td,th');
3911
+ }
3912
+
3913
+ function postRenderMergeCell() {
3914
+ /*jshint validthis:true*/
3915
+ handleDisabledState(this, 'td,th', true);
3916
+ }
3917
+
3918
+ function generateTableGrid() {
3919
+ var html = '';
3920
+
3921
+ html = '<table role="grid" class="mce-grid mce-grid-border" aria-readonly="true">';
3922
+
3923
+ for (var y = 0; y < 10; y++) {
3924
+ html += '<tr>';
3925
+
3926
+ for (var x = 0; x < 10; x++) {
3927
+ html += '<td role="gridcell" tabindex="-1"><a id="mcegrid' + (y * 10 + x) + '" href="#" ' +
3928
+ 'data-mce-x="' + x + '" data-mce-y="' + y + '"></a></td>';
3929
+ }
3930
+
3931
+ html += '</tr>';
3932
+ }
3933
+
3934
+ html += '</table>';
3935
+
3936
+ html += '<div class="mce-text-center" role="presentation">1 x 1</div>';
3937
+
3938
+ return html;
3939
+ }
3940
+
3941
+ function selectGrid(tx, ty, control) {
3942
+ var table = control.getEl().getElementsByTagName('table')[0];
3943
+ var x, y, focusCell, cell, active;
3944
+ var rtl = control.isRtl() || control.parent().rel == 'tl-tr';
3945
+
3946
+ table.nextSibling.innerHTML = (tx + 1) + ' x ' + (ty + 1);
3947
+
3948
+ if (rtl) {
3949
+ tx = 9 - tx;
3950
+ }
3951
+
3952
+ for (y = 0; y < 10; y++) {
3953
+ for (x = 0; x < 10; x++) {
3954
+ cell = table.rows[y].childNodes[x].firstChild;
3955
+ active = (rtl ? x >= tx : x <= tx) && y <= ty;
3956
+
3957
+ editor.dom.toggleClass(cell, 'mce-active', active);
3958
+
3959
+ if (active) {
3960
+ focusCell = cell;
3961
+ }
3962
+ }
3963
+ }
3964
+
3965
+ return focusCell.parentNode;
3966
+ }
3967
+
3968
+ if (editor.settings.table_grid === false) {
3969
+ editor.addMenuItem('inserttable', {
3970
+ text: 'Insert table',
3971
+ icon: 'table',
3972
+ context: 'table',
3973
+ onclick: dialogs.table
3974
+ });
3975
+ } else {
3976
+ editor.addMenuItem('inserttable', {
3977
+ text: 'Insert table',
3978
+ icon: 'table',
3979
+ context: 'table',
3980
+ ariaHideMenu: true,
3981
+ onclick: function(e) {
3982
+ if (e.aria) {
3983
+ this.parent().hideAll();
3984
+ e.stopImmediatePropagation();
3985
+ dialogs.table();
3986
+ }
3987
+ },
3988
+ onshow: function() {
3989
+ selectGrid(0, 0, this.menu.items()[0]);
3990
+ },
3991
+ onhide: function() {
3992
+ var elements = this.menu.items()[0].getEl().getElementsByTagName('a');
3993
+ editor.dom.removeClass(elements, 'mce-active');
3994
+ editor.dom.addClass(elements[0], 'mce-active');
3995
+ },
3996
+ menu: [
3997
+ {
3998
+ type: 'container',
3999
+ html: generateTableGrid(),
4000
+
4001
+ onPostRender: function() {
4002
+ this.lastX = this.lastY = 0;
4003
+ },
4004
+
4005
+ onmousemove: function(e) {
4006
+ var target = e.target, x, y;
4007
+
4008
+ if (target.tagName.toUpperCase() == 'A') {
4009
+ x = parseInt(target.getAttribute('data-mce-x'), 10);
4010
+ y = parseInt(target.getAttribute('data-mce-y'), 10);
4011
+
4012
+ if (this.isRtl() || this.parent().rel == 'tl-tr') {
4013
+ x = 9 - x;
4014
+ }
4015
+
4016
+ if (x !== this.lastX || y !== this.lastY) {
4017
+ selectGrid(x, y, e.control);
4018
+
4019
+ this.lastX = x;
4020
+ this.lastY = y;
4021
+ }
4022
+ }
4023
+ },
4024
+
4025
+ onclick: function(e) {
4026
+ var self = this;
4027
+
4028
+ if (e.target.tagName.toUpperCase() == 'A') {
4029
+ e.preventDefault();
4030
+ e.stopPropagation();
4031
+ self.parent().cancel();
4032
+
4033
+ editor.undoManager.transact(function() {
4034
+ insertTable(self.lastX + 1, self.lastY + 1);
4035
+ });
4036
+
4037
+ editor.addVisual();
4038
+ }
4039
+ }
4040
+ }
4041
+ ]
4042
+ });
4043
+ }
4044
+
4045
+ editor.addMenuItem('tableprops', {
4046
+ text: 'Table properties',
4047
+ context: 'table',
4048
+ onPostRender: postRender,
4049
+ onclick: dialogs.tableProps
4050
+ });
4051
+
4052
+ editor.addMenuItem('deletetable', {
4053
+ text: 'Delete table',
4054
+ context: 'table',
4055
+ onPostRender: postRender,
4056
+ cmd: 'mceTableDelete'
4057
+ });
4058
+
4059
+ editor.addMenuItem('cell', {
4060
+ separator: 'before',
4061
+ text: 'Cell',
4062
+ context: 'table',
4063
+ menu: [
4064
+ {text: 'Cell properties', onclick: cmd('mceTableCellProps'), onPostRender: postRenderCell},
4065
+ {text: 'Merge cells', onclick: cmd('mceTableMergeCells'), onPostRender: postRenderMergeCell},
4066
+ {text: 'Split cell', onclick: cmd('mceTableSplitCells'), onPostRender: postRenderCell}
4067
+ ]
4068
+ });
4069
+
4070
+ editor.addMenuItem('row', {
4071
+ text: 'Row',
4072
+ context: 'table',
4073
+ menu: [
4074
+ {text: 'Insert row before', onclick: cmd('mceTableInsertRowBefore'), onPostRender: postRenderCell},
4075
+ {text: 'Insert row after', onclick: cmd('mceTableInsertRowAfter'), onPostRender: postRenderCell},
4076
+ {text: 'Delete row', onclick: cmd('mceTableDeleteRow'), onPostRender: postRenderCell},
4077
+ {text: 'Row properties', onclick: cmd('mceTableRowProps'), onPostRender: postRenderCell},
4078
+ {text: '-'},
4079
+ {text: 'Cut row', onclick: cmd('mceTableCutRow'), onPostRender: postRenderCell},
4080
+ {text: 'Copy row', onclick: cmd('mceTableCopyRow'), onPostRender: postRenderCell},
4081
+ {text: 'Paste row before', onclick: cmd('mceTablePasteRowBefore'), onPostRender: postRenderCell},
4082
+ {text: 'Paste row after', onclick: cmd('mceTablePasteRowAfter'), onPostRender: postRenderCell}
4083
+ ]
4084
+ });
4085
+
4086
+ editor.addMenuItem('column', {
4087
+ text: 'Column',
4088
+ context: 'table',
4089
+ menu: [
4090
+ {text: 'Insert column before', onclick: cmd('mceTableInsertColBefore'), onPostRender: postRenderCell},
4091
+ {text: 'Insert column after', onclick: cmd('mceTableInsertColAfter'), onPostRender: postRenderCell},
4092
+ {text: 'Delete column', onclick: cmd('mceTableDeleteCol'), onPostRender: postRenderCell}
4093
+ ]
4094
+ });
4095
+
4096
+ var menuItems = [];
4097
+ each("inserttable tableprops deletetable | cell row column".split(' '), function(name) {
4098
+ if (name == '|') {
4099
+ menuItems.push({text: '-'});
4100
+ } else {
4101
+ menuItems.push(editor.menuItems[name]);
4102
+ }
4103
+ });
4104
+
4105
+ editor.addButton("table", {
4106
+ type: "menubutton",
4107
+ title: "Table",
4108
+ menu: menuItems
4109
+ });
4110
+
4111
+ // Select whole table is a table border is clicked
4112
+ if (!Env.isIE) {
4113
+ editor.on('click', function(e) {
4114
+ e = e.target;
4115
+
4116
+ if (e.nodeName === 'TABLE') {
4117
+ editor.selection.select(e);
4118
+ editor.nodeChanged();
4119
+ }
4120
+ });
4121
+ }
4122
+
4123
+ self.quirks = new Quirks(editor);
4124
+
4125
+ editor.on('Init', function() {
4126
+ self.cellSelection = new CellSelection(editor, function (selecting) {
4127
+ if (selecting) {
4128
+ resizeBars.clearBars();
4129
+ }
4130
+ });
4131
+ self.resizeBars = resizeBars;
4132
+ });
4133
+
4134
+ editor.on('PreInit', function() {
4135
+ // Remove internal data attributes
4136
+ editor.serializer.addAttributeFilter(
4137
+ 'data-mce-cell-padding,data-mce-border,data-mce-border-color',
4138
+ function(nodes, name) {
4139
+
4140
+ var i = nodes.length;
4141
+
4142
+ while (i--) {
4143
+ nodes[i].attr(name, null);
4144
+ }
4145
+ });
4146
+ });
4147
+
4148
+ // Register action commands
4149
+ each({
4150
+ mceTableSplitCells: function(grid) {
4151
+ grid.split();
4152
+ },
4153
+
4154
+ mceTableMergeCells: function(grid) {
4155
+ var cell;
4156
+
4157
+ cell = editor.dom.getParent(editor.selection.getStart(), 'th,td');
4158
+
4159
+ if (!editor.dom.select('td[data-mce-selected],th[data-mce-selected]').length) {
4160
+ dialogs.merge(grid, cell);
4161
+ } else {
4162
+ grid.merge();
4163
+ }
4164
+ },
4165
+
4166
+ mceTableInsertRowBefore: function(grid) {
4167
+ grid.insertRow(true);
4168
+ },
4169
+
4170
+ mceTableInsertRowAfter: function(grid) {
4171
+ grid.insertRow();
4172
+ },
4173
+
4174
+ mceTableInsertColBefore: function(grid) {
4175
+ grid.insertCol(true);
4176
+ },
4177
+
4178
+ mceTableInsertColAfter: function(grid) {
4179
+ grid.insertCol();
4180
+ },
4181
+
4182
+ mceTableDeleteCol: function(grid) {
4183
+ grid.deleteCols();
4184
+ },
4185
+
4186
+ mceTableDeleteRow: function(grid) {
4187
+ grid.deleteRows();
4188
+ },
4189
+
4190
+ mceTableCutRow: function(grid) {
4191
+ clipboardRows = grid.cutRows();
4192
+ },
4193
+
4194
+ mceTableCopyRow: function(grid) {
4195
+ clipboardRows = grid.copyRows();
4196
+ },
4197
+
4198
+ mceTablePasteRowBefore: function(grid) {
4199
+ grid.pasteRows(clipboardRows, true);
4200
+ },
4201
+
4202
+ mceTablePasteRowAfter: function(grid) {
4203
+ grid.pasteRows(clipboardRows);
4204
+ },
4205
+
4206
+ mceSplitColsBefore: function(grid) {
4207
+ grid.splitCols(true);
4208
+ },
4209
+
4210
+ mceSplitColsAfter: function(grid) {
4211
+ grid.splitCols(false);
4212
+ },
4213
+
4214
+ mceTableDelete: function(grid) {
4215
+ if (resizeBars) {
4216
+ resizeBars.clearBars();
4217
+ }
4218
+ grid.deleteTable();
4219
+ }
4220
+ }, function(func, name) {
4221
+ editor.addCommand(name, function() {
4222
+ var grid = new TableGrid(editor);
4223
+
4224
+ if (grid) {
4225
+ func(grid);
4226
+ editor.execCommand('mceRepaint');
4227
+ self.cellSelection.clear();
4228
+ }
4229
+ });
4230
+ });
4231
+
4232
+ // Register dialog commands
4233
+ each({
4234
+ mceInsertTable: dialogs.table,
4235
+ mceTableProps: function() {
4236
+ dialogs.table(true);
4237
+ },
4238
+ mceTableRowProps: dialogs.row,
4239
+ mceTableCellProps: dialogs.cell
4240
+ }, function(func, name) {
4241
+ editor.addCommand(name, function(ui, val) {
4242
+ func(val);
4243
+ });
4244
+ });
4245
+
4246
+ function addButtons() {
4247
+ editor.addButton('tableprops', {
4248
+ title: 'Table properties',
4249
+ onclick: dialogs.tableProps,
4250
+ icon: 'table'
4251
+ });
4252
+
4253
+ editor.addButton('tabledelete', {
4254
+ title: 'Delete table',
4255
+ onclick: cmd('mceTableDelete')
4256
+ });
4257
+
4258
+ editor.addButton('tablecellprops', {
4259
+ title: 'Cell properties',
4260
+ onclick: cmd('mceTableCellProps')
4261
+ });
4262
+
4263
+ editor.addButton('tablemergecells', {
4264
+ title: 'Merge cells',
4265
+ onclick: cmd('mceTableMergeCells')
4266
+ });
4267
+
4268
+ editor.addButton('tablesplitcells', {
4269
+ title: 'Split cell',
4270
+ onclick: cmd('mceTableSplitCells')
4271
+ });
4272
+
4273
+ editor.addButton('tableinsertrowbefore', {
4274
+ title: 'Insert row before',
4275
+ onclick: cmd('mceTableInsertRowBefore')
4276
+ });
4277
+
4278
+ editor.addButton('tableinsertrowafter', {
4279
+ title: 'Insert row after',
4280
+ onclick: cmd('mceTableInsertRowAfter')
4281
+ });
4282
+
4283
+ editor.addButton('tabledeleterow', {
4284
+ title: 'Delete row',
4285
+ onclick: cmd('mceTableDeleteRow')
4286
+ });
4287
+
4288
+ editor.addButton('tablerowprops', {
4289
+ title: 'Row properties',
4290
+ onclick: cmd('mceTableRowProps')
4291
+ });
4292
+
4293
+ editor.addButton('tablecutrow', {
4294
+ title: 'Cut row',
4295
+ onclick: cmd('mceTableCutRow')
4296
+ });
4297
+
4298
+ editor.addButton('tablecopyrow', {
4299
+ title: 'Copy row',
4300
+ onclick: cmd('mceTableCopyRow')
4301
+ });
4302
+
4303
+ editor.addButton('tablepasterowbefore', {
4304
+ title: 'Paste row before',
4305
+ onclick: cmd('mceTablePasteRowBefore')
4306
+ });
4307
+
4308
+ editor.addButton('tablepasterowafter', {
4309
+ title: 'Paste row after',
4310
+ onclick: cmd('mceTablePasteRowAfter')
4311
+ });
4312
+
4313
+ editor.addButton('tableinsertcolbefore', {
4314
+ title: 'Insert column before',
4315
+ onclick: cmd('mceTableInsertColBefore')
4316
+ });
4317
+
4318
+ editor.addButton('tableinsertcolafter', {
4319
+ title: 'Insert column after',
4320
+ onclick: cmd('mceTableInsertColAfter')
4321
+ });
4322
+
4323
+ editor.addButton('tabledeletecol', {
4324
+ title: 'Delete column',
4325
+ onclick: cmd('mceTableDeleteCol')
4326
+ });
4327
+
4328
+ }
4329
+
4330
+ function isTable(table) {
4331
+
4332
+ var selectorMatched = editor.dom.is(table, 'table') && editor.getBody().contains(table);
4333
+
4334
+ return selectorMatched;
4335
+ }
4336
+
4337
+ function addToolbars() {
4338
+ var toolbarItems = editor.settings.table_toolbar;
4339
+
4340
+ if (toolbarItems === '' || toolbarItems === false) {
4341
+ return;
4342
+ }
4343
+
4344
+ if (!toolbarItems) {
4345
+ toolbarItems = 'tableprops tabledelete | ' +
4346
+ 'tableinsertrowbefore tableinsertrowafter tabledeleterow | ' +
4347
+ 'tableinsertcolbefore tableinsertcolafter tabledeletecol';
4348
+ }
4349
+
4350
+ editor.addContextToolbar(
4351
+ isTable,
4352
+ toolbarItems
4353
+ );
4354
+ }
4355
+
4356
+ function getClipboardRows() {
4357
+ return clipboardRows;
4358
+ }
4359
+
4360
+ function setClipboardRows(rows) {
4361
+ clipboardRows = rows;
4362
+ }
4363
+
4364
+ addButtons();
4365
+ addToolbars();
4366
+
4367
+ // Enable tab key cell navigation
4368
+ if (editor.settings.table_tab_navigation !== false) {
4369
+ editor.on('keydown', function(e) {
4370
+ var cellElm, grid, delta;
4371
+
4372
+ if (e.keyCode == 9) {
4373
+ cellElm = editor.dom.getParent(editor.selection.getStart(), 'th,td');
4374
+
4375
+ if (cellElm) {
4376
+ e.preventDefault();
4377
+
4378
+ grid = new TableGrid(editor);
4379
+ delta = e.shiftKey ? -1 : 1;
4380
+
4381
+ editor.undoManager.transact(function() {
4382
+ if (!grid.moveRelIdx(cellElm, delta) && delta > 0) {
4383
+ grid.insertRow();
4384
+ grid.refresh();
4385
+ grid.moveRelIdx(cellElm, delta);
4386
+ }
4387
+ });
4388
+ }
4389
+ }
4390
+ });
4391
+ }
4392
+
4393
+ self.insertTable = insertTable;
4394
+ self.setClipboardRows = setClipboardRows;
4395
+ self.getClipboardRows = getClipboardRows;
4396
+ }
4397
+
4398
+ PluginManager.add('table', Plugin);
4399
+ });
4400
+ })(this);