diff --git a/components/editorControls/KeyValuePrevalueEditor.cs b/components/editorControls/KeyValuePrevalueEditor.cs index 3ff51f2a52..ff79c487fb 100644 --- a/components/editorControls/KeyValuePrevalueEditor.cs +++ b/components/editorControls/KeyValuePrevalueEditor.cs @@ -2,13 +2,14 @@ using System; using System.Collections; using System.Web.UI; using System.Web.UI.WebControls; - +using ClientDependency.Core; using umbraco.BusinessLogic; using umbraco.DataLayer; using System.Collections.Generic; using System.Web.UI.HtmlControls; + [assembly: System.Web.UI.WebResource("umbraco.editorControls.KeyValuePrevalueEditor.js", "text/js")] [assembly: System.Web.UI.WebResource("umbraco.editorControls.KeyValuePrevalueEditor.css", "text/css")] namespace umbraco.editorControls @@ -16,6 +17,8 @@ namespace umbraco.editorControls /// /// Summary description for KeyValuePrevalueEditor. /// + + [ClientDependency(ClientDependencyType.Javascript, "Jeditable/jquery.jeditable.js", "UmbracoClient")] public class KeyValuePrevalueEditor : System.Web.UI.WebControls.PlaceHolder, interfaces.IDataPrevalue { diff --git a/components/editorControls/KeyValuePrevalueEditor.js b/components/editorControls/KeyValuePrevalueEditor.js index 854a42c50f..9ed375208e 100644 --- a/components/editorControls/KeyValuePrevalueEditor.js +++ b/components/editorControls/KeyValuePrevalueEditor.js @@ -1,7 +1,7 @@  jQuery(document).ready(function () { - jQuery("#prevalues .text").valuesInlineEdit(); + jQuery("#prevalues .text").editable(function (value, settings) { $(this).html(value); ResetValues(); }, { onblur: 'submit', tooltip: 'Click to edit', cssclass: 'inlineEditor' }); jQuery("#prevalues tbody").sortable({ items: "tr:not(.header)", @@ -35,83 +35,3 @@ function ResetValues() { jQuery(".valuesHiddenInput").val(val); } -//inline edit -(function ($) { - - $.fn.valuesInlineEdit = function (options) { - - options = $.extend({ - hover: 'hover', - value: '', - save: '', - placeholder: 'Click to edit' - }, options); - - return $.each(this, function () { - $.valuesInlineEdit(this, options); - }); - } - - $.valuesInlineEdit = function (obj, options) { - - var self = $(obj), - placeholderHtml = '' + options.placeholder + ''; - - self.value = function (newValue) { - if (arguments.length) { - self.data('value', $(newValue).hasClass('inlineEdit-placeholder') ? '' : newValue); - } - return self.data('value'); - } - - self.value($.trim(self.text()) || options.value); - - self.bind('click', function (event) { - var $this = $(event.target); - - if ($this.is(self[0].tagName) || $this.hasClass('inlineEdit-placeholder')) { - self - .html('') - .find('input') - .bind('blur', function () { - if ($this.children('input').val().length > 0) { - - try { - self.value($this.children('input').val()); - - - - } catch (err) { } - - - } - else { - self.value('Click to edit'); - } - if (self.timer) { - window.clearTimeout(self.timer); - } - self.timer = window.setTimeout(function () { - self.html(self.value() || placeholderHtml); - self.removeClass(options.hover); - ResetValues(); - }, 200); - }) - .focus(); - } - }) - .hover( - function () { - $(this).addClass(options.hover); - }, - function () { - $(this).removeClass(options.hover); - } - ); - - if (!self.value()) { - self.html($(placeholderHtml)); - } - } - -})(jQuery); \ No newline at end of file diff --git a/components/editorControls/umbraco.editorControls.csproj b/components/editorControls/umbraco.editorControls.csproj index 5005307577..e52d320516 100644 --- a/components/editorControls/umbraco.editorControls.csproj +++ b/components/editorControls/umbraco.editorControls.csproj @@ -424,6 +424,7 @@ + diff --git a/umbraco/presentation/umbraco.presentation.csproj b/umbraco/presentation/umbraco.presentation.csproj index 2d702bbee9..3574dcbc12 100644 --- a/umbraco/presentation/umbraco.presentation.csproj +++ b/umbraco/presentation/umbraco.presentation.csproj @@ -1778,6 +1778,7 @@ + diff --git a/umbraco/presentation/umbraco_client/Jeditable/jquery.jeditable.js b/umbraco/presentation/umbraco_client/Jeditable/jquery.jeditable.js new file mode 100644 index 0000000000..17c3029a0a --- /dev/null +++ b/umbraco/presentation/umbraco_client/Jeditable/jquery.jeditable.js @@ -0,0 +1,543 @@ +/* +* Jeditable - jQuery in place edit plugin +* +* Copyright (c) 2006-2009 Mika Tuupola, Dylan Verheul +* +* Licensed under the MIT license: +* http://www.opensource.org/licenses/mit-license.php +* +* Project home: +* http://www.appelsiini.net/projects/jeditable +* +* Based on editable by Dylan Verheul : +* http://www.dyve.net/jquery/?editable +* +*/ + +/** +* Version 1.7.1 +* +* ** means there is basic unit tests for this parameter. +* +* @name Jeditable +* @type jQuery +* @param String target (POST) URL or function to send edited content to ** +* @param Hash options additional options +* @param String options[method] method to use to send edited content (POST or PUT) ** +* @param Function options[callback] Function to run after submitting edited content ** +* @param String options[name] POST parameter name of edited content +* @param String options[id] POST parameter name of edited div id +* @param Hash options[submitdata] Extra parameters to send when submitting edited content. +* @param String options[type] text, textarea or select (or any 3rd party input type) ** +* @param Integer options[rows] number of rows if using textarea ** +* @param Integer options[cols] number of columns if using textarea ** +* @param Mixed options[height] 'auto', 'none' or height in pixels ** +* @param Mixed options[width] 'auto', 'none' or width in pixels ** +* @param String options[loadurl] URL to fetch input content before editing ** +* @param String options[loadtype] Request type for load url. Should be GET or POST. +* @param String options[loadtext] Text to display while loading external content. +* @param Mixed options[loaddata] Extra parameters to pass when fetching content before editing. +* @param Mixed options[data] Or content given as paramameter. String or function.** +* @param String options[indicator] indicator html to show when saving +* @param String options[tooltip] optional tooltip text via title attribute ** +* @param String options[event] jQuery event such as 'click' of 'dblclick' ** +* @param String options[submit] submit button value, empty means no button ** +* @param String options[cancel] cancel button value, empty means no button ** +* @param String options[cssclass] CSS class to apply to input form. 'inherit' to copy from parent. ** +* @param String options[style] Style to apply to input form 'inherit' to copy from parent. ** +* @param String options[select] true or false, when true text is highlighted ?? +* @param String options[placeholder] Placeholder text or html to insert when element is empty. ** +* @param String options[onblur] 'cancel', 'submit', 'ignore' or function ?? +* +* @param Function options[onsubmit] function(settings, original) { ... } called before submit +* @param Function options[onreset] function(settings, original) { ... } called before reset +* @param Function options[onerror] function(settings, original, xhr) { ... } called on error +* +* @param Hash options[ajaxoptions] jQuery Ajax options. See docs.jquery.com. +* +*/ + +(function ($) { + + $.fn.editable = function (target, options) { + + if ('disable' == target) { + $(this).data('disabled.editable', true); + return; + } + if ('enable' == target) { + $(this).data('disabled.editable', false); + return; + } + if ('destroy' == target) { + $(this) + .unbind($(this).data('event.editable')) + .removeData('disabled.editable') + .removeData('event.editable'); + return; + } + + var settings = $.extend({}, $.fn.editable.defaults, { target: target }, options); + + /* setup some functions */ + var plugin = $.editable.types[settings.type].plugin || function () { }; + var submit = $.editable.types[settings.type].submit || function () { }; + var buttons = $.editable.types[settings.type].buttons + || $.editable.types['defaults'].buttons; + var content = $.editable.types[settings.type].content + || $.editable.types['defaults'].content; + var element = $.editable.types[settings.type].element + || $.editable.types['defaults'].element; + var reset = $.editable.types[settings.type].reset + || $.editable.types['defaults'].reset; + var callback = settings.callback || function () { }; + var onedit = settings.onedit || function () { }; + var onsubmit = settings.onsubmit || function () { }; + var onreset = settings.onreset || function () { }; + var onerror = settings.onerror || reset; + + /* show tooltip */ + if (settings.tooltip) { + $(this).attr('title', settings.tooltip); + } + + settings.autowidth = 'auto' == settings.width; + settings.autoheight = 'auto' == settings.height; + + return this.each(function () { + + /* save this to self because this changes when scope changes */ + var self = this; + + /* inlined block elements lose their width and height after first edit */ + /* save them for later use as workaround */ + var savedwidth = $(self).width(); + var savedheight = $(self).height(); + + /* save so it can be later used by $.editable('destroy') */ + $(this).data('event.editable', settings.event); + + /* if element is empty add something clickable (if requested) */ + if (!$.trim($(this).html())) { + $(this).html(settings.placeholder); + } + + $(this).bind(settings.event, function (e) { + + /* abort if disabled for this element */ + if (true === $(this).data('disabled.editable')) { + return; + } + + /* prevent throwing an exeption if edit field is clicked again */ + if (self.editing) { + return; + } + + /* abort if onedit hook returns false */ + if (false === onedit.apply(this, [settings, self])) { + return; + } + + /* prevent default action and bubbling */ + e.preventDefault(); + e.stopPropagation(); + + /* remove tooltip */ + if (settings.tooltip) { + $(self).removeAttr('title'); + } + + /* figure out how wide and tall we are, saved width and height */ + /* are workaround for http://dev.jquery.com/ticket/2190 */ + if (0 == $(self).width()) { + //$(self).css('visibility', 'hidden'); + settings.width = savedwidth; + settings.height = savedheight; + } else { + if (settings.width != 'none') { + settings.width = + settings.autowidth ? $(self).width() : settings.width; + } + if (settings.height != 'none') { + settings.height = + settings.autoheight ? $(self).height() : settings.height; + } + } + //$(this).css('visibility', ''); + + /* remove placeholder text, replace is here because of IE */ + if ($(this).html().toLowerCase().replace(/(;|")/g, '') == + settings.placeholder.toLowerCase().replace(/(;|")/g, '')) { + $(this).html(''); + } + + self.editing = true; + self.revert = $(self).html(); + $(self).html(''); + + /* create the form object */ + var form = $('
'); + + /* apply css or style or both */ + if (settings.cssclass) { + if ('inherit' == settings.cssclass) { + form.attr('class', $(self).attr('class')); + } else { + form.attr('class', settings.cssclass); + } + } + + if (settings.style) { + if ('inherit' == settings.style) { + form.attr('style', $(self).attr('style')); + /* IE needs the second line or display wont be inherited */ + form.css('display', $(self).css('display')); + } else { + form.attr('style', settings.style); + } + } + + /* add main input element to form and store it in input */ + var input = element.apply(form, [settings, self]); + + /* set input content via POST, GET, given data or existing value */ + var input_content; + + if (settings.loadurl) { + var t = setTimeout(function () { + input.disabled = true; + content.apply(form, [settings.loadtext, settings, self]); + }, 100); + + var loaddata = {}; + loaddata[settings.id] = self.id; + if ($.isFunction(settings.loaddata)) { + $.extend(loaddata, settings.loaddata.apply(self, [self.revert, settings])); + } else { + $.extend(loaddata, settings.loaddata); + } + $.ajax({ + type: settings.loadtype, + url: settings.loadurl, + data: loaddata, + async: false, + success: function (result) { + window.clearTimeout(t); + input_content = result; + input.disabled = false; + } + }); + } else if (settings.data) { + input_content = settings.data; + if ($.isFunction(settings.data)) { + input_content = settings.data.apply(self, [self.revert, settings]); + } + } else { + input_content = self.revert; + } + content.apply(form, [input_content, settings, self]); + + input.attr('name', settings.name); + + /* add buttons to the form */ + buttons.apply(form, [settings, self]); + + /* add created form to self */ + $(self).append(form); + + /* attach 3rd party plugin if requested */ + plugin.apply(form, [settings, self]); + + /* focus to first visible form element */ + $(':input:visible:enabled:first', form).focus(); + + /* highlight input contents when requested */ + if (settings.select) { + input.select(); + } + + /* discard changes if pressing esc */ + input.keydown(function (e) { + if (e.keyCode == 27) { + e.preventDefault(); + //self.reset(); + reset.apply(form, [settings, self]); + } + }); + + /* discard, submit or nothing with changes when clicking outside */ + /* do nothing is usable when navigating with tab */ + var t; + if ('cancel' == settings.onblur) { + input.blur(function (e) { + /* prevent canceling if submit was clicked */ + t = setTimeout(function () { + reset.apply(form, [settings, self]); + }, 500); + }); + } else if ('submit' == settings.onblur) { + input.blur(function (e) { + /* prevent double submit if submit was clicked */ + t = setTimeout(function () { + form.submit(); + }, 200); + }); + } else if ($.isFunction(settings.onblur)) { + input.blur(function (e) { + settings.onblur.apply(self, [input.val(), settings]); + }); + } else { + input.blur(function (e) { + /* TODO: maybe something here */ + }); + } + + form.submit(function (e) { + + if (t) { + clearTimeout(t); + } + + /* do no submit */ + e.preventDefault(); + + /* call before submit hook. */ + /* if it returns false abort submitting */ + if (false !== onsubmit.apply(form, [settings, self])) { + /* custom inputs call before submit hook. */ + /* if it returns false abort submitting */ + if (false !== submit.apply(form, [settings, self])) { + + /* check if given target is function */ + if ($.isFunction(settings.target)) { + var str = settings.target.apply(self, [input.val(), settings]); + $(self).html(str); + self.editing = false; + callback.apply(self, [self.innerHTML, settings]); + /* TODO: this is not dry */ + if (!$.trim($(self).html())) { + $(self).html(settings.placeholder); + } + } else { + /* add edited content and id of edited element to POST */ + var submitdata = {}; + submitdata[settings.name] = input.val(); + submitdata[settings.id] = self.id; + /* add extra data to be POST:ed */ + if ($.isFunction(settings.submitdata)) { + $.extend(submitdata, settings.submitdata.apply(self, [self.revert, settings])); + } else { + $.extend(submitdata, settings.submitdata); + } + + /* quick and dirty PUT support */ + if ('PUT' == settings.method) { + submitdata['_method'] = 'put'; + } + + /* show the saving indicator */ + $(self).html(settings.indicator); + + /* defaults for ajaxoptions */ + var ajaxoptions = { + type: 'POST', + data: submitdata, + dataType: 'html', + url: settings.target, + success: function (result, status) { + if (ajaxoptions.dataType == 'html') { + $(self).html(result); + } + self.editing = false; + callback.apply(self, [result, settings]); + if (!$.trim($(self).html())) { + $(self).html(settings.placeholder); + } + }, + error: function (xhr, status, error) { + onerror.apply(form, [settings, self, xhr]); + } + }; + + /* override with what is given in settings.ajaxoptions */ + $.extend(ajaxoptions, settings.ajaxoptions); + $.ajax(ajaxoptions); + + } + } + } + + /* show tooltip again */ + $(self).attr('title', settings.tooltip); + + return false; + }); + }); + + /* privileged methods */ + this.reset = function (form) { + /* prevent calling reset twice when blurring */ + if (this.editing) { + /* before reset hook, if it returns false abort reseting */ + if (false !== onreset.apply(form, [settings, self])) { + $(self).html(self.revert); + self.editing = false; + if (!$.trim($(self).html())) { + $(self).html(settings.placeholder); + } + /* show tooltip again */ + if (settings.tooltip) { + $(self).attr('title', settings.tooltip); + } + } + } + }; + }); + + }; + + + $.editable = { + types: { + defaults: { + element: function (settings, original) { + var input = $(''); + $(this).append(input); + return (input); + }, + content: function (string, settings, original) { + $(':input:first', this).val(string); + }, + reset: function (settings, original) { + original.reset(this); + }, + buttons: function (settings, original) { + var form = this; + if (settings.submit) { + /* if given html string use that */ + if (settings.submit.match(/>$/)) { + var submit = $(settings.submit).click(function () { + if (submit.attr("type") != "submit") { + form.submit(); + } + }); + /* otherwise use button with given string as text */ + } else { + var submit = $('