src/ztfy/myams/resources/js/ext/jquery-jeditable.js
changeset 75 e4c5705087a0
equal deleted inserted replaced
74:c2bd88e15b5c 75:e4c5705087a0
       
     1 /*
       
     2  * Jeditable - jQuery in place edit plugin
       
     3  *
       
     4  * Copyright (c) 2006-2013 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.3
       
    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);