[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/editor/atto/plugins/image/yui/build/moodle-atto_image-button/ -> moodle-atto_image-button.js (source)

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


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