PageRenderTime 22ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 1ms

/files/jquery.jeditable/1.7.2/jquery.jeditable.js

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