mosaico 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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);