[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/editor/atto/plugins/image/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_image
  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_image_alignment-button
  24   */
  25  
  26  /**
  27   * Atto image selection tool.
  28   *
  29   * @namespace M.atto_image
  30   * @class Button
  31   * @extends M.editor_atto.EditorPlugin
  32   */
  33  
  34  var CSS = {
  35          RESPONSIVE: 'img-responsive',
  36          INPUTALIGNMENT: 'atto_image_alignment',
  37          INPUTALT: 'atto_image_altentry',
  38          INPUTHEIGHT: 'atto_image_heightentry',
  39          INPUTSUBMIT: 'atto_image_urlentrysubmit',
  40          INPUTURL: 'atto_image_urlentry',
  41          INPUTSIZE: 'atto_image_size',
  42          INPUTWIDTH: 'atto_image_widthentry',
  43          IMAGEALTWARNING: 'atto_image_altwarning',
  44          IMAGEBROWSER: 'openimagebrowser',
  45          IMAGEPRESENTATION: 'atto_image_presentation',
  46          INPUTCONSTRAIN: 'atto_image_constrain',
  47          INPUTCUSTOMSTYLE: 'atto_image_customstyle',
  48          IMAGEPREVIEW: 'atto_image_preview',
  49          IMAGEPREVIEWBOX: 'atto_image_preview_box'
  50      },
  51      SELECTORS = {
  52          INPUTURL: '.' + CSS.INPUTURL
  53      },
  54      ALIGNMENTS = [
  55          // Vertical alignment.
  56          {
  57              name: 'text-top',
  58              str: 'alignment_top',
  59              value: 'vertical-align',
  60              margin: '0 .5em'
  61          }, {
  62              name: 'middle',
  63              str: 'alignment_middle',
  64              value: 'vertical-align',
  65              margin: '0 .5em'
  66          }, {
  67              name: 'text-bottom',
  68              str: 'alignment_bottom',
  69              value: 'vertical-align',
  70              margin: '0 .5em',
  71              isDefault: true
  72          },
  73  
  74          // Floats.
  75          {
  76              name: 'left',
  77              str: 'alignment_left',
  78              value: 'float',
  79              margin: '0 .5em 0 0'
  80          }, {
  81              name: 'right',
  82              str: 'alignment_right',
  83              value: 'float',
  84              margin: '0 0 0 .5em'
  85          }, {
  86              name: 'customstyle',
  87              str: 'customstyle',
  88              value: 'style'
  89          }
  90      ],
  91  
  92      REGEX = {
  93          ISPERCENT: /\d+%/
  94      },
  95  
  96      COMPONENTNAME = 'atto_image',
  97  
  98      TEMPLATE = '' +
  99              '<form class="atto_form">' +
 100                  '<label for="{{elementid}}_{{CSS.INPUTURL}}">{{get_string "enterurl" component}}</label>' +
 101                  '<input class="fullwidth {{CSS.INPUTURL}}" type="url" id="{{elementid}}_{{CSS.INPUTURL}}" size="32"/>' +
 102                  '<br/>' +
 103  
 104                  // Add the repository browser button.
 105                  '{{#if showFilepicker}}' +
 106                      '<button class="{{CSS.IMAGEBROWSER}}" type="button">{{get_string "browserepositories" component}}</button>' +
 107                  '{{/if}}' +
 108  
 109                  // Add the Alt box.
 110                  '<div style="display:none" role="alert" class="warning {{CSS.IMAGEALTWARNING}}">' +
 111                      '{{get_string "presentationoraltrequired" component}}' +
 112                  '</div>' +
 113                  '<label for="{{elementid}}_{{CSS.INPUTALT}}">{{get_string "enteralt" component}}</label>' +
 114                  '<input class="fullwidth {{CSS.INPUTALT}}" type="text" value="" id="{{elementid}}_{{CSS.INPUTALT}}" size="32"/>' +
 115                  '<br/>' +
 116  
 117                  // Add the presentation select box.
 118                  '<input type="checkbox" class="{{CSS.IMAGEPRESENTATION}}" id="{{elementid}}_{{CSS.IMAGEPRESENTATION}}"/>' +
 119                  '<label class="sameline" for="{{elementid}}_{{CSS.IMAGEPRESENTATION}}">' +
 120                      '{{get_string "presentation" component}}' +
 121                  '</label>' +
 122                  '<br/>' +
 123  
 124                  // Add the size entry boxes.
 125                  '<label class="sameline" for="{{elementid}}_{{CSS.INPUTSIZE}}">{{get_string "size" component}}</label>' +
 126                  '<div id="{{elementid}}_{{CSS.INPUTSIZE}}" class="{{CSS.INPUTSIZE}}">' +
 127                  '<label class="accesshide" for="{{elementid}}_{{CSS.INPUTWIDTH}}">{{get_string "width" component}}</label>' +
 128                  '<input type="text" class="{{CSS.INPUTWIDTH}} input-mini" id="{{elementid}}_{{CSS.INPUTWIDTH}}" size="4"/> x ' +
 129  
 130                  // Add the height entry box.
 131                  '<label class="accesshide" for="{{elementid}}_{{CSS.INPUTHEIGHT}}">{{get_string "height" component}}</label>' +
 132                  '<input type="text" class="{{CSS.INPUTHEIGHT}} input-mini" id="{{elementid}}_{{CSS.INPUTHEIGHT}}" size="4"/>' +
 133  
 134                  // Add the constrain checkbox.
 135                  '<input type="checkbox" class="{{CSS.INPUTCONSTRAIN}} sameline" id="{{elementid}}_{{CSS.INPUTCONSTRAIN}}"/>' +
 136                  '<label for="{{elementid}}_{{CSS.INPUTCONSTRAIN}}">{{get_string "constrain" component}}</label>' +
 137                  '</div>' +
 138  
 139                  // Add the alignment selector.
 140                  '<label class="sameline" for="{{elementid}}_{{CSS.INPUTALIGNMENT}}">{{get_string "alignment" component}}</label>' +
 141                  '<select class="{{CSS.INPUTALIGNMENT}}" id="{{elementid}}_{{CSS.INPUTALIGNMENT}}">' +
 142                      '{{#each alignments}}' +
 143                          '<option value="{{value}}:{{name}};">{{get_string str ../component}}</option>' +
 144                      '{{/each}}' +
 145                  '</select>' +
 146                  // Hidden input to store custom styles.
 147                  '<input type="hidden" class="{{CSS.INPUTCUSTOMSTYLE}}"/>' +
 148                  '<br/>' +
 149  
 150                  // Add the image preview.
 151                  '<div class="mdl-align">' +
 152                  '<div class="{{CSS.IMAGEPREVIEWBOX}}">' +
 153                      '<img src="#" class="{{CSS.IMAGEPREVIEW}}" alt="" style="display: none;"/>' +
 154                  '</div>' +
 155  
 156                  // Add the submit button and close the form.
 157                  '<button class="{{CSS.INPUTSUBMIT}}" type="submit">{{get_string "saveimage" component}}</button>' +
 158                  '</div>' +
 159              '</form>',
 160  
 161          IMAGETEMPLATE = '' +
 162              '<img src="{{url}}" alt="{{alt}}" ' +
 163                  '{{#if width}}width="{{width}}" {{/if}}' +
 164                  '{{#if height}}height="{{height}}" {{/if}}' +
 165                  '{{#if presentation}}role="presentation" {{/if}}' +
 166                  'style="{{alignment}}{{margin}}{{customstyle}}"' +
 167                  '{{#if classlist}}class="{{classlist}}" {{/if}}' +
 168                  '{{#if id}}id="{{id}}" {{/if}}' +
 169                  '/>';
 170  
 171  Y.namespace('M.atto_image').Button = Y.Base.create('button', Y.M.editor_atto.EditorPlugin, [], {
 172      /**
 173       * A reference to the current selection at the time that the dialogue
 174       * was opened.
 175       *
 176       * @property _currentSelection
 177       * @type Range
 178       * @private
 179       */
 180      _currentSelection: null,
 181  
 182      /**
 183       * The most recently selected image.
 184       *
 185       * @param _selectedImage
 186       * @type Node
 187       * @private
 188       */
 189      _selectedImage: null,
 190  
 191      /**
 192       * A reference to the currently open form.
 193       *
 194       * @param _form
 195       * @type Node
 196       * @private
 197       */
 198      _form: null,
 199  
 200      /**
 201       * The dimensions of the raw image before we manipulate it.
 202       *
 203       * @param _rawImageDimensions
 204       * @type Object
 205       * @private
 206       */
 207      _rawImageDimensions: null,
 208  
 209      initializer: function() {
 210  
 211          this.addButton({
 212              icon: 'e/insert_edit_image',
 213              callback: this._displayDialogue,
 214              tags: 'img',
 215              tagMatchRequiresAll: false
 216          });
 217          this.editor.delegate('dblclick', this._displayDialogue, 'img', this);
 218          this.editor.delegate('click', this._handleClick, 'img', this);
 219          this.editor.on('drop', this._handleDragDrop, this);
 220  
 221          // e.preventDefault needed to stop the default event from clobbering the desired behaviour in some browsers.
 222          this.editor.on('dragover', function(e) {
 223              e.preventDefault();
 224          }, this);
 225          this.editor.on('dragenter', function(e) {
 226              e.preventDefault();
 227          }, this);
 228      },
 229  
 230      /**
 231       * Handle a drag and drop event with an image.
 232       *
 233       * @method _handleDragDrop
 234       * @param {EventFacade} e
 235       * @private
 236       */
 237      _handleDragDrop: function(e) {
 238  
 239          var self = this,
 240              host = this.get('host'),
 241              template = Y.Handlebars.compile(IMAGETEMPLATE);
 242  
 243          host.saveSelection();
 244          e = e._event;
 245  
 246          // Only handle the event if an image file was dropped in.
 247          var handlesDataTransfer = (e.dataTransfer && e.dataTransfer.files && e.dataTransfer.files.length);
 248          if (handlesDataTransfer && /^image\//.test(e.dataTransfer.files[0].type)) {
 249  
 250              var options = host.get('filepickeroptions').image,
 251                  savepath = (options.savepath === undefined) ? '/' : options.savepath,
 252                  formData = new FormData(),
 253                  timestamp = 0,
 254                  uploadid = "",
 255                  xhr = new XMLHttpRequest(),
 256                  imagehtml = "",
 257                  keys = Object.keys(options.repositories);
 258  
 259              e.preventDefault();
 260              e.stopPropagation();
 261              formData.append('repo_upload_file', e.dataTransfer.files[0]);
 262              formData.append('itemid', options.itemid);
 263  
 264              // List of repositories is an object rather than an array.  This makes iteration more awkward.
 265              for (var i = 0; i < keys.length; i++) {
 266                  if (options.repositories[keys[i]].type === 'upload') {
 267                      formData.append('repo_id', options.repositories[keys[i]].id);
 268                      break;
 269                  }
 270              }
 271              formData.append('env', options.env);
 272              formData.append('sesskey', M.cfg.sesskey);
 273              formData.append('client_id', options.client_id);
 274              formData.append('savepath', savepath);
 275              formData.append('ctx_id', options.context.id);
 276  
 277              // Insert spinner as a placeholder.
 278              timestamp = new Date().getTime();
 279              uploadid = 'moodleimage_' + Math.round(Math.random() * 100000) + '-' + timestamp;
 280              host.focus();
 281              host.restoreSelection();
 282              imagehtml = template({
 283                  url: M.util.image_url("i/loading_small", 'moodle'),
 284                  alt: M.util.get_string('uploading', COMPONENTNAME),
 285                  id: uploadid
 286              });
 287              host.insertContentAtFocusPoint(imagehtml);
 288              self.markUpdated();
 289  
 290              // Kick off a XMLHttpRequest.
 291              xhr.onreadystatechange = function() {
 292                  var placeholder = self.editor.one('#' + uploadid),
 293                      result,
 294                      file,
 295                      newhtml,
 296                      newimage;
 297  
 298                  if (xhr.readyState === 4) {
 299                      if (xhr.status === 200) {
 300                          result = JSON.parse(xhr.responseText);
 301                          if (result) {
 302                              if (result.error) {
 303                                  if (placeholder) {
 304                                      placeholder.remove(true);
 305                                  }
 306                                  return new M.core.ajaxException(result);
 307                              }
 308  
 309                              file = result;
 310                              if (result.event && result.event === 'fileexists') {
 311                                  // A file with this name is already in use here - rename to avoid conflict.
 312                                  // Chances are, it's a different image (stored in a different folder on the user's computer).
 313                                  // If the user wants to reuse an existing image, they can copy/paste it within the editor.
 314                                  file = result.newfile;
 315                              }
 316  
 317                              // Replace placeholder with actual image.
 318                              newhtml = template({
 319                                  url: file.url,
 320                                  presentation: true
 321                              });
 322                              newimage = Y.Node.create(newhtml);
 323                              if (placeholder) {
 324                                  placeholder.replace(newimage);
 325                              } else {
 326                                  self.editor.appendChild(newimage);
 327                              }
 328                              self.markUpdated();
 329                          }
 330                      } else {
 331                          Y.use('moodle-core-notification-alert', function() {
 332                              new M.core.alert({message: M.util.get_string('servererror', 'moodle')});
 333                          });
 334                          if (placeholder) {
 335                              placeholder.remove(true);
 336                          }
 337                      }
 338                  }
 339              };
 340              xhr.open("POST", M.cfg.wwwroot + '/repository/repository_ajax.php?action=upload', true);
 341              xhr.send(formData);
 342              return false;
 343          }
 344  
 345  },
 346  
 347      /**
 348       * Handle a click on an image.
 349       *
 350       * @method _handleClick
 351       * @param {EventFacade} e
 352       * @private
 353       */
 354      _handleClick: function(e) {
 355          var image = e.target;
 356  
 357          var selection = this.get('host').getSelectionFromNode(image);
 358          if (this.get('host').getSelection() !== selection) {
 359              this.get('host').setSelection(selection);
 360          }
 361      },
 362  
 363      /**
 364       * Display the image editing tool.
 365       *
 366       * @method _displayDialogue
 367       * @private
 368       */
 369      _displayDialogue: function() {
 370          // Store the current selection.
 371          this._currentSelection = this.get('host').getSelection();
 372          if (this._currentSelection === false) {
 373              return;
 374          }
 375  
 376          // Reset the image dimensions.
 377          this._rawImageDimensions = null;
 378  
 379          var dialogue = this.getDialogue({
 380              headerContent: M.util.get_string('imageproperties', COMPONENTNAME),
 381              width: '480px',
 382              focusAfterHide: true,
 383              focusOnShowSelector: SELECTORS.INPUTURL
 384          });
 385  
 386          // Set the dialogue content, and then show the dialogue.
 387          dialogue.set('bodyContent', this._getDialogueContent())
 388                  .show();
 389      },
 390  
 391      /**
 392       * Set the inputs for width and height if they are not set, and calculate
 393       * if the constrain checkbox should be checked or not.
 394       *
 395       * @method _loadPreviewImage
 396       * @param {String} url
 397       * @private
 398       */
 399      _loadPreviewImage: function(url) {
 400          var image = new Image();
 401          var self = this;
 402  
 403          image.onerror = function() {
 404              var preview = self._form.one('.' + CSS.IMAGEPREVIEW);
 405              preview.setStyles({
 406                  'display': 'none'
 407              });
 408  
 409              // Centre the dialogue when clearing the image preview.
 410              self.getDialogue().centerDialogue();
 411          };
 412  
 413          image.onload = function() {
 414              var input, currentwidth, currentheight, widthRatio, heightRatio;
 415  
 416              self._rawImageDimensions = {
 417                  width: this.width,
 418                  height: this.height
 419              };
 420  
 421              input = self._form.one('.' + CSS.INPUTWIDTH);
 422              currentwidth = input.get('value');
 423              if (currentwidth === '') {
 424                  input.set('value', this.width);
 425                  currentwidth = "" + this.width;
 426              }
 427              input = self._form.one('.' + CSS.INPUTHEIGHT);
 428              currentheight = input.get('value');
 429              if (currentheight === '') {
 430                  input.set('value', this.height);
 431                  currentheight = "" + this.height;
 432              }
 433              input = self._form.one('.' + CSS.IMAGEPREVIEW);
 434              input.setAttribute('src', this.src);
 435              input.setStyles({
 436                  'display': 'inline'
 437              });
 438  
 439              input = self._form.one('.' + CSS.INPUTCONSTRAIN);
 440              if (currentwidth.match(REGEX.ISPERCENT) && currentheight.match(REGEX.ISPERCENT)) {
 441                  input.set('checked', currentwidth === currentheight);
 442              } else {
 443                  if (this.width === 0) {
 444                      this.width = 1;
 445                  }
 446                  if (this.height === 0) {
 447                      this.height = 1;
 448                  }
 449                  // This is the same as comparing to 3 decimal places.
 450                  widthRatio = Math.round(1000 * parseInt(currentwidth, 10) / this.width);
 451                  heightRatio = Math.round(1000 * parseInt(currentheight, 10) / this.height);
 452                  input.set('checked', widthRatio === heightRatio);
 453              }
 454  
 455              // Apply the image sizing.
 456              self._autoAdjustSize(self);
 457  
 458              // Centre the dialogue once the preview image has loaded.
 459              self.getDialogue().centerDialogue();
 460          };
 461  
 462          image.src = url;
 463      },
 464  
 465      /**
 466       * Return the dialogue content for the tool, attaching any required
 467       * events.
 468       *
 469       * @method _getDialogueContent
 470       * @return {Node} The content to place in the dialogue.
 471       * @private
 472       */
 473      _getDialogueContent: function() {
 474          var template = Y.Handlebars.compile(TEMPLATE),
 475              canShowFilepicker = this.get('host').canShowFilepicker('image'),
 476              content = Y.Node.create(template({
 477                  elementid: this.get('host').get('elementid'),
 478                  CSS: CSS,
 479                  component: COMPONENTNAME,
 480                  showFilepicker: canShowFilepicker,
 481                  alignments: ALIGNMENTS
 482              }));
 483  
 484          this._form = content;
 485  
 486          // Configure the view of the current image.
 487          this._applyImageProperties(this._form);
 488  
 489          this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
 490          this._form.one('.' + CSS.IMAGEPRESENTATION).on('change', this._updateWarning, this);
 491          this._form.one('.' + CSS.INPUTALT).on('change', this._updateWarning, this);
 492          this._form.one('.' + CSS.INPUTWIDTH).on('blur', this._autoAdjustSize, this);
 493          this._form.one('.' + CSS.INPUTHEIGHT).on('blur', this._autoAdjustSize, this, true);
 494          this._form.one('.' + CSS.INPUTCONSTRAIN).on('change', function(event) {
 495              if (event.target.get('checked')) {
 496                  this._autoAdjustSize(event);
 497              }
 498          }, this);
 499          this._form.one('.' + CSS.INPUTURL).on('blur', this._urlChanged, this);
 500          this._form.one('.' + CSS.INPUTSUBMIT).on('click', this._setImage, this);
 501  
 502          if (canShowFilepicker) {
 503              this._form.one('.' + CSS.IMAGEBROWSER).on('click', function() {
 504                      this.get('host').showFilepicker('image', this._filepickerCallback, this);
 505              }, this);
 506          }
 507  
 508          return content;
 509      },
 510  
 511      _autoAdjustSize: function(e, forceHeight) {
 512          forceHeight = forceHeight || false;
 513  
 514          var keyField = this._form.one('.' + CSS.INPUTWIDTH),
 515              keyFieldType = 'width',
 516              subField = this._form.one('.' + CSS.INPUTHEIGHT),
 517              subFieldType = 'height',
 518              constrainField = this._form.one('.' + CSS.INPUTCONSTRAIN),
 519              keyFieldValue = keyField.get('value'),
 520              subFieldValue = subField.get('value'),
 521              imagePreview = this._form.one('.' + CSS.IMAGEPREVIEW),
 522              rawPercentage,
 523              rawSize;
 524  
 525          // If we do not know the image size, do not do anything.
 526          if (!this._rawImageDimensions) {
 527              return;
 528          }
 529  
 530          // Set the width back to default if it is empty.
 531          if (keyFieldValue === '') {
 532              keyFieldValue = this._rawImageDimensions[keyFieldType];
 533              keyField.set('value', keyFieldValue);
 534              keyFieldValue = keyField.get('value');
 535          }
 536  
 537          // Clear the existing preview sizes.
 538          imagePreview.setStyles({
 539              width: null,
 540              height: null
 541          });
 542  
 543          // Now update with the new values.
 544          if (!constrainField.get('checked')) {
 545              // We are not keeping the image proportion - update the preview accordingly.
 546  
 547              // Width.
 548              if (keyFieldValue.match(REGEX.ISPERCENT)) {
 549                  rawPercentage = parseInt(keyFieldValue, 10);
 550                  rawSize = this._rawImageDimensions.width / 100 * rawPercentage;
 551                  imagePreview.setStyle('width', rawSize + 'px');
 552              } else {
 553                  imagePreview.setStyle('width', keyFieldValue + 'px');
 554              }
 555  
 556              // Height.
 557              if (subFieldValue.match(REGEX.ISPERCENT)) {
 558                  rawPercentage = parseInt(subFieldValue, 10);
 559                  rawSize = this._rawImageDimensions.height / 100 * rawPercentage;
 560                  imagePreview.setStyle('height', rawSize + 'px');
 561              } else {
 562                  imagePreview.setStyle('height', subFieldValue + 'px');
 563              }
 564          } else {
 565              // We are keeping the image in proportion.
 566              if (forceHeight) {
 567                  // By default we update based on width. Swap the key and sub fields around to achieve a height-based scale.
 568                  var _temporaryValue;
 569                  _temporaryValue = keyField;
 570                  keyField = subField;
 571                  subField = _temporaryValue;
 572  
 573                  _temporaryValue = keyFieldType;
 574                  keyFieldType = subFieldType;
 575                  subFieldType = _temporaryValue;
 576  
 577                  _temporaryValue = keyFieldValue;
 578                  keyFieldValue = subFieldValue;
 579                  subFieldValue = _temporaryValue;
 580              }
 581  
 582              if (keyFieldValue.match(REGEX.ISPERCENT)) {
 583                  // This is a percentage based change. Copy it verbatim.
 584                  subFieldValue = keyFieldValue;
 585  
 586                  // Set the width to the calculated pixel width.
 587                  rawPercentage = parseInt(keyFieldValue, 10);
 588                  rawSize = this._rawImageDimensions.width / 100 * rawPercentage;
 589  
 590                  // And apply the width/height to the container.
 591                  imagePreview.setStyle('width', rawSize);
 592                  rawSize = this._rawImageDimensions.height / 100 * rawPercentage;
 593                  imagePreview.setStyle('height', rawSize);
 594              } else {
 595                  // Calculate the scaled subFieldValue from the keyFieldValue.
 596                  subFieldValue = Math.round((keyFieldValue / this._rawImageDimensions[keyFieldType]) *
 597                          this._rawImageDimensions[subFieldType]);
 598  
 599                  if (forceHeight) {
 600                      imagePreview.setStyles({
 601                          'width': subFieldValue,
 602                          'height': keyFieldValue
 603                      });
 604                  } else {
 605                      imagePreview.setStyles({
 606                          'width': keyFieldValue,
 607                          'height': subFieldValue
 608                      });
 609                  }
 610              }
 611  
 612              // Update the subField's value within the form to reflect the changes.
 613              subField.set('value', subFieldValue);
 614          }
 615      },
 616  
 617      /**
 618       * Update the dialogue after an image was selected in the File Picker.
 619       *
 620       * @method _filepickerCallback
 621       * @param {object} params The parameters provided by the filepicker
 622       * containing information about the image.
 623       * @private
 624       */
 625      _filepickerCallback: function(params) {
 626          if (params.url !== '') {
 627              var input = this._form.one('.' + CSS.INPUTURL);
 628              input.set('value', params.url);
 629  
 630              // Auto set the width and height.
 631              this._form.one('.' + CSS.INPUTWIDTH).set('value', '');
 632              this._form.one('.' + CSS.INPUTHEIGHT).set('value', '');
 633  
 634              // Load the preview image.
 635              this._loadPreviewImage(params.url);
 636          }
 637      },
 638  
 639      /**
 640       * Applies properties of an existing image to the image dialogue for editing.
 641       *
 642       * @method _applyImageProperties
 643       * @param {Node} form
 644       * @private
 645       */
 646      _applyImageProperties: function(form) {
 647          var properties = this._getSelectedImageProperties(),
 648              img = form.one('.' + CSS.IMAGEPREVIEW),
 649              i,
 650              css;
 651  
 652          if (properties === false) {
 653              img.setStyle('display', 'none');
 654              // Set the default alignment.
 655              for (i in ALIGNMENTS) {
 656                  if (ALIGNMENTS[i].isDefault === true) {
 657                      css = ALIGNMENTS[i].value + ':' + ALIGNMENTS[i].name + ';';
 658                      form.one('.' + CSS.INPUTALIGNMENT).set('value', css);
 659                  }
 660              }
 661              // Remove the custom style option if this is a new image.
 662              form.one('.' + CSS.INPUTALIGNMENT).getDOMNode().options.remove(ALIGNMENTS.length - 1);
 663              return;
 664          }
 665  
 666          if (properties.align) {
 667              form.one('.' + CSS.INPUTALIGNMENT).set('value', properties.align);
 668              // Remove the custom style option if we have a standard alignment.
 669              form.one('.' + CSS.INPUTALIGNMENT).getDOMNode().options.remove(ALIGNMENTS.length - 1);
 670          } else {
 671              form.one('.' + CSS.INPUTALIGNMENT).set('value', 'style:customstyle;');
 672          }
 673          if (properties.customstyle) {
 674              form.one('.' + CSS.INPUTCUSTOMSTYLE).set('value', properties.customstyle);
 675          }
 676          if (properties.width) {
 677              form.one('.' + CSS.INPUTWIDTH).set('value', properties.width);
 678          }
 679          if (properties.height) {
 680              form.one('.' + CSS.INPUTHEIGHT).set('value', properties.height);
 681          }
 682          if (properties.alt) {
 683              form.one('.' + CSS.INPUTALT).set('value', properties.alt);
 684          }
 685          if (properties.src) {
 686              form.one('.' + CSS.INPUTURL).set('value', properties.src);
 687              this._loadPreviewImage(properties.src);
 688          }
 689          if (properties.presentation) {
 690              form.one('.' + CSS.IMAGEPRESENTATION).set('checked', 'checked');
 691          }
 692  
 693          // Update the image preview based on the form properties.
 694          this._autoAdjustSize();
 695      },
 696  
 697      /**
 698       * Gets the properties of the currently selected image.
 699       *
 700       * The first image only if multiple images are selected.
 701       *
 702       * @method _getSelectedImageProperties
 703       * @return {object}
 704       * @private
 705       */
 706      _getSelectedImageProperties: function() {
 707          var properties = {
 708                  src: null,
 709                  alt: null,
 710                  width: null,
 711                  height: null,
 712                  align: '',
 713                  presentation: false
 714              },
 715  
 716              // Get the current selection.
 717              images = this.get('host').getSelectedNodes(),
 718              i,
 719              width,
 720              height,
 721              style,
 722              css,
 723              image,
 724              margin;
 725  
 726          if (images) {
 727              images = images.filter('img');
 728          }
 729  
 730          if (images && images.size()) {
 731              image = images.item(0);
 732              this._selectedImage = image;
 733  
 734              style = image.getAttribute('style');
 735              properties.customstyle = style;
 736              style = style.replace(/ /g, '');
 737  
 738              width = image.getAttribute('width');
 739              if (!width.match(REGEX.ISPERCENT)) {
 740                  width = parseInt(width, 10);
 741              }
 742              height = image.getAttribute('height');
 743              if (!height.match(REGEX.ISPERCENT)) {
 744                  height = parseInt(height, 10);
 745              }
 746  
 747              if (width !== 0) {
 748                  properties.width = width;
 749              }
 750              if (height !== 0) {
 751                  properties.height = height;
 752              }
 753              for (i in ALIGNMENTS) {
 754                  css = ALIGNMENTS[i].value + ':' + ALIGNMENTS[i].name + ';';
 755                  if (style.indexOf(css) !== -1) {
 756                      margin = 'margin:' + ALIGNMENTS[i].margin + ';';
 757                      margin = margin.replace(/ /g, '');
 758                      // Must match alignment and margins - otherwise custom style is selected.
 759                      if (style.indexOf(margin) !== -1) {
 760                          properties.align = css;
 761                          break;
 762                      }
 763                  }
 764              }
 765              properties.src = image.getAttribute('src');
 766              properties.alt = image.getAttribute('alt') || '';
 767              properties.presentation = (image.get('role') === 'presentation');
 768              return properties;
 769          }
 770  
 771          // No image selected - clean up.
 772          this._selectedImage = null;
 773          return false;
 774      },
 775  
 776      /**
 777       * Update the form when the URL was changed. This includes updating the
 778       * height, width, and image preview.
 779       *
 780       * @method _urlChanged
 781       * @private
 782       */
 783      _urlChanged: function() {
 784          var input = this._form.one('.' + CSS.INPUTURL);
 785  
 786          if (input.get('value') !== '') {
 787              // Load the preview image.
 788              this._loadPreviewImage(input.get('value'));
 789          }
 790      },
 791  
 792      /**
 793       * Update the image in the contenteditable.
 794       *
 795       * @method _setImage
 796       * @param {EventFacade} e
 797       * @private
 798       */
 799      _setImage: function(e) {
 800          var form = this._form,
 801              url = form.one('.' + CSS.INPUTURL).get('value'),
 802              alt = form.one('.' + CSS.INPUTALT).get('value'),
 803              width = form.one('.' + CSS.INPUTWIDTH).get('value'),
 804              height = form.one('.' + CSS.INPUTHEIGHT).get('value'),
 805              alignment = form.one('.' + CSS.INPUTALIGNMENT).get('value'),
 806              margin = '',
 807              presentation = form.one('.' + CSS.IMAGEPRESENTATION).get('checked'),
 808              constrain = form.one('.' + CSS.INPUTCONSTRAIN).get('checked'),
 809              imagehtml,
 810              customstyle = '',
 811              i,
 812              css,
 813              classlist = [],
 814              host = this.get('host');
 815  
 816          e.preventDefault();
 817  
 818          // Check if there are any accessibility issues.
 819          if (this._updateWarning()) {
 820              return;
 821          }
 822  
 823          // Focus on the editor in preparation for inserting the image.
 824          host.focus();
 825          if (url !== '') {
 826              if (this._selectedImage) {
 827                  host.setSelection(host.getSelectionFromNode(this._selectedImage));
 828              } else {
 829                  host.setSelection(this._currentSelection);
 830              }
 831  
 832              if (alignment === 'style:customstyle;') {
 833                  alignment = '';
 834                  customstyle = form.one('.' + CSS.INPUTCUSTOMSTYLE).get('value');
 835              } else {
 836                  for (i in ALIGNMENTS) {
 837                      css = ALIGNMENTS[i].value + ':' + ALIGNMENTS[i].name + ';';
 838                      if (alignment === css) {
 839                          margin = ' margin: ' + ALIGNMENTS[i].margin + ';';
 840                      }
 841                  }
 842              }
 843  
 844              if (constrain) {
 845                  classlist.push(CSS.RESPONSIVE);
 846              }
 847  
 848              if (!width.match(REGEX.ISPERCENT) && isNaN(parseInt(width, 10))) {
 849                  form.one('.' + CSS.INPUTWIDTH).focus();
 850                  return;
 851              }
 852              if (!height.match(REGEX.ISPERCENT) && isNaN(parseInt(height, 10))) {
 853                  form.one('.' + CSS.INPUTHEIGHT).focus();
 854                  return;
 855              }
 856  
 857              var template = Y.Handlebars.compile(IMAGETEMPLATE);
 858              imagehtml = template({
 859                  url: url,
 860                  alt: alt,
 861                  width: width,
 862                  height: height,
 863                  presentation: presentation,
 864                  alignment: alignment,
 865                  margin: margin,
 866                  customstyle: customstyle,
 867                  classlist: classlist.join(' ')
 868              });
 869  
 870              this.get('host').insertContentAtFocusPoint(imagehtml);
 871  
 872              this.markUpdated();
 873          }
 874  
 875          this.getDialogue({
 876              focusAfterHide: null
 877          }).hide();
 878  
 879      },
 880  
 881      /**
 882       * Update the alt text warning live.
 883       *
 884       * @method _updateWarning
 885       * @return {boolean} whether a warning should be displayed.
 886       * @private
 887       */
 888      _updateWarning: function() {
 889          var form = this._form,
 890              state = true,
 891              alt = form.one('.' + CSS.INPUTALT).get('value'),
 892              presentation = form.one('.' + CSS.IMAGEPRESENTATION).get('checked');
 893          if (alt === '' && !presentation) {
 894              form.one('.' + CSS.IMAGEALTWARNING).setStyle('display', 'block');
 895              form.one('.' + CSS.INPUTALT).setAttribute('aria-invalid', true);
 896              form.one('.' + CSS.IMAGEPRESENTATION).setAttribute('aria-invalid', true);
 897              state = true;
 898          } else {
 899              form.one('.' + CSS.IMAGEALTWARNING).setStyle('display', 'none');
 900              form.one('.' + CSS.INPUTALT).setAttribute('aria-invalid', false);
 901              form.one('.' + CSS.IMAGEPRESENTATION).setAttribute('aria-invalid', false);
 902              state = false;
 903          }
 904          this.getDialogue().centerDialogue();
 905          return state;
 906      }
 907  });


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