From 39bb5a3e716f5e23fdd7d4f74f04a72baff64bd5 Mon Sep 17 00:00:00 2001
From: Micha <espey@smart-q.de>
Date: Sun, 23 Feb 2025 15:59:23 +0100
Subject: [PATCH] updted README.md

---
 README.md                                     |    3 +
 .../js/bootstrap-editable.esm.js              |    1 +
 .../js/bootstrap-editable.js                  | 6812 +----------------
 package.json                                  |   35 +-
 4 files changed, 31 insertions(+), 6820 deletions(-)
 create mode 100644 dist/bootstrap5-editable/js/bootstrap-editable.esm.js

diff --git a/README.md b/README.md
index 42354a2..6e3c6f4 100644
--- a/README.md
+++ b/README.md
@@ -9,6 +9,9 @@ The original **x-editable** library was designed for Bootstrap 3 and has not bee
 - **Bootstrap Icons** (replacing Glyphicons)
 - General improvements for usability and maintainability
 
+NOTE: This repo has not yet been completely tested and is not available via NPM right now!
+
+Later: 
 ### Using npm:
 ```sh
 npm install x-editable-bootstrap5
diff --git a/dist/bootstrap5-editable/js/bootstrap-editable.esm.js b/dist/bootstrap5-editable/js/bootstrap-editable.esm.js
new file mode 100644
index 0000000..478a546
--- /dev/null
+++ b/dist/bootstrap5-editable/js/bootstrap-editable.esm.js
@@ -0,0 +1 @@
+!function(t){const n=t.fn.editableform.Constructor.prototype.initInput;t.extend(t.fn.editableform.Constructor.prototype,{initInput:function(){n.apply(this);const i=null===this.input.options.inputclass||!1===this.input.options.inputclass,s="input-sm",e="text,select,textarea,password,email,url,tel,number,range,time,typeaheadjs".split(",");~t.inArray(this.input.type,e)&&(this.input.$input.addClass("form-control editable"),i&&(this.input.options.inputclass=s,this.input.$input.addClass(s))),"select"===this.input.type&&setTimeout((()=>{this.input.$input.focus().click()}),50);const o=this.$form.find(".editable-buttons"),l=i?[s]:this.input.options.inputclass.split(" ");for(let t=0;t<l.length;t++)"input-lg"===l[t].toLowerCase()&&o.find("button").removeClass("btn-sm").addClass("btn-lg")}}),t.fn.editableform.buttons='<button type="submit" class="btn btn-primary btn-sm editable-submit"><i class="bi bi-check"></i></button><button type="button" class="btn btn-secondary btn-sm editable-cancel"><i class="bi bi-x"></i></button>',t.fn.editableform.errorGroupClass="has-error",t.fn.editableform.errorBlockClass=null,t.fn.editableform.engine="bs3"}(window.jQuery);
\ No newline at end of file
diff --git a/dist/bootstrap5-editable/js/bootstrap-editable.js b/dist/bootstrap5-editable/js/bootstrap-editable.js
index 890199c..068d164 100644
--- a/dist/bootstrap5-editable/js/bootstrap-editable.js
+++ b/dist/bootstrap5-editable/js/bootstrap-editable.js
@@ -1,6811 +1 @@
-/*! X-editable - v1.5.1 
-* In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery
-* http://github.com/vitalets/x-editable
-* Copyright (c) 2025 Vitaliy Potapov; Licensed MIT */
-/**
-Form with single input element, two buttons and two states: normal/loading.
-Applied as jQuery method to DIV tag (not to form tag!). This is because form can be in loading state when spinner shown.
-Editableform is linked with one of input types, e.g. 'text', 'select' etc.
-
-@class editableform
-@uses text
-@uses textarea
-**/
-(function ($) {
-    "use strict";
-    
-    var EditableForm = function (div, options) {
-        this.options = $.extend({}, $.fn.editableform.defaults, options);
-        this.$div = $(div); //div, containing form. Not form tag. Not editable-element.
-        if(!this.options.scope) {
-            this.options.scope = this;
-        }
-        //nothing shown after init
-    };
-
-    EditableForm.prototype = {
-        constructor: EditableForm,
-        initInput: function() {  //called once
-            //take input from options (as it is created in editable-element)
-            this.input = this.options.input;
-            
-            //set initial value
-            //todo: may be add check: typeof str === 'string' ? 
-            this.value = this.input.str2value(this.options.value); 
-            
-            //prerender: get input.$input
-            this.input.prerender();
-        },
-        initTemplate: function() {
-            this.$form = $($.fn.editableform.template); 
-        },
-        initButtons: function() {
-            var $btn = this.$form.find('.editable-buttons');
-            $btn.append($.fn.editableform.buttons);
-            if(this.options.showbuttons === 'bottom') {
-                $btn.addClass('editable-buttons-bottom');
-            }
-        },
-        /**
-        Renders editableform
-
-        @method render
-        **/        
-        render: function() {
-            //init loader
-            this.$loading = $($.fn.editableform.loading);        
-            this.$div.empty().append(this.$loading);
-            
-            //init form template and buttons
-            this.initTemplate();
-            if(this.options.showbuttons) {
-                this.initButtons();
-            } else {
-                this.$form.find('.editable-buttons').remove();
-            }
-
-            //show loading state
-            this.showLoading();            
-            
-            //flag showing is form now saving value to server. 
-            //It is needed to wait when closing form.
-            this.isSaving = false;
-            
-            /**        
-            Fired when rendering starts
-            @event rendering 
-            @param {Object} event event object
-            **/            
-            this.$div.triggerHandler('rendering');
-            
-            //init input
-            this.initInput();
-            
-            //append input to form
-            this.$form.find('div.editable-input').append(this.input.$tpl);            
-            
-            //append form to container
-            this.$div.append(this.$form);
-            
-            //render input
-            $.when(this.input.render())
-            .then($.proxy(function () {
-                //setup input to submit automatically when no buttons shown
-                if(!this.options.showbuttons) {
-                    this.input.autosubmit(); 
-                }
-                 
-                //attach 'cancel' handler
-                this.$form.find('.editable-cancel').click($.proxy(this.cancel, this));
-                
-                if(this.input.error) {
-                    this.error(this.input.error);
-                    this.$form.find('.editable-submit').attr('disabled', true);
-                    this.input.$input.attr('disabled', true);
-                    //prevent form from submitting
-                    this.$form.submit(function(e){ e.preventDefault(); });
-                } else {
-                    this.error(false);
-                    this.input.$input.removeAttr('disabled');
-                    this.$form.find('.editable-submit').removeAttr('disabled');
-                    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));
-                }
-
-                /**        
-                Fired when form is rendered
-                @event rendered
-                @param {Object} event event object
-                **/            
-                this.$div.triggerHandler('rendered');                
-
-                this.showForm();
-                
-                //call postrender method to perform actions required visibility of form
-                if(this.input.postrender) {
-                    this.input.postrender();
-                }                
-            }, this));
-        },
-        cancel: function() {   
-            /**        
-            Fired when form was cancelled by user
-            @event cancel 
-            @param {Object} event event object
-            **/              
-            this.$div.triggerHandler('cancel');
-        },
-        showLoading: function() {
-            var w, h;
-            if(this.$form) {
-                //set loading size equal to form
-                w = this.$form.outerWidth();
-                h = this.$form.outerHeight(); 
-                if(w) {
-                    this.$loading.width(w);
-                }
-                if(h) {
-                    this.$loading.height(h);
-                }
-                this.$form.hide();
-            } else {
-                //stretch loading to fill container width
-                w = this.$loading.parent().width();
-                if(w) {
-                    this.$loading.width(w);
-                }
-            }
-            this.$loading.show(); 
-        },
-
-        showForm: function(activate) {
-            this.$loading.hide();
-            this.$form.show();
-            if(activate !== false) {
-                this.input.activate(); 
-            }
-            /**        
-            Fired when form is shown
-            @event show 
-            @param {Object} event event object
-            **/                    
-            this.$div.triggerHandler('show');
-        },
-
-        error: function(msg) {
-            var $group = this.$form.find('.control-group'),
-                $block = this.$form.find('.editable-error-block'),
-                lines;
-
-            if(msg === false) {
-                $group.removeClass($.fn.editableform.errorGroupClass);
-                $block.removeClass($.fn.editableform.errorBlockClass).empty().hide(); 
-            } else {
-                //convert newline to <br> for more pretty error display
-                if(msg) {
-                    lines = (''+msg).split('\n');
-                    for (var i = 0; i < lines.length; i++) {
-                        lines[i] = $('<div>').text(lines[i]).html();
-                    }
-                    msg = lines.join('<br>');
-                }
-                $group.addClass($.fn.editableform.errorGroupClass);
-                $block.addClass($.fn.editableform.errorBlockClass).html(msg).show();
-            }
-        },
-
-        submit: function(e) {
-            e.stopPropagation();
-            e.preventDefault();
-            
-            //get new value from input
-            var newValue = this.input.input2value(); 
-
-            //validation: if validate returns string or truthy value - means error
-            //if returns object like {newValue: '...'} => submitted value is reassigned to it
-            var error = this.validate(newValue);
-            if ($.type(error) === 'object' && error.newValue !== undefined) {
-                newValue = error.newValue;
-                this.input.value2input(newValue);
-                if(typeof error.msg === 'string') {
-                    this.error(error.msg);
-                    this.showForm();
-                    return;
-                }
-            } else if (error) {
-                this.error(error);
-                this.showForm();
-                return;
-            } 
-            
-            //if value not changed --> trigger 'nochange' event and return
-            /*jslint eqeq: true*/
-            if (!this.options.savenochange && this.input.value2str(newValue) == this.input.value2str(this.value)) {
-            /*jslint eqeq: false*/                
-                /**        
-                Fired when value not changed but form is submitted. Requires savenochange = false.
-                @event nochange 
-                @param {Object} event event object
-                **/                    
-                this.$div.triggerHandler('nochange');            
-                return;
-            } 
-
-            //convert value for submitting to server
-            var submitValue = this.input.value2submit(newValue);
-            
-            this.isSaving = true;
-            
-            //sending data to server
-            $.when(this.save(submitValue))
-            .done($.proxy(function(response) {
-                this.isSaving = false;
-
-                //run success callback
-                var res = typeof this.options.success === 'function' ? this.options.success.call(this.options.scope, response, newValue) : null;
-
-                //if success callback returns false --> keep form open and do not activate input
-                if(res === false) {
-                    this.error(false);
-                    this.showForm(false);
-                    return;
-                }
-
-                //if success callback returns string -->  keep form open, show error and activate input               
-                if(typeof res === 'string') {
-                    this.error(res);
-                    this.showForm();
-                    return;
-                }
-
-                //if success callback returns object like {newValue: <something>} --> use that value instead of submitted
-                //it is usefull if you want to chnage value in url-function
-                if(res && typeof res === 'object' && res.hasOwnProperty('newValue')) {
-                    newValue = res.newValue;
-                }
-
-                //clear error message
-                this.error(false);   
-                this.value = newValue;
-                /**        
-                Fired when form is submitted
-                @event save 
-                @param {Object} event event object
-                @param {Object} params additional params
-                @param {mixed} params.newValue raw new value
-                @param {mixed} params.submitValue submitted value as string
-                @param {Object} params.response ajax response
-
-                @example
-                $('#form-div').on('save'), function(e, params){
-                    if(params.newValue === 'username') {...}
-                });
-                **/
-                this.$div.triggerHandler('save', {newValue: newValue, submitValue: submitValue, response: response});
-            }, this))
-            .fail($.proxy(function(xhr) {
-                this.isSaving = false;
-
-                var msg;
-                if(typeof this.options.error === 'function') {
-                    msg = this.options.error.call(this.options.scope, xhr, newValue);
-                } else {
-                    msg = typeof xhr === 'string' ? xhr : xhr.responseText || xhr.statusText || 'Unknown error!';
-                }
-
-                this.error(msg);
-                this.showForm();
-            }, this));
-        },
-
-        save: function(submitValue) {
-            //try parse composite pk defined as json string in data-pk 
-            this.options.pk = $.fn.editableutils.tryParseJson(this.options.pk, true); 
-            
-            var pk = (typeof this.options.pk === 'function') ? this.options.pk.call(this.options.scope) : this.options.pk,
-            /*
-              send on server in following cases:
-              1. url is function
-              2. url is string AND (pk defined OR send option = always) 
-            */
-            send = !!(typeof this.options.url === 'function' || (this.options.url && ((this.options.send === 'always') || (this.options.send === 'auto' && pk !== null && pk !== undefined)))),
-            params;
-
-            if (send) { //send to server
-                this.showLoading();
-
-                //standard params
-                params = {
-                    name: this.options.name || '',
-                    value: submitValue,
-                    pk: pk 
-                };
-
-                //additional params
-                if(typeof this.options.params === 'function') {
-                    params = this.options.params.call(this.options.scope, params);  
-                } else {
-                    //try parse json in single quotes (from data-params attribute)
-                    this.options.params = $.fn.editableutils.tryParseJson(this.options.params, true);   
-                    $.extend(params, this.options.params);
-                }
-
-                if(typeof this.options.url === 'function') { //user's function
-                    return this.options.url.call(this.options.scope, params);
-                } else {  
-                    //send ajax to server and return deferred object
-                    return $.ajax($.extend({
-                        url     : this.options.url,
-                        data    : params,
-                        type    : 'POST'
-                    }, this.options.ajaxOptions));
-                }
-            }
-        }, 
-
-        validate: function (value) {
-            if (value === undefined) {
-                value = this.value;
-            }
-            if (typeof this.options.validate === 'function') {
-                return this.options.validate.call(this.options.scope, value);
-            }
-        },
-
-        option: function(key, value) {
-            if(key in this.options) {
-                this.options[key] = value;
-            }
-            
-            if(key === 'value') {
-                this.setValue(value);
-            }
-            
-            //do not pass option to input as it is passed in editable-element
-        },
-
-        setValue: function(value, convertStr) {
-            if(convertStr) {
-                this.value = this.input.str2value(value);
-            } else {
-                this.value = value;
-            }
-            
-            //if form is visible, update input
-            if(this.$form && this.$form.is(':visible')) {
-                this.input.value2input(this.value);
-            }            
-        }               
-    };
-
-    /*
-    Initialize editableform. Applied to jQuery object.
-
-    @method $().editableform(options)
-    @params {Object} options
-    @example
-    var $form = $('&lt;div&gt;').editableform({
-        type: 'text',
-        name: 'username',
-        url: '/post',
-        value: 'vitaliy'
-    });
-
-    //to display form you should call 'render' method
-    $form.editableform('render');     
-    */
-    $.fn.editableform = function (option) {
-        var args = arguments;
-        return this.each(function () {
-            var $this = $(this), 
-            data = $this.data('editableform'), 
-            options = typeof option === 'object' && option; 
-            if (!data) {
-                $this.data('editableform', (data = new EditableForm(this, options)));
-            }
-
-            if (typeof option === 'string') { //call method 
-                data[option].apply(data, Array.prototype.slice.call(args, 1));
-            } 
-        });
-    };
-
-    //keep link to constructor to allow inheritance
-    $.fn.editableform.Constructor = EditableForm;    
-
-    //defaults
-    $.fn.editableform.defaults = {
-        /* see also defaults for input */
-
-        /**
-        Type of input. Can be <code>text|textarea|select|date|checklist</code>
-
-        @property type 
-        @type string
-        @default 'text'
-        **/
-        type: 'text',
-        /**
-        Url for submit, e.g. <code>'/post'</code>  
-        If function - it will be called instead of ajax. Function should return deferred object to run fail/done callbacks.
-
-        @property url 
-        @type string|function
-        @default null
-        @example
-        url: function(params) {
-            var d = new $.Deferred;
-            if(params.value === 'abc') {
-                return d.reject('error message'); //returning error via deferred object
-            } else {
-                //async saving data in js model
-                someModel.asyncSaveMethod({
-                   ..., 
-                   success: function(){
-                      d.resolve();
-                   }
-                }); 
-                return d.promise();
-            }
-        } 
-        **/        
-        url:null,
-        /**
-        Additional params for submit. If defined as <code>object</code> - it is **appended** to original ajax data (pk, name and value).  
-        If defined as <code>function</code> - returned object **overwrites** original ajax data.
-        @example
-        params: function(params) {
-            //originally params contain pk, name and value
-            params.a = 1;
-            return params;
-        }
-
-        @property params 
-        @type object|function
-        @default null
-        **/          
-        params:null,
-        /**
-        Name of field. Will be submitted on server. Can be taken from <code>id</code> attribute
-
-        @property name 
-        @type string
-        @default null
-        **/         
-        name: null,
-        /**
-        Primary key of editable object (e.g. record id in database). For composite keys use object, e.g. <code>{id: 1, lang: 'en'}</code>.
-        Can be calculated dynamically via function.
-
-        @property pk 
-        @type string|object|function
-        @default null
-        **/         
-        pk: null,
-        /**
-        Initial value. If not defined - will be taken from element's content.
-        For __select__ type should be defined (as it is ID of shown text).
-
-        @property value 
-        @type string|object
-        @default null
-        **/        
-        value: null,
-        /**
-        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', 
-        /**
-        Function for client-side validation. If returns string - means validation not passed and string showed as error.
-        Since 1.5.1 you can modify submitted value by returning object from `validate`: 
-        `{newValue: '...'}` or `{newValue: '...', msg: '...'}`
-
-        @property validate 
-        @type function
-        @default null
-        @example
-        validate: function(value) {
-            if($.trim(value) == '') {
-                return 'This field is required';
-            }
-        }
-        **/         
-        validate: null,
-        /**
-        Success callback. Called when value successfully sent on server and **response status = 200**.  
-        Usefull to work with json response. For example, if your backend response can be <code>{success: true}</code>
-        or <code>{success: false, msg: "server error"}</code> you can check it inside this callback.  
-        If it returns **string** - means error occured and string is shown as error message.  
-        If it returns **object like** <code>{newValue: &lt;something&gt;}</code> - it overwrites value, submitted by user.  
-        Otherwise newValue simply rendered into element.
-        
-        @property success 
-        @type function
-        @default null
-        @example
-        success: function(response, newValue) {
-            if(!response.success) return response.msg;
-        }
-        **/          
-        success: null,
-        /**
-        Error callback. Called when request failed (response status != 200).  
-        Usefull when you want to parse error response and display a custom message.
-        Must return **string** - the message to be displayed in the error block.
-                
-        @property error 
-        @type function
-        @default null
-        @since 1.4.4
-        @example
-        error: function(response, newValue) {
-            if(response.status === 500) {
-                return 'Service unavailable. Please try later.';
-            } else {
-                return response.responseText;
-            }
-        }
-        **/          
-        error: null,
-        /**
-        Additional options for submit ajax request.
-        List of values: http://api.jquery.com/jQuery.ajax
-        
-        @property ajaxOptions 
-        @type object
-        @default null
-        @since 1.1.1        
-        @example 
-        ajaxOptions: {
-            type: 'put',
-            dataType: 'json'
-        }        
-        **/        
-        ajaxOptions: null,
-        /**
-        Where to show buttons: left(true)|bottom|false  
-        Form without buttons is auto-submitted.
-
-        @property showbuttons 
-        @type boolean|string
-        @default true
-        @since 1.1.1
-        **/         
-        showbuttons: true,
-        /**
-        Scope for callback methods (success, validate).  
-        If <code>null</code> means editableform instance itself. 
-
-        @property scope 
-        @type DOMElement|object
-        @default null
-        @since 1.2.0
-        @private
-        **/            
-        scope: null,
-        /**
-        Whether to save or cancel value when it was not changed but form was submitted
-
-        @property savenochange 
-        @type boolean
-        @default false
-        @since 1.2.0
-        **/
-        savenochange: false
-    };   
-
-    /*
-    Note: following params could redefined in engine: bootstrap or jqueryui:
-    Classes 'control-group' and 'editable-error-block' must always present!
-    */      
-    $.fn.editableform.template = '<form class="form-inline editableform">'+
-    '<div class="control-group">' + 
-    '<div><div class="editable-input"></div><div class="editable-buttons"></div></div>'+
-    '<div class="editable-error-block"></div>' + 
-    '</div>' + 
-    '</form>';
-
-    //loading div
-    $.fn.editableform.loading = '<div class="editableform-loading"></div>';
-
-    //buttons
-    $.fn.editableform.buttons = '<button type="submit" class="editable-submit">ok</button>'+
-    '<button type="button" class="editable-cancel">cancel</button>';      
-
-    //error class attached to control-group
-    $.fn.editableform.errorGroupClass = null;  
-
-    //error class attached to editable-error-block
-    $.fn.editableform.errorBlockClass = 'editable-error';
-    
-    //engine
-    $.fn.editableform.engine = 'jquery';
-}(window.jQuery));
-
-/**
-* EditableForm utilites
-*/
-(function ($) {
-    "use strict";
-    
-    //utils
-    $.fn.editableutils = {
-        /**
-        * classic JS inheritance function
-        */  
-        inherit: function (Child, Parent) {
-            var F = function() { };
-            F.prototype = Parent.prototype;
-            Child.prototype = new F();
-            Child.prototype.constructor = Child;
-            Child.superclass = Parent.prototype;
-        },
-
-        /**
-        * set caret position in input
-        * see http://stackoverflow.com/questions/499126/jquery-set-cursor-position-in-text-area
-        */        
-        setCursorPosition: function(elem, pos) {
-            if (elem.setSelectionRange) {
-                elem.setSelectionRange(pos, pos);
-            } else if (elem.createTextRange) {
-                var range = elem.createTextRange();
-                range.collapse(true);
-                range.moveEnd('character', pos);
-                range.moveStart('character', pos);
-                range.select();
-            }
-        },
-
-        /**
-        * function to parse JSON in *single* quotes. (jquery automatically parse only double quotes)
-        * That allows such code as: <a data-source="{'a': 'b', 'c': 'd'}">
-        * safe = true --> means no exception will be thrown
-        * for details see http://stackoverflow.com/questions/7410348/how-to-set-json-format-to-html5-data-attributes-in-the-jquery
-        */
-        tryParseJson: function(s, safe) {
-            if (typeof s === 'string' && s.length && s.match(/^[\{\[].*[\}\]]$/)) {
-                if (safe) {
-                    try {
-                        /*jslint evil: true*/
-                        s = (new Function('return ' + s))();
-                        /*jslint evil: false*/
-                    } catch (e) {} finally {
-                        return s;
-                    }
-                } else {
-                    /*jslint evil: true*/
-                    s = (new Function('return ' + s))();
-                    /*jslint evil: false*/
-                }
-            }
-            return s;
-        },
-
-        /**
-        * slice object by specified keys
-        */
-        sliceObj: function(obj, keys, caseSensitive /* default: false */) {
-            var key, keyLower, newObj = {};
-
-            if (!$.isArray(keys) || !keys.length) {
-                return newObj;
-            }
-
-            for (var i = 0; i < keys.length; i++) {
-                key = keys[i];
-                if (obj.hasOwnProperty(key)) {
-                    newObj[key] = obj[key];
-                }
-
-                if(caseSensitive === true) {
-                    continue;
-                }
-
-                //when getting data-* attributes via $.data() it's converted to lowercase.
-                //details: http://stackoverflow.com/questions/7602565/using-data-attributes-with-jquery
-                //workaround is code below.
-                keyLower = key.toLowerCase();
-                if (obj.hasOwnProperty(keyLower)) {
-                    newObj[key] = obj[keyLower];
-                }
-            }
-
-            return newObj;
-        },
-
-        /*
-        exclude complex objects from $.data() before pass to config
-        */
-        getConfigData: function($element) {
-            var data = {};
-            $.each($element.data(), function(k, v) {
-                if(typeof v !== 'object' || (v && typeof v === 'object' && (v.constructor === Object || v.constructor === Array))) {
-                    data[k] = v;
-                }
-            });
-            return data;
-        },
-
-        /*
-         returns keys of object
-        */
-        objectKeys: function(o) {
-            if (Object.keys) {
-                return Object.keys(o);  
-            } else {
-                if (o !== Object(o)) {
-                    throw new TypeError('Object.keys called on a non-object');
-                }
-                var k=[], p;
-                for (p in o) {
-                    if (Object.prototype.hasOwnProperty.call(o,p)) {
-                        k.push(p);
-                    }
-                }
-                return k;
-            }
-
-        },
-        
-       /**
-        method to escape html.
-       **/
-       escape: function(str) {
-           return $('<div>').text(str).html();
-       },
-       
-       /*
-        returns array items from sourceData having value property equal or inArray of 'value'
-       */
-       itemsByValue: function(value, sourceData, valueProp) {
-           if(!sourceData || value === null) {
-               return [];
-           }
-           
-           if (typeof(valueProp) !== "function") {
-               var idKey = valueProp || 'value';
-               valueProp = function (e) { return e[idKey]; };
-           }
-                      
-           var isValArray = $.isArray(value),
-           result = [], 
-           that = this;
-
-           $.each(sourceData, function(i, o) {
-               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' ? valueProp(o) : o); }).length) {
-                           result.push(o); 
-                       }
-                   } else {
-                       var itemValue = (o && (typeof o === 'object')) ? valueProp(o) : o;
-                       if(value == itemValue) {
-                           result.push(o); 
-                       }
-                   }
-                   /*jslint eqeq: false*/
-               }
-           });
-           
-           return result;
-       },
-       
-       /*
-       Returns input by options: type, mode. 
-       */
-       createInput: function(options) {
-           var TypeConstructor, typeOptions, input,
-           type = options.type;
-
-           //`date` is some kind of virtual type that is transformed to one of exact types
-           //depending on mode and core lib
-           if(type === 'date') {
-               //inline
-               if(options.mode === 'inline') {
-                   if($.fn.editabletypes.datefield) {
-                       type = 'datefield';
-                   } else if($.fn.editabletypes.dateuifield) {
-                       type = 'dateuifield';
-                   }
-               //popup
-               } else {
-                   if($.fn.editabletypes.date) {
-                       type = 'date';
-                   } else if($.fn.editabletypes.dateui) {
-                       type = 'dateui';
-                   }
-               }
-               
-               //if type still `date` and not exist in types, replace with `combodate` that is base input
-               if(type === 'date' && !$.fn.editabletypes.date) {
-                   type = 'combodate';
-               } 
-           }
-           
-           //`datetime` should be datetimefield in 'inline' mode
-           if(type === 'datetime' && options.mode === 'inline') {
-             type = 'datetimefield';  
-           }           
-
-           //change wysihtml5 to textarea for jquery UI and plain versions
-           if(type === 'wysihtml5' && !$.fn.editabletypes[type]) {
-               type = 'textarea';
-           }
-
-           //create input of specified type. Input will be used for converting value, not in form
-           if(typeof $.fn.editabletypes[type] === 'function') {
-               TypeConstructor = $.fn.editabletypes[type];
-               typeOptions = this.sliceObj(options, this.objectKeys(TypeConstructor.defaults));
-               input = new TypeConstructor(typeOptions);
-               return input;
-           } else {
-               $.error('Unknown type: '+ type);
-               return false; 
-           }  
-       },
-       
-       //see http://stackoverflow.com/questions/7264899/detect-css-transitions-using-javascript-and-without-modernizr
-       supportsTransitions: function () {
-           var b = document.body || document.documentElement,
-               s = b.style,
-               p = 'transition',
-               v = ['Moz', 'Webkit', 'Khtml', 'O', 'ms'];
-               
-           if(typeof s[p] === 'string') {
-               return true; 
-           }
-
-           // Tests for vendor specific prop
-           p = p.charAt(0).toUpperCase() + p.substr(1);
-           for(var i=0; i<v.length; i++) {
-               if(typeof s[v[i] + p] === 'string') { 
-                   return true; 
-               }
-           }
-           return false;
-       }            
-       
-    };      
-}(window.jQuery));
-
-/**
-Attaches stand-alone container with editable-form to HTML element. Element is used only for positioning, value is not stored anywhere.<br>
-This method applied internally in <code>$().editable()</code>. You should subscribe on it's events (save / cancel) to get profit of it.<br>
-Final realization can be different: bootstrap-popover, jqueryui-tooltip, poshytip, inline-div. It depends on which js file you include.<br>
-Applied as jQuery method.
-
-@class editableContainer
-@uses editableform
-**/
-(function ($) {
-    "use strict";
-
-    var Popup = function (element, options) {
-        this.init(element, options);
-    };
-    
-    var Inline = function (element, options) {
-        this.init(element, options);
-    };    
-
-    //methods
-    Popup.prototype = {
-        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
-        defaults: {}, //container itself defaults
-        
-        init: function(element, options) {
-            this.$element = $(element);
-            //since 1.4.1 container do not use data-* directly as they already merged into options.
-            this.options = $.extend({}, $.fn.editableContainer.defaults, options);         
-            this.splitOptions();
-            
-            //set scope of form callbacks to element
-            this.formOptions.scope = this.$element[0]; 
-            
-            this.initContainer();
-            
-            //flag to hide container, when saving value will finish
-            this.delayedHide = false;
-
-            //bind 'destroyed' listener to destroy container when element is removed from dom
-            this.$element.on('destroyed', $.proxy(function(){
-                this.destroy();
-            }, this)); 
-            
-            //attach document handler to close containers on click / escape
-            if(!$(document).data('editable-handlers-attached')) {
-                //close all on escape
-                $(document).on('keyup.editable', function (e) {
-                    if (e.which === 27) {
-                        $('.editable-open').editableContainer('hide');
-                        //todo: return focus on element 
-                    }
-                });
-
-                //close containers when click outside 
-                //(mousedown could be better than click, it closes everything also on drag drop)
-                $(document).on('click.editable', function(e) {
-                    var $target = $(e.target), i,
-                        exclude_classes = ['.editable-container', 
-                                           '.ui-datepicker-header', 
-                                           '.datepicker', //in inline mode datepicker is rendered into body
-                                           '.modal-backdrop', 
-                                           '.bootstrap-wysihtml5-insert-image-modal', 
-                                           '.bootstrap-wysihtml5-insert-link-modal'
-                                           ];
-                    
-                    //check if element is detached. It occurs when clicking in bootstrap datepicker
-                    if (!$.contains(document.documentElement, e.target)) {
-                      return;
-                    }
-
-                    //for some reason FF 20 generates extra event (click) in select2 widget with e.target = document
-                    //we need to filter it via construction below. See https://github.com/vitalets/x-editable/issues/199
-                    //Possibly related to http://stackoverflow.com/questions/10119793/why-does-firefox-react-differently-from-webkit-and-ie-to-click-event-on-selec
-                    if($target.is(document)) {
-                       return; 
-                    }
-                    
-                    //if click inside one of exclude classes --> no nothing
-                    for(i=0; i<exclude_classes.length; i++) {
-                         if($target.is(exclude_classes[i]) || $target.parents(exclude_classes[i]).length) {
-                             return;
-                         }
-                    }
-                      
-                    //close all open containers (except one - target)
-                    Popup.prototype.closeOthers(e.target);
-                });
-                
-                $(document).data('editable-handlers-attached', true);
-            }                        
-        },
-
-        //split options on containerOptions and formOptions
-        splitOptions: function() {
-            this.containerOptions = {};
-            this.formOptions = {};
-            
-            if(!$.fn[this.containerName]) {
-                throw new Error(this.containerName + ' not found. Have you included corresponding js file?');   
-            }
-            
-            //keys defined in container defaults go to container, others go to form
-            for(var k in this.options) {
-              if(k in this.defaults) {
-                 this.containerOptions[k] = this.options[k];
-              } else {
-                 this.formOptions[k] = this.options[k];
-              } 
-            }
-        },
-        
-        /*
-        Returns jquery object of container
-        @method tip()
-        */         
-        tip: function() {
-            return this.container() ? this.container().$tip : null;
-        },
-
-        /* returns container object */
-        container: function() {
-            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); 
-        },        
-        
-        initContainer: function(){
-            this.call(this.containerOptions);
-        },
-
-        renderForm: function() {
-            this.$form
-            .editableform(this.formOptions)
-            .on({
-                save: $.proxy(this.save, this), //click on submit button (value changed)
-                nochange: $.proxy(function(){ this.hide('nochange'); }, this), //click on submit button (value NOT changed)                
-                cancel: $.proxy(function(){ this.hide('cancel'); }, this), //click on cancel button
-                show: $.proxy(function() {
-                    if(this.delayedHide) {
-                        this.hide(this.delayedHide.reason);
-                        this.delayedHide = false;
-                    } else {
-                        this.setPosition();
-                    }
-                }, this), //re-position container every time form is shown (occurs each time after loading state)
-                rendering: $.proxy(this.setPosition, this), //this allows to place container correctly when loading shown
-                resize: $.proxy(this.setPosition, this), //this allows to re-position container when form size is changed 
-                rendered: $.proxy(function(){
-                    /**        
-                    Fired when container is shown and form is rendered (for select will wait for loading dropdown options).  
-                    **Note:** Bootstrap popover has own `shown` event that now cannot be separated from x-editable's one.
-                    The workaround is to check `arguments.length` that is always `2` for x-editable.                     
-                    
-                    @event shown 
-                    @param {Object} event event object
-                    @example
-                    $('#username').on('shown', function(e, editable) {
-                        editable.input.$input.val('overwriting value of input..');
-                    });                     
-                    **/                      
-                    /*
-                     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.options.scope).data('editable')); 
-                }, this) 
-            })
-            .editableform('render');
-        },        
-
-        /**
-        Shows container with form
-        @method show()
-        @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
-        **/
-        /* Note: poshytip owerwrites this method totally! */          
-        show: function (closeAll) {
-            this.$element.addClass('editable-open');
-            if(closeAll !== false) {
-                //close all open containers (except this)
-                this.closeOthers(this.$element[0]);  
-            }
-            
-            //show container itself
-            this.innerShow();
-            this.tip().addClass(this.containerClass);
-
-            /*
-            Currently, form is re-rendered on every show. 
-            The main reason is that we dont know, what will container do with content when closed:
-            remove(), detach() or just hide() - it depends on container.
-            
-            Detaching form itself before hide and re-insert before show is good solution, 
-            but visually it looks ugly --> container changes size before hide.  
-            */             
-            
-            //if form already exist - delete previous data 
-            if(this.$form) {
-                //todo: destroy prev data!
-                //this.$form.destroy();
-            }
-
-            this.$form = $('<div>');
-            
-            //insert form into container body
-            if(this.tip().is(this.innerCss)) {
-                //for inline container
-                this.tip().append(this.$form); 
-            } else {
-                this.tip().find(this.innerCss).append(this.$form);
-            } 
-            
-            //render form
-            this.renderForm();
-        },
-
-        /**
-        Hides container with form
-        @method hide()
-        @param {string} reason Reason caused hiding. Can be <code>save|cancel|onblur|nochange|undefined (=manual)</code>
-        **/         
-        hide: function(reason) {  
-            if(!this.tip() || !this.tip().is(':visible') || !this.$element.hasClass('editable-open')) {
-                return;
-            }
-            
-            //if form is saving value, schedule hide
-            if(this.$form.data('editableform').isSaving) {
-                this.delayedHide = {reason: reason};
-                return;    
-            } else {
-                this.delayedHide = false;
-            }
-
-            this.$element.removeClass('editable-open');   
-            this.innerHide();
-
-            /**
-            Fired when container was hidden. It occurs on both save or cancel.  
-            **Note:** Bootstrap popover has own `hidden` event that now cannot be separated from x-editable's one.
-            The workaround is to check `arguments.length` that is always `2` for x-editable. 
-
-            @event hidden 
-            @param {object} event event object
-            @param {string} reason Reason caused hiding. Can be <code>save|cancel|onblur|nochange|manual</code>
-            @example
-            $('#username').on('hidden', function(e, reason) {
-                if(reason === 'save' || reason === 'cancel') {
-                    //auto-open next editable
-                    $(this).closest('tr').next().find('.editable').editable('show');
-                } 
-            });
-            **/
-            this.$element.triggerHandler('hidden', reason || 'manual');   
-        },
-
-        /* internal show method. To be overwritten in child classes */
-        innerShow: function () {
-             
-        },        
-
-        /* internal hide method. To be overwritten in child classes */
-        innerHide: function () {
-
-        },
-        
-        /**
-        Toggles container visibility (show / hide)
-        @method toggle()
-        @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
-        **/          
-        toggle: function(closeAll) {
-            if(this.container() && this.tip() && this.tip().is(':visible')) {
-                this.hide();
-            } else {
-                this.show(closeAll);
-            } 
-        },
-
-        /*
-        Updates the position of container when content changed.
-        @method setPosition()
-        */       
-        setPosition: function() {
-            //tbd in child class
-        },
-
-        save: function(e, params) {
-            /**        
-            Fired when new value was submitted. You can use <code>$(this).data('editableContainer')</code> inside handler to access to editableContainer instance
-            
-            @event save 
-            @param {Object} event event object
-            @param {Object} params additional params
-            @param {mixed} params.newValue submitted value
-            @param {Object} params.response ajax response
-            @example
-            $('#username').on('save', function(e, params) {
-                //assuming server response: '{success: true}'
-                var pk = $(this).data('editableContainer').options.pk;
-                if(params.response && params.response.success) {
-                    alert('value: ' + params.newValue + ' with pk: ' + pk + ' saved!');
-                } else {
-                    alert('error!'); 
-                } 
-            });
-            **/             
-            this.$element.triggerHandler('save', params);
-            
-            //hide must be after trigger, as saving value may require methods of plugin, applied to input
-            this.hide('save');
-        },
-
-        /**
-        Sets new option
-        
-        @method option(key, value)
-        @param {string} key 
-        @param {mixed} value 
-        **/         
-        option: function(key, value) {
-            this.options[key] = value;
-            if(key in this.containerOptions) {
-                this.containerOptions[key] = value;
-                this.setContainerOption(key, value); 
-            } else {
-                this.formOptions[key] = value;
-                if(this.$form) {
-                    this.$form.editableform('option', key, value);  
-                }
-            }
-        },
-        
-        setContainerOption: function(key, value) {
-            this.call('option', key, value);
-        },
-
-        /**
-        Destroys the container instance
-        @method destroy()
-        **/        
-        destroy: function() {
-            this.hide();
-            this.innerDestroy();
-            this.$element.off('destroyed');
-            this.$element.removeData('editableContainer');
-        },
-        
-        /* to be overwritten in child classes */
-        innerDestroy: function() {
-            
-        }, 
-        
-        /*
-        Closes other containers except one related to passed element. 
-        Other containers can be cancelled or submitted (depends on onblur option)
-        */
-        closeOthers: function(element) {
-            $('.editable-open').each(function(i, el){
-                //do nothing with passed element and it's children
-                if(el === element || $(el).find(element).length) {
-                    return;
-                }
-
-                //otherwise cancel or submit all open containers 
-                var $el = $(el),
-                ec = $el.data('editableContainer');
-
-                if(!ec) {
-                    return;  
-                }
-                
-                if(ec.options.onblur === 'cancel') {
-                    $el.data('editableContainer').hide('onblur');
-                } else if(ec.options.onblur === 'submit') {
-                    $el.data('editableContainer').tip().find('form').submit();
-                }
-            });
-
-        },
-        
-        /**
-        Activates input of visible container (e.g. set focus)
-        @method activate()
-        **/         
-        activate: function() {
-            if(this.tip && this.tip().is(':visible') && this.$form) {
-               this.$form.data('editableform').input.activate(); 
-            }
-        } 
-
-    };
-
-    /**
-    jQuery method to initialize editableContainer.
-    
-    @method $().editableContainer(options)
-    @params {Object} options
-    @example
-    $('#edit').editableContainer({
-        type: 'text',
-        url: '/post',
-        pk: 1,
-        value: 'hello'
-    });
-    **/  
-    $.fn.editableContainer = function (option) {
-        var args = arguments;
-        return this.each(function () {
-            var $this = $(this),
-            dataKey = 'editableContainer', 
-            data = $this.data(dataKey),
-            options = typeof option === 'object' && option,
-            Constructor = (options.mode === 'inline') ? Inline : Popup;             
-
-            if (!data) {
-                $this.data(dataKey, (data = new Constructor(this, options)));
-            }
-
-            if (typeof option === 'string') { //call method 
-                data[option].apply(data, Array.prototype.slice.call(args, 1));
-            }            
-        });
-    };     
-
-    //store constructors
-    $.fn.editableContainer.Popup = Popup;
-    $.fn.editableContainer.Inline = Inline;
-
-    //defaults
-    $.fn.editableContainer.defaults = {
-        /**
-        Initial value of form input
-
-        @property value 
-        @type mixed
-        @default null
-        @private
-        **/        
-        value: null,
-        /**
-        Placement of container relative to element. Can be <code>top|right|bottom|left</code>. Not used for inline container.
-
-        @property placement 
-        @type string
-        @default 'top'
-        **/        
-        placement: 'top',
-        /**
-        Whether to hide container on save/cancel.
-
-        @property autohide 
-        @type boolean
-        @default true
-        @private 
-        **/        
-        autohide: true,
-        /**
-        Action when user clicks outside the container. Can be <code>cancel|submit|ignore</code>.  
-        Setting <code>ignore</code> allows to have several containers open. 
-
-        @property onblur 
-        @type string
-        @default 'cancel'
-        @since 1.1.1
-        **/        
-        onblur: 'cancel',
-        
-        /**
-        Animation speed (inline mode only)
-        @property anim 
-        @type string
-        @default false
-        **/        
-        anim: false,
-        
-        /**
-        Mode of editable, can be `popup` or `inline` 
-        
-        @property mode 
-        @type string         
-        @default 'popup'
-        @since 1.4.0        
-        **/        
-        mode: 'popup'        
-    };
-
-    /* 
-    * workaround to have 'destroyed' event to destroy popover when element is destroyed
-    * see http://stackoverflow.com/questions/2200494/jquery-trigger-event-when-an-element-is-removed-from-the-dom
-    */
-    jQuery.event.special.destroyed = {
-        remove: function(o) {
-            if (o.handler) {
-                o.handler();
-            }
-        }
-    };    
-
-}(window.jQuery));
-
-/**
-* Editable Inline 
-* ---------------------
-*/
-(function ($) {
-    "use strict";
-    
-    //copy prototype from EditableContainer
-    //extend methods
-    $.extend($.fn.editableContainer.Inline.prototype, $.fn.editableContainer.Popup.prototype, {
-        containerName: 'editableform',
-        innerCss: '.editable-inline',
-        containerClass: 'editable-container editable-inline', //css class applied to container element
-                 
-        initContainer: function(){
-            //container is <span> element
-            this.$tip = $('<span></span>');
-            
-            //convert anim to miliseconds (int)
-            if(!this.options.anim) {
-                this.options.anim = 0;
-            }         
-        },
-        
-        splitOptions: function() {
-            //all options are passed to form
-            this.containerOptions = {};
-            this.formOptions = this.options;
-        },
-        
-        tip: function() {
-           return this.$tip; 
-        },
-        
-        innerShow: function () {
-            this.$element.hide();
-            this.tip().insertAfter(this.$element).show();
-        }, 
-        
-        innerHide: function () {
-            this.$tip.hide(this.options.anim, $.proxy(function() {
-                this.$element.show();
-                this.innerDestroy();
-            }, this)); 
-        },
-        
-        innerDestroy: function() {
-            if(this.tip()) {
-                this.tip().empty().remove();
-            }
-        } 
-    });
-
-}(window.jQuery));
-/**
-Makes editable any HTML element on the page. Applied as jQuery method.
-
-@class editable
-@uses editableContainer
-**/
-(function ($) {
-    "use strict";
-
-    var Editable = function (element, options) {
-        this.$element = $(element);
-        //data-* has more priority over js options: because dynamically created elements may change data-* 
-        this.options = $.extend({}, $.fn.editable.defaults, options, $.fn.editableutils.getConfigData(this.$element));  
-        if(this.options.selector) {
-            this.initLive();
-        } else {
-            this.init();
-        }
-        
-        //check for transition support
-        if(this.options.highlight && !$.fn.editableutils.supportsTransitions()) {
-            this.options.highlight = false;
-        }
-    };
-
-    Editable.prototype = {
-        constructor: Editable, 
-        init: function () {
-            var isValueByText = false, 
-                doAutotext, finalize;
-
-            //name
-            this.options.name = this.options.name || this.$element.attr('id');
-             
-            //create input of specified type. Input needed already here to convert value for initial display (e.g. show text by id for select)
-            //also we set scope option to have access to element inside input specific callbacks (e. g. source as function)
-            this.options.scope = this.$element[0]; 
-            this.input = $.fn.editableutils.createInput(this.options);
-            if(!this.input) {
-                return; 
-            }            
-
-            //set value from settings or by element's text
-            if (this.options.value === undefined || this.options.value === null) {
-                this.value = this.input.html2value($.trim(this.$element.html()));
-                isValueByText = true;
-            } else {
-                /*
-                  value can be string when received from 'data-value' attribute
-                  for complext objects value can be set as json string in data-value attribute, 
-                  e.g. data-value="{city: 'Moscow', street: 'Lenina'}"
-                */
-                this.options.value = $.fn.editableutils.tryParseJson(this.options.value, true); 
-                if(typeof this.options.value === 'string') {
-                    this.value = this.input.str2value(this.options.value);
-                } else {
-                    this.value = this.options.value;
-                }
-            }
-            
-            //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
-                    if(!this.options.disabled) {
-                        e.preventDefault();
-                    }
-                    
-                    //stop propagation not required because in document click handler it checks event target
-                    //e.stopPropagation();
-                    
-                    if(this.options.toggle === 'mouseenter') {
-                        //for hover only show container
-                        this.show();
-                    } else {
-                        //when toggle='click' we should not close all other containers as they will be closed automatically in document click listener
-                        var closeAll = (this.options.toggle !== 'click');
-                        this.toggle(closeAll);
-                    }
-                }, this));
-            } else {
-                this.$element.attr('tabindex', -1); //do not stop focus on element when toggled manually
-            }
-            
-            //if display is function it's far more convinient to have autotext = always to render correctly on init
-            //see https://github.com/vitalets/x-editable-yii/issues/34
-            if(typeof this.options.display === 'function') {
-                this.options.autotext = 'always';
-            }
-            
-            //check conditions for autotext:
-            switch(this.options.autotext) {
-              case 'always':
-               doAutotext = true;
-              break;
-              case 'auto':
-                //if element text is empty and value is defined and value not generated by text --> run autotext
-                doAutotext = !$.trim(this.$element.text()).length && this.value !== null && this.value !== undefined && !isValueByText;
-              break;
-              default:
-               doAutotext = false;
-            }
-
-            //depending on autotext run render() or just finilize init
-            $.when(doAutotext ? this.render() : true).then($.proxy(function() {
-                if(this.options.disabled) {
-                    this.disable();
-                } else {
-                    this.enable(); 
-                }
-               /**        
-               Fired when element was initialized by `$().editable()` method. 
-               Please note that you should setup `init` handler **before** applying `editable`. 
-                              
-               @event init 
-               @param {Object} event event object
-               @param {Object} editable editable instance (as here it cannot accessed via data('editable'))
-               @since 1.2.0
-               @example
-               $('#username').on('init', function(e, editable) {
-                   alert('initialized ' + editable.options.name);
-               });
-               $('#username').editable();
-               **/                  
-                this.$element.triggerHandler('init', this);
-            }, this));
-        },
-
-        /*
-         Initializes parent element for live editables 
-        */
-        initLive: function() {
-           //store selector 
-           var selector = this.options.selector;
-           //modify options for child elements
-           this.options.selector = false; 
-           this.options.autotext = 'never';
-           //listen toggle events
-           this.$element.on(this.options.toggle + '.editable', selector, $.proxy(function(e){
-               var $target = $(e.target);
-               if(!$target.data('editable')) {
-                   //if delegated element initially empty, we need to clear it's text (that was manually set to `empty` by user)
-                   //see https://github.com/vitalets/x-editable/issues/137 
-                   if($target.hasClass(this.options.emptyclass)) {
-                      $target.empty();
-                   }
-                   $target.editable(this.options).trigger(e);
-               }
-           }, this)); 
-        },
-        
-        /*
-        Renders value into element's text.
-        Can call custom display method from options.
-        Can return deferred object.
-        @method render()
-        @param {mixed} response server response (if exist) to pass into display function
-        */          
-        render: function(response) {
-            //do not display anything
-            if(this.options.display === false) {
-                return;
-            }
-
-            //if input has `value2htmlFinal` method, we pass callback in third param to be called when source is loaded
-            if(this.input.value2htmlFinal) {
-                return this.input.value2html(this.value, this.$element[0], this.options.display, response);
-            //if display method defined --> use it
-            } else if(typeof this.options.display === 'function') {
-                return this.options.display.call(this.$element[0], this.value, response);
-            //else use input's original value2html() method
-            } else {
-                return this.input.value2html(this.value, this.$element[0]);
-            }
-        },
-        
-        /**
-        Enables editable
-        @method enable()
-        **/          
-        enable: function() {
-            this.options.disabled = false;
-            this.$element.removeClass('editable-disabled');
-            this.handleEmpty(this.isEmpty);
-            if(this.options.toggle !== 'manual') {
-                if(this.$element.attr('tabindex') === '-1') {    
-                    this.$element.removeAttr('tabindex');                                
-                }
-            }
-        },
-        
-        /**
-        Disables editable
-        @method disable()
-        **/         
-        disable: function() {
-            this.options.disabled = true; 
-            this.hide();           
-            this.$element.addClass('editable-disabled');
-            this.handleEmpty(this.isEmpty);
-            //do not stop focus on this element
-            this.$element.attr('tabindex', -1);                
-        },
-        
-        /**
-        Toggles enabled / disabled state of editable element
-        @method toggleDisabled()
-        **/         
-        toggleDisabled: function() {
-            if(this.options.disabled) {
-                this.enable();
-            } else { 
-                this.disable(); 
-            }
-        },  
-        
-        /**
-        Sets new option
-        
-        @method option(key, value)
-        @param {string|object} key option name or object with several options
-        @param {mixed} value option new value
-        @example
-        $('.editable').editable('option', 'pk', 2);
-        **/          
-        option: function(key, value) {
-            //set option(s) by object
-            if(key && typeof key === 'object') {
-               $.each(key, $.proxy(function(k, v){
-                  this.option($.trim(k), v); 
-               }, this)); 
-               return;
-            }
-
-            //set option by string             
-            this.options[key] = value;                          
-            
-            //disabled
-            if(key === 'disabled') {
-               return value ? this.disable() : this.enable();
-            } 
-            
-            //value
-            if(key === 'value') {
-                this.setValue(value);
-            }
-            
-            //transfer new option to container! 
-            if(this.container) {
-                this.container.option(key, value);  
-            }
-             
-            //pass option to input directly (as it points to the same in form)
-            if(this.input.option) {
-                this.input.option(key, value);
-            }
-            
-        },              
-        
-        /*
-        * set emptytext if element is empty
-        */
-        handleEmpty: function (isEmpty) {
-            //do not handle empty if we do not display anything
-            if(this.options.display === false) {
-                return;
-            }
-
-            /* 
-            isEmpty may be set directly as param of method.
-            It is required when we enable/disable field and can't rely on content 
-            as node content is text: "Empty" that is not empty %)
-            */
-            if(isEmpty !== undefined) { 
-                this.isEmpty = isEmpty;
-            } else {
-                //detect empty
-                //for some inputs we need more smart check
-                //e.g. wysihtml5 may have <br>, <p></p>, <img>
-                if(typeof(this.input.isEmpty) === 'function') {
-                    this.isEmpty = this.input.isEmpty(this.$element);                    
-                } else {
-                    this.isEmpty = $.trim(this.$element.html()) === '';
-                }
-            }           
-            
-            //emptytext shown only for enabled
-            if(!this.options.disabled) {
-                if (this.isEmpty) {
-                    this.$element.html(this.options.emptytext);
-                    if(this.options.emptyclass) {
-                        this.$element.addClass(this.options.emptyclass);
-                    }
-                } else if(this.options.emptyclass) {
-                    this.$element.removeClass(this.options.emptyclass);
-                }
-            } else {
-                //below required if element disable property was changed
-                if(this.isEmpty) {
-                    this.$element.empty();
-                    if(this.options.emptyclass) {
-                        this.$element.removeClass(this.options.emptyclass);
-                    }
-                }
-            }
-        },        
-        
-        /**
-        Shows container with form
-        @method show()
-        @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
-        **/  
-        show: function (closeAll) {
-            if(this.options.disabled) {
-                return;
-            }
-            
-            //init editableContainer: popover, tooltip, inline, etc..
-            if(!this.container) {
-                var containerOptions = $.extend({}, this.options, {
-                    value: this.value,
-                    input: this.input //pass input to form (as it is already created)
-                });
-                this.$element.editableContainer(containerOptions);
-                //listen `save` event 
-                this.$element.on("save.internal", $.proxy(this.save, this));
-                this.container = this.$element.data('editableContainer'); 
-            } else if(this.container.tip().is(':visible')) {
-                return;
-            }      
-            
-            //show container
-            this.container.show(closeAll);
-        },
-        
-        /**
-        Hides container with form
-        @method hide()
-        **/       
-        hide: function () {   
-            if(this.container) {  
-                this.container.hide();
-            }
-        },
-        
-        /**
-        Toggles container visibility (show / hide)
-        @method toggle()
-        @param {boolean} closeAll Whether to close all other editable containers when showing this one. Default true.
-        **/  
-        toggle: function(closeAll) {
-            if(this.container && this.container.tip().is(':visible')) {
-                this.hide();
-            } else {
-                this.show(closeAll);
-            }
-        },
-        
-        /*
-        * called when form was submitted
-        */          
-        save: function(e, params) {
-            //mark element with unsaved class if needed
-            if(this.options.unsavedclass) {
-                /*
-                 Add unsaved css to element if:
-                  - url is not user's function 
-                  - value was not sent to server
-                  - params.response === undefined, that means data was not sent
-                  - value changed 
-                */
-                var sent = false;
-                sent = sent || typeof this.options.url === 'function';
-                sent = sent || this.options.display === false; 
-                sent = sent || params.response !== undefined; 
-                sent = sent || (this.options.savenochange && this.input.value2str(this.value) !== this.input.value2str(params.newValue)); 
-                
-                if(sent) {
-                    this.$element.removeClass(this.options.unsavedclass); 
-                } else {
-                    this.$element.addClass(this.options.unsavedclass);                    
-                }
-            }
-            
-            //highlight when saving
-            if(this.options.highlight) {
-                var $e = this.$element,
-                    bgColor = $e.css('background-color');
-                    
-                $e.css('background-color', this.options.highlight);
-                setTimeout(function(){
-                    if(bgColor === 'transparent') {
-                        bgColor = ''; 
-                    }
-                    $e.css('background-color', bgColor);
-                    $e.addClass('editable-bg-transition');
-                    setTimeout(function(){
-                       $e.removeClass('editable-bg-transition');  
-                    }, 1700);
-                }, 10);
-            }
-            
-            //set new value
-            this.setValue(params.newValue, false, params.response);
-            
-            /**        
-            Fired when new value was submitted. You can use <code>$(this).data('editable')</code> to access to editable instance
-            
-            @event save 
-            @param {Object} event event object
-            @param {Object} params additional params
-            @param {mixed} params.newValue submitted value
-            @param {Object} params.response ajax response
-            @example
-            $('#username').on('save', function(e, params) {
-                alert('Saved value: ' + params.newValue);
-            });
-            **/
-            //event itself is triggered by editableContainer. Description here is only for documentation              
-        },
-
-        validate: function () {
-            if (typeof this.options.validate === 'function') {
-                return this.options.validate.call(this, this.value);
-            }
-        },
-        
-        /**
-        Sets new value of editable
-        @method setValue(value, convertStr)
-        @param {mixed} value new value 
-        @param {boolean} convertStr whether to convert value from string to internal format
-        **/         
-        setValue: function(value, convertStr, response) {
-            if(convertStr) {
-                this.value = this.input.str2value(value);
-            } else {
-                this.value = value;
-            }
-            if(this.container) {
-                this.container.option('value', this.value);
-            }
-            $.when(this.render(response))
-            .then($.proxy(function() {
-                this.handleEmpty();
-            }, this));
-        },
-        
-        /**
-        Activates input of visible container (e.g. set focus)
-        @method activate()
-        **/         
-        activate: function() {
-            if(this.container) {
-               this.container.activate(); 
-            }
-        },
-        
-        /**
-        Removes editable feature from element
-        @method destroy()
-        **/        
-        destroy: function() {
-            this.disable();
-            
-            if(this.container) {
-               this.container.destroy(); 
-            }
-            
-            this.input.destroy();
-
-            if(this.options.toggle !== 'manual') {
-                this.$element.removeClass('editable-click');
-                this.$element.off(this.options.toggle + '.editable');
-            } 
-            
-            this.$element.off("save.internal");
-            
-            this.$element.removeClass('editable editable-open editable-disabled');
-            this.$element.removeData('editable');
-        }        
-    };
-
-    /* EDITABLE PLUGIN DEFINITION
-    * ======================= */
-
-    /**
-    jQuery method to initialize editable element.
-    
-    @method $().editable(options)
-    @params {Object} options
-    @example
-    $('#username').editable({
-        type: 'text',
-        url: '/post',
-        pk: 1
-    });
-    **/
-    $.fn.editable = function (option) {
-        //special API methods returning non-jquery object
-        var result = {}, args = arguments, datakey = 'editable';
-        switch (option) {
-            /**
-            Runs client-side validation for all matched editables
-            
-            @method validate()
-            @returns {Object} validation errors map
-            @example
-            $('#username, #fullname').editable('validate');
-            // possible result:
-            {
-              username: "username is required",
-              fullname: "fullname should be minimum 3 letters length"
-            }
-            **/
-            case 'validate':
-                this.each(function () {
-                    var $this = $(this), data = $this.data(datakey), error;
-                    if (data && (error = data.validate())) {
-                        result[data.options.name] = error;
-                    }
-                });
-            return result;
-
-            /**
-            Returns current values of editable elements.   
-            Note that it returns an **object** with name-value pairs, not a value itself. It allows to get data from several elements.    
-            If value of some editable is `null` or `undefined` it is excluded from result object.
-            When param `isSingle` is set to **true** - it is supposed you have single element and will return value of editable instead of object.   
-             
-            @method getValue()
-            @param {bool} isSingle whether to return just value of single element
-            @returns {Object} object of element names and values
-            @example
-            $('#username, #fullname').editable('getValue');
-            //result:
-            {
-            username: "superuser",
-            fullname: "John"
-            }
-            //isSingle = true
-            $('#username').editable('getValue', true);
-            //result "superuser" 
-            **/
-            case 'getValue':
-                if(arguments.length === 2 && arguments[1] === true) { //isSingle = true
-                    result = this.eq(0).data(datakey).value;
-                } else {
-                    this.each(function () {
-                        var $this = $(this), data = $this.data(datakey);
-                        if (data && data.value !== undefined && data.value !== null) {
-                            result[data.options.name] = data.input.value2submit(data.value);
-                        }
-                    });
-                }
-            return result;
-
-            /**
-            This method collects values from several editable elements and submit them all to server.   
-            Internally it runs client-side validation for all fields and submits only in case of success.  
-            See <a href="#newrecord">creating new records</a> for details.  
-            Since 1.5.1 `submit` can be applied to single element to send data programmatically. In that case
-            `url`, `success` and `error` is taken from initial options and you can just call `$('#username').editable('submit')`. 
-            
-            @method submit(options)
-            @param {object} options 
-            @param {object} options.url url to submit data 
-            @param {object} options.data additional data to submit
-            @param {object} options.ajaxOptions additional ajax options
-            @param {function} options.error(obj) error handler 
-            @param {function} options.success(obj,config) success handler
-            @returns {Object} jQuery object
-            **/
-            case 'submit':  //collects value, validate and submit to server for creating new record
-                var config = arguments[1] || {},
-                $elems = this,
-                errors = this.editable('validate');
-
-                // validation ok
-                if($.isEmptyObject(errors)) {
-                    var ajaxOptions = {};
-                                                      
-                    // for single element use url, success etc from options
-                    if($elems.length === 1) {
-                        var editable = $elems.data('editable');
-                        //standard params
-                        var params = {
-                            name: editable.options.name || '',
-                            value: editable.input.value2submit(editable.value),
-                            pk: (typeof editable.options.pk === 'function') ? 
-                                editable.options.pk.call(editable.options.scope) : 
-                                editable.options.pk 
-                        };
-
-                        //additional params
-                        if(typeof editable.options.params === 'function') {
-                            params = editable.options.params.call(editable.options.scope, params);  
-                        } else {
-                            //try parse json in single quotes (from data-params attribute)
-                            editable.options.params = $.fn.editableutils.tryParseJson(editable.options.params, true);   
-                            $.extend(params, editable.options.params);
-                        }
-
-                        ajaxOptions = {
-                            url: editable.options.url,
-                            data: params,
-                            type: 'POST'  
-                        };
-                        
-                        // use success / error from options 
-                        config.success = config.success || editable.options.success;
-                        config.error = config.error || editable.options.error;
-                        
-                    // multiple elements
-                    } else {
-                        var values = this.editable('getValue'); 
-                        
-                        ajaxOptions = {
-                            url: config.url,
-                            data: values, 
-                            type: 'POST'
-                        };                        
-                    }                    
-
-                    // ajax success callabck (response 200 OK)
-                    ajaxOptions.success = typeof config.success === 'function' ? function(response) {
-                            config.success.call($elems, response, config);
-                        } : $.noop;
-                                  
-                    // ajax error callabck
-                    ajaxOptions.error = typeof config.error === 'function' ? function() {
-                             config.error.apply($elems, arguments);
-                        } : $.noop;
-                       
-                    // extend ajaxOptions    
-                    if(config.ajaxOptions) { 
-                        $.extend(ajaxOptions, config.ajaxOptions);
-                    }
-                    
-                    // extra data 
-                    if(config.data) {
-                        $.extend(ajaxOptions.data, config.data);
-                    }                     
-                    
-                    // perform ajax request
-                    $.ajax(ajaxOptions);
-                } else { //client-side validation error
-                    if(typeof config.error === 'function') {
-                        config.error.call($elems, errors);
-                    }
-                }
-            return this;
-        }
-
-        //return jquery object
-        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 
-                data[option].apply(data, Array.prototype.slice.call(args, 1));
-            } 
-        });
-    };    
-            
-
-    $.fn.editable.defaults = {
-        /**
-        Type of input. Can be <code>text|textarea|select|date|checklist</code> and more
-
-        @property type 
-        @type string
-        @default 'text'
-        **/
-        type: 'text',        
-        /**
-        Sets disabled state of editable
-
-        @property disabled 
-        @type boolean
-        @default false
-        **/         
-        disabled: false,
-        /**
-        How to toggle editable. Can be <code>click|dblclick|mouseenter|manual</code>.   
-        When set to <code>manual</code> you should manually call <code>show/hide</code> methods of editable.    
-        **Note**: if you call <code>show</code> or <code>toggle</code> inside **click** handler of some DOM element, 
-        you need to apply <code>e.stopPropagation()</code> because containers are being closed on any click on document.
-        
-        @example
-        $('#edit-button').click(function(e) {
-            e.stopPropagation();
-            $('#username').editable('toggle');
-        });
-
-        @property toggle 
-        @type string
-        @default 'click'
-        **/          
-        toggle: 'click',
-        /**
-        Text shown when element is empty.
-
-        @property emptytext 
-        @type string
-        @default 'Empty'
-        **/         
-        emptytext: 'Empty',
-        /**
-        Allows to automatically set element's text based on it's value. Can be <code>auto|always|never</code>. Useful for select and date.
-        For example, if dropdown list is <code>{1: 'a', 2: 'b'}</code> and element's value set to <code>1</code>, it's html will be automatically set to <code>'a'</code>.  
-        <code>auto</code> - text will be automatically set only if element is empty.  
-        <code>always|never</code> - always(never) try to set element's text.
-
-        @property autotext 
-        @type string
-        @default 'auto'
-        **/          
-        autotext: 'auto', 
-        /**
-        Initial value of input. If not set, taken from element's text.  
-        Note, that if element's text is empty - text is automatically generated from value and can be customized (see `autotext` option).  
-        For example, to display currency sign:
-        @example
-        <a id="price" data-type="text" data-value="100"></a>
-        <script>
-        $('#price').editable({
-            ...
-            display: function(value) {
-              $(this).text(value + '$');
-            } 
-        }) 
-        </script>
-                
-        @property value 
-        @type mixed
-        @default element's text
-        **/
-        value: null,
-        /**
-        Callback to perform custom displaying of value in element's text.  
-        If `null`, default input's display used.  
-        If `false`, no displaying methods will be called, element's text will never change.  
-        Runs under element's scope.  
-        _**Parameters:**_  
-        
-        * `value` current value to be displayed
-        * `response` server response (if display called after ajax submit), since 1.4.0
-         
-        For _inputs with source_ (select, checklist) parameters are different:  
-          
-        * `value` current value to be displayed
-        * `sourceData` array of items for current input (e.g. dropdown items) 
-        * `response` server response (if display called after ajax submit), since 1.4.0
-                  
-        To get currently selected items use `$.fn.editableutils.itemsByValue(value, sourceData)`.
-        
-        @property display 
-        @type function|boolean
-        @default null
-        @since 1.2.0
-        @example
-        display: function(value, sourceData) {
-           //display checklist as comma-separated values
-           var html = [],
-               checked = $.fn.editableutils.itemsByValue(value, sourceData);
-               
-           if(checked.length) {
-               $.each(checked, function(i, v) { html.push($.fn.editableutils.escape(v.text)); });
-               $(this).html(html.join(', '));
-           } else {
-               $(this).empty(); 
-           }
-        }
-        **/          
-        display: null,
-        /**
-        Css class applied when editable text is empty.
-
-        @property emptyclass 
-        @type string
-        @since 1.4.1        
-        @default editable-empty
-        **/        
-        emptyclass: 'editable-empty',
-        /**
-        Css class applied when value was stored but not sent to server (`pk` is empty or `send = 'never'`).  
-        You may set it to `null` if you work with editables locally and submit them together.  
-
-        @property unsavedclass 
-        @type string
-        @since 1.4.1        
-        @default editable-unsaved
-        **/        
-        unsavedclass: 'editable-unsaved',
-        /**
-        If selector is provided, editable will be delegated to the specified targets.  
-        Usefull for dynamically generated DOM elements.  
-        **Please note**, that delegated targets can't be initialized with `emptytext` and `autotext` options, 
-        as they actually become editable only after first click.  
-        You should manually set class `editable-click` to these elements.  
-        Also, if element originally empty you should add class `editable-empty`, set `data-value=""` and write emptytext into element:
-
-        @property selector 
-        @type string
-        @since 1.4.1        
-        @default null
-        @example
-        <div id="user">
-          <!-- empty -->
-          <a href="#" data-name="username" data-type="text" class="editable-click editable-empty" data-value="" title="Username">Empty</a>
-          <!-- non-empty -->
-          <a href="#" data-name="group" data-type="select" data-source="/groups" data-value="1" class="editable-click" title="Group">Operator</a>
-        </div>     
-        
-        <script>
-        $('#user').editable({
-            selector: 'a',
-            url: '/post',
-            pk: 1
-        });
-        </script>
-        **/         
-        selector: null,
-        /**
-        Color used to highlight element after update. Implemented via CSS3 transition, works in modern browsers.
-        
-        @property highlight 
-        @type string|boolean
-        @since 1.4.5        
-        @default #FFFF80 
-        **/
-        highlight: '#FFFF80'
-    };
-    
-}(window.jQuery));
-
-/**
-AbstractInput - base class for all editable inputs.
-It defines interface to be implemented by any input type.
-To create your own input you can inherit from this class.
-
-@class abstractinput
-**/
-(function ($) {
-    "use strict";
-
-    //types
-    $.fn.editabletypes = {};
-
-    var AbstractInput = function () { };
-
-    AbstractInput.prototype = {
-       /**
-        Initializes input
-
-        @method init() 
-        **/
-       init: function(type, options, defaults) {
-           this.type = type;
-           this.options = $.extend({}, defaults, options);
-       },
-
-       /*
-       this method called before render to init $tpl that is inserted in DOM
-       */
-       prerender: function() {
-           this.$tpl = $(this.options.tpl); //whole tpl as jquery object    
-           this.$input = this.$tpl;         //control itself, can be changed in render method
-           this.$clear = null;              //clear button
-           this.error = null;               //error message, if input cannot be rendered           
-       },
-       
-       /**
-        Renders input from tpl. Can return jQuery deferred object.
-        Can be overwritten in child objects
-
-        @method render()
-       **/
-       render: function() {
-
-       }, 
-
-       /**
-        Sets element's html by value. 
-
-        @method value2html(value, element)
-        @param {mixed} value
-        @param {DOMElement} element
-       **/
-       value2html: function(value, element) {
-           $(element)[this.options.escape ? 'text' : 'html']($.trim(value));
-       },
-
-       /**
-        Converts element's html to value
-
-        @method html2value(html)
-        @param {string} html
-        @returns {mixed}
-       **/
-       html2value: function(html) {
-           return $('<div>').html(html).text();
-       },
-
-       /**
-        Converts value to string (for internal compare). For submitting to server used value2submit().
-
-        @method value2str(value) 
-        @param {mixed} value
-        @returns {string}
-       **/
-       value2str: function(value) {
-           return value;
-       }, 
-
-       /**
-        Converts string received from server into value. Usually from `data-value` attribute.
-
-        @method str2value(str)
-        @param {string} str
-        @returns {mixed}
-       **/
-       str2value: function(str) {
-           return str;
-       }, 
-       
-       /**
-        Converts value for submitting to server. Result can be string or object.
-
-        @method value2submit(value) 
-        @param {mixed} value
-        @returns {mixed}
-       **/
-       value2submit: function(value) {
-           return value;
-       },
-
-       /**
-        Sets value of input.
-
-        @method value2input(value) 
-        @param {mixed} value
-       **/
-       value2input: function(value) {
-           this.$input.val(value);
-       },
-
-       /**
-        Returns value of input. Value can be object (e.g. datepicker)
-
-        @method input2value() 
-       **/
-       input2value: function() { 
-           return this.$input.val();
-       }, 
-
-       /**
-        Activates input. For text it sets focus.
-
-        @method activate() 
-       **/
-       activate: function() {
-           if(this.$input.is(':visible')) {
-               this.$input.focus();
-           }
-       },
-
-       /**
-        Creates input.
-
-        @method clear() 
-       **/        
-       clear: function() {
-           this.$input.val(null);
-       },
-
-       /**
-        method to escape html.
-       **/
-       escape: function(str) {
-           return $('<div>').text(str).html();
-       },
-       
-       /**
-        attach handler to automatically submit form when value changed (useful when buttons not shown)
-       **/
-       autosubmit: function() {
-        
-       },
-       
-       /**
-       Additional actions when destroying element 
-       **/
-       destroy: function() {
-       },
-
-       // -------- helper functions --------
-       setClass: function() {          
-           if(this.options.inputclass) {
-               this.$input.addClass(this.options.inputclass); 
-           } 
-       },
-
-       setAttr: function(attr) {
-           if (this.options[attr] !== undefined && this.options[attr] !== null) {
-               this.$input.attr(attr, this.options[attr]);
-           } 
-       },
-       
-       option: function(key, value) {
-            this.options[key] = value;
-       }
-       
-    };
-        
-    AbstractInput.defaults = {  
-        /**
-        HTML template of input. Normally you should not change it.
-
-        @property tpl 
-        @type string
-        @default ''
-        **/   
-        tpl: '',
-        /**
-        CSS class automatically applied to input
-        
-        @property inputclass 
-        @type string
-        @default null
-        **/         
-        inputclass: null,
-        
-        /**
-        If `true` - html will be escaped in content of element via $.text() method.  
-        If `false` - html will not be escaped, $.html() used.  
-        When you use own `display` function, this option obviosly has no effect.
-        
-        @property escape 
-        @type boolean
-        @since 1.5.0
-        @default true
-        **/         
-        escape: true,
-                
-        //scope for external methods (e.g. source defined as function)
-        //for internal use only
-        scope: null,
-        
-        //need to re-declare showbuttons here to get it's value from common config (passed only options existing in defaults)
-        showbuttons: true 
-    };
-    
-    $.extend($.fn.editabletypes, {abstractinput: AbstractInput});
-        
-}(window.jQuery));
-
-/**
-List - abstract class for inputs that have source option loaded from js array or via ajax
-
-@class list
-@extends abstractinput
-**/
-(function ($) {
-    "use strict";
-    
-    var List = function (options) {
-       
-    };
-
-    $.fn.editableutils.inherit(List, $.fn.editabletypes.abstractinput);
-
-    $.extend(List.prototype, {
-        render: function () {
-
-            var deferred = $.Deferred();
-
-            this.error = null;
-            this.onSourceReady(function () {
-                this.renderList();
-                deferred.resolve();
-            }, function () {
-                this.error = this.options.sourceError;
-                deferred.resolve();
-            });
-
-            return deferred.promise();
-        },
-
-        html2value: function (html) {
-            return null; //can't set value by text
-        },
-        
-        value2html: function (value, element, display, response) {
-            var deferred = $.Deferred(),
-                success = function () {
-                    if(typeof display === 'function') {
-                        //custom display method
-                        display.call(element, value, this.sourceData, response); 
-                    } else {
-                        this.value2htmlFinal(value, element);
-                    }
-                    deferred.resolve();
-               };
-            
-            //for null value just call success without loading source
-            if(value === null) {
-               success.call(this);   
-            } else {
-               this.onSourceReady(success, function () { deferred.resolve(); });
-            }
-
-            return deferred.promise();
-        },  
-
-        // ------------- additional functions ------------
-
-        onSourceReady: function (success, error) {
-            //run source if it function
-            var source;
-            if ($.isFunction(this.options.source)) {
-                source = this.options.source.call(this.options.scope);
-                this.sourceData = null;
-                //note: if function returns the same source as URL - sourceData will be taken from cahce and no extra request performed
-            } else {
-                source = this.options.source;
-            }            
-            
-            //if allready loaded just call success
-            if(this.options.sourceCache && $.isArray(this.sourceData)) {
-                success.call(this);
-                return; 
-            }
-
-            //try parse json in single quotes (for double quotes jquery does automatically)
-            try {
-                source = $.fn.editableutils.tryParseJson(source, false);
-            } catch (e) {
-                error.call(this);
-                return;
-            }
-
-            //loading from url
-            if (typeof source === 'string') {
-                //try to get sourceData from cache
-                if(this.options.sourceCache) {
-                    var cacheID = source,
-                    cache;
-
-                    if (!$(document).data(cacheID)) {
-                        $(document).data(cacheID, {});
-                    }
-                    cache = $(document).data(cacheID);
-
-                    //check for cached data
-                    if (cache.loading === false && cache.sourceData) { //take source from cache
-                        this.sourceData = cache.sourceData;
-                        this.doPrepend();
-                        success.call(this);
-                        return;
-                    } else if (cache.loading === true) { //cache is loading, put callback in stack to be called later
-                        cache.callbacks.push($.proxy(function () {
-                            this.sourceData = cache.sourceData;
-                            this.doPrepend();
-                            success.call(this);
-                        }, this));
-
-                        //also collecting error callbacks
-                        cache.err_callbacks.push($.proxy(error, this));
-                        return;
-                    } else { //no cache yet, activate it
-                        cache.loading = true;
-                        cache.callbacks = [];
-                        cache.err_callbacks = [];
-                    }
-                }
-                
-                //ajaxOptions for source. Can be overwritten bt options.sourceOptions
-                var ajaxOptions = $.extend({
-                    url: source,
-                    type: 'get',
-                    cache: false,
-                    dataType: 'json',
-                    success: $.proxy(function (data) {
-                        if(cache) {
-                            cache.loading = false;
-                        }
-                        this.sourceData = this.makeArray(data);
-                        if($.isArray(this.sourceData)) {
-                            if(cache) {
-                                //store result in cache
-                                cache.sourceData = this.sourceData;
-                                //run success callbacks for other fields waiting for this source
-                                $.each(cache.callbacks, function () { this.call(); }); 
-                            }
-                            this.doPrepend();
-                            success.call(this);
-                        } else {
-                            error.call(this);
-                            if(cache) {
-                                //run error callbacks for other fields waiting for this source
-                                $.each(cache.err_callbacks, function () { this.call(); }); 
-                            }
-                        }
-                    }, this),
-                    error: $.proxy(function () {
-                        error.call(this);
-                        if(cache) {
-                             cache.loading = false;
-                             //run error callbacks for other fields
-                             $.each(cache.err_callbacks, function () { this.call(); }); 
-                        }
-                    }, this)
-                }, this.options.sourceOptions);
-                
-                //loading sourceData from server
-                $.ajax(ajaxOptions);
-                
-            } else { //options as json/array
-                this.sourceData = this.makeArray(source);
-                    
-                if($.isArray(this.sourceData)) {
-                    this.doPrepend();
-                    success.call(this);   
-                } else {
-                    error.call(this);
-                }
-            }
-        },
-
-        doPrepend: function () {
-            if(this.options.prepend === null || this.options.prepend === undefined) {
-                return;  
-            }
-            
-            if(!$.isArray(this.prependData)) {
-                //run prepend if it is function (once)
-                if ($.isFunction(this.options.prepend)) {
-                    this.options.prepend = this.options.prepend.call(this.options.scope);
-                }
-              
-                //try parse json in single quotes
-                this.options.prepend = $.fn.editableutils.tryParseJson(this.options.prepend, true);
-                
-                //convert prepend from string to object
-                if (typeof this.options.prepend === 'string') {
-                    this.options.prepend = {'': this.options.prepend};
-                }
-                
-                this.prependData = this.makeArray(this.options.prepend);
-            }
-
-            if($.isArray(this.prependData) && $.isArray(this.sourceData)) {
-                this.sourceData = this.prependData.concat(this.sourceData);
-            }
-        },
-
-        /*
-         renders input list
-        */
-        renderList: function() {
-            // this method should be overwritten in child class
-        },
-       
-         /*
-         set element's html by value
-        */
-        value2htmlFinal: function(value, element) {
-            // this method should be overwritten in child class
-        },        
-
-        /**
-        * convert data to array suitable for sourceData, e.g. [{value: 1, text: 'abc'}, {...}]
-        */
-        makeArray: function(data) {
-            var count, obj, result = [], item, iterateItem;
-            if(!data || typeof data === 'string') {
-                return null; 
-            }
-
-            if($.isArray(data)) { //array
-                /* 
-                   function to iterate inside item of array if item is object.
-                   Caclulates count of keys in item and store in obj. 
-                */
-                iterateItem = function (k, v) {
-                    obj = {value: k, text: v};
-                    if(count++ >= 2) {
-                        return false;// exit from `each` if item has more than one key.
-                    }
-                };
-            
-                for(var i = 0; i < data.length; i++) {
-                    item = data[i]; 
-                    if(typeof item === 'object') {
-                        count = 0; //count of keys inside item
-                        $.each(item, iterateItem);
-                        //case: [{val1: 'text1'}, {val2: 'text2} ...]
-                        if(count === 1) { 
-                            result.push(obj); 
-                            //case: [{value: 1, text: 'text1'}, {value: 2, text: 'text2'}, ...]
-                        } else if(count > 1) {
-                            //removed check of existance: item.hasOwnProperty('value') && item.hasOwnProperty('text')
-                            if(item.children) {
-                                item.children = this.makeArray(item.children);   
-                            }
-                            result.push(item);
-                        }
-                    } else {
-                        //case: ['text1', 'text2' ...]
-                        result.push({value: item, text: item}); 
-                    }
-                }
-            } else {  //case: {val1: 'text1', val2: 'text2, ...}
-                $.each(data, function (k, v) {
-                    result.push({value: k, text: v});
-                });  
-            }
-            return result;
-        },
-        
-        option: function(key, value) {
-            this.options[key] = value;
-            if(key === 'source') {
-                this.sourceData = null;
-            }
-            if(key === 'prepend') {
-                this.prependData = null;
-            }            
-        }        
-
-    });      
-
-    List.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
-        /**
-        Source data for list.  
-        If **array** - it should be in format: `[{value: 1, text: "text1"}, {value: 2, text: "text2"}, ...]`  
-        For compability, object format is also supported: `{"1": "text1", "2": "text2" ...}` but it does not guarantee elements order.
-        
-        If **string** - considered ajax url to load items. In that case results will be cached for fields with the same source and name. See also `sourceCache` option.
-          
-        If **function**, it should return data in format above (since 1.4.0).
-        
-        Since 1.4.1 key `children` supported to render OPTGROUP (for **select** input only).  
-        `[{text: "group1", children: [{value: 1, text: "text1"}, {value: 2, text: "text2"}]}, ...]` 
-
-		
-        @property source 
-        @type string | array | object | function
-        @default null
-        **/         
-        source: null, 
-        /**
-        Data automatically prepended to the beginning of dropdown list.
-        
-        @property prepend 
-        @type string | array | object | function
-        @default false
-        **/         
-        prepend: false,
-        /**
-        Error message when list cannot be loaded (e.g. ajax error)
-        
-        @property sourceError 
-        @type string
-        @default Error when loading list
-        **/          
-        sourceError: 'Error when loading list',
-        /**
-        if <code>true</code> and source is **string url** - results will be cached for fields with the same source.    
-        Usefull for editable column in grid to prevent extra requests.
-        
-        @property sourceCache 
-        @type boolean
-        @default true
-        @since 1.2.0
-        **/        
-        sourceCache: true,
-        /**
-        Additional ajax options to be used in $.ajax() when loading list from server.
-        Useful to send extra parameters (`data` key) or change request method (`type` key).
-        
-        @property sourceOptions 
-        @type object|function
-        @default null
-        @since 1.5.0
-        **/        
-        sourceOptions: null
-    });
-
-    $.fn.editabletypes.list = List;      
-
-}(window.jQuery));
-
-/**
-Text input
-
-@class text
-@extends abstractinput
-@final
-@example
-<a href="#" id="username" data-type="text" data-pk="1">awesome</a>
-<script>
-$(function(){
-    $('#username').editable({
-        url: '/post',
-        title: 'Enter username'
-    });
-});
-</script>
-**/
-(function ($) {
-    "use strict";
-    
-    var Text = function (options) {
-        this.init('text', options, Text.defaults);
-    };
-
-    $.fn.editableutils.inherit(Text, $.fn.editabletypes.abstractinput);
-
-    $.extend(Text.prototype, {
-        render: function() {
-           this.renderClear();
-           this.setClass();
-           this.setAttr('placeholder');
-        },
-        
-        activate: function() {
-            if(this.$input.is(':visible')) {
-                this.$input.focus();
-                $.fn.editableutils.setCursorPosition(this.$input.get(0), this.$input.val().length);
-                if(this.toggleClear) {
-                    this.toggleClear();
-                }
-            }
-        },
-        
-        //render clear button
-        renderClear:  function() {
-           if (this.options.clear) {
-               this.$clear = $('<span class="editable-clear-x"></span>');
-               this.$input.after(this.$clear)
-                          .css('padding-right', 24)
-                          .keyup($.proxy(function(e) {
-                              //arrows, enter, tab, etc
-                              if(~$.inArray(e.keyCode, [40,38,9,13,27])) {
-                                return;
-                              }                            
-
-                              clearTimeout(this.t);
-                              var that = this;
-                              this.t = setTimeout(function() {
-                                that.toggleClear(e);
-                              }, 100);
-                              
-                          }, this))
-                          .parent().css('position', 'relative');
-                          
-               this.$clear.click($.proxy(this.clear, this));                       
-           }            
-        },
-        
-        postrender: function() {
-            /*
-            //now `clear` is positioned via css
-            if(this.$clear) {
-                //can position clear button only here, when form is shown and height can be calculated
-//                var h = this.$input.outerHeight(true) || 20,
-                var h = this.$clear.parent().height(),
-                    delta = (h - this.$clear.height()) / 2;
-                    
-                //this.$clear.css({bottom: delta, right: delta});
-            }
-            */ 
-        },
-        
-        //show / hide clear button
-        toggleClear: function(e) {
-            if(!this.$clear) {
-                return;
-            }
-            
-            var len = this.$input.val().length,
-                visible = this.$clear.is(':visible');
-                 
-            if(len && !visible) {
-                this.$clear.show();
-            } 
-            
-            if(!len && visible) {
-                this.$clear.hide();
-            } 
-        },
-        
-        clear: function() {
-           this.$clear.hide();
-           this.$input.val('').focus();
-        }          
-    });
-
-    Text.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
-        /**
-        @property tpl 
-        @default <input type="text">
-        **/         
-        tpl: '<input type="text">',
-        /**
-        Placeholder attribute of input. Shown when input is empty.
-
-        @property placeholder 
-        @type string
-        @default null
-        **/             
-        placeholder: null,
-        
-        /**
-        Whether to show `clear` button 
-        
-        @property clear 
-        @type boolean
-        @default true        
-        **/
-        clear: true
-    });
-
-    $.fn.editabletypes.text = Text;
-
-}(window.jQuery));
-
-/**
-Textarea input
-
-@class textarea
-@extends abstractinput
-@final
-@example
-<a href="#" id="comments" data-type="textarea" data-pk="1">awesome comment!</a>
-<script>
-$(function(){
-    $('#comments').editable({
-        url: '/post',
-        title: 'Enter comments',
-        rows: 10
-    });
-});
-</script>
-**/
-(function ($) {
-    "use strict";
-    
-    var Textarea = function (options) {
-        this.init('textarea', options, Textarea.defaults);
-    };
-
-    $.fn.editableutils.inherit(Textarea, $.fn.editabletypes.abstractinput);
-
-    $.extend(Textarea.prototype, {
-        render: function () {
-            this.setClass();
-            this.setAttr('placeholder');
-            this.setAttr('rows');                        
-            
-            //ctrl + enter
-            this.$input.keydown(function (e) {
-                if (e.ctrlKey && e.which === 13) {
-                    $(this).closest('form').submit();
-                }
-            });
-        },
-        
-       //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 '';
-            }
-
-            var regex = new RegExp(String.fromCharCode(10), 'g');
-            var lines = html.split(/<br\s*\/?>/i);
-            for (var i = 0; i < lines.length; i++) {
-                var text = $('<div>').html(lines[i]).text();
-
-                // Remove newline characters (\n) to avoid them being converted by value2html() method
-                // thus adding extra <br> tags
-                text = text.replace(regex, '');
-
-                lines[i] = text;
-            }
-            return lines.join("\n");
-        },
-         */
-        activate: function() {
-            $.fn.editabletypes.text.prototype.activate.call(this);
-        }
-    });
-
-    Textarea.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
-        /**
-        @property tpl
-        @default <textarea></textarea>
-        **/
-        tpl:'<textarea></textarea>',
-        /**
-        @property inputclass
-        @default input-large
-        **/
-        inputclass: 'input-large',
-        /**
-        Placeholder attribute of input. Shown when input is empty.
-
-        @property placeholder
-        @type string
-        @default null
-        **/
-        placeholder: null,
-        /**
-        Number of rows in textarea
-
-        @property rows
-        @type integer
-        @default 7
-        **/        
-        rows: 7        
-    });
-
-    $.fn.editabletypes.textarea = Textarea;
-
-}(window.jQuery));
-
-/**
-Select (dropdown)
-
-@class select
-@extends list
-@final
-@example
-<a href="#" id="status" data-type="select" data-pk="1" data-url="/post" data-title="Select status"></a>
-<script>
-$(function(){
-    $('#status').editable({
-        value: 2,    
-        source: [
-              {value: 1, text: 'Active'},
-              {value: 2, text: 'Blocked'},
-              {value: 3, text: 'Deleted'}
-           ]
-    });
-});
-</script>
-**/
-(function ($) {
-    "use strict";
-    
-    var Select = function (options) {
-        this.init('select', options, Select.defaults);
-    };
-
-    $.fn.editableutils.inherit(Select, $.fn.editabletypes.list);
-
-    $.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) {
-                            attr.label = data[i].text;
-                            $el.append(fillItems($('<optgroup>', attr), data[i].children)); 
-                        } else {
-                            attr.value = data[i].value;
-                            if(data[i].disabled) {
-                                attr.disabled = true;
-                            }
-                            $el.append($('<option>', attr).text(data[i].text)); 
-                        }
-                    }
-                }
-                return $el;
-            };        
-
-            fillItems(this.$input, this.sourceData);
-            
-            this.setClass();
-            
-            //enter submit
-            this.$input.on('keydown.editable', function (e) {
-                if (e.which === 13) {
-                    $(this).closest('form').submit();
-                }
-            });            
-        },
-       
-        value2htmlFinal: function(value, element) {
-            var text = '', 
-                items = $.fn.editableutils.itemsByValue(value, this.sourceData);
-                
-            if(items.length) {
-                text = items[0].text;
-            }
-            
-            //$(element).text(text);
-            $.fn.editabletypes.abstractinput.prototype.value2html.call(this, text, element);
-        },
-        
-        autosubmit: function() {
-            this.$input.off('keydown.editable').on('change.editable', function(){
-                $(this).closest('form').submit();
-            });
-        }
-    });      
-
-    Select.defaults = $.extend({}, $.fn.editabletypes.list.defaults, {
-        /**
-        @property tpl 
-        @default <select></select>
-        **/         
-        tpl:'<select></select>'
-    });
-
-    $.fn.editabletypes.select = Select;      
-
-}(window.jQuery));
-
-/**
-List of checkboxes. 
-Internally value stored as javascript array of values.
-
-@class checklist
-@extends list
-@final
-@example
-<a href="#" id="options" data-type="checklist" data-pk="1" data-url="/post" data-title="Select options"></a>
-<script>
-$(function(){
-    $('#options').editable({
-        value: [2, 3],    
-        source: [
-              {value: 1, text: 'option1'},
-              {value: 2, text: 'option2'},
-              {value: 3, text: 'option3'}
-           ]
-    });
-});
-</script>
-**/
-(function ($) {
-    "use strict";
-    
-    var Checklist = function (options) {
-        this.init('checklist', options, Checklist.defaults);
-    };
-
-    $.fn.editableutils.inherit(Checklist, $.fn.editabletypes.list);
-
-    $.extend(Checklist.prototype, {
-        renderList: function() {
-            var $label, $div;
-            
-            this.$tpl.empty();
-            
-            if(!$.isArray(this.sourceData)) {
-                return;
-            }
-
-            for(var i=0; i<this.sourceData.length; i++) {
-                $label = $('<label>').append($('<input>', {
-                                           type: 'checkbox',
-                                           value: this.sourceData[i].value 
-                                     }))
-                                     .append($('<span>').text(' '+this.sourceData[i].text));
-                
-                $('<div>').append($label).appendTo(this.$tpl);
-            }
-            
-            this.$input = this.$tpl.find('input[type="checkbox"]');
-            this.setClass();
-        },
-       
-       value2str: function(value) {
-           return $.isArray(value) ? value.sort().join($.trim(this.options.separator)) : '';
-       },  
-       
-       //parse separated string
-        str2value: function(str) {
-           var reg, value = null;
-           if(typeof str === 'string' && str.length) {
-               reg = new RegExp('\\s*'+$.trim(this.options.separator)+'\\s*');
-               value = str.split(reg);
-           } else if($.isArray(str)) {
-               value = str; 
-           } else {
-               value = [str];
-           }
-           return value;
-        },       
-       
-       //set checked on required checkboxes
-       value2input: function(value) {
-            this.$input.prop('checked', false);
-            if($.isArray(value) && value.length) {
-               this.$input.each(function(i, el) {
-                   var $el = $(el);
-                   // cannot use $.inArray as it performs strict comparison
-                   $.each(value, function(j, val){
-                       /*jslint eqeq: true*/
-                       if($el.val() == val) {
-                       /*jslint eqeq: false*/                           
-                           $el.prop('checked', true);
-                       }
-                   });
-               }); 
-            }  
-        },  
-        
-       input2value: function() { 
-           var checked = [];
-           this.$input.filter(':checked').each(function(i, el) {
-               checked.push($(el).val());
-           });
-           return checked;
-       },            
-          
-       //collect text of checked boxes
-        value2htmlFinal: function(value, element) {
-           var html = [],
-               checked = $.fn.editableutils.itemsByValue(value, this.sourceData),
-               escape = this.options.escape;
-               
-           if(checked.length) {
-               $.each(checked, function(i, v) {
-                   var text = escape ? $.fn.editableutils.escape(v.text) : v.text; 
-                   html.push(text); 
-               });
-               $(element).html(html.join('<br>'));
-           } else {
-               $(element).empty(); 
-           }
-        },
-        
-       activate: function() {
-           this.$input.first().focus();
-       },
-       
-       autosubmit: function() {
-           this.$input.on('keydown', function(e){
-               if (e.which === 13) {
-                   $(this).closest('form').submit();
-               }
-           });
-       }
-    });      
-
-    Checklist.defaults = $.extend({}, $.fn.editabletypes.list.defaults, {
-        /**
-        @property tpl 
-        @default <div></div>
-        **/         
-        tpl:'<div class="editable-checklist"></div>',
-        
-        /**
-        @property inputclass 
-        @type string
-        @default null
-        **/         
-        inputclass: null,        
-        
-        /**
-        Separator of values when reading from `data-value` attribute
-
-        @property separator 
-        @type string
-        @default ','
-        **/         
-        separator: ','
-    });
-
-    $.fn.editabletypes.checklist = Checklist;      
-
-}(window.jQuery));
-
-/**
-HTML5 input types.
-Following types are supported:
-
-* password
-* 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
-            
-@class html5types 
-@extends text
-@final
-@since 1.3.0
-@example
-<a href="#" id="email" data-type="email" data-pk="1">admin@example.com</a>
-<script>
-$(function(){
-    $('#email').editable({
-        url: '/post',
-        title: 'Enter email'
-    });
-});
-</script>
-**/
-
-/**
-@property tpl 
-@default depends on type
-**/ 
-
-/*
-Password
-*/
-(function ($) {
-    "use strict";
-    
-    var Password = function (options) {
-        this.init('password', options, Password.defaults);
-    };
-    $.fn.editableutils.inherit(Password, $.fn.editabletypes.text);
-    $.extend(Password.prototype, {
-       //do not display password, show '[hidden]' instead
-       value2html: function(value, element) {
-           if(value) {
-               $(element).text('[hidden]');
-           } else {
-               $(element).empty(); 
-           }
-       },
-       //as password not displayed, should not set value by html
-       html2value: function(html) {
-           return null;
-       }       
-    });    
-    Password.defaults = $.extend({}, $.fn.editabletypes.text.defaults, {
-        tpl: '<input type="password">'
-    });
-    $.fn.editabletypes.password = Password;
-}(window.jQuery));
-
-
-/*
-Email
-*/
-(function ($) {
-    "use strict";
-    
-    var Email = function (options) {
-        this.init('email', options, Email.defaults);
-    };
-    $.fn.editableutils.inherit(Email, $.fn.editabletypes.text);
-    Email.defaults = $.extend({}, $.fn.editabletypes.text.defaults, {
-        tpl: '<input type="email">'
-    });
-    $.fn.editabletypes.email = Email;
-}(window.jQuery));
-
-
-/*
-Url
-*/
-(function ($) {
-    "use strict";
-    
-    var Url = function (options) {
-        this.init('url', options, Url.defaults);
-    };
-    $.fn.editableutils.inherit(Url, $.fn.editabletypes.text);
-    Url.defaults = $.extend({}, $.fn.editabletypes.text.defaults, {
-        tpl: '<input type="url">'
-    });
-    $.fn.editabletypes.url = Url;
-}(window.jQuery));
-
-
-/*
-Tel
-*/
-(function ($) {
-    "use strict";
-    
-    var Tel = function (options) {
-        this.init('tel', options, Tel.defaults);
-    };
-    $.fn.editableutils.inherit(Tel, $.fn.editabletypes.text);
-    Tel.defaults = $.extend({}, $.fn.editabletypes.text.defaults, {
-        tpl: '<input type="tel">'
-    });
-    $.fn.editabletypes.tel = Tel;
-}(window.jQuery));
-
-
-/*
-Number
-*/
-(function ($) {
-    "use strict";
-    
-    var NumberInput = function (options) {
-        this.init('number', options, NumberInput.defaults);
-    };
-    $.fn.editableutils.inherit(NumberInput, $.fn.editabletypes.text);
-    $.extend(NumberInput.prototype, {
-         render: function () {
-            NumberInput.superclass.render.call(this);
-            this.setAttr('min');
-            this.setAttr('max');
-            this.setAttr('step');
-        },
-        postrender: function() {
-            if(this.$clear) {
-                //increase right ffset  for up/down arrows
-                this.$clear.css({right: 24});
-                /*
-                //can position clear button only here, when form is shown and height can be calculated
-                var h = this.$input.outerHeight(true) || 20,
-                    delta = (h - this.$clear.height()) / 2;
-                
-                //add 12px to offset right for up/down arrows    
-                this.$clear.css({top: delta, right: delta + 16});
-                */
-            } 
-        }        
-    });     
-    NumberInput.defaults = $.extend({}, $.fn.editabletypes.text.defaults, {
-        tpl: '<input type="number">',
-        inputclass: 'input-mini',
-        min: null,
-        max: null,
-        step: null
-    });
-    $.fn.editabletypes.number = NumberInput;
-}(window.jQuery));
-
-
-/*
-Range (inherit from number)
-*/
-(function ($) {
-    "use strict";
-    
-    var Range = function (options) {
-        this.init('range', options, Range.defaults);
-    };
-    $.fn.editableutils.inherit(Range, $.fn.editabletypes.number);
-    $.extend(Range.prototype, {
-        render: function () {
-            this.$input = this.$tpl.filter('input');
-            
-            this.setClass();
-            this.setAttr('min');
-            this.setAttr('max');
-            this.setAttr('step');           
-            
-            this.$input.on('input', function(){
-                $(this).siblings('output').text($(this).val()); 
-            });  
-        },
-        activate: function() {
-            this.$input.focus();
-        }         
-    });
-    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));
-
-/*
-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.  
- 
-You should manually download and include select2 distributive:  
-
-    <link href="select2/select2.css" rel="stylesheet" type="text/css"></link>  
-    <script src="select2/select2.js"></script>  
-    
-To make it **bootstrap-styled** you can use css from [here](https://github.com/t0m/select2-bootstrap-css): 
-
-    <link href="select2-bootstrap.css" rel="stylesheet" type="text/css"></link>    
-    
-**Note:** currently `autotext` feature does not work for select2 with `ajax` remote source.    
-You need initially put both `data-value` and element's text youself:    
-
-    <a href="#" data-type="select2" data-value="1">Text1</a>
-    
-    
-@class select2
-@extends abstractinput
-@since 1.4.1
-@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',
-        select2: {
-            placeholder: 'Select Country',
-            minimumInputLength: 1
-        }
-    });
-    //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";
-    
-    var Constructor = function (options) {
-        this.init('select2', options, Constructor.defaults);
-
-        options.select2 = options.select2 || {};
-
-        this.sourceData = null;
-        
-        //placeholder
-        if(options.placeholder) {
-            options.select2.placeholder = options.placeholder;
-        }
-       
-        //if not `tags` mode, use source
-        if(!options.select2.tags && options.source) {
-            var source = options.source;
-            //if source is function, call it (once!)
-            if ($.isFunction(options.source)) {
-                source = options.source.call(options.scope);
-            }               
-
-            if (typeof source === 'string') {
-                options.select2.ajax = options.select2.ajax || {};
-                //some default ajax params
-                if(!options.select2.ajax.data) {
-                    options.select2.ajax.data = function(term) {return { query:term };};
-                }
-                if(!options.select2.ajax.results) {
-                    options.select2.ajax.results = function(data) { return {results:data };};
-                }
-                options.select2.ajax.url = source;
-            } else {
-                //check format and convert x-editable format to select2 format (if needed)
-                this.sourceData = this.convertSource(source);
-                options.select2.data = this.sourceData;
-            }
-        } 
-
-        //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);
-
-        //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, {
-        render: function() {
-            this.setClass();
-
-            //can not apply select2 here as it calls initSelection 
-            //over input that does not have correct value yet.
-            //apply select2 only in value2input
-            //this.$input.select2(this.options.select2);
-
-            //when data is loaded via ajax, we need to know when it's done to populate listData
-            if(this.isRemote) {
-                //listen to loaded event to populate data
-                this.$input.on('select2-loaded', $.proxy(function(e) {
-                    this.sourceData = e.items.results;
-                }, this));
-            }
-
-            //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,
-               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, 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' ? that.formatSelection(v) : v);
-               });
-           } else if(data) {
-               text = that.formatSelection(data);
-           }
-
-           text = $.isArray(text) ? text.join(this.options.viewseparator) : text;
-
-           //$(element).text(text);
-           Constructor.superclass.value2html.call(this, text, element); 
-       },
-
-       html2value: function(html) {
-           return this.options.select2.tags ? this.str2value(html, this.options.viewseparator) : null;
-       },
-
-       value2input: function(value) {
-           // if value array => join it anyway
-           if($.isArray(value)) {
-              value = value.join(this.getSeparator());
-           }
-
-           //for remote source just set value, text is updated by initSelection
-           if(!this.$input.data('select2')) {
-               this.$input.val(value);
-               this.$input.select2(this.options.select2);
-           } else {
-               //second argument needed to separate initial change from user's click (for autosubmit)   
-               this.$input.val(value).trigger('change', true); 
-
-               //Uncaught Error: cannot call val() if initSelection() is not defined
-               //this.$input.select2('val', value);
-           }
-
-           // if defined remote source AND no multiple mode AND no user's initSelection provided --> 
-           // we should somehow get text for provided id.
-           // The solution is to use element's text as text for that id (exclude empty)
-           if(this.isRemote && !this.isMultiple && !this.options.select2.initSelection) {
-               // customId and customText are methods to extract `id` and `text` from data object
-               // we can use this workaround only if user did not define these methods
-               // otherwise we cant construct data object
-               var customId = this.options.select2.id,
-                   customText = this.options.select2.formatSelection;
-
-               if(!customId && !customText) {
-                   var $el = $(this.options.scope);
-                   if (!$el.data('editable').isEmpty) {
-                       var data = {id: value, text: $el.text()};
-                       this.$input.select2('data', data); 
-                   }
-               }
-           }
-       },
-       
-       input2value: function() { 
-           return this.$input.select2('val');
-       },
-
-       str2value: function(str, separator) {
-            if(typeof str !== 'string' || !this.isMultiple) {
-                return str;
-            }
-
-            separator = separator || this.getSeparator();
-
-            var val, i, l;
-
-            if (str === null || str.length < 1) {
-                return null;
-            }
-            val = str.split(separator);
-            for (i = 0, l = val.length; i < l; i = i + 1) {
-                val[i] = $.trim(val[i]);
-            }
-
-            return val;
-       },
-
-        autosubmit: function() {
-            this.$input.on('change', function(e, isInitial){
-                if(!isInitial) {
-                  $(this).closest('form').submit();
-                }
-            });
-        },
-
-        getSeparator: function() {
-            return this.options.select2.separator || $.fn.select2.defaults.separator;
-        },
-
-        /*
-        Converts source from x-editable format: {value: 1, text: "1"} to
-        select2 format: {id: 1, text: "1"}
-        */
-        convertSource: function(source) {
-            if($.isArray(source) && source.length && source[0].value !== undefined) {
-                for(var i = 0; i<source.length; i++) {
-                    if(source[i].value !== undefined) {
-                        source[i].id = source[i].value;
-                        delete source[i].value;
-                    }
-                }
-            }
-            return source;
-        },
-        
-        destroy: function() {
-	        if(this.$input) {
-	            if(this.$input.data('select2')) {
-	                this.$input.select2('destroy');
-	            }
-	        }
-        }
-        
-    });
-
-    Constructor.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
-        /**
-        @property tpl 
-        @default <input type="hidden">
-        **/
-        tpl:'<input type="hidden">',
-        /**
-        Configuration of select2. [Full list of options](http://ivaynberg.github.com/select2).
-
-        @property select2 
-        @type object
-        @default null
-        **/
-        select2: null,
-        /**
-        Placeholder attribute of select
-
-        @property placeholder 
-        @type string
-        @default null
-        **/
-        placeholder: null,
-        /**
-        Source data for select. It will be assigned to select2 `data` property and kept here just for convenience.
-        Please note, that format is different from simple `select` input: use 'id' instead of 'value'.
-        E.g. `[{id: 1, text: "text1"}, {id: 2, text: "text2"}, ...]`.
-
-        @property source 
-        @type array|string|function
-        @default null        
-        **/
-        source: null,
-        /**
-        Separator used to display tags.
-
-        @property viewseparator 
-        @type string
-        @default ', '        
-        **/
-        viewseparator: ', '
-    });
-
-    $.fn.editabletypes.select2 = Constructor;
-
-}(window.jQuery));
-
-/**
-* Combodate - 1.0.5
-* 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 
-*
-* 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)
-* 
-* Differs from momentjs parse rules:
-* 00:00 pm, 12:00 pm --> 12:00 (24-h format, day not change)
-* 00:00 am, 12:00 am --> 00:00 (24-h format, day not change)
-* 
-* 
-* Author: Vitaliy Potapov
-* Project page: http://github.com/vitalets/combodate
-* Copyright (c) 2012 Vitaliy Potapov. Released under MIT License.
-**/
-(function ($) {
-
-    var Combodate = function (element, options) {
-        this.$element = $(element);
-        if(!this.$element.is('input')) {
-            $.error('Combodate should be applied to INPUT element');
-            return;
-        }
-        this.options = $.extend({}, $.fn.combodate.defaults, options, this.$element.data());
-        this.init();  
-     };
-
-    Combodate.prototype = {
-        constructor: Combodate, 
-        init: function () {
-            this.map = {
-                //key   regexp    moment.method
-                day:    ['D',    'date'], 
-                month:  ['M',    'month'], 
-                year:   ['Y',    'year'], 
-                hour:   ['[Hh]', 'hours'],
-                minute: ['m',    'minutes'], 
-                second: ['s',    'seconds'],
-                ampm:   ['[Aa]', ''] 
-            };
-            
-            this.$widget = $('<span class="combodate"></span>').html(this.getTemplate());
-                      
-            this.initCombos();
-            
-            //update original input on change 
-            this.$widget.on('change', 'select', $.proxy(function(e) {
-                this.$element.val(this.getValue()).change();
-                // update days count if month or year changes
-                if (this.options.smartDays) {
-                    if ($(e.target).is('.month') || $(e.target).is('.year')) {
-                        this.fillCombo('day');
-                    }
-                }
-            }, this));
-            
-            this.$widget.find('select').css('width', 'auto');
-                                       
-            // hide original input and insert widget                                       
-            this.$element.hide().after(this.$widget);
-            
-            // set initial value
-            this.setValue(this.$element.val() || this.options.value);
-        },
-        
-        /*
-         Replace tokens in template with <select> elements 
-        */         
-        getTemplate: function() {
-            var tpl = this.options.template;
-
-            //first pass
-            $.each(this.map, function(k, v) {
-                v = v[0]; 
-                var r = new RegExp(v+'+'),
-                    token = v.length > 1 ? v.substring(1, 2) : v;
-                    
-                tpl = tpl.replace(r, '{'+token+'}');
-            });
-
-            //replace spaces with &nbsp;
-            tpl = tpl.replace(/ /g, '&nbsp;');
-
-            //second pass
-            $.each(this.map, function(k, v) {
-                v = v[0];
-                var token = v.length > 1 ? v.substring(1, 2) : v;
-                    
-                tpl = tpl.replace('{'+token+'}', '<select class="'+k+'"></select>');
-            });   
-
-            return tpl;
-        },
-        
-        /*
-         Initialize combos that presents in template 
-        */        
-        initCombos: function() {
-            for (var k in this.map) {
-                var $c = this.$widget.find('.'+k);
-                // set properties like this.$day, this.$month etc.
-                this['$'+k] = $c.length ? $c : null;
-                // fill with items
-                this.fillCombo(k);
-            }
-        },
-
-        /*
-         Fill combo with items 
-        */        
-        fillCombo: function(k) {
-            var $combo = this['$'+k];
-            if (!$combo) {
-                return;
-            }
-
-            // define method name to fill items, e.g `fillDays`
-            var f = 'fill' + k.charAt(0).toUpperCase() + k.slice(1); 
-            var items = this[f]();
-            var value = $combo.val();
-
-            $combo.empty();
-            for(var i=0; i<items.length; i++) {
-                $combo.append('<option value="'+items[i][0]+'">'+items[i][1]+'</option>');
-            }
-
-            $combo.val(value);
-        },
-
-        /*
-         Initialize items of combos. Handles `firstItem` option 
-        */
-        fillCommon: function(key) {
-            var values = [],
-                relTime;
-                
-            if(this.options.firstItem === 'name') {
-                //need both to support moment ver < 2 and  >= 2
-                relTime = moment.relativeTime || moment.langData()._relativeTime; 
-                var header = typeof relTime[key] === 'function' ? relTime[key](1, true, key, false) : relTime[key];
-                //take last entry (see momentjs lang files structure) 
-                header = header.split(' ').reverse()[0];                
-                values.push(['', header]);
-            } else if(this.options.firstItem === 'empty') {
-                values.push(['', '']);
-            }
-            return values;
-        },  
-
-
-        /*
-        fill day
-        */
-        fillDay: function() {
-            var items = this.fillCommon('d'), name, i,
-                twoDigit = this.options.template.indexOf('DD') !== -1,
-                daysCount = 31;
-
-            // detect days count (depends on month and year)
-            // originally https://github.com/vitalets/combodate/pull/7
-            if (this.options.smartDays && this.$month && this.$year) {
-                var month = parseInt(this.$month.val(), 10);
-                var year = parseInt(this.$year.val(), 10);
-
-                if (!isNaN(month) && !isNaN(year)) {
-                    daysCount = moment([year, month]).daysInMonth();
-                }
-            }
-
-            for (i = 1; i <= daysCount; i++) {
-                name = twoDigit ? this.leadZero(i) : i;
-                items.push([i, name]);
-            }
-            return items;        
-        },
-        
-        /*
-        fill month
-        */
-        fillMonth: function() {
-            var items = this.fillCommon('M'), name, i, 
-                longNames = this.options.template.indexOf('MMMM') !== -1,
-                shortNames = this.options.template.indexOf('MMM') !== -1,
-                twoDigit = this.options.template.indexOf('MM') !== -1;
-                
-            for(i=0; i<=11; i++) {
-                if(longNames) {
-                    //see https://github.com/timrwood/momentjs.com/pull/36
-                    name = moment().date(1).month(i).format('MMMM');
-                } else if(shortNames) {
-                    name = moment().date(1).month(i).format('MMM');
-                } else if(twoDigit) {
-                    name = this.leadZero(i+1);
-                } else {
-                    name = i+1;
-                }
-                items.push([i, name]);
-            } 
-            return items;
-        },  
-        
-        /*
-        fill year
-        */
-        fillYear: function() {
-            var items = [], name, i, 
-                longNames = this.options.template.indexOf('YYYY') !== -1;
-           
-            for(i=this.options.maxYear; i>=this.options.minYear; i--) {
-                name = longNames ? i : (i+'').substring(2);
-                items[this.options.yearDescending ? 'push' : 'unshift']([i, name]);
-            }
-            
-            items = this.fillCommon('y').concat(items);
-            
-            return items;              
-        },    
-        
-        /*
-        fill hour
-        */
-        fillHour: function() {
-            var items = this.fillCommon('h'), name, i,
-                h12 = this.options.template.indexOf('h') !== -1,
-                h24 = this.options.template.indexOf('H') !== -1,
-                twoDigit = this.options.template.toLowerCase().indexOf('hh') !== -1,
-                min = h12 ? 1 : 0, 
-                max = h12 ? 12 : 23;
-                
-            for(i=min; i<=max; i++) {
-                name = twoDigit ? this.leadZero(i) : i;
-                items.push([i, name]);
-            } 
-            return items;                 
-        },    
-        
-        /*
-        fill minute
-        */
-        fillMinute: function() {
-            var items = this.fillCommon('m'), name, i,
-                twoDigit = this.options.template.indexOf('mm') !== -1;
-
-            for(i=0; i<=59; i+= this.options.minuteStep) {
-                name = twoDigit ? this.leadZero(i) : i;
-                items.push([i, name]);
-            }    
-            return items;              
-        },  
-        
-        /*
-        fill second
-        */
-        fillSecond: function() {
-            var items = this.fillCommon('s'), name, i,
-                twoDigit = this.options.template.indexOf('ss') !== -1;
-
-            for(i=0; i<=59; i+= this.options.secondStep) {
-                name = twoDigit ? this.leadZero(i) : i;
-                items.push([i, name]);
-            }    
-            return items;              
-        },  
-        
-        /*
-        fill ampm
-        */
-        fillAmpm: function() {
-            var ampmL = this.options.template.indexOf('a') !== -1,
-                ampmU = this.options.template.indexOf('A') !== -1,            
-                items = [
-                    ['am', ampmL ? 'am' : 'AM'],
-                    ['pm', ampmL ? 'pm' : 'PM']
-                ];
-            return items;                              
-        },                                       
-
-        /*
-         Returns current date value from combos. 
-         If format not specified - `options.format` used.
-         If format = `null` - Moment object returned.
-        */
-        getValue: function(format) {
-            var dt, values = {}, 
-                that = this,
-                notSelected = false;
-                
-            //getting selected values    
-            $.each(this.map, function(k, v) {
-                if(k === 'ampm') {
-                    return;
-                }
-                var def = k === 'day' ? 1 : 0;
-                  
-                values[k] = that['$'+k] ? parseInt(that['$'+k].val(), 10) : def; 
-                
-                if(isNaN(values[k])) {
-                   notSelected = true;
-                   return false; 
-                }
-            });
-            
-            //if at least one visible combo not selected - return empty string
-            if(notSelected) {
-               return '';
-            }
-            
-            //convert hours 12h --> 24h 
-            if(this.$ampm) {
-                //12:00 pm --> 12:00 (24-h format, midday), 12:00 am --> 00:00 (24-h format, midnight, start of day)
-                if(values.hour === 12) {
-                    values.hour = this.$ampm.val() === 'am' ? 0 : 12;                    
-                } else {
-                    values.hour = this.$ampm.val() === 'am' ? values.hour : values.hour+12;
-                }
-            }    
-            
-            dt = moment([values.year, values.month, values.day, values.hour, values.minute, values.second]);
-            
-            //highlight invalid date
-            this.highlight(dt);
-                              
-            format = format === undefined ? this.options.format : format;
-            if(format === null) {
-               return dt.isValid() ? dt : null; 
-            } else {
-               return dt.isValid() ? dt.format(format) : ''; 
-            }           
-        },
-        
-        setValue: function(value) {
-            if(!value) {
-                return;
-            }
-            
-            var dt = typeof value === 'string' ? moment(value, this.options.format) : moment(value),
-                that = this,
-                values = {};
-            
-            //function to find nearest value in select options
-            function getNearest($select, value) {
-                var delta = {};
-                $select.children('option').each(function(i, opt){
-                    var optValue = $(opt).attr('value'),
-                    distance;
-
-                    if(optValue === '') return;
-                    distance = Math.abs(optValue - value); 
-                    if(typeof delta.distance === 'undefined' || distance < delta.distance) {
-                        delta = {value: optValue, distance: distance};
-                    } 
-                }); 
-                return delta.value;
-            }             
-            
-            if(dt.isValid()) {
-                //read values from date object
-                $.each(this.map, function(k, v) {
-                    if(k === 'ampm') {
-                       return; 
-                    }
-                    values[k] = dt[v[1]]();
-                });
-               
-                if(this.$ampm) {
-                    //12:00 pm --> 12:00 (24-h format, midday), 12:00 am --> 00:00 (24-h format, midnight, start of day)
-                    if(values.hour >= 12) {
-                        values.ampm = 'pm';
-                        if(values.hour > 12) {
-                            values.hour -= 12;
-                        }
-                    } else {
-                        values.ampm = 'am';
-                        if(values.hour === 0) {
-                            values.hour = 12;
-                        }
-                    } 
-                }
-               
-                $.each(values, function(k, v) {
-                    //call val() for each existing combo, e.g. this.$hour.val()
-                    if(that['$'+k]) {
-                       
-                        if(k === 'minute' && that.options.minuteStep > 1 && that.options.roundTime) {
-                           v = getNearest(that['$'+k], v);
-                        }
-                       
-                        if(k === 'second' && that.options.secondStep > 1 && that.options.roundTime) {
-                           v = getNearest(that['$'+k], v);
-                        }                       
-                       
-                        that['$'+k].val(v);
-                    }
-                });
-
-                // update days count
-                if (this.options.smartDays) {
-                    this.fillCombo('day');
-                }
-               
-               this.$element.val(dt.format(this.options.format)).change();
-            }
-        },
-        
-        /*
-         highlight combos if date is invalid
-        */
-        highlight: function(dt) {
-            if(!dt.isValid()) {
-                if(this.options.errorClass) {
-                    this.$widget.addClass(this.options.errorClass);
-                } else {
-                    //store original border color
-                    if(!this.borderColor) {
-                        this.borderColor = this.$widget.find('select').css('border-color'); 
-                    }
-                    this.$widget.find('select').css('border-color', 'red');
-                } 
-            } else {
-                if(this.options.errorClass) {
-                    this.$widget.removeClass(this.options.errorClass);
-                } else {
-                    this.$widget.find('select').css('border-color', this.borderColor);
-                }  
-            }
-        },
-        
-        leadZero: function(v) {
-            return v <= 9 ? '0' + v : v; 
-        },
-        
-        destroy: function() {
-            this.$widget.remove();
-            this.$element.removeData('combodate').show();
-        }
-        
-        //todo: clear method        
-    };
-
-    $.fn.combodate = function ( option ) {
-        var d, args = Array.apply(null, arguments);
-        args.shift();
-
-        //getValue returns date as string / object (not jQuery object)
-        if(option === 'getValue' && this.length && (d = this.eq(0).data('combodate'))) {
-          return d.getValue.apply(d, args);
-        }        
-        
-        return this.each(function () {
-            var $this = $(this),
-            data = $this.data('combodate'),
-            options = typeof option == 'object' && option;
-            if (!data) {
-                $this.data('combodate', (data = new Combodate(this, options)));
-            }
-            if (typeof option == 'string' && typeof data[option] == 'function') {
-                data[option].apply(data, args);
-            }
-        });
-    };  
-    
-    $.fn.combodate.defaults = {
-         //in this format value stored in original input
-        format: 'DD-MM-YYYY HH:mm',      
-        //in this format items in dropdowns are displayed
-        template: 'D / MMM / YYYY   H : mm',
-        //initial value, can be `new Date()`    
-        value: null,                       
-        minYear: 1970,
-        maxYear: (new Date().getFullYear()),
-        yearDescending: true,
-        minuteStep: 5,
-        secondStep: 1,
-        firstItem: 'empty', //'name', 'empty', 'none'
-        errorClass: null,
-        roundTime: true, // whether to round minutes and seconds if step > 1
-        smartDays: false // whether days in combo depend on selected month: 31, 30, 28
-    };
-
-}(window.jQuery));
-
-/**
-Combodate input - dropdown date and time picker.    
-Based on [combodate](http://vitalets.github.com/combodate) plugin (included). To use it you should manually include [momentjs](http://momentjs.com).
-
-    <script src="js/moment.min.js"></script>
-   
-Allows to input:
-
-* only date
-* only time 
-* both date and time  
-
-Please note, that format is taken from momentjs and **not compatible** with bootstrap-datepicker / jquery UI datepicker.  
-Internally value stored as `momentjs` object. 
-
-@class combodate
-@extends abstractinput
-@final
-@since 1.4.0
-@example
-<a href="#" id="dob" data-type="combodate" data-pk="1" data-url="/post" data-value="1984-05-15" data-title="Select date"></a>
-<script>
-$(function(){
-    $('#dob').editable({
-        format: 'YYYY-MM-DD',    
-        viewformat: 'DD.MM.YYYY',    
-        template: 'D / MMMM / YYYY',    
-        combodate: {
-                minYear: 2000,
-                maxYear: 2015,
-                minuteStep: 1
-           }
-        }
-    });
-});
-</script>
-**/
-
-/*global moment*/
-
-(function ($) {
-    "use strict";
-    
-    var Constructor = function (options) {
-        this.init('combodate', options, Constructor.defaults);
-        
-        //by default viewformat equals to format
-        if(!this.options.viewformat) {
-            this.options.viewformat = this.options.format;
-        }        
-        
-        //try parse combodate config defined as json string in data-combodate
-        options.combodate = $.fn.editableutils.tryParseJson(options.combodate, true);
-
-        //overriding combodate config (as by default jQuery extend() is not recursive)
-        this.options.combodate = $.extend({}, Constructor.defaults.combodate, options.combodate, {
-            format: this.options.format,
-            template: this.options.template
-        });
-    };
-
-    $.fn.editableutils.inherit(Constructor, $.fn.editabletypes.abstractinput);    
-    
-    $.extend(Constructor.prototype, {
-        render: function () {
-            this.$input.combodate(this.options.combodate);
-                    
-            if($.fn.editableform.engine === 'bs3') {
-                this.$input.siblings().find('select').addClass('form-control');
-            }
-            
-            if(this.options.inputclass) {
-                this.$input.siblings().find('select').addClass(this.options.inputclass);
-            }            
-            //"clear" link
-            /*
-            if(this.options.clear) {
-                this.$clear = $('<a href="#"></a>').html(this.options.clear).click($.proxy(function(e){
-                    e.preventDefault();
-                    e.stopPropagation();
-                    this.clear();
-                }, this));
-                
-                this.$tpl.parent().append($('<div class="editable-clear">').append(this.$clear));  
-            } 
-            */               
-        },
-        
-        value2html: function(value, element) {
-            var text = value ? value.format(this.options.viewformat) : '';
-            //$(element).text(text);
-            Constructor.superclass.value2html.call(this, text, element);  
-        },
-
-        html2value: function(html) {
-            return html ? moment(html, this.options.viewformat) : null;
-        },   
-        
-        value2str: function(value) {
-            return value ? value.format(this.options.format) : '';
-       }, 
-       
-       str2value: function(str) {
-           return str ? moment(str, this.options.format) : null;
-       }, 
-       
-       value2submit: function(value) {
-           return this.value2str(value);
-       },                    
-
-       value2input: function(value) {
-           this.$input.combodate('setValue', value);
-       },
-        
-       input2value: function() { 
-           return this.$input.combodate('getValue', null);
-       },       
-       
-       activate: function() {
-           this.$input.siblings('.combodate').find('select').eq(0).focus();
-       },
-       
-       /*
-       clear:  function() {
-          this.$input.data('datepicker').date = null;
-          this.$input.find('.active').removeClass('active');
-       },
-       */
-       
-       autosubmit: function() {
-           
-       }
-
-    });
-    
-    Constructor.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
-        /**
-        @property tpl 
-        @default <input type="text">
-        **/         
-        tpl:'<input type="text">',
-        /**
-        @property inputclass 
-        @default null
-        **/         
-        inputclass: null,
-        /**
-        Format used for sending value to server. Also applied when converting date from <code>data-value</code> attribute.<br>
-        See list of tokens in [momentjs docs](http://momentjs.com/docs/#/parsing/string-format)  
-        
-        @property format 
-        @type string
-        @default YYYY-MM-DD
-        **/         
-        format:'YYYY-MM-DD',
-        /**
-        Format used for displaying date. Also applied when converting date from element's text on init.   
-        If not specified equals to `format`.
-        
-        @property viewformat 
-        @type string
-        @default null
-        **/          
-        viewformat: null,        
-        /**
-        Template used for displaying dropdowns.
-        
-        @property template 
-        @type string
-        @default D / MMM / YYYY
-        **/          
-        template: 'D / MMM / YYYY',  
-        /**
-        Configuration of combodate.
-        Full list of options: http://vitalets.github.com/combodate/#docs
-        
-        @property combodate 
-        @type object
-        @default null
-        **/
-        combodate: null
-        
-        /*
-        (not implemented yet)
-        Text shown as clear date button. 
-        If <code>false</code> clear button will not be rendered.
-        
-        @property clear 
-        @type boolean|string
-        @default 'x clear'         
-        */
-        //clear: '&times; clear'
-    });   
-
-    $.fn.editabletypes.combodate = Constructor;
-
-}(window.jQuery));
-
-/*
-Editableform based on Twitter Bootstrap 3
-*/
-(function ($) {
-    "use strict";
-    
-    //store parent methods
-    const pInitInput = $.fn.editableform.Constructor.prototype.initInput;
-    
-    $.extend($.fn.editableform.Constructor.prototype, {
-        // initTemplate: function() {
-        //     this.$form = $($.fn.editableform.template);
-        //     this.$form.find('.control-group').addClass('form-group');
-        //     this.$form.find('.editable-error-block').addClass('help-block');
-        // },
-        initInput: function() {  
-            pInitInput.apply(this);
-
-            //for bs3 set default class `input-sm` to standard inputs
-            const emptyInputClass = this.input.options.inputclass === null || this.input.options.inputclass === false;
-            const defaultClass = 'input-sm';
-            
-            //bs3 add `form-control` class to standard inputs
-            const stdtypes = 'text,select,textarea,password,email,url,tel,number,range,time,typeaheadjs'.split(',');
-            if(~$.inArray(this.input.type, stdtypes)) {
-                this.input.$input.addClass('form-control editable');
-                if(emptyInputClass) {
-                    this.input.options.inputclass = defaultClass;
-                    this.input.$input.addClass(defaultClass);
-                }
-            }
-            // Automatically open select dropdown when clicked
-            if (this.input.type === 'select') {
-                setTimeout(() => {
-                    this.input.$input.focus().click();
-                }, 50);
-            }
-        
-            //apply bs3 size class also to buttons (to fit size of control)
-            const $btn = this.$form.find('.editable-buttons');
-            const classes = emptyInputClass ? [defaultClass] : this.input.options.inputclass.split(' ');
-            for(let i=0; i<classes.length; i++) {
-                if(classes[i].toLowerCase() === 'input-lg') {
-                    $btn.find('button').removeClass('btn-sm').addClass('btn-lg'); 
-                }
-            }
-        }
-    });    
-    
-    //buttons
-    $.fn.editableform.buttons = 
-      '<button type="submit" class="btn btn-primary btn-sm editable-submit">'+
-        '<i class="bi bi-check"></i>'+
-      '</button>'+
-      '<button type="button" class="btn btn-secondary btn-sm editable-cancel">'+
-        '<i class="bi bi-x"></i>'+
-      '</button>';         
-    
-    //error classes
-    $.fn.editableform.errorGroupClass = 'has-error';
-    $.fn.editableform.errorBlockClass = null;  
-    //engine
-    $.fn.editableform.engine = 'bs3';  
-}(window.jQuery));
-/**
-* Editable Popover3 (for Bootstrap 3) 
-* ---------------------
-* requires bootstrap-popover.js
-*/
-(function ($) {
-    "use strict";
-
-    //extend methods
-    $.extend($.fn.editableContainer.Popup.prototype, {
-        containerName: 'popover',
-        containerDataName: 'bs.popover',
-        innerCss: '.popover-content',
-        defaults: $.fn.popover.Constructor.DEFAULTS,
-
-        initContainer: function(){
-            $.extend(this.containerOptions, {
-                trigger: 'manual',
-                selector: false,
-                content: ' ',
-                template: this.defaults.template
-            });
-            
-            //as template property is used in inputs, hide it from popover
-            var t;
-            if(this.$element.data('template')) {
-               t = this.$element.data('template');
-               this.$element.removeData('template');  
-            } 
-            
-            this.call(this.containerOptions);
-            
-            if(t) {
-               //restore data('template')
-               this.$element.data('template', t); 
-            }
-        }, 
-        
-        /* show */
-        innerShow: function () {
-            this.call('show');                
-        },  
-        
-        /* hide */
-        innerHide: function () {
-            this.call('hide');       
-        }, 
-        
-        /* destroy */
-        innerDestroy: function() {
-            this.call('destroy');
-        },                               
-        
-        setContainerOption: function(key, value) {
-            this.container().options[key] = value; 
-        },               
-
-        /**
-        * move popover to new position. This function mainly copied from bootstrap-popover.
-        */
-        /*jshint laxcomma: true, eqeqeq: false*/
-        setPosition: function () { 
-
-            (function() {
-            /*    
-                var $tip = this.tip()
-                , inside
-                , pos
-                , actualWidth
-                , actualHeight
-                , placement
-                , tp
-                , tpt
-                , tpb
-                , tpl
-                , tpr;
-
-                placement = typeof this.options.placement === 'function' ?
-                this.options.placement.call(this, $tip[0], this.$element[0]) :
-                this.options.placement;
-
-                inside = /in/.test(placement);
-               
-                $tip
-              //  .detach()
-              //vitalets: remove any placement class because otherwise they dont influence on re-positioning of visible popover
-                .removeClass('top right bottom left')
-                .css({ top: 0, left: 0, display: 'block' });
-              //  .insertAfter(this.$element);
-               
-                pos = this.getPosition(inside);
-
-                actualWidth = $tip[0].offsetWidth;
-                actualHeight = $tip[0].offsetHeight;
-
-                placement = inside ? placement.split(' ')[1] : placement;
-
-                tpb = {top: pos.top + pos.height, left: pos.left + pos.width / 2 - actualWidth / 2};
-                tpt = {top: pos.top - actualHeight, left: pos.left + pos.width / 2 - actualWidth / 2};
-                tpl = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left - actualWidth};
-                tpr = {top: pos.top + pos.height / 2 - actualHeight / 2, left: pos.left + pos.width};
-
-                switch (placement) {
-                    case 'bottom':
-                        if ((tpb.top + actualHeight) > ($(window).scrollTop() + $(window).height())) {
-                            if (tpt.top > $(window).scrollTop()) {
-                                placement = 'top';
-                            } else if ((tpr.left + actualWidth) < ($(window).scrollLeft() + $(window).width())) {
-                                placement = 'right';
-                            } else if (tpl.left > $(window).scrollLeft()) {
-                                placement = 'left';
-                            } else {
-                                placement = 'right';
-                            }
-                        }
-                        break;
-                    case 'top':
-                        if (tpt.top < $(window).scrollTop()) {
-                            if ((tpb.top + actualHeight) < ($(window).scrollTop() + $(window).height())) {
-                                placement = 'bottom';
-                            } else if ((tpr.left + actualWidth) < ($(window).scrollLeft() + $(window).width())) {
-                                placement = 'right';
-                            } else if (tpl.left > $(window).scrollLeft()) {
-                                placement = 'left';
-                            } else {
-                                placement = 'right';
-                            }
-                        }
-                        break;
-                    case 'left':
-                        if (tpl.left < $(window).scrollLeft()) {
-                            if ((tpr.left + actualWidth) < ($(window).scrollLeft() + $(window).width())) {
-                                placement = 'right';
-                            } else if (tpt.top > $(window).scrollTop()) {
-                                placement = 'top';
-                            } else if (tpt.top > $(window).scrollTop()) {
-                                placement = 'bottom';
-                            } else {
-                                placement = 'right';
-                            }
-                        }
-                        break;
-                    case 'right':
-                        if ((tpr.left + actualWidth) > ($(window).scrollLeft() + $(window).width())) {
-                            if (tpl.left > $(window).scrollLeft()) {
-                                placement = 'left';
-                            } else if (tpt.top > $(window).scrollTop()) {
-                                placement = 'top';
-                            } else if (tpt.top > $(window).scrollTop()) {
-                                placement = 'bottom';
-                            }
-                        }
-                        break;
-                }
-
-                switch (placement) {
-                    case 'bottom':
-                        tp = tpb;
-                        break;
-                    case 'top':
-                        tp = tpt;
-                        break;
-                    case 'left':
-                        tp = tpl;
-                        break;
-                    case 'right':
-                        tp = tpr;
-                        break;
-                }
-
-                $tip
-                .offset(tp)
-                .addClass(placement)
-                .addClass('in');
-           */
-                     
-           
-            var $tip = this.tip();
-            
-            var placement = typeof this.options.placement == 'function' ?
-                this.options.placement.call(this, $tip[0], this.$element[0]) :
-                this.options.placement;            
-
-            var autoToken = /\s?auto?\s?/i;
-            var autoPlace = autoToken.test(placement);
-            if (autoPlace) {
-                placement = placement.replace(autoToken, '') || 'top';
-            }
-            
-            
-            var pos = this.getPosition();
-            var actualWidth = $tip[0].offsetWidth;
-            var actualHeight = $tip[0].offsetHeight;
-
-            if (autoPlace) {
-                var $parent = this.$element.parent();
-
-                var orgPlacement = placement;
-                var docScroll    = document.documentElement.scrollTop || document.body.scrollTop;
-                var parentWidth  = this.options.container == 'body' ? window.innerWidth  : $parent.outerWidth();
-                var parentHeight = this.options.container == 'body' ? window.innerHeight : $parent.outerHeight();
-                var parentLeft   = this.options.container == 'body' ? 0 : $parent.offset().left;
-
-                placement = placement == 'bottom' && pos.top   + pos.height  + actualHeight - docScroll > parentHeight  ? 'top'    :
-                            placement == 'top'    && pos.top   - docScroll   - actualHeight < 0                         ? 'bottom' :
-                            placement == 'right'  && pos.right + actualWidth > parentWidth                              ? 'left'   :
-                            placement == 'left'   && pos.left  - actualWidth < parentLeft                               ? 'right'  :
-                            placement;
-
-                $tip
-                  .removeClass(orgPlacement)
-                  .addClass(placement);
-            }
-
-
-            var calculatedOffset = this.getCalculatedOffset(placement, pos, actualWidth, actualHeight);
-
-            this.applyPlacement(calculatedOffset, placement);            
-                     
-                
-            }).call(this.container());
-          /*jshint laxcomma: false, eqeqeq: true*/  
-        }            
-    });
-
-}(window.jQuery));
-
-/* =========================================================
- * bootstrap-datepicker.js
- * http://www.eyecon.ro/bootstrap-datepicker
- * =========================================================
- * Copyright 2012 Stefan Petre
- * Improvements by Andrew Rowls
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- * ========================================================= */
-
-(function( $ ) {
-
-	function UTCDate(){
-		return new Date(Date.UTC.apply(Date, arguments));
-	}
-	function UTCToday(){
-		var today = new Date();
-		return UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
-	}
-
-	// Picker object
-
-	var Datepicker = function(element, options) {
-		var that = this;
-
-		this._process_options(options);
-
-		this.element = $(element);
-		this.isInline = false;
-		this.isInput = this.element.is('input');
-		this.component = this.element.is('.date') ? this.element.find('.add-on, .btn') : false;
-		this.hasInput = this.component && this.element.find('input').length;
-		if(this.component && this.component.length === 0)
-			this.component = false;
-
-		this.picker = $(DPGlobal.template);
-		this._buildEvents();
-		this._attachEvents();
-
-		if(this.isInline) {
-			this.picker.addClass('datepicker-inline').appendTo(this.element);
-		} else {
-			this.picker.addClass('datepicker-dropdown dropdown-menu');
-		}
-
-		if (this.o.rtl){
-			this.picker.addClass('datepicker-rtl');
-			this.picker.find('.prev i, .next i')
-						.toggleClass('icon-arrow-left icon-arrow-right');
-		}
-
-
-		this.viewMode = this.o.startView;
-
-		if (this.o.calendarWeeks)
-			this.picker.find('tfoot th.today')
-						.attr('colspan', function(i, val){
-							return parseInt(val) + 1;
-						});
-
-		this._allow_update = false;
-
-		this.setStartDate(this.o.startDate);
-		this.setEndDate(this.o.endDate);
-		this.setDaysOfWeekDisabled(this.o.daysOfWeekDisabled);
-
-		this.fillDow();
-		this.fillMonths();
-
-		this._allow_update = true;
-
-		this.update();
-		this.showMode();
-
-		if(this.isInline) {
-			this.show();
-		}
-	};
-
-	Datepicker.prototype = {
-		constructor: Datepicker,
-
-		_process_options: function(opts){
-			// Store raw options for reference
-			this._o = $.extend({}, this._o, opts);
-			// Processed options
-			var o = this.o = $.extend({}, this._o);
-
-			// Check if "de-DE" style date is available, if not language should
-			// fallback to 2 letter code eg "de"
-			var lang = o.language;
-			if (!dates[lang]) {
-				lang = lang.split('-')[0];
-				if (!dates[lang])
-					lang = defaults.language;
-			}
-			o.language = lang;
-
-			switch(o.startView){
-				case 2:
-				case 'decade':
-					o.startView = 2;
-					break;
-				case 1:
-				case 'year':
-					o.startView = 1;
-					break;
-				default:
-					o.startView = 0;
-			}
-
-			switch (o.minViewMode) {
-				case 1:
-				case 'months':
-					o.minViewMode = 1;
-					break;
-				case 2:
-				case 'years':
-					o.minViewMode = 2;
-					break;
-				default:
-					o.minViewMode = 0;
-			}
-
-			o.startView = Math.max(o.startView, o.minViewMode);
-
-			o.weekStart %= 7;
-			o.weekEnd = ((o.weekStart + 6) % 7);
-
-			var format = DPGlobal.parseFormat(o.format)
-			if (o.startDate !== -Infinity) {
-				o.startDate = DPGlobal.parseDate(o.startDate, format, o.language);
-			}
-			if (o.endDate !== Infinity) {
-				o.endDate = DPGlobal.parseDate(o.endDate, format, o.language);
-			}
-
-			o.daysOfWeekDisabled = o.daysOfWeekDisabled||[];
-			if (!$.isArray(o.daysOfWeekDisabled))
-				o.daysOfWeekDisabled = o.daysOfWeekDisabled.split(/[,\s]*/);
-			o.daysOfWeekDisabled = $.map(o.daysOfWeekDisabled, function (d) {
-				return parseInt(d, 10);
-			});
-		},
-		_events: [],
-		_secondaryEvents: [],
-		_applyEvents: function(evs){
-			for (var i=0, el, ev; i<evs.length; i++){
-				el = evs[i][0];
-				ev = evs[i][1];
-				el.on(ev);
-			}
-		},
-		_unapplyEvents: function(evs){
-			for (var i=0, el, ev; i<evs.length; i++){
-				el = evs[i][0];
-				ev = evs[i][1];
-				el.off(ev);
-			}
-		},
-		_buildEvents: function(){
-			if (this.isInput) { // single input
-				this._events = [
-					[this.element, {
-						focus: $.proxy(this.show, this),
-						keyup: $.proxy(this.update, this),
-						keydown: $.proxy(this.keydown, this)
-					}]
-				];
-			}
-			else if (this.component && this.hasInput){ // component: input + button
-				this._events = [
-					// For components that are not readonly, allow keyboard nav
-					[this.element.find('input'), {
-						focus: $.proxy(this.show, this),
-						keyup: $.proxy(this.update, this),
-						keydown: $.proxy(this.keydown, this)
-					}],
-					[this.component, {
-						click: $.proxy(this.show, this)
-					}]
-				];
-			}
-			else if (this.element.is('div')) {  // inline datepicker
-				this.isInline = true;
-			}
-			else {
-				this._events = [
-					[this.element, {
-						click: $.proxy(this.show, this)
-					}]
-				];
-			}
-
-			this._secondaryEvents = [
-				[this.picker, {
-					click: $.proxy(this.click, this)
-				}],
-				[$(window), {
-					resize: $.proxy(this.place, this)
-				}],
-				[$(document), {
-					mousedown: $.proxy(function (e) {
-						// Clicked outside the datepicker, hide it
-						if (!(
-							this.element.is(e.target) ||
-							this.element.find(e.target).size() ||
-							this.picker.is(e.target) ||
-							this.picker.find(e.target).size()
-						)) {
-							this.hide();
-						}
-					}, this)
-				}]
-			];
-		},
-		_attachEvents: function(){
-			this._detachEvents();
-			this._applyEvents(this._events);
-		},
-		_detachEvents: function(){
-			this._unapplyEvents(this._events);
-		},
-		_attachSecondaryEvents: function(){
-			this._detachSecondaryEvents();
-			this._applyEvents(this._secondaryEvents);
-		},
-		_detachSecondaryEvents: function(){
-			this._unapplyEvents(this._secondaryEvents);
-		},
-		_trigger: function(event, altdate){
-			var date = altdate || this.date,
-				local_date = new Date(date.getTime() + (date.getTimezoneOffset()*60000));
-
-			this.element.trigger({
-				type: event,
-				date: local_date,
-				format: $.proxy(function(altformat){
-					var format = altformat || this.o.format;
-					return DPGlobal.formatDate(date, format, this.o.language);
-				}, this)
-			});
-		},
-
-		show: function(e) {
-			if (!this.isInline)
-				this.picker.appendTo('body');
-			this.picker.show();
-			this.height = this.component ? this.component.outerHeight() : this.element.outerHeight();
-			this.place();
-			this._attachSecondaryEvents();
-			if (e) {
-				e.preventDefault();
-			}
-			this._trigger('show');
-		},
-
-		hide: function(e){
-			if(this.isInline) return;
-			if (!this.picker.is(':visible')) return;
-			this.picker.hide().detach();
-			this._detachSecondaryEvents();
-			this.viewMode = this.o.startView;
-			this.showMode();
-
-			if (
-				this.o.forceParse &&
-				(
-					this.isInput && this.element.val() ||
-					this.hasInput && this.element.find('input').val()
-				)
-			)
-				this.setValue();
-			this._trigger('hide');
-		},
-
-		remove: function() {
-			this.hide();
-			this._detachEvents();
-			this._detachSecondaryEvents();
-			this.picker.remove();
-			delete this.element.data().datepicker;
-			if (!this.isInput) {
-				delete this.element.data().date;
-			}
-		},
-
-		getDate: function() {
-			var d = this.getUTCDate();
-			return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
-		},
-
-		getUTCDate: function() {
-			return this.date;
-		},
-
-		setDate: function(d) {
-			this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
-		},
-
-		setUTCDate: function(d) {
-			this.date = d;
-			this.setValue();
-		},
-
-		setValue: function() {
-			var formatted = this.getFormattedDate();
-			if (!this.isInput) {
-				if (this.component){
-					this.element.find('input').val(formatted);
-				}
-			} else {
-				this.element.val(formatted);
-			}
-		},
-
-		getFormattedDate: function(format) {
-			if (format === undefined)
-				format = this.o.format;
-			return DPGlobal.formatDate(this.date, format, this.o.language);
-		},
-
-		setStartDate: function(startDate){
-			this._process_options({startDate: startDate});
-			this.update();
-			this.updateNavArrows();
-		},
-
-		setEndDate: function(endDate){
-			this._process_options({endDate: endDate});
-			this.update();
-			this.updateNavArrows();
-		},
-
-		setDaysOfWeekDisabled: function(daysOfWeekDisabled){
-			this._process_options({daysOfWeekDisabled: daysOfWeekDisabled});
-			this.update();
-			this.updateNavArrows();
-		},
-
-		place: function(){
-						if(this.isInline) return;
-			var zIndex = parseInt(this.element.parents().filter(function() {
-							return $(this).css('z-index') != 'auto';
-						}).first().css('z-index'))+10;
-			var offset = this.component ? this.component.parent().offset() : this.element.offset();
-			var height = this.component ? this.component.outerHeight(true) : this.element.outerHeight(true);
-			this.picker.css({
-				top: offset.top + height,
-				left: offset.left,
-				zIndex: zIndex
-			});
-		},
-
-		_allow_update: true,
-		update: function(){
-			if (!this._allow_update) return;
-
-			var date, fromArgs = false;
-			if(arguments && arguments.length && (typeof arguments[0] === 'string' || arguments[0] instanceof Date)) {
-				date = arguments[0];
-				fromArgs = true;
-			} else {
-				date = this.isInput ? this.element.val() : this.element.data('date') || this.element.find('input').val();
-				delete this.element.data().date;
-			}
-
-			this.date = DPGlobal.parseDate(date, this.o.format, this.o.language);
-
-			if(fromArgs) this.setValue();
-
-			if (this.date < this.o.startDate) {
-				this.viewDate = new Date(this.o.startDate);
-			} else if (this.date > this.o.endDate) {
-				this.viewDate = new Date(this.o.endDate);
-			} else {
-				this.viewDate = new Date(this.date);
-			}
-			this.fill();
-		},
-
-		fillDow: function(){
-			var dowCnt = this.o.weekStart,
-			html = '<tr>';
-			if(this.o.calendarWeeks){
-				var cell = '<th class="cw">&nbsp;</th>';
-				html += cell;
-				this.picker.find('.datepicker-days thead tr:first-child').prepend(cell);
-			}
-			while (dowCnt < this.o.weekStart + 7) {
-				html += '<th class="dow">'+dates[this.o.language].daysMin[(dowCnt++)%7]+'</th>';
-			}
-			html += '</tr>';
-			this.picker.find('.datepicker-days thead').append(html);
-		},
-
-		fillMonths: function(){
-			var html = '',
-			i = 0;
-			while (i < 12) {
-				html += '<span class="month">'+dates[this.o.language].monthsShort[i++]+'</span>';
-			}
-			this.picker.find('.datepicker-months td').html(html);
-		},
-
-		setRange: function(range){
-			if (!range || !range.length)
-				delete this.range;
-			else
-				this.range = $.map(range, function(d){ return d.valueOf(); });
-			this.fill();
-		},
-
-		getClassNames: function(date){
-			var cls = [],
-				year = this.viewDate.getUTCFullYear(),
-				month = this.viewDate.getUTCMonth(),
-				currentDate = this.date.valueOf(),
-				today = new Date();
-			if (date.getUTCFullYear() < year || (date.getUTCFullYear() == year && date.getUTCMonth() < month)) {
-				cls.push('old');
-			} else if (date.getUTCFullYear() > year || (date.getUTCFullYear() == year && date.getUTCMonth() > month)) {
-				cls.push('new');
-			}
-			// Compare internal UTC date with local today, not UTC today
-			if (this.o.todayHighlight &&
-				date.getUTCFullYear() == today.getFullYear() &&
-				date.getUTCMonth() == today.getMonth() &&
-				date.getUTCDate() == today.getDate()) {
-				cls.push('today');
-			}
-			if (currentDate && date.valueOf() == currentDate) {
-				cls.push('active');
-			}
-			if (date.valueOf() < this.o.startDate || date.valueOf() > this.o.endDate ||
-				$.inArray(date.getUTCDay(), this.o.daysOfWeekDisabled) !== -1) {
-				cls.push('disabled');
-			}
-			if (this.range){
-				if (date > this.range[0] && date < this.range[this.range.length-1]){
-					cls.push('range');
-				}
-				if ($.inArray(date.valueOf(), this.range) != -1){
-					cls.push('selected');
-				}
-			}
-			return cls;
-		},
-
-		fill: function() {
-			var d = new Date(this.viewDate),
-				year = d.getUTCFullYear(),
-				month = d.getUTCMonth(),
-				startYear = this.o.startDate !== -Infinity ? this.o.startDate.getUTCFullYear() : -Infinity,
-				startMonth = this.o.startDate !== -Infinity ? this.o.startDate.getUTCMonth() : -Infinity,
-				endYear = this.o.endDate !== Infinity ? this.o.endDate.getUTCFullYear() : Infinity,
-				endMonth = this.o.endDate !== Infinity ? this.o.endDate.getUTCMonth() : Infinity,
-				currentDate = this.date && this.date.valueOf(),
-				tooltip;
-			this.picker.find('.datepicker-days thead th.datepicker-switch')
-						.text(dates[this.o.language].months[month]+' '+year);
-			this.picker.find('tfoot th.today')
-						.text(dates[this.o.language].today)
-						.toggle(this.o.todayBtn !== false);
-			this.picker.find('tfoot th.clear')
-						.text(dates[this.o.language].clear)
-						.toggle(this.o.clearBtn !== false);
-			this.updateNavArrows();
-			this.fillMonths();
-			var prevMonth = UTCDate(year, month-1, 28,0,0,0,0),
-				day = DPGlobal.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
-			prevMonth.setUTCDate(day);
-			prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.o.weekStart + 7)%7);
-			var nextMonth = new Date(prevMonth);
-			nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
-			nextMonth = nextMonth.valueOf();
-			var html = [];
-			var clsName;
-			while(prevMonth.valueOf() < nextMonth) {
-				if (prevMonth.getUTCDay() == this.o.weekStart) {
-					html.push('<tr>');
-					if(this.o.calendarWeeks){
-						// ISO 8601: First week contains first thursday.
-						// ISO also states week starts on Monday, but we can be more abstract here.
-						var
-							// Start of current week: based on weekstart/current date
-							ws = new Date(+prevMonth + (this.o.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
-							// Thursday of this week
-							th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
-							// First Thursday of year, year from thursday
-							yth = new Date(+(yth = UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
-							// Calendar week: ms between thursdays, div ms per day, div 7 days
-							calWeek =  (th - yth) / 864e5 / 7 + 1;
-						html.push('<td class="cw">'+ calWeek +'</td>');
-
-					}
-				}
-				clsName = this.getClassNames(prevMonth);
-				clsName.push('day');
-
-				var before = this.o.beforeShowDay(prevMonth);
-				if (before === undefined)
-					before = {};
-				else if (typeof(before) === 'boolean')
-					before = {enabled: before};
-				else if (typeof(before) === 'string')
-					before = {classes: before};
-				if (before.enabled === false)
-					clsName.push('disabled');
-				if (before.classes)
-					clsName = clsName.concat(before.classes.split(/\s+/));
-				if (before.tooltip)
-					tooltip = before.tooltip;
-
-				clsName = $.unique(clsName);
-				html.push('<td class="'+clsName.join(' ')+'"' + (tooltip ? ' title="'+tooltip+'"' : '') + '>'+prevMonth.getUTCDate() + '</td>');
-				if (prevMonth.getUTCDay() == this.o.weekEnd) {
-					html.push('</tr>');
-				}
-				prevMonth.setUTCDate(prevMonth.getUTCDate()+1);
-			}
-			this.picker.find('.datepicker-days tbody').empty().append(html.join(''));
-			var currentYear = this.date && this.date.getUTCFullYear();
-
-			var months = this.picker.find('.datepicker-months')
-						.find('th:eq(1)')
-							.text(year)
-							.end()
-						.find('span').removeClass('active');
-			if (currentYear && currentYear == year) {
-				months.eq(this.date.getUTCMonth()).addClass('active');
-			}
-			if (year < startYear || year > endYear) {
-				months.addClass('disabled');
-			}
-			if (year == startYear) {
-				months.slice(0, startMonth).addClass('disabled');
-			}
-			if (year == endYear) {
-				months.slice(endMonth+1).addClass('disabled');
-			}
-
-			html = '';
-			year = parseInt(year/10, 10) * 10;
-			var yearCont = this.picker.find('.datepicker-years')
-								.find('th:eq(1)')
-									.text(year + '-' + (year + 9))
-									.end()
-								.find('td');
-			year -= 1;
-			for (var i = -1; i < 11; i++) {
-				html += '<span class="year'+(i == -1 ? ' old' : i == 10 ? ' new' : '')+(currentYear == year ? ' active' : '')+(year < startYear || year > endYear ? ' disabled' : '')+'">'+year+'</span>';
-				year += 1;
-			}
-			yearCont.html(html);
-		},
-
-		updateNavArrows: function() {
-			if (!this._allow_update) return;
-
-			var d = new Date(this.viewDate),
-				year = d.getUTCFullYear(),
-				month = d.getUTCMonth();
-			switch (this.viewMode) {
-				case 0:
-					if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear() && month <= this.o.startDate.getUTCMonth()) {
-						this.picker.find('.prev').css({visibility: 'hidden'});
-					} else {
-						this.picker.find('.prev').css({visibility: 'visible'});
-					}
-					if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear() && month >= this.o.endDate.getUTCMonth()) {
-						this.picker.find('.next').css({visibility: 'hidden'});
-					} else {
-						this.picker.find('.next').css({visibility: 'visible'});
-					}
-					break;
-				case 1:
-				case 2:
-					if (this.o.startDate !== -Infinity && year <= this.o.startDate.getUTCFullYear()) {
-						this.picker.find('.prev').css({visibility: 'hidden'});
-					} else {
-						this.picker.find('.prev').css({visibility: 'visible'});
-					}
-					if (this.o.endDate !== Infinity && year >= this.o.endDate.getUTCFullYear()) {
-						this.picker.find('.next').css({visibility: 'hidden'});
-					} else {
-						this.picker.find('.next').css({visibility: 'visible'});
-					}
-					break;
-			}
-		},
-
-		click: function(e) {
-			e.preventDefault();
-			var target = $(e.target).closest('span, td, th');
-			if (target.length == 1) {
-				switch(target[0].nodeName.toLowerCase()) {
-					case 'th':
-						switch(target[0].className) {
-							case 'datepicker-switch':
-								this.showMode(1);
-								break;
-							case 'prev':
-							case 'next':
-								var dir = DPGlobal.modes[this.viewMode].navStep * (target[0].className == 'prev' ? -1 : 1);
-								switch(this.viewMode){
-									case 0:
-										this.viewDate = this.moveMonth(this.viewDate, dir);
-										break;
-									case 1:
-									case 2:
-										this.viewDate = this.moveYear(this.viewDate, dir);
-										break;
-								}
-								this.fill();
-								break;
-							case 'today':
-								var date = new Date();
-								date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
-
-								this.showMode(-2);
-								var which = this.o.todayBtn == 'linked' ? null : 'view';
-								this._setDate(date, which);
-								break;
-							case 'clear':
-								var element;
-								if (this.isInput)
-									element = this.element;
-								else if (this.component)
-									element = this.element.find('input');
-								if (element)
-									element.val("").change();
-								this._trigger('changeDate');
-								this.update();
-								if (this.o.autoclose)
-									this.hide();
-								break;
-						}
-						break;
-					case 'span':
-						if (!target.is('.disabled')) {
-							this.viewDate.setUTCDate(1);
-							if (target.is('.month')) {
-								var day = 1;
-								var month = target.parent().find('span').index(target);
-								var year = this.viewDate.getUTCFullYear();
-								this.viewDate.setUTCMonth(month);
-								this._trigger('changeMonth', this.viewDate);
-								if (this.o.minViewMode === 1) {
-									this._setDate(UTCDate(year, month, day,0,0,0,0));
-								}
-							} else {
-								var year = parseInt(target.text(), 10)||0;
-								var day = 1;
-								var month = 0;
-								this.viewDate.setUTCFullYear(year);
-								this._trigger('changeYear', this.viewDate);
-								if (this.o.minViewMode === 2) {
-									this._setDate(UTCDate(year, month, day,0,0,0,0));
-								}
-							}
-							this.showMode(-1);
-							this.fill();
-						}
-						break;
-					case 'td':
-						if (target.is('.day') && !target.is('.disabled')){
-							var day = parseInt(target.text(), 10)||1;
-							var year = this.viewDate.getUTCFullYear(),
-								month = this.viewDate.getUTCMonth();
-							if (target.is('.old')) {
-								if (month === 0) {
-									month = 11;
-									year -= 1;
-								} else {
-									month -= 1;
-								}
-							} else if (target.is('.new')) {
-								if (month == 11) {
-									month = 0;
-									year += 1;
-								} else {
-									month += 1;
-								}
-							}
-							this._setDate(UTCDate(year, month, day,0,0,0,0));
-						}
-						break;
-				}
-			}
-		},
-
-		_setDate: function(date, which){
-			if (!which || which == 'date')
-				this.date = new Date(date);
-			if (!which || which  == 'view')
-				this.viewDate = new Date(date);
-			this.fill();
-			this.setValue();
-			this._trigger('changeDate');
-			var element;
-			if (this.isInput) {
-				element = this.element;
-			} else if (this.component){
-				element = this.element.find('input');
-			}
-			if (element) {
-				element.change();
-				if (this.o.autoclose && (!which || which == 'date')) {
-					this.hide();
-				}
-			}
-		},
-
-		moveMonth: function(date, dir){
-			if (!dir) return date;
-			var new_date = new Date(date.valueOf()),
-				day = new_date.getUTCDate(),
-				month = new_date.getUTCMonth(),
-				mag = Math.abs(dir),
-				new_month, test;
-			dir = dir > 0 ? 1 : -1;
-			if (mag == 1){
-				test = dir == -1
-					// If going back one month, make sure month is not current month
-					// (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
-					? function(){ return new_date.getUTCMonth() == month; }
-					// If going forward one month, make sure month is as expected
-					// (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
-					: function(){ return new_date.getUTCMonth() != new_month; };
-				new_month = month + dir;
-				new_date.setUTCMonth(new_month);
-				// Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
-				if (new_month < 0 || new_month > 11)
-					new_month = (new_month + 12) % 12;
-			} else {
-				// For magnitudes >1, move one month at a time...
-				for (var i=0; i<mag; i++)
-					// ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
-					new_date = this.moveMonth(new_date, dir);
-				// ...then reset the day, keeping it in the new month
-				new_month = new_date.getUTCMonth();
-				new_date.setUTCDate(day);
-				test = function(){ return new_month != new_date.getUTCMonth(); };
-			}
-			// Common date-resetting loop -- if date is beyond end of month, make it
-			// end of month
-			while (test()){
-				new_date.setUTCDate(--day);
-				new_date.setUTCMonth(new_month);
-			}
-			return new_date;
-		},
-
-		moveYear: function(date, dir){
-			return this.moveMonth(date, dir*12);
-		},
-
-		dateWithinRange: function(date){
-			return date >= this.o.startDate && date <= this.o.endDate;
-		},
-
-		keydown: function(e){
-			if (this.picker.is(':not(:visible)')){
-				if (e.keyCode == 27) // allow escape to hide and re-show picker
-					this.show();
-				return;
-			}
-			var dateChanged = false,
-				dir, day, month,
-				newDate, newViewDate;
-			switch(e.keyCode){
-				case 27: // escape
-					this.hide();
-					e.preventDefault();
-					break;
-				case 37: // left
-				case 39: // right
-					if (!this.o.keyboardNavigation) break;
-					dir = e.keyCode == 37 ? -1 : 1;
-					if (e.ctrlKey){
-						newDate = this.moveYear(this.date, dir);
-						newViewDate = this.moveYear(this.viewDate, dir);
-					} else if (e.shiftKey){
-						newDate = this.moveMonth(this.date, dir);
-						newViewDate = this.moveMonth(this.viewDate, dir);
-					} else {
-						newDate = new Date(this.date);
-						newDate.setUTCDate(this.date.getUTCDate() + dir);
-						newViewDate = new Date(this.viewDate);
-						newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
-					}
-					if (this.dateWithinRange(newDate)){
-						this.date = newDate;
-						this.viewDate = newViewDate;
-						this.setValue();
-						this.update();
-						e.preventDefault();
-						dateChanged = true;
-					}
-					break;
-				case 38: // up
-				case 40: // down
-					if (!this.o.keyboardNavigation) break;
-					dir = e.keyCode == 38 ? -1 : 1;
-					if (e.ctrlKey){
-						newDate = this.moveYear(this.date, dir);
-						newViewDate = this.moveYear(this.viewDate, dir);
-					} else if (e.shiftKey){
-						newDate = this.moveMonth(this.date, dir);
-						newViewDate = this.moveMonth(this.viewDate, dir);
-					} else {
-						newDate = new Date(this.date);
-						newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
-						newViewDate = new Date(this.viewDate);
-						newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
-					}
-					if (this.dateWithinRange(newDate)){
-						this.date = newDate;
-						this.viewDate = newViewDate;
-						this.setValue();
-						this.update();
-						e.preventDefault();
-						dateChanged = true;
-					}
-					break;
-				case 13: // enter
-					this.hide();
-					e.preventDefault();
-					break;
-				case 9: // tab
-					this.hide();
-					break;
-			}
-			if (dateChanged){
-				this._trigger('changeDate');
-				var element;
-				if (this.isInput) {
-					element = this.element;
-				} else if (this.component){
-					element = this.element.find('input');
-				}
-				if (element) {
-					element.change();
-				}
-			}
-		},
-
-		showMode: function(dir) {
-			if (dir) {
-				this.viewMode = Math.max(this.o.minViewMode, Math.min(2, this.viewMode + dir));
-			}
-			/*
-				vitalets: fixing bug of very special conditions:
-				jquery 1.7.1 + webkit + show inline datepicker in bootstrap popover.
-				Method show() does not set display css correctly and datepicker is not shown.
-				Changed to .css('display', 'block') solve the problem.
-				See https://github.com/vitalets/x-editable/issues/37
-
-				In jquery 1.7.2+ everything works fine.
-			*/
-			//this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).show();
-			this.picker.find('>div').hide().filter('.datepicker-'+DPGlobal.modes[this.viewMode].clsName).css('display', 'block');
-			this.updateNavArrows();
-		}
-	};
-
-	var DateRangePicker = function(element, options){
-		this.element = $(element);
-		this.inputs = $.map(options.inputs, function(i){ return i.jquery ? i[0] : i; });
-		delete options.inputs;
-
-		$(this.inputs)
-			.datepicker(options)
-			.bind('changeDate', $.proxy(this.dateUpdated, this));
-
-		this.pickers = $.map(this.inputs, function(i){ return $(i).data('datepicker'); });
-		this.updateDates();
-	};
-	DateRangePicker.prototype = {
-		updateDates: function(){
-			this.dates = $.map(this.pickers, function(i){ return i.date; });
-			this.updateRanges();
-		},
-		updateRanges: function(){
-			var range = $.map(this.dates, function(d){ return d.valueOf(); });
-			$.each(this.pickers, function(i, p){
-				p.setRange(range);
-			});
-		},
-		dateUpdated: function(e){
-			var dp = $(e.target).data('datepicker'),
-				new_date = dp.getUTCDate(),
-				i = $.inArray(e.target, this.inputs),
-				l = this.inputs.length;
-			if (i == -1) return;
-
-			if (new_date < this.dates[i]){
-				// Date being moved earlier/left
-				while (i>=0 && new_date < this.dates[i]){
-					this.pickers[i--].setUTCDate(new_date);
-				}
-			}
-			else if (new_date > this.dates[i]){
-				// Date being moved later/right
-				while (i<l && new_date > this.dates[i]){
-					this.pickers[i++].setUTCDate(new_date);
-				}
-			}
-			this.updateDates();
-		},
-		remove: function(){
-			$.map(this.pickers, function(p){ p.remove(); });
-			delete this.element.data().datepicker;
-		}
-	};
-
-	function opts_from_el(el, prefix){
-		// Derive options from element data-attrs
-		var data = $(el).data(),
-			out = {}, inkey,
-			replace = new RegExp('^' + prefix.toLowerCase() + '([A-Z])'),
-			prefix = new RegExp('^' + prefix.toLowerCase());
-		for (var key in data)
-			if (prefix.test(key)){
-				inkey = key.replace(replace, function(_,a){ return a.toLowerCase(); });
-				out[inkey] = data[key];
-			}
-		return out;
-	}
-
-	function opts_from_locale(lang){
-		// Derive options from locale plugins
-		var out = {};
-		// Check if "de-DE" style date is available, if not language should
-		// fallback to 2 letter code eg "de"
-		if (!dates[lang]) {
-			lang = lang.split('-')[0]
-			if (!dates[lang])
-				return;
-		}
-		var d = dates[lang];
-		$.each(locale_opts, function(i,k){
-			if (k in d)
-				out[k] = d[k];
-		});
-		return out;
-	}
-
-	var old = $.fn.datepicker;
-	var datepicker = $.fn.datepicker = function ( option ) {
-		var args = Array.apply(null, arguments);
-		args.shift();
-		var internal_return,
-			this_return;
-		this.each(function () {
-			var $this = $(this),
-				data = $this.data('datepicker'),
-				options = typeof option == 'object' && option;
-			if (!data) {
-				var elopts = opts_from_el(this, 'date'),
-					// Preliminary otions
-					xopts = $.extend({}, defaults, elopts, options),
-					locopts = opts_from_locale(xopts.language),
-					// Options priority: js args, data-attrs, locales, defaults
-					opts = $.extend({}, defaults, locopts, elopts, options);
-				if ($this.is('.input-daterange') || opts.inputs){
-					var ropts = {
-						inputs: opts.inputs || $this.find('input').toArray()
-					};
-					$this.data('datepicker', (data = new DateRangePicker(this, $.extend(opts, ropts))));
-				}
-				else{
-					$this.data('datepicker', (data = new Datepicker(this, opts)));
-				}
-			}
-			if (typeof option == 'string' && typeof data[option] == 'function') {
-				internal_return = data[option].apply(data, args);
-				if (internal_return !== undefined)
-					return false;
-			}
-		});
-		if (internal_return !== undefined)
-			return internal_return;
-		else
-			return this;
-	};
-
-	var defaults = $.fn.datepicker.defaults = {
-		autoclose: false,
-		beforeShowDay: $.noop,
-		calendarWeeks: false,
-		clearBtn: false,
-		daysOfWeekDisabled: [],
-		endDate: Infinity,
-		forceParse: true,
-		format: 'mm/dd/yyyy',
-		keyboardNavigation: true,
-		language: 'en',
-		minViewMode: 0,
-		rtl: false,
-		startDate: -Infinity,
-		startView: 0,
-		todayBtn: false,
-		todayHighlight: false,
-		weekStart: 0
-	};
-	var locale_opts = $.fn.datepicker.locale_opts = [
-		'format',
-		'rtl',
-		'weekStart'
-	];
-	$.fn.datepicker.Constructor = Datepicker;
-	var dates = $.fn.datepicker.dates = {
-		en: {
-			days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
-			daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
-			daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
-			months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
-			monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
-			today: "Today",
-			clear: "Clear"
-		}
-	};
-
-	var DPGlobal = {
-		modes: [
-			{
-				clsName: 'days',
-				navFnc: 'Month',
-				navStep: 1
-			},
-			{
-				clsName: 'months',
-				navFnc: 'FullYear',
-				navStep: 1
-			},
-			{
-				clsName: 'years',
-				navFnc: 'FullYear',
-				navStep: 10
-		}],
-		isLeapYear: function (year) {
-			return (((year % 4 === 0) && (year % 100 !== 0)) || (year % 400 === 0));
-		},
-		getDaysInMonth: function (year, month) {
-			return [31, (DPGlobal.isLeapYear(year) ? 29 : 28), 31, 30, 31, 30, 31, 31, 30, 31, 30, 31][month];
-		},
-		validParts: /dd?|DD?|mm?|MM?|yy(?:yy)?/g,
-		nonpunctuation: /[^ -\/:-@\[\u3400-\u9fff-`{-~\t\n\r]+/g,
-		parseFormat: function(format){
-			// IE treats \0 as a string end in inputs (truncating the value),
-			// so it's a bad format delimiter, anyway
-			var separators = format.replace(this.validParts, '\0').split('\0'),
-				parts = format.match(this.validParts);
-			if (!separators || !separators.length || !parts || parts.length === 0){
-				throw new Error("Invalid date format.");
-			}
-			return {separators: separators, parts: parts};
-		},
-		parseDate: function(date, format, language) {
-			if (date instanceof Date) return date;
-			if (typeof format === 'string')
-				format = DPGlobal.parseFormat(format);
-			if (/^[\-+]\d+[dmwy]([\s,]+[\-+]\d+[dmwy])*$/.test(date)) {
-				var part_re = /([\-+]\d+)([dmwy])/,
-					parts = date.match(/([\-+]\d+)([dmwy])/g),
-					part, dir;
-				date = new Date();
-				for (var i=0; i<parts.length; i++) {
-					part = part_re.exec(parts[i]);
-					dir = parseInt(part[1]);
-					switch(part[2]){
-						case 'd':
-							date.setUTCDate(date.getUTCDate() + dir);
-							break;
-						case 'm':
-							date = Datepicker.prototype.moveMonth.call(Datepicker.prototype, date, dir);
-							break;
-						case 'w':
-							date.setUTCDate(date.getUTCDate() + dir * 7);
-							break;
-						case 'y':
-							date = Datepicker.prototype.moveYear.call(Datepicker.prototype, date, dir);
-							break;
-					}
-				}
-				return UTCDate(date.getUTCFullYear(), date.getUTCMonth(), date.getUTCDate(), 0, 0, 0);
-			}
-			var parts = date && date.match(this.nonpunctuation) || [],
-				date = new Date(),
-				parsed = {},
-				setters_order = ['yyyy', 'yy', 'M', 'MM', 'm', 'mm', 'd', 'dd'],
-				setters_map = {
-					yyyy: function(d,v){ return d.setUTCFullYear(v); },
-					yy: function(d,v){ return d.setUTCFullYear(2000+v); },
-					m: function(d,v){
-						v -= 1;
-						while (v<0) v += 12;
-						v %= 12;
-						d.setUTCMonth(v);
-						while (d.getUTCMonth() != v)
-							d.setUTCDate(d.getUTCDate()-1);
-						return d;
-					},
-					d: function(d,v){ return d.setUTCDate(v); }
-				},
-				val, filtered, part;
-			setters_map['M'] = setters_map['MM'] = setters_map['mm'] = setters_map['m'];
-			setters_map['dd'] = setters_map['d'];
-			date = UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
-			var fparts = format.parts.slice();
-			// Remove noop parts
-			if (parts.length != fparts.length) {
-				fparts = $(fparts).filter(function(i,p){
-					return $.inArray(p, setters_order) !== -1;
-				}).toArray();
-			}
-			// Process remainder
-			if (parts.length == fparts.length) {
-				for (var i=0, cnt = fparts.length; i < cnt; i++) {
-					val = parseInt(parts[i], 10);
-					part = fparts[i];
-					if (isNaN(val)) {
-						switch(part) {
-							case 'MM':
-								filtered = $(dates[language].months).filter(function(){
-									var m = this.slice(0, parts[i].length),
-										p = parts[i].slice(0, m.length);
-									return m == p;
-								});
-								val = $.inArray(filtered[0], dates[language].months) + 1;
-								break;
-							case 'M':
-								filtered = $(dates[language].monthsShort).filter(function(){
-									var m = this.slice(0, parts[i].length),
-										p = parts[i].slice(0, m.length);
-									return m == p;
-								});
-								val = $.inArray(filtered[0], dates[language].monthsShort) + 1;
-								break;
-						}
-					}
-					parsed[part] = val;
-				}
-				for (var i=0, s; i<setters_order.length; i++){
-					s = setters_order[i];
-					if (s in parsed && !isNaN(parsed[s]))
-						setters_map[s](date, parsed[s]);
-				}
-			}
-			return date;
-		},
-		formatDate: function(date, format, language){
-			if (typeof format === 'string')
-				format = DPGlobal.parseFormat(format);
-			var val = {
-				d: date.getUTCDate(),
-				D: dates[language].daysShort[date.getUTCDay()],
-				DD: dates[language].days[date.getUTCDay()],
-				m: date.getUTCMonth() + 1,
-				M: dates[language].monthsShort[date.getUTCMonth()],
-				MM: dates[language].months[date.getUTCMonth()],
-				yy: date.getUTCFullYear().toString().substring(2),
-				yyyy: date.getUTCFullYear()
-			};
-			val.dd = (val.d < 10 ? '0' : '') + val.d;
-			val.mm = (val.m < 10 ? '0' : '') + val.m;
-			var date = [],
-				seps = $.extend([], format.separators);
-			for (var i=0, cnt = format.parts.length; i <= cnt; i++) {
-				if (seps.length)
-					date.push(seps.shift());
-				date.push(val[format.parts[i]]);
-			}
-			return date.join('');
-		},
-		headTemplate: '<thead>'+
-							'<tr>'+
-								'<th class="prev"><i class="icon-arrow-left"/></th>'+
-								'<th colspan="5" class="datepicker-switch"></th>'+
-								'<th class="next"><i class="icon-arrow-right"/></th>'+
-							'</tr>'+
-						'</thead>',
-		contTemplate: '<tbody><tr><td colspan="7"></td></tr></tbody>',
-		footTemplate: '<tfoot><tr><th colspan="7" class="today"></th></tr><tr><th colspan="7" class="clear"></th></tr></tfoot>'
-	};
-	DPGlobal.template = '<div class="datepicker">'+
-							'<div class="datepicker-days">'+
-								'<table class=" table-condensed">'+
-									DPGlobal.headTemplate+
-									'<tbody></tbody>'+
-									DPGlobal.footTemplate+
-								'</table>'+
-							'</div>'+
-							'<div class="datepicker-months">'+
-								'<table class="table-condensed">'+
-									DPGlobal.headTemplate+
-									DPGlobal.contTemplate+
-									DPGlobal.footTemplate+
-								'</table>'+
-							'</div>'+
-							'<div class="datepicker-years">'+
-								'<table class="table-condensed">'+
-									DPGlobal.headTemplate+
-									DPGlobal.contTemplate+
-									DPGlobal.footTemplate+
-								'</table>'+
-							'</div>'+
-						'</div>';
-
-	$.fn.datepicker.DPGlobal = DPGlobal;
-
-
-	/* DATEPICKER NO CONFLICT
-	* =================== */
-
-	$.fn.datepicker.noConflict = function(){
-		$.fn.datepicker = old;
-		return this;
-	};
-
-
-	/* DATEPICKER DATA-API
-	* ================== */
-
-	$(document).on(
-		'focus.datepicker.data-api click.datepicker.data-api',
-		'[data-provide="datepicker"]',
-		function(e){
-			var $this = $(this);
-			if ($this.data('datepicker')) return;
-			e.preventDefault();
-			// component click requires us to explicitly show it
-			datepicker.call($this, 'show');
-		}
-	);
-	$(function(){
-		//$('[data-provide="datepicker-inline"]').datepicker();
-        //vit: changed to support noConflict()
-        datepicker.call($('[data-provide="datepicker-inline"]'));
-	});
-
-}( window.jQuery ));
-
-/**
-Bootstrap-datepicker.  
-Description and examples: https://github.com/eternicode/bootstrap-datepicker.  
-For **i18n** you should include js file from here: https://github.com/eternicode/bootstrap-datepicker/tree/master/js/locales
-and set `language` option.  
-Since 1.4.0 date has different appearance in **popup** and **inline** modes. 
-
-@class date
-@extends abstractinput
-@final
-@example
-<a href="#" id="dob" data-type="date" data-pk="1" data-url="/post" data-title="Select date">15/05/1984</a>
-<script>
-$(function(){
-    $('#dob').editable({
-        format: 'yyyy-mm-dd',    
-        viewformat: 'dd/mm/yyyy',    
-        datepicker: {
-                weekStart: 1
-           }
-        }
-    });
-});
-</script>
-**/
-(function ($) {
-    "use strict";
-    
-    //store bootstrap-datepicker as bdateicker to exclude conflict with jQuery UI one
-    $.fn.bdatepicker = $.fn.datepicker.noConflict();
-    if(!$.fn.datepicker) { //if there were no other datepickers, keep also original name
-        $.fn.datepicker = $.fn.bdatepicker;    
-    }    
-    
-    var Date = function (options) {
-        this.init('date', options, Date.defaults);
-        this.initPicker(options, Date.defaults);
-    };
-
-    $.fn.editableutils.inherit(Date, $.fn.editabletypes.abstractinput);    
-    
-    $.extend(Date.prototype, {
-        initPicker: function(options, defaults) {
-            //'format' is set directly from settings or data-* attributes
-
-            //by default viewformat equals to format
-            if(!this.options.viewformat) {
-                this.options.viewformat = this.options.format;
-            }
-            
-            //try parse datepicker config defined as json string in data-datepicker
-            options.datepicker = $.fn.editableutils.tryParseJson(options.datepicker, true);
-            
-            //overriding datepicker config (as by default jQuery extend() is not recursive)
-            //since 1.4 datepicker internally uses viewformat instead of format. Format is for submit only
-            this.options.datepicker = $.extend({}, defaults.datepicker, options.datepicker, {
-                format: this.options.viewformat
-            });
-            
-            //language
-            this.options.datepicker.language = this.options.datepicker.language || 'en'; 
-
-            //store DPglobal
-            this.dpg = $.fn.bdatepicker.DPGlobal; 
-
-            //store parsed formats
-            this.parsedFormat = this.dpg.parseFormat(this.options.format);
-            this.parsedViewFormat = this.dpg.parseFormat(this.options.viewformat);            
-        },
-        
-        render: function () {
-            this.$input.bdatepicker(this.options.datepicker);
-            
-            //"clear" link
-            if(this.options.clear) {
-                this.$clear = $('<a href="#"></a>').html(this.options.clear).click($.proxy(function(e){
-                    e.preventDefault();
-                    e.stopPropagation();
-                    this.clear();
-                }, this));
-                
-                this.$tpl.parent().append($('<div class="editable-clear">').append(this.$clear));  
-            }                
-        },
-        
-        value2html: function(value, element) {
-           var text = value ? this.dpg.formatDate(value, this.parsedViewFormat, this.options.datepicker.language) : '';
-           Date.superclass.value2html.call(this, text, element); 
-        },
-
-        html2value: function(html) {
-            return this.parseDate(html, this.parsedViewFormat);
-        },   
-
-        value2str: function(value) {
-            return value ? this.dpg.formatDate(value, this.parsedFormat, this.options.datepicker.language) : '';
-        }, 
-
-        str2value: function(str) {
-            return this.parseDate(str, this.parsedFormat);
-        }, 
-
-        value2submit: function(value) {
-            return this.value2str(value);
-        },                    
-
-        value2input: function(value) {
-            this.$input.bdatepicker('update', value);
-        },
-
-        input2value: function() { 
-            return this.$input.data('datepicker').date;
-        },       
-
-        activate: function() {
-        },
-
-        clear:  function() {
-            this.$input.data('datepicker').date = null;
-            this.$input.find('.active').removeClass('active');
-            if(!this.options.showbuttons) {
-                this.$input.closest('form').submit(); 
-            }
-        },
-
-        autosubmit: function() {
-            this.$input.on('mouseup', '.day', function(e){
-                if($(e.currentTarget).is('.old') || $(e.currentTarget).is('.new')) {
-                    return;
-                }
-                var $form = $(this).closest('form');
-                setTimeout(function() {
-                    $form.submit();
-                }, 200);
-            });
-           //changedate is not suitable as it triggered when showing datepicker. see #149
-           /*
-           this.$input.on('changeDate', function(e){
-               var $form = $(this).closest('form');
-               setTimeout(function() {
-                   $form.submit();
-               }, 200);
-           });
-           */
-       },
-       
-       /*
-        For incorrect date bootstrap-datepicker returns current date that is not suitable
-        for datefield.
-        This function returns null for incorrect date.  
-       */
-       parseDate: function(str, format) {
-           var date = null, formattedBack;
-           if(str) {
-               date = this.dpg.parseDate(str, format, this.options.datepicker.language);
-               if(typeof str === 'string') {
-                   formattedBack = this.dpg.formatDate(date, format, this.options.datepicker.language);
-                   if(str !== formattedBack) {
-                       date = null;
-                   }
-               }
-           }
-           return date;
-       }
-
-    });
-
-    Date.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
-        /**
-        @property tpl 
-        @default <div></div>
-        **/         
-        tpl:'<div class="editable-date well"></div>',
-        /**
-        @property inputclass 
-        @default null
-        **/
-        inputclass: null,
-        /**
-        Format used for sending value to server. Also applied when converting date from <code>data-value</code> attribute.<br>
-        Possible tokens are: <code>d, dd, m, mm, yy, yyyy</code>  
-
-        @property format 
-        @type string
-        @default yyyy-mm-dd
-        **/
-        format:'yyyy-mm-dd',
-        /**
-        Format used for displaying date. Also applied when converting date from element's text on init.   
-        If not specified equals to <code>format</code>
-
-        @property viewformat 
-        @type string
-        @default null
-        **/
-        viewformat: null,
-        /**
-        Configuration of datepicker.
-        Full list of options: http://bootstrap-datepicker.readthedocs.org/en/latest/options.html
-
-        @property datepicker 
-        @type object
-        @default {
-            weekStart: 0,
-            startView: 0,
-            minViewMode: 0,
-            autoclose: false
-        }
-        **/
-        datepicker:{
-            weekStart: 0,
-            startView: 0,
-            minViewMode: 0,
-            autoclose: false
-        },
-        /**
-        Text shown as clear date button. 
-        If <code>false</code> clear button will not be rendered.
-
-        @property clear 
-        @type boolean|string
-        @default 'x clear'
-        **/
-        clear: '&times; clear'
-    });
-
-    $.fn.editabletypes.date = Date;
-
-}(window.jQuery));
-
-/**
-Bootstrap datefield input - modification for inline mode.
-Shows normal <input type="text"> and binds popup datepicker.  
-Automatically shown in inline mode.
-
-@class datefield
-@extends date
-
-@since 1.4.0
-**/
-(function ($) {
-    "use strict";
-    
-    var DateField = function (options) {
-        this.init('datefield', options, DateField.defaults);
-        this.initPicker(options, DateField.defaults);
-    };
-
-    $.fn.editableutils.inherit(DateField, $.fn.editabletypes.date);    
-    
-    $.extend(DateField.prototype, {
-        render: function () {
-            this.$input = this.$tpl.find('input');
-            this.setClass();
-            this.setAttr('placeholder');
-    
-            //bootstrap-datepicker is set `bdateicker` to exclude conflict with jQuery UI one. (in date.js)        
-            this.$tpl.bdatepicker(this.options.datepicker);
-            
-            //need to disable original event handlers
-            this.$input.off('focus keydown');
-            
-            //update value of datepicker
-            this.$input.keyup($.proxy(function(){
-               this.$tpl.removeData('date');
-               this.$tpl.bdatepicker('update');
-            }, this));
-            
-        },   
-        
-       value2input: function(value) {
-           this.$input.val(value ? this.dpg.formatDate(value, this.parsedViewFormat, this.options.datepicker.language) : '');
-           this.$tpl.bdatepicker('update');
-       },
-        
-       input2value: function() { 
-           return this.html2value(this.$input.val());
-       },              
-        
-       activate: function() {
-           $.fn.editabletypes.text.prototype.activate.call(this);
-       },
-       
-       autosubmit: function() {
-         //reset autosubmit to empty  
-       }
-    });
-    
-    DateField.defaults = $.extend({}, $.fn.editabletypes.date.defaults, {
-        /**
-        @property tpl 
-        **/         
-        tpl:'<div class="input-append date"><input type="text"/><span class="add-on"><i class="icon-th"></i></span></div>',
-        /**
-        @property inputclass 
-        @default 'input-small'
-        **/         
-        inputclass: 'input-small',
-        
-        /* datepicker config */
-        datepicker: {
-            weekStart: 0,
-            startView: 0,
-            minViewMode: 0,
-            autoclose: true
-        }
-    });
-    
-    $.fn.editabletypes.datefield = DateField;
-
-}(window.jQuery));
-/**
-Bootstrap-datetimepicker.  
-Based on [smalot bootstrap-datetimepicker plugin](https://github.com/smalot/bootstrap-datetimepicker). 
-Before usage you should manually include dependent js and css:
-
-    <link href="css/datetimepicker.css" rel="stylesheet" type="text/css"></link> 
-    <script src="js/bootstrap-datetimepicker.js"></script>
-
-For **i18n** you should include js file from here: https://github.com/smalot/bootstrap-datetimepicker/tree/master/js/locales
-and set `language` option.  
-
-@class datetime
-@extends abstractinput
-@final
-@since 1.4.4
-@example
-<a href="#" id="last_seen" data-type="datetime" data-pk="1" data-url="/post" title="Select date & time">15/03/2013 12:45</a>
-<script>
-$(function(){
-    $('#last_seen').editable({
-        format: 'yyyy-mm-dd hh:ii',    
-        viewformat: 'dd/mm/yyyy hh:ii',    
-        datetimepicker: {
-                weekStart: 1
-           }
-        }
-    });
-});
-</script>
-**/
-(function ($) {
-    "use strict";
-
-    var DateTime = function (options) {
-        this.init('datetime', options, DateTime.defaults);
-        this.initPicker(options, DateTime.defaults);
-    };
-
-    $.fn.editableutils.inherit(DateTime, $.fn.editabletypes.abstractinput);
-
-    $.extend(DateTime.prototype, {
-        initPicker: function(options, defaults) {
-            //'format' is set directly from settings or data-* attributes
-
-            //by default viewformat equals to format
-            if(!this.options.viewformat) {
-                this.options.viewformat = this.options.format;
-            }
-            
-            //try parse datetimepicker config defined as json string in data-datetimepicker
-            options.datetimepicker = $.fn.editableutils.tryParseJson(options.datetimepicker, true);
-
-            //overriding datetimepicker config (as by default jQuery extend() is not recursive)
-            //since 1.4 datetimepicker internally uses viewformat instead of format. Format is for submit only
-            this.options.datetimepicker = $.extend({}, defaults.datetimepicker, options.datetimepicker, {
-                format: this.options.viewformat
-            });
-
-            //language
-            this.options.datetimepicker.language = this.options.datetimepicker.language || 'en'; 
-
-            //store DPglobal
-            this.dpg = $.fn.datetimepicker.DPGlobal; 
-
-            //store parsed formats
-            this.parsedFormat = this.dpg.parseFormat(this.options.format, this.options.formatType);
-            this.parsedViewFormat = this.dpg.parseFormat(this.options.viewformat, this.options.formatType);
-        },
-
-        render: function () {
-            this.$input.datetimepicker(this.options.datetimepicker);
-
-            //adjust container position when viewMode changes
-            //see https://github.com/smalot/bootstrap-datetimepicker/pull/80
-            this.$input.on('changeMode', function(e) {
-                var f = $(this).closest('form').parent();
-                //timeout here, otherwise container changes position before form has new size
-                setTimeout(function(){
-                    f.triggerHandler('resize');
-                }, 0);
-            });
-
-            //"clear" link
-            if(this.options.clear) {
-                this.$clear = $('<a href="#"></a>').html(this.options.clear).click($.proxy(function(e){
-                    e.preventDefault();
-                    e.stopPropagation();
-                    this.clear();
-                }, this));
-
-                this.$tpl.parent().append($('<div class="editable-clear">').append(this.$clear));  
-            }
-        },
-
-        value2html: function(value, element) {
-            //formatDate works with UTCDate!
-            var text = value ? this.dpg.formatDate(this.toUTC(value), this.parsedViewFormat, this.options.datetimepicker.language, this.options.formatType) : '';
-            if(element) {
-                DateTime.superclass.value2html.call(this, text, element);
-            } else {
-                return text;
-            }
-        },
-
-        html2value: function(html) {
-            //parseDate return utc date!
-            var value = this.parseDate(html, this.parsedViewFormat); 
-            return value ? this.fromUTC(value) : null;
-        },
-
-        value2str: function(value) {
-            //formatDate works with UTCDate!
-            return value ? this.dpg.formatDate(this.toUTC(value), this.parsedFormat, this.options.datetimepicker.language, this.options.formatType) : '';
-       },
-
-       str2value: function(str) {
-           //parseDate return utc date!
-           var value = this.parseDate(str, this.parsedFormat);
-           return value ? this.fromUTC(value) : null;
-       },
-
-       value2submit: function(value) {
-           return this.value2str(value);
-       },
-
-       value2input: function(value) {
-           if(value) {
-             this.$input.data('datetimepicker').setDate(value);
-           }
-       },
-
-       input2value: function() { 
-           //date may be cleared, in that case getDate() triggers error
-           var dt = this.$input.data('datetimepicker');
-           return dt.date ? dt.getDate() : null;
-       },
-
-       activate: function() {
-       },
-
-       clear: function() {
-          this.$input.data('datetimepicker').date = null;
-          this.$input.find('.active').removeClass('active');
-          if(!this.options.showbuttons) {
-             this.$input.closest('form').submit(); 
-          }          
-       },
-
-       autosubmit: function() {
-           this.$input.on('mouseup', '.minute', function(e){
-               var $form = $(this).closest('form');
-               setTimeout(function() {
-                   $form.submit();
-               }, 200);
-           });
-       },
-
-       //convert date from local to utc
-       toUTC: function(value) {
-         return value ? new Date(value.valueOf() - value.getTimezoneOffset() * 60000) : value;  
-       },
-
-       //convert date from utc to local
-       fromUTC: function(value) {
-         return value ? new Date(value.valueOf() + value.getTimezoneOffset() * 60000) : value;  
-       },
-
-       /*
-        For incorrect date bootstrap-datetimepicker returns current date that is not suitable
-        for datetimefield.
-        This function returns null for incorrect date.  
-       */
-       parseDate: function(str, format) {
-           var date = null, formattedBack;
-           if(str) {
-               date = this.dpg.parseDate(str, format, this.options.datetimepicker.language, this.options.formatType);
-               if(typeof str === 'string') {
-                   formattedBack = this.dpg.formatDate(date, format, this.options.datetimepicker.language, this.options.formatType);
-                   if(str !== formattedBack) {
-                       date = null;
-                   } 
-               }
-           }
-           return date;
-       }
-
-    });
-
-    DateTime.defaults = $.extend({}, $.fn.editabletypes.abstractinput.defaults, {
-        /**
-        @property tpl 
-        @default <div></div>
-        **/         
-        tpl:'<div class="editable-date well"></div>',
-        /**
-        @property inputclass 
-        @default null
-        **/
-        inputclass: null,
-        /**
-        Format used for sending value to server. Also applied when converting date from <code>data-value</code> attribute.<br>
-        Possible tokens are: <code>d, dd, m, mm, yy, yyyy, h, i</code>  
-        
-        @property format 
-        @type string
-        @default yyyy-mm-dd hh:ii
-        **/         
-        format:'yyyy-mm-dd hh:ii',
-        formatType:'standard',
-        /**
-        Format used for displaying date. Also applied when converting date from element's text on init.   
-        If not specified equals to <code>format</code>
-        
-        @property viewformat 
-        @type string
-        @default null
-        **/
-        viewformat: null,
-        /**
-        Configuration of datetimepicker.
-        Full list of options: https://github.com/smalot/bootstrap-datetimepicker
-
-        @property datetimepicker 
-        @type object
-        @default { }
-        **/
-        datetimepicker:{
-            todayHighlight: false,
-            autoclose: false
-        },
-        /**
-        Text shown as clear date button. 
-        If <code>false</code> clear button will not be rendered.
-
-        @property clear 
-        @type boolean|string
-        @default 'x clear'
-        **/
-        clear: '&times; clear'
-    });
-
-    $.fn.editabletypes.datetime = DateTime;
-
-}(window.jQuery));
-/**
-Bootstrap datetimefield input - datetime input for inline mode.
-Shows normal <input type="text"> and binds popup datetimepicker.  
-Automatically shown in inline mode.
-
-@class datetimefield
-@extends datetime
-
-**/
-(function ($) {
-    "use strict";
-    
-    var DateTimeField = function (options) {
-        this.init('datetimefield', options, DateTimeField.defaults);
-        this.initPicker(options, DateTimeField.defaults);
-    };
-
-    $.fn.editableutils.inherit(DateTimeField, $.fn.editabletypes.datetime);
-    
-    $.extend(DateTimeField.prototype, {
-        render: function () {
-            this.$input = this.$tpl.find('input');
-            this.setClass();
-            this.setAttr('placeholder');
-            
-            this.$tpl.datetimepicker(this.options.datetimepicker);
-            
-            //need to disable original event handlers
-            this.$input.off('focus keydown');
-            
-            //update value of datepicker
-            this.$input.keyup($.proxy(function(){
-               this.$tpl.removeData('date');
-               this.$tpl.datetimepicker('update');
-            }, this));
-            
-        },   
-      
-       value2input: function(value) {
-           this.$input.val(this.value2html(value));
-           this.$tpl.datetimepicker('update');
-       },
-        
-       input2value: function() { 
-           return this.html2value(this.$input.val());
-       },              
-        
-       activate: function() {
-           $.fn.editabletypes.text.prototype.activate.call(this);
-       },
-       
-       autosubmit: function() {
-         //reset autosubmit to empty  
-       }
-    });
-    
-    DateTimeField.defaults = $.extend({}, $.fn.editabletypes.datetime.defaults, {
-        /**
-        @property tpl 
-        **/         
-        tpl:'<div class="input-append date"><input type="text"/><span class="add-on"><i class="icon-th"></i></span></div>',
-        /**
-        @property inputclass 
-        @default 'input-medium'
-        **/         
-        inputclass: 'input-medium',
-        
-        /* datetimepicker config */
-        datetimepicker:{
-            todayHighlight: false,
-            autoclose: true
-        }
-    });
-    
-    $.fn.editabletypes.datetimefield = DateTimeField;
-
-}(window.jQuery));
\ No newline at end of file
+!function(t){"use strict";const i=t.fn.editableform.Constructor.prototype.initInput;t.extend(t.fn.editableform.Constructor.prototype,{initInput:function(){i.apply(this);const n=null===this.input.options.inputclass||!1===this.input.options.inputclass,s="input-sm",e="text,select,textarea,password,email,url,tel,number,range,time,typeaheadjs".split(",");~t.inArray(this.input.type,e)&&(this.input.$input.addClass("form-control editable"),n&&(this.input.options.inputclass=s,this.input.$input.addClass(s))),"select"===this.input.type&&setTimeout((()=>{this.input.$input.focus().click()}),50);const o=this.$form.find(".editable-buttons"),l=n?[s]:this.input.options.inputclass.split(" ");for(let t=0;t<l.length;t++)"input-lg"===l[t].toLowerCase()&&o.find("button").removeClass("btn-sm").addClass("btn-lg")}}),t.fn.editableform.buttons='<button type="submit" class="btn btn-primary btn-sm editable-submit"><i class="bi bi-check"></i></button><button type="button" class="btn btn-secondary btn-sm editable-cancel"><i class="bi bi-x"></i></button>',t.fn.editableform.errorGroupClass="has-error",t.fn.editableform.errorBlockClass=null,t.fn.editableform.engine="bs3"}(window.jQuery),module.exports={};
\ No newline at end of file
diff --git a/package.json b/package.json
index e382d70..ac61006 100644
--- a/package.json
+++ b/package.json
@@ -1,26 +1,37 @@
 {
   "name": "@24unix/x-editable",
   "title": "X-editable",
-  "description": "In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery",
+  "description": "A maintained fork of x-editable for Bootstrap 5 support.",
   "version": "1.5.1",
-  "homepage": "http://github.com/vitalets/x-editable",
+  "homepage": "https://git.24unix.net/tracer/x-editable",
   "author": {
-    "name": "Vitaliy Potapov",
-    "email": "noginsk@rambler.ru"
+    "name": "Micha Espey",
+    "email": "tracer@24unix.net"
   },
+  "contributors": [
+    {
+      "name": "Vitaliy Potapov",
+      "email": "noginsk@rambler.ru",
+      "url": "https://github.com/vitalets",
+      "contributions": "Original author of x-editable"
+    }
+  ],
   "repository": {
     "type": "git",
-    "url": "git://github.com/vitalets/x-editable.git"
+    "url": "https://git.24unix.net/tracer/x-editable.git"
   },
   "bugs": {
-    "url": "https://github.com/vitalets/x-editable/issues"
+    "url": "https://git.24unix.net/tracer/x-editable/issues"
   },
   "licenses": [
     {
       "type": "MIT",
-      "url": "https://github.com/vitalets/x-editable/blob/master/LICENSE-MIT"
+      "url": "https://git.24unix.net/tracer/x-editable/blob/master/LICENSE-MIT"
     }
   ],
+  "main": "dist/bootstrap5-editable/js/bootstrap-editable.js",
+  "module": "dist/bootstrap5-editable/js/bootstrap-editable.esm.js",
+  "style": "dist/bootstrap5-editable/css/bootstrap-editable.css",
   "dependencies": {
     "bootstrap": "^5.3.3",
     "bootstrap-icons": "^1.11.3",
@@ -43,5 +54,11 @@
     "style-loader": "^4.0.0",
     "webpack-cli": "^6.0.1"
   },
-  "keywords": []
-}
+  "keywords": [
+    "editable",
+    "bootstrap5",
+    "jquery",
+    "in-place-editing",
+    "x-editable"
+  ]
+}
\ No newline at end of file