diff options
Diffstat (limited to 'plugins/jqueryui')
-rw-r--r-- | plugins/jqueryui/jqueryui.php | 21 | ||||
-rwxr-xr-x | plugins/jqueryui/js/jquery-ui-1.10.4.custom.min.js | 31 | ||||
-rw-r--r-- | plugins/jqueryui/js/jquery-ui-accessible-datepicker.js | 31 | ||||
-rw-r--r-- | plugins/jqueryui/js/jquery.miniColors.min.js | 4 | ||||
-rw-r--r-- | plugins/jqueryui/js/jquery.tagedit.js | 683 | ||||
-rwxr-xr-x | plugins/jqueryui/themes/larry/jquery-ui-1.10.4.custom.css | 20 | ||||
-rw-r--r-- | plugins/jqueryui/themes/larry/jquery-ui-css.diff | 4 | ||||
-rw-r--r-- | plugins/jqueryui/themes/larry/tagedit.css | 122 |
8 files changed, 893 insertions, 23 deletions
diff --git a/plugins/jqueryui/jqueryui.php b/plugins/jqueryui/jqueryui.php index 2dfa8add1..1511ac5cf 100644 --- a/plugins/jqueryui/jqueryui.php +++ b/plugins/jqueryui/jqueryui.php @@ -108,6 +108,27 @@ class jqueryui extends rcube_plugin $rcube->output->set_env('mscolors', self::get_color_values()); } + public static function tagedit() + { + if (in_array('tagedit', self::$features)) { + return; + } + + self::$features[] = 'tagedit'; + + $script = 'plugins/jqueryui/js/jquery.tagedit.js'; + $rcube = rcube::get_instance(); + $ui_theme = self::$ui_theme; + $css = "plugins/jqueryui/themes/$ui_theme/tagedit.css"; + + if (!file_exists(INSTALL_PATH . $css)) { + $css = "plugins/jqueryui/themes/larry/tagedit.css"; + } + + $rcube->output->include_css($css); + $rcube->output->add_header(html::tag('script', array('type' => "text/javascript", 'src' => $script))); + } + /** * Return a (limited) list of color values to be used for calendar and category coloring * diff --git a/plugins/jqueryui/js/jquery-ui-1.10.4.custom.min.js b/plugins/jqueryui/js/jquery-ui-1.10.4.custom.min.js index ea4edecb3..73248ecac 100755 --- a/plugins/jqueryui/js/jquery-ui-1.10.4.custom.min.js +++ b/plugins/jqueryui/js/jquery-ui-1.10.4.custom.min.js @@ -109,17 +109,23 @@ $.extend($.datepicker, { var that = this; // register additional keyboard events to control date selection with cursor keys - $(target).unbind('keyup.datepicker-extended').bind('keyup.datepicker-extended', function(event) { + $(target).unbind('keydown.datepicker-extended').bind('keydown.datepicker-extended', function(event) { var inc = 1; switch (event.keyCode) { case 109: + case 173: case 189: // "minus" inc = -1; + case 61: case 107: case 187: // "plus" + // do nothing if the input does not contain full date string + if (this.value.length < that._formatDate(inst, inst.selectedDay, inst.selectedMonth, inst.selectedYear).length) { + return true; + } that._adjustInstDate(inst, inc, 'D'); - that._selectDate(target, that._formatDate(inst, inst.selectedDay, inst.selectedMonth, inst.selectedYear)); - break; + that._selectDateRC(target, that._formatDate(inst, inst.selectedDay, inst.selectedMonth, inst.selectedYear)); + return false; case $.ui.keyCode.UP: case $.ui.keyCode.DOWN: @@ -222,8 +228,23 @@ $.extend($.datepicker, { inst.dpDiv.find('.ui-datepicker-calendar').focus(); inst._hasfocus = false; } - } + }, + _selectDateRC: function(id, dateStr) { + var target = $(id), inst = this._getInst(target[0]); + + dateStr = (dateStr != null ? dateStr : this._formatDate(inst)); + if (inst.input) { + inst.input.val(dateStr); + } + this._updateAlternate(inst); + if (inst.input) { + inst.input.trigger("change"); // fire the change event + } + if (inst.inline) { + this._updateDatepicker(inst); + } + } }); -}(jQuery));
\ No newline at end of file +}(jQuery)); diff --git a/plugins/jqueryui/js/jquery-ui-accessible-datepicker.js b/plugins/jqueryui/js/jquery-ui-accessible-datepicker.js index 1c79e13bd..ef7561c7b 100644 --- a/plugins/jqueryui/js/jquery-ui-accessible-datepicker.js +++ b/plugins/jqueryui/js/jquery-ui-accessible-datepicker.js @@ -94,17 +94,23 @@ $.extend($.datepicker, { var that = this; // register additional keyboard events to control date selection with cursor keys - $(target).unbind('keyup.datepicker-extended').bind('keyup.datepicker-extended', function(event) { + $(target).unbind('keydown.datepicker-extended').bind('keydown.datepicker-extended', function(event) { var inc = 1; switch (event.keyCode) { case 109: + case 173: case 189: // "minus" inc = -1; + case 61: case 107: case 187: // "plus" + // do nothing if the input does not contain full date string + if (this.value.length < that._formatDate(inst, inst.selectedDay, inst.selectedMonth, inst.selectedYear).length) { + return true; + } that._adjustInstDate(inst, inc, 'D'); - that._selectDate(target, that._formatDate(inst, inst.selectedDay, inst.selectedMonth, inst.selectedYear)); - break; + that._selectDateRC(target, that._formatDate(inst, inst.selectedDay, inst.selectedMonth, inst.selectedYear)); + return false; case $.ui.keyCode.UP: case $.ui.keyCode.DOWN: @@ -207,8 +213,23 @@ $.extend($.datepicker, { inst.dpDiv.find('.ui-datepicker-calendar').focus(); inst._hasfocus = false; } - } + }, + _selectDateRC: function(id, dateStr) { + var target = $(id), inst = this._getInst(target[0]); + + dateStr = (dateStr != null ? dateStr : this._formatDate(inst)); + if (inst.input) { + inst.input.val(dateStr); + } + this._updateAlternate(inst); + if (inst.input) { + inst.input.trigger("change"); // fire the change event + } + if (inst.inline) { + this._updateDatepicker(inst); + } + } }); -}(jQuery));
\ No newline at end of file +}(jQuery)); diff --git a/plugins/jqueryui/js/jquery.miniColors.min.js b/plugins/jqueryui/js/jquery.miniColors.min.js index 4b42ab7ed..487e6ade1 100644 --- a/plugins/jqueryui/js/jquery.miniColors.min.js +++ b/plugins/jqueryui/js/jquery.miniColors.min.js @@ -35,8 +35,8 @@ jQuery&&function(d){d.extend(d.fn,{miniColors:function(j,k){var x=function(a,b){var e=l(a.val());e||(e="FFFFFF");var c=p(e),e=d('<a class="miniColors-trigger" style="background-color: #'+e+'" href="#"></a>');e.insertAfter(a);a.addClass("miniColors").attr("maxlength",7).attr("autocomplete","off");a.data("trigger",e);a.data("hsb",c);b.change&&a.data("change",b.change);b.readonly&&a.attr("readonly",true);b.disabled&&q(a);b.colorValues&&a.data("colorValues",b.colorValues);e.bind("click.miniColors",function(b){b.preventDefault(); a.trigger("focus")});a.bind("focus.miniColors",function(){w(a)});a.bind("blur.miniColors",function(){var b=l(a.val());a.val(b?"#"+b:"")});a.bind("keydown.miniColors",function(b){b.keyCode===9&&i(a)});a.bind("keyup.miniColors",function(){var b=a.val().replace(/[^A-F0-9#]/ig,"");a.val(b);r(a)||a.data("trigger").css("backgroundColor","#FFF")});a.bind("paste.miniColors",function(){setTimeout(function(){a.trigger("keyup")},5)})},q=function(a){i(a);a.attr("disabled",true);a.data("trigger").css("opacity", 0.5)},w=function(a){if(a.attr("disabled"))return false;i();var b=d('<div class="miniColors-selector"></div>');b.append('<div class="miniColors-colors" style="background-color: #FFF;"><div class="miniColors-colorPicker"></div></div>');b.append('<div class="miniColors-hues"><div class="miniColors-huePicker"></div></div>');b.css({top:a.is(":visible")?a.offset().top+a.outerHeight():a.data("trigger").offset().top+a.data("trigger").outerHeight(),left:a.is(":visible")?a.offset().left:a.data("trigger").offset().left, -display:"none"}).addClass(a.attr("class"));var e=a.data("colorValues");if(e&&e.length){var c,f='<div class="miniColors-presets">',g;for(g in e)c=l(e[g]),f+='<div class="miniColors-colorPreset" style="background-color:#'+c+'" rel="'+c+'"></div>';f+="</div>";b.append(f);c=Math.ceil(e.length/7)*24;b.css("width",b.width()+c+5+"px");b.find(".miniColors-presets").css("width",c+"px")}c=a.data("hsb");b.find(".miniColors-colors").css("backgroundColor","#"+n(m({h:c.h,s:100,b:100})));(f=a.data("colorPosition"))|| -(f=s(c));b.find(".miniColors-colorPicker").css("top",f.y+"px").css("left",f.x+"px");(f=a.data("huePosition"))||(f=t(c));b.find(".miniColors-huePicker").css("top",f.y+"px");a.data("selector",b);a.data("huePicker",b.find(".miniColors-huePicker"));a.data("colorPicker",b.find(".miniColors-colorPicker"));a.data("mousebutton",0);d("BODY").append(b);b.fadeIn(100);b.bind("selectstart",function(){return false});d(document).bind("mousedown.miniColors",function(b){a.data("mousebutton",1);d(b.target).parents().andSelf().hasClass("miniColors-colors")&& +display:"none"}).addClass(a.attr("class")).appendTo(d("BODY"));;var e=a.data("colorValues");if(e&&e.length){var c,f='<div class="miniColors-presets">',g;for(g in e)c=l(e[g]),f+='<div class="miniColors-colorPreset" style="background-color:#'+c+'" rel="'+c+'"></div>';f+="</div>";b.append(f);c=Math.ceil(e.length/7)*24;b.css("width",b.width()+c+5+"px");b.find(".miniColors-presets").css("width",c+"px")}c=a.data("hsb");b.find(".miniColors-colors").css("backgroundColor","#"+n(m({h:c.h,s:100,b:100})));(f=a.data("colorPosition"))|| +(f=s(c));b.find(".miniColors-colorPicker").css("top",f.y+"px").css("left",f.x+"px");(f=a.data("huePosition"))||(f=t(c));b.find(".miniColors-huePicker").css("top",f.y+"px");a.data("selector",b);a.data("huePicker",b.find(".miniColors-huePicker"));a.data("colorPicker",b.find(".miniColors-colorPicker"));a.data("mousebutton",0);b.fadeIn(100);b.bind("selectstart",function(){return false});d(document).bind("mousedown.miniColors",function(b){a.data("mousebutton",1);d(b.target).parents().andSelf().hasClass("miniColors-colors")&& (b.preventDefault(),a.data("moving","colors"),u(a,b));d(b.target).parents().andSelf().hasClass("miniColors-hues")&&(b.preventDefault(),a.data("moving","hues"),v(a,b));d(b.target).parents().andSelf().hasClass("miniColors-selector")?b.preventDefault():d(b.target).parents().andSelf().hasClass("miniColors")||i(a)});d(document).bind("mouseup.miniColors",function(){a.data("mousebutton",0);a.removeData("moving")});d(document).bind("mousemove.miniColors",function(b){a.data("mousebutton")===1&&(a.data("moving")=== "colors"&&u(a,b),a.data("moving")==="hues"&&v(a,b))});e&&(b.find(".miniColors-colorPreset").click(function(){a.val(d(this).attr("rel"));r(a)}),b.find('.miniColors-presets div[rel="'+a.val().replace(/#/,"")+'"]').addClass("miniColors-colorPreset-active"))},i=function(a){a||(a=".miniColors");d(a).each(function(){var a=d(this).data("selector");d(this).removeData("selector");d(a).fadeOut(100,function(){d(this).remove()})});d(document).unbind("mousedown.miniColors");d(document).unbind("mousemove.miniColors")}, u=function(a,b){var e=a.data("colorPicker");e.hide();var c={x:b.clientX-a.data("selector").find(".miniColors-colors").offset().left+d(document).scrollLeft()-5,y:b.clientY-a.data("selector").find(".miniColors-colors").offset().top+d(document).scrollTop()-5};if(c.x<=-5)c.x=-5;if(c.x>=144)c.x=144;if(c.y<=-5)c.y=-5;if(c.y>=144)c.y=144;a.data("colorPosition",c);e.css("left",c.x).css("top",c.y).show();e=Math.round((c.x+5)*0.67);e<0&&(e=0);e>100&&(e=100);c=100-Math.round((c.y+5)*0.67);c<0&&(c=0);c>100&& diff --git a/plugins/jqueryui/js/jquery.tagedit.js b/plugins/jqueryui/js/jquery.tagedit.js new file mode 100644 index 000000000..baab701cf --- /dev/null +++ b/plugins/jqueryui/js/jquery.tagedit.js @@ -0,0 +1,683 @@ +/* +* Tagedit - jQuery Plugin +* The Plugin can be used to edit tags from a database the easy way +* +* Examples and documentation at: tagedit.webwork-albrecht.de +* +* License: +* This work is licensed under a MIT License +* +* @licstart The following is the entire license notice for the +* JavaScript code in this file. +* +* Copyright (c) 2010 Oliver Albrecht <info@webwork-albrecht.de> +* Copyright (c) 2014 Thomas BrĂ¼derli <thomas@roundcube.net> +* +* Licensed under the MIT licenses +* +* Permission is hereby granted, free of charge, to any person obtaining +* a copy of this software and associated documentation files (the +* "Software"), to deal in the Software without restriction, including +* without limitation the rights to use, copy, modify, merge, publish, +* distribute, sublicense, and/or sell copies of the Software, and to +* permit persons to whom the Software is furnished to do so, subject to +* the following conditions: +* +* The above copyright notice and this permission notice shall be +* included in all copies or substantial portions of the Software. +* +* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +* LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +* OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +* WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +* +* @licend The above is the entire license notice +* for the JavaScript code in this file. +* +* @author Oliver Albrecht Mial: info@webwork-albrecht.de Twitter: @webworka +* @version 1.5.2 (06/2014) +* Requires: jQuery v1.4+, jQueryUI v1.8+, jQuerry.autoGrowInput +* +* Example of usage: +* +* $( "input.tag" ).tagedit(); +* +* Possible options: +* +* autocompleteURL: '', // url for a autocompletion +* deleteEmptyItems: true, // Deletes items with empty value +* deletedPostfix: '-d', // will be put to the Items that are marked as delete +* addedPostfix: '-a', // will be put to the Items that are choosem from the database +* additionalListClass: '', // put a classname here if the wrapper ul shoud receive a special class +* allowEdit: true, // Switch on/off edit entries +* allowDelete: true, // Switch on/off deletion of entries. Will be ignored if allowEdit = false +* allowAdd: true, // switch on/off the creation of new entries +* direction: 'ltr' // Sets the writing direction for Outputs and Inputs +* animSpeed: 500 // Sets the animation speed for effects +* autocompleteOptions: {}, // Setting Options for the jquery UI Autocomplete (http://jqueryui.com/demos/autocomplete/) +* breakKeyCodes: [ 13, 44 ], // Sets the characters to break on to parse the tags (defaults: return, comma) +* checkNewEntriesCaseSensitive: false, // If there is a new Entry, it is checked against the autocompletion list. This Flag controlls if the check is (in-)casesensitive +* texts: { // some texts +* removeLinkTitle: 'Remove from list.', +* saveEditLinkTitle: 'Save changes.', +* deleteLinkTitle: 'Delete this tag from database.', +* deleteConfirmation: 'Are you sure to delete this entry?', +* deletedElementTitle: 'This Element will be deleted.', +* breakEditLinkTitle: 'Cancel' +* } +*/ + +(function($) { + + $.fn.tagedit = function(options) { + /** + * Merge Options with defaults + */ + options = $.extend(true, { + // default options here + autocompleteURL: null, + checkToDeleteURL: null, + deletedPostfix: '-d', + addedPostfix: '-a', + additionalListClass: '', + allowEdit: true, + allowDelete: true, + allowAdd: true, + direction: 'ltr', + animSpeed: 500, + autocompleteOptions: { + select: function( event, ui ) { + $(this).val(ui.item.value).trigger('transformToTag', [ui.item.id]); + return false; + } + }, + breakKeyCodes: [ 13, 44 ], + checkNewEntriesCaseSensitive: false, + texts: { + removeLinkTitle: 'Remove from list.', + saveEditLinkTitle: 'Save changes.', + deleteLinkTitle: 'Delete this tag from database.', + deleteConfirmation: 'Are you sure to delete this entry?', + deletedElementTitle: 'This Element will be deleted.', + breakEditLinkTitle: 'Cancel', + forceDeleteConfirmation: 'There are more records using this tag, are you sure do you want to remove it?' + }, + tabindex: false + }, options || {}); + + // no action if there are no elements + if(this.length == 0) { + return; + } + + // set the autocompleteOptions source + if(options.autocompleteURL) { + options.autocompleteOptions.source = options.autocompleteURL; + } + + // Set the direction of the inputs + var direction= this.attr('dir'); + if(direction && direction.length > 0) { + options.direction = this.attr('dir'); + } + + var elements = this; + var focusItem = null; + + var baseNameRegexp = new RegExp("^(.*)\\[([0-9]*?("+options.deletedPostfix+"|"+options.addedPostfix+")?)?\]$", "i"); + + var baseName = elements.eq(0).attr('name').match(baseNameRegexp); + if(baseName && baseName.length == 4) { + baseName = baseName[1]; + } + else { + // Elementname does not match the expected format, exit + alert('elementname dows not match the expected format (regexp: '+baseNameRegexp+')') + return; + } + + // read tabindex from source element + var ti; + if (!options.tabindex && (ti = elements.eq(0).attr('tabindex'))) + options.tabindex = ti; + + // init elements + inputsToList(); + + /** + * Creates the tageditinput from a list of textinputs + * + */ + function inputsToList() { + var html = '<ul class="tagedit-list '+options.additionalListClass+'">'; + + elements.each(function(i) { + var element_name = $(this).attr('name').match(baseNameRegexp); + if(element_name && element_name.length == 4 && (options.deleteEmptyItems == false || $(this).val().length > 0)) { + if(element_name[1].length > 0) { + var elementId = typeof element_name[2] != 'undefined'? element_name[2]: '', + domId = 'tagedit-' + baseName + '-' + (elementId || i); + + html += '<li class="tagedit-listelement tagedit-listelement-old" aria-labelledby="'+domId+'">'; + html += '<span dir="'+options.direction+'" id="'+domId+'">' + $(this).val() + '</span>'; + html += '<input type="hidden" name="'+baseName+'['+elementId+']" value="'+$(this).val()+'" />'; + if (options.allowDelete) + html += '<a class="tagedit-close" title="'+options.texts.removeLinkTitle+'" aria-label="'+options.texts.removeLinkTitle+' '+$(this).val()+'">x</a>'; + html += '</li>'; + } + } + }); + + // replace Elements with the list and save the list in the local variable elements + elements.last().after(html) + var newList = elements.last().next(); + elements.remove(); + elements = newList; + + // Check if some of the elementshav to be marked as deleted + if(options.deletedPostfix.length > 0) { + elements.find('input[name$="'+options.deletedPostfix+'\]"]').each(function() { + markAsDeleted($(this).parent()); + }); + } + + // put an input field at the End + // Put an empty element at the end + html = '<li class="tagedit-listelement tagedit-listelement-new">'; + if (options.allowAdd) + html += '<input type="text" name="'+baseName+'[]" value="" id="tagedit-input" disabled="disabled" class="tagedit-input-disabled" dir="'+options.direction+'"/>'; + html += '</li>'; + html += '</ul>'; + + elements + .append(html) + .attr('tabindex', options.tabindex) // set tabindex to <ul> to recieve focus + + // Set function on the input + .find('#tagedit-input') + .attr('tabindex', options.tabindex) + .each(function() { + $(this).autoGrowInput({comfortZone: 15, minWidth: 15, maxWidth: 20000}); + + // Event is triggert in case of choosing an item from the autocomplete, or finish the input + $(this).bind('transformToTag', function(event, id) { + var oldValue = (typeof id != 'undefined' && (id.length > 0 || id > 0)); + + var checkAutocomplete = oldValue == true || options.autocompleteOptions.noCheck ? false : true; + // check if the Value ist new + var isNewResult = isNew($(this).val(), checkAutocomplete); + if(isNewResult[0] === true || (isNewResult[0] === false && typeof isNewResult[1] == 'string')) { + + if(oldValue == false && typeof isNewResult[1] == 'string') { + oldValue = true; + id = isNewResult[1]; + } + + if(options.allowAdd == true || oldValue) { + var domId = 'tagedit-' + baseName + '-' + id; + // Make a new tag in front the input + html = '<li class="tagedit-listelement tagedit-listelement-old" aria-labelledby="'+domId+'">'; + html += '<span dir="'+options.direction+'" id="'+domId+'">' + $(this).val() + '</span>'; + var name = oldValue? baseName + '['+id+options.addedPostfix+']' : baseName + '[]'; + html += '<input type="hidden" name="'+name+'" value="'+$(this).val()+'" />'; + html += '<a class="tagedit-close" title="'+options.texts.removeLinkTitle+'" aria-label="'+options.texts.removeLinkTitle+' '+$(this).val()+'">x</a>'; + html += '</li>'; + + $(this).parent().before(html); + } + } + $(this).val(''); + + // close autocomplete + if(options.autocompleteOptions.source) { + if($(this).is(':ui-autocomplete')) + $(this).autocomplete( "close" ); + } + + }) + .keydown(function(event) { + var code = event.keyCode > 0? event.keyCode : event.which; + + switch(code) { + case 46: + if (!focusItem) + break; + case 8: // BACKSPACE + if(focusItem) { + focusItem.fadeOut(options.animSpeed, function() { + $(this).remove(); + }) + unfocusItem(); + event.preventDefault(); + return false; + } + else if($(this).val().length == 0) { + // delete Last Tag + var elementToRemove = elements.find('li.tagedit-listelement-old').last(); + elementToRemove.fadeOut(options.animSpeed, function() {elementToRemove.remove();}) + event.preventDefault(); + return false; + } + break; + case 9: // TAB + if($(this).val().length > 0 && $('ul.ui-autocomplete #ui-active-menuitem').length == 0) { + $(this).trigger('transformToTag'); + event.preventDefault(); + return false; + } + break; + case 37: // LEFT + case 39: // RIGHT + if($(this).val().length == 0) { + // select previous Tag + var inc = code == 37 ? -1 : 1, + items = elements.find('li.tagedit-listelement-old') + x = items.length, next = 0; + items.each(function(i, elem) { + if ($(elem).hasClass('tagedit-listelement-focus')) { + x = i; + return true; + } + }); + unfocusItem(); + next = Math.max(0, x + inc); + if (items.get(next)) { + focusItem = items.eq(next).addClass('tagedit-listelement-focus'); + $(this).attr('aria-activedescendant', focusItem.attr('aria-labelledby')) + + if(options.autocompleteOptions.source != false) { + $(this).autocomplete('close').autocomplete('disable'); + } + } + event.preventDefault(); + return false; + } + break; + default: + // ignore input if an item is focused + if (focusItem !== null) { + event.preventDefault(); + event.bubble = false; + return false; + } + } + return true; + }) + .keypress(function(event) { + var code = event.keyCode > 0? event.keyCode : event.which; + if($.inArray(code, options.breakKeyCodes) > -1) { + if($(this).val().length > 0 && $('ul.ui-autocomplete #ui-active-menuitem').length == 0) { + $(this).trigger('transformToTag'); + } + event.preventDefault(); + return false; + } + else if($(this).val().length > 0){ + unfocusItem(); + } + return true; + }) + .bind('paste', function(e){ + var that = $(this); + if (e.type == 'paste'){ + setTimeout(function(){ + that.trigger('transformToTag'); + }, 1); + } + }) + .blur(function() { + if($(this).val().length == 0) { + // disable the field to prevent sending with the form + $(this).attr('disabled', 'disabled').addClass('tagedit-input-disabled'); + } + else { + // Delete entry after a timeout + var input = $(this); + $(this).data('blurtimer', window.setTimeout(function() {input.val('');}, 500)); + } + unfocusItem(); + // restore tabindex when widget looses focus + if (options.tabindex) + elements.attr('tabindex', options.tabindex); + }) + .focus(function() { + window.clearTimeout($(this).data('blurtimer')); + // remove tabindex on <ul> because #tagedit-input now has it + elements.attr('tabindex', '-1'); + }); + + if(options.autocompleteOptions.source != false) { + $(this).autocomplete(options.autocompleteOptions); + } + }) + .end() + .click(function(event) { + switch(event.target.tagName) { + case 'A': + $(event.target).parent().fadeOut(options.animSpeed, function() { + $(event.target).parent().remove(); + elements.find('#tagedit-input').focus(); + }); + break; + case 'INPUT': + case 'SPAN': + case 'LI': + if($(event.target).hasClass('tagedit-listelement-deleted') == false && + $(event.target).parent('li').hasClass('tagedit-listelement-deleted') == false) { + // Don't edit an deleted Items + return doEdit(event); + } + default: + $(this).find('#tagedit-input') + .removeAttr('disabled') + .removeClass('tagedit-input-disabled') + .focus(); + } + return false; + }) + // forward focus event (on tabbing through the form) + .focus(function(e){ $(this).click(); }) + } + + /** + * Remove class and reference to currently focused tag item + */ + function unfocusItem() { + if(focusItem){ + if(options.autocompleteOptions.source != false) { + elements.find('#tagedit-input').autocomplete('enable'); + } + focusItem.removeClass('tagedit-listelement-focus'); + focusItem = null; + elements.find('#tagedit-input').removeAttr('aria-activedescendant'); + } + } + + /** + * Sets all Actions and events for editing an Existing Tag. + * + * @param event {object} The original Event that was given + * return {boolean} + */ + function doEdit(event) { + if(options.allowEdit == false) { + // Do nothing + return; + } + + var element = event.target.tagName == 'SPAN'? $(event.target).parent() : $(event.target); + + var closeTimer = null; + + // Event that is fired if the User finishes the edit of a tag + element.bind('finishEdit', function(event, doReset) { + window.clearTimeout(closeTimer); + + var textfield = $(this).find(':text'); + var isNewResult = isNew(textfield.val(), true); + if(textfield.val().length > 0 && (typeof doReset == 'undefined' || doReset === false) && (isNewResult[0] == true)) { + // This is a new Value and we do not want to do a reset. Set the new value + $(this).find(':hidden').val(textfield.val()); + $(this).find('span').html(textfield.val()); + } + + textfield.remove(); + $(this).find('a.tagedit-save, a.tagedit-break, a.tagedit-delete').remove(); // Workaround. This normaly has to be done by autogrow Plugin + $(this).removeClass('tagedit-listelement-edit').unbind('finishEdit'); + return false; + }); + + var hidden = element.find(':hidden'); + html = '<input type="text" name="tmpinput" autocomplete="off" value="'+hidden.val()+'" class="tagedit-edit-input" dir="'+options.direction+'"/>'; + html += '<a class="tagedit-save" title="'+options.texts.saveEditLinkTitle+'">o</a>'; + html += '<a class="tagedit-break" title="'+options.texts.breakEditLinkTitle+'">x</a>'; + + // If the Element is one from the Database, it can be deleted + if(options.allowDelete == true && element.find(':hidden').length > 0 && + typeof element.find(':hidden').attr('name').match(baseNameRegexp)[3] != 'undefined') { + html += '<a class="tagedit-delete" title="'+options.texts.deleteLinkTitle+'">d</a>'; + } + + hidden.after(html); + element + .addClass('tagedit-listelement-edit') + .find('a.tagedit-save') + .click(function() { + $(this).parent().trigger('finishEdit'); + return false; + }) + .end() + .find('a.tagedit-break') + .click(function() { + $(this).parent().trigger('finishEdit', [true]); + return false; + }) + .end() + .find('a.tagedit-delete') + .click(function() { + window.clearTimeout(closeTimer); + if(confirm(options.texts.deleteConfirmation)) { + var canDelete = checkToDelete($(this).parent()); + if (!canDelete && confirm(options.texts.forceDeleteConfirmation)) { + markAsDeleted($(this).parent()); + } + + if(canDelete) { + markAsDeleted($(this).parent()); + } + + $(this).parent().find(':text').trigger('finishEdit', [true]); + } + else { + $(this).parent().find(':text').trigger('finishEdit', [true]); + } + return false; + }) + .end() + .find(':text') + .focus() + .autoGrowInput({comfortZone: 10, minWidth: 15, maxWidth: 20000}) + .keypress(function(event) { + switch(event.keyCode) { + case 13: // RETURN + event.preventDefault(); + $(this).parent().trigger('finishEdit'); + return false; + case 27: // ESC + event.preventDefault(); + $(this).parent().trigger('finishEdit', [true]); + return false; + } + return true; + }) + .blur(function() { + var that = $(this); + closeTimer = window.setTimeout(function() {that.parent().trigger('finishEdit', [true])}, 500); + }); + } + + /** + * Verifies if the tag select to be deleted is used by other records using an Ajax request. + * + * @param element + * @returns {boolean} + */ + function checkToDelete(element) { + // if no URL is provide will not verify + if(options.checkToDeleteURL === null) { + return false; + } + + var inputName = element.find('input:hidden').attr('name'); + var idPattern = new RegExp('\\d'); + var tagId = inputName.match(idPattern); + var checkResult = false; + + $.ajax({ + async : false, + url : options.checkToDeleteURL, + dataType: 'json', + type : 'POST', + data : { 'tagId' : tagId}, + complete: function (XMLHttpRequest, textStatus) { + + // Expected JSON Object: { "success": Boolean, "allowDelete": Boolean} + var result = $.parseJSON(XMLHttpRequest.responseText); + if(result.success === true){ + checkResult = result.allowDelete; + } + } + }); + + return checkResult; + } + + /** + * Marks a single Tag as deleted. + * + * @param element {object} + */ + function markAsDeleted(element) { + element + .trigger('finishEdit', [true]) + .addClass('tagedit-listelement-deleted') + .attr('title', options.deletedElementTitle); + element.find(':hidden').each(function() { + var nameEndRegexp = new RegExp('('+options.addedPostfix+'|'+options.deletedPostfix+')?\]'); + var name = $(this).attr('name').replace(nameEndRegexp, options.deletedPostfix+']'); + $(this).attr('name', name); + }); + + } + + /** + * Checks if a tag is already choosen. + * + * @param value {string} + * @param checkAutocomplete {boolean} optional Check also the autocomplet values + * @returns {Array} First item is a boolean, telling if the item should be put to the list, second is optional the ID from autocomplete list + */ + function isNew(value, checkAutocomplete) { + checkAutocomplete = typeof checkAutocomplete == 'undefined'? false : checkAutocomplete; + var autoCompleteId = null; + + var compareValue = options.checkNewEntriesCaseSensitive == true? value : value.toLowerCase(); + + var isNew = true; + elements.find('li.tagedit-listelement-old input:hidden').each(function() { + var elementValue = options.checkNewEntriesCaseSensitive == true? $(this).val() : $(this).val().toLowerCase(); + if(elementValue == compareValue) { + isNew = false; + } + }); + + if (isNew == true && checkAutocomplete == true && options.autocompleteOptions.source != false) { + var result = []; + if ($.isArray(options.autocompleteOptions.source)) { + result = options.autocompleteOptions.source; + } + else if ($.isFunction(options.autocompleteOptions.source)) { + options.autocompleteOptions.source({term: value}, function (data) {result = data}); + } + else if (typeof options.autocompleteOptions.source === "string") { + // Check also autocomplete values + var autocompleteURL = options.autocompleteOptions.source; + if (autocompleteURL.match(/\?/)) { + autocompleteURL += '&'; + } else { + autocompleteURL += '?'; + } + autocompleteURL += 'term=' + value; + $.ajax({ + async: false, + url: autocompleteURL, + dataType: 'json', + complete: function (XMLHttpRequest, textStatus) { + result = $.parseJSON(XMLHttpRequest.responseText); + } + }); + } + + // If there is an entry for that already in the autocomplete, don't use it (Check could be case sensitive or not) + for (var i = 0; i < result.length; i++) { + var resultValue = result[i].label? result[i].label : result[i]; + var label = options.checkNewEntriesCaseSensitive == true? resultValue : resultValue.toLowerCase(); + if (label == compareValue) { + isNew = false; + autoCompleteId = typeof result[i] == 'string' ? i : result[i].id; + break; + } + } + } + + return new Array(isNew, autoCompleteId); + } + } +})(jQuery); + +(function($){ + +// jQuery autoGrowInput plugin by James Padolsey +// See related thread: http://stackoverflow.com/questions/931207/is-there-a-jquery-autogrow-plugin-for-text-fields + +$.fn.autoGrowInput = function(o) { + + o = $.extend({ + maxWidth: 1000, + minWidth: 0, + comfortZone: 70 + }, o); + + this.filter('input:text').each(function(){ + + var minWidth = o.minWidth || $(this).width(), + val = '', + input = $(this), + testSubject = $('<tester/>').css({ + position: 'absolute', + top: -9999, + left: -9999, + width: 'auto', + fontSize: input.css('fontSize'), + fontFamily: input.css('fontFamily'), + fontWeight: input.css('fontWeight'), + letterSpacing: input.css('letterSpacing'), + whiteSpace: 'nowrap' + }), + check = function() { + + if (val === (val = input.val())) {return;} + + // Enter new content into testSubject + var escaped = val.replace(/&/g, '&').replace(/\s/g,' ').replace(/</g, '<').replace(/>/g, '>'); + testSubject.html(escaped); + + // Calculate new width + whether to change + var testerWidth = testSubject.width(), + newWidth = (testerWidth + o.comfortZone) >= minWidth ? testerWidth + o.comfortZone : minWidth, + currentWidth = input.width(), + isValidWidthChange = (newWidth < currentWidth && newWidth >= minWidth) + || (newWidth > minWidth && newWidth < o.maxWidth); + + // Animate width + if (isValidWidthChange) { + input.width(newWidth); + } + + }; + + testSubject.insertAfter(input); + + $(this).bind('keyup keydown blur update', check); + + check(); + }); + + return this; + +}; + +})(jQuery);
\ No newline at end of file diff --git a/plugins/jqueryui/themes/larry/jquery-ui-1.10.4.custom.css b/plugins/jqueryui/themes/larry/jquery-ui-1.10.4.custom.css index 737f4771a..106e24305 100755 --- a/plugins/jqueryui/themes/larry/jquery-ui-1.10.4.custom.css +++ b/plugins/jqueryui/themes/larry/jquery-ui-1.10.4.custom.css @@ -1442,21 +1442,23 @@ body .ui-tooltip { display: none; } .ie10 .ui-datepicker .ui-datepicker-title select, -.webkit .ui-datepicker .ui-datepicker-title select, -.mozilla .ui-datepicker .ui-datepicker-title select { +.webkit .ui-datepicker .ui-datepicker-title select { background-image: url("images/ui-icons-datepicker.png"); background-position: right -18px; background-repeat: no-repeat; - padding-right: 14px; + padding-right: 16px; -webkit-appearance: none; - -moz-appearance: none; appearance: none; } -.mozilla .ui-datepicker .ui-datepicker-title select { - background-position: right -17px; - text-indent: 0.01px; - text-overflow: ''; - padding-right: 0; + +@supports (-moz-appearance:none) and (mask-type:alpha) { + .mozilla .ui-datepicker .ui-datepicker-title select { + background-image: url("images/ui-icons-datepicker.png"); + background-position: right -14px; + background-repeat: no-repeat; + padding-right: 16px; + -moz-appearance: none; + } } .ui-datepicker .ui-datepicker-month:focus, .ui-datepicker .ui-datepicker-year:focus { diff --git a/plugins/jqueryui/themes/larry/jquery-ui-css.diff b/plugins/jqueryui/themes/larry/jquery-ui-css.diff index cce990679..e3971ecdb 100644 --- a/plugins/jqueryui/themes/larry/jquery-ui-css.diff +++ b/plugins/jqueryui/themes/larry/jquery-ui-css.diff @@ -538,10 +538,10 @@ + appearance: none; +} +.mozilla .ui-datepicker .ui-datepicker-title select { -+ background-position: right -17px; ++ background-position: right -14px; + text-indent: 0.01px; + text-overflow: ''; -+ padding-right: 0; ++ padding-right: 10px; +} +.ui-datepicker .ui-datepicker-month:focus, +.ui-datepicker .ui-datepicker-year:focus { diff --git a/plugins/jqueryui/themes/larry/tagedit.css b/plugins/jqueryui/themes/larry/tagedit.css new file mode 100644 index 000000000..600481c61 --- /dev/null +++ b/plugins/jqueryui/themes/larry/tagedit.css @@ -0,0 +1,122 @@ +/** + * Styles of the tagedit inputsforms + */ +.tagedit-list { + width: 100%; + margin: 0; + padding: 4px 4px 0 5px; + overflow: auto; + min-height: 26px; + background: #fff; + border: 1px solid #b2b2b2; + border-radius: 4px; + box-shadow: inset 0 0 2px 1px rgba(0,0,0, 0.1); + -moz-box-shadow: inset 0 0 2px 1px rgba(0,0,0, 0.1); + -webkit-box-shadow: inset 0 0 2px 1px rgba(0,0,0, 0.1); + -o-box-shadow: inset 0 0 2px 1px rgba(0,0,0, 0.1); +} +.tagedit-list li.tagedit-listelement { + list-style-type: none; + float: left; + margin: 0 4px 4px 0; + padding: 0; +} + +/* New Item input */ +.tagedit-list li.tagedit-listelement-new input { + border: 0; + height: 100%; + padding: 4px 1px; + width: 15px; + background: #fff; + border-radius: 0; + box-shadow: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + -o-box-shadow: none; +} +.tagedit-list li.tagedit-listelement-new input:focus { + box-shadow: none; + -moz-box-shadow: none; + -webkit-box-shadow: none; + -o-box-shadow: none; + outline: none; +} +.tagedit-list li.tagedit-listelement-new input.tagedit-input-disabled { + display: none; +} + +/* Item that is put to the List */ +.tagedit span.tag-element, +.tagedit-list li.tagedit-listelement-old { + padding: 3px 6px 1px 6px; + background: #ddeef5; + background: -moz-linear-gradient(top, #edf6fa 0%, #d6e9f3 100%); + background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#edf6fa), color-stop(100%,#d6e9f3)); + background: -o-linear-gradient(top, #edf6fa 0%, #d6e9f3 100%); + background: -ms-linear-gradient(top, #edf6fa 0%, #d6e9f3 100%); + background: linear-gradient(top, #edf6fa 0%, #d6e9f3 100%); + border: 1px solid #c2dae5; + -moz-border-radius: 4px; + -webkit-border-radius: 4px; + border-radius: 4px; + color: #0d5165; + line-height: 1.3em; +} + +.tagedit-list li.tagedit-listelement-focus { + border-color: #4787b1; + -moz-box-shadow: 0 0 3px 1px rgba(71,135,177, 0.8); + -webkit-box-shadow: 0 0 3px 1px rgba(71,135,177, 0.8); + -o-box-shadow: 0 0 3px 1px rgba(71,135,177, 0.8); + box-shadow: 0 0 3px 1px rgba(71,135,177, 0.8); +} + +.tagedit span.tag-element { + margin-right: 0.6em; + padding: 2px 6px; +/* cursor: pointer; */ +} + +.tagedit span.tag-element.inherit { + color: #666; + background: #f2f2f2; + border-color: #ddd; +} + +.tagedit-list li.tagedit-listelement-old a.tagedit-close, +.tagedit-list li.tagedit-listelement-old a.tagedit-break, +.tagedit-list li.tagedit-listelement-old a.tagedit-delete, +.tagedit-list li.tagedit-listelement-old a.tagedit-save { + text-indent: -2000px; + display: inline-block; + position: relative; + top: -1px; + width: 16px; + height: 16px; + margin: 0 -4px 0 6px; + background: url('data:image/gif;base64,iVBORw0KGgoAAAANSUhEUgAAAA0AAAAOCAYAAAD0f5bSAAAAgUlEQVQoz2NgQAKzdxwWAOIEIG5AwiC+AAM2AJQIAOL3QPwfCwaJB6BrSMChGB0nwDQYwATP3nn4f+Ge4ygKQXyQOJKYAUjTepjAm09fwBimEUTDxJA0rWdANxWmaMXB0xiGwDADurthGkEAmwbqaCLFeWQFBOlBTlbkkp2MSE2wAA8R50rWvqeRAAAAAElFTkSuQmCC') left 1px no-repeat; + cursor: pointer; +} + +.tagedit-list li.tagedit-listelement-old span { + display: inline-block; + height: 15px; +} + +/** Special hacks for IE7 **/ + +html.ie7 .tagedit span.tag-element, +html.ie7 .tagedit-list li.tagedit-listelement-old { + filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#edf6fa', endColorstr='#d6e9f3', GradientType=0); +} + +html.ie7 .tagedit-list li.tagedit-listelement span { + position: relative; + top: -3px; +} + +html.ie7 .tagedit-list li.tagedit-listelement-old a.tagedit-close { + left: 5px; +} + |