[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/editor/atto/plugins/table/yui/src/button/js/ -> button.js (source)

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


Generated: Thu Aug 11 10:00:09 2016 Cross-referenced by PHPXref 0.7.1