vendor/assets/javascripts/editable/jquery-editable-poshytip.js in x-editable-rails-1.0.2 vs vendor/assets/javascripts/editable/jquery-editable-poshytip.js in x-editable-rails-1.0.3

- old
+ new

@@ -1,6 +1,6 @@ -/*! X-editable - v1.4.5 +/*! X-editable - v1.4.6 * In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery * http://github.com/vitalets/x-editable * Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */ /** @@ -105,11 +105,12 @@ this.$form.submit(function(e){ e.preventDefault(); }); } else { this.error(false); this.input.$input.removeAttr('disabled'); this.$form.find('.editable-submit').removeAttr('disabled'); - this.input.value2input(this.value); + var value = (this.value === null || this.value === undefined || this.value === '') ? this.options.defaultValue : this.value; + this.input.value2input(value); //attach submit handler this.$form.submit($.proxy(this.submit, this)); } /** @@ -480,13 +481,22 @@ @type string|object @default null **/ value: null, /** - Strategy for sending data on server. Can be <code>auto|always|never</code>. - When 'auto' data will be sent on server only if pk defined, otherwise new value will be stored in element. + Value that will be displayed in input if original field value is empty (`null|undefined|''`). + @property defaultValue + @type string|object + @default null + @since 1.4.6 + **/ + defaultValue: null, + /** + Strategy for sending data on server. Can be `auto|always|never`. + When 'auto' data will be sent on server **only if pk and url defined**, otherwise new value will be stored locally. + @property send @type string @default 'auto' **/ send: 'auto', @@ -751,11 +761,14 @@ itemsByValue: function(value, sourceData, valueProp) { if(!sourceData || value === null) { return []; } - valueProp = valueProp || 'value'; + if (typeof(valueProp) !== "function") { + var idKey = valueProp || 'value'; + valueProp = function (e) { return e[idKey]; }; + } var isValArray = $.isArray(value), result = [], that = this; @@ -763,15 +776,16 @@ if(o.children) { result = result.concat(that.itemsByValue(value, o.children, valueProp)); } else { /*jslint eqeq: true*/ if(isValArray) { - if($.grep(value, function(v){ return v == (o && typeof o === 'object' ? o[valueProp] : o); }).length) { + if($.grep(value, function(v){ return v == (o && typeof o === 'object' ? valueProp(o) : o); }).length) { result.push(o); } } else { - if(value == (o && typeof o === 'object' ? o[valueProp] : o)) { + var itemValue = (o && (typeof o === 'object')) ? valueProp(o) : o; + if(value == itemValue) { result.push(o); } } /*jslint eqeq: false*/ } @@ -878,11 +892,12 @@ this.init(element, options); }; //methods Popup.prototype = { - containerName: null, //tbd in child class + containerName: null, //method to call container on element + containerDataName: null, //object name in element's .data() innerCss: null, //tbd in child class containerClass: 'editable-container editable-popup', //css class applied to container element init: function(element, options) { this.$element = $(element); //since 1.4.1 container do not use data-* directly as they already merged into options. @@ -979,11 +994,20 @@ return this.container() ? this.container().$tip : null; }, /* returns container object */ container: function() { - return this.$element.data(this.containerDataName || this.containerName); + var container; + //first, try get it by `containerDataName` + if(this.containerDataName) { + if(container = this.$element.data(this.containerDataName)) { + return container; + } + } + //second, try `containerName` + container = this.$element.data(this.containerName); + return container; }, /* call native method of underlying container, e.g. this.$element.popover('method') */ call: function() { this.$element[this.containerName].apply(this.$element, arguments); @@ -1024,11 +1048,11 @@ }); **/ /* TODO: added second param mainly to distinguish from bootstrap's shown event. It's a hotfix that will be solved in future versions via namespaced events. */ - this.$element.triggerHandler('shown', this); + this.$element.triggerHandler('shown', $(this.options.scope).data('editable')); }, this) }) .editableform('render'); }, @@ -1478,10 +1502,15 @@ } //add 'editable' class to every editable element this.$element.addClass('editable'); + //specifically for "textarea" add class .editable-pre-wrapped to keep linebreaks + if(this.input.type === 'textarea') { + this.$element.addClass('editable-pre-wrapped'); + } + //attach handler activating editable. In disabled mode it just prevent default action (useful for links) if(this.options.toggle !== 'manual') { this.$element.addClass('editable-click'); this.$element.on(this.options.toggle + '.editable', $.proxy(function(e){ //prevent following link if editable enabled @@ -1807,20 +1836,23 @@ } //highlight when saving if(this.options.highlight) { var $e = this.$element, - $bgColor = $e.css('background-color'); + bgColor = $e.css('background-color'); $e.css('background-color', this.options.highlight); setTimeout(function(){ - $e.css('background-color', $bgColor); + if(bgColor === 'transparent') { + bgColor = ''; + } + $e.css('background-color', bgColor); $e.addClass('editable-bg-transition'); setTimeout(function(){ $e.removeClass('editable-bg-transition'); }, 1700); - }, 0); + }, 10); } //set new value this.setValue(params.newValue, false, params.response); @@ -2030,10 +2062,18 @@ return this.each(function () { var $this = $(this), data = $this.data(datakey), options = typeof option === 'object' && option; + //for delegated targets do not store `editable` object for element + //it's allows several different selectors. + //see: https://github.com/vitalets/x-editable/issues/312 + if(options && options.selector) { + data = new Editable(this, options); + return; + } + if (!data) { $this.data(datakey, (data = new Editable(this, options))); } if (typeof option === 'string') { //call method @@ -2369,11 +2409,11 @@ }, /** Additional actions when destroying element **/ - destroy: function() { + destroy: function() { }, // -------- helper functions -------- setClass: function() { if(this.options.inputclass) { @@ -2917,23 +2957,25 @@ if (e.ctrlKey && e.which === 13) { $(this).closest('form').submit(); } }); }, - - value2html: function(value, element) { + + //using `white-space: pre-wrap` solves \n <--> BR conversion very elegant! + /* + value2html: function(value, element) { var html = '', lines; if(value) { lines = value.split("\n"); for (var i = 0; i < lines.length; i++) { lines[i] = $('<div>').text(lines[i]).html(); } html = lines.join('<br>'); } $(element).html(html); }, - + html2value: function(html) { if(!html) { return ''; } @@ -2948,11 +2990,11 @@ lines[i] = text; } return lines.join("\n"); }, - + */ activate: function() { $.fn.editabletypes.text.prototype.activate.call(this); } }); @@ -3022,16 +3064,23 @@ $.extend(Select.prototype, { renderList: function() { this.$input.empty(); var fillItems = function($el, data) { + var attr; if($.isArray(data)) { for(var i=0; i<data.length; i++) { + attr = {}; if(data[i].children) { - $el.append(fillItems($('<optgroup>', {label: data[i].text}), data[i].children)); + attr.label = data[i].text; + $el.append(fillItems($('<optgroup>', attr), data[i].children)); } else { - $el.append($('<option>', {value: data[i].value}).text(data[i].text)); + attr.value = data[i].value; + if(data[i].disabled) { + attr.disabled = true; + } + $el.append($('<option>', attr).text(data[i].text)); } } } return $el; }; @@ -3239,10 +3288,11 @@ * email * url * tel * number * range +* time Learn more about html5 inputs: http://www.w3.org/wiki/HTML5_form_additions To check browser compatibility please see: https://developer.mozilla.org/en-US/docs/HTML/Element/Input @@ -3423,11 +3473,34 @@ Range.defaults = $.extend({}, $.fn.editabletypes.number.defaults, { tpl: '<input type="range"><output style="width: 30px; display: inline-block"></output>', inputclass: 'input-medium' }); $.fn.editabletypes.range = Range; -}(window.jQuery)); +}(window.jQuery)); + +/* +Time +*/ +(function ($) { + "use strict"; + + var Time = function (options) { + this.init('time', options, Time.defaults); + }; + //inherit from abstract, as inheritance from text gives selection error. + $.fn.editableutils.inherit(Time, $.fn.editabletypes.abstractinput); + $.extend(Time.prototype, { + render: function() { + this.setClass(); + } + }); + Time.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, { + tpl: '<input type="time">' + }); + $.fn.editabletypes.time = Time; +}(window.jQuery)); + /** Select2 input. Based on amazing work of Igor Vaynberg https://github.com/ivaynberg/select2. Please see [original select2 docs](http://ivaynberg.github.com/select2) for detailed description and options. Compatible **select2 version is 3.4.1**! You should manually download and include select2 distributive: @@ -3451,20 +3524,57 @@ @final @example <a href="#" id="country" data-type="select2" data-pk="1" data-value="ru" data-url="/post" data-title="Select country"></a> <script> $(function(){ + //local source $('#country').editable({ source: [ {id: 'gb', text: 'Great Britain'}, {id: 'us', text: 'United States'}, {id: 'ru', text: 'Russia'} ], select2: { multiple: true } }); + //remote source (simple) + $('#country').editable({ + source: '/getCountries' + }); + //remote source (advanced) + $('#country').editable({ + select2: { + placeholder: 'Select Country', + allowClear: true, + minimumInputLength: 3, + id: function (item) { + return item.CountryId; + }, + ajax: { + url: '/getCountries', + dataType: 'json', + data: function (term, page) { + return { query: term }; + }, + results: function (data, page) { + return { results: data }; + } + }, + formatResult: function (item) { + return item.CountryName; + }, + formatSelection: function (item) { + return item.CountryName; + }, + initSelection: function (element, callback) { + return $.get('/getCountryById', { query: element.val() }, function (data) { + callback(data); + }); + } + } + }); }); </script> **/ (function ($) { "use strict"; @@ -3509,11 +3619,25 @@ //overriding objects in config (as by default jQuery extend() is not recursive) this.options.select2 = $.extend({}, Constructor.defaults.select2, options.select2); //detect whether it is multi-valued this.isMultiple = this.options.select2.tags || this.options.select2.multiple; - this.isRemote = ('ajax' in this.options.select2); + this.isRemote = ('ajax' in this.options.select2); + + //store function returning ID of item + //should be here as used inautotext for local source + this.idFunc = this.options.select2.id; + if (typeof(this.idFunc) !== "function") { + var idKey = this.idFunc || 'id'; + this.idFunc = function (e) { return e[idKey]; }; + } + + //store function that renders text in select2 + this.formatSelection = this.options.select2.formatSelection; + if (typeof(this.formatSelection) !== "function") { + this.formatSelection = function (e) { return e.text; }; + } }; $.fn.editableutils.inherit(Constructor, $.fn.editabletypes.abstractinput); $.extend(Constructor.prototype, { @@ -3534,33 +3658,35 @@ //trigger resize of editableform to re-position container in multi-valued mode if(this.isMultiple) { this.$input.on('change', function() { $(this).closest('form').parent().triggerHandler('resize'); }); - } + } }, value2html: function(value, element) { - var text = '', data; + var text = '', data, + that = this; if(this.options.select2.tags) { //in tags mode just assign value data = value; + //data = $.fn.editableutils.itemsByValue(value, this.options.select2.tags, this.idFunc); } else if(this.sourceData) { - data = $.fn.editableutils.itemsByValue(value, this.sourceData, 'id'); + data = $.fn.editableutils.itemsByValue(value, this.sourceData, this.idFunc); } else { //can not get list of possible values (e.g. autotext for select2 with ajax source) } //data may be array (when multiple values allowed) if($.isArray(data)) { //collect selected data and show with separator text = []; $.each(data, function(k, v){ - text.push(v && typeof v === 'object' ? v.text : v); + text.push(v && typeof v === 'object' ? that.formatSelection(v) : v); }); } else if(data) { - text = data.text; + text = that.formatSelection(data); } text = $.isArray(text) ? text.join(this.options.viewseparator) : text; $(element).text(text); @@ -3569,29 +3695,32 @@ html2value: function(html) { return this.options.select2.tags ? this.str2value(html, this.options.viewseparator) : null; }, value2input: function(value) { - //for remote source .val() is not working, need to look in sourceData - if(this.isRemote) { - //todo: check value for array - var item, items; - //if sourceData loaded, use it to get text for display - if(this.sourceData) { - items = $.fn.editableutils.itemsByValue(value, this.sourceData, 'id'); - if(items.length) { - item = items[0]; - } - } - //if item not found by sourceData, use element text (e.g. for the first show) - if(!item) { - item = {id: value, text: $(this.options.scope).text()}; - } - //select2('data', ...) allows to set both id and text --> usefull for initial show when items are not loaded - this.$input.select2('data', item).trigger('change', true); //second argument needed to separate initial change from user's click (for autosubmit) - } else { - this.$input.val(value).trigger('change', true); //second argument needed to separate initial change from user's click (for autosubmit) + //for local source use data directly from source (to allow autotext) + /* + if(!this.isRemote && !this.isMultiple) { + var items = $.fn.editableutils.itemsByValue(value, this.sourceData, this.idFunc); + if(items.length) { + this.$input.select2('data', items[0]); + return; + } + } + */ + + //for remote source just set value, text is updated by initSelection + this.$input.val(value).trigger('change', true); //second argument needed to separate initial change from user's click (for autosubmit) + + //if remote source AND no user's initSelection provided --> try to use element's text + if(this.isRemote && !this.isMultiple && !this.options.select2.initSelection) { + var customId = this.options.select2.id, + customText = this.options.select2.formatSelection; + if(!customId && !customText) { + var data = {id: value, text: $(this.options.scope).text()}; + this.$input.select2('data', data); + } } }, input2value: function() { return this.$input.select2('val'); @@ -3637,10 +3766,16 @@ delete source[i].value; } } } return source; + }, + + destroy: function() { + if(this.$input.data('select2')) { + this.$input.select2('destroy'); + } } }); Constructor.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, { @@ -3692,10 +3827,10 @@ /** * Combodate - 1.0.4 * Dropdown date and time picker. * Converts text input into dropdowns to pick day, month, year, hour, minute and second. * Uses momentjs as datetime library http://momentjs.com. -* For i18n include corresponding file from https://github.com/timrwood/moment/tree/master/lang +* For internalization include corresponding file from https://github.com/timrwood/moment/tree/master/lang * * Confusion at noon and midnight - see http://en.wikipedia.org/wiki/12-hour_clock#Confusion_at_noon_and_midnight * In combodate: * 12:00 pm --> 12:00 (24-h format, midday) * 12:00 am --> 00:00 (24-h format, midnight, start of day) \ No newline at end of file