[ Index ]

PHP Cross Reference of Unnamed Project

title

Body

[close]

/lib/amd/src/ -> inplace_editable.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   * AJAX helper for the inline editing a value.
  18   *
  19   * This script is automatically included from template core/inplace_editable
  20   * It registers a click-listener on [data-inplaceeditablelink] link (the "inplace edit" icon),
  21   * then replaces the displayed value with an input field. On "Enter" it sends a request
  22   * to web service core_update_inplace_editable, which invokes the specified callback.
  23   * Any exception thrown by the web service (or callback) is displayed as an error popup.
  24   *
  25   * @module     core/inplace_editable
  26   * @package    core
  27   * @copyright  2016 Marina Glancy
  28   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  29   * @since      3.1
  30   */
  31  define(['jquery', 'core/ajax', 'core/templates', 'core/notification', 'core/str', 'core/config', 'core/url'],
  32          function($, ajax, templates, notification, str, cfg, url) {
  33  
  34      $('body').on('click keypress', '[data-inplaceeditable] [data-inplaceeditablelink]', function(e) {
  35          if (e.type === 'keypress' && e.keyCode !== 13) {
  36              return;
  37          }
  38          e.stopImmediatePropagation();
  39          e.preventDefault();
  40          var target = $(this),
  41              mainelement = target.closest('[data-inplaceeditable]');
  42  
  43          var addSpinner = function(element) {
  44              element.addClass('updating');
  45              var spinner = element.find('img.spinner');
  46              if (spinner.length) {
  47                  spinner.show();
  48              } else {
  49                  spinner = $('<img/>')
  50                          .attr('src', url.imageUrl('i/loading_small'))
  51                          .addClass('spinner').addClass('smallicon')
  52                      ;
  53                  element.append(spinner);
  54              }
  55          };
  56  
  57          var removeSpinner = function(element) {
  58              element.removeClass('updating');
  59              element.find('img.spinner').hide();
  60          };
  61  
  62          var updateValue = function(mainelement, value) {
  63              addSpinner(mainelement);
  64              ajax
  65                  .call([{
  66                      methodname: 'core_update_inplace_editable',
  67                      args: {
  68                          itemid: mainelement.attr('data-itemid'),
  69                          component: mainelement.attr('data-component'),
  70                          itemtype: mainelement.attr('data-itemtype'),
  71                          value: value
  72                      },
  73                      done: function(data) {
  74                          var oldvalue = mainelement.attr('data-value');
  75                          templates.render('core/inplace_editable', data).done(function(html, js) {
  76                              var newelement = $(html);
  77                              templates.replaceNode(mainelement, newelement, js);
  78                              newelement.find('[data-inplaceeditablelink]').focus();
  79                              newelement.trigger({type: 'updated', ajaxreturn: data, oldvalue: oldvalue});
  80                          });
  81                      },
  82                      fail: function(ex) {
  83                          var e = $.Event('updatefailed', {
  84                                  exception: ex,
  85                                  newvalue: value
  86                              });
  87                          removeSpinner(mainelement);
  88                          mainelement.trigger(e);
  89                          if (!e.isDefaultPrevented()) {
  90                              notification.exception(ex);
  91                          }
  92                      }
  93                  }], true);
  94          };
  95  
  96          var turnEditingOff = function(el) {
  97              el.find('input').off();
  98              el.find('select').off();
  99              el.html(el.attr('data-oldcontent'));
 100              el.removeAttr('data-oldcontent');
 101              el.removeClass('inplaceeditingon');
 102              el.find('[data-inplaceeditablelink]').focus();
 103          };
 104  
 105          var turnEditingOffEverywhere = function() {
 106              $('span.inplaceeditable.inplaceeditingon').each(function() {
 107                  turnEditingOff($(this));
 108              });
 109          };
 110  
 111          var uniqueId = function(prefix, idlength) {
 112              var uniqid = prefix,
 113                  i;
 114              for (i = 0; i < idlength; i++) {
 115                  uniqid += String(Math.floor(Math.random() * 10));
 116              }
 117              // Make sure this ID is not already taken by an existing element.
 118              if ($("#" + uniqid).length === 0) {
 119                  return uniqid;
 120              }
 121              return uniqueId(prefix, idlength);
 122          };
 123  
 124          var turnEditingOnText = function(el) {
 125              str.get_string('edittitleinstructions').done(function(s) {
 126                  var instr = $('<span class="editinstructions">' + s + '</span>').
 127                          attr('id', uniqueId('id_editinstructions_', 20)),
 128                      inputelement = $('<input type="text"/>').
 129                          attr('id', uniqueId('id_inplacevalue_', 20)).
 130                          attr('value', el.attr('data-value')).
 131                          attr('aria-describedby', instr.attr('id')),
 132                      lbl = $('<label class="accesshide">' + mainelement.attr('data-editlabel') + '</label>').
 133                          attr('for', inputelement.attr('id'));
 134                  el.html('').append(instr).append(lbl).append(inputelement);
 135  
 136                  inputelement.focus();
 137                  inputelement.select();
 138                  inputelement.on('keyup keypress focusout', function(e) {
 139                      if (cfg.behatsiterunning && e.type === 'focusout') {
 140                          // Behat triggers focusout too often.
 141                          return;
 142                      }
 143                      if (e.type === 'keypress' && e.keyCode === 13) {
 144                          // We need 'keypress' event for Enter because keyup/keydown would catch Enter that was
 145                          // pressed in other fields.
 146                          var val = inputelement.val();
 147                          turnEditingOff(el);
 148                          updateValue(el, val);
 149                      }
 150                      if ((e.type === 'keyup' && e.keyCode === 27) || e.type === 'focusout') {
 151                          // We need 'keyup' event for Escape because keypress does not work with Escape.
 152                          turnEditingOff(el);
 153                      }
 154                  });
 155              });
 156          };
 157  
 158          var turnEditingOnToggle = function(el, newvalue) {
 159              turnEditingOff(el);
 160              updateValue(el, newvalue);
 161          };
 162  
 163          var turnEditingOnSelect = function(el, options) {
 164              var i,
 165                  inputelement = $('<select></select>')
 166                      .attr('id', uniqueId('id_inplacevalue_', 20)),
 167                  lbl = $('<label class="accesshide">' + mainelement.attr('data-editlabel') + '</label>')
 168                      .attr('for', inputelement.attr('id'));
 169              for (i in options) {
 170                  inputelement
 171                      .append($('<option>')
 172                      .attr('value', options[i].key)
 173                      .html(options[i].value));
 174              }
 175              inputelement.val(el.attr('data-value'));
 176              el.html('')
 177                  .append(lbl)
 178                  .append(inputelement);
 179  
 180              inputelement.focus();
 181              inputelement.select();
 182              inputelement.on('keyup change focusout', function(e) {
 183                  if (cfg.behatsiterunning && e.type === 'focusout') {
 184                      // Behat triggers focusout too often.
 185                      return;
 186                  }
 187                  if (e.type === 'change') {
 188                      var val = inputelement.val();
 189                      turnEditingOff(el);
 190                      updateValue(el, val);
 191                  }
 192                  if ((e.type === 'keyup' && e.keyCode === 27) || e.type === 'focusout') {
 193                      // We need 'keyup' event for Escape because keypress does not work with Escape.
 194                      turnEditingOff(el);
 195                  }
 196              });
 197          };
 198  
 199          var turnEditingOn = function(el) {
 200              el.addClass('inplaceeditingon');
 201              el.attr('data-oldcontent', el.html());
 202  
 203              var type = el.attr('data-type');
 204              var options = el.attr('data-options');
 205  
 206              if (type === 'toggle') {
 207                  turnEditingOnToggle(el, options);
 208              } else if (type === 'select') {
 209                  turnEditingOnSelect(el, $.parseJSON(options));
 210              } else {
 211                  turnEditingOnText(el);
 212              }
 213          };
 214  
 215          // Turn editing on for the current element and register handler for Enter/Esc keys.
 216          turnEditingOffEverywhere();
 217          turnEditingOn(mainelement);
 218  
 219      });
 220  
 221      return {};
 222  });


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