spud_core 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (190) hide show
  1. data/README.markdown +41 -0
  2. data/app/assets/images/back_disabled.jpg +0 -0
  3. data/app/assets/images/back_enabled.jpg +0 -0
  4. data/app/assets/images/favicon.ico +0 -0
  5. data/app/assets/images/forward_disabled.jpg +0 -0
  6. data/app/assets/images/forward_enabled.jpg +0 -0
  7. data/app/assets/images/sort_asc.png +0 -0
  8. data/app/assets/images/sort_asc_disabled.png +0 -0
  9. data/app/assets/images/sort_both.png +0 -0
  10. data/app/assets/images/sort_desc.png +0 -0
  11. data/app/assets/images/sort_desc_disabled.png +0 -0
  12. data/app/assets/images/spud/admin/flick/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  13. data/app/assets/images/spud/admin/flick/ui-bg_flat_0_eeeeee_40x100.png +0 -0
  14. data/app/assets/images/spud/admin/flick/ui-bg_flat_55_ffffff_40x100.png +0 -0
  15. data/app/assets/images/spud/admin/flick/ui-bg_flat_75_ffffff_40x100.png +0 -0
  16. data/app/assets/images/spud/admin/flick/ui-bg_glass_65_ffffff_1x400.png +0 -0
  17. data/app/assets/images/spud/admin/flick/ui-bg_highlight-soft_100_f6f6f6_1x100.png +0 -0
  18. data/app/assets/images/spud/admin/flick/ui-bg_highlight-soft_25_0073ea_1x100.png +0 -0
  19. data/app/assets/images/spud/admin/flick/ui-bg_highlight-soft_50_dddddd_1x100.png +0 -0
  20. data/app/assets/images/spud/admin/flick/ui-icons_0073ea_256x240.png +0 -0
  21. data/app/assets/images/spud/admin/flick/ui-icons_454545_256x240.png +0 -0
  22. data/app/assets/images/spud/admin/flick/ui-icons_666666_256x240.png +0 -0
  23. data/app/assets/images/spud/admin/flick/ui-icons_ff0084_256x240.png +0 -0
  24. data/app/assets/images/spud/admin/flick/ui-icons_ffffff_256x240.png +0 -0
  25. data/app/assets/images/spud/admin/users_thumb.png +0 -0
  26. data/app/assets/javascripts/spud/admin/application.js +90 -0
  27. data/app/assets/javascripts/spud/admin/jquery.dataTables.min.js +151 -0
  28. data/app/assets/javascripts/spud/admin/settings.js +2 -0
  29. data/app/assets/javascripts/spud/admin/users.js +2 -0
  30. data/app/assets/javascripts/spud/setup.js +2 -0
  31. data/app/assets/javascripts/spud/sitemaps.js +2 -0
  32. data/app/assets/javascripts/spud/user_sessions.js +2 -0
  33. data/app/assets/javascripts/spud/users.js +2 -0
  34. data/app/assets/javascripts/wymeditor/iframe/default/lbl-article.png +0 -0
  35. data/app/assets/javascripts/wymeditor/iframe/default/lbl-aside.png +0 -0
  36. data/app/assets/javascripts/wymeditor/iframe/default/lbl-blockquote.png +0 -0
  37. data/app/assets/javascripts/wymeditor/iframe/default/lbl-command.png +0 -0
  38. data/app/assets/javascripts/wymeditor/iframe/default/lbl-details.png +0 -0
  39. data/app/assets/javascripts/wymeditor/iframe/default/lbl-figcaption.png +0 -0
  40. data/app/assets/javascripts/wymeditor/iframe/default/lbl-figure.png +0 -0
  41. data/app/assets/javascripts/wymeditor/iframe/default/lbl-footer.png +0 -0
  42. data/app/assets/javascripts/wymeditor/iframe/default/lbl-h1.png +0 -0
  43. data/app/assets/javascripts/wymeditor/iframe/default/lbl-h2.png +0 -0
  44. data/app/assets/javascripts/wymeditor/iframe/default/lbl-h3.png +0 -0
  45. data/app/assets/javascripts/wymeditor/iframe/default/lbl-h4.png +0 -0
  46. data/app/assets/javascripts/wymeditor/iframe/default/lbl-h5.png +0 -0
  47. data/app/assets/javascripts/wymeditor/iframe/default/lbl-h6.png +0 -0
  48. data/app/assets/javascripts/wymeditor/iframe/default/lbl-header.png +0 -0
  49. data/app/assets/javascripts/wymeditor/iframe/default/lbl-hgroup.png +0 -0
  50. data/app/assets/javascripts/wymeditor/iframe/default/lbl-mark.png +0 -0
  51. data/app/assets/javascripts/wymeditor/iframe/default/lbl-meter.png +0 -0
  52. data/app/assets/javascripts/wymeditor/iframe/default/lbl-nav.png +0 -0
  53. data/app/assets/javascripts/wymeditor/iframe/default/lbl-p.png +0 -0
  54. data/app/assets/javascripts/wymeditor/iframe/default/lbl-pre.png +0 -0
  55. data/app/assets/javascripts/wymeditor/iframe/default/lbl-progress.png +0 -0
  56. data/app/assets/javascripts/wymeditor/iframe/default/lbl-rp.png +0 -0
  57. data/app/assets/javascripts/wymeditor/iframe/default/lbl-rt.png +0 -0
  58. data/app/assets/javascripts/wymeditor/iframe/default/lbl-ruby.png +0 -0
  59. data/app/assets/javascripts/wymeditor/iframe/default/lbl-section.png +0 -0
  60. data/app/assets/javascripts/wymeditor/iframe/default/lbl-summary.png +0 -0
  61. data/app/assets/javascripts/wymeditor/iframe/default/lbl-time.png +0 -0
  62. data/app/assets/javascripts/wymeditor/iframe/default/readme.md +1 -0
  63. data/app/assets/javascripts/wymeditor/iframe/default/wymiframe.css +91 -0
  64. data/app/assets/javascripts/wymeditor/iframe/default/wymiframe.html +26 -0
  65. data/app/assets/javascripts/wymeditor/jquery.wymeditor.js +9983 -0
  66. data/app/assets/javascripts/wymeditor/jquery.wymeditor.min.js +34 -0
  67. data/app/assets/javascripts/wymeditor/lang/bg.js +45 -0
  68. data/app/assets/javascripts/wymeditor/lang/ca.js +45 -0
  69. data/app/assets/javascripts/wymeditor/lang/cs.js +45 -0
  70. data/app/assets/javascripts/wymeditor/lang/cy.js +45 -0
  71. data/app/assets/javascripts/wymeditor/lang/de.js +45 -0
  72. data/app/assets/javascripts/wymeditor/lang/en.js +45 -0
  73. data/app/assets/javascripts/wymeditor/lang/es.js +45 -0
  74. data/app/assets/javascripts/wymeditor/lang/fa.js +46 -0
  75. data/app/assets/javascripts/wymeditor/lang/fi.js +44 -0
  76. data/app/assets/javascripts/wymeditor/lang/fr.js +45 -0
  77. data/app/assets/javascripts/wymeditor/lang/gl.js +45 -0
  78. data/app/assets/javascripts/wymeditor/lang/he.js +45 -0
  79. data/app/assets/javascripts/wymeditor/lang/hr.js +45 -0
  80. data/app/assets/javascripts/wymeditor/lang/hu.js +45 -0
  81. data/app/assets/javascripts/wymeditor/lang/it.js +45 -0
  82. data/app/assets/javascripts/wymeditor/lang/ja.js +44 -0
  83. data/app/assets/javascripts/wymeditor/lang/lt.js +45 -0
  84. data/app/assets/javascripts/wymeditor/lang/nb.js +45 -0
  85. data/app/assets/javascripts/wymeditor/lang/nl.js +45 -0
  86. data/app/assets/javascripts/wymeditor/lang/nn.js +45 -0
  87. data/app/assets/javascripts/wymeditor/lang/pl.js +45 -0
  88. data/app/assets/javascripts/wymeditor/lang/pt-br.js +45 -0
  89. data/app/assets/javascripts/wymeditor/lang/pt.js +45 -0
  90. data/app/assets/javascripts/wymeditor/lang/ru.js +45 -0
  91. data/app/assets/javascripts/wymeditor/lang/sv.js +46 -0
  92. data/app/assets/javascripts/wymeditor/lang/tr.js +45 -0
  93. data/app/assets/javascripts/wymeditor/lang/zh_cn.js +47 -0
  94. data/app/assets/javascripts/wymeditor/plugins/embed/jquery.wymeditor.embed.js +82 -0
  95. data/app/assets/javascripts/wymeditor/plugins/fullscreen/icon_fullscreen.gif +0 -0
  96. data/app/assets/javascripts/wymeditor/plugins/fullscreen/jquery.wymeditor.fullscreen.js +124 -0
  97. data/app/assets/javascripts/wymeditor/plugins/hovertools/jquery.wymeditor.hovertools.js +49 -0
  98. data/app/assets/javascripts/wymeditor/plugins/list/jquery.wymeditor.list.js +60 -0
  99. data/app/assets/javascripts/wymeditor/plugins/rdfa/jquery.wymeditor.rdfa.js +186 -0
  100. data/app/assets/javascripts/wymeditor/plugins/resizable/jquery.wymeditor.resizable.js +77 -0
  101. data/app/assets/javascripts/wymeditor/plugins/resizable/readme.txt +124 -0
  102. data/app/assets/javascripts/wymeditor/plugins/table/jquery.wymeditor.table.js +721 -0
  103. data/app/assets/javascripts/wymeditor/plugins/table/table_delete_column.png +0 -0
  104. data/app/assets/javascripts/wymeditor/plugins/table/table_delete_row.png +0 -0
  105. data/app/assets/javascripts/wymeditor/plugins/table/table_insert_column.png +0 -0
  106. data/app/assets/javascripts/wymeditor/plugins/table/table_insert_row.png +0 -0
  107. data/app/assets/javascripts/wymeditor/plugins/table/table_join_row.png +0 -0
  108. data/app/assets/javascripts/wymeditor/plugins/tidy/README +19 -0
  109. data/app/assets/javascripts/wymeditor/plugins/tidy/jquery.wymeditor.tidy.js +78 -0
  110. data/app/assets/javascripts/wymeditor/plugins/tidy/tidy.php +58 -0
  111. data/app/assets/javascripts/wymeditor/plugins/tidy/wand.png +0 -0
  112. data/app/assets/javascripts/wymeditor/skins/compact/icons.png +0 -0
  113. data/app/assets/javascripts/wymeditor/skins/compact/skin.css +134 -0
  114. data/app/assets/javascripts/wymeditor/skins/compact/skin.js +37 -0
  115. data/app/assets/javascripts/wymeditor/skins/default/icons.png +0 -0
  116. data/app/assets/javascripts/wymeditor/skins/default/skin.css +133 -0
  117. data/app/assets/javascripts/wymeditor/skins/default/skin.js +40 -0
  118. data/app/assets/javascripts/wymeditor/skins/minimal/images/bg.header.gif +0 -0
  119. data/app/assets/javascripts/wymeditor/skins/minimal/images/bg.selector.silver.gif +0 -0
  120. data/app/assets/javascripts/wymeditor/skins/minimal/images/bg.wymeditor.png +0 -0
  121. data/app/assets/javascripts/wymeditor/skins/minimal/images/icons.silver.gif +0 -0
  122. data/app/assets/javascripts/wymeditor/skins/minimal/skin.css +131 -0
  123. data/app/assets/javascripts/wymeditor/skins/minimal/skin.js +30 -0
  124. data/app/assets/javascripts/wymeditor/skins/silver/COPYING +674 -0
  125. data/app/assets/javascripts/wymeditor/skins/silver/README +27 -0
  126. data/app/assets/javascripts/wymeditor/skins/silver/images/bg.header.gif +0 -0
  127. data/app/assets/javascripts/wymeditor/skins/silver/images/bg.selector.silver.gif +0 -0
  128. data/app/assets/javascripts/wymeditor/skins/silver/images/bg.wymeditor.png +0 -0
  129. data/app/assets/javascripts/wymeditor/skins/silver/images/icons.silver.gif +0 -0
  130. data/app/assets/javascripts/wymeditor/skins/silver/skin.css +297 -0
  131. data/app/assets/javascripts/wymeditor/skins/silver/skin.js +61 -0
  132. data/app/assets/javascripts/wymeditor/skins/twopanels/icons.png +0 -0
  133. data/app/assets/javascripts/wymeditor/skins/twopanels/skin.css +134 -0
  134. data/app/assets/javascripts/wymeditor/skins/twopanels/skin.js +39 -0
  135. data/app/assets/javascripts/wymeditor/skins/wymeditor_icon.png +0 -0
  136. data/app/assets/stylesheets/spud/admin/application.css +464 -0
  137. data/app/assets/stylesheets/spud/admin/jquery-ui.css +568 -0
  138. data/app/assets/stylesheets/spud/admin/settings.css +4 -0
  139. data/app/assets/stylesheets/spud/admin/users.css +4 -0
  140. data/app/assets/stylesheets/spud/forms.css +108 -0
  141. data/app/assets/stylesheets/spud/login/application.css +302 -0
  142. data/app/assets/stylesheets/spud/login.css +58 -0
  143. data/app/assets/stylesheets/spud/setup.css +4 -0
  144. data/app/assets/stylesheets/spud/sitemaps.css +4 -0
  145. data/app/assets/stylesheets/spud/user_sessions.css +4 -0
  146. data/app/assets/stylesheets/spud/users.css +4 -0
  147. data/app/controllers/spud/admin/application_controller.rb +19 -0
  148. data/app/controllers/spud/admin/dashboard_controller.rb +7 -0
  149. data/app/controllers/spud/admin/settings_controller.rb +15 -0
  150. data/app/controllers/spud/admin/users_controller.rb +111 -0
  151. data/app/controllers/spud/application_controller.rb +48 -0
  152. data/app/controllers/spud/setup_controller.rb +22 -0
  153. data/app/controllers/spud/sitemaps_controller.rb +6 -0
  154. data/app/controllers/spud/user_sessions_controller.rb +28 -0
  155. data/app/controllers/spud/users_controller.rb +21 -0
  156. data/app/helpers/spud/admin/application_helper.rb +17 -0
  157. data/app/helpers/spud/admin/settings_helper.rb +2 -0
  158. data/app/helpers/spud/admin/users_helper.rb +2 -0
  159. data/app/helpers/spud/setup_helper.rb +2 -0
  160. data/app/helpers/spud/sitemaps_helper.rb +2 -0
  161. data/app/helpers/spud/user_sessions_helper.rb +2 -0
  162. data/app/helpers/spud/users_helper.rb +2 -0
  163. data/app/models/spud_admin_permission.rb +2 -0
  164. data/app/models/spud_setting.rb +2 -0
  165. data/app/models/spud_user.rb +16 -0
  166. data/app/models/spud_user_session.rb +3 -0
  167. data/app/views/layouts/spud/admin/application.html.erb +44 -0
  168. data/app/views/layouts/spud/admin/detail.html.erb +31 -0
  169. data/app/views/layouts/spud/login/application.html.erb +17 -0
  170. data/app/views/layouts/spud/setup.html.erb +47 -0
  171. data/app/views/spud/admin/dashboard/index.html.erb +11 -0
  172. data/app/views/spud/admin/users/_edit.html.erb +14 -0
  173. data/app/views/spud/admin/users/_form.html.erb +51 -0
  174. data/app/views/spud/admin/users/_new.html.erb +14 -0
  175. data/app/views/spud/admin/users/index.html.erb +41 -0
  176. data/app/views/spud/admin/users/show.html.erb +17 -0
  177. data/app/views/spud/setup/index.html.erb +25 -0
  178. data/app/views/spud/sitemaps/show.xml.builder +12 -0
  179. data/app/views/spud/user_sessions/new.html.erb +22 -0
  180. data/app/views/spud/users/_form.html.erb +35 -0
  181. data/app/views/spud/users/settings.html.erb +14 -0
  182. data/config/application.rb +48 -0
  183. data/config/boot.rb +6 -0
  184. data/config/routes.rb +15 -0
  185. data/lib/spud_core/belongs_to_app.rb +54 -0
  186. data/lib/spud_core/configuration.rb +9 -0
  187. data/lib/spud_core/engine.rb +34 -0
  188. data/lib/spud_core/test_files.rb +25 -0
  189. data/lib/spud_core.rb +8 -0
  190. metadata +311 -0
@@ -0,0 +1,721 @@
1
+ /**
2
+ * Copyright (c) 2011 PolicyStat LLC.
3
+ * MIT licensed (MIT-license.txt)
4
+ *
5
+ * @author Wes Winham (winhamwr@gmail.com)
6
+ */
7
+
8
+ // Fugue icons by Yusuke Kamiyamane http://p.yusukekamiyamane.com/
9
+ // and licensed under Creative Commons Attribution
10
+
11
+ /**
12
+ * A Table editing plugin that gives the user ability to add and remove
13
+ * rows and columns as well as merge rows and columns.
14
+ *
15
+ * @param options A configuration object.
16
+ * @param wym The WYMeditor instance to which the TableEditor should attach.
17
+ * @class
18
+ */
19
+ function TableEditor(options, wym) {
20
+ options = jQuery.extend({
21
+ sMergeRowButtonHtml: String() +
22
+ '<li class="wym_tools_merge_row">' +
23
+ '<a name="merge_row" href="#" title="Merge Cells" ' +
24
+ 'style="background-image: ' +
25
+ "url('" + wym._options.basePath +
26
+ "plugins/table/table_join_row.png')" + '">' +
27
+ 'Merge Table Row' +
28
+ '</a>' +
29
+ '</li>',
30
+
31
+ sMergeRowButtonSelector: "li.wym_tools_merge_row a",
32
+
33
+ sAddRowButtonHtml: String() +
34
+ "<li class='wym_tools_add_row'>" +
35
+ "<a name='add_row' href='#' " +
36
+ "title='Add Row' " +
37
+ "style='background-image:" +
38
+ " url(" + wym._options.basePath +
39
+ "plugins/table/table_insert_row.png)'>" +
40
+ "Add Table Row" +
41
+ "</a>" +
42
+ "</li>",
43
+ sAddRowButtonSelector: "li.wym_tools_add_row a",
44
+
45
+ sRemoveRowButtonHtml: String() +
46
+ "<li class='wym_tools_remove_row'>" +
47
+ "<a name='remove_row' href='#' " +
48
+ "title='Remove Row' " +
49
+ "style='background-image: " +
50
+ "url(" + wym._options.basePath +
51
+ "plugins/table/table_delete_row.png)'>" +
52
+ "Remove Table Row" +
53
+ "</a>" +
54
+ "</li>",
55
+ sRemoveRowButtonSelector: "li.wym_tools_remove_row a",
56
+
57
+ sAddColumnButtonHtml: String() +
58
+ "<li class='wym_tools_add_column'>" +
59
+ "<a name='add_column' href='#' " +
60
+ "title='Add Column' " +
61
+ "style='background-image: " +
62
+ "url(" + wym._options.basePath +
63
+ "plugins/table/table_insert_column.png)'>" +
64
+ "Add Table Column" +
65
+ "</a>" +
66
+ "</li>",
67
+ sAddColumnButtonSelector: "li.wym_tools_add_column a",
68
+
69
+ sRemoveColumnButtonHtml: String() +
70
+ "<li class='wym_tools_remove_column'>" +
71
+ "<a name='remove_column' href='#' " +
72
+ "title='Remove Column' " +
73
+ "style='background-image: " +
74
+ "url(" + wym._options.basePath +
75
+ "plugins/table/table_delete_column.png)'>" +
76
+ "Remove Table Column" +
77
+ "</a>" +
78
+ "</li>",
79
+ sRemoveColumnButtonSelector: "li.wym_tools_remove_column a",
80
+
81
+ enableCellTabbing: true
82
+
83
+ }, options);
84
+
85
+ this._options = options;
86
+ this._wym = wym;
87
+
88
+ this.init();
89
+ }
90
+
91
+ /**
92
+ * Construct and return a table objects using the given options object.
93
+ *
94
+ * @param options The configuration object.
95
+ */
96
+ WYMeditor.editor.prototype.table = function (options) {
97
+ var tableEditor = new TableEditor(options, this);
98
+ this.tableEditor = tableEditor;
99
+
100
+ return tableEditor;
101
+ };
102
+
103
+ /**
104
+ * Initialize the TableEditor object by adding appropriate toolbar buttons and
105
+ * binding any required event listeners.
106
+ */
107
+ TableEditor.prototype.init = function () {
108
+ var wym = this._wym,
109
+ tableEditor = this,
110
+ // Add the tool panel buttons
111
+ tools = $(wym._box).find(
112
+ wym._options.toolsSelector + wym._options.toolsListSelector
113
+ );
114
+
115
+ tools.append(tableEditor._options.sMergeRowButtonHtml);
116
+ tools.append(tableEditor._options.sAddRowButtonHtml);
117
+ tools.append(tableEditor._options.sRemoveRowButtonHtml);
118
+ tools.append(tableEditor._options.sAddColumnButtonHtml);
119
+ tools.append(tableEditor._options.sRemoveColumnButtonHtml);
120
+
121
+ tableEditor.bindEvents();
122
+ rangy.init();
123
+ };
124
+
125
+ /**
126
+ * Bind all required event listeners, including button listeners and support for
127
+ * tabbing through table cells if enableCellTabbing is true.
128
+ */
129
+ TableEditor.prototype.bindEvents = function () {
130
+ var wym = this._wym,
131
+ tableEditor = this;
132
+
133
+ // Handle tool button click
134
+ $(wym._box).find(tableEditor._options.sMergeRowButtonSelector).click(function () {
135
+ var sel = rangy.getIframeSelection(wym._iframe);
136
+ tableEditor.mergeRow(sel);
137
+ return false;
138
+ });
139
+ $(wym._box).find(tableEditor._options.sAddRowButtonSelector).click(function () {
140
+ return tableEditor.addRow(wym.selected());
141
+ });
142
+ $(wym._box).find(tableEditor._options.sRemoveRowButtonSelector).click(function () {
143
+ return tableEditor.removeRow(wym.selected());
144
+ });
145
+ $(wym._box).find(tableEditor._options.sAddColumnButtonSelector).click(function () {
146
+ return tableEditor.addColumn(wym.selected());
147
+ });
148
+ $(wym._box).find(tableEditor._options.sRemoveColumnButtonSelector).click(function () {
149
+ return tableEditor.removeColumn(wym.selected());
150
+ });
151
+
152
+ // Handle tab clicks
153
+ if (tableEditor._options.enableCellTabbing) {
154
+ $(wym._doc).bind('keydown', tableEditor.keyDown);
155
+ }
156
+ };
157
+
158
+ /**
159
+ * Get the number of columns in a given tr element, accounting for colspan and
160
+ * rowspan. This function assumes that the table structure is valid, and will
161
+ * return incorrect results for uneven tables.
162
+ *
163
+ * @param tr The <tr> node whose number of columns we need to count.
164
+ *
165
+ * @returns {Number} The number of columns in the given tr, accounting for
166
+ * colspan and rowspan.
167
+ */
168
+ TableEditor.prototype.getNumColumns = function (tr) {
169
+ var wym = this._wym,
170
+ numColumns = 0,
171
+ table,
172
+ firstTr;
173
+
174
+ table = wym.findUp(tr, 'table');
175
+ firstTr = $(table).find('tr:eq(0)');
176
+
177
+ // Count the tds and ths in the FIRST ROW of this table, accounting for
178
+ // colspan. We count the first td because it won't have any rowspan's before
179
+ // it to complicate things
180
+ $(firstTr).children('td,th').each(function (index, elmnt) {
181
+ numColumns += TableEditor.GET_COLSPAN_PROP(elmnt);
182
+ });
183
+
184
+ return numColumns;
185
+ };
186
+
187
+ /**
188
+ TableEditor.GET_COLSPAN_PROP
189
+ ============================
190
+
191
+ Get the integer value of the inferred colspan property on the given cell in
192
+ a cross-browser compatible way that's also compatible across jquery versions.
193
+
194
+ jquery 1.6 changed the way .attr works, which affected certain browsers
195
+ differently with regard to colspan and rowspan for cells that didn't explcility
196
+ have that attribue set.
197
+ */
198
+ TableEditor.GET_COLSPAN_PROP = function (cell) {
199
+ var colspan = $(cell).attr('colspan');
200
+ if (typeof colspan === 'undefined') {
201
+ colspan = 1;
202
+ }
203
+ return parseInt(colspan, 10);
204
+ };
205
+
206
+ /**
207
+ TableEditor.GET_ROWSPAN_PROP
208
+ ============================
209
+
210
+ Get the integer value of the inferred rowspan property on the given cell in
211
+ a cross-browser compatible way that's also compatible across jquery versions.
212
+
213
+ See GET_COLSPAN_PROP for details
214
+ */
215
+ TableEditor.GET_ROWSPAN_PROP = function (cell) {
216
+ var rowspan = $(cell).attr('rowspan');
217
+ if (typeof rowspan === 'undefined') {
218
+ rowspan = 1;
219
+ }
220
+ return parseInt(rowspan, 10);
221
+ };
222
+ /**
223
+ * Get the X grid index of the given td or th table cell (0-indexed). This takes
224
+ * in to account all colspans and rowspans.
225
+ *
226
+ * @param cell The td or th node whose X index we're returning.
227
+ */
228
+ TableEditor.prototype.getCellXIndex = function (cell) {
229
+ var tableEditor = this,
230
+ i,
231
+ parentTr,
232
+ baseRowColumns,
233
+ rowColCount,
234
+ missingCells,
235
+ rowspanIndexes,
236
+ checkTr,
237
+ rowOffset,
238
+ trChildren,
239
+ elmnt,
240
+ colspan,
241
+ indexCounter,
242
+ cellIndex;
243
+ parentTr = $(cell).parent('tr')[0];
244
+
245
+ baseRowColumns = this.getNumColumns(parentTr);
246
+
247
+ // Figure out how many explicit cells are missing which is how many rowspans
248
+ // we're affected by
249
+ rowColCount = 0;
250
+ $(parentTr).children('td,th').each(function (index, elmnt) {
251
+ rowColCount += TableEditor.GET_COLSPAN_PROP(elmnt);
252
+ });
253
+
254
+ missingCells = baseRowColumns - rowColCount;
255
+ rowspanIndexes = [];
256
+ checkTr = parentTr;
257
+ rowOffset = 1;
258
+
259
+ // If this cell is affected by a rowspan from farther up the table,
260
+ // we need to take in to account any possible colspan attributes on that
261
+ // cell. Store the real X index of the cells to the left of our cell to use
262
+ // in the colspan calculation.
263
+ while (missingCells > 0) {
264
+ checkTr = $(checkTr).prev('tr');
265
+ rowOffset += 1;
266
+ trChildren = $(checkTr).children('td,th');
267
+ for (i = 0; i < trChildren.length; i++) {
268
+ elmnt = trChildren[i];
269
+ if (TableEditor.GET_ROWSPAN_PROP(elmnt) >= rowOffset) {
270
+ // Actually affects our source row
271
+ missingCells -= 1;
272
+ colspan = TableEditor.GET_COLSPAN_PROP(elmnt);
273
+ rowspanIndexes[tableEditor.getCellXIndex(elmnt)] = colspan;
274
+ }
275
+ }
276
+ }
277
+
278
+ indexCounter = 0;
279
+ cellIndex = null;
280
+ // Taking in to account the real X indexes of all of the columns to the left
281
+ // of this cell, determine the real X index.
282
+ $(parentTr).children('td,th').each(function (index, elmnt) {
283
+ if (cellIndex !== null) {
284
+ // We've already iterated to the cell we're checking
285
+ return;
286
+ }
287
+ // Account for an inferred colspan created by a rowspan from above
288
+ while (typeof rowspanIndexes[indexCounter] !== 'undefined') {
289
+ indexCounter += parseInt(rowspanIndexes[indexCounter], 10);
290
+ }
291
+ if (elmnt === cell) {
292
+ // We're at our cell, no need to keep moving to the right.
293
+ // Signal this by setting the cellIndex
294
+ cellIndex = indexCounter;
295
+ return;
296
+ }
297
+ // Account for an explicit colspan on this cell
298
+ indexCounter += TableEditor.GET_COLSPAN_PROP(elmnt);
299
+ });
300
+
301
+ if (cellIndex === null) {
302
+ // Somehow, we never found the cell when iterating over its row.
303
+ throw "Cell index not found";
304
+ }
305
+ return cellIndex;
306
+ };
307
+
308
+ /**
309
+ * Get the number of columns represented by the given array of contiguous cell
310
+ * (td/th) nodes.
311
+ * Accounts for colspan and rowspan attributes.
312
+ *
313
+ * @param cells An array of td/th nodes whose total column span we're checking.
314
+ *
315
+ * @return {Number} The number of columns represented by the "cells"
316
+ */
317
+ TableEditor.prototype.getTotalColumns = function (cells) {
318
+ var tableEditor = this,
319
+ rootTr = this.getCommonParentTr(cells),
320
+ baseRowColumns,
321
+ colspanCount,
322
+ rowColCount;
323
+
324
+ if (rootTr === null) {
325
+ // Non-contiguous columns
326
+ throw "getTotalColumns only allowed for contiguous cells";
327
+ }
328
+
329
+ baseRowColumns = this.getNumColumns(rootTr);
330
+
331
+ // Count the number of simple columns, not accounting for rowspans
332
+ colspanCount = 0;
333
+ $(cells).each(function (index, elmnt) {
334
+ colspanCount += TableEditor.GET_COLSPAN_PROP(elmnt);
335
+ });
336
+
337
+ // Determine if we're affected by rowspans. If the number of simple columns
338
+ // in the row equals the number of columns in the first row, we don't have
339
+ // any rowspans
340
+ rowColCount = 0;
341
+ $(rootTr).children('td,th').each(function (index, elmnt) {
342
+ rowColCount += TableEditor.GET_COLSPAN_PROP(elmnt);
343
+ });
344
+
345
+ if (rowColCount === baseRowColumns) {
346
+ // Easy case. No rowspans to deal with
347
+ return colspanCount;
348
+ } else {
349
+ if (cells.length === 1) {
350
+ // Easy. Just the colspan
351
+ return TableEditor.GET_COLSPAN_PROP(cells[0]);
352
+ } else {
353
+ var lastCell = $(cells).eq(cells.length - 1)[0],
354
+ firstCell = $(cells).eq(0)[0];
355
+ // On jQuery 1.4 upgrade, $(cells).eq(-1)
356
+ return 1 + tableEditor.getCellXIndex(lastCell) -
357
+ tableEditor.getCellXIndex(firstCell);
358
+ }
359
+ }
360
+ };
361
+
362
+ /**
363
+ * Merge the table cells in the given selection using a colspan.
364
+ *
365
+ * @param sel A rangy selection object across which to row merge.
366
+ *
367
+ * @return {Boolean} true if changes are made, false otherwise
368
+ */
369
+ TableEditor.prototype.mergeRow = function (sel) {
370
+ var wym = this._wym,
371
+ tableEditor = this,
372
+ i,
373
+ // Get all of the affected nodes in the range
374
+ nodes = [],
375
+ range = null,
376
+ cells,
377
+ rootTr,
378
+ mergeCell,
379
+ $elmnt,
380
+ rowspanProp;
381
+
382
+ for (i = 0; i < sel.rangeCount; i++) {
383
+ range = sel.getRangeAt(i);
384
+ nodes = nodes.concat(range.getNodes(false));
385
+ }
386
+
387
+ // Just use the td and th nodes
388
+ cells = $(nodes).filter('td,th');
389
+ if (cells.length === 0) {
390
+ return false;
391
+ }
392
+
393
+ // If the selection is across multiple tables, don't merge
394
+ rootTr = tableEditor.getCommonParentTr(cells);
395
+ if (rootTr === null) {
396
+ return false;
397
+ }
398
+
399
+ mergeCell = cells[0];
400
+ // If any of the cells have a rowspan, create the inferred cells
401
+ $(cells).each(function (i, elmnt) {
402
+ $elmnt = $(elmnt);
403
+ rowspanProp = TableEditor.GET_ROWSPAN_PROP(elmnt);
404
+ if (rowspanProp <= 1) {
405
+ // We don't care about cells without a rowspan
406
+ return;
407
+ }
408
+
409
+ // This cell has an actual rowspan, we need to account for it
410
+ // Figure out the x index for this cell in the table grid
411
+ var prevCells = $elmnt.prevAll('td,th'),
412
+ index = tableEditor.getCellXIndex(elmnt),
413
+ // Create the previously-inferred cell in the appropriate index
414
+ // with one less rowspan
415
+ newRowspan = rowspanProp - 1,
416
+ newTd;
417
+ if (newRowspan === 1) {
418
+ newTd = '<td>' + $elmnt.html() + '</td>';
419
+ } else {
420
+ newTd = String() +
421
+ '<td rowspan="' + newRowspan + '">' +
422
+ $elmnt.html() +
423
+ '</td>';
424
+ }
425
+ if (index === 0) {
426
+ $elmnt.parent('tr')
427
+ .next('tr')
428
+ .prepend(newTd);
429
+ } else {
430
+ // TODO: account for colspan/rowspan with insertion
431
+ // Account for colspan/rowspan by walking from right to left looking
432
+ // for the cell closest to the desired index to APPEND to
433
+ var insertionIndex = index - 1,
434
+ insertionCells = $elmnt.parent('tr').next('tr')
435
+ .find('td,th'),
436
+ cellInserted = false;
437
+ for (i = insertionCells.length - 1; i >= 0; i--) {
438
+ var xIndex = tableEditor.getCellXIndex(insertionCells[i]);
439
+ if (xIndex <= insertionIndex) {
440
+ $(insertionCells[i]).append(newTd);
441
+ cellInserted = true;
442
+ break;
443
+ }
444
+ }
445
+ if (!cellInserted) {
446
+ // Bail out now before we clear HTML and break things
447
+ throw "Cell rowspan invalid";
448
+ }
449
+ }
450
+
451
+ // Clear the cell's html, since we just moved it down
452
+ $elmnt.html('');
453
+ });
454
+
455
+ // Remove any rowspan from the mergecell now that we've shifted rowspans
456
+ // down
457
+ // ie fails when we try to remove a rowspan for some reason
458
+ try {
459
+ $(mergeCell).removeAttr('rowspan');
460
+ } catch (err) {
461
+ $(mergeCell).attr('rowspan', 1);
462
+ }
463
+
464
+ // Build the content of the new combined cell from all of the included cells
465
+ var newContent = '';
466
+ $(cells).each(function (index, elmnt) {
467
+ newContent += $(elmnt).html();
468
+ });
469
+
470
+ // Add a colspan to the farthest-left cell
471
+ var combinedColspan = this.getTotalColumns(cells);
472
+ if ($.browser.msie) {
473
+ // jQuery.attr doesn't work for colspan in ie
474
+ mergeCell.colSpan = combinedColspan;
475
+ } else {
476
+ $(mergeCell).attr('colspan', combinedColspan);
477
+ }
478
+
479
+ // Delete the rest of the cells
480
+ $(cells).each(function (index, elmnt) {
481
+ if (index !== 0) {
482
+ $(elmnt).remove();
483
+ }
484
+ });
485
+
486
+ // Change the content in our newly-merged cell
487
+ $(mergeCell).html(newContent);
488
+
489
+ tableEditor.selectElement(mergeCell);
490
+
491
+ return true;
492
+ };
493
+
494
+ /**
495
+ * Add a row to the given elmnt (representing a <tr> or a child of a <tr>).
496
+ *
497
+ * @param The node which will have a row appended after its parent row.
498
+ */
499
+ TableEditor.prototype.addRow = function (elmnt) {
500
+ var wym = this._wym,
501
+ tr = this._wym.findUp(elmnt, 'tr'),
502
+ numColumns,
503
+ tdHtml,
504
+ i;
505
+
506
+ if (tr === null) {
507
+ return false;
508
+ }
509
+
510
+ numColumns = this.getNumColumns(tr);
511
+
512
+ tdHtml = '';
513
+ for (i = 0; i < numColumns; i++) {
514
+ tdHtml += '<td>&nbsp;</td>';
515
+ }
516
+ $(tr).after('<tr>' + tdHtml + '</tr>');
517
+
518
+ return false;
519
+ };
520
+
521
+ /**
522
+ * Remove the given table if it doesn't have any rows/columns.
523
+ *
524
+ * @param table The table to delete if it is empty.
525
+ */
526
+ TableEditor.prototype.removeEmptyTable = function (table) {
527
+ var cells = $(table).find('td,th');
528
+ if (cells.length === 0) {
529
+ $(table).remove();
530
+ }
531
+ };
532
+
533
+ /**
534
+ * Remove the row for the given element (representing a <tr> or a child
535
+ * of a <tr>).
536
+ *
537
+ * @param elmnt The node whose parent tr will be removed.
538
+ */
539
+ TableEditor.prototype.removeRow = function (elmnt) {
540
+ var wym = this._wym,
541
+ tr = this._wym.findUp(elmnt, 'tr'),
542
+ table;
543
+
544
+ if (tr === null) {
545
+ return false;
546
+ }
547
+ table = wym.findUp(elmnt, 'table');
548
+ $(tr).remove();
549
+ this.removeEmptyTable(table);
550
+
551
+ return false;
552
+ };
553
+
554
+ /**
555
+ * Add a column to the given elmnt (representing a <td> or a child of a <td>).
556
+ *
557
+ * @param elmnt The node which will have a column appended afterward.
558
+ */
559
+ TableEditor.prototype.addColumn = function (elmnt) {
560
+ var wym = this._wym,
561
+ td = this._wym.findUp(elmnt, ['td', 'th']),
562
+ prevTds,
563
+ tdIndex,
564
+ tr,
565
+ newTd = '<td>&nbsp;</td>',
566
+ newTh = '<th>&nbsp;</th>',
567
+ insertionElement;
568
+
569
+ if (td === null) {
570
+ return false;
571
+ }
572
+ prevTds = $(td).prevAll();
573
+ tdIndex = prevTds.length;
574
+
575
+ tr = wym.findUp(td, 'tr');
576
+ $(tr).siblings('tr').andSelf().each(function (index, element) {
577
+ insertionElement = newTd;
578
+ if ($(element).find('th').length > 0) {
579
+ // The row has a TH, so insert a th
580
+ insertionElement = newTh;
581
+ }
582
+
583
+ $(element).find('td,th').eq(tdIndex).after(insertionElement);
584
+ });
585
+
586
+ return false;
587
+ };
588
+
589
+ /**
590
+ * Remove the column to the right of the given elmnt (representing a <td> or a
591
+ * child of a <td>).
592
+ */
593
+ TableEditor.prototype.removeColumn = function (elmnt) {
594
+ var wym = this._wym,
595
+ td = this._wym.findUp(elmnt, ['td', 'th']),
596
+ table,
597
+ prevTds,
598
+ tdIndex,
599
+ tr;
600
+ if (td === null) {
601
+ return false;
602
+ }
603
+ table = wym.findUp(elmnt, 'table');
604
+ prevTds = $(td).prevAll();
605
+ tdIndex = prevTds.length;
606
+
607
+ tr = wym.findUp(td, 'tr');
608
+ $(tr).siblings('tr').each(function (index, element) {
609
+ $(element).find('td,th').eq(tdIndex).remove();
610
+ });
611
+ $(td).remove();
612
+ this.removeEmptyTable(table);
613
+
614
+ return false;
615
+ };
616
+
617
+ /**
618
+ * keyDown event handler used for consistent tab key cell movement.
619
+ */
620
+ TableEditor.prototype.keyDown = function (evt) {
621
+ //'this' is the doc
622
+ var wym = WYMeditor.INSTANCES[this.title],
623
+ tableEditor = wym.tableEditor;
624
+
625
+ if (evt.keyCode === WYMeditor.KEY.TAB) {
626
+ return tableEditor.selectNextCell(wym.selected());
627
+ }
628
+
629
+ return null;
630
+ };
631
+
632
+ /**
633
+ * Move the focus to the next cell.
634
+ */
635
+ TableEditor.prototype.selectNextCell = function (elmnt) {
636
+ var wym = this._wym,
637
+ tableEditor = this,
638
+ cell = wym.findUp(elmnt, ['td', 'th']),
639
+ nextCells,
640
+ tr,
641
+ nextRows;
642
+
643
+ if (cell === null) {
644
+ return null;
645
+ }
646
+
647
+ // Try moving to the next cell to the right
648
+ nextCells = $(cell).next('td,th');
649
+ if (nextCells.length > 0) {
650
+ tableEditor.selectElement(nextCells[0]);
651
+ return false;
652
+ }
653
+
654
+ // There was no cell to the right, use the first cell in the next row
655
+ tr = wym.findUp(cell, 'tr');
656
+ nextRows = $(tr).next('tr');
657
+ if (nextRows.length !== 0) {
658
+ nextCells = $(nextRows).children('td,th');
659
+ if (nextCells.length > 0) {
660
+ tableEditor.selectElement(nextCells[0]);
661
+ return false;
662
+ }
663
+ }
664
+
665
+ // There is no next row. Do a normal tab
666
+ return null;
667
+ };
668
+
669
+ /**
670
+ * Select the given element using rangy selectors.
671
+ */
672
+ TableEditor.prototype.selectElement = function (elmnt) {
673
+ var sel = rangy.getIframeSelection(this._wym._iframe),
674
+ range = rangy.createRange(this._wym._doc);
675
+
676
+ range.setStart(elmnt, 0);
677
+ range.setEnd(elmnt, 0);
678
+ range.collapse(false);
679
+
680
+ try {
681
+ sel.setSingleRange(range);
682
+ } catch (err) {
683
+ // ie8 can raise an "unkown runtime error" trying to empty the range
684
+ }
685
+ // IE selection hack
686
+ if ($.browser.msie) {
687
+ this._wym.saveCaret();
688
+ }
689
+ };
690
+
691
+ /**
692
+ * Get the common parent tr for the given table cell nodes. If the closest parent
693
+ * tr for each cell isn't the same, returns null.
694
+ */
695
+ TableEditor.prototype.getCommonParentTr = function (cells) {
696
+ var firstCell,
697
+ parentTrList,
698
+ rootTr;
699
+
700
+ cells = $(cells).filter('td,th');
701
+ if (cells.length === 0) {
702
+ return null;
703
+ }
704
+ firstCell = cells[0];
705
+ parentTrList = $(firstCell).parent('tr');
706
+
707
+ if (parentTrList.length === 0) {
708
+ return null;
709
+ }
710
+ rootTr = parentTrList[0];
711
+
712
+ // Ensure that all of the cells have the same parent tr
713
+ $(cells).each(function (index, elmnt) {
714
+ parentTrList = $(elmnt).parent('tr');
715
+ if (parentTrList.length === 0 || parentTrList[0] !== rootTr) {
716
+ return null;
717
+ }
718
+ });
719
+
720
+ return rootTr;
721
+ };