MDL-64506 templates: Move BS2 btns' to BS4 btns'
[moodle.git] / lib / editor / atto / plugins / table / yui / build / moodle-atto_table-button / moodle-atto_table-button.js
CommitLineData
adca7326
DW
1YUI.add('moodle-atto_table-button', function (Y, NAME) {
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17
3ee53a42 18/**
62467795
AN
19 * @package atto_table
20 * @copyright 2013 Damyon Wiese <damyon@moodle.com>
21 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22 */
23
24/**
25 * @module moodle-atto_table-button
3ee53a42 26 */
3ee53a42 27
adca7326
DW
28/**
29 * Atto text editor table plugin.
30 *
62467795
AN
31 * @namespace M.atto_table
32 * @class Button
33 * @extends M.editor_atto.EditorPlugin
adca7326 34 */
62467795
AN
35
36var COMPONENT = 'atto_table',
b87e11c8 37 DEFAULT = {
d26d0d9b 38 BORDERSTYLE: 'none',
b87e11c8 39 BORDERWIDTH: '1'
40 },
41 DIALOGUE = {
1c53bc81 42 WIDTH: '480px'
b87e11c8 43 },
62467795 44 TEMPLATE = '' +
353473aa 45 '<form class="{{CSS.FORM}}">' +
1b217025
BB
46 '<div class="m-b-1 form-group row-fluid">' +
47 '<div class="col-sm-4 span4">' +
62467795 48 '<label for="{{elementid}}_atto_table_caption">{{get_string "caption" component}}</label>' +
1b217025
BB
49 '</div><div class="col-sm-8 span8">' +
50 '<input type="text" class="form-control {{CSS.CAPTION}}" id="{{elementid}}_atto_table_caption" required />' +
51 '</div>' +
52 '</div>' +
53 '<div class="m-b-1 form-group row-fluid">' +
54 '<div class="col-sm-4 span4">' +
55 '<label for="{{elementid}}_atto_table_captionposition">' +
b87e11c8 56 '{{get_string "captionposition" component}}</label>' +
1b217025
BB
57 '</div><div class="col-sm-8 span8">' +
58 '<select class="custom-select {{CSS.CAPTIONPOSITION}}" id="{{elementid}}_atto_table_captionposition">' +
556decf1
DM
59 '<option value=""></option>' +
60 '<option value="top">{{get_string "top" "editor"}}</option>' +
61 '<option value="bottom">{{get_string "bottom" "editor"}}</option>' +
62 '</select>' +
1b217025
BB
63 '</div>' +
64 '</div>' +
65 '<div class="m-b-1 form-group row-fluid">' +
66 '<div class="col-sm-4 span4">' +
67 '<label for="{{elementid}}_atto_table_headers">{{get_string "headers" component}}</label>' +
68 '</div><div class="col-sm-8 span8">' +
69 '<select class="custom-select {{CSS.HEADERS}}" id="{{elementid}}_atto_table_headers">' +
62467795
AN
70 '<option value="columns">{{get_string "columns" component}}' + '</option>' +
71 '<option value="rows">{{get_string "rows" component}}' + '</option>' +
72 '<option value="both">{{get_string "both" component}}' + '</option>' +
73 '</select>' +
1b217025
BB
74 '</div>' +
75 '</div>' +
b87e11c8 76 '{{#if nonedit}}' +
1b217025
BB
77 '<div class="m-b-1 form-group row-fluid">' +
78 '<div class="col-sm-4 span4">' +
79 '<label for="{{elementid}}_atto_table_rows">{{get_string "numberofrows" component}}</label>' +
80 '</div><div class="col-sm-8 span8">' +
81 '<input class="form-control w-auto {{CSS.ROWS}}" type="number" value="3" ' +
b87e11c8 82 'id="{{elementid}}_atto_table_rows" size="8" min="1" max="50"/>' +
1b217025
BB
83 '</div>' +
84 '</div>' +
85 '<div class="m-b-1 form-group row-fluid">' +
86 '<div class="col-sm-4 span4">' +
b87e11c8 87 '<label for="{{elementid}}_atto_table_columns" ' +
1b217025
BB
88 '>{{get_string "numberofcolumns" component}}</label>' +
89 '</div><div class="col-sm-8 span8">' +
90 '<input class="form-control w-auto {{CSS.COLUMNS}}" type="number" value="3" ' +
91 'id="{{elementid}}_atto_table_columns"' +
b87e11c8 92 'size="8" min="1" max="20"/>' +
1b217025
BB
93 '</div>' +
94 '</div>' +
b87e11c8 95 '{{/if}}' +
96 '{{#if allowStyling}}' +
97 '<fieldset>' +
98 '<legend class="mdl-align">{{get_string "appearance" component}}</legend>' +
99 '{{#if allowBorders}}' +
1b217025
BB
100 '<div class="m-b-1 form-group row-fluid">' +
101 '<div class="col-sm-4 span4">' +
102 '<label for="{{elementid}}_atto_table_borders">{{get_string "borders" component}}</label>' +
103 '</div><div class="col-sm-8 span8">' +
104 '<select name="borders" class="custom-select {{CSS.BORDERS}}" id="{{elementid}}_atto_table_borders">' +
b87e11c8 105 '<option value="default">{{get_string "themedefault" component}}' + '</option>' +
b87e11c8 106 '<option value="outer">{{get_string "outer" component}}' + '</option>' +
107 '<option value="all">{{get_string "all" component}}' + '</option>' +
108 '</select>' +
1b217025
BB
109 '</div>' +
110 '</div>' +
111 '<div class="m-b-1 form-group row-fluid">' +
112 '<div class="col-sm-4 span4">' +
113 '<label for="{{elementid}}_atto_table_borderstyle">' +
d26d0d9b 114 '{{get_string "borderstyles" component}}</label>' +
1b217025
BB
115 '</div><div class="col-sm-8 span8">' +
116 '<select name="borderstyles" class="custom-select {{CSS.BORDERSTYLE}}" ' +
117 'id="{{elementid}}_atto_table_borderstyle">' +
d26d0d9b
RW
118 '{{#each borderStyles}}' +
119 '<option value="' + '{{this}}' + '">' + '{{get_string this ../component}}' + '</option>' +
120 '{{/each}}' +
121 '</select>' +
1b217025
BB
122 '</div>' +
123 '</div>' +
124 '<div class="m-b-1 form-group row-fluid">' +
125 '<div class="col-sm-4 span4">' +
126 '<label for="{{elementid}}_atto_table_bordersize">' +
d26d0d9b 127 '{{get_string "bordersize" component}}</label>' +
1b217025
BB
128 '</div><div class="col-sm-8 span8">' +
129 '<div class="form-inline">' +
130 '<input name="bordersize" id="{{elementid}}_atto_table_bordersize" ' +
131 'class="form-control w-auto m-r-1 {{CSS.BORDERSIZE}}"' +
d26d0d9b 132 'type="number" value="1" size="8" min="1" max="50"/>' +
1b217025
BB
133 '<label>{{CSS.BORDERSIZEUNIT}}</label>' +
134 '</div>' +
135 '</div>' +
136 '</div>' +
137 '<div class="m-b-1 form-group row-fluid">' +
138 '<div class="col-sm-4 span4">' +
139 '<label for="{{elementid}}_atto_table_bordercolour">' +
d26d0d9b 140 '{{get_string "bordercolour" component}}</label>' +
1b217025 141 '</div><div class="col-sm-8 span8">' +
d26d0d9b 142 '<div id="{{elementid}}_atto_table_bordercolour"' +
1b217025
BB
143 'class="form-inline {{CSS.BORDERCOLOUR}} {{CSS.AVAILABLECOLORS}}" size="1">' +
144 '<div class="tablebordercolor" style="background-color:transparent;color:transparent">' +
d26d0d9b 145 '<input id="{{../elementid}}_atto_table_bordercolour_-1"' +
1b217025 146 'type="radio" class="m-0" name="borderColour" value="none" checked="checked"' +
d26d0d9b 147 'title="{{get_string "themedefault" component}}"></input>' +
1b217025
BB
148 '<label for="{{../elementid}}_atto_table_bordercolour_-1" class="accesshide">' +
149 '{{get_string "themedefault" component}}</label>' +
150 '</div>' +
d26d0d9b 151 '{{#each availableColours}}' +
1b217025 152 '<div class="tablebordercolor" style="background-color:{{this}};color:{{this}}">' +
d26d0d9b 153 '<input id="{{../elementid}}_atto_table_bordercolour_{{@index}}"' +
1b217025
BB
154 'type="radio" class="m-0" name="borderColour" value="' + '{{this}}' + '" title="{{this}}">' +
155 '<label for="{{../elementid}}_atto_table_bordercolour_{{@index}}" class="accesshide">' +
156 '{{this}}</label>' +
157 '</div>' +
d26d0d9b
RW
158 '{{/each}}' +
159 '</div>' +
1b217025
BB
160 '</div>' +
161 '</div>' +
b87e11c8 162 '{{/if}}' +
163 '{{#if allowBackgroundColour}}' +
1b217025
BB
164 '<div class="m-b-1 form-group row-fluid">' +
165 '<div class="col-sm-4 span4">' +
166 '<label for="{{elementid}}_atto_table_backgroundcolour">' +
b87e11c8 167 '{{get_string "backgroundcolour" component}}</label>' +
1b217025 168 '</div><div class="col-sm-8 span8">' +
b87e11c8 169 '<div id="{{elementid}}_atto_table_backgroundcolour"' +
1b217025
BB
170 'class="form-inline {{CSS.BACKGROUNDCOLOUR}} {{CSS.AVAILABLECOLORS}}" size="1">' +
171 '<div class="tablebackgroundcolor" style="background-color:transparent;color:transparent">' +
b87e11c8 172 '<input id="{{../elementid}}_atto_table_backgroundcolour_-1"' +
1b217025 173 'type="radio" class="m-0" name="backgroundColour" value="none" checked="checked"' +
f45937e1 174 'title="{{get_string "themedefault" component}}"></input>' +
1b217025
BB
175 '<label for="{{../elementid}}_atto_table_backgroundcolour_-1" class="accesshide">' +
176 '{{get_string "themedefault" component}}</label>' +
177 '</div>' +
b87e11c8 178
179 '{{#each availableColours}}' +
1b217025 180 '<div class="tablebackgroundcolor" style="background-color:{{this}};color:{{this}}">' +
b87e11c8 181 '<input id="{{../elementid}}_atto_table_backgroundcolour_{{@index}}"' +
1b217025
BB
182 'type="radio" class="m-0" name="backgroundColour" value="' + '{{this}}' + '" title="{{this}}">' +
183 '<label for="{{../elementid}}_atto_table_backgroundcolour_{{@index}}" class="accesshide">' +
184 '{{this}}</label>' +
185 '</div>' +
b87e11c8 186 '{{/each}}' +
187 '</div>' +
1b217025
BB
188 '</div>' +
189 '</div>' +
b87e11c8 190 '{{/if}}' +
191 '{{#if allowWidth}}' +
1b217025
BB
192 '<div class="m-b-1 form-group row-fluid">' +
193 '<div class="col-sm-4 span4">' +
194 '<label for="{{elementid}}_atto_table_width">' +
b87e11c8 195 '{{get_string "width" component}}</label>' +
1b217025
BB
196 '</div><div class="col-sm-8 span8">' +
197 '<div class="form-inline">' +
198 '<input name="width" id="{{elementid}}_atto_table_width" ' +
199 'class="form-control w-auto m-r-1 {{CSS.WIDTH}}" size="8" ' +
d26d0d9b 200 'type="number" min="0" max="100"/>' +
1b217025
BB
201 '<label>{{CSS.WIDTHUNIT}}</label>' +
202 '</div>' +
203 '</div>' +
204 '</div>' +
b87e11c8 205 '{{/if}}' +
206 '</fieldset>' +
207 '{{/if}}' +
208 '<div class="mdl-align">' +
209 '<br/>' +
210 '{{#if edit}}' +
29551c4b 211 '<button class="btn btn-secondary submit" type="submit">{{get_string "updatetable" component}}</button>' +
b87e11c8 212 '{{/if}}' +
213 '{{#if nonedit}}' +
29551c4b 214 '<button class="btn btn-secondary submit" type="submit">{{get_string "createtable" component}}</button>' +
b87e11c8 215 '{{/if}}' +
62467795 216 '</div>' +
23cead68 217 '</form>',
62467795 218 CSS = {
353473aa 219 CAPTION: 'caption',
556decf1 220 CAPTIONPOSITION: 'captionposition',
353473aa
DW
221 HEADERS: 'headers',
222 ROWS: 'rows',
223 COLUMNS: 'columns',
224 SUBMIT: 'submit',
b87e11c8 225 FORM: 'atto_form',
226 BORDERS: 'borders',
227 BORDERSIZE: 'bordersize',
228 BORDERSIZEUNIT: 'px',
229 BORDERCOLOUR: 'bordercolour',
230 BORDERSTYLE: 'borderstyle',
231 BACKGROUNDCOLOUR: 'backgroundcolour',
232 WIDTH: 'customwidth',
233 WIDTHUNIT: '%',
234 AVAILABLECOLORS: 'availablecolors',
235 COLOURROW: 'colourrow'
353473aa
DW
236 },
237 SELECTORS = {
238 CAPTION: '.' + CSS.CAPTION,
556decf1 239 CAPTIONPOSITION: '.' + CSS.CAPTIONPOSITION,
353473aa
DW
240 HEADERS: '.' + CSS.HEADERS,
241 ROWS: '.' + CSS.ROWS,
242 COLUMNS: '.' + CSS.COLUMNS,
243 SUBMIT: '.' + CSS.SUBMIT,
b87e11c8 244 BORDERS: '.' + CSS.BORDERS,
245 BORDERSIZE: '.' + CSS.BORDERSIZE,
246 BORDERCOLOURS: '.' + CSS.BORDERCOLOUR + ' input[name="borderColour"]',
247 SELECTEDBORDERCOLOUR: '.' + CSS.BORDERCOLOUR + ' input[name="borderColour"]:checked',
248 BORDERSTYLE: '.' + CSS.BORDERSTYLE,
249 BACKGROUNDCOLOURS: '.' + CSS.BACKGROUNDCOLOUR + ' input[name="backgroundColour"]',
250 SELECTEDBACKGROUNDCOLOUR: '.' + CSS.BACKGROUNDCOLOUR + ' input[name="backgroundColour"]:checked',
251 FORM: '.atto_form',
252 WIDTH: '.' + CSS.WIDTH,
253 AVAILABLECOLORS: '.' + CSS.AVAILABLECOLORS
62467795
AN
254 };
255
256Y.namespace('M.atto_table').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
adca7326
DW
257
258 /**
62467795
AN
259 * A reference to the current selection at the time that the dialogue
260 * was opened.
adca7326 261 *
62467795
AN
262 * @property _currentSelection
263 * @type Range
264 * @private
adca7326 265 */
62467795 266 _currentSelection: null,
adca7326
DW
267
268 /**
62467795 269 * The contextual menu that we can open.
adca7326 270 *
62467795
AN
271 * @property _contextMenu
272 * @type M.editor_atto.Menu
273 * @private
274 */
275 _contextMenu: null,
276
277 /**
278 * The last modified target.
279 *
280 * @property _lastTarget
281 * @type Node
282 * @private
adca7326 283 */
62467795
AN
284 _lastTarget: null,
285
ee616cff
AN
286 /**
287 * The list of menu items.
288 *
289 * @property _menuOptions
290 * @type Object
291 * @private
292 */
293 _menuOptions: null,
294
62467795
AN
295 initializer: function() {
296 this.addButton({
297 icon: 'e/table',
298 callback: this._displayTableEditor,
299 tags: 'table'
300 });
62467795
AN
301 // Disable mozilla table controls.
302 if (Y.UA.gecko) {
303 document.execCommand("enableInlineTableEditing", false, false);
304 document.execCommand("enableObjectResizing", false, false);
305 }
306 },
adca7326
DW
307
308 /**
62467795 309 * Display the table tool.
adca7326 310 *
62467795
AN
311 * @method _displayDialogue
312 * @private
adca7326 313 */
62467795
AN
314 _displayDialogue: function() {
315 // Store the current cursor position.
316 this._currentSelection = this.get('host').getSelection();
317
318 if (this._currentSelection !== false && (!this._currentSelection.collapsed)) {
319 var dialogue = this.getDialogue({
320 headerContent: M.util.get_string('createtable', COMPONENT),
e5ddec38 321 focusAfterHide: true,
b87e11c8 322 focusOnShowSelector: SELECTORS.CAPTION,
323 width: DIALOGUE.WIDTH
62467795
AN
324 });
325
326 // Set the dialogue content, and then show the dialogue.
b87e11c8 327 dialogue.set('bodyContent', this._getDialogueContent(false))
62467795 328 .show();
f45937e1
RW
329
330 this._updateAvailableSettings();
62467795
AN
331 }
332 },
adca7326
DW
333
334 /**
62467795 335 * Display the appropriate table editor.
adca7326 336 *
62467795
AN
337 * If the current selection includes a table, then we show the
338 * contextual menu, otherwise show the table creation dialogue.
339 *
340 * @method _displayTableEditor
341 * @param {EventFacade} e
342 * @private
adca7326 343 */
62467795 344 _displayTableEditor: function(e) {
f0ddce4d 345 var cell = this._getSuitableTableCell();
62467795
AN
346 if (cell) {
347 // Add the cell to the EventFacade to save duplication in when showing the menu.
348 e.tableCell = cell;
349 return this._showTableMenu(e);
350 }
7b280674 351 return this._displayDialogue(e);
3a6511a5
JC
352 },
353
f0ddce4d
JC
354 /**
355 * Returns whether or not the parameter node exists within the editor.
356 *
357 * @method _stopAtContentEditableFilter
358 * @param {Node} node
359 * @private
360 * @return {boolean} whether or not the parameter node exists within the editor.
361 */
362 _stopAtContentEditableFilter: function(node) {
db3da830 363 return this.editor.contains(node);
f0ddce4d
JC
364 },
365
adca7326 366 /**
62467795
AN
367 * Return the dialogue content for the tool, attaching any required
368 * events.
adca7326 369 *
62467795
AN
370 * @method _getDialogueContent
371 * @private
372 * @return {Node} The content to place in the dialogue.
adca7326 373 */
b87e11c8 374 _getDialogueContent: function(edit) {
62467795 375 var template = Y.Handlebars.compile(TEMPLATE);
f45937e1 376 var allowBorders = this.get('allowBorders');
b87e11c8 377
62467795
AN
378 this._content = Y.Node.create(template({
379 CSS: CSS,
380 elementid: this.get('host').get('elementid'),
b87e11c8 381 component: COMPONENT,
382 edit: edit,
383 nonedit: !edit,
384 allowStyling: this.get('allowStyling'),
f45937e1 385 allowBorders: allowBorders,
b87e11c8 386 borderStyles: this.get('borderStyles'),
b87e11c8 387 allowBackgroundColour: this.get('allowBackgroundColour'),
f45937e1 388 availableColours: this.get('availableColors'),
b87e11c8 389 allowWidth: this.get('allowWidth')
62467795
AN
390 }));
391
392 // Handle table setting.
b87e11c8 393 if (edit) {
394 this._content.one('.submit').on('click', this._updateTable, this);
395 } else {
396 this._content.one('.submit').on('click', this._setTable, this);
397 }
62467795 398
f45937e1
RW
399 if (allowBorders) {
400 this._content.one('[name="borders"]').on('change', this._updateAvailableSettings, this);
401 }
402
62467795
AN
403 return this._content;
404 },
adca7326 405
f45937e1
RW
406 /**
407 * Disables options within the dialogue if they shouldn't be available.
408 * E.g.
409 * If borders are set to "Theme default" then the border size, style and
410 * colour options are disabled.
411 *
412 * @method _updateAvailableSettings
413 * @private
414 */
415 _updateAvailableSettings: function() {
416 var tableForm = this._content,
417 enableBorders = tableForm.one('[name="borders"]'),
3a0bc0fd
DP
418 borderStyle = tableForm.one('[name="borderstyles"]'),
419 borderSize = tableForm.one('[name="bordersize"]'),
420 borderColour = tableForm.all('[name="borderColour"]'),
421 disabledValue = 'removeAttribute';
f45937e1 422
d26d0d9b
RW
423 if (!enableBorders) {
424 return;
425 }
426
f45937e1
RW
427 if (enableBorders.get('value') === 'default') {
428 disabledValue = 'setAttribute';
429 }
430
431 if (borderStyle) {
432 borderStyle[disabledValue]('disabled');
433 }
434
435 if (borderSize) {
436 borderSize[disabledValue]('disabled');
437 }
438
439 if (borderColour) {
440 borderColour[disabledValue]('disabled');
441 }
442
443 },
444
f0ddce4d
JC
445 /**
446 * Given the current selection, return a table cell suitable for table editing
447 * purposes, i.e. the first table cell selected, or the first cell in the table
448 * that the selection exists in, or null if not within a table.
449 *
450 * @method _getSuitableTableCell
451 * @private
452 * @return {Node} suitable target cell, or null if not within a table
453 */
454 _getSuitableTableCell: function() {
455 var targetcell = null,
456 host = this.get('host');
db3da830 457 var stopAtContentEditableFilter = Y.bind(this._stopAtContentEditableFilter, this);
f0ddce4d 458
3a0bc0fd 459 host.getSelectedNodes().some(function(node) {
db3da830 460 if (node.ancestor('td, th, caption', true, stopAtContentEditableFilter)) {
f0ddce4d
JC
461 targetcell = node;
462
db3da830 463 var caption = node.ancestor('caption', true, stopAtContentEditableFilter);
f0ddce4d
JC
464 if (caption) {
465 var table = caption.get('parentNode');
466 if (table) {
467 targetcell = table.one('td, th');
468 }
469 }
470
471 // Once we've found a cell to target, we shouldn't need to keep looking.
472 return true;
473 }
474 });
475
476 if (targetcell) {
477 var selection = host.getSelectionFromNode(targetcell);
478 host.setSelection(selection);
479 }
480
481 return targetcell;
482 },
483
353473aa
DW
484 /**
485 * Change a node from one type to another, copying all attributes and children.
486 *
487 * @method _changeNodeType
488 * @param {Y.Node} node
489 * @param {String} new node type
490 * @private
491 * @chainable
492 */
493 _changeNodeType: function(node, newType) {
494 var newNode = Y.Node.create('<' + newType + '></' + newType + '>');
495 newNode.setAttrs(node.getAttrs());
496 node.get('childNodes').each(function(child) {
497 newNode.append(child.remove());
498 });
499 node.replace(newNode);
500 return newNode;
501 },
502
503 /**
504 * Handle updating an existing table.
505 *
506 * @method _updateTable
507 * @param {EventFacade} e
508 * @private
509 */
510 _updateTable: function(e) {
511 var caption,
556decf1 512 captionposition,
353473aa 513 headers,
b87e11c8 514 borders,
515 bordersize,
516 borderstyle,
517 bordercolour,
518 backgroundcolour,
353473aa 519 table,
b87e11c8 520 width,
353473aa
DW
521 captionnode;
522
523 e.preventDefault();
524 // Hide the dialogue.
525 this.getDialogue({
526 focusAfterHide: null
527 }).hide();
528
529 // Add/update the caption.
530 caption = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.CAPTION);
556decf1 531 captionposition = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.CAPTIONPOSITION);
353473aa 532 headers = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.HEADERS);
b87e11c8 533 borders = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.BORDERS);
534 bordersize = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.BORDERSIZE);
535 bordercolour = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.SELECTEDBORDERCOLOUR);
536 borderstyle = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.BORDERSTYLE);
537 backgroundcolour = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.SELECTEDBACKGROUNDCOLOUR);
538 width = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.WIDTH);
539
353473aa 540 table = this._lastTarget.ancestor('table');
f45937e1
RW
541 this._setAppearance(table, {
542 width: width,
543 borders: borders,
544 borderColour: bordercolour,
545 borderSize: bordersize,
546 borderStyle: borderstyle,
547 backgroundColour: backgroundcolour
b87e11c8 548 });
549
353473aa
DW
550 captionnode = table.one('caption');
551 if (!captionnode) {
556decf1 552 captionnode = Y.Node.create('<caption></caption>');
353473aa
DW
553 table.insert(captionnode, 0);
554 }
555 captionnode.setHTML(caption.get('value'));
556decf1
DM
556 captionnode.setStyle('caption-side', captionposition.get('value'));
557 if (!captionnode.getAttribute('style')) {
558 captionnode.removeAttribute('style');
559 }
353473aa
DW
560
561 // Add the row headers.
562 if (headers.get('value') === 'rows' || headers.get('value') === 'both') {
3a0bc0fd 563 table.all('tr').each(function(row) {
353473aa
DW
564 var cells = row.all('th, td'),
565 firstCell = cells.shift(),
566 newCell;
567
568 if (firstCell.get('tagName') === 'TD') {
569 // Cell is a td but should be a th - change it.
570 newCell = this._changeNodeType(firstCell, 'th');
571 newCell.setAttribute('scope', 'row');
572 } else {
573 firstCell.setAttribute('scope', 'row');
574 }
575
576 // Now make sure all other cells in the row are td.
3a0bc0fd 577 cells.each(function(cell) {
353473aa
DW
578 if (cell.get('tagName') === 'TH') {
579 newCell = this._changeNodeType(cell, 'td');
580 newCell.removeAttribute('scope');
581 }
582 }, this);
583
584 }, this);
585 }
586 // Add the col headers. These may overrule the row headers in the first cell.
587 if (headers.get('value') === 'columns' || headers.get('value') === 'both') {
588 var rows = table.all('tr'),
589 firstRow = rows.shift(),
590 newCell;
591
3a0bc0fd 592 firstRow.all('td, th').each(function(cell) {
353473aa
DW
593 if (cell.get('tagName') === 'TD') {
594 // Cell is a td but should be a th - change it.
595 newCell = this._changeNodeType(cell, 'th');
596 newCell.setAttribute('scope', 'col');
597 } else {
598 cell.setAttribute('scope', 'col');
599 }
600 }, this);
601 // Change all the cells in the rest of the table to tds (unless they are row headers).
602 rows.each(function(row) {
603 var cells = row.all('th, td');
604
605 if (headers.get('value') === 'both') {
606 // Ignore the first cell because it's a row header.
607 cells.shift();
608 }
609 cells.each(function(cell) {
610 if (cell.get('tagName') === 'TH') {
611 newCell = this._changeNodeType(cell, 'td');
612 newCell.removeAttribute('scope');
613 }
614 }, this);
615
616 }, this);
617 }
7d8f825b
DW
618 // Clean the HTML.
619 this.markUpdated();
353473aa
DW
620 },
621
adca7326 622 /**
62467795 623 * Handle creation of a new table.
adca7326 624 *
62467795
AN
625 * @method _setTable
626 * @param {EventFacade} e
627 * @private
adca7326 628 */
62467795
AN
629 _setTable: function(e) {
630 var caption,
556decf1 631 captionposition,
b87e11c8 632 borders,
b87e11c8 633 bordersize,
634 borderstyle,
635 bordercolour,
62467795
AN
636 rows,
637 cols,
638 headers,
639 tablehtml,
b87e11c8 640 backgroundcolour,
641 width,
62467795
AN
642 i, j;
643
adca7326 644 e.preventDefault();
adca7326 645
62467795
AN
646 // Hide the dialogue.
647 this.getDialogue({
648 focusAfterHide: null
649 }).hide();
650
48dc9f01 651 caption = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.CAPTION);
556decf1 652 captionposition = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.CAPTIONPOSITION);
b87e11c8 653 borders = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.BORDERS);
654 bordersize = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.BORDERSIZE);
655 bordercolour = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.SELECTEDBORDERCOLOUR);
656 borderstyle = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.BORDERSTYLE);
657 backgroundcolour = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.SELECTEDBACKGROUNDCOLOUR);
353473aa
DW
658 rows = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.ROWS);
659 cols = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.COLUMNS);
660 headers = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.HEADERS);
b87e11c8 661 width = e.currentTarget.ancestor(SELECTORS.FORM).one(SELECTORS.WIDTH);
662
62467795
AN
663 // Set the selection.
664 this.get('host').setSelection(this._currentSelection);
665
666 // Note there are some spaces inserted in the cells and before and after, so that users have somewhere to click.
667 var nl = "\n";
f45937e1
RW
668 var tableId = Y.guid();
669 tablehtml = '<br/>' + nl + '<table id="' + tableId + '">' + nl;
adca7326 670
556decf1
DM
671 var captionstyle = '';
672 if (captionposition.get('value')) {
673 captionstyle = ' style="caption-side: ' + captionposition.get('value') + '"';
674 }
675 tablehtml += '<caption' + captionstyle + '>' + Y.Escape.html(caption.get('value')) + '</caption>' + nl;
62467795
AN
676 i = 0;
677 if (headers.get('value') === 'columns' || headers.get('value') === 'both') {
678 i = 1;
679 tablehtml += '<thead>' + nl + '<tr>' + nl;
680 for (j = 0; j < parseInt(cols.get('value'), 10); j++) {
f45937e1 681 tablehtml += '<th scope="col"></th>' + nl;
62467795
AN
682 }
683 tablehtml += '</tr>' + nl + '</thead>' + nl;
684 }
685 tablehtml += '<tbody>' + nl;
686 for (; i < parseInt(rows.get('value'), 10); i++) {
687 tablehtml += '<tr>' + nl;
688 for (j = 0; j < parseInt(cols.get('value'), 10); j++) {
689 if (j === 0 && (headers.get('value') === 'rows' || headers.get('value') === 'both')) {
f45937e1 690 tablehtml += '<th scope="row"></th>' + nl;
62467795 691 } else {
f45937e1 692 tablehtml += '<td ></td>' + nl;
62467795
AN
693 }
694 }
695 tablehtml += '</tr>' + nl;
adca7326 696 }
62467795
AN
697 tablehtml += '</tbody>' + nl;
698 tablehtml += '</table>' + nl + '<br/>';
adca7326 699
62467795
AN
700 this.get('host').insertContentAtFocusPoint(tablehtml);
701
f45937e1
RW
702 var tableNode = Y.one('#' + tableId);
703 this._setAppearance(tableNode, {
704 width: width,
705 borders: borders,
706 borderColour: bordercolour,
707 borderSize: bordersize,
708 borderStyle: borderstyle,
709 backgroundColour: backgroundcolour
710 });
711 tableNode.removeAttribute('id');
712
62467795
AN
713 // Mark the content as updated.
714 this.markUpdated();
adca7326
DW
715 },
716
353473aa
DW
717 /**
718 * Search for all the cells in the current, next and previous columns.
719 *
720 * @method _findColumnCells
721 * @private
722 * @return {Object} containing current, prev and next {Y.NodeList}s
723 */
724 _findColumnCells: function() {
725 var columnindex = this._getColumnIndex(this._lastTarget),
726 rows = this._lastTarget.ancestor('table').all('tr'),
727 currentcells = new Y.NodeList(),
728 prevcells = new Y.NodeList(),
729 nextcells = new Y.NodeList();
730
731 rows.each(function(row) {
732 var cells = row.all('td, th'),
733 cell = cells.item(columnindex),
3a0bc0fd
DP
734 cellprev = cells.item(columnindex - 1),
735 cellnext = cells.item(columnindex + 1);
353473aa
DW
736 currentcells.push(cell);
737 if (cellprev) {
738 prevcells.push(cellprev);
739 }
740 if (cellnext) {
741 nextcells.push(cellnext);
742 }
743 });
744
745 return {
746 current: currentcells,
747 prev: prevcells,
748 next: nextcells
749 };
750 },
751
752 /**
753 * Hide the entries in the context menu that don't make sense with the
754 * current selection.
755 *
756 * @method _hideInvalidEntries
757 * @param {Y.Node} node - The node containing the menu.
758 * @private
759 */
760 _hideInvalidEntries: function(node) {
761 // Moving rows.
762 var table = this._lastTarget.ancestor('table'),
763 row = this._lastTarget.ancestor('tr'),
764 rows = table.all('tr'),
765 rowindex = rows.indexOf(row),
766 prevrow = rows.item(rowindex - 1),
767 prevrowhascells = prevrow ? prevrow.one('td') : null;
768
769 if (!row || !prevrowhascells) {
770 node.one('[data-change="moverowup"]').hide();
771 } else {
772 node.one('[data-change="moverowup"]').show();
773 }
774
775 var nextrow = rows.item(rowindex + 1),
776 rowhascell = row ? row.one('td') : false;
777
778 if (!row || !nextrow || !rowhascell) {
779 node.one('[data-change="moverowdown"]').hide();
780 } else {
781 node.one('[data-change="moverowdown"]').show();
782 }
783
784 // Moving columns.
785 var cells = this._findColumnCells();
786 if (cells.prev.filter('td').size() > 0) {
787 node.one('[data-change="movecolumnleft"]').show();
788 } else {
789 node.one('[data-change="movecolumnleft"]').hide();
790 }
791
792 var colhascell = cells.current.filter('td').size() > 0;
793 if ((cells.next.size() > 0) && colhascell) {
794 node.one('[data-change="movecolumnright"]').show();
795 } else {
796 node.one('[data-change="movecolumnright"]').hide();
797 }
798
799 // Delete col
800 if (cells.current.filter('td').size() > 0) {
801 node.one('[data-change="deletecolumn"]').show();
802 } else {
803 node.one('[data-change="deletecolumn"]').hide();
804 }
805 // Delete row
806 if (!row || !row.one('td')) {
807 node.one('[data-change="deleterow"]').hide();
808 } else {
809 node.one('[data-change="deleterow"]').show();
810 }
811 },
812
adca7326 813 /**
62467795 814 * Display the table menu.
adca7326 815 *
62467795
AN
816 * @method _showTableMenu
817 * @param {EventFacade} e
818 * @private
adca7326 819 */
62467795 820 _showTableMenu: function(e) {
adca7326
DW
821 e.preventDefault();
822
62467795
AN
823 var boundingBox;
824
825 if (!this._contextMenu) {
ee616cff
AN
826 this._menuOptions = [
827 {
828 text: M.util.get_string("addcolumnafter", COMPONENT),
829 data: {
830 change: "addcolumnafter"
831 }
832 }, {
833 text: M.util.get_string("addrowafter", COMPONENT),
834 data: {
835 change: "addrowafter"
836 }
837 }, {
838 text: M.util.get_string("moverowup", COMPONENT),
839 data: {
840 change: "moverowup"
841 }
842 }, {
843 text: M.util.get_string("moverowdown", COMPONENT),
844 data: {
845 change: "moverowdown"
846 }
847 }, {
848 text: M.util.get_string("movecolumnleft", COMPONENT),
849 data: {
850 change: "movecolumnleft"
851 }
852 }, {
853 text: M.util.get_string("movecolumnright", COMPONENT),
854 data: {
855 change: "movecolumnright"
856 }
857 }, {
858 text: M.util.get_string("deleterow", COMPONENT),
859 data: {
860 change: "deleterow"
861 }
862 }, {
863 text: M.util.get_string("deletecolumn", COMPONENT),
864 data: {
865 change: "deletecolumn"
866 }
92810c07
DW
867 }, {
868 text: M.util.get_string("edittable", COMPONENT),
869 data: {
870 change: "edittable"
871 }
ee616cff
AN
872 }
873 ];
62467795
AN
874
875 this._contextMenu = new Y.M.editor_atto.Menu({
ee616cff 876 items: this._menuOptions
adca7326 877 });
62467795
AN
878
879 // Add event handlers for table control menus.
880 boundingBox = this._contextMenu.get('boundingBox');
881 boundingBox.delegate('click', this._handleTableChange, 'a', this);
adca7326 882 }
ee616cff 883
62467795
AN
884 boundingBox = this._contextMenu.get('boundingBox');
885
adca7326 886 // We store the cell of the last click (the control node is transient).
62467795
AN
887 this._lastTarget = e.tableCell.ancestor('.editor_atto_content td, .editor_atto_content th', true);
888
353473aa
DW
889 this._hideInvalidEntries(boundingBox);
890
c63f9053
AN
891 // Clear the focusAfterHide for any other menus which may be open.
892 Y.Array.each(this.get('host').openMenus, function(menu) {
893 menu.set('focusAfterHide', null);
894 });
895
896 // Ensure that we focus on the button in the toolbar when we tab back to the menu.
897 var creatorButton = this.buttons[this.name];
898 this.get('host')._setTabFocus(creatorButton);
899
62467795
AN
900 // Show the context menu, and align to the current position.
901 this._contextMenu.show();
f8c3af13 902 this._contextMenu.align(this.buttons.table, [Y.WidgetPositionAlign.TL, Y.WidgetPositionAlign.BL]);
c63f9053 903 this._contextMenu.set('focusAfterHide', creatorButton);
62467795
AN
904
905 // If there are any anchors in the bounding box, focus on the first.
906 if (boundingBox.one('a')) {
907 boundingBox.one('a').focus();
26f8822d 908 }
c63f9053
AN
909
910 // Add this menu to the list of open menus.
911 this.get('host').openMenus = [this._contextMenu];
62467795
AN
912 },
913
914 /**
915 * Handle a selection from the table control menu.
916 *
917 * @method _handleTableChange
918 * @param {EventFacade} e
919 * @private
920 */
921 _handleTableChange: function(e) {
922 e.preventDefault();
adca7326 923
ee616cff 924 this._contextMenu.set('focusAfterHide', this.get('host').editor);
62467795 925 // Hide the context menu.
ee616cff 926 this._contextMenu.hide(e);
62467795
AN
927
928 // Make our changes.
929 switch (e.target.getData('change')) {
930 case 'addcolumnafter':
931 this._addColumnAfter();
932 break;
933 case 'addrowafter':
934 this._addRowAfter();
935 break;
936 case 'deleterow':
937 this._deleteRow();
938 break;
939 case 'deletecolumn':
940 this._deleteColumn();
941 break;
353473aa
DW
942 case 'edittable':
943 this._editTable();
944 break;
62467795
AN
945 case 'moverowdown':
946 this._moveRowDown();
947 break;
948 case 'moverowup':
949 this._moveRowUp();
950 break;
951 case 'movecolumnleft':
952 this._moveColumnLeft();
953 break;
954 case 'movecolumnright':
955 this._moveColumnRight();
956 break;
adca7326
DW
957 }
958 },
959
960 /**
961 * Determine the index of a row in a table column.
962 *
62467795
AN
963 * @method _getRowIndex
964 * @param {Node} cell
965 * @private
adca7326 966 */
62467795 967 _getRowIndex: function(cell) {
adca7326
DW
968 var tablenode = cell.ancestor('table'),
969 rownode = cell.ancestor('tr');
970
971 if (!tablenode || !rownode) {
972 return;
973 }
974
975 var rows = tablenode.all('tr');
976
977 return rows.indexOf(rownode);
978 },
979
980 /**
981 * Determine the index of a column in a table row.
982 *
62467795
AN
983 * @method _getColumnIndex
984 * @param {Node} cellnode
985 * @private
adca7326 986 */
62467795 987 _getColumnIndex: function(cellnode) {
adca7326
DW
988 var rownode = cellnode.ancestor('tr');
989
990 if (!rownode) {
991 return;
992 }
993
994 var cells = rownode.all('td, th');
995
996 return cells.indexOf(cellnode);
997 },
998
999 /**
62467795 1000 * Delete the current row.
adca7326 1001 *
62467795
AN
1002 * @method _deleteRow
1003 * @private
adca7326 1004 */
62467795
AN
1005 _deleteRow: function() {
1006 var row = this._lastTarget.ancestor('tr');
adca7326 1007
353473aa
DW
1008 if (row && row.one('td')) {
1009 // Only delete rows with at least one non-header cell.
1010 row.remove(true);
adca7326
DW
1011 }
1012
1013 // Clean the HTML.
62467795 1014 this.markUpdated();
adca7326
DW
1015 },
1016
1017 /**
1018 * Move row up
1019 *
62467795
AN
1020 * @method _moveRowUp
1021 * @private
adca7326 1022 */
62467795 1023 _moveRowUp: function() {
353473aa
DW
1024 var row = this._lastTarget.ancestor('tr'),
1025 prevrow = row.previous('tr');
adca7326
DW
1026 if (!row || !prevrow) {
1027 return;
1028 }
1029
1030 row.swap(prevrow);
1031 // Clean the HTML.
62467795 1032 this.markUpdated();
adca7326
DW
1033 },
1034
1035 /**
1036 * Move column left
1037 *
62467795
AN
1038 * @method _moveColumnLeft
1039 * @private
adca7326 1040 */
62467795 1041 _moveColumnLeft: function() {
353473aa 1042 var cells = this._findColumnCells();
adca7326 1043
353473aa 1044 if (cells.current.size() > 0 && cells.prev.size() > 0 && cells.current.size() === cells.prev.size()) {
adca7326 1045 var i = 0;
353473aa
DW
1046 for (i = 0; i < cells.current.size(); i++) {
1047 var cell = cells.current.item(i),
1048 prevcell = cells.prev.item(i);
adca7326
DW
1049
1050 cell.swap(prevcell);
1051 }
1052 }
1053 // Cleanup.
62467795 1054 this.markUpdated();
adca7326
DW
1055 },
1056
353473aa
DW
1057 /**
1058 * Add a caption to the table if it doesn't have one.
1059 *
1060 * @method _addCaption
1061 * @private
1062 */
1063 _addCaption: function() {
1064 var table = this._lastTarget.ancestor('table'),
1065 caption = table.one('caption');
1066
1067 if (!caption) {
1068 table.insert(Y.Node.create('<caption>&nbsp;</caption>'), 1);
1069 }
1070 },
1071
1072 /**
1073 * Remove a caption from the table if has one.
1074 *
1075 * @method _removeCaption
1076 * @private
1077 */
1078 _removeCaption: function() {
1079 var table = this._lastTarget.ancestor('table'),
1080 caption = table.one('caption');
1081
1082 if (caption) {
1083 caption.remove(true);
1084 }
1085 },
1086
adca7326 1087 /**
62467795 1088 * Move column right.
adca7326 1089 *
62467795
AN
1090 * @method _moveColumnRight
1091 * @private
adca7326 1092 */
62467795 1093 _moveColumnRight: function() {
353473aa 1094 var cells = this._findColumnCells();
adca7326 1095
353473aa 1096 // Check we have some tds in this column, and one exists to the right.
3a0bc0fd 1097 if ((cells.next.size() > 0) &&
353473aa
DW
1098 (cells.current.size() === cells.next.size()) &&
1099 (cells.current.filter('td').size() > 0)) {
adca7326 1100 var i = 0;
353473aa
DW
1101 for (i = 0; i < cells.current.size(); i++) {
1102 var cell = cells.current.item(i),
1103 nextcell = cells.next.item(i);
adca7326
DW
1104
1105 cell.swap(nextcell);
1106 }
1107 }
1108 // Cleanup.
62467795 1109 this.markUpdated();
adca7326
DW
1110 },
1111
1112 /**
62467795 1113 * Move row down.
adca7326 1114 *
62467795
AN
1115 * @method _moveRowDown
1116 * @private
adca7326 1117 */
62467795 1118 _moveRowDown: function() {
353473aa
DW
1119 var row = this._lastTarget.ancestor('tr'),
1120 nextrow = row.next('tr');
1121 if (!row || !nextrow || !row.one('td')) {
adca7326
DW
1122 return;
1123 }
1124
1125 row.swap(nextrow);
1126 // Clean the HTML.
62467795 1127 this.markUpdated();
adca7326
DW
1128 },
1129
b87e11c8 1130 /**
1131 * Obtain values for the table borders
1132 *
1133 * @method _getBorderConfiguration
1134 * @param {Node} node
1135 * @private
1136 * @return {Array} or {Boolean} Returns the settings, if presents, or else returns false
1137 */
1138 _getBorderConfiguration: function(node) {
1139 // We need to make a clone of the node in order to avoid grabbing any
1140 // of the computed styles from the DOM. We only want inline styles set by us.
1141 var shadowNode = node.cloneNode(true);
1142 var borderStyle = shadowNode.getStyle('borderStyle'),
1143 borderColor = shadowNode.getStyle('borderColor'),
1144 borderWidth = shadowNode.getStyle('borderWidth');
1145
1146 if (borderStyle || borderColor || borderWidth) {
1147 var hexColour = Y.Color.toHex(borderColor);
1148 var width = parseInt(borderWidth, 10);
1149 return {
1150 borderStyle: borderStyle,
1151 borderColor: hexColour === "#" ? null : hexColour,
1152 borderWidth: isNaN(width) ? null : width
1153 };
1154 }
1155
1156 return false;
1157 },
1158
f45937e1
RW
1159 /**
1160 * Set the appropriate styles on the given table node according to
1161 * the provided configuration.
1162 *
1163 * @method _setAppearance
1164 * @param {Node} The table node to be modified.
1165 * @param {Object} Configuration object (associative array) containing the form nodes for
1166 * border styling.
1167 * @private
1168 */
1169 _setAppearance: function(tableNode, configuration) {
1170 var borderhex,
1171 borderSizeValue,
1172 borderStyleValue,
1173 backgroundcolourvalue;
1174
1175 if (configuration.borderColour) {
1176 borderhex = configuration.borderColour.get('value');
1177 }
1178
1179 if (configuration.borderSize) {
1180 borderSizeValue = configuration.borderSize.get('value');
1181 }
1182
1183 if (configuration.borderStyle) {
1184 borderStyleValue = configuration.borderStyle.get('value');
1185 }
1186
1187 if (configuration.backgroundColour) {
1188 backgroundcolourvalue = configuration.backgroundColour.get('value');
1189 }
1190
1191 // Clear the inline border styling
1192 tableNode.removeAttribute('style');
1193 tableNode.all('td, th').each(function(cell) {
1194 cell.removeAttribute('style');
1195 }, this);
1196
1197 if (configuration.borders) {
1198 if (configuration.borders.get('value') === 'outer') {
1199 tableNode.setStyle('borderWidth', borderSizeValue + CSS.BORDERSIZEUNIT);
1200 tableNode.setStyle('borderStyle', borderStyleValue);
1201
1202 if (borderhex !== 'none') {
1203 tableNode.setStyle('borderColor', borderhex);
1204 }
1205 } else if (configuration.borders.get('value') === 'all') {
1206 tableNode.all('td, th').each(function(cell) {
1207 cell.setStyle('borderWidth', borderSizeValue + CSS.BORDERSIZEUNIT);
1208 cell.setStyle('borderStyle', borderStyleValue);
1209
1210 if (borderhex !== 'none') {
1211 cell.setStyle('borderColor', borderhex);
1212 }
1213 }, this);
1214 }
1215 }
1216
1217 if (backgroundcolourvalue !== 'none') {
1c53bc81 1218 tableNode.setStyle('backgroundColor', backgroundcolourvalue);
f45937e1
RW
1219 }
1220
1221 if (configuration.width && configuration.width.get('value')) {
1222 tableNode.setStyle('width', configuration.width.get('value') + CSS.WIDTHUNIT);
1223 }
1224 },
1225
353473aa
DW
1226 /**
1227 * Edit table (show the dialogue).
1228 *
1229 * @method _editTable
1230 * @private
1231 */
1232 _editTable: function() {
1233 var dialogue = this.getDialogue({
1234 headerContent: M.util.get_string('edittable', COMPONENT),
e5ddec38 1235 focusAfterHide: false,
b87e11c8 1236 focusOnShowSelector: SELECTORS.CAPTION,
1237 width: DIALOGUE.WIDTH
353473aa
DW
1238 });
1239
1240 // Set the dialogue content, and then show the dialogue.
b87e11c8 1241 var node = this._getDialogueContent(true),
353473aa 1242 captioninput = node.one(SELECTORS.CAPTION),
556decf1 1243 captionpositioninput = node.one(SELECTORS.CAPTIONPOSITION),
353473aa 1244 headersinput = node.one(SELECTORS.HEADERS),
b87e11c8 1245 borderinput = node.one(SELECTORS.BORDERS),
1246 borderstyle = node.one(SELECTORS.BORDERSTYLE),
1247 bordercolours = node.all(SELECTORS.BORDERCOLOURS),
1248 bordersize = node.one(SELECTORS.BORDERSIZE),
1249 backgroundcolours = node.all(SELECTORS.BACKGROUNDCOLOURS),
1250 width = node.one(SELECTORS.WIDTH),
353473aa 1251 table = this._lastTarget.ancestor('table'),
f45937e1
RW
1252 captionnode = table.one('caption'),
1253 hexColour,
1254 matchedInput;
353473aa
DW
1255
1256 if (captionnode) {
1257 captioninput.set('value', captionnode.getHTML());
1258 } else {
1259 captioninput.set('value', '');
1260 }
1261
b87e11c8 1262 if (width && table.getStyle('width').indexOf('px') === -1) {
1263 width.set('value', parseInt(table.getStyle('width'), 10));
1264 }
1265
bcd58d9f 1266 if (captionpositioninput && captionnode && captionnode.getAttribute('style')) {
556decf1
DM
1267 captionpositioninput.set('value', captionnode.getStyle('caption-side'));
1268 } else {
1269 // Default to none.
1270 captionpositioninput.set('value', '');
1271 }
1272
1c53bc81
RW
1273 if (table.getStyle('backgroundColor') && this.get('allowBackgroundColour')) {
1274 hexColour = Y.Color.toHex(table.getStyle('backgroundColor'));
f45937e1 1275 matchedInput = backgroundcolours.filter('[value="' + hexColour + '"]');
b87e11c8 1276
1277 if (matchedInput) {
1278 matchedInput.set("checked", true);
1279 }
1280 }
1281
1282 if (this.get('allowBorders')) {
1283 var borderValue = 'default',
1284 borderConfiguration = this._getBorderConfiguration(table);
1285
1286 if (borderConfiguration) {
f45937e1 1287 borderValue = 'outer';
b87e11c8 1288 } else {
1289 borderConfiguration = this._getBorderConfiguration(table.one('td'));
1290 if (borderConfiguration) {
1291 borderValue = 'all';
1292 }
1293 }
1294
1295 if (borderConfiguration) {
1296 var borderStyle = borderConfiguration.borderStyle || DEFAULT.BORDERSTYLE;
1297 var borderSize = borderConfiguration.borderWidth || DEFAULT.BORDERWIDTH;
1298 borderstyle.set('value', borderStyle);
1299 bordersize.set('value', borderSize);
1300 borderinput.set('value', borderValue);
1301
f45937e1
RW
1302 hexColour = borderConfiguration.borderColor;
1303 matchedInput = bordercolours.filter('[value="' + hexColour + '"]');
b87e11c8 1304
1305 if (matchedInput) {
1306 matchedInput.set("checked", true);
1307 }
1308 }
1309 }
1310
353473aa
DW
1311 var headersvalue = 'columns';
1312 if (table.one('th[scope="row"]')) {
1313 headersvalue = 'rows';
1314 if (table.one('th[scope="col"]')) {
1315 headersvalue = 'both';
1316 }
1317 }
1318 headersinput.set('value', headersvalue);
1319 dialogue.set('bodyContent', node).show();
f45937e1 1320 this._updateAvailableSettings();
353473aa
DW
1321 },
1322
1323
adca7326 1324 /**
62467795 1325 * Delete the current column.
adca7326 1326 *
62467795
AN
1327 * @method _deleteColumn
1328 * @private
adca7326 1329 */
62467795 1330 _deleteColumn: function() {
353473aa
DW
1331 var columnindex = this._getColumnIndex(this._lastTarget),
1332 table = this._lastTarget.ancestor('table'),
1333 rows = table.all('tr'),
1334 columncells = new Y.NodeList(),
1335 hastd = false;
adca7326
DW
1336
1337 rows.each(function(row) {
1338 var cells = row.all('td, th');
1339 var cell = cells.item(columnindex);
1340 if (cell.get('tagName') === 'TD') {
1341 hastd = true;
1342 }
1343 columncells.push(cell);
1344 });
1345
353473aa 1346 // Do not delete all the headers.
adca7326
DW
1347 if (hastd) {
1348 columncells.remove(true);
1349 }
1350
1351 // Clean the HTML.
62467795 1352 this.markUpdated();
adca7326
DW
1353 },
1354
1355 /**
1356 * Add a row after the current row.
1357 *
62467795
AN
1358 * @method _addRowAfter
1359 * @private
adca7326 1360 */
62467795 1361 _addRowAfter: function() {
aa65ecea 1362 var target = this._lastTarget.ancestor('tr'),
1363 tablebody = this._lastTarget.ancestor('table').one('tbody');
adca7326
DW
1364 if (!tablebody) {
1365 // Not all tables have tbody.
62467795 1366 tablebody = this._lastTarget.ancestor('table');
adca7326
DW
1367 }
1368
1369 var firstrow = tablebody.one('tr');
1370 if (!firstrow) {
62467795 1371 firstrow = this._lastTarget.ancestor('table').one('tr');
adca7326
DW
1372 }
1373 if (!firstrow) {
1374 // Table has no rows. Boo.
1375 return;
1376 }
557f44d9 1377 var newrow = firstrow.cloneNode(true);
3a0bc0fd 1378 newrow.all('th, td').each(function(tablecell) {
adca7326
DW
1379 if (tablecell.get('tagName') === 'TH') {
1380 if (tablecell.getAttribute('scope') !== 'row') {
1381 var newcell = Y.Node.create('<td></td>');
1382 tablecell.replace(newcell);
1383 tablecell = newcell;
1384 }
1385 }
1386 tablecell.setHTML('&nbsp;');
1387 });
1388
aa65ecea 1389 if (target.ancestor('thead')) {
1390 target = firstrow;
1391 tablebody.insert(newrow, target);
1392 } else {
1393 target.insert(newrow, 'after');
1394 }
adca7326
DW
1395
1396 // Clean the HTML.
62467795 1397 this.markUpdated();
adca7326
DW
1398 },
1399
1400 /**
1401 * Add a column after the current column.
1402 *
62467795
AN
1403 * @method _addColumnAfter
1404 * @private
adca7326 1405 */
62467795 1406 _addColumnAfter: function() {
353473aa
DW
1407 var cells = this._findColumnCells(),
1408 before = true,
1409 clonecells = cells.next;
1410 if (cells.next.size() <= 0) {
1411 before = false;
1412 clonecells = cells.current;
1413 }
adca7326 1414
353473aa
DW
1415 Y.each(clonecells, function(cell) {
1416 var newcell = cell.cloneNode();
adca7326
DW
1417 // Clear the content of the cell.
1418 newcell.setHTML('&nbsp;');
1419
353473aa
DW
1420 if (before) {
1421 cell.get('parentNode').insert(newcell, cell);
1422 } else {
1423 cell.get('parentNode').insert(newcell, cell);
1424 cell.swap(newcell);
1425 }
adca7326
DW
1426 }, this);
1427
1428 // Clean the HTML.
62467795 1429 this.markUpdated();
adca7326 1430 }
353473aa 1431
b87e11c8 1432}, {
1433 ATTRS: {
1434 /**
1435 * Whether or not to allow borders
1436 *
1437 * @attribute allowBorder
1438 * @type Boolean
1439 */
1440 allowBorders: {
1441 value: true
1442 },
1443
b87e11c8 1444 /**
1445 * What border styles to allow
1446 *
1447 * @attribute borderStyles
1448 * @type Array
1449 */
1450 borderStyles: {
1451 value: [
d26d0d9b 1452 'none',
b87e11c8 1453 'solid',
1454 'dashed',
1455 'dotted'
d26d0d9b 1456 ]
b87e11c8 1457 },
1458
1459 /**
1460 * Whether or not to allow colourizing the background
1461 *
1462 * @attribute allowBackgroundColour
1463 * @type Boolean
1464 */
1465 allowBackgroundColour: {
1466 value: true
1467 },
1468
1469 /**
1470 * Whether or not to allow setting the table width
1471 *
1472 * @attribute allowWidth
1473 * @type Boolean
1474 */
1475 allowWidth: {
1476 value: true
1477 },
1478
1479 /**
1480 * Whether we allow styling
1481 * @attribute allowStyling
1482 * @type Boolean
1483 */
1484 allowStyling: {
1485 readOnly: true,
1486 getter: function() {
1487 return this.get('allowBorders') || this.get('allowBackgroundColour') || this.get('allowWidth');
1488 }
1489 },
1490
1491 /**
1492 * Available colors
1493 * @attribute availableColors
1494 * @type Array
1495 */
1496 availableColors: {
1497 value: [
1498 '#FFFFFF',
1499 '#EF4540',
1500 '#FFCF35',
1501 '#98CA3E',
1502 '#7D9FD3',
1503 '#333333'
1504 ],
1505 readOnly: true
1506 }
1507 }
62467795 1508});
adca7326
DW
1509
1510
62467795 1511}, '@VERSION@', {"requires": ["moodle-editor_atto-plugin", "moodle-editor_atto-menu", "event", "event-valuechange"]});