b2evolution PHP Cross Reference Blogging Systems

Source: /rsc/js/jquery/jquery.jeditable.js - 546 lines - 24516 bytes - Text - Print

Description: Version 1.7.2-dev

   1  /*
   2   * Jeditable - jQuery in place edit plugin
   3   *
   4   * Copyright (c) 2006-2009 Mika Tuupola, Dylan Verheul
   5   *
   6   * Licensed under the MIT license:
   7   *   http://www.opensource.org/licenses/mit-license.php
   8   *
   9   * Project home:
  10   *   http://www.appelsiini.net/projects/jeditable
  11   *
  12   * Based on editable by Dylan Verheul <dylan_at_dyve.net>:
  13   *    http://www.dyve.net/jquery/?editable
  14   *
  15   */
  16  
  17  /**
  18    * Version 1.7.2-dev
  19    *
  20    * ** means there is basic unit tests for this parameter. 
  21    *
  22    * @name  Jeditable
  23    * @type  jQuery
  24    * @param String  target             (POST) URL or function to send edited content to **
  25    * @param Hash    options            additional options 
  26    * @param String  options[method]    method to use to send edited content (POST or PUT) **
  27    * @param Function options[callback] Function to run after submitting edited content **
  28    * @param String  options[name]      POST parameter name of edited content
  29    * @param String  options[id]        POST parameter name of edited div id
  30    * @param Hash    options[submitdata] Extra parameters to send when submitting edited content.
  31    * @param String  options[type]      text, textarea or select (or any 3rd party input type) **
  32    * @param Integer options[rows]      number of rows if using textarea ** 
  33    * @param Integer options[cols]      number of columns if using textarea **
  34    * @param Mixed   options[height]    'auto', 'none' or height in pixels **
  35    * @param Mixed   options[width]     'auto', 'none' or width in pixels **
  36    * @param String  options[loadurl]   URL to fetch input content before editing **
  37    * @param String  options[loadtype]  Request type for load url. Should be GET or POST.
  38    * @param String  options[loadtext]  Text to display while loading external content.
  39    * @param Mixed   options[loaddata]  Extra parameters to pass when fetching content before editing.
  40    * @param Mixed   options[data]      Or content given as paramameter. String or function.**
  41    * @param String  options[indicator] indicator html to show when saving
  42    * @param String  options[tooltip]   optional tooltip text via title attribute **
  43    * @param String  options[event]     jQuery event such as 'click' of 'dblclick' **
  44    * @param String  options[submit]    submit button value, empty means no button **
  45    * @param String  options[cancel]    cancel button value, empty means no button **
  46    * @param String  options[cssclass]  CSS class to apply to input form. 'inherit' to copy from parent. **
  47    * @param String  options[style]     Style to apply to input form 'inherit' to copy from parent. **
  48    * @param String  options[select]    true or false, when true text is highlighted ??
  49    * @param String  options[placeholder] Placeholder text or html to insert when element is empty. **
  50    * @param String  options[onblur]    'cancel', 'submit', 'ignore' or function ??
  51    *             
  52    * @param Function options[onsubmit] function(settings, original) { ... } called before submit
  53    * @param Function options[onreset]  function(settings, original) { ... } called before reset
  54    * @param Function options[onerror]  function(settings, original, xhr) { ... } called on error
  55    *             
  56    * @param Hash    options[ajaxoptions]  jQuery Ajax options. See docs.jquery.com.
  57    *             
  58    */
  59  
  60  (function($) {
  61  
  62      $.fn.editable = function(target, options) {
  63              
  64          if ('disable' == target) {
  65              $(this).data('disabled.editable', true);
  66              return;
  67          }
  68          if ('enable' == target) {
  69              $(this).data('disabled.editable', false);
  70              return;
  71          }
  72          if ('destroy' == target) {
  73              $(this)
  74                  .unbind($(this).data('event.editable'))
  75                  .removeData('disabled.editable')
  76                  .removeData('event.editable');
  77              return;
  78          }
  79          
  80          var settings = $.extend({}, $.fn.editable.defaults, {target:target}, options);
  81          
  82          /* setup some functions */
  83          var plugin   = $.editable.types[settings.type].plugin || function() { };
  84          var submit   = $.editable.types[settings.type].submit || function() { };
  85          var buttons  = $.editable.types[settings.type].buttons 
  86                      || $.editable.types['defaults'].buttons;
  87          var content  = $.editable.types[settings.type].content 
  88                      || $.editable.types['defaults'].content;
  89          var element  = $.editable.types[settings.type].element 
  90                      || $.editable.types['defaults'].element;
  91          var reset    = $.editable.types[settings.type].reset 
  92                      || $.editable.types['defaults'].reset;
  93          var callback = settings.callback || function() { };
  94          var onedit   = settings.onedit   || function() { }; 
  95          var onsubmit = settings.onsubmit || function() { };
  96          var onreset  = settings.onreset  || function() { };
  97          var onerror  = settings.onerror  || reset;
  98            
  99          /* Show tooltip. */
 100          if (settings.tooltip) {
 101              $(this).attr('title', settings.tooltip);
 102          }
 103          
 104          settings.autowidth  = 'auto' == settings.width;
 105          settings.autoheight = 'auto' == settings.height;
 106          
 107          return this.each(function() {
 108                          
 109              /* Save this to self because this changes when scope changes. */
 110              var self = this;  
 111                     
 112              /* Inlined block elements lose their width and height after first edit. */
 113              /* Save them for later use as workaround. */
 114              var savedwidth  = $(self).width();
 115              var savedheight = $(self).height();
 116  
 117              /* Save so it can be later used by $.editable('destroy') */
 118              $(this).data('event.editable', settings.event);
 119              
 120              /* If element is empty add something clickable (if requested) */
 121              if (!$.trim($(this).html())) {
 122                  $(this).html(settings.placeholder);
 123              }
 124              
 125              $(this).bind(settings.event, function(e) {
 126                  
 127                  /* Abort if element is disabled. */
 128                  if (true === $(this).data('disabled.editable')) {
 129                      return;
 130                  }
 131                  
 132                  /* Prevent throwing an exeption if edit field is clicked again. */
 133                  if (self.editing) {
 134                      return;
 135                  }
 136                  
 137                  /* Abort if onedit hook returns false. */
 138                  if (false === onedit.apply(this, [settings, self])) {
 139                     return;
 140                  }
 141                  
 142                  /* Prevent default action and bubbling. */
 143                  e.preventDefault();
 144                  e.stopPropagation();
 145                  
 146                  /* Remove tooltip. */
 147                  if (settings.tooltip) {
 148                      $(self).removeAttr('title');
 149                  }
 150                  
 151                  /* Figure out how wide and tall we are, saved width and height. */
 152                  /* Workaround for http://dev.jquery.com/ticket/2190 */
 153                  if (0 == $(self).width()) {
 154                      settings.width  = savedwidth;
 155                      settings.height = savedheight;
 156                  } else {
 157                      if (settings.width != 'none') {
 158                          settings.width = 
 159                              settings.autowidth ? $(self).width()  : settings.width;
 160                      }
 161                      if (settings.height != 'none') {
 162                          settings.height = 
 163                              settings.autoheight ? $(self).height() : settings.height;
 164                      }
 165                  }
 166                  
 167                  /* Remove placeholder text, replace is here because of IE. */
 168                  if ($(this).html().toLowerCase().replace(/(;|"|\/)/g, '') == 
 169                      settings.placeholder.toLowerCase().replace(/(;|"|\/)/g, '')) {
 170                          $(this).html('');
 171                  }
 172                                  
 173                  self.editing    = true;
 174                  self.revert     = $(self).html();
 175                  $(self).html('');
 176  
 177                  /* Create the form object. */
 178                  var form = $('<form />');
 179                  
 180                  /* Apply css or style or both. */
 181                  if (settings.cssclass) {
 182                      if ('inherit' == settings.cssclass) {
 183                          form.attr('class', $(self).attr('class'));
 184                      } else {
 185                          form.attr('class', settings.cssclass);
 186                      }
 187                  }
 188  
 189                  if (settings.style) {
 190                      if ('inherit' == settings.style) {
 191                          form.attr('style', $(self).attr('style'));
 192                          /* IE needs the second line or display wont be inherited. */
 193                          form.css('display', $(self).css('display'));                
 194                      } else {
 195                          form.attr('style', settings.style);
 196                      }
 197                  }
 198  
 199                  /* Add main input element to form and store it in input. */
 200                  var input = element.apply(form, [settings, self]);
 201  
 202                  /* Set input content via POST, GET, given data or existing value. */
 203                  var input_content;
 204                  
 205                  if (settings.loadurl) {
 206                      var t = setTimeout(function() {
 207                          input.disabled = true;
 208                          content.apply(form, [settings.loadtext, settings, self]);
 209                      }, 100);
 210  
 211                      var loaddata = {};
 212                      loaddata[settings.id] = self.id;
 213                      if ($.isFunction(settings.loaddata)) {
 214                          $.extend(loaddata, settings.loaddata.apply(self, [self.revert, settings]));
 215                      } else {
 216                          $.extend(loaddata, settings.loaddata);
 217                      }
 218                      $.ajax({
 219                         type : settings.loadtype,
 220                         url  : settings.loadurl,
 221                         data : loaddata,
 222                         async : false,
 223                         success: function(result) {
 224                            window.clearTimeout(t);
 225                            input_content = result;
 226                            input.disabled = false;
 227                         }
 228                      });
 229                  } else if (settings.data) {
 230                      input_content = settings.data;
 231                      if ($.isFunction(settings.data)) {
 232                          input_content = settings.data.apply(self, [self.revert, settings]);
 233                      }
 234                  } else {
 235                      input_content = self.revert; 
 236                  }
 237                  content.apply(form, [input_content, settings, self]);
 238  
 239                  input.attr('name', settings.name);
 240          
 241                  /* Add buttons to the form. */
 242                  buttons.apply(form, [settings, self]);
 243           
 244                  /* Add created form to self. */
 245                  $(self).append(form);
 246           
 247                  /* Attach 3rd party plugin if requested. */
 248                  plugin.apply(form, [settings, self]);
 249  
 250                  /* Focus to first visible form element. */
 251                  $(':input:visible:enabled:first', form).focus();
 252  
 253                  /* Highlight input contents when requested. */
 254                  if (settings.select) {
 255                      input.select();
 256                  }
 257          
 258                  /* discard changes if pressing esc */
 259                  input.keydown(function(e) {
 260                      if (e.keyCode == 27) {
 261                          e.preventDefault();
 262                          reset.apply(form, [settings, self]);
 263                      }
 264                  });
 265  
 266                  /* Discard, submit or nothing with changes when clicking outside. */
 267                  /* Do nothing is usable when navigating with tab. */
 268                  var t;
 269                  if ('cancel' == settings.onblur) {
 270                      input.blur(function(e) {
 271                          /* Prevent canceling if submit was clicked. */
 272                          t = setTimeout(function() {
 273                              reset.apply(form, [settings, self]);
 274                          }, 500);
 275                      });
 276                  } else if ('submit' == settings.onblur) {
 277                      input.blur(function(e) {
 278                          /* Prevent double submit if submit was clicked. */
 279                          t = setTimeout(function() {
 280                              form.submit();
 281                          }, 200);
 282                      });
 283                  } else if ($.isFunction(settings.onblur)) {
 284                      input.blur(function(e) {
 285                          settings.onblur.apply(self, [input.val(), settings]);
 286                      });
 287                  } else {
 288                      input.blur(function(e) {
 289                        /* TODO: maybe something here */
 290                      });
 291                  }
 292  
 293                  form.submit(function(e) {
 294  
 295                      if (t) { 
 296                          clearTimeout(t);
 297                      }
 298  
 299                      /* Do no submit. */
 300                      e.preventDefault(); 
 301              
 302                      /* Call before submit hook. */
 303                      /* If it returns false abort submitting. */                    
 304                      if (false !== onsubmit.apply(form, [settings, self])) { 
 305                          /* Custom inputs call before submit hook. */
 306                          /* If it returns false abort submitting. */
 307                          if (false !== submit.apply(form, [settings, self])) { 
 308  
 309                            /* Check if given target is function */
 310                            if ($.isFunction(settings.target)) {
 311                                var str = settings.target.apply(self, [input.val(), settings]);
 312                                $(self).html(str);
 313                                self.editing = false;
 314                                callback.apply(self, [self.innerHTML, settings]);
 315                                /* TODO: this is not dry */                              
 316                                if (!$.trim($(self).html())) {
 317                                    $(self).html(settings.placeholder);
 318                                }
 319                            } else {
 320                                /* Add edited content and id of edited element to POST. */
 321                                var submitdata = {};
 322                                submitdata[settings.name] = input.val();
 323                                submitdata[settings.id] = self.id;
 324                                /* Add extra data to be POST:ed. */
 325                                if ($.isFunction(settings.submitdata)) {
 326                                    $.extend(submitdata, settings.submitdata.apply(self, [self.revert, settings]));
 327                                } else {
 328                                    $.extend(submitdata, settings.submitdata);
 329                                }
 330  
 331                                /* Quick and dirty PUT support. */
 332                                if ('PUT' == settings.method) {
 333                                    submitdata['_method'] = 'put';
 334                                }
 335  
 336                                /* Show the saving indicator. */
 337                                $(self).html(settings.indicator);
 338                                
 339                                /* Defaults for ajaxoptions. */
 340                                var ajaxoptions = {
 341                                    type    : 'POST',
 342                                    data    : submitdata,
 343                                    dataType: 'html',
 344                                    url     : settings.target,
 345                                    success : function(result, status) {
 346                                        if (ajaxoptions.dataType == 'html') {
 347                                          $(self).html(result);
 348                                        }
 349                                        self.editing = false;
 350                                        callback.apply(self, [result, settings]);
 351                                        if (!$.trim($(self).html())) {
 352                                            $(self).html(settings.placeholder);
 353                                        }
 354                                    },
 355                                    error   : function(xhr, status, error) {
 356                                        onerror.apply(form, [settings, self, xhr]);
 357                                    }
 358                                };
 359                                
 360                                /* Override with what is given in settings.ajaxoptions. */
 361                                $.extend(ajaxoptions, settings.ajaxoptions);   
 362                                $.ajax(ajaxoptions);          
 363                                
 364                              }
 365                          }
 366                      }
 367                      
 368                      /* Show tooltip again. */
 369                      $(self).attr('title', settings.tooltip);
 370                      
 371                      return false;
 372                  });
 373              });
 374              
 375              /* Privileged methods */
 376              this.reset = function(form) {
 377                  /* Prevent calling reset twice when blurring. */
 378                  if (this.editing) {
 379                      /* Before reset hook, if it returns false abort reseting. */
 380                      if (false !== onreset.apply(form, [settings, self])) { 
 381                          $(self).html(self.revert);
 382                          self.editing   = false;
 383                          if (!$.trim($(self).html())) {
 384                              $(self).html(settings.placeholder);
 385                          }
 386                          /* Show tooltip again. */
 387                          if (settings.tooltip) {
 388                              $(self).attr('title', settings.tooltip);                
 389                          }
 390                      }                    
 391                  }
 392              };            
 393          });
 394  
 395      };
 396  
 397  
 398      $.editable = {
 399          types: {
 400              defaults: {
 401                  element : function(settings, original) {
 402                      var input = $('<input type="hidden"></input>');                
 403                      $(this).append(input);
 404                      return(input);
 405                  },
 406                  content : function(string, settings, original) {
 407                      $(':input:first', this).val(string);
 408                  },
 409                  reset : function(settings, original) {
 410                    original.reset(this);
 411                  },
 412                  buttons : function(settings, original) {
 413                      var form = this;
 414                      if (settings.submit) {
 415                          /* If given html string use that. */
 416                          if (settings.submit.match(/>$/)) {
 417                              var submit = $(settings.submit).click(function() {
 418                                  if (submit.attr("type") != "submit") {
 419                                      form.submit();
 420                                  }
 421                              });
 422                          /* Otherwise use button with given string as text. */
 423                          } else {
 424                              var submit = $('<button type="submit" />');
 425                              submit.html(settings.submit);                            
 426                          }
 427                          $(this).append(submit);
 428                      }
 429                      if (settings.cancel) {
 430                          /* If given html string use that. */
 431                          if (settings.cancel.match(/>$/)) {
 432                              var cancel = $(settings.cancel);
 433                          /* otherwise use button with given string as text */
 434                          } else {
 435                              var cancel = $('<button type="cancel" />');
 436                              cancel.html(settings.cancel);
 437                          }
 438                          $(this).append(cancel);
 439  
 440                          $(cancel).click(function(event) {
 441                              if ($.isFunction($.editable.types[settings.type].reset)) {
 442                                  var reset = $.editable.types[settings.type].reset;                                                                
 443                              } else {
 444                                  var reset = $.editable.types['defaults'].reset;                                
 445                              }
 446                              reset.apply(form, [settings, original]);
 447                              return false;
 448                          });
 449                      }
 450                  }
 451              },
 452              text: {
 453                  element : function(settings, original) {
 454                      var input = $('<input />');
 455                      if (settings.width  != 'none') { input.attr('width', settings.width);  }
 456                      if (settings.height != 'none') { input.attr('height', settings.height); }
 457                      /* https://bugzilla.mozilla.org/show_bug.cgi?id=236791 */
 458                      //input[0].setAttribute('autocomplete','off');
 459                      input.attr('autocomplete','off');
 460                      $(this).append(input);
 461                      return(input);
 462                  }
 463              },
 464              textarea: {
 465                  element : function(settings, original) {
 466                      var textarea = $('<textarea />');
 467                      if (settings.rows) {
 468                          textarea.attr('rows', settings.rows);
 469                      } else if (settings.height != "none") {
 470                          textarea.height(settings.height);
 471                      }
 472                      if (settings.cols) {
 473                          textarea.attr('cols', settings.cols);
 474                      } else if (settings.width != "none") {
 475                          textarea.width(settings.width);
 476                      }
 477                      $(this).append(textarea);
 478                      return(textarea);
 479                  }
 480              },
 481              select: {
 482                 element : function(settings, original) {
 483                      var select = $('<select />');
 484                      $(this).append(select);
 485                      return(select);
 486                  },
 487                  content : function(data, settings, original) {
 488                      /* If it is string assume it is json. */
 489                      if (String == data.constructor) {      
 490                          eval ('var json = ' + data);
 491                      } else {
 492                      /* Otherwise assume it is a hash already. */
 493                          var json = data;
 494                      }
 495                      for (var key in json) {
 496                          if (!json.hasOwnProperty(key)) {
 497                              continue;
 498                          }
 499                          if ('selected' == key) {
 500                              continue;
 501                          } 
 502                          var option = $('<option />').val(key).append(json[key]);
 503                          $('select', this).append(option);    
 504                      }                    
 505                      /* Loop option again to set selected. IE needed this... */ 
 506                      $('select', this).children().each(function() {
 507                          if ($(this).val() == json['selected'] || 
 508                              $(this).text() == $.trim(original.revert)) {
 509                                  $(this).attr('selected', 'selected');
 510                          }
 511                      });
 512                      /* Submit on change if no submit button defined. */
 513                      if (!settings.submit) {
 514                          var form = this;
 515                          $('select', this).change(function() {
 516                              form.submit();
 517                          });
 518                      }
 519                  }
 520              }
 521          },
 522  
 523          /* Add new input type */
 524          addInputType: function(name, input) {
 525              $.editable.types[name] = input;
 526          }
 527      };
 528  
 529      /* Publicly accessible defaults. */
 530      $.fn.editable.defaults = {
 531          name       : 'value',
 532          id         : 'id',
 533          type       : 'text',
 534          width      : 'auto',
 535          height     : 'auto',
 536          event      : 'click.editable',
 537          onblur     : 'cancel',
 538          loadtype   : 'GET',
 539          loadtext   : 'Loading...',
 540          placeholder: 'Click to edit',
 541          loaddata   : {},
 542          submitdata : {},
 543          ajaxoptions: {}
 544      };
 545  
 546  })(jQuery);

title

Description

title

Description

title

Description

title

title

Body